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
158 changes: 144 additions & 14 deletions src/machine/machine_esp32c6_adc.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,27 +21,41 @@ const (
atten11dB = 3
)

// InitADC initialises the APB_SARADC peripheral on ESP32-C6.
// 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() {
esp.PCR.SetSARADC_CONF_SARADC_CLK_EN(1)

// Reset and enable the SARADC bus clock via PCR.
esp.PCR.SetSARADC_CONF_SARADC_REG_CLK_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)

// Select clock source 2 (PLL_F80M), 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)
esp.PCR.SetSARADC_CONF_SARADC_REG_RST_EN(1)
esp.PCR.SetSARADC_CONF_SARADC_REG_RST_EN(0)

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)

// Select clock source 1 (PLL_F80M)
esp.PCR.SetSARADC_CLKM_CONF_SARADC_CLKM_SEL(1)
// At boot we have the CLKM values (4, 0, 0) but in ADC one-shot mode
// the CLKM values are changed to (15, 1, 0) (source: esp-idf hal/adc_oneshot_hal.c)
esp.PCR.SetSARADC_CLKM_CONF_SARADC_CLKM_DIV_NUM(15) // see ADC_LL_CLKM_DIV_NUM_DEFAULT hal/esp32c6/include/hal/adc_ll.h
esp.PCR.SetSARADC_CLKM_CONF_SARADC_CLKM_DIV_B(1)
esp.PCR.SetSARADC_CLKM_CONF_SARADC_CLKM_DIV_A(0)
esp.PCR.SetSARADC_CLKM_CONF_SARADC_CLKM_EN(1)

// Power up the SAR ADC and configure FSM timing (same register layout as C3).
esp.APB_SARADC.SetCTRL_SARADC_XPD_SAR_FORCE(1)
esp.APB_SARADC.SetFSM_WAIT_SARADC_XPD_WAIT(8)
esp.APB_SARADC.SetFSM_WAIT_SARADC_RSTB_WAIT(8)
esp.APB_SARADC.SetFSM_WAIT_SARADC_STANDBY_WAIT(100)
// Power up the SAR ADC in software mode (3), no FSM config needed, FSM_REG defaults to 0x00FF0808
esp.APB_SARADC.SetCTRL_SARADC_XPD_SAR_FORCE(3)
esp.APB_SARADC.SetCTRL_SARADC_SAR_CLK_GATED(1)
esp.APB_SARADC.SetCTRL_SARADC_SAR_CLK_DIV(1)

adcSelfCalibrate()
}
Expand All @@ -62,17 +76,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 +418,105 @@ func c6ApplyADC1Code(reg regI2C, code uint32) {
reg.setCalibrationParam(0, code)
reg.calibrationFinish(0)
}

// Enables the modem FE clock for the ADC.
// Ported from s_sar_power_acquire() in 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
)

// Sets the default modem ICG bitmaps.
// Ported from modem_clock_module_icg_map_init_all() in esp_hw_support/modem_clock.c.
func initModemClocks() {
for domain := modemClockDomainModemAPB; domain < modemClockDomainMax; domain++ {
bitmap := getInitialGatingMode(domain)
modemClockSetClockDomainICGBitmap(domain, bitmap)
}
}

// Ported from initial_gating_mode in esp_hw_support/modem_clock.c.
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
}

// Ported from hal/esp32c6/modem_clock_hal.c.
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)
}
}