echoplayer: implement ADC to read battery voltage

rockbox-gerrit-noreply--- via rockbox-cvs <[email protected]>
Newsgroups gmane.comp.systems.archos.rockbox.cvs
Message-ID <[email protected]>
commit acd3a5f0cefed9b6f6edb85584d54f104d8de961
Author: Aidan MacDonald <[email protected]>
Date:   Wed Mar 18 12:42:18 2026 +0000

    echoplayer: implement ADC to read battery voltage
    
    Change-Id: I8043e7d2f02c10cb8c9d9ec59b7d216945431481

diff --git a/firmware/SOURCES b/firmware/SOURCES
index ca90c2f3d7..14365f965e 100644
--- a/firmware/SOURCES
+++ b/firmware/SOURCES
@@ -1926,7 +1926,6 @@ target/arm/rk27xx/ihifi2/audio-ihifi800.c
 #if CONFIG_CPU == STM32H743
 target/arm/stm32/crt0-stm32h7.S
 target/arm/stm32/vectors-stm32h7.S
-target/arm/stm32/adc-stm32h7.c
 #ifndef BOOTLOADER
 target/arm/stm32/debug-stm32h7.c
 #endif
@@ -1942,6 +1941,7 @@ target/arm/stm32/usb-stm32h7.c
 #endif
 
 #if defined(ECHO_R1)
+target/arm/stm32/echoplayer/adc-echoplayer.c
 target/arm/stm32/echoplayer/audiohw-echoplayer.c
 target/arm/stm32/echoplayer/backlight-echoplayer.c
 target/arm/stm32/echoplayer/button-echoplayer.c
diff --git a/firmware/reggen/stm32h743.regs b/firmware/reggen/stm32h743.regs
index 7dc6153c2c..03292c347c 100644
--- a/firmware/reggen/stm32h743.regs
+++ b/firmware/reggen/stm32h743.regs
@@ -1509,3 +1509,251 @@ I2C1 @ 0x40005400 : I2C
 I2C2 @ 0x40005800 : I2C
 I2C3 @ 0x40005C00 : I2C
 I2C4 @ 0x58001C00 : I2C
+
+block ADC {
+    ISR @ 0x00 : reg {
+        12 LDORDY
+        10 JQOVF
+        09 AWD3
+        08 AWD2
+        07 AWD1
+        06 JEOS
+        05 JEOC
+        04 OVR
+        03 EOS
+        02 EOC
+        01 EOSMP
+        00 ADRDY
+    }
+
+    IER @ 0x04 : reg {
+        10 JQOVFIE
+        09 AWD3IE
+        08 AWD2IE
+        07 AWD1IE
+        06 JEOSIE
+        05 JEOCIE
+        04 OVRIE
+        03 EOSIE
+        02 EOCIE
+        01 EOSMPIE
+        00 ADRDYIE
+    }
+
+    CR @ 0x08 : reg {
+        -- 31 ADCAL
+        -- 30 ADCALDIF
+        -- 29 DEEPPWD
+        -- 28 ADVREGEN
+        -- 27 LINCALRDYW6
+        -- 26 LINCALRDYW5
+        -- 25 LINCALRDYW4
+        -- 24 LINCALRDYW3
+        -- 23 LINCALRDYW2
+        -- 22 LINCALRDYW1
+        -- 16 ADCALLIN
+        -- 09 BOOST1 // present on revision V only
+        -- 08 BOOST0
+        -- 05 JADSTP
+        -- 04 ADSTP
+        -- 03 JADSTART
+        -- 02 ADSTART
+        -- 01 ADDIS
+        -- 00 ADEN
+    }
+
+    CFGR @ 0x0c : reg {
+        -- 31 JQDIS
+        30 26 AWD1CH
+        -- 25 JAUTO
+        -- 24 JAWD1EN
+        -- 23 AWD1EN
+        -- 22 AWD1SGL
+        -- 21 JQM
+        -- 20 JDISCEN
+        19 17 DISCNUM
+        -- 16 DISCEN
+        -- 14 AUTDLY
+        -- 13 CONT
+        -- 12 OVRMOD
+        11 10 EXTEN
+        09 05 EXTSEL
+        04 02 RES
+        01 00 DMNGT
+    }
+
+    CFGR2 @ 0x10 : reg {
+        31 28 LSHIFT
+        25 16 OSVR
+        -- 14 RSHIFT4
+        -- 13 RSHIFT3
+        -- 12 RSHIFT2
+        -- 11 RSHIFT1
+        -- 10 ROVSM
+        -- 09 TROVS
+        08 05 OVSS
+        -- 01 JOVSE
+        -- 00 ROVSE
+    }
+
+    enum SMPTIME {
+        0 = 1_5CYCLE
+        1 = 2_5CYCLE
+        2 = 8_5CYCLE
+        3 = 16_5CYCLE
+        4 = 32_5CYCLE
+        5 = 64_5CYCLE
+        6 = 387_5CYCLE
+        7 = 810_5CYCLE
+    }
+
+    SMPR1 @ 0x14 : reg {
+        29 27 SMP9 : SMPTIME
+        26 24 SMP8 : SMPTIME
+        23 21 SMP7 : SMPTIME
+        20 18 SMP6 : SMPTIME
+        17 15 SMP5 : SMPTIME
+        14 12 SMP4 : SMPTIME
+        11 09 SMP3 : SMPTIME
+        08 06 SMP2 : SMPTIME
+        05 03 SMP1 : SMPTIME
+        02 00 SMP0 : SMPTIME
+    }
+
+    SMPR2 @ 0x18 : reg {
+        29 27 SMP19 : SMPTIME
+        26 24 SMP18 : SMPTIME
+        23 21 SMP17 : SMPTIME
+        20 18 SMP16 : SMPTIME
+        17 15 SMP15 : SMPTIME
+        14 12 SMP14 : SMPTIME
+        11 09 SMP13 : SMPTIME
+        08 06 SMP12 : SMPTIME
+        05 03 SMP11 : SMPTIME
+        02 00 SMP10 : SMPTIME
+    }
+
+    PCSEL @ 0x1c : reg
+    LTR1 @ 0x20 : reg
+    HTR1 @ 0x24 : reg
+
+    SQR1 @ 0x30 : reg {
+        28 24 SQ4
+        22 18 SQ3
+        16 12 SQ2
+        10 06 SQ1
+        03 00 L
+    }
+
+    SQR2 @ 0x34 : reg {
+        28 24 SQ9
+        22 18 SQ8
+        16 12 SQ7
+        10 06 SQ6
+        04 00 SQ5
+    }
+
+    SQR3 @ 0x38 : reg {
+        28 24 SQ14
+        22 18 SQ13
+        16 12 SQ12
+        10 06 SQ11
+        04 00 SQ10
+    }
+
+    SQR4 @ 0x38 : reg {
+        10 06 SQ16
+        04 00 SQ15
+    }
+
+    DR @ 0x40 : reg
+
+    JSQR @ 0x4c : reg {
+        31 27 JSQ4
+        25 21 JSQ3
+        19 15 JSQ2
+        13 09 JSQ1
+        08 07 JEXTEN
+        06 02 JEXTSEL
+        01 00 JL
+    }
+
+    OFR @ 0x60 [4; 0x04] : reg {
+        -- 31 SSATE
+        30 26 OFFSET_CH
+        25 00 OFFSET
+    }
+
+    JDR @ 0x80 [4; 0x04] : reg
+
+    AWD2CR @ 0xa0 : reg {
+        19 00 AWD2CH
+    }
+
+    AWD3CR @ 0xa4 : reg {
+        19 00 AWD3CH
+    }
+
+    LTR2 @ 0xb0 : reg
+    HTR2 @ 0xb4 : reg
+    LTR3 @ 0xb8 : reg
+    HTR3 @ 0xbc : reg
+
+    DIFSEL @ 0xc0 : reg
+
+    CALFACT @ 0xc4 : reg {
+        26 16 D
+        10 00 S
+    }
+
+    CALFACT2 @ 0xc8 : reg {
+        29 00 LINCALFACT
+    }
+
+    CSR @ 0x300 : reg {
+        26 JQOVF_SLV
+        25 AWD3_SLV
+        24 AWD2_SLV
+        23 AWD1_SLV
+        22 JEOS_SLV
+        21 JEOC_SLV
+        20 OVR_SLV
+        19 EOS_SLV
+        18 EOC_SLV
+        17 EOSMP_SLV
+        16 ADRDY_SLV
+        10 JQOVF_MST
+        09 AWD3_MST
+        08 AWD2_MST
+        07 AWD1_MST
+        06 JEOS_MST
+        05 JEOC_MST
+        04 OVR_MST
+        03 EOS_MST
+        02 EOC_MST
+        01 EOSMP_MST
+        00 ADRDY_MST
+    }
+
+    CCR @ 0x308 : reg {
+        -- 24 VBATEN
+        -- 23 TSEN
+        -- 22 VREFEN
+        21 18 PRESC
+        17 16 CKMOD
+        15 14 DAMDF
+        10 08 DELAY
+        04 00 DUAL
+    }
+
+    CDR @ 0x30c : reg {
+        31 16 RDATA_SLV
+        15 00 RDATA_MST
+    }
+
+    CDR2 @ 0x310 : reg
+}
+
+ADC1 @ 0x40022000 : ADC
+ADC2 @ 0x40022100 : ADC
+ADC3 @ 0x58026000 : ADC
diff --git a/firmware/target/arm/stm32/adc-stm32h7.c b/firmware/target/arm/stm32/adc-stm32h7.c
deleted file mode 100644
index 8d53ba8e1f..0000000000
--- a/firmware/target/arm/stm32/adc-stm32h7.c
+++ /dev/null
@@ -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)
-{
-}
diff --git a/firmware/target/arm/stm32/echoplayer/adc-echoplayer.c b/firmware/target/arm/stm32/echoplayer/adc-echoplayer.c
new file mode 100644
index 0000000000..245f0375db
--- /dev/null
+++ b/firmware/target/arm/stm32/echoplayer/adc-echoplayer.c
@@ -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;
+    }
+}
diff --git a/firmware/target/arm/stm32/echoplayer/adc-target.h b/firmware/target/arm/stm32/echoplayer/adc-target.h
index 6c0eafc463..5edc098b37 100644
--- a/firmware/target/arm/stm32/echoplayer/adc-target.h
+++ b/firmware/target/arm/stm32/echoplayer/adc-target.h
@@ -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__ */
diff --git a/firmware/target/arm/stm32/echoplayer/clock-echoplayer.c b/firmware/target/arm/stm32/echoplayer/clock-echoplayer.c
index 1e2be5cd5c..d9bd0d31c5 100644
--- a/firmware/target/arm/stm32/echoplayer/clock-echoplayer.c
+++ b/firmware/target/arm/stm32/echoplayer/clock-echoplayer.c
@@ -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));
diff --git a/firmware/target/arm/stm32/echoplayer/gpio-target.h b/firmware/target/arm/stm32/echoplayer/gpio-target.h
index 2e92f249c8..36c5df281b 100644
--- a/firmware/target/arm/stm32/echoplayer/gpio-target.h
+++ b/firmware/target/arm/stm32/echoplayer/gpio-target.h
@@ -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__ */
diff --git a/firmware/target/arm/stm32/echoplayer/power-echoplayer.c b/firmware/target/arm/stm32/echoplayer/power-echoplayer.c
index 63bf173d33..bbe3c627e5 100644
--- a/firmware/target/arm/stm32/echoplayer/power-echoplayer.c
+++ b/firmware/target/arm/stm32/echoplayer/power-echoplayer.c
@@ -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);
 }
diff --git a/firmware/target/arm/stm32/echoplayer/system-echoplayer.c b/firmware/target/arm/stm32/echoplayer/system-echoplayer.c
index 04e2a563ba..29d017eec9 100644
--- a/firmware/target/arm/stm32/echoplayer/system-echoplayer.c
+++ b/firmware/target/arm/stm32/echoplayer/system-echoplayer.c
@@ -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 */
-- 
rockbox-cvs mailing list
[email protected]
https://lists.haxx.se/mailman/listinfo/rockbox-cvs
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.