Skip to content

Commit f50eccf

Browse files
committed
esp32c3/esp32s3: refactor ADC implementation to reduce code duplication.
This refactoring reduces code duplication from the esp32c3/esp32s3 ADC implementation, by reusing the register/efuse calibration code since the same basic procedures are used by both processors. Signed-off-by: deadprogram <ron@hybridgroup.com>
1 parent f195d09 commit f50eccf

File tree

3 files changed

+312
-498
lines changed

3 files changed

+312
-498
lines changed

src/machine/machine_esp32c3_adc.go

Lines changed: 43 additions & 252 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,11 @@ package machine
55
import (
66
"device/esp"
77
"errors"
8-
"runtime/volatile"
9-
"unsafe"
108
)
119

10+
// newRegI2C returns the regI2C configured for ESP32-C3: hostID=0, drefInit=1.
11+
func newRegI2C() regI2C { return regI2C{hostID: 0, drefInit: 1} }
12+
1213
const (
1314
// ADC attenuation values for ESP32-C3 APB_SARADC.
1415
// 0 dB : ~0 .. 1.1 V
@@ -34,8 +35,7 @@ func InitADC() {
3435
esp.APB_SARADC.SetCLKM_CONF_CLKM_DIV_A(0)
3536
esp.APB_SARADC.SetCLKM_CONF_CLK_EN(1)
3637

37-
var c adcSelfCalibration
38-
c.calibrate()
38+
adcSelfCalibrate()
3939
}
4040

4141
// ESP32-C3: ADC1 = GPIO0–GPIO4 (ch 0–4), ADC2 = GPIO5 (ch 0). ADC2 shares with Wi‑Fi;
@@ -88,51 +88,51 @@ func (a ADC) Get() uint16 {
8888

8989
// adcSelfCalibration
9090
const (
91-
adcCalTimesC3 = 15
92-
adcCalOffsetRangeC3 = uint32(4096)
93-
adcCalRtcMagicC3 = uint32(0xADC1C401)
94-
adcCalInitMinC3 = uint32(1000)
95-
adcCalInitMaxC3 = uint32(4096)
96-
adcGndOffsetCompC3 = uint32(0)
91+
adcCalTimesC3 = 15
92+
adcCalRtcMagicC3 = uint32(0xADC1C401)
93+
adcCalInitMinC3 = uint32(1000)
94+
adcCalInitMaxC3 = uint32(4096)
9795
)
9896

99-
type adcSelfCalibration struct {
100-
digiRefMv uint32
101-
}
102-
103-
// calibrate sets ADC1/ADC2 init code from RTC or runs self-calibration (GND).
97+
// selfCalibrate sets ADC1/ADC2 init code from RTC or runs self-calibration (GND).
10498
// eFuse is not used: on ESP32-C3 the ADC calibration fields in BLK2 are often unprogrammed.
105-
func (c *adcSelfCalibration) calibrate() {
106-
reg := regI2C{}
99+
func adcSelfCalibrate() {
100+
reg := newRegI2C()
107101
reg.sarEnable()
108102

109103
var adc1Code uint32
110-
if saved, ok := c.restoreFromRTC(); ok {
104+
if saved, ok := restoreFromRTC(); ok {
111105
adc1Code = saved
112106
} else {
113-
c.calSetupADC1()
114-
reg.adc1CalibrationInit(0)
115-
reg.adc1CalibrationPrepare(0)
116-
adc1Code = c.calibrateUnit(reg, 0, c.readADC1)
117-
c.saveToRTC(adc1Code)
118-
reg.adc1CalibrationFinish(0)
107+
calSetupADC1()
108+
reg.calibrationInit(0)
109+
reg.calibrationPrepare(0)
110+
adc1Code = reg.calibrateBinarySearch(0, adcCalTimesC3, readADC1)
111+
if adc1Code < adcCalInitMinC3 {
112+
adc1Code = adcCalInitMinC3
113+
}
114+
if adc1Code > adcCalInitMaxC3 {
115+
adc1Code = adcCalInitMaxC3
116+
}
117+
saveToRTC(adc1Code)
118+
reg.calibrationFinish(0)
119119
}
120120

121-
c.applyADC1Code(reg, adc1Code)
122-
c.applyADC2Code(reg, adc1Code)
121+
applyADC1Code(reg, adc1Code)
122+
applyADC2Code(reg, adc1Code)
123123
}
124124

125125
// calSetupADC1 configures APB_SARADC for oneshot sampling on ADC1 channel 0
126126
// with fixed attenuation. This is used only during self‑calibration.
127-
func (c *adcSelfCalibration) calSetupADC1() {
127+
func calSetupADC1() {
128128
esp.APB_SARADC.SetONETIME_SAMPLE_SARADC_ONETIME_ATTEN(atten11dB)
129129
esp.APB_SARADC.SetONETIME_SAMPLE_SARADC_ONETIME_CHANNEL(0)
130130
esp.APB_SARADC.SetONETIME_SAMPLE_SARADC1_ONETIME_SAMPLE(1)
131131
}
132132

133133
// calSetupADC2 configures APB_SARADC for oneshot sampling on ADC2 (GPIO5, ch 0).
134134
// On C3, onetime_channel = (unit<<3)|channel → ADC2 ch0 = 8.
135-
func (c *adcSelfCalibration) calSetupADC2() {
135+
func calSetupADC2() {
136136
esp.APB_SARADC.SetONETIME_SAMPLE_SARADC_ONETIME_ATTEN(atten11dB)
137137
esp.APB_SARADC.SetONETIME_SAMPLE_SARADC_ONETIME_CHANNEL(8) // (1<<3)|0 for ADC2
138138
esp.APB_SARADC.SetARB_CTRL_ADC_ARB_APB_FORCE(1)
@@ -142,7 +142,7 @@ func (c *adcSelfCalibration) calSetupADC2() {
142142

143143
// readADC1 performs a single ADC1 conversion using the APB_SARADC
144144
// oneshot path and returns the raw 12‑bit result (0..4095).
145-
func (c *adcSelfCalibration) readADC1() uint32 {
145+
func readADC1() uint32 {
146146
esp.APB_SARADC.SetINT_CLR_APB_SARADC1_DONE_INT_CLR(1)
147147
esp.APB_SARADC.SetONETIME_SAMPLE_SARADC_ONETIME_START(0)
148148
esp.APB_SARADC.SetONETIME_SAMPLE_SARADC_ONETIME_START(1)
@@ -154,7 +154,7 @@ func (c *adcSelfCalibration) readADC1() uint32 {
154154
}
155155

156156
// readADC2 performs a single ADC2 conversion and returns the raw 12‑bit result (0..4095).
157-
func (c *adcSelfCalibration) readADC2() uint32 {
157+
func readADC2() uint32 {
158158
esp.APB_SARADC.SetINT_CLR_APB_SARADC2_DONE_INT_CLR(1)
159159
esp.APB_SARADC.SetONETIME_SAMPLE_SARADC_ONETIME_START(0)
160160
esp.APB_SARADC.SetONETIME_SAMPLE_SARADC_ONETIME_START(1)
@@ -167,7 +167,7 @@ func (c *adcSelfCalibration) readADC2() uint32 {
167167
return uint32(raw)
168168
}
169169

170-
func (c *adcSelfCalibration) restoreFromRTC() (uint32, bool) {
170+
func restoreFromRTC() (uint32, bool) {
171171
if esp.RTC_CNTL.GetSTORE0() != adcCalRtcMagicC3 {
172172
return 0, false
173173
}
@@ -178,7 +178,7 @@ func (c *adcSelfCalibration) restoreFromRTC() (uint32, bool) {
178178
return code, true
179179
}
180180

181-
func (c *adcSelfCalibration) saveToRTC(code uint32) {
181+
func saveToRTC(code uint32) {
182182
if code < adcCalInitMinC3 || code > adcCalInitMaxC3 {
183183
return
184184
}
@@ -187,228 +187,19 @@ func (c *adcSelfCalibration) saveToRTC(code uint32) {
187187
}
188188

189189
// applyADC1Code sets ADC1 init code and finishes calibration.
190-
func (c *adcSelfCalibration) applyADC1Code(reg regI2C, code uint32) {
191-
c.calSetupADC1()
192-
reg.adc1CalibrationInit(0)
193-
reg.adc1CalibrationPrepare(0)
194-
reg.adc1SetCalibrationParam(0, code)
195-
reg.adc1CalibrationFinish(0)
190+
func applyADC1Code(reg regI2C, code uint32) {
191+
calSetupADC1()
192+
reg.calibrationInit(0)
193+
reg.calibrationPrepare(0)
194+
reg.setCalibrationParam(0, code)
195+
reg.calibrationFinish(0)
196196
}
197197

198198
// applyADC2Code sets ADC2 init code and finishes calibration. On C3 eFuse V1
199199
// there is no separate ADC2 calibration; IDF uses ADC1 init code for both units.
200-
func (c *adcSelfCalibration) applyADC2Code(reg regI2C, code uint32) {
201-
reg.adc1CalibrationInit(1)
202-
reg.adc1CalibrationPrepare(1)
203-
reg.adc1SetCalibrationParam(1, code)
204-
reg.adc1CalibrationFinish(1)
205-
}
206-
207-
func (c *adcSelfCalibration) calibrateUnit(reg regI2C, adcN uint8, readADC func() uint32) uint32 {
208-
var codeList [adcCalTimesC3]uint32
209-
var codeSum uint32
210-
211-
for rpt := 0; rpt < adcCalTimesC3; rpt++ {
212-
codeH := adcCalOffsetRangeC3
213-
codeL := uint32(0)
214-
chkCode := (codeH + codeL) / 2
215-
reg.adc1SetCalibrationParam(adcN, chkCode)
216-
selfCal := readADC()
217-
218-
for codeH-codeL > 1 {
219-
if selfCal == 0 {
220-
codeH = chkCode
221-
} else {
222-
codeL = chkCode
223-
}
224-
chkCode = (codeH + codeL) / 2
225-
reg.adc1SetCalibrationParam(adcN, chkCode)
226-
selfCal = readADC()
227-
if codeH-codeL == 1 {
228-
chkCode++
229-
reg.adc1SetCalibrationParam(adcN, chkCode)
230-
selfCal = readADC()
231-
}
232-
}
233-
codeList[rpt] = chkCode
234-
codeSum += chkCode
235-
}
236-
237-
codeL := codeList[0]
238-
codeH := codeList[0]
239-
for i := 0; i < adcCalTimesC3; i++ {
240-
if codeList[i] < codeL {
241-
codeL = codeList[i]
242-
}
243-
if codeList[i] > codeH {
244-
codeH = codeList[i]
245-
}
246-
}
247-
excluded := codeH + codeL
248-
remaining := codeSum - excluded
249-
finalCode := remaining / (adcCalTimesC3 - 2)
250-
if remaining%(adcCalTimesC3-2) >= 4 {
251-
finalCode++
252-
}
253-
if finalCode < adcCalInitMinC3 {
254-
finalCode = adcCalInitMinC3
255-
}
256-
if finalCode > adcCalInitMaxC3 {
257-
finalCode = adcCalInitMaxC3
258-
}
259-
260-
reg.adc1SetCalibrationParam(adcN, finalCode)
261-
return finalCode
262-
}
263-
264-
// regi2c
265-
266-
// regI2C on ESP32‑C3 exposes the internal analog I2C bus that controls
267-
// SAR ADC trim registers. Constants below mirror the layout from
268-
// ESP‑IDF's soc/regi2c_saradc.h and TRM (I2C_RTC_CONFIG2 block).
269-
const (
270-
// i2cSarADC/i2cSarADCHostID select the SAR ADC block on the internal bus.
271-
i2cSarADC = uint8(0x69)
272-
i2cSarADCHostID = uint8(0)
273-
274-
// adc*_Dref* define the DREF (reference) bitfields for ADC1/ADC2.
275-
adc1DrefAddr = uint8(0x2)
276-
adc1DrefMSB = uint8(6)
277-
adc1DrefLSB = uint8(4)
278-
279-
adc2DrefAddr = uint8(0x5)
280-
adc2DrefMSB = uint8(6)
281-
adc2DrefLSB = uint8(4)
282-
283-
// adc*_EncalGnd* control ENCAL_GND: route internal ground to ADC input
284-
// during self‑calibration so that the pin is effectively disconnected.
285-
adc1EncalGndAddr = uint8(0x7)
286-
adc1EncalGndMSB = uint8(5)
287-
adc1EncalGndLSB = uint8(5)
288-
289-
adc2EncalGndAddr = uint8(0x7)
290-
adc2EncalGndMSB = uint8(7)
291-
adc2EncalGndLSB = uint8(7)
292-
293-
// adc*_InitCode* hold the INIT_CODE (offset) that hardware uses to
294-
// compensate ADC1/ADC2 offset error.
295-
adc1InitCodeHighAddr = uint8(0x1)
296-
adc1InitCodeHighMSB = uint8(3)
297-
adc1InitCodeHighLSB = uint8(0)
298-
adc1InitCodeLowAddr = uint8(0x0)
299-
adc1InitCodeLowMSB = uint8(7)
300-
adc1InitCodeLowLSB = uint8(0)
301-
302-
adc2InitCodeHighAddr = uint8(0x4)
303-
adc2InitCodeHighMSB = uint8(3)
304-
adc2InitCodeHighLSB = uint8(0)
305-
adc2InitCodeLowAddr = uint8(0x3)
306-
adc2InitCodeLowMSB = uint8(7)
307-
adc2InitCodeLowLSB = uint8(0)
308-
309-
// ANA_CONFIG/ANA_CONFIG2: enable analog SAR I2C domain before regI2C access.
310-
anaConfigReg = uintptr(0x6000E044)
311-
i2cSarEnMask = uint32(1 << 18)
312-
anaConfig2Reg = uintptr(0x6000E048)
313-
anaSarCfg2En = uint32(1 << 16)
314-
315-
// I2C_RTC_CONFIG2 master control register used by regI2C operations.
316-
i2cMstCtrlHost = uintptr(0x6000E000)
317-
i2cMstBusyBit = uint32(1 << 25)
318-
i2cMstWrCntl = uint32(1 << 24)
319-
i2cMstDataMask = uint32(0xFF << 16)
320-
i2cMstDataShift = 16
321-
i2cMstTimeout = 10000
322-
)
323-
324-
type regI2C struct{}
325-
326-
// sarEnable enables the SAR analog I2C domain before any regI2C access.
327-
func (r *regI2C) sarEnable() {
328-
cfg := (*volatile.Register32)(unsafe.Pointer(anaConfigReg))
329-
cfg2 := (*volatile.Register32)(unsafe.Pointer(anaConfig2Reg))
330-
esp.RTC_CNTL.SetANA_CONF_SAR_I2C_PU(1)
331-
cfg.Set(cfg.Get() &^ i2cSarEnMask)
332-
cfg2.Set(cfg2.Get() | anaSarCfg2En)
333-
}
334-
335-
// adc1CalibrationInit sets DREF for the selected ADC unit
336-
// before running the self‑calibration procedure.
337-
func (r *regI2C) adc1CalibrationInit(adcN uint8) {
338-
if adcN == 0 {
339-
r.writeMask(i2cSarADC, i2cSarADCHostID, adc1DrefAddr, adc1DrefMSB, adc1DrefLSB, 1)
340-
} else {
341-
r.writeMask(i2cSarADC, i2cSarADCHostID, adc2DrefAddr, adc2DrefMSB, adc2DrefLSB, 1)
342-
}
343-
}
344-
345-
// adc1CalibrationPrepare enables ENCAL_GND so that the ADC input
346-
// is internally shorted to ground during self‑calibration.
347-
func (r *regI2C) adc1CalibrationPrepare(adcN uint8) {
348-
if adcN == 0 {
349-
r.writeMask(i2cSarADC, i2cSarADCHostID, adc1EncalGndAddr, adc1EncalGndMSB, adc1EncalGndLSB, 1)
350-
} else {
351-
r.writeMask(i2cSarADC, i2cSarADCHostID, adc2EncalGndAddr, adc2EncalGndMSB, adc2EncalGndLSB, 1)
352-
}
353-
}
354-
355-
// adc1CalibrationFinish clears ENCAL_GND and reconnects the ADC
356-
// input back to the external pad after self‑calibration.
357-
func (r *regI2C) adc1CalibrationFinish(adcN uint8) {
358-
if adcN == 0 {
359-
r.writeMask(i2cSarADC, i2cSarADCHostID, adc1EncalGndAddr, adc1EncalGndMSB, adc1EncalGndLSB, 0)
360-
} else {
361-
r.writeMask(i2cSarADC, i2cSarADCHostID, adc2EncalGndAddr, adc2EncalGndMSB, adc2EncalGndLSB, 0)
362-
}
363-
}
364-
365-
// adc1SetCalibrationParam writes the INIT_CODE (offset trim) for
366-
// the selected ADC unit using the regI2C bitfields.
367-
func (r *regI2C) adc1SetCalibrationParam(adcN uint8, param uint32) {
368-
msb := uint8(param >> 8)
369-
lsb := uint8(param & 0xFF)
370-
if adcN == 0 {
371-
r.writeMask(i2cSarADC, i2cSarADCHostID, adc1InitCodeHighAddr, adc1InitCodeHighMSB, adc1InitCodeHighLSB, msb)
372-
r.writeMask(i2cSarADC, i2cSarADCHostID, adc1InitCodeLowAddr, adc1InitCodeLowMSB, adc1InitCodeLowLSB, lsb)
373-
} else {
374-
r.writeMask(i2cSarADC, i2cSarADCHostID, adc2InitCodeHighAddr, adc2InitCodeHighMSB, adc2InitCodeHighLSB, msb)
375-
r.writeMask(i2cSarADC, i2cSarADCHostID, adc2InitCodeLowAddr, adc2InitCodeLowMSB, adc2InitCodeLowLSB, lsb)
376-
}
377-
}
378-
379-
// waitIdle polls the REGI2C master BUSY bit until it clears or the
380-
// simple software timeout expires. This matches the busy‑wait helper
381-
// used in ESP‑IDF's regi2c_ctrl.c.
382-
func (r *regI2C) waitIdle(reg *volatile.Register32) bool {
383-
for i := 0; i < i2cMstTimeout; i++ {
384-
if reg.Get()&i2cMstBusyBit == 0 {
385-
return true
386-
}
387-
}
388-
return false
389-
}
390-
391-
// writeMask is a software implementation of REGI2C_WRITE_MASK macro:
392-
// 1. select block + regAddr,
393-
// 2. read current byte,
394-
// 3. update only [msb:lsb] bitfield,
395-
// 4. write it back via internal I2C master.
396-
func (r *regI2C) writeMask(block, hostID, regAddr, msb, lsb, data uint8) {
397-
if hostID != i2cSarADCHostID {
398-
return
399-
}
400-
reg := (*volatile.Register32)(unsafe.Pointer(i2cMstCtrlHost))
401-
if !r.waitIdle(reg) {
402-
return
403-
}
404-
reg.Set(uint32(block) | uint32(regAddr)<<8)
405-
if !r.waitIdle(reg) {
406-
return
407-
}
408-
cur := (reg.Get() & i2cMstDataMask) >> i2cMstDataShift
409-
mask := uint32(1<<(msb-lsb+1)-1) << lsb
410-
cur &^= mask
411-
cur |= uint32(data&(1<<(msb-lsb+1)-1)) << lsb
412-
reg.Set(uint32(block) | uint32(regAddr)<<8 | i2cMstWrCntl | (cur<<i2cMstDataShift)&i2cMstDataMask)
413-
r.waitIdle(reg)
200+
func applyADC2Code(reg regI2C, code uint32) {
201+
reg.calibrationInit(1)
202+
reg.calibrationPrepare(1)
203+
reg.setCalibrationParam(1, code)
204+
reg.calibrationFinish(1)
414205
}

0 commit comments

Comments
 (0)