machine/stm32u585: implement ADC

Signed-off-by: deadprogram <ron@hybridgroup.com>
This commit is contained in:
deadprogram
2026-03-18 12:29:53 +01:00
parent a9c0501826
commit 7ca53138bc
4 changed files with 151 additions and 0 deletions
+8
View File
@@ -19,6 +19,14 @@ const (
A4 = PC1
A5 = PC0
// ADC pin aliases
ADC0 = A0
ADC1 = A1
ADC2 = A2
ADC3 = A3
ADC4 = A4
ADC5 = A5
D0 = PB7
D1 = PB6
D2 = PB3
+131
View File
@@ -0,0 +1,131 @@
//go:build stm32u5
package machine
import (
"device/stm32"
"unsafe"
)
// InitADC initializes the registers needed for ADC1.
func InitADC() {
// Enable ADC bus clock.
enableAltFuncClock(unsafe.Pointer(stm32.ADC1))
// Declare VDDA analog supply valid. Without ASV the ADC LDO cannot start.
stm32.PWR.SVMCR.SetBits(stm32.PWR_SVMCR_ASV)
// Set ADC12_Common CCR: synchronous clock HCLK/1.
// ADC12_Common is mapped as ADC_Type; CCR at offset 0x08 = .CR field.
stm32.ADC12_Common.CR.Set(1 << 16) // CKMODE=01
// Exit deep power-down mode.
stm32.ADC1.CR.ClearBits(stm32.ADC_CR_DEEPPWD)
// Enable internal voltage regulator and wait for LDO ready.
stm32.ADC1.CR.SetBits(stm32.ADC_CR_ADVREGEN)
for !stm32.ADC1.ISR.HasBits(stm32.ADC_ISR_LDORDY) {
}
// Calibrate ADC (single-ended).
stm32.ADC1.CR.SetBits(stm32.ADC_CR_ADCAL)
for stm32.ADC1.CR.HasBits(stm32.ADC_CR_ADCAL) {
}
// 12-bit resolution, overwrite DR on overrun.
stm32.ADC1.CFGR1.Set(stm32.ADC_CFGR1_RES_TwelveBit<<stm32.ADC_CFGR1_RES_Pos |
stm32.ADC_CFGR1_OVRMOD)
// Pre-select all channels (PCSEL must be written while ADEN=0).
stm32.ADC1.PCSEL.Set(0xFFFFF)
// Clear ADRDY flag, enable ADC, wait for ready.
stm32.ADC1.ISR.Set(stm32.ADC_ISR_ADRDY)
stm32.ADC1.CR.SetBits(stm32.ADC_CR_ADEN)
for !stm32.ADC1.ISR.HasBits(stm32.ADC_ISR_ADRDY) {
}
}
// Configure configures an ADC pin to be able to read analog data.
func (a ADC) Configure(config ADCConfig) {
a.Pin.Configure(PinConfig{Mode: PinInputAnalog})
ch := a.getChannel()
// Set sampling time for this channel.
// Channels 0-9 use SMPR1, channels 10-19 use SMPR2.
// Each channel uses 3 bits. Default to 36.5 cycles for reliable reads.
const smpVal = 0x4 // 36.5 ADC clock cycles
if ch <= 9 {
pos := uint8(ch) * 3
stm32.ADC1.SMPR1.ReplaceBits(smpVal, 0x7, pos)
} else {
pos := uint8(ch-10) * 3
stm32.ADC1.SMPR2.ReplaceBits(smpVal, 0x7, pos)
}
}
// Get returns the current value of an ADC pin in the range 0..0xffff.
func (a ADC) Get() uint16 {
ch := uint32(a.getChannel())
// PCSEL already set for all channels in InitADC (must be written while ADEN=0).
// Set up regular sequence: 1 conversion, channel in SQ1
// Note: ADC_SQR1_SQ1_Pos (0xC) in the device header is wrong for the 14-bit ADC1/ADC2.
// RM0456 shows SQ1 at bits [10:6] = position 6.
stm32.ADC1.SQR1.Set(ch << 6) // L=0 means 1 conversion
// Start conversion
stm32.ADC1.CR.SetBits(stm32.ADC_CR_ADSTART)
// Wait for end of conversion
for stm32.ADC1.ISR.Get()&stm32.ADC_ISR_EOC == 0 {
}
// Read 12-bit result and scale to 16-bit
result := uint16(stm32.ADC1.DR.Get()) << 4
return result
}
// getChannel returns the ADC1 channel number for a given GPIO pin.
// STM32U585 ADC1 channel mapping (from datasheet Table 24):
//
// PC0=CH1, PC1=CH2, PC2=CH3, PC3=CH4
// PA0=CH5, PA1=CH6, PA2=CH7, PA3=CH8
// PA4=CH9, PA5=CH10, PA6=CH11, PA7=CH12
// PB0=CH13, PB1=CH14
func (a ADC) getChannel() uint8 {
switch a.Pin {
case PC0:
return 1
case PC1:
return 2
case PC2:
return 3
case PC3:
return 4
case PA0:
return 5
case PA1:
return 6
case PA2:
return 7
case PA3:
return 8
case PA4:
return 9
case PA5:
return 10
case PA6:
return 11
case PA7:
return 12
case PB0:
return 13
case PB1:
return 14
}
return 0
}
+4
View File
@@ -272,6 +272,10 @@ func enableAltFuncClock(bus unsafe.Pointer) {
stm32.RCC.APB1ENR1.SetBits(stm32.RCC_APB1ENR1_SPI2EN)
case unsafe.Pointer(stm32.SPI3): // SPI3 clock enable
stm32.RCC.APB3ENR.SetBits(stm32.RCC_APB3ENR_SPI3EN)
case unsafe.Pointer(stm32.ADC1): // ADC1/ADC2 clock enable
stm32.RCC.AHB2ENR1.SetBits(stm32.RCC_AHB2ENR1_ADC12EN)
case unsafe.Pointer(stm32.ADC4): // ADC4 clock enable
stm32.RCC.AHB3ENR.SetBits(stm32.RCC_AHB3ENR_ADC4EN)
case unsafe.Pointer(stm32.WWDG): // Window watchdog clock enable
stm32.RCC.APB1ENR1.SetBits(stm32.RCC_APB1ENR1_WWDGEN)
case unsafe.Pointer(stm32.TIM1): // TIM1 clock enable
+8
View File
@@ -30,4 +30,12 @@ func initCLK() {
// Enable PWR peripheral clock (required on STM32U5 before accessing PWR registers).
stm32.RCC.AHB3ENR.SetBits(stm32.RCC_AHB3ENR_PWREN)
_ = stm32.RCC.AHB3ENR.Get() // read-back for clock stabilization
// Switch from VOS Range 4 to Range 3. Range 4 doesn't support ADC (RM0456 §6.3.6).
// Range 3 supports up to 50 MHz HCLK and enables ADC. No flash wait-state change needed at 4 MHz.
// The EPOD booster must be enabled before changing VOS (RM0456 §10.5.4).
stm32.PWR.VOSR.SetBits(stm32.PWR_VOSR_BOOSTEN)
stm32.PWR.VOSR.ReplaceBits(stm32.PWR_VOSR_VOS_Range3<<stm32.PWR_VOSR_VOS_Pos, stm32.PWR_VOSR_VOS_Msk, 0)
for !stm32.PWR.VOSR.HasBits(stm32.PWR_VOSR_VOSRDY) {
}
}