mirror of
https://github.com/tinygo-org/tinygo.git
synced 2026-08-14 07:53:40 +00:00
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>
This commit is contained in:
@@ -5,10 +5,11 @@ package machine
|
|||||||
import (
|
import (
|
||||||
"device/esp"
|
"device/esp"
|
||||||
"errors"
|
"errors"
|
||||||
"runtime/volatile"
|
|
||||||
"unsafe"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// newRegI2C returns the regI2C configured for ESP32-C3: hostID=0, drefInit=1.
|
||||||
|
func newRegI2C() regI2C { return regI2C{hostID: 0, drefInit: 1} }
|
||||||
|
|
||||||
const (
|
const (
|
||||||
// ADC attenuation values for ESP32-C3 APB_SARADC.
|
// ADC attenuation values for ESP32-C3 APB_SARADC.
|
||||||
// 0 dB : ~0 .. 1.1 V
|
// 0 dB : ~0 .. 1.1 V
|
||||||
@@ -34,8 +35,7 @@ func InitADC() {
|
|||||||
esp.APB_SARADC.SetCLKM_CONF_CLKM_DIV_A(0)
|
esp.APB_SARADC.SetCLKM_CONF_CLKM_DIV_A(0)
|
||||||
esp.APB_SARADC.SetCLKM_CONF_CLK_EN(1)
|
esp.APB_SARADC.SetCLKM_CONF_CLK_EN(1)
|
||||||
|
|
||||||
var c adcSelfCalibration
|
adcSelfCalibrate()
|
||||||
c.calibrate()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ESP32-C3: ADC1 = GPIO0–GPIO4 (ch 0–4), ADC2 = GPIO5 (ch 0). ADC2 shares with Wi‑Fi;
|
// ESP32-C3: ADC1 = GPIO0–GPIO4 (ch 0–4), ADC2 = GPIO5 (ch 0). ADC2 shares with Wi‑Fi;
|
||||||
@@ -88,43 +88,43 @@ func (a ADC) Get() uint16 {
|
|||||||
|
|
||||||
// adcSelfCalibration
|
// adcSelfCalibration
|
||||||
const (
|
const (
|
||||||
adcCalTimesC3 = 15
|
adcCalTimesC3 = 15
|
||||||
adcCalOffsetRangeC3 = uint32(4096)
|
adcCalRtcMagicC3 = uint32(0xADC1C401)
|
||||||
adcCalRtcMagicC3 = uint32(0xADC1C401)
|
adcCalInitMinC3 = uint32(1000)
|
||||||
adcCalInitMinC3 = uint32(1000)
|
adcCalInitMaxC3 = uint32(4096)
|
||||||
adcCalInitMaxC3 = uint32(4096)
|
|
||||||
adcGndOffsetCompC3 = uint32(0)
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type adcSelfCalibration struct {
|
// selfCalibrate sets ADC1/ADC2 init code from RTC or runs self-calibration (GND).
|
||||||
digiRefMv uint32
|
|
||||||
}
|
|
||||||
|
|
||||||
// calibrate sets ADC1/ADC2 init code from RTC or runs self-calibration (GND).
|
|
||||||
// eFuse is not used: on ESP32-C3 the ADC calibration fields in BLK2 are often unprogrammed.
|
// eFuse is not used: on ESP32-C3 the ADC calibration fields in BLK2 are often unprogrammed.
|
||||||
func (c *adcSelfCalibration) calibrate() {
|
func adcSelfCalibrate() {
|
||||||
reg := regI2C{}
|
reg := newRegI2C()
|
||||||
reg.sarEnable()
|
reg.sarEnable()
|
||||||
|
|
||||||
var adc1Code uint32
|
var adc1Code uint32
|
||||||
if saved, ok := c.restoreFromRTC(); ok {
|
if saved, ok := restoreFromRTC(); ok {
|
||||||
adc1Code = saved
|
adc1Code = saved
|
||||||
} else {
|
} else {
|
||||||
c.calSetupADC1()
|
calSetupADC1()
|
||||||
reg.adc1CalibrationInit(0)
|
reg.calibrationInit(0)
|
||||||
reg.adc1CalibrationPrepare(0)
|
reg.calibrationPrepare(0)
|
||||||
adc1Code = c.calibrateUnit(reg, 0, c.readADC1)
|
adc1Code = reg.calibrateBinarySearch(0, adcCalTimesC3, readADC1)
|
||||||
c.saveToRTC(adc1Code)
|
if adc1Code < adcCalInitMinC3 {
|
||||||
reg.adc1CalibrationFinish(0)
|
adc1Code = adcCalInitMinC3
|
||||||
|
}
|
||||||
|
if adc1Code > adcCalInitMaxC3 {
|
||||||
|
adc1Code = adcCalInitMaxC3
|
||||||
|
}
|
||||||
|
saveToRTC(adc1Code)
|
||||||
|
reg.calibrationFinish(0)
|
||||||
}
|
}
|
||||||
|
|
||||||
c.applyADC1Code(reg, adc1Code)
|
applyADC1Code(reg, adc1Code)
|
||||||
c.applyADC2Code(reg, adc1Code)
|
applyADC2Code(reg, adc1Code)
|
||||||
}
|
}
|
||||||
|
|
||||||
// calSetupADC1 configures APB_SARADC for oneshot sampling on ADC1 channel 0
|
// calSetupADC1 configures APB_SARADC for oneshot sampling on ADC1 channel 0
|
||||||
// with fixed attenuation. This is used only during self‑calibration.
|
// with fixed attenuation. This is used only during self‑calibration.
|
||||||
func (c *adcSelfCalibration) calSetupADC1() {
|
func calSetupADC1() {
|
||||||
esp.APB_SARADC.SetONETIME_SAMPLE_SARADC_ONETIME_ATTEN(atten11dB)
|
esp.APB_SARADC.SetONETIME_SAMPLE_SARADC_ONETIME_ATTEN(atten11dB)
|
||||||
esp.APB_SARADC.SetONETIME_SAMPLE_SARADC_ONETIME_CHANNEL(0)
|
esp.APB_SARADC.SetONETIME_SAMPLE_SARADC_ONETIME_CHANNEL(0)
|
||||||
esp.APB_SARADC.SetONETIME_SAMPLE_SARADC1_ONETIME_SAMPLE(1)
|
esp.APB_SARADC.SetONETIME_SAMPLE_SARADC1_ONETIME_SAMPLE(1)
|
||||||
@@ -132,7 +132,7 @@ func (c *adcSelfCalibration) calSetupADC1() {
|
|||||||
|
|
||||||
// calSetupADC2 configures APB_SARADC for oneshot sampling on ADC2 (GPIO5, ch 0).
|
// calSetupADC2 configures APB_SARADC for oneshot sampling on ADC2 (GPIO5, ch 0).
|
||||||
// On C3, onetime_channel = (unit<<3)|channel → ADC2 ch0 = 8.
|
// On C3, onetime_channel = (unit<<3)|channel → ADC2 ch0 = 8.
|
||||||
func (c *adcSelfCalibration) calSetupADC2() {
|
func calSetupADC2() {
|
||||||
esp.APB_SARADC.SetONETIME_SAMPLE_SARADC_ONETIME_ATTEN(atten11dB)
|
esp.APB_SARADC.SetONETIME_SAMPLE_SARADC_ONETIME_ATTEN(atten11dB)
|
||||||
esp.APB_SARADC.SetONETIME_SAMPLE_SARADC_ONETIME_CHANNEL(8) // (1<<3)|0 for ADC2
|
esp.APB_SARADC.SetONETIME_SAMPLE_SARADC_ONETIME_CHANNEL(8) // (1<<3)|0 for ADC2
|
||||||
esp.APB_SARADC.SetARB_CTRL_ADC_ARB_APB_FORCE(1)
|
esp.APB_SARADC.SetARB_CTRL_ADC_ARB_APB_FORCE(1)
|
||||||
@@ -142,7 +142,7 @@ func (c *adcSelfCalibration) calSetupADC2() {
|
|||||||
|
|
||||||
// readADC1 performs a single ADC1 conversion using the APB_SARADC
|
// readADC1 performs a single ADC1 conversion using the APB_SARADC
|
||||||
// oneshot path and returns the raw 12‑bit result (0..4095).
|
// oneshot path and returns the raw 12‑bit result (0..4095).
|
||||||
func (c *adcSelfCalibration) readADC1() uint32 {
|
func readADC1() uint32 {
|
||||||
esp.APB_SARADC.SetINT_CLR_APB_SARADC1_DONE_INT_CLR(1)
|
esp.APB_SARADC.SetINT_CLR_APB_SARADC1_DONE_INT_CLR(1)
|
||||||
esp.APB_SARADC.SetONETIME_SAMPLE_SARADC_ONETIME_START(0)
|
esp.APB_SARADC.SetONETIME_SAMPLE_SARADC_ONETIME_START(0)
|
||||||
esp.APB_SARADC.SetONETIME_SAMPLE_SARADC_ONETIME_START(1)
|
esp.APB_SARADC.SetONETIME_SAMPLE_SARADC_ONETIME_START(1)
|
||||||
@@ -154,7 +154,7 @@ func (c *adcSelfCalibration) readADC1() uint32 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// readADC2 performs a single ADC2 conversion and returns the raw 12‑bit result (0..4095).
|
// readADC2 performs a single ADC2 conversion and returns the raw 12‑bit result (0..4095).
|
||||||
func (c *adcSelfCalibration) readADC2() uint32 {
|
func readADC2() uint32 {
|
||||||
esp.APB_SARADC.SetINT_CLR_APB_SARADC2_DONE_INT_CLR(1)
|
esp.APB_SARADC.SetINT_CLR_APB_SARADC2_DONE_INT_CLR(1)
|
||||||
esp.APB_SARADC.SetONETIME_SAMPLE_SARADC_ONETIME_START(0)
|
esp.APB_SARADC.SetONETIME_SAMPLE_SARADC_ONETIME_START(0)
|
||||||
esp.APB_SARADC.SetONETIME_SAMPLE_SARADC_ONETIME_START(1)
|
esp.APB_SARADC.SetONETIME_SAMPLE_SARADC_ONETIME_START(1)
|
||||||
@@ -167,7 +167,7 @@ func (c *adcSelfCalibration) readADC2() uint32 {
|
|||||||
return uint32(raw)
|
return uint32(raw)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *adcSelfCalibration) restoreFromRTC() (uint32, bool) {
|
func restoreFromRTC() (uint32, bool) {
|
||||||
if esp.RTC_CNTL.GetSTORE0() != adcCalRtcMagicC3 {
|
if esp.RTC_CNTL.GetSTORE0() != adcCalRtcMagicC3 {
|
||||||
return 0, false
|
return 0, false
|
||||||
}
|
}
|
||||||
@@ -178,7 +178,7 @@ func (c *adcSelfCalibration) restoreFromRTC() (uint32, bool) {
|
|||||||
return code, true
|
return code, true
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *adcSelfCalibration) saveToRTC(code uint32) {
|
func saveToRTC(code uint32) {
|
||||||
if code < adcCalInitMinC3 || code > adcCalInitMaxC3 {
|
if code < adcCalInitMinC3 || code > adcCalInitMaxC3 {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -187,228 +187,19 @@ func (c *adcSelfCalibration) saveToRTC(code uint32) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// applyADC1Code sets ADC1 init code and finishes calibration.
|
// applyADC1Code sets ADC1 init code and finishes calibration.
|
||||||
func (c *adcSelfCalibration) applyADC1Code(reg regI2C, code uint32) {
|
func applyADC1Code(reg regI2C, code uint32) {
|
||||||
c.calSetupADC1()
|
calSetupADC1()
|
||||||
reg.adc1CalibrationInit(0)
|
reg.calibrationInit(0)
|
||||||
reg.adc1CalibrationPrepare(0)
|
reg.calibrationPrepare(0)
|
||||||
reg.adc1SetCalibrationParam(0, code)
|
reg.setCalibrationParam(0, code)
|
||||||
reg.adc1CalibrationFinish(0)
|
reg.calibrationFinish(0)
|
||||||
}
|
}
|
||||||
|
|
||||||
// applyADC2Code sets ADC2 init code and finishes calibration. On C3 eFuse V1
|
// applyADC2Code sets ADC2 init code and finishes calibration. On C3 eFuse V1
|
||||||
// there is no separate ADC2 calibration; IDF uses ADC1 init code for both units.
|
// there is no separate ADC2 calibration; IDF uses ADC1 init code for both units.
|
||||||
func (c *adcSelfCalibration) applyADC2Code(reg regI2C, code uint32) {
|
func applyADC2Code(reg regI2C, code uint32) {
|
||||||
reg.adc1CalibrationInit(1)
|
reg.calibrationInit(1)
|
||||||
reg.adc1CalibrationPrepare(1)
|
reg.calibrationPrepare(1)
|
||||||
reg.adc1SetCalibrationParam(1, code)
|
reg.setCalibrationParam(1, code)
|
||||||
reg.adc1CalibrationFinish(1)
|
reg.calibrationFinish(1)
|
||||||
}
|
|
||||||
|
|
||||||
func (c *adcSelfCalibration) calibrateUnit(reg regI2C, adcN uint8, readADC func() uint32) uint32 {
|
|
||||||
var codeList [adcCalTimesC3]uint32
|
|
||||||
var codeSum uint32
|
|
||||||
|
|
||||||
for rpt := 0; rpt < adcCalTimesC3; rpt++ {
|
|
||||||
codeH := adcCalOffsetRangeC3
|
|
||||||
codeL := uint32(0)
|
|
||||||
chkCode := (codeH + codeL) / 2
|
|
||||||
reg.adc1SetCalibrationParam(adcN, chkCode)
|
|
||||||
selfCal := readADC()
|
|
||||||
|
|
||||||
for codeH-codeL > 1 {
|
|
||||||
if selfCal == 0 {
|
|
||||||
codeH = chkCode
|
|
||||||
} else {
|
|
||||||
codeL = chkCode
|
|
||||||
}
|
|
||||||
chkCode = (codeH + codeL) / 2
|
|
||||||
reg.adc1SetCalibrationParam(adcN, chkCode)
|
|
||||||
selfCal = readADC()
|
|
||||||
if codeH-codeL == 1 {
|
|
||||||
chkCode++
|
|
||||||
reg.adc1SetCalibrationParam(adcN, chkCode)
|
|
||||||
selfCal = readADC()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
codeList[rpt] = chkCode
|
|
||||||
codeSum += chkCode
|
|
||||||
}
|
|
||||||
|
|
||||||
codeL := codeList[0]
|
|
||||||
codeH := codeList[0]
|
|
||||||
for i := 0; i < adcCalTimesC3; i++ {
|
|
||||||
if codeList[i] < codeL {
|
|
||||||
codeL = codeList[i]
|
|
||||||
}
|
|
||||||
if codeList[i] > codeH {
|
|
||||||
codeH = codeList[i]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
excluded := codeH + codeL
|
|
||||||
remaining := codeSum - excluded
|
|
||||||
finalCode := remaining / (adcCalTimesC3 - 2)
|
|
||||||
if remaining%(adcCalTimesC3-2) >= 4 {
|
|
||||||
finalCode++
|
|
||||||
}
|
|
||||||
if finalCode < adcCalInitMinC3 {
|
|
||||||
finalCode = adcCalInitMinC3
|
|
||||||
}
|
|
||||||
if finalCode > adcCalInitMaxC3 {
|
|
||||||
finalCode = adcCalInitMaxC3
|
|
||||||
}
|
|
||||||
|
|
||||||
reg.adc1SetCalibrationParam(adcN, finalCode)
|
|
||||||
return finalCode
|
|
||||||
}
|
|
||||||
|
|
||||||
// regi2c
|
|
||||||
|
|
||||||
// regI2C on ESP32‑C3 exposes the internal analog I2C bus that controls
|
|
||||||
// SAR ADC trim registers. Constants below mirror the layout from
|
|
||||||
// ESP‑IDF's soc/regi2c_saradc.h and TRM (I2C_RTC_CONFIG2 block).
|
|
||||||
const (
|
|
||||||
// i2cSarADC/i2cSarADCHostID select the SAR ADC block on the internal bus.
|
|
||||||
i2cSarADC = uint8(0x69)
|
|
||||||
i2cSarADCHostID = uint8(0)
|
|
||||||
|
|
||||||
// adc*_Dref* define the DREF (reference) bitfields for ADC1/ADC2.
|
|
||||||
adc1DrefAddr = uint8(0x2)
|
|
||||||
adc1DrefMSB = uint8(6)
|
|
||||||
adc1DrefLSB = uint8(4)
|
|
||||||
|
|
||||||
adc2DrefAddr = uint8(0x5)
|
|
||||||
adc2DrefMSB = uint8(6)
|
|
||||||
adc2DrefLSB = uint8(4)
|
|
||||||
|
|
||||||
// adc*_EncalGnd* control ENCAL_GND: route internal ground to ADC input
|
|
||||||
// during self‑calibration so that the pin is effectively disconnected.
|
|
||||||
adc1EncalGndAddr = uint8(0x7)
|
|
||||||
adc1EncalGndMSB = uint8(5)
|
|
||||||
adc1EncalGndLSB = uint8(5)
|
|
||||||
|
|
||||||
adc2EncalGndAddr = uint8(0x7)
|
|
||||||
adc2EncalGndMSB = uint8(7)
|
|
||||||
adc2EncalGndLSB = uint8(7)
|
|
||||||
|
|
||||||
// adc*_InitCode* hold the INIT_CODE (offset) that hardware uses to
|
|
||||||
// compensate ADC1/ADC2 offset error.
|
|
||||||
adc1InitCodeHighAddr = uint8(0x1)
|
|
||||||
adc1InitCodeHighMSB = uint8(3)
|
|
||||||
adc1InitCodeHighLSB = uint8(0)
|
|
||||||
adc1InitCodeLowAddr = uint8(0x0)
|
|
||||||
adc1InitCodeLowMSB = uint8(7)
|
|
||||||
adc1InitCodeLowLSB = uint8(0)
|
|
||||||
|
|
||||||
adc2InitCodeHighAddr = uint8(0x4)
|
|
||||||
adc2InitCodeHighMSB = uint8(3)
|
|
||||||
adc2InitCodeHighLSB = uint8(0)
|
|
||||||
adc2InitCodeLowAddr = uint8(0x3)
|
|
||||||
adc2InitCodeLowMSB = uint8(7)
|
|
||||||
adc2InitCodeLowLSB = uint8(0)
|
|
||||||
|
|
||||||
// ANA_CONFIG/ANA_CONFIG2: enable analog SAR I2C domain before regI2C access.
|
|
||||||
anaConfigReg = uintptr(0x6000E044)
|
|
||||||
i2cSarEnMask = uint32(1 << 18)
|
|
||||||
anaConfig2Reg = uintptr(0x6000E048)
|
|
||||||
anaSarCfg2En = uint32(1 << 16)
|
|
||||||
|
|
||||||
// I2C_RTC_CONFIG2 master control register used by regI2C operations.
|
|
||||||
i2cMstCtrlHost = uintptr(0x6000E000)
|
|
||||||
i2cMstBusyBit = uint32(1 << 25)
|
|
||||||
i2cMstWrCntl = uint32(1 << 24)
|
|
||||||
i2cMstDataMask = uint32(0xFF << 16)
|
|
||||||
i2cMstDataShift = 16
|
|
||||||
i2cMstTimeout = 10000
|
|
||||||
)
|
|
||||||
|
|
||||||
type regI2C struct{}
|
|
||||||
|
|
||||||
// sarEnable enables the SAR analog I2C domain before any regI2C access.
|
|
||||||
func (r *regI2C) sarEnable() {
|
|
||||||
cfg := (*volatile.Register32)(unsafe.Pointer(anaConfigReg))
|
|
||||||
cfg2 := (*volatile.Register32)(unsafe.Pointer(anaConfig2Reg))
|
|
||||||
esp.RTC_CNTL.SetANA_CONF_SAR_I2C_PU(1)
|
|
||||||
cfg.Set(cfg.Get() &^ i2cSarEnMask)
|
|
||||||
cfg2.Set(cfg2.Get() | anaSarCfg2En)
|
|
||||||
}
|
|
||||||
|
|
||||||
// adc1CalibrationInit sets DREF for the selected ADC unit
|
|
||||||
// before running the self‑calibration procedure.
|
|
||||||
func (r *regI2C) adc1CalibrationInit(adcN uint8) {
|
|
||||||
if adcN == 0 {
|
|
||||||
r.writeMask(i2cSarADC, i2cSarADCHostID, adc1DrefAddr, adc1DrefMSB, adc1DrefLSB, 1)
|
|
||||||
} else {
|
|
||||||
r.writeMask(i2cSarADC, i2cSarADCHostID, adc2DrefAddr, adc2DrefMSB, adc2DrefLSB, 1)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// adc1CalibrationPrepare enables ENCAL_GND so that the ADC input
|
|
||||||
// is internally shorted to ground during self‑calibration.
|
|
||||||
func (r *regI2C) adc1CalibrationPrepare(adcN uint8) {
|
|
||||||
if adcN == 0 {
|
|
||||||
r.writeMask(i2cSarADC, i2cSarADCHostID, adc1EncalGndAddr, adc1EncalGndMSB, adc1EncalGndLSB, 1)
|
|
||||||
} else {
|
|
||||||
r.writeMask(i2cSarADC, i2cSarADCHostID, adc2EncalGndAddr, adc2EncalGndMSB, adc2EncalGndLSB, 1)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// adc1CalibrationFinish clears ENCAL_GND and reconnects the ADC
|
|
||||||
// input back to the external pad after self‑calibration.
|
|
||||||
func (r *regI2C) adc1CalibrationFinish(adcN uint8) {
|
|
||||||
if adcN == 0 {
|
|
||||||
r.writeMask(i2cSarADC, i2cSarADCHostID, adc1EncalGndAddr, adc1EncalGndMSB, adc1EncalGndLSB, 0)
|
|
||||||
} else {
|
|
||||||
r.writeMask(i2cSarADC, i2cSarADCHostID, adc2EncalGndAddr, adc2EncalGndMSB, adc2EncalGndLSB, 0)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// adc1SetCalibrationParam writes the INIT_CODE (offset trim) for
|
|
||||||
// the selected ADC unit using the regI2C bitfields.
|
|
||||||
func (r *regI2C) adc1SetCalibrationParam(adcN uint8, param uint32) {
|
|
||||||
msb := uint8(param >> 8)
|
|
||||||
lsb := uint8(param & 0xFF)
|
|
||||||
if adcN == 0 {
|
|
||||||
r.writeMask(i2cSarADC, i2cSarADCHostID, adc1InitCodeHighAddr, adc1InitCodeHighMSB, adc1InitCodeHighLSB, msb)
|
|
||||||
r.writeMask(i2cSarADC, i2cSarADCHostID, adc1InitCodeLowAddr, adc1InitCodeLowMSB, adc1InitCodeLowLSB, lsb)
|
|
||||||
} else {
|
|
||||||
r.writeMask(i2cSarADC, i2cSarADCHostID, adc2InitCodeHighAddr, adc2InitCodeHighMSB, adc2InitCodeHighLSB, msb)
|
|
||||||
r.writeMask(i2cSarADC, i2cSarADCHostID, adc2InitCodeLowAddr, adc2InitCodeLowMSB, adc2InitCodeLowLSB, lsb)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// waitIdle polls the REGI2C master BUSY bit until it clears or the
|
|
||||||
// simple software timeout expires. This matches the busy‑wait helper
|
|
||||||
// used in ESP‑IDF's regi2c_ctrl.c.
|
|
||||||
func (r *regI2C) waitIdle(reg *volatile.Register32) bool {
|
|
||||||
for i := 0; i < i2cMstTimeout; i++ {
|
|
||||||
if reg.Get()&i2cMstBusyBit == 0 {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// writeMask is a software implementation of REGI2C_WRITE_MASK macro:
|
|
||||||
// 1. select block + regAddr,
|
|
||||||
// 2. read current byte,
|
|
||||||
// 3. update only [msb:lsb] bitfield,
|
|
||||||
// 4. write it back via internal I2C master.
|
|
||||||
func (r *regI2C) writeMask(block, hostID, regAddr, msb, lsb, data uint8) {
|
|
||||||
if hostID != i2cSarADCHostID {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
reg := (*volatile.Register32)(unsafe.Pointer(i2cMstCtrlHost))
|
|
||||||
if !r.waitIdle(reg) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
reg.Set(uint32(block) | uint32(regAddr)<<8)
|
|
||||||
if !r.waitIdle(reg) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
cur := (reg.Get() & i2cMstDataMask) >> i2cMstDataShift
|
|
||||||
mask := uint32(1<<(msb-lsb+1)-1) << lsb
|
|
||||||
cur &^= mask
|
|
||||||
cur |= uint32(data&(1<<(msb-lsb+1)-1)) << lsb
|
|
||||||
reg.Set(uint32(block) | uint32(regAddr)<<8 | i2cMstWrCntl | (cur<<i2cMstDataShift)&i2cMstDataMask)
|
|
||||||
r.waitIdle(reg)
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,6 +25,9 @@ import (
|
|||||||
"unsafe"
|
"unsafe"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// newRegI2C returns the regI2C configured for ESP32-S3: hostID=1, drefInit=4.
|
||||||
|
func newRegI2C() regI2C { return regI2C{hostID: 1, drefInit: 4} }
|
||||||
|
|
||||||
var adcDigiRefMv uint32
|
var adcDigiRefMv uint32
|
||||||
|
|
||||||
func InitADC() {
|
func InitADC() {
|
||||||
@@ -85,9 +88,8 @@ func InitADC() {
|
|||||||
esp.APB_SARADC.SetFILTER_CTRL1_FILTER_FACTOR0(0)
|
esp.APB_SARADC.SetFILTER_CTRL1_FILTER_FACTOR0(0)
|
||||||
esp.APB_SARADC.SetFILTER_CTRL1_FILTER_FACTOR1(0)
|
esp.APB_SARADC.SetFILTER_CTRL1_FILTER_FACTOR1(0)
|
||||||
|
|
||||||
adcCal := adcCalibration{}
|
adcSelfCalibrate()
|
||||||
adcCal.calibrate()
|
adcDigiRefMv = getDigiRef()
|
||||||
adcDigiRefMv = adcCal.getDigiRef()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@@ -115,7 +117,6 @@ func (a ADC) Configure(config ADCConfig) error {
|
|||||||
return errors.New("invalid ADC pin for ESP32-S3")
|
return errors.New("invalid ADC pin for ESP32-S3")
|
||||||
}
|
}
|
||||||
a.Pin.Configure(PinConfig{Mode: PinAnalog})
|
a.Pin.Configure(PinConfig{Mode: PinAnalog})
|
||||||
InitADC()
|
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -189,11 +190,11 @@ func (a ADC) GetVoltage() (raw uint32, v float64) {
|
|||||||
//
|
//
|
||||||
// Mapping to ESP-IDF (adc_hal_common.c, hal/esp32s3/adc_ll.h):
|
// Mapping to ESP-IDF (adc_hal_common.c, hal/esp32s3/adc_ll.h):
|
||||||
// - adc_hal_self_calibration() → ADCSelfCalibrate()
|
// - adc_hal_self_calibration() → ADCSelfCalibrate()
|
||||||
// - adc_ll_calibration_init() → regI2C.ADC1CalibrationInit (DREF=4);
|
// - adc_ll_calibration_init() → regI2C.calibrationInit (DREF=4);
|
||||||
// in IDF it is not called from self_cal, we call it explicitly.
|
// in IDF it is not called from self_cal, we call it explicitly.
|
||||||
// - adc_ll_calibration_prepare() → SarEnable + ADC1CalibrationPrepare (ENCAL_GND=1)
|
// - adc_ll_calibration_prepare() → SarEnable + calibrationPrepare (ENCAL_GND=1)
|
||||||
// - adc_ll_calibration_finish() → ADC1CalibrationFinish (ENCAL_GND=0)
|
// - adc_ll_calibration_finish() → calibrationFinish (ENCAL_GND=0)
|
||||||
// - adc_ll_set_calibration_param() → ADC1SetCalibrationParam()
|
// - adc_ll_set_calibration_param() → setCalibrationParam()
|
||||||
// - read_cal_channel() → adcCalibration.readADC1():
|
// - read_cal_channel() → adcCalibration.readADC1():
|
||||||
// wait for meas_status==0, start 0→1, wait done, read data
|
// wait for meas_status==0, start 0→1, wait done, read data
|
||||||
// (similar to adc_oneshot_ll_start + get_raw_result).
|
// (similar to adc_oneshot_ll_start + get_raw_result).
|
||||||
@@ -209,7 +210,6 @@ func (a ADC) GetVoltage() (raw uint32, v float64) {
|
|||||||
|
|
||||||
const (
|
const (
|
||||||
adcCalTimes = 10
|
adcCalTimes = 10
|
||||||
adcCalOffsetMax = uint32(4096)
|
|
||||||
adcCalRtcMagic = uint32(0xADC1C401)
|
adcCalRtcMagic = uint32(0xADC1C401)
|
||||||
adcCalInitMin = uint32(2000)
|
adcCalInitMin = uint32(2000)
|
||||||
adcCalInitMax = uint32(3900)
|
adcCalInitMax = uint32(3900)
|
||||||
@@ -220,44 +220,40 @@ const (
|
|||||||
// adcCalibration encapsulates the self-calibration flow for ADC1
|
// adcCalibration encapsulates the self-calibration flow for ADC1
|
||||||
// and remembers per-chip calibration data (such as DIGI_REF) when it is
|
// and remembers per-chip calibration data (such as DIGI_REF) when it is
|
||||||
// available from eFuse.
|
// available from eFuse.
|
||||||
type adcCalibration struct {
|
func adcSelfCalibrate() {
|
||||||
digiRefMv uint32
|
reg := newRegI2C()
|
||||||
}
|
|
||||||
|
|
||||||
func (c *adcCalibration) calibrate() {
|
|
||||||
reg := regI2C{}
|
|
||||||
f := fuse{}
|
f := fuse{}
|
||||||
|
|
||||||
if vref, ok := f.adc1DigiRefAtten3(); ok {
|
if vref, ok := f.adc1DigiRefAtten3(); ok {
|
||||||
c.digiRefMv = vref
|
adcDigiRefMv = vref
|
||||||
}
|
}
|
||||||
|
|
||||||
if saved, ok := c.restoreFromRTC(); ok {
|
if saved, ok := restoreFromRTC(); ok {
|
||||||
reg.sarEnable()
|
reg.sarEnable()
|
||||||
reg.adc1CalibrationInit(0)
|
reg.calibrationInit(0)
|
||||||
c.adc1CalibrateHigh(reg, saved)
|
adc1CalibrateHigh(reg, saved)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
initCode, useEfuse := f.adc1InitCodeAtten3()
|
initCode, useEfuse := f.adc1InitCodeAtten3()
|
||||||
c.adc1CalibrationSetup(reg)
|
adc1CalibrationSetup(reg)
|
||||||
|
|
||||||
if useEfuse {
|
if useEfuse {
|
||||||
c.saveToRTC(initCode)
|
saveToRTC(initCode)
|
||||||
c.adc1CalibrateHigh(reg, initCode)
|
adc1CalibrateHigh(reg, initCode)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
finalCode := c.adc1CalibrateLow(reg)
|
finalCode := reg.calibrateBinarySearch(0, adcCalTimes, readADC1)
|
||||||
c.saveToRTC(finalCode)
|
saveToRTC(finalCode)
|
||||||
c.adc1CalibrateHigh(reg, finalCode)
|
adc1CalibrateHigh(reg, finalCode)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *adcCalibration) getDigiRef() uint32 {
|
func getDigiRef() uint32 {
|
||||||
return c.digiRefMv
|
return adcDigiRefMv
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *adcCalibration) adc1CalibrationSetup(reg regI2C) {
|
func adc1CalibrationSetup(reg regI2C) {
|
||||||
reg.sarEnable()
|
reg.sarEnable()
|
||||||
|
|
||||||
esp.SENS.SetSAR_MEAS1_MUX_SAR1_DIG_FORCE(0)
|
esp.SENS.SetSAR_MEAS1_MUX_SAR1_DIG_FORCE(0)
|
||||||
@@ -268,67 +264,17 @@ func (c *adcCalibration) adc1CalibrationSetup(reg regI2C) {
|
|||||||
esp.SENS.SetSAR_MEAS1_CTRL2_MEAS1_START_FORCE(1)
|
esp.SENS.SetSAR_MEAS1_CTRL2_MEAS1_START_FORCE(1)
|
||||||
esp.SENS.SetSAR_MEAS1_CTRL2_SAR1_EN_PAD_FORCE(1)
|
esp.SENS.SetSAR_MEAS1_CTRL2_SAR1_EN_PAD_FORCE(1)
|
||||||
|
|
||||||
reg.adc1CalibrationInit(0)
|
reg.calibrationInit(0)
|
||||||
reg.adc1CalibrationPrepare(0)
|
reg.calibrationPrepare(0)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *adcCalibration) adc1CalibrateLow(reg regI2C) uint32 {
|
func adc1CalibrateHigh(reg regI2C, code uint32) {
|
||||||
var codeList [adcCalTimes]uint32
|
reg.setCalibrationParam(0, code)
|
||||||
var codeSum uint32
|
reg.calibrationFinish(0)
|
||||||
|
adc1StartWithPadForce()
|
||||||
for rpt := 0; rpt < adcCalTimes; rpt++ {
|
|
||||||
codeH := adcCalOffsetMax
|
|
||||||
codeL := uint32(0)
|
|
||||||
chkCode := (codeH + codeL) / 2
|
|
||||||
reg.adc1SetCalibrationParam(0, chkCode)
|
|
||||||
selfCal := c.readADC1()
|
|
||||||
|
|
||||||
for codeH-codeL > 1 {
|
|
||||||
if selfCal == 0 {
|
|
||||||
codeH = chkCode
|
|
||||||
} else {
|
|
||||||
codeL = chkCode
|
|
||||||
}
|
|
||||||
chkCode = (codeH + codeL) / 2
|
|
||||||
reg.adc1SetCalibrationParam(0, chkCode)
|
|
||||||
selfCal = c.readADC1()
|
|
||||||
if codeH-codeL == 1 {
|
|
||||||
chkCode++
|
|
||||||
reg.adc1SetCalibrationParam(0, chkCode)
|
|
||||||
selfCal = c.readADC1()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
codeList[rpt] = chkCode
|
|
||||||
codeSum += chkCode
|
|
||||||
}
|
|
||||||
|
|
||||||
codeL := codeList[0]
|
|
||||||
codeH := codeList[0]
|
|
||||||
for i := 0; i < adcCalTimes; i++ {
|
|
||||||
if codeList[i] < codeL {
|
|
||||||
codeL = codeList[i]
|
|
||||||
}
|
|
||||||
if codeList[i] > codeH {
|
|
||||||
codeH = codeList[i]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
excluded := codeH + codeL
|
|
||||||
remaining := codeSum - excluded
|
|
||||||
finalCode := remaining / (adcCalTimes - 2)
|
|
||||||
if remaining%(adcCalTimes-2) >= 4 {
|
|
||||||
finalCode++
|
|
||||||
}
|
|
||||||
|
|
||||||
return finalCode
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *adcCalibration) adc1CalibrateHigh(reg regI2C, code uint32) {
|
func adc1StartWithPadForce() {
|
||||||
reg.adc1SetCalibrationParam(0, code)
|
|
||||||
reg.adc1CalibrationFinish(0)
|
|
||||||
c.adc1StartWithPadForce()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *adcCalibration) adc1StartWithPadForce() {
|
|
||||||
esp.SENS.SetSAR_MEAS1_CTRL2_SAR1_EN_PAD_FORCE(1)
|
esp.SENS.SetSAR_MEAS1_CTRL2_SAR1_EN_PAD_FORCE(1)
|
||||||
esp.SENS.SetSAR_MEAS1_CTRL2_MEAS1_START_FORCE(1)
|
esp.SENS.SetSAR_MEAS1_CTRL2_MEAS1_START_FORCE(1)
|
||||||
}
|
}
|
||||||
@@ -336,7 +282,7 @@ func (c *adcCalibration) adc1StartWithPadForce() {
|
|||||||
// readADC1 performs one ADC1 conversion via RTC path (used during calibration).
|
// readADC1 performs one ADC1 conversion via RTC path (used during calibration).
|
||||||
// Internal GND is connected via ENCAL_GND, so the pin input is disconnected.
|
// Internal GND is connected via ENCAL_GND, so the pin input is disconnected.
|
||||||
// Matches IDF: wait conversion idle (meas_status==0), then start 0→1, wait done, read data.
|
// Matches IDF: wait conversion idle (meas_status==0), then start 0→1, wait done, read data.
|
||||||
func (c *adcCalibration) readADC1() uint32 {
|
func readADC1() uint32 {
|
||||||
for esp.SENS.GetSAR_SLAVE_ADDR1_SAR_SARADC_MEAS_STATUS() != 0 {
|
for esp.SENS.GetSAR_SLAVE_ADDR1_SAR_SARADC_MEAS_STATUS() != 0 {
|
||||||
}
|
}
|
||||||
esp.SENS.SetSAR_MEAS1_CTRL2_MEAS1_START_SAR(0)
|
esp.SENS.SetSAR_MEAS1_CTRL2_MEAS1_START_SAR(0)
|
||||||
@@ -346,7 +292,7 @@ func (c *adcCalibration) readADC1() uint32 {
|
|||||||
return uint32(esp.SENS.GetSAR_MEAS1_CTRL2_MEAS1_DATA_SAR() & 0xfff)
|
return uint32(esp.SENS.GetSAR_MEAS1_CTRL2_MEAS1_DATA_SAR() & 0xfff)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *adcCalibration) restoreFromRTC() (uint32, bool) {
|
func restoreFromRTC() (uint32, bool) {
|
||||||
if esp.RTC_CNTL.GetSTORE0() != adcCalRtcMagic {
|
if esp.RTC_CNTL.GetSTORE0() != adcCalRtcMagic {
|
||||||
return 0, false
|
return 0, false
|
||||||
}
|
}
|
||||||
@@ -357,169 +303,11 @@ func (c *adcCalibration) restoreFromRTC() (uint32, bool) {
|
|||||||
return code, true
|
return code, true
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *adcCalibration) saveToRTC(code uint32) {
|
func saveToRTC(code uint32) {
|
||||||
esp.RTC_CNTL.SetSTORE0(adcCalRtcMagic)
|
esp.RTC_CNTL.SetSTORE0(adcCalRtcMagic)
|
||||||
esp.RTC_CNTL.SetSTORE1(code)
|
esp.RTC_CNTL.SetSTORE1(code)
|
||||||
}
|
}
|
||||||
|
|
||||||
// regI2C — internal I2C for SAR ADC (ESP32-S2 I2C_RTC_CONFIG2, reg 0x6000E000).
|
|
||||||
// Source: idf-source/components/soc/esp32s3/include/soc/regi2c_saradc.h
|
|
||||||
|
|
||||||
const (
|
|
||||||
// I2C_SAR_ADC / I2C_SAR_ADC_HOSTID in regi2c_saradc.h
|
|
||||||
i2cSarADC = uint8(0x69) // I2C_SAR_ADC
|
|
||||||
i2cSarADCHostID = uint8(1) // I2C_SAR_ADC_HOSTID
|
|
||||||
|
|
||||||
// ADC_SAR1_DREF_ADDR(_MSB/_LSB)
|
|
||||||
adc1DrefAddr = uint8(0x2) // ADC_SAR1_DREF_ADDR
|
|
||||||
adc1DrefMSB = uint8(6) // ADC_SAR1_DREF_ADDR_MSB
|
|
||||||
adc1DrefLSB = uint8(4) // ADC_SAR1_DREF_ADDR_LSB
|
|
||||||
|
|
||||||
// ADC_SAR2_DREF_ADDR(_MSB/_LSB)
|
|
||||||
adc2DrefAddr = uint8(0x5) // ADC_SAR2_DREF_ADDR
|
|
||||||
adc2DrefMSB = uint8(6) // ADC_SAR2_DREF_ADDR_MSB
|
|
||||||
adc2DrefLSB = uint8(4) // ADC_SAR2_DREF_ADDR_LSB
|
|
||||||
|
|
||||||
// ADC_SAR1_ENCAL_GND_ADDR(_MSB/_LSB)
|
|
||||||
adc1EncalGndAddr = uint8(0x7) // ADC_SAR1_ENCAL_GND_ADDR
|
|
||||||
adc1EncalGndMSB = uint8(5) // ADC_SAR1_ENCAL_GND_ADDR_MSB
|
|
||||||
adc1EncalGndLSB = uint8(5) // ADC_SAR1_ENCAL_GND_ADDR_LSB
|
|
||||||
|
|
||||||
// ADC_SAR2_ENCAL_GND_ADDR(_MSB/_LSB)
|
|
||||||
adc2EncalGndAddr = uint8(0x7) // ADC_SAR2_ENCAL_GND_ADDR
|
|
||||||
adc2EncalGndMSB = uint8(7) // ADC_SAR2_ENCAL_GND_ADDR_MSB
|
|
||||||
adc2EncalGndLSB = uint8(7) // ADC_SAR2_ENCAL_GND_ADDR_LSB
|
|
||||||
|
|
||||||
// ADC_SAR1_INITIAL_CODE_HIGH/LOW_ADDR(_MSB/_LSB)
|
|
||||||
adc1InitCodeHighAddr = uint8(0x1) // ADC_SAR1_INITIAL_CODE_HIGH_ADDR
|
|
||||||
adc1InitCodeHighMSB = uint8(3) // ADC_SAR1_INITIAL_CODE_HIGH_ADDR_MSB
|
|
||||||
adc1InitCodeHighLSB = uint8(0) // ADC_SAR1_INITIAL_CODE_HIGH_ADDR_LSB
|
|
||||||
adc1InitCodeLowAddr = uint8(0x0) // ADC_SAR1_INITIAL_CODE_LOW_ADDR
|
|
||||||
adc1InitCodeLowMSB = uint8(7) // ADC_SAR1_INITIAL_CODE_LOW_ADDR_MSB
|
|
||||||
adc1InitCodeLowLSB = uint8(0) // ADC_SAR1_INITIAL_CODE_LOW_ADDR_LSB
|
|
||||||
|
|
||||||
// ADC_SAR2_INITIAL_CODE_HIGH/LOW_ADDR(_MSB/_LSB)
|
|
||||||
adc2InitCodeHighAddr = uint8(0x4) // ADC_SAR2_INITIAL_CODE_HIGH_ADDR
|
|
||||||
adc2InitCodeHighMSB = uint8(3) // ADC_SAR2_INITIAL_CODE_HIGH_ADDR_MSB
|
|
||||||
adc2InitCodeHighLSB = uint8(0) // ADC_SAR2_INITIAL_CODE_HIGH_ADDR_LSB
|
|
||||||
adc2InitCodeLowAddr = uint8(0x3) // ADC_SAR2_INITIAL_CODE_LOW_ADDR
|
|
||||||
adc2InitCodeLowMSB = uint8(7) // ADC_SAR2_INITIAL_CODE_LOW_ADDR_MSB
|
|
||||||
adc2InitCodeLowLSB = uint8(0) // ADC_SAR2_INITIAL_CODE_LOW_ADDR_LSB
|
|
||||||
|
|
||||||
// Analog config registers for regI2C block (RTC/ANA config in TRM).
|
|
||||||
anaConfigReg = uintptr(0x6000E044)
|
|
||||||
i2cSarEnMask = uint32(1 << 18)
|
|
||||||
anaConfig2Reg = uintptr(0x6000E048)
|
|
||||||
anaSarCfg2En = uint32(1 << 16)
|
|
||||||
|
|
||||||
// REGI2C master control register and helper masks.
|
|
||||||
i2cMstCtrlHost1 = uintptr(0x6000E000)
|
|
||||||
i2cMstBusyBit = uint32(1 << 25)
|
|
||||||
i2cMstWrCntlBit = uint32(1 << 24)
|
|
||||||
i2cMstDataMask = uint32(0xFF << 16)
|
|
||||||
i2cMstDataShift = 16
|
|
||||||
i2cMstBusyTimeout = 10000
|
|
||||||
)
|
|
||||||
|
|
||||||
type regI2C struct{}
|
|
||||||
|
|
||||||
// waitIdle mimics the IDF regi2c busy-wait helper (see regi2c_ctrl.c).
|
|
||||||
// It polls the REGI2C master control register until the BUSY bit clears
|
|
||||||
// or a small timeout expires, to avoid writing while a previous transfer
|
|
||||||
// is still in progress.
|
|
||||||
func (r *regI2C) waitIdle(reg *volatile.Register32) bool {
|
|
||||||
for i := 0; i < i2cMstBusyTimeout; i++ {
|
|
||||||
if reg.Get()&i2cMstBusyBit == 0 {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// writeMask is a software implementation of the REGI2C_WRITE_MASK macro
|
|
||||||
// from IDF (see soc/regi2c_saradc.h). It:
|
|
||||||
// - selects the regI2C SAR ADC block + register address,
|
|
||||||
// - reads the current byte,
|
|
||||||
// - updates only the [msb:lsb] bitfield,
|
|
||||||
// - writes the new value back via the internal I2C master.
|
|
||||||
func (r *regI2C) writeMask(block, hostID, regAddr, msb, lsb, data uint8) {
|
|
||||||
if hostID != i2cSarADCHostID {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
reg := (*volatile.Register32)(unsafe.Pointer(i2cMstCtrlHost1))
|
|
||||||
if !r.waitIdle(reg) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
reg.Set(uint32(block) | uint32(regAddr)<<8)
|
|
||||||
if !r.waitIdle(reg) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
cur := (reg.Get() & i2cMstDataMask) >> i2cMstDataShift
|
|
||||||
mask := uint32(1<<(msb-lsb+1)-1) << lsb
|
|
||||||
cur &^= mask
|
|
||||||
cur |= uint32(data&(1<<(msb-lsb+1)-1)) << lsb
|
|
||||||
reg.Set(uint32(block) | uint32(regAddr)<<8 | i2cMstWrCntlBit | (cur<<i2cMstDataShift)&i2cMstDataMask)
|
|
||||||
r.waitIdle(reg)
|
|
||||||
}
|
|
||||||
|
|
||||||
// sarEnable enables the analog SAR I2C domain before any regI2C access,
|
|
||||||
// matching the prologue in adc_ll_calibration_prepare() (sets ANA_SAR_CFG2_EN).
|
|
||||||
func (r *regI2C) sarEnable() {
|
|
||||||
cfg := (*volatile.Register32)(unsafe.Pointer(anaConfigReg))
|
|
||||||
cfg2 := (*volatile.Register32)(unsafe.Pointer(anaConfig2Reg))
|
|
||||||
esp.RTC_CNTL.SetANA_CONF_SAR_I2C_PU(1)
|
|
||||||
cfg.Set(cfg.Get() &^ i2cSarEnMask)
|
|
||||||
cfg2.Set(cfg2.Get() | anaSarCfg2En)
|
|
||||||
}
|
|
||||||
|
|
||||||
// adc1CalibrationInit corresponds to adc_ll_calibration_init() for ESP32-S3:
|
|
||||||
// it sets the DREF field to 4 for the selected ADC unit, which is the
|
|
||||||
// reference index used by Espressif's calibration flow.
|
|
||||||
func (r *regI2C) adc1CalibrationInit(adcN uint8) {
|
|
||||||
if adcN == 0 {
|
|
||||||
r.writeMask(i2cSarADC, i2cSarADCHostID, adc1DrefAddr, adc1DrefMSB, adc1DrefLSB, 4)
|
|
||||||
} else {
|
|
||||||
r.writeMask(i2cSarADC, i2cSarADCHostID, adc2DrefAddr, adc2DrefMSB, adc2DrefLSB, 4)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// adc1CalibrationPrepare corresponds to the ENCAL_GND part of
|
|
||||||
// adc_ll_calibration_prepare(): it temporarily routes the internal
|
|
||||||
// ground reference into the SAR input so that self-calibration can
|
|
||||||
// measure offset with the pin disconnected.
|
|
||||||
func (r *regI2C) adc1CalibrationPrepare(adcN uint8) {
|
|
||||||
if adcN == 0 {
|
|
||||||
r.writeMask(i2cSarADC, i2cSarADCHostID, adc1EncalGndAddr, adc1EncalGndMSB, adc1EncalGndLSB, 1)
|
|
||||||
} else {
|
|
||||||
r.writeMask(i2cSarADC, i2cSarADCHostID, adc2EncalGndAddr, adc2EncalGndMSB, adc2EncalGndLSB, 1)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// adc1CalibrationFinish corresponds to adc_ll_calibration_finish():
|
|
||||||
// it clears ENCAL_GND so that ADC input is again connected to the pad.
|
|
||||||
func (r *regI2C) adc1CalibrationFinish(adcN uint8) {
|
|
||||||
if adcN == 0 {
|
|
||||||
r.writeMask(i2cSarADC, i2cSarADCHostID, adc1EncalGndAddr, adc1EncalGndMSB, adc1EncalGndLSB, 0)
|
|
||||||
} else {
|
|
||||||
r.writeMask(i2cSarADC, i2cSarADCHostID, adc2EncalGndAddr, adc2EncalGndMSB, adc2EncalGndLSB, 0)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// adc1SetCalibrationParam corresponds to adc_ll_set_calibration_param():
|
|
||||||
// it writes the 9-bit initial code (offset) into the high/low INIT_CODE
|
|
||||||
// regI2C registers for the selected ADC unit.
|
|
||||||
func (r *regI2C) adc1SetCalibrationParam(adcN uint8, param uint32) {
|
|
||||||
msb := uint8(param >> 8)
|
|
||||||
lsb := uint8(param & 0xFF)
|
|
||||||
if adcN == 0 {
|
|
||||||
r.writeMask(i2cSarADC, i2cSarADCHostID, adc1InitCodeHighAddr, adc1InitCodeHighMSB, adc1InitCodeHighLSB, msb)
|
|
||||||
r.writeMask(i2cSarADC, i2cSarADCHostID, adc1InitCodeLowAddr, adc1InitCodeLowMSB, adc1InitCodeLowLSB, lsb)
|
|
||||||
} else {
|
|
||||||
r.writeMask(i2cSarADC, i2cSarADCHostID, adc2InitCodeHighAddr, adc2InitCodeHighMSB, adc2InitCodeHighLSB, msb)
|
|
||||||
r.writeMask(i2cSarADC, i2cSarADCHostID, adc2InitCodeLowAddr, adc2InitCodeLowMSB, adc2InitCodeLowLSB, lsb)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// fuse
|
// fuse
|
||||||
const (
|
const (
|
||||||
// Base address for eFuse controller (EFUSE_BLKx region in TRM).
|
// Base address for eFuse controller (EFUSE_BLKx region in TRM).
|
||||||
|
|||||||
@@ -0,0 +1,235 @@
|
|||||||
|
//go:build esp32s3 || (esp32c3 && !m5stamp_c3)
|
||||||
|
|
||||||
|
// Shared regI2C-based ADC calibration helpers for ESP32-S3 and ESP32-C3.
|
||||||
|
//
|
||||||
|
// The internal I2C bus ("regI2C") and SAR ADC trim register layout are
|
||||||
|
// identical across both chips; chip-specific differences (host ID, DREF
|
||||||
|
// init value, calibration iterations) are captured in the regI2C struct
|
||||||
|
// fields, keeping each target file free of duplicated low-level code.
|
||||||
|
|
||||||
|
package machine
|
||||||
|
|
||||||
|
import (
|
||||||
|
"device/esp"
|
||||||
|
"runtime/volatile"
|
||||||
|
"unsafe"
|
||||||
|
)
|
||||||
|
|
||||||
|
// regI2C wraps the internal I2C bus used for SAR ADC calibration registers.
|
||||||
|
// Fields hold chip-specific parameters that differ between ESP32-S3 and ESP32-C3.
|
||||||
|
type regI2C struct {
|
||||||
|
// hostID is the I2C_SAR_ADC_HOSTID (1 for ESP32-S3, 0 for ESP32-C3).
|
||||||
|
hostID uint8
|
||||||
|
// drefInit is the DREF reference value written during calibrationInit
|
||||||
|
// (4 for ESP32-S3, 1 for ESP32-C3).
|
||||||
|
drefInit uint8
|
||||||
|
}
|
||||||
|
|
||||||
|
// SAR ADC I2C register layout constants shared across ESP32-S3 and ESP32-C3.
|
||||||
|
// Source: ESP-IDF soc/regi2c_saradc.h
|
||||||
|
const (
|
||||||
|
// i2cSarADC is the I2C_SAR_ADC block address on the internal bus.
|
||||||
|
i2cSarADC = uint8(0x69)
|
||||||
|
|
||||||
|
// DREF (reference) bitfields for ADC1 and ADC2.
|
||||||
|
adc1DrefAddr = uint8(0x2)
|
||||||
|
adc1DrefMSB = uint8(6)
|
||||||
|
adc1DrefLSB = uint8(4)
|
||||||
|
adc2DrefAddr = uint8(0x5)
|
||||||
|
adc2DrefMSB = uint8(6)
|
||||||
|
adc2DrefLSB = uint8(4)
|
||||||
|
|
||||||
|
// ENCAL_GND: routes internal ground to ADC input during self-calibration.
|
||||||
|
adc1EncalGndAddr = uint8(0x7)
|
||||||
|
adc1EncalGndMSB = uint8(5)
|
||||||
|
adc1EncalGndLSB = uint8(5)
|
||||||
|
adc2EncalGndAddr = uint8(0x7)
|
||||||
|
adc2EncalGndMSB = uint8(7)
|
||||||
|
adc2EncalGndLSB = uint8(7)
|
||||||
|
|
||||||
|
// INIT_CODE (offset) high/low for ADC1 and ADC2.
|
||||||
|
adc1InitCodeHighAddr = uint8(0x1)
|
||||||
|
adc1InitCodeHighMSB = uint8(3)
|
||||||
|
adc1InitCodeHighLSB = uint8(0)
|
||||||
|
adc1InitCodeLowAddr = uint8(0x0)
|
||||||
|
adc1InitCodeLowMSB = uint8(7)
|
||||||
|
adc1InitCodeLowLSB = uint8(0)
|
||||||
|
adc2InitCodeHighAddr = uint8(0x4)
|
||||||
|
adc2InitCodeHighMSB = uint8(3)
|
||||||
|
adc2InitCodeHighLSB = uint8(0)
|
||||||
|
adc2InitCodeLowAddr = uint8(0x3)
|
||||||
|
adc2InitCodeLowMSB = uint8(7)
|
||||||
|
adc2InitCodeLowLSB = uint8(0)
|
||||||
|
|
||||||
|
// ANA_CONFIG / ANA_CONFIG2: enable analog SAR I2C domain.
|
||||||
|
anaConfigReg = uintptr(0x6000E044)
|
||||||
|
i2cSarEnMask = uint32(1 << 18)
|
||||||
|
anaConfig2Reg = uintptr(0x6000E048)
|
||||||
|
anaSarCfg2En = uint32(1 << 16)
|
||||||
|
|
||||||
|
// REGI2C master control register and helper masks.
|
||||||
|
i2cMstCtrlReg = uintptr(0x6000E000)
|
||||||
|
i2cMstBusyBit = uint32(1 << 25)
|
||||||
|
i2cMstWrCntlBit = uint32(1 << 24)
|
||||||
|
i2cMstDataMask = uint32(0xFF << 16)
|
||||||
|
i2cMstDataShift = 16
|
||||||
|
i2cMstBusyTimeout = 10000
|
||||||
|
|
||||||
|
// adcCalOffsetRange is the binary search upper bound (12-bit full scale).
|
||||||
|
adcCalOffsetRange = uint32(4096)
|
||||||
|
|
||||||
|
// adcCalMaxIterations is the maximum number of calibration iterations
|
||||||
|
// supported by calibrateBinarySearch. Must be >= max(S3=10, C3=15).
|
||||||
|
adcCalMaxIterations = 16
|
||||||
|
)
|
||||||
|
|
||||||
|
// waitIdle polls the REGI2C master BUSY bit until it clears or a
|
||||||
|
// timeout expires, matching the busy-wait helper in ESP-IDF's regi2c_ctrl.c.
|
||||||
|
func (r regI2C) waitIdle(reg *volatile.Register32) bool {
|
||||||
|
for i := 0; i < i2cMstBusyTimeout; i++ {
|
||||||
|
if reg.Get()&i2cMstBusyBit == 0 {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// writeMask is a software implementation of the IDF REGI2C_WRITE_MASK macro.
|
||||||
|
// It reads the current byte at regAddr on the SAR ADC I2C block, updates
|
||||||
|
// only the [msb:lsb] bitfield, and writes it back via the internal I2C master.
|
||||||
|
func (r regI2C) writeMask(regAddr, msb, lsb, data uint8) {
|
||||||
|
reg := (*volatile.Register32)(unsafe.Pointer(i2cMstCtrlReg))
|
||||||
|
if !r.waitIdle(reg) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
reg.Set(uint32(i2cSarADC) | uint32(regAddr)<<8)
|
||||||
|
if !r.waitIdle(reg) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
cur := (reg.Get() & i2cMstDataMask) >> i2cMstDataShift
|
||||||
|
mask := uint32(1<<(msb-lsb+1)-1) << lsb
|
||||||
|
cur &^= mask
|
||||||
|
cur |= uint32(data&(1<<(msb-lsb+1)-1)) << lsb
|
||||||
|
reg.Set(uint32(i2cSarADC) | uint32(regAddr)<<8 | i2cMstWrCntlBit | (cur<<i2cMstDataShift)&i2cMstDataMask)
|
||||||
|
r.waitIdle(reg)
|
||||||
|
}
|
||||||
|
|
||||||
|
// sarEnable enables the analog SAR I2C domain before any regI2C access,
|
||||||
|
// matching the prologue in adc_ll_calibration_prepare().
|
||||||
|
func (r regI2C) sarEnable() {
|
||||||
|
cfg := (*volatile.Register32)(unsafe.Pointer(anaConfigReg))
|
||||||
|
cfg2 := (*volatile.Register32)(unsafe.Pointer(anaConfig2Reg))
|
||||||
|
esp.RTC_CNTL.SetANA_CONF_SAR_I2C_PU(1)
|
||||||
|
cfg.Set(cfg.Get() &^ i2cSarEnMask)
|
||||||
|
cfg2.Set(cfg2.Get() | anaSarCfg2En)
|
||||||
|
}
|
||||||
|
|
||||||
|
// calibrationInit sets the DREF reference for the selected ADC unit to
|
||||||
|
// the chip-specific init value before running self-calibration.
|
||||||
|
// Corresponds to adc_ll_calibration_init() in ESP-IDF.
|
||||||
|
func (r regI2C) calibrationInit(adcN uint8) {
|
||||||
|
if adcN == 0 {
|
||||||
|
r.writeMask(adc1DrefAddr, adc1DrefMSB, adc1DrefLSB, r.drefInit)
|
||||||
|
} else {
|
||||||
|
r.writeMask(adc2DrefAddr, adc2DrefMSB, adc2DrefLSB, r.drefInit)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// calibrationPrepare enables ENCAL_GND so that the ADC input is
|
||||||
|
// internally shorted to ground during self-calibration.
|
||||||
|
// Corresponds to the ENCAL_GND part of adc_ll_calibration_prepare().
|
||||||
|
func (r regI2C) calibrationPrepare(adcN uint8) {
|
||||||
|
if adcN == 0 {
|
||||||
|
r.writeMask(adc1EncalGndAddr, adc1EncalGndMSB, adc1EncalGndLSB, 1)
|
||||||
|
} else {
|
||||||
|
r.writeMask(adc2EncalGndAddr, adc2EncalGndMSB, adc2EncalGndLSB, 1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// calibrationFinish clears ENCAL_GND to reconnect the ADC input to the
|
||||||
|
// external pad after self-calibration.
|
||||||
|
// Corresponds to adc_ll_calibration_finish() in ESP-IDF.
|
||||||
|
func (r regI2C) calibrationFinish(adcN uint8) {
|
||||||
|
if adcN == 0 {
|
||||||
|
r.writeMask(adc1EncalGndAddr, adc1EncalGndMSB, adc1EncalGndLSB, 0)
|
||||||
|
} else {
|
||||||
|
r.writeMask(adc2EncalGndAddr, adc2EncalGndMSB, adc2EncalGndLSB, 0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// setCalibrationParam writes the INIT_CODE (offset trim) for the selected
|
||||||
|
// ADC unit via the regI2C bitfields.
|
||||||
|
// Corresponds to adc_ll_set_calibration_param() in ESP-IDF.
|
||||||
|
func (r regI2C) setCalibrationParam(adcN uint8, param uint32) {
|
||||||
|
msb := uint8(param >> 8)
|
||||||
|
lsb := uint8(param & 0xFF)
|
||||||
|
if adcN == 0 {
|
||||||
|
r.writeMask(adc1InitCodeHighAddr, adc1InitCodeHighMSB, adc1InitCodeHighLSB, msb)
|
||||||
|
r.writeMask(adc1InitCodeLowAddr, adc1InitCodeLowMSB, adc1InitCodeLowLSB, lsb)
|
||||||
|
} else {
|
||||||
|
r.writeMask(adc2InitCodeHighAddr, adc2InitCodeHighMSB, adc2InitCodeHighLSB, msb)
|
||||||
|
r.writeMask(adc2InitCodeLowAddr, adc2InitCodeLowMSB, adc2InitCodeLowLSB, lsb)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// calibrateBinarySearch runs the ADC self-calibration binary search loop.
|
||||||
|
// It performs 'iterations' rounds of binary search to find the optimal offset
|
||||||
|
// code, drops the min/max outliers, and returns the rounded mean of the
|
||||||
|
// remaining values. This matches adc_hal_self_calibration() in ESP-IDF.
|
||||||
|
//
|
||||||
|
// The readADC callback must perform a single conversion using the target's
|
||||||
|
// oneshot path (SENS or APB_SARADC) and return the raw 12-bit result.
|
||||||
|
// During calibration, ENCAL_GND is active so the ADC reads its internal ground.
|
||||||
|
func (r regI2C) calibrateBinarySearch(adcN uint8, iterations int, readADC func() uint32) uint32 {
|
||||||
|
if iterations > adcCalMaxIterations {
|
||||||
|
iterations = adcCalMaxIterations
|
||||||
|
}
|
||||||
|
var codeList [adcCalMaxIterations]uint32
|
||||||
|
var codeSum uint32
|
||||||
|
|
||||||
|
for rpt := 0; rpt < iterations; rpt++ {
|
||||||
|
codeH := adcCalOffsetRange
|
||||||
|
codeL := uint32(0)
|
||||||
|
chkCode := (codeH + codeL) / 2
|
||||||
|
r.setCalibrationParam(adcN, chkCode)
|
||||||
|
selfCal := readADC()
|
||||||
|
|
||||||
|
for codeH-codeL > 1 {
|
||||||
|
if selfCal == 0 {
|
||||||
|
codeH = chkCode
|
||||||
|
} else {
|
||||||
|
codeL = chkCode
|
||||||
|
}
|
||||||
|
chkCode = (codeH + codeL) / 2
|
||||||
|
r.setCalibrationParam(adcN, chkCode)
|
||||||
|
selfCal = readADC()
|
||||||
|
if codeH-codeL == 1 {
|
||||||
|
chkCode++
|
||||||
|
r.setCalibrationParam(adcN, chkCode)
|
||||||
|
selfCal = readADC()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
codeList[rpt] = chkCode
|
||||||
|
codeSum += chkCode
|
||||||
|
}
|
||||||
|
|
||||||
|
// Drop min and max outliers, then average with IDF-style rounding.
|
||||||
|
codeMin := codeList[0]
|
||||||
|
codeMax := codeList[0]
|
||||||
|
for i := 0; i < iterations; i++ {
|
||||||
|
if codeList[i] < codeMin {
|
||||||
|
codeMin = codeList[i]
|
||||||
|
}
|
||||||
|
if codeList[i] > codeMax {
|
||||||
|
codeMax = codeList[i]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
remaining := codeSum - codeMax - codeMin
|
||||||
|
divisor := uint32(iterations - 2)
|
||||||
|
finalCode := remaining / divisor
|
||||||
|
if remaining%divisor >= 4 {
|
||||||
|
finalCode++
|
||||||
|
}
|
||||||
|
|
||||||
|
return finalCode
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user