From 66023da10c0de8a599d1db9eb3163646b6dfca0e Mon Sep 17 00:00:00 2001 From: Dong Heng Date: Thu, 14 Oct 2021 20:22:24 +0800 Subject: [PATCH] risc-v/esp32c3: Refactor ADC calibration Use calibration parameters from efuse rather than self-calibration. --- arch/risc-v/src/esp32c3/esp32c3_adc.c | 175 ++++++++++++++++++++------ 1 file changed, 140 insertions(+), 35 deletions(-) diff --git a/arch/risc-v/src/esp32c3/esp32c3_adc.c b/arch/risc-v/src/esp32c3/esp32c3_adc.c index 976a062db7..fa374381e0 100644 --- a/arch/risc-v/src/esp32c3/esp32c3_adc.c +++ b/arch/risc-v/src/esp32c3/esp32c3_adc.c @@ -38,6 +38,7 @@ #include "esp32c3_adc.h" #include "hardware/esp32c3_system.h" +#include "hardware/esp32c3_efuse.h" #include "hardware/esp32c3_saradc.h" #include "hardware/esp32c3_gpio_sigmap.h" #include "hardware/regi2c_ctrl.h" @@ -63,6 +64,16 @@ #define ADC_VAL_MASK (0xfff) +#define ADC_CAL_BASE_REG EFUSE_RD_SYS_DATA_PART1_0_REG + +#define ADC_CAL_VER_OFF (128) +#define ADC_CAL_VER_LEN (3) + +#define ADC_CAL_DATA_LEN (10) +#define ADC_CAL_DATA_COMP (1000) + +#define ADC_CAL_VOL_LEN (10) + /* ADC input voltage attenuation, this affects measuring range */ #define ADC_ATTEN_DB_0 (0) /* Vmax = 800 mV */ @@ -74,16 +85,32 @@ #if defined(CONFIG_ESP32C3_ADC_VOL_750) # define ADC_ATTEN_DEF ADC_ATTEN_DB_0 -# define ADC_VOL_VAL (750) + +# define ADC_CAL_DATA_OFF (148) +# define ADC_CAL_VOL_OFF (188) + +# define ADC_CAL_VOL_DEF (400) #elif defined(CONFIG_ESP32C3_ADC_VOL_1050) # define ADC_ATTEN_DEF ADC_ATTEN_DB_2_5 -# define ADC_VOL_VAL (1050) + +# define ADC_CAL_DATA_OFF (158) +# define ADC_CAL_VOL_OFF (198) + +# define ADC_CAL_VOL_DEF (550) #elif defined(CONFIG_ESP32C3_ADC_VOL_1300) # define ADC_ATTEN_DEF ADC_ATTEN_DB_6 -# define ADC_VOL_VAL (1300) + +# define ADC_CAL_DATA_OFF (168) +# define ADC_CAL_VOL_OFF (208) + +# define ADC_CAL_VOL_DEF (750) #elif defined(CONFIG_ESP32C3_ADC_VOL_2500) # define ADC_ATTEN_DEF ADC_ATTEN_DB_11 -# define ADC_VOL_VAL (2500) + +# define ADC_CAL_DATA_OFF (178) +# define ADC_CAL_VOL_OFF (218) + +# define ADC_CAL_VOL_DEF (1370) #endif #define ADC_WORK_DELAY (1) @@ -214,6 +241,10 @@ static struct adc_dev_s g_adc1_chan4_dev = static bool g_calibrated; +/* ADC calibration digital parameter */ + +static uint16_t g_cal_digit; + /* ADC clock reference */ static uint32_t g_clk_ref; @@ -224,6 +255,48 @@ static sem_t g_sem_excl = SEM_INITIALIZER(1); * Private Functions ****************************************************************************/ +/**************************************************************************** + * Name: read_efuse + * + * Description: + * Read Efuse data. + * + * Input Parameters: + * addr - register address + * b_off - bit offset + * b_size - bit size + * + * Returned Value: + * Efuse data. + * + ****************************************************************************/ + +static uint32_t read_efuse(uint32_t addr, uint32_t b_off, uint32_t b_size) +{ + uint32_t data; + uint32_t regval; + uint32_t shift = 32 - b_size; + uint32_t mask = UINT32_MAX >> shift; + uint32_t res = b_off % 32; + uint32_t regaddr = addr + (b_off / 32 * 4); + + regval = getreg32(regaddr); + data = regval >> res; + if (res <= shift) + { + data &= mask; + } + else + { + shift = 32 - res; + + regval = getreg32(regaddr + 4); + data |= (regval & (mask >> shift)) << shift; + } + + return data; +} + /**************************************************************************** * Name: adc_enable_clk * @@ -410,47 +483,74 @@ static void adc_calibrate(void) uint16_t adc_max = 0; uint16_t adc_min = UINT16_MAX; uint32_t adc_sum = 0; + uint32_t regval; - /* Enable Vdef */ - - rom_i2c_writereg_mask(I2C_ADC, I2C_ADC_HOSTID, - I2C_ADC1_DEF, I2C_ADC1_DEF_MSB, - I2C_ADC1_DEF_LSB, 1); - - /* Start sampling */ - - adc_samplecfg(ADC_CAL_CHANNEL); - - /* Enable internal connect GND (for calibration). */ - - rom_i2c_writereg_mask(I2C_ADC, I2C_ADC_HOSTID, - I2C_ADC1_ENCAL_GND, I2C_ADC1_ENCAL_GND_MSB, - I2C_ADC1_ENCAL_GND_LSB, 1); - - for (int i = 1; i < ADC_CAL_CNT_MAX ; i++) + regval = read_efuse(ADC_CAL_BASE_REG, ADC_CAL_VER_OFF, ADC_CAL_VER_LEN); + if (regval == 1) { - adc_set_calibration(0); - adc = adc_read(); + ainfo("Calibrate based on efuse data\n"); - adc_sum += adc; - adc_max = MAX(adc, adc_max); - adc_min = MIN(adc, adc_min); + regval = read_efuse(ADC_CAL_BASE_REG, ADC_CAL_DATA_OFF, + ADC_CAL_DATA_LEN); + cali_val = regval + ADC_CAL_DATA_COMP; } + else + { + ainfo("Calibrate based on GND voltage\n"); - cali_val = (adc_sum - adc_max - adc_min) / (ADC_CAL_CNT_MAX - 2); + /* Enable Vdef */ - /* Disable internal connect GND (for calibration). */ + rom_i2c_writereg_mask(I2C_ADC, I2C_ADC_HOSTID, + I2C_ADC1_DEF, I2C_ADC1_DEF_MSB, + I2C_ADC1_DEF_LSB, 1); - rom_i2c_writereg_mask(I2C_ADC, I2C_ADC_HOSTID, - I2C_ADC1_ENCAL_GND, - I2C_ADC1_ENCAL_GND_MSB, - I2C_ADC1_ENCAL_GND_LSB, 0); + /* Start sampling */ + + adc_samplecfg(ADC_CAL_CHANNEL); + + /* Enable internal connect GND (for calibration). */ + + rom_i2c_writereg_mask(I2C_ADC, I2C_ADC_HOSTID, + I2C_ADC1_ENCAL_GND, I2C_ADC1_ENCAL_GND_MSB, + I2C_ADC1_ENCAL_GND_LSB, 1); + + for (int i = 1; i < ADC_CAL_CNT_MAX ; i++) + { + adc_set_calibration(0); + adc = adc_read(); + + adc_sum += adc; + adc_max = MAX(adc, adc_max); + adc_min = MIN(adc, adc_min); + } + + cali_val = (adc_sum - adc_max - adc_min) / (ADC_CAL_CNT_MAX - 2); + + /* Disable internal connect GND (for calibration). */ + + rom_i2c_writereg_mask(I2C_ADC, I2C_ADC_HOSTID, + I2C_ADC1_ENCAL_GND, + I2C_ADC1_ENCAL_GND_MSB, + I2C_ADC1_ENCAL_GND_LSB, 0); + } ainfo("calibration value: %" PRIu16 "\n", cali_val); /* Set final calibration parameters */ adc_set_calibration(cali_val); + + /* Set calibration digital parameters */ + + regval = read_efuse(ADC_CAL_BASE_REG, ADC_CAL_VOL_OFF, ADC_CAL_VOL_LEN); + if (regval & BIT(ADC_CAL_VOL_LEN - 1)) + { + g_cal_digit = 2000 - (regval & ~(BIT(ADC_CAL_VOL_LEN - 1))); + } + else + { + g_cal_digit = 2000 + regval; + } } /**************************************************************************** @@ -470,7 +570,8 @@ static void adc_calibrate(void) static void adc_read_work(struct adc_dev_s *dev) { int ret; - uint16_t adc; + uint32_t value; + int32_t adc; struct adc_chan_s *priv = (struct adc_chan_s *)dev->ad_priv; ret = sem_wait(&g_sem_excl); @@ -481,12 +582,15 @@ static void adc_read_work(struct adc_dev_s *dev) } adc_samplecfg(priv->channel); - adc = adc_read(); + value = adc_read(); + + adc = (int32_t)(value * (UINT16_MAX * ADC_CAL_VOL_DEF / g_cal_digit) / + UINT16_MAX); priv->cb->au_receive(dev, priv->channel, adc); ainfo("channel: %" PRIu8 ", voltage: %" PRIu32 " mV\n", priv->channel, - (uint32_t)adc * ADC_VOL_VAL / ADC_CAL_VAL_MAX); + adc); sem_post(&g_sem_excl); } @@ -579,6 +683,7 @@ static int adc_setup(struct adc_dev_s *dev) if (priv->ref > 0) { + priv->ref++; return OK; }