echoplayer: implement ADC to read battery voltage

Change-Id: I8043e7d2f02c10cb8c9d9ec59b7d216945431481
This commit is contained in:
Aidan MacDonald 2026-03-18 12:42:18 +00:00
parent 684b3d8c49
commit acd3a5f0ce
9 changed files with 402 additions and 27 deletions

View file

@ -1,25 +0,0 @@
/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
* $Id$
*
* Copyright (C) 2025 by Aidan MacDonald
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
****************************************************************************/
#include "adc.h"
void adc_init(void)
{
}

View file

@ -0,0 +1,140 @@
/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
* $Id$
*
* Copyright (C) 2026 by Aidan MacDonald
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
****************************************************************************/
#include "adc.h"
#include "mutex.h"
#include "kernel.h"
#include "fixedpoint.h"
#include "regs/stm32h743/adc.h"
#include "regs/stm32h743/rcc.h"
#define VREF_MV 3300
static volatile void *const adc3 = (void *)ITA_ADC3;
static struct mutex adc_mutex;
static void set_smptime(volatile void *adcbase, int channel, uint32_t smpval)
{
uint32_t reg_off = ITO_ADC_SMPR1;
if (channel > 10)
{
reg_off = ITO_ADC_SMPR2;
channel -= 10;
}
volatile uint32_t *reg = adcbase + reg_off;
uint32_t reg_val = *reg;
reg_val &= ~(0x7 << (channel * 3));
reg_val |= smpval << (channel * 3);
*reg = reg_val;
}
void adc_init(void)
{
mutex_init(&adc_mutex);
reg_writef(RCC_AHB4ENR, ADC3EN(1));
/* Power up ADC */
reg_writelf(adc3, ADC_CR, DEEPPWD(0));
reg_writelf(adc3, ADC_CR, ADVREGEN(1));
while (!reg_readlf(adc3, ADC_ISR, LDORDY));
/* Run calibration */
reg_writelf(adc3, ADC_CR, ADCALDIF(0), ADCALLIN(1), ADCAL(1));
while (reg_readlf(adc3, ADC_CR, ADCAL));
/* Enable the ADC */
reg_assignlf(adc3, ADC_ISR, ADRDY(1));
reg_writelf(adc3, ADC_CR, ADEN(1));
while (!reg_readlf(adc3, ADC_ISR, ADRDY));
/* Set sampling times for each channel (387.5 cycles @ 6 MHz = 64.5us) */
set_smptime(adc3, ADC_CHANNEL_VBUS, BV_ADC_SMPR1_SMP0_387_5CYCLE);
set_smptime(adc3, ADC_CHANNEL_BATTERY, BV_ADC_SMPR1_SMP0_387_5CYCLE);
}
/*
* Converts raw ADC reading to millivolts, assuming the
* ADC samples the output of a voltage divider as in the
* following circuit:
*
* [VIN]---[R1]
* |
* +----[ADC]
* |
* [R2]
* |
* [GND]
*
* The value returned is the voltage at the VIN node.
*/
static uint32_t scale_adc_val(uint32_t raw_val,
uint32_t vref_mV,
uint32_t r1_ohms,
uint32_t r2_ohms)
{
/*
* Finds the best fracbits value which doesn't overflow.
* GCC will optimize this loop to a constant.
*/
uint32_t fracbits = 16;
uint32_t conv_fac;
for (; fracbits > 0; --fracbits)
{
conv_fac = fp_div(r1_ohms + r2_ohms, r2_ohms, fracbits);
if (conv_fac <= UINT16_MAX)
break;
}
uint32_t conv_val = fp_mul(raw_val, conv_fac, fracbits);
return fp_mul(vref_mV, conv_val, 16);
}
unsigned short adc_read(int channel)
{
mutex_lock(&adc_mutex);
/* Configure ADC to read the channel */
reg_writelf(adc3, ADC_SQR1, L(1 - 1), SQ1(channel));
reg_varl(adc3, ADC_PCSEL) = (1 << channel);
/* Start conversion & wait until complete */
reg_writelf(adc3, ADC_CR, ADSTART(1));
while (reg_readlf(adc3, ADC_CR, ADSTART));
uint32_t raw_val = reg_readl(adc3, ADC_DR);
mutex_unlock(&adc_mutex);
/* Convert to millivolts */
switch (channel)
{
case ADC_CHANNEL_VBUS:
return scale_adc_val(raw_val, VREF_MV, 174, 100);
case ADC_CHANNEL_BATTERY:
return scale_adc_val(raw_val, VREF_MV, 174, 390);
default:
return 0;
}
}

View file

@ -21,4 +21,8 @@
#ifndef __ECHOPLAYER_ADC_TARGET_H__
#define __ECHOPLAYER_ADC_TARGET_H__
/* ADC3 channels */
#define ADC_CHANNEL_VBUS 11
#define ADC_CHANNEL_BATTERY 16 /* real battery voltage */
#endif /* __ECHOPLAYER_ADC_TARGET_H__ */

View file

@ -165,6 +165,7 @@ INIT_ATTR static void init_periph_clock(void)
reg_writef(RCC_D1CCIPR, SDMMCSEL_V(PLL1Q));
reg_writef(RCC_D2CCIP1R, SAI1SEL_V(PLL2P), SPI45SEL_V(HSE));
reg_writef(RCC_D2CCIP2R, I2C123SEL_V(HSI));
reg_writef(RCC_D3CCIPR, ADCSEL_V(PLL3R));
/* Enable AXI SRAM in sleep mode to allow DMA'ing out of it */
reg_writef(RCC_AHB3LPENR, AXISRAMEN(1));

View file

@ -55,4 +55,7 @@
#define GPIO_USB_VBUS GPIO_PA(9)
#define GPIO_ADC_VBAT GPIO_PH(5)
#define GPIO_ADC_VBUS GPIO_PC(1)
#endif /* __ECHOPLAYER_GPIO_TARGET_H__ */

View file

@ -20,6 +20,7 @@
****************************************************************************/
#include "power.h"
#include "mutex.h"
#include "adc.h"
#include "gpio-stm32h7.h"
#include "system-echoplayer.h"
#include "regs/cortex-m/cm_scb.h"
@ -130,5 +131,5 @@ bool charging_state(void)
int _battery_voltage(void)
{
return 4000;
return adc_read(ADC_CHANNEL_BATTERY);
}

View file

@ -49,6 +49,7 @@
#define F_LCD_AF9 GPIOF_FUNCTION(9, GPIO_TYPE_PUSH_PULL, GPIO_SPEED_MEDIUM, GPIO_PULL_DISABLED)
#define F_LPTIM4_OUT GPIOF_FUNCTION(3, GPIO_TYPE_PUSH_PULL, GPIO_SPEED_LOW, GPIO_PULL_DISABLED)
#define F_LPTIM1_OUT GPIOF_FUNCTION(1, GPIO_TYPE_PUSH_PULL, GPIO_SPEED_VERYHIGH, GPIO_PULL_DISABLED)
#define F_ANALOG GPIOF_ANALOG()
#if STM32H743_USBOTG_INSTANCE == STM32H743_USBOTG_INSTANCE_USB1
# define F_OTG_FS GPIOF_ANALOG()
@ -90,6 +91,8 @@ static const struct gpio_setting gpios[] = {
STM_DEFGPIO(GPIO_LCD_RESET, F_OUT_LS(0)), /* active low */
STM_DEFGPIO(GPIO_BACKLIGHT, F_LPTIM1_OUT),
STM_DEFGPIO(GPIO_USB_VBUS, F_INPUT), /* active high */
STM_DEFGPIO(GPIO_ADC_VBAT, F_ANALOG),
STM_DEFGPIO(GPIO_ADC_VBUS, F_ANALOG),
};
/* TODO - replace hex constants - there are probably mistakes here */