Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
164 changes: 159 additions & 5 deletions src/machine/machine_esp32c6_adc.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,16 +21,27 @@ const (
atten11dB = 3
)

// InitADC initialises the APB_SARADC peripheral on ESP32-C6.
const (
c6PwDetConfigReg = uintptr(0x600A0810) // see soc/esp32c6/register/soc/reg_base.h
c6PwDetSarPowerForceBit = uint32(1 << 24) // bits are defined in hal/esp32c6/include/hal/sar_ctrl_ll.h
c6PwDetSarPowerControlBit = uint32(1 << 23)
)

var c6PwDetConfReg = (*volatile.Register32)(unsafe.Pointer(c6AnaConfigReg))

// InitADC initialises the APB_SARADC and Modem/ADC peripheral on ESP32-C6.
// On C6 the clock/reset gating moved to PCR (not SYSTEM as on C3), and the
// SARADC CLKM divider configuration also lives in PCR.
func InitADC() {
// Reset and enable the SARADC bus clock via PCR.
esp.PCR.SetSARADC_CONF_SARADC_REG_CLK_EN(1)
esp.PCR.SetSARADC_CLKM_CONF_SARADC_CLKM_EN(1)
esp.PCR.SetSARADC_CONF_SARADC_RST_EN(1)
esp.PCR.SetSARADC_CONF_SARADC_CLK_EN(1)
esp.PCR.SetSARADC_CONF_SARADC_RST_EN(0)
esp.PCR.SetSARADC_CONF_SARADC_REG_RST_EN(1)
esp.PCR.SetSARADC_CONF_SARADC_REG_RST_EN(0)

// Select clock source 2 (PLL_F80M), divider = 1, no fractional.
// Select clock source 2 (XTAL), divider = 1, no fractional.
esp.PCR.SetSARADC_CLKM_CONF_SARADC_CLKM_SEL(2)
esp.PCR.SetSARADC_CLKM_CONF_SARADC_CLKM_DIV_NUM(1)
esp.PCR.SetSARADC_CLKM_CONF_SARADC_CLKM_DIV_B(0)
Expand All @@ -43,6 +54,20 @@ func InitADC() {
esp.APB_SARADC.SetFSM_WAIT_SARADC_RSTB_WAIT(8)
esp.APB_SARADC.SetFSM_WAIT_SARADC_STANDBY_WAIT(100)

modemClockModuleEnableForADC()

// Enable REG_I2C: Enter regi2c reset mode
esp.PMU.SetRF_PWC_PERIF_I2C_RSTB(0)
// Enable REGI2C for SAR_ADC and TSENS
esp.PMU.SetRF_PWC_XPD_PERIF_I2C(1)
// Release regi2c reset mode, enter work mode
esp.PMU.SetRF_PWC_PERIF_I2C_RSTB(1)

// Enable PWDET see hal at: sar_ctrl_ll_set_power_mode_from_pwdet(SAR_CTRL_LL_POWER_ON);
c6PwDetCfg := (*volatile.Register32)(unsafe.Pointer(c6PwDetConfReg))
c6PwDetCfg.SetBits(c6PwDetSarPowerForceBit)
c6PwDetCfg.SetBits(c6PwDetSarPowerControlBit)

adcSelfCalibrate()
}

Expand All @@ -62,17 +87,31 @@ func (a ADC) Get() uint16 {
if a.Pin > 6 {
return 0
}
esp.APB_SARADC.SetONETIME_SAMPLE_SARADC_ONETIME_ATTEN(atten11dB)

// Clear stale DONE
esp.APB_SARADC.SetINT_CLR_APB_SARADC1_DONE_INT_CLR(1)
esp.APB_SARADC.SetONETIME_SAMPLE_SARADC_ONETIME_START(0)

// Disable oneshot conversion trigger for ADC unit
esp.APB_SARADC.SetONETIME_SAMPLE_SARADC1_ONETIME_SAMPLE(0)

esp.APB_SARADC.SetONETIME_SAMPLE_SARADC_ONETIME_CHANNEL(uint32(a.Pin))
esp.APB_SARADC.SetONETIME_SAMPLE_SARADC_ONETIME_ATTEN(atten11dB)

// Select ADC1 one-time mode
esp.APB_SARADC.SetONETIME_SAMPLE_SARADC1_ONETIME_SAMPLE(1)

// Trigger one-time conversion
esp.APB_SARADC.SetONETIME_SAMPLE_SARADC_ONETIME_START(0)
// No delay needed here because adc_ctrl_clk is fast (>= APB_CLK_FREQ/8).
// ESP-IDF evaluates this condition and calls esp_rom_delay_us(0).
esp.APB_SARADC.SetONETIME_SAMPLE_SARADC_ONETIME_START(1)

for esp.APB_SARADC.GetINT_RAW_APB_SARADC1_DONE_INT_RAW() == 0 {
}
raw := esp.APB_SARADC.GetSAR1DATA_STATUS_APB_SARADC1_DATA()
esp.APB_SARADC.SetONETIME_SAMPLE_SARADC_ONETIME_START(0)
esp.APB_SARADC.SetONETIME_SAMPLE_SARADC1_ONETIME_SAMPLE(0)

return uint16(raw&0xfff) << 4
}

Expand Down Expand Up @@ -390,3 +429,118 @@ func c6ApplyADC1Code(reg regI2C, code uint32) {
reg.setCalibrationParam(0, code)
reg.calibrationFinish(0)
}

// ── Modem Clock for ADC ──────────────────────────────────────────────────────

// Enable the clock for the shared ADC and Front-End (FE) controller logic.
// ESP-IDF initializes this during ADC setup with:
// modem_clock_module_enable(PERIPH_MODEM_ADC_COMMON_FE_MODULE);
// (see esp_hw_support/port/esp32c6/sar_periph_ctrl.c).
func modemClockModuleEnableForADC() {
initModemClocks()

// Enable Modem Clk - ADC ( => see modem_clock_device_enable(ctx, 1))
esp.MODEM_SYSCON.SetCLK_CONF1_CLK_FE_APB_EN(1)
esp.MODEM_SYSCON.SetCLK_CONF1_CLK_FE_80M_EN(1)
}

type c6ModemClockDomain int

const (
modemClockDomainModemAPB c6ModemClockDomain = iota // see hal/include/hal/modem_clock_types.h
modemClockDomainModemPeriph
modemClockDomainWiFi
modemClockDomainBT
modemClockDomainModemFE
modemClockDomainIEEE802154
modemClockDomainLPAPB
modemClockDomainI2CMaster
modemClockDomainCoex
modemClockDomainWiFiPwr
modemClockDomainMax
)

const (
pmuHpIcgModemCodeSleep = 0 // see esp_hw_support/include/esp_private/esp_pmu.h
pmuHpIcgModemCodeModem = 1
pmuHpIcgModemCodeActive = 2
)

// The ICG code's bit 0, 1 and 2 indicates the ICG state
// of pmu SLEEP, MODEM and ACTIVE mode respectively
const (
icgNogatingActive = 1 << pmuHpIcgModemCodeActive // see esp_hw_support/modem_clock.c
icgNogatingSleep = 1 << pmuHpIcgModemCodeSleep
icgNogatingModem = 1 << pmuHpIcgModemCodeModem
)

// Set default ICG (Internal Clock Gating) bitmaps for modem modules (ADC, WiFi, BT, 802.15.4).
// Ported from modem_clock_module_icg_map_init_all()
// TODO: Consider moving this code into a global ESP32-C6 peripheral setup.
func initModemClocks() {
for domain := modemClockDomainModemAPB; domain < modemClockDomainMax; domain++ {
bitmap := getInitialGatingMode(domain)
modemClockSetClockDomainICGBitmap(domain, bitmap)
}
}

// getInitialGatingMode returns the baseline gating configuration for a given modem clock domain.
// Replaces ESP-IDF's static DRAM array `initial_gating_mode` with a switch statement to avoid
// allocating DRAM in embedded environments.
// See: esp_hw_support/modem_clock.c, void modem_clock_module_icg_map_init_all()
func getInitialGatingMode(domain c6ModemClockDomain) uint32 {
switch domain {
case modemClockDomainModemAPB:
return esp.MODEM_SYSCON.GetCLK_CONF_POWER_ST_CLK_MODEM_APB_ST_MAP() | icgNogatingActive | icgNogatingModem
case modemClockDomainModemPeriph:
return esp.MODEM_SYSCON.GetCLK_CONF_POWER_ST_CLK_MODEM_PERI_ST_MAP() | icgNogatingActive
case modemClockDomainWiFi:
return esp.MODEM_SYSCON.GetCLK_CONF_POWER_ST_CLK_WIFI_ST_MAP() | icgNogatingActive | icgNogatingModem
case modemClockDomainBT:
return esp.MODEM_SYSCON.GetCLK_CONF_POWER_ST_CLK_BT_ST_MAP() | icgNogatingActive
case modemClockDomainModemFE:
return esp.MODEM_SYSCON.GetCLK_CONF_POWER_ST_CLK_FE_ST_MAP() | icgNogatingActive | icgNogatingModem
case modemClockDomainIEEE802154:
return esp.MODEM_SYSCON.GetCLK_CONF_POWER_ST_CLK_ZB_ST_MAP() | icgNogatingActive
case modemClockDomainLPAPB:
return esp.MODEM_LPCON.GetCLK_CONF_POWER_ST_CLK_LP_APB_ST_MAP() | icgNogatingActive | icgNogatingModem
case modemClockDomainI2CMaster:
return esp.MODEM_LPCON.GetCLK_CONF_POWER_ST_CLK_I2C_MST_ST_MAP() | icgNogatingActive | icgNogatingModem
case modemClockDomainCoex:
return esp.MODEM_LPCON.GetCLK_CONF_POWER_ST_CLK_COEX_ST_MAP() | icgNogatingActive | icgNogatingModem
case modemClockDomainWiFiPwr:
return esp.MODEM_LPCON.GetCLK_CONF_POWER_ST_CLK_WIFIPWR_ST_MAP() | icgNogatingActive | icgNogatingModem
}
return 0
}

// see hal/esp32c6/modem_clock_hal.c
// see soc/esp32c6/include/modem/modem_syscon_struct.h for SysconDev
// see hal/esp32c6/include/hal/modem_lpcon_ll.h for LPConDev
// see hal/esp32c6/include/hal/modem_syscon_ll.h for definition of modem_syscon_ll_get_modem_apb_icg_bitmap, ...
func modemClockSetClockDomainICGBitmap(domain c6ModemClockDomain, bitmap uint32) {
switch domain {
case modemClockDomainModemAPB:
esp.MODEM_SYSCON.SetCLK_CONF_POWER_ST_CLK_MODEM_APB_ST_MAP(bitmap)
case modemClockDomainModemPeriph:
esp.MODEM_SYSCON.SetCLK_CONF_POWER_ST_CLK_MODEM_PERI_ST_MAP(bitmap)
case modemClockDomainWiFi:
esp.MODEM_SYSCON.SetCLK_CONF_POWER_ST_CLK_WIFI_ST_MAP(bitmap)
case modemClockDomainBT:
esp.MODEM_SYSCON.SetCLK_CONF_POWER_ST_CLK_BT_ST_MAP(bitmap)
case modemClockDomainModemFE:
esp.MODEM_SYSCON.SetCLK_CONF_POWER_ST_CLK_FE_ST_MAP(bitmap)
case modemClockDomainIEEE802154:
esp.MODEM_SYSCON.SetCLK_CONF_POWER_ST_CLK_ZB_ST_MAP(bitmap)
case modemClockDomainLPAPB:
esp.MODEM_LPCON.SetCLK_CONF_POWER_ST_CLK_LP_APB_ST_MAP(bitmap)
case modemClockDomainI2CMaster:
esp.MODEM_LPCON.SetCLK_CONF_POWER_ST_CLK_I2C_MST_ST_MAP(bitmap)
case modemClockDomainCoex:
esp.MODEM_LPCON.SetCLK_CONF_POWER_ST_CLK_COEX_ST_MAP(bitmap)
case modemClockDomainWiFiPwr:
esp.MODEM_LPCON.SetCLK_CONF_POWER_ST_CLK_WIFIPWR_ST_MAP(bitmap)
}
}

// ── Modem Clock for ADC END ──────────────────────────────────────────────────
Loading