GD32L23X ADC+DMA读取多通道电压
ADC +DMA采样配置
D32L23x系列芯片上集成了一个12位连续近似模数转换器模块(ADC)。
是一种采用逐次逼近方式的模拟数字转换器。
可以从16个外部通道和4个内部通道的模拟信号进行采样。
20个ADC采样通道均支持多种操作模式。
经过采样和转换后,转换结果可以根据最小有效位对齐或最显著位对齐存储在相应的数据寄存器中。
ADC的位数是12位,参考电压为3.3V,那么其数字值变化1对应的模拟信号电压变化为3.3V/(2^12)≈0.8mV
采用一个ADC三个通道+DMA对其进行采样,经过测试有较为稳定的数据
ADC一般需要配置的内容包括:
- IO配置(时钟,模拟输入)
- ADC参数配置(模式-扫描模式,连续模式;触发方式;通道配置-规则组or注入组;ADC校正)
- 中断和DMA(使能)配置
【注】ADC的规则组和注入组的区别可简单理解为:规则组是周期执行的程序,注入组是中断程序
ADC 全部初始化,这里采用PA 1和6,PB1 作为电压读取通道
具体对应的通道需要去看datasheet, 如PA1如下
void ADC_Init(void)
{
// GPIO configure
rcu_periph_clock_enable(RCU_GPIOA);
rcu_periph_clock_enable(RCU_GPIOB);
gpio_mode_set(GPIOA, GPIO_MODE_ANALOG, GPIO_PUPD_NONE, GPIO_PIN_1 | GPIO_PIN_6);
gpio_mode_set(GPIOB, GPIO_MODE_ANALOG, GPIO_PUPD_NONE, GPIO_PIN_1);
// ADC configure
adc_configure();
// DMA configure
dma_adc_configure();
}
配置ADC
可编程采样时间计算
对于12位分辨率,总采样和转换时间为“采样时间+12.5”CK_ADC周期。例如: CK_ADC = 16MHz,采样时间为2.5个周期,总转换时间为“2.5+12.5”CK_ADC周期,(2.5+12.5)/16MHZ即0.9375us。
在本项目中:APB2为16Mhz
8分频
转换时间 = (1/2M)*(55.5+12.5)=34us
自校准功能
ADC带有一个前置校准功能。在校准期间,ADC计算一个校准系数,这个系数是应用于ADC内部的,它直到ADC下次掉电才无效。在校准期间,应用不能使用ADC,必须等到校准完成。在校准期间CLB位会一直保持1,直到校准完成,该位由硬件清0。 当ADC运行条件改变(例如,VDDA、VREF+以及温度等)或第一次开始A/D转换,建议执行一次校准操作
static void adc_configure(void)
{
// 16M/8=2Mhz ADC MAX CLOCK = 16Mhz
rcu_periph_clock_enable(RCU_ADC);
rcu_adc_clock_config(RCU_ADCCK_APB2_DIV8);
// ADC configure
adc_deinit();
adc_special_function_config(ADC_SCAN_MODE, ENABLE);
adc_special_function_config(ADC_CONTINUOUS_MODE, ENABLE);
adc_resolution_config(ADC_RESOLUTION_12B);
adc_data_alignment_config(ADC_DATAALIGN_RIGHT);
adc_channel_length_config(ADC_REGULAR_CHANNEL, 3);
adc_external_trigger_source_config(ADC_REGULAR_CHANNEL, ADC_EXTTRIG_REGULAR_NONE);
adc_external_trigger_config(ADC_REGULAR_CHANNEL, ENABLE);
// PA1 - AD1 change time = (1/2M)*(55.5+12.5)=34us
adc_regular_channel_config(0, ADC_CHANNEL_1, ADC_SAMPLETIME_55POINT5);
// PB1 - AD9
adc_regular_channel_config(1, ADC_CHANNEL_9, ADC_SAMPLETIME_55POINT5);
// PA6 - AD6
adc_regular_channel_config(2, ADC_CHANNEL_6, ADC_SAMPLETIME_55POINT5);
// adc enable
adc_enable();
adc_calibration_enable();
adc_dma_mode_enable();
adc_software_trigger_enable(ADC_REGULAR_CHANNEL);
}
配置DMA
static void dma_adc_configure(void)
{
// DMA configure
dma_parameter_struct dma_init_struct;
rcu_periph_clock_enable(RCU_DMA);
dma_deinit(DMA_CH0);
dma_init_struct.request = DMA_REQUEST_ADC;
dma_init_struct.direction = DMA_PERIPHERAL_TO_MEMORY;
dma_init_struct.number = 3;
dma_init_struct.periph_addr = (uint32_t) & (ADC_RDATA);
dma_init_struct.periph_inc = DMA_PERIPH_INCREASE_DISABLE;
dma_init_struct.periph_width = DMA_PERIPHERAL_WIDTH_16BIT;
dma_init_struct.memory_addr = (uint32_t)(&adc_datainfo.sampling_data);
dma_init_struct.memory_inc = DMA_MEMORY_INCREASE_ENABLE;
dma_init_struct.memory_width = DMA_MEMORY_WIDTH_16BIT;
dma_init_struct.priority = DMA_PRIORITY_HIGH;
dma_init(DMA_CH0, &dma_init_struct);
// dma enable
dma_circulation_enable(DMA_CH0);
dma_channel_enable(DMA_CH0);
}
ADC轮询读取
static uint16_t get_adc_ch(uint8_t i)
{
uint16_t value = 0;
// software enable
adc_software_trigger_enable(ADC_REGULAR_CHANNEL);
value = adc_datainfo.sampling_data[i];
adc_flag_clear(ADC_FLAG_STRC);
return value;
}
一些GD的ADC+DMA的坑
一定要配置为scan 和 continue 模式
adc_special_function_config(ADC_SCAN_MODE, ENABLE);
adc_special_function_config(ADC_CONTINUOUS_MODE, ENABLE);
配置规则通道之后需要不断地进行软件触发,才能收到数据,有些教程仅在初始化的时候触发一次,会导致只读到一次数据
adc_software_trigger_enable(ADC_REGULAR_CHANNEL);
3个ADC通道对应的GPIO需要去手册好好看看
// PA1 - AD1 change time = (1/2M)*(55.5+12.5)=34us
adc_regular_channel_config(0, ADC_CHANNEL_1, ADC_SAMPLETIME_55POINT5);
// PB1 - AD9
adc_regular_channel_config(1, ADC_CHANNEL_9, ADC_SAMPLETIME_55POINT5);
// PA6 - AD6
adc_regular_channel_config(2, ADC_CHANNEL_6, ADC_SAMPLETIME_55POINT5);
最好配置为circular模式,循环DMA读取。