Analog to Digital Converter(ADC)
1、Overview of ADC
- ADC is a module in CI130X chip to realize analog digital conversion function. It is mainly used to convert analog quantity of voltage value into digital quantity for CPU to monitor voltage or other processing.
2、ADC characteristics
- 4-channel, 12 bit resolution, only supports single input channel, SAR ADC;
- The sampling rate can reach 1 MSPS (15 cycles are required for one conversion, so to achieve the sampling rate of 1 MSPS, the clock frequency of the ADC must be 15 MHz at this time);
- At 1MSPS, the working current is 450uA; The current is less than 1uA when it is turned off;
- Analog inputs range from VREFL to VREF.
- There are two types of interrupts. One is that the ADC sampling value is abnormal and triggers the interrupt. The other is that the ADC will trigger interrupts every time it completes a conversion. In this case, the ADC triggers interrupts too frequently, which will have a certain impact on the whole system. Therefore, the ADC interrupt mode only uses the case of abnormal sampling values.
- CIC130X ADC has 4 external channels_ CHANNEL_ 2(AIN2)、ADC_ CHANNEL_ 3(AIN3)、ADC_ CHANNEL_ 4(AIN4)、ADC_ CHANNEL_ 5(AIN5)
3、Example
3.1、Continuous conversion mode
- In the case of continuous sampling mode, the ADC will work all the time after it is started. If the user wants to read the ADC conversion result at some time, he can directly call the ADC_ get_ The result function.
#include "ci130x_system.h"
#include <string.h>
#include "ci130x_core_eclic.h"
#include "ci_log.h"
#include "ci130x_scu.h"
#include "ci130x_dpmu.h"
#include "ci130x_adc.h"
uint32_t adc_current[502] = {0};
void adc_series_mode_test(void)
{
uint32_t recv_count = 0;
uint32_t temp;
uint32_t val;
scu_set_device_gate(HAL_ADC_BASE,1); //Turn on ADC clock
dpmu_set_adio_reuse(PC1,ANALOG_MODE); //Configure the pin of ADC channel 5 as an analog function. For more information, see ci130x_ IO reuse comment of sch. h
dpmu_set_io_pull(PC1,DPMU_IO_PULL_DISABLE); //Close the pull-up and pull-down inside the pin
eclic_irq_enable(ADC_IRQn); //Enable interrupt
__eclic_irq_set_vector(ADC_IRQn, (int32_t)ADC_irqhandle); //Specify interrupt service function
adc_poweron();
adc_series_mode(ADC_CHANNEL_5); //Initialize to continuous mode
for(;;)
{
for(int i = 0;i < 200;i++)
{
temp = adc_get_result(ADC_CHANNEL_5);
temp = temp*33000/4096;
temp /= 10;
adc_current[i] = temp;
vTaskDelay(pdMS_TO_TICKS(1));
}
for(int i = 0;i < 200;i++)
{
val += adc_current[i];
}
val /= 200;
mprintf("val:%dmv\n",val);
vTaskDelay(pdMS_TO_TICKS(500));
}
}
3.2、Common software trigger mode
- Common software trigger mode test requires software to write a trigger to collect data once, and then call adc_ get_ The result function gets the voltage.
#include "ci130x_system.h"
#include <string.h>
#include "ci130x_core_eclic.h"
#include "ci_log.h"
#include "ci130x_scu.h"
#include "ci130x_dpmu.h"
#include "ci130x_adc.h"
uint32_t adc_current[502] = {0};
void adc_signal_mode_test()
{
uint32_t temp;
uint32_t val;
scu_set_device_gate(HAL_ADC_BASE,1); //Turn on ADC clock
dpmu_set_adio_reuse(PC1,ANALOG_MODE); //Configure the pin of ADC channel 5 as an analog function. For more information, see ci130x_ IO reuse comment of sch. h
dpmu_set_io_pull(PC1,DPMU_IO_PULL_DISABLE); //Close the pull-up and pull-down inside the pin
eclic_irq_enable(ADC_IRQn); //Enable interrupt
__eclic_irq_set_vector(ADC_IRQn, (int32_t)ADC_irqhandle); //Specify interrupt service function
adc_poweron();
adc_signal_mode(ADC_CHANNEL_5); //Initialize to normal software trigger mode
for(;;)
{
for(int i = 0;i < 200;i++)
{
adc_soc_soft_ctrl(ENABLE); //ADC software trigger (software forced start conversion)
adc_wait_int(ADC_CHANNEL_5); //Wait for interrupt
temp = adc_get_result(ADC_CHANNEL_5); //Obtain the instantaneous voltage value
temp = temp*33000/4096;
temp /= 10;
adc_current[i] = temp;
vTaskDelay(pdMS_TO_TICKS(2));
}
for(int i = 0;i < 200;i++)
{
val += adc_current[i];
}
val /= 200;
mprintf("val:%dmv\n",val);
vTaskDelay(pdMS_TO_TICKS(500));
}
}
3.3、Cycle switching mode
- In the cycle conversion mode, ADC will convert once per cycle according to the set cycle. If ADC interrupt is turned on, the conversion will trigger the interrupt.
#include "ci130x_system.h"
#include <string.h>
#include "ci130x_core_eclic.h"
#include "ci_log.h"
#include "ci130x_scu.h"
#include "ci130x_dpmu.h"
#include "ci130x_adc.h"
uint32_t adc_current[502] = {0};
void adc_cycle_mode_test(void)
{
uint32_t temp;
uint32_t val;
extern uint8_t adc_cha5_int_flag; //Flag for querying abnormal interrupts
scu_set_device_gate(HAL_ADC_BASE,1); //Turn on ADC clock
dpmu_set_adio_reuse(PC1,ANALOG_MODE); //Configure the pin of ADC channel 5 as an analog function. For more information, see ci130x_ IO reuse comment of sch. h
dpmu_set_io_pull(PC1,DPMU_IO_PULL_DISABLE); //Close the pull-up and pull-down inside the pin
eclic_irq_enable(ADC_IRQn); //Enable interrupt
__eclic_irq_set_vector(ADC_IRQn, (int32_t)ADC_irqhandle); //Specify interrupt service function
adc_poweron();
adc_cycle_mode(ADC_CHANNEL_5,1000); //Configuration cycle (1000 + 1) * 512
adc_channel_min_value(ADC_CHANNEL_5,1241); //Minimum threshold setting, width range is 0~4095
adc_channel_min_value_int(ADC_CHANNEL_5,ENABLE);
adc_channel_max_value(cha,4095); //Maximum threshold setting, width range is 0~4095
adc_channel_max_value_int(cha,ENABLE);
for(;;)
{
for(int i = 0;i < 200;i++)
{
temp = adc_get_result(ADC_CHANNEL_5);
temp = temp*33000/4096;
temp /= 10;
adc_current[i] = temp;
vTaskDelay(pdMS_TO_TICKS(1));
}
if(adc_cha5_int_flag)
{
mprintf("电压异常!!\n"); //If the value beyond the threshold value is collected, an exception is reported
adc_cha5_int_flag = 0;
}
for(int i = 0;i < 200;i++)
{
val += adc_current[i];
}
val /= 200;
mprintf("val:%dmv\n",val);
vTaskDelay(pdMS_TO_TICKS(500));
}
}
3.4、Calibration mode
- In the calibration mode, the ADC will not collect the voltage connected to the pin, but test a voltage reference inside the ADC. In the calibration mode, the sampling value of the ADC should be constant near (0xfff/2).
#include "ci130x_system.h"
#include <string.h>
#include "ci130x_core_eclic.h"
#include "ci_log.h"
#include "ci130x_scu.h"
#include "ci130x_dpmu.h"
#include "ci130x_adc.h"
uint32_t adc_test_count = 10000;
uint32_t adc_current[502] = {0};
void adc_correct_test(void)
{
uint32_t recv_count = 0;
uint32_t temp;
uint32_t val;
scu_set_device_gate(HAL_ADC_BASE,1); //Turn on ADC clock
dpmu_set_adio_reuse(PC1,ANALOG_MODE); //Configure the pin of ADC channel 5 as an analog function. For more information, see ci130x_ IO reuse comment of sch. h
eclic_irq_enable(ADC_IRQn);
__eclic_irq_set_vector(ADC_IRQn, (int32_t)ADC_irqhandle); //Specify interrupt service function
adc_poweron();
adc_caculate_mode(); //Turn on ADC check mode
adc_signal_mode(ADC_CHANNEL_5); Configure single conversion mode
for(;;)
{
for(recv_count=0;recv_count<adc_test_count;recv_count++)
{
for(int i = 0;i < 200;i++)
{
adc_soc_soft_ctrl(ENABLE);
adc_wait_int(ADC_CHANNEL_5);
temp = adc_get_result(ADC_CHANNEL_5);
temp = temp*33000/4096;
temp /= 10;
adc_current[i] = temp;
vTaskDelay(pdMS_TO_TICKS(1));
}
for(int i = 0;i < 200;i++)
{
val += adc_current[i];
}
val /= 200;
mprintf("第%d次val:%dmv\n",recv_count,val);
}
}
}
3.5、Internal ADC voltage detection
- Internal adc voltage detection: detect the voltage once a cycle, and then call ADC_ get_ The result function gets the voltage.
#include "ci130x_system.h"
#include <string.h>
#include "ci130x_core_eclic.h"
#include "ci_log.h"
#include "ci130x_scu.h"
#include "ci130x_dpmu.h"
#include "ci130x_adc.h"
void adc_vol_test()
{
uint32_t temp;
scu_set_device_gate(HAL_ADC_BASE,0);
scu_set_device_reset(HAL_ADC_BASE);
scu_para_en_disable(HAL_ADC_BASE);
scu_set_div_parameter(HAL_ADC_BASE,12);
scu_para_en_enable(HAL_ADC_BASE);
scu_set_device_reset_release(HAL_ADC_BASE);
scu_set_device_gate(HAL_ADC_BASE,1);
dpmu_unlock_cfg_config();//Unlock
dpmu_pmu_div_resistance_en(ENABLE);//Partial resistance enable
dpmu_config_update_en(DPMU_UPDATE_EN_NUM_LDO1);//LDO configuration update enable
dpmu_config_update_en(DPMU_UPDATE_EN_NUM_LDO2);
dpmu_config_update_en(DPMU_UPDATE_EN_NUM_LDO3);
dpmu_config_update_en(DPMU_UPDATE_EN_NUM_VDT);
dpmu_config_update_en(DPMU_UPDATE_EN_NUM_TRIM);
dpmu_lock_cfg_config();
eclic_irq_enable(ADC_IRQn); //Enable interrupt
__eclic_irq_set_vector(ADC_IRQn, (int32_t)ADC_irqhandle); //Specify interrupt service function
adc_poweron();
for(;;)
{
adc_cycle_mode(ADC_CHANNEL_1,10);
for(int i = 0;i < 2000;i++)
{
temp = adc_get_result(ADC_CHANNEL_1);
temp = temp*33000/4096;
temp /= 10;
mprintf("%d:%d\n",ADC_CHANNEL_1,temp);
}
}
}