前言:一般在使用ADC的时候都是采用软件启动转换,软件查询转换结果的方式,本文聊聊使用外部信号触发转换,转换完成后DMA搬运数据到内存,软件在需要的地方,仅需从内存中读取转换结果的方式来实现ADC转换。
1、引脚
硬件设计上必须按照手册要求,外部通道为标有ADCx_INxx的引脚可用于采样。
void adc_pin_init(void) {rcu_periph_clock_enable(RCU_GPIOA);gpio_mode_set(GPIOA, GPIO_MODE_ANALOG, GPIO_PUPD_NONE, GPIO_PIN_4 | GPIO_PIN_5 | GPIO_PIN_6);rcu_periph_clock_enable(RCU_GPIOB);gpio_mode_set(GPIOB, GPIO_MODE_ANALOG, GPIO_PUPD_NONE, GPIO_PIN_0);}
2、ADC外设初始化
void gd32_adc_init(void) {/* 时钟配置 */rcu_periph_clock_enable(RCU_ADC0);/* 重置ADC */adc_deinit();/* 时钟配置 */adc_clock_config(ADC_ADCCK_PCLK2_DIV4);//最大40M/* ADC模式配置 */adc_sync_mode_config(ADC_SYNC_MODE_INDEPENDENT);/* ADC连续转换功能配置 */adc_special_function_config(ADC0, ADC_CONTINUOUS_MODE, DISABLE);/* ADC扫描模式配置 */adc_special_function_config(ADC0, ADC_SCAN_MODE, ENABLE);/* ADC数据对齐配置 */adc_data_alignment_config(ADC0, ADC_DATAALIGN_RIGHT);/* ADC数据对齐配置 */adc_resolution_config(ADC0, ADC_RESOLUTION_12B);/* 使能17通道 */adc_channel_16_to_18(ADC_TEMP_VREF_CHANNEL_SWITCH, ENABLE);/* ADC通道长度配置 */adc_channel_length_config(ADC0, ADC_ROUTINE_CHANNEL, 5);/* ADC通道组配置 */adc_routine_channel_config(ADC0, 0, ADC_CHANNEL_6, ADC_SAMPLETIME_56);adc_routine_channel_config(ADC0, 1, ADC_CHANNEL_8, ADC_SAMPLETIME_56);adc_routine_channel_config(ADC0, 2, ADC_CHANNEL_4, ADC_SAMPLETIME_56);adc_routine_channel_config(ADC0, 3, ADC_CHANNEL_5, ADC_SAMPLETIME_56);adc_routine_channel_config(ADC0, 4, ADC_CHANNEL_17, ADC_SAMPLETIME_56);/* ADC外部触发配置 */adc_external_trigger_source_config(ADC0, ADC_ROUTINE_CHANNEL, ADC_EXTTRIG_ROUTINE_T1_TRGO);//触发源选择定时器1的TRGO信号adc_external_trigger_config(ADC0, ADC_ROUTINE_CHANNEL, EXTERNAL_TRIGGER_RISING);/* ADC DMA使能*/adc_dma_mode_enable(ADC0);adc_dma_request_after_last_enable(ADC_DEV); //关键/* 使能ADC接口 */adc_enable(ADC0);/* 短暂延时,确保ADC稳定 */gd32_delay_ms(20);/* ADC校准 */adc_calibration_enable(ADC0);}
3、DMA请求与配置
void gd32_adc_dma_init(void) {dma_single_data_parameter_struct dma_init_struct;rcu_periph_clock_enable(RCU_DMA1);dma_deinit(DMA1, DMA_CH4);dma_init_struct.direction = DMA_PERIPH_TO_MEMORY;dma_init_struct.memory0_addr = (uint32_t)ntc_val;dma_init_struct.memory_inc = DMA_MEMORY_INCREASE_ENABLE;dma_init_struct.number = 5 * sizeof(uint16_t);dma_init_struct.periph_addr = (uint32_t)(&ADC_RDATA(ADC_DEV));dma_init_struct.periph_inc = DMA_PERIPH_INCREASE_DISABLE;dma_init_struct.periph_memory_width = DMA_PERIPH_WIDTH_16BIT;dma_init_struct.priority = DMA_PRIORITY_ULTRA_HIGH;dma_init_struct.circular_mode = DMA_CIRCULAR_MODE_ENABLE;dma_single_data_mode_init(DMA1, DMA_CH4, &dma_init_struct);dma_channel_subperipheral_select(DMA1, DMA_CH4, DMA_SUBPERI0);dma_channel_enable(DMA1, DMA_CH4);}
代码中选择DMA1的外设0、通道4来转运ADC转换结构,转运方向为从寄存器到内存。
4、外部触发信号
void gd32_time_init(void){timer_parameter_struct para;rcu_periph_clock_enable(RCU_TIMER1);timer_deinit(TIMER1);/* 参数 */timer_struct_para_init(¶);para.alignedmode = TIMER_COUNTER_EDGE;para.counterdirection = TIMER_COUNTER_UP;para.clockdivision = TIMER_CKDIV_DIV1;para.repetitioncounter = 0U;para.prescaler = (1200 - 1) & 0x0ffff;para.period = (100 - 1) & 0x0ffff; //定时器频率120M/(1200*100)=1KHZ/* 配置 */timer_init(TIMER1, ¶);timer_update_event_enable(TIMER1);timer_auto_reload_shadow_enable(TIMER1);timer_master_output_trigger_source_select(TIMER1, TIMER_TRI_OUT_SRC_UPDATE); //关键,产生TRGO信号timer_enable(TIMER1);}
代码中选择使用定时器1的TRGO信号触发,ADC转换。
5、应用程序中循环读取结果
void adc_volt(void){float vref=1.2f;float volt=0;vref = 1.2f * 4096.0f / adc_val[4];//使用17通道校准外部参考电压for (uint8_t i = 0; i < 4; i++) {volt = adc_val[i] * vref / 4096.0f;dbg_info("volt %d =%.2f rn", i,volt);}//定时器触发了转换,因此只需要读取即可gd32_delay_ms(1000);}
通过以上代码,就实现了高效ADC转换。
3458
