|
| 1 | +//go:build esp32c6 |
| 2 | + |
| 3 | +package machine |
| 4 | + |
| 5 | +import ( |
| 6 | + "device/esp" |
| 7 | + "errors" |
| 8 | + "runtime/volatile" |
| 9 | + "unsafe" |
| 10 | +) |
| 11 | + |
| 12 | +// newRegI2C returns the regI2C configured for ESP32-C6: hostID=0, drefInit=1. |
| 13 | +// I2C_SAR_ADC_HOSTID = 0 per soc/esp32c6/include/soc/regi2c_saradc.h. |
| 14 | +func newRegI2C() regI2C { return regI2C{hostID: 0, drefInit: 1} } |
| 15 | + |
| 16 | +const ( |
| 17 | + // ADC attenuation values for ESP32-C6 APB_SARADC. |
| 18 | + // 0 dB : ~0 .. 1.1 V |
| 19 | + // 11 dB : ~0 .. 3.3 V (matches typical VDD) |
| 20 | + atten0dB = 0 |
| 21 | + atten11dB = 3 |
| 22 | +) |
| 23 | + |
| 24 | +// InitADC initialises the APB_SARADC peripheral on ESP32-C6. |
| 25 | +// On C6 the clock/reset gating moved to PCR (not SYSTEM as on C3), and the |
| 26 | +// SARADC CLKM divider configuration also lives in PCR. |
| 27 | +func InitADC() { |
| 28 | + // Reset and enable the SARADC bus clock via PCR. |
| 29 | + esp.PCR.SetSARADC_CONF_SARADC_RST_EN(1) |
| 30 | + esp.PCR.SetSARADC_CONF_SARADC_CLK_EN(1) |
| 31 | + esp.PCR.SetSARADC_CONF_SARADC_RST_EN(0) |
| 32 | + |
| 33 | + // Select clock source 2 (PLL_F80M), divider = 1, no fractional. |
| 34 | + esp.PCR.SetSARADC_CLKM_CONF_SARADC_CLKM_SEL(2) |
| 35 | + esp.PCR.SetSARADC_CLKM_CONF_SARADC_CLKM_DIV_NUM(1) |
| 36 | + esp.PCR.SetSARADC_CLKM_CONF_SARADC_CLKM_DIV_B(0) |
| 37 | + esp.PCR.SetSARADC_CLKM_CONF_SARADC_CLKM_DIV_A(0) |
| 38 | + esp.PCR.SetSARADC_CLKM_CONF_SARADC_CLKM_EN(1) |
| 39 | + |
| 40 | + // Power up the SAR ADC and configure FSM timing (same register layout as C3). |
| 41 | + esp.APB_SARADC.SetCTRL_SARADC_XPD_SAR_FORCE(1) |
| 42 | + esp.APB_SARADC.SetFSM_WAIT_SARADC_XPD_WAIT(8) |
| 43 | + esp.APB_SARADC.SetFSM_WAIT_SARADC_RSTB_WAIT(8) |
| 44 | + esp.APB_SARADC.SetFSM_WAIT_SARADC_STANDBY_WAIT(100) |
| 45 | + |
| 46 | + adcSelfCalibrate() |
| 47 | +} |
| 48 | + |
| 49 | +// ESP32-C6 ADC pin mapping: ADC1 = GPIO0–GPIO6 (ch 0–6). There is no ADC2. |
| 50 | +// (The machine_esp32c6.go file defines ADC0..ADC6 as GPIO0..GPIO6.) |
| 51 | +func (a ADC) Configure(config ADCConfig) error { |
| 52 | + if a.Pin > 6 { |
| 53 | + return errors.New("invalid ADC pin for ESP32-C6") |
| 54 | + } |
| 55 | + a.Pin.Configure(PinConfig{Mode: PinAnalog}) |
| 56 | + return nil |
| 57 | +} |
| 58 | + |
| 59 | +// Get performs a single ADC1 conversion and returns a 16-bit value. |
| 60 | +// The raw 12-bit result (0..4095) is left-shifted by 4 to fill 16 bits. |
| 61 | +func (a ADC) Get() uint16 { |
| 62 | + if a.Pin > 6 { |
| 63 | + return 0 |
| 64 | + } |
| 65 | + esp.APB_SARADC.SetONETIME_SAMPLE_SARADC_ONETIME_ATTEN(atten11dB) |
| 66 | + esp.APB_SARADC.SetINT_CLR_APB_SARADC1_DONE_INT_CLR(1) |
| 67 | + esp.APB_SARADC.SetONETIME_SAMPLE_SARADC_ONETIME_START(0) |
| 68 | + esp.APB_SARADC.SetONETIME_SAMPLE_SARADC_ONETIME_CHANNEL(uint32(a.Pin)) |
| 69 | + esp.APB_SARADC.SetONETIME_SAMPLE_SARADC1_ONETIME_SAMPLE(1) |
| 70 | + esp.APB_SARADC.SetONETIME_SAMPLE_SARADC_ONETIME_START(1) |
| 71 | + for esp.APB_SARADC.GetINT_RAW_APB_SARADC1_DONE_INT_RAW() == 0 { |
| 72 | + } |
| 73 | + raw := esp.APB_SARADC.GetSAR1DATA_STATUS_APB_SARADC1_DATA() |
| 74 | + esp.APB_SARADC.SetONETIME_SAMPLE_SARADC_ONETIME_START(0) |
| 75 | + esp.APB_SARADC.SetONETIME_SAMPLE_SARADC1_ONETIME_SAMPLE(0) |
| 76 | + return uint16(raw&0xfff) << 4 |
| 77 | +} |
| 78 | + |
| 79 | +// ── regI2C: internal I2C-bus (LP_I2C_ANA_MST) for SAR ADC calibration ─────── |
| 80 | +// |
| 81 | +// On ESP32-C6 the "REGI2C" master moved from the embedded SENS/APB_SARADC |
| 82 | +// controller (0x6000_E000) used on C3/S3 to the dedicated LP_I2C_ANA_MST |
| 83 | +// peripheral at 0x600b_2400. The SAR ADC block address and register layout |
| 84 | +// (DREF, ENCAL_GND, INIT_CODE) remain identical to C3. |
| 85 | +// |
| 86 | +// LP_I2C_ANA_MST.I2C0_CTRL bit layout (25-bit command field): |
| 87 | +// [7:0] = slave block address (0x69 for I2C_SAR_ADC) |
| 88 | +// [15:8] = register address within the block |
| 89 | +// [23:16]= write data (8 bits) |
| 90 | +// [24] = WR_CNTL: 0=read, 1=write |
| 91 | +// [25] = BUSY (read-only, set by hardware while processing) |
| 92 | +// |
| 93 | +// Source: components/esp_rom/patches/esp_rom_regi2c_esp32c6.c in esp-idf |
| 94 | + |
| 95 | +// regI2C wraps the internal I2C bus used for SAR ADC calibration registers. |
| 96 | +// Fields hold chip-specific parameters. |
| 97 | +type regI2C struct { |
| 98 | + // hostID is the I2C_SAR_ADC_HOSTID (0 for ESP32-C6, matching regi2c_saradc.h). |
| 99 | + hostID uint8 |
| 100 | + // drefInit is the DREF reference value written during calibrationInit (1 for C6). |
| 101 | + drefInit uint8 |
| 102 | +} |
| 103 | + |
| 104 | +// SAR ADC I2C register layout — identical to ESP32-C3 / ESP32-S3. |
| 105 | +// Source: soc/esp32c6/include/soc/regi2c_saradc.h |
| 106 | +const ( |
| 107 | + i2cSarADC = uint8(0x69) |
| 108 | + |
| 109 | + adc1DrefAddr = uint8(0x2) |
| 110 | + adc1DrefMSB = uint8(6) |
| 111 | + adc1DrefLSB = uint8(4) |
| 112 | + adc2DrefAddr = uint8(0x5) |
| 113 | + adc2DrefMSB = uint8(6) |
| 114 | + adc2DrefLSB = uint8(4) |
| 115 | + |
| 116 | + adc1EncalGndAddr = uint8(0x7) |
| 117 | + adc1EncalGndMSB = uint8(5) |
| 118 | + adc1EncalGndLSB = uint8(5) |
| 119 | + adc2EncalGndAddr = uint8(0x7) |
| 120 | + adc2EncalGndMSB = uint8(7) |
| 121 | + adc2EncalGndLSB = uint8(7) |
| 122 | + |
| 123 | + adc1InitCodeHighAddr = uint8(0x1) |
| 124 | + adc1InitCodeHighMSB = uint8(3) |
| 125 | + adc1InitCodeHighLSB = uint8(0) |
| 126 | + adc1InitCodeLowAddr = uint8(0x0) |
| 127 | + adc1InitCodeLowMSB = uint8(7) |
| 128 | + adc1InitCodeLowLSB = uint8(0) |
| 129 | + adc2InitCodeHighAddr = uint8(0x4) |
| 130 | + adc2InitCodeHighMSB = uint8(3) |
| 131 | + adc2InitCodeHighLSB = uint8(0) |
| 132 | + adc2InitCodeLowAddr = uint8(0x3) |
| 133 | + adc2InitCodeLowMSB = uint8(7) |
| 134 | + adc2InitCodeLowLSB = uint8(0) |
| 135 | + |
| 136 | + // adcCalOffsetRange is the binary search upper bound (12-bit full scale). |
| 137 | + adcCalOffsetRange = uint32(4096) |
| 138 | + // adcCalMaxIterations caps binary search iterations. |
| 139 | + adcCalMaxIterations = 16 |
| 140 | +) |
| 141 | + |
| 142 | +// LP_I2C_ANA_MST I2C0_CTRL bit-field shifts (see file header comment). |
| 143 | +const ( |
| 144 | + c6SlaveIDShift = 0 // bits [7:0] |
| 145 | + c6AddrShift = 8 // bits [15:8] |
| 146 | + c6DataShift = 16 // bits [23:16] |
| 147 | + c6WrCntlShift = 24 // bit [24] |
| 148 | + c6BusyBit = uint32(1 << 25) |
| 149 | + |
| 150 | + // c6SarI2CDeviceEn is BIT(7) in LP_I2C_ANA_MST.DEVICE_EN for I2C_SAR_ADC (0x69). |
| 151 | + c6SarI2CDeviceEn = uint32(1 << 7) |
| 152 | +) |
| 153 | + |
| 154 | +// ANA_CONFIG / ANA_CONFIG2 register addresses and bits for the internal SAR I2C |
| 155 | +// domain on ESP32-C6. These differ from C3's SENS block (0x6000_E044/048). |
| 156 | +// Source: soc/esp32c6/include/soc/regi2c_defs.h |
| 157 | +const ( |
| 158 | + c6AnaConfigReg = uintptr(0x600AF81C) // clear ANA_I2C_SAR_FORCE_PD (bit 18) |
| 159 | + c6AnaConfig2Reg = uintptr(0x600AF820) // set ANA_I2C_SAR_FORCE_PU (bit 16) |
| 160 | + c6SarForcePD = uint32(1 << 18) |
| 161 | + c6SarForcePU = uint32(1 << 16) |
| 162 | +) |
| 163 | + |
| 164 | +// sarEnable powers up the internal SAR I2C domain and enables the LP_I2C_ANA_MST |
| 165 | +// clock and SAR slave device before any regI2C access. |
| 166 | +// Matches regi2c_ctrl_ll_i2c_saradc_enable() + regi2c_enable_block(REGI2C_SAR_I2C). |
| 167 | +func (r regI2C) sarEnable() { |
| 168 | + cfg := (*volatile.Register32)(unsafe.Pointer(c6AnaConfigReg)) |
| 169 | + cfg2 := (*volatile.Register32)(unsafe.Pointer(c6AnaConfig2Reg)) |
| 170 | + cfg.Set(cfg.Get() &^ c6SarForcePD) |
| 171 | + cfg2.Set(cfg2.Get() | c6SarForcePU) |
| 172 | + |
| 173 | + // Enable the LP_I2C_ANA_MST master clock (MODEM_LPCON.CLK_CONF bit 2). |
| 174 | + esp.MODEM_LPCON.SetCLK_CONF_CLK_I2C_MST_EN(1) |
| 175 | + // Enable the master's own clock gate (LP_I2C_ANA_MST.DATE bit 28). |
| 176 | + esp.LP_I2C_ANA_MST.SetDATE_LP_I2C_ANA_MAST_I2C_MAT_CLK_EN(1) |
| 177 | + // Enable the SAR ADC slave device (DEVICE_EN bit 7). |
| 178 | + dev := esp.LP_I2C_ANA_MST.GetDEVICE_EN_LP_I2C_ANA_MAST_I2C_DEVICE_EN() |
| 179 | + esp.LP_I2C_ANA_MST.SetDEVICE_EN_LP_I2C_ANA_MAST_I2C_DEVICE_EN(dev | c6SarI2CDeviceEn) |
| 180 | +} |
| 181 | + |
| 182 | +// writeMask implements the REGI2C_WRITE_MASK macro for ESP32-C6 via LP_I2C_ANA_MST. |
| 183 | +// It reads the current byte at regAddr, updates the [msb:lsb] bitfield, and writes |
| 184 | +// it back. Matches esp_rom_regi2c_write_mask() in esp_rom_regi2c_esp32c6.c. |
| 185 | +func (r regI2C) writeMask(regAddr, msb, lsb, data uint8) { |
| 186 | + ctrl := &esp.LP_I2C_ANA_MST.I2C0_CTRL |
| 187 | + rdata := &esp.LP_I2C_ANA_MST.I2C0_DATA |
| 188 | + |
| 189 | + // Issue a read command: slave_id | (reg_addr << 8), no WR_CNTL bit. |
| 190 | + readCmd := (uint32(i2cSarADC) << c6SlaveIDShift) | (uint32(regAddr) << c6AddrShift) |
| 191 | + volatile.StoreUint32(&ctrl.Reg, readCmd) |
| 192 | + for volatile.LoadUint32(&ctrl.Reg)&c6BusyBit != 0 { |
| 193 | + } |
| 194 | + cur := volatile.LoadUint32(&rdata.Reg) & 0xFF |
| 195 | + |
| 196 | + // Modify the [msb:lsb] bitfield. |
| 197 | + mask := uint32(1<<(msb-lsb+1)-1) << lsb |
| 198 | + cur &^= mask |
| 199 | + cur |= uint32(data&(1<<(msb-lsb+1)-1)) << lsb |
| 200 | + |
| 201 | + // Issue a write command: slave_id | (reg_addr<<8) | WR_CNTL | (data<<16). |
| 202 | + writeCmd := (uint32(i2cSarADC) << c6SlaveIDShift) | |
| 203 | + (uint32(regAddr) << c6AddrShift) | |
| 204 | + (uint32(1) << c6WrCntlShift) | |
| 205 | + ((cur & 0xFF) << c6DataShift) |
| 206 | + volatile.StoreUint32(&ctrl.Reg, writeCmd) |
| 207 | + for volatile.LoadUint32(&ctrl.Reg)&c6BusyBit != 0 { |
| 208 | + } |
| 209 | +} |
| 210 | + |
| 211 | +// calibrationInit sets the DREF reference for the selected ADC unit. |
| 212 | +func (r regI2C) calibrationInit(adcN uint8) { |
| 213 | + if adcN == 0 { |
| 214 | + r.writeMask(adc1DrefAddr, adc1DrefMSB, adc1DrefLSB, r.drefInit) |
| 215 | + } else { |
| 216 | + r.writeMask(adc2DrefAddr, adc2DrefMSB, adc2DrefLSB, r.drefInit) |
| 217 | + } |
| 218 | +} |
| 219 | + |
| 220 | +// calibrationPrepare enables ENCAL_GND so the ADC input is shorted to ground. |
| 221 | +func (r regI2C) calibrationPrepare(adcN uint8) { |
| 222 | + if adcN == 0 { |
| 223 | + r.writeMask(adc1EncalGndAddr, adc1EncalGndMSB, adc1EncalGndLSB, 1) |
| 224 | + } else { |
| 225 | + r.writeMask(adc2EncalGndAddr, adc2EncalGndMSB, adc2EncalGndLSB, 1) |
| 226 | + } |
| 227 | +} |
| 228 | + |
| 229 | +// calibrationFinish clears ENCAL_GND to reconnect the ADC input to the pad. |
| 230 | +func (r regI2C) calibrationFinish(adcN uint8) { |
| 231 | + if adcN == 0 { |
| 232 | + r.writeMask(adc1EncalGndAddr, adc1EncalGndMSB, adc1EncalGndLSB, 0) |
| 233 | + } else { |
| 234 | + r.writeMask(adc2EncalGndAddr, adc2EncalGndMSB, adc2EncalGndLSB, 0) |
| 235 | + } |
| 236 | +} |
| 237 | + |
| 238 | +// setCalibrationParam writes the INIT_CODE (offset trim) for the selected ADC unit. |
| 239 | +func (r regI2C) setCalibrationParam(adcN uint8, param uint32) { |
| 240 | + msb := uint8(param >> 8) |
| 241 | + lsb := uint8(param & 0xFF) |
| 242 | + if adcN == 0 { |
| 243 | + r.writeMask(adc1InitCodeHighAddr, adc1InitCodeHighMSB, adc1InitCodeHighLSB, msb) |
| 244 | + r.writeMask(adc1InitCodeLowAddr, adc1InitCodeLowMSB, adc1InitCodeLowLSB, lsb) |
| 245 | + } else { |
| 246 | + r.writeMask(adc2InitCodeHighAddr, adc2InitCodeHighMSB, adc2InitCodeHighLSB, msb) |
| 247 | + r.writeMask(adc2InitCodeLowAddr, adc2InitCodeLowMSB, adc2InitCodeLowLSB, lsb) |
| 248 | + } |
| 249 | +} |
| 250 | + |
| 251 | +// calibrateBinarySearch runs the ADC self-calibration binary search loop. |
| 252 | +// It performs 'iterations' rounds, drops the min/max outliers, and returns |
| 253 | +// the rounded mean of the remaining values. Matches adc_hal_self_calibration(). |
| 254 | +func (r regI2C) calibrateBinarySearch(adcN uint8, iterations int, readADC func() uint32) uint32 { |
| 255 | + if iterations > adcCalMaxIterations { |
| 256 | + iterations = adcCalMaxIterations |
| 257 | + } |
| 258 | + var codeList [adcCalMaxIterations]uint32 |
| 259 | + var codeSum uint32 |
| 260 | + |
| 261 | + for rpt := 0; rpt < iterations; rpt++ { |
| 262 | + codeH := adcCalOffsetRange |
| 263 | + codeL := uint32(0) |
| 264 | + chkCode := (codeH + codeL) / 2 |
| 265 | + r.setCalibrationParam(adcN, chkCode) |
| 266 | + selfCal := readADC() |
| 267 | + |
| 268 | + for codeH-codeL > 1 { |
| 269 | + if selfCal == 0 { |
| 270 | + codeH = chkCode |
| 271 | + } else { |
| 272 | + codeL = chkCode |
| 273 | + } |
| 274 | + chkCode = (codeH + codeL) / 2 |
| 275 | + r.setCalibrationParam(adcN, chkCode) |
| 276 | + selfCal = readADC() |
| 277 | + if codeH-codeL == 1 { |
| 278 | + chkCode++ |
| 279 | + r.setCalibrationParam(adcN, chkCode) |
| 280 | + selfCal = readADC() |
| 281 | + } |
| 282 | + } |
| 283 | + codeList[rpt] = chkCode |
| 284 | + codeSum += chkCode |
| 285 | + } |
| 286 | + |
| 287 | + codeMin := codeList[0] |
| 288 | + codeMax := codeList[0] |
| 289 | + for i := 0; i < iterations; i++ { |
| 290 | + if codeList[i] < codeMin { |
| 291 | + codeMin = codeList[i] |
| 292 | + } |
| 293 | + if codeList[i] > codeMax { |
| 294 | + codeMax = codeList[i] |
| 295 | + } |
| 296 | + } |
| 297 | + remaining := codeSum - codeMax - codeMin |
| 298 | + divisor := uint32(iterations - 2) |
| 299 | + finalCode := remaining / divisor |
| 300 | + if remaining%divisor >= 4 { |
| 301 | + finalCode++ |
| 302 | + } |
| 303 | + return finalCode |
| 304 | +} |
| 305 | + |
| 306 | +// ── Self-calibration ────────────────────────────────────────────────────────── |
| 307 | + |
| 308 | +const ( |
| 309 | + adcCalTimesC6 = 15 |
| 310 | + adcCalRtcMagicC6 = uint32(0xADC1C601) // magic distinguishes C6 from C3 |
| 311 | + adcCalInitMinC6 = uint32(1000) |
| 312 | + adcCalInitMaxC6 = uint32(4096) |
| 313 | +) |
| 314 | + |
| 315 | +// adcSelfCalibrate runs a self-calibration for ADC1 (the only ADC unit on C6). |
| 316 | +// The calibration code is cached in LP_AON scratch registers to survive sleep. |
| 317 | +// eFuse calibration is not used: the fields are often unprogrammed. |
| 318 | +func adcSelfCalibrate() { |
| 319 | + reg := newRegI2C() |
| 320 | + reg.sarEnable() |
| 321 | + |
| 322 | + var adc1Code uint32 |
| 323 | + if saved, ok := c6RestoreFromLP(); ok { |
| 324 | + adc1Code = saved |
| 325 | + } else { |
| 326 | + c6CalSetupADC1() |
| 327 | + reg.calibrationInit(0) |
| 328 | + reg.calibrationPrepare(0) |
| 329 | + adc1Code = reg.calibrateBinarySearch(0, adcCalTimesC6, readADC1) |
| 330 | + if adc1Code < adcCalInitMinC6 { |
| 331 | + adc1Code = adcCalInitMinC6 |
| 332 | + } |
| 333 | + if adc1Code > adcCalInitMaxC6 { |
| 334 | + adc1Code = adcCalInitMaxC6 |
| 335 | + } |
| 336 | + c6SaveToLP(adc1Code) |
| 337 | + reg.calibrationFinish(0) |
| 338 | + } |
| 339 | + |
| 340 | + c6ApplyADC1Code(reg, adc1Code) |
| 341 | +} |
| 342 | + |
| 343 | +// c6CalSetupADC1 configures APB_SARADC for oneshot ADC1 ch0 with fixed attenuation. |
| 344 | +func c6CalSetupADC1() { |
| 345 | + esp.APB_SARADC.SetONETIME_SAMPLE_SARADC_ONETIME_ATTEN(atten11dB) |
| 346 | + esp.APB_SARADC.SetONETIME_SAMPLE_SARADC_ONETIME_CHANNEL(0) |
| 347 | + esp.APB_SARADC.SetONETIME_SAMPLE_SARADC1_ONETIME_SAMPLE(1) |
| 348 | +} |
| 349 | + |
| 350 | +// readADC1 performs a single ADC1 conversion and returns the raw 12-bit result. |
| 351 | +func readADC1() uint32 { |
| 352 | + esp.APB_SARADC.SetINT_CLR_APB_SARADC1_DONE_INT_CLR(1) |
| 353 | + esp.APB_SARADC.SetONETIME_SAMPLE_SARADC_ONETIME_START(0) |
| 354 | + esp.APB_SARADC.SetONETIME_SAMPLE_SARADC_ONETIME_START(1) |
| 355 | + for esp.APB_SARADC.GetINT_RAW_APB_SARADC1_DONE_INT_RAW() == 0 { |
| 356 | + } |
| 357 | + raw := esp.APB_SARADC.GetSAR1DATA_STATUS_APB_SARADC1_DATA() & 0xfff |
| 358 | + esp.APB_SARADC.SetONETIME_SAMPLE_SARADC_ONETIME_START(0) |
| 359 | + return uint32(raw) |
| 360 | +} |
| 361 | + |
| 362 | +// c6RestoreFromLP reads the saved calibration code from LP_AON scratch registers. |
| 363 | +// On C6, LP_AON replaces the C3's RTC_CNTL for scratch storage. |
| 364 | +func c6RestoreFromLP() (uint32, bool) { |
| 365 | + if esp.LP_AON.GetSTORE0() != adcCalRtcMagicC6 { |
| 366 | + return 0, false |
| 367 | + } |
| 368 | + code := esp.LP_AON.GetSTORE1() |
| 369 | + if code < adcCalInitMinC6 || code > adcCalInitMaxC6 { |
| 370 | + return 0, false |
| 371 | + } |
| 372 | + return code, true |
| 373 | +} |
| 374 | + |
| 375 | +// c6SaveToLP stores the calibration code in LP_AON scratch registers. |
| 376 | +func c6SaveToLP(code uint32) { |
| 377 | + if code < adcCalInitMinC6 || code > adcCalInitMaxC6 { |
| 378 | + return |
| 379 | + } |
| 380 | + esp.LP_AON.SetSTORE0(adcCalRtcMagicC6) |
| 381 | + esp.LP_AON.SetSTORE1(code) |
| 382 | +} |
| 383 | + |
| 384 | +// c6ApplyADC1Code sets ADC1 init code and finishes calibration. |
| 385 | +// ESP32-C6 has no ADC2 so only ADC1 (adcN=0) needs to be configured. |
| 386 | +func c6ApplyADC1Code(reg regI2C, code uint32) { |
| 387 | + c6CalSetupADC1() |
| 388 | + reg.calibrationInit(0) |
| 389 | + reg.calibrationPrepare(0) |
| 390 | + reg.setCalibrationParam(0, code) |
| 391 | + reg.calibrationFinish(0) |
| 392 | +} |
0 commit comments