一、工具
1、硬件:GD32F30x系列单片机
2、编译环境:KEIL
二、需求分析
使用单片机的ADC多通道采集不同的电压值。因为采集的通道比较多,为了能够迅速转换每个通道这里我使用了ADC的“规则并行模式”。
对于“规则并行模式”,官方文档是如下解释:
同时也给出了基于16个通道的规则并行模式采样和转换图,如下图所示:
三、代码实现
1、ADC通道引脚配置
/** * @brief ADC01 引脚配置 * @retval none * @author Mr.W * @date 2020-11-10 */ static void bsp_adc01_gpio_cfg(void) { /* enable GPIOA GPIOB GPIOC clock */ rcu_periph_clock_enable(RCU_GPIOA); rcu_periph_clock_enable(RCU_GPIOB); rcu_periph_clock_enable(RCU_GPIOC); /* enable alternate function clock */ rcu_periph_clock_enable(RCU_AF); /* configure ADC GPIO port */ gpio_init(RCU_GPIOA, GPIO_MODE_AIN, GPIO_OSPEED_50MHZ, GPIO_PIN_4 | GPIO_PIN_5 | GPIO_PIN_6 | GPIO_PIN_7); gpio_init(RCU_GPIOB, GPIO_MODE_AIN, GPIO_OSPEED_50MHZ, GPIO_PIN_0 | GPIO_PIN_1); gpio_init(RCU_GPIOC, GPIO_MODE_AIN, GPIO_OSPEED_50MHZ, GPIO_PIN_2 | GPIO_PIN_4 | GPIO_PIN_5); }
2、ADC功能配置,为了能够获取每个通道实时的电压值,需要打开“扫描模式”和“连续转换模式”。经过测试发现规则同步模式下是不能转换内部参考电压的。
/** * @brief ADC01 配置 * @retval none * @author Mr.W * @date 2020-11-10 */ static void bsp_adc01_cfg(void) { /* enable ADC0 and ADC1 clock */ rcu_periph_clock_enable(RCU_ADC0); rcu_periph_clock_enable(RCU_ADC1); /* config ADC clock */ rcu_adc_clock_config(RCU_CKADC_CKAPB2_DIV4); /* ADC continous function enable */ adc_special_function_config(ADC0, ADC_SCAN_MODE, ENABLE); adc_special_function_config(ADC0, ADC_CONTINUOUS_MODE, ENABLE); adc_special_function_config(ADC1, ADC_SCAN_MODE, ENABLE); adc_special_function_config(ADC1, ADC_CONTINUOUS_MODE, ENABLE); /* ADC trigger config */ adc_external_trigger_source_config(ADC0, ADC_REGULAR_CHANNEL, ADC0_1_2_EXTTRIG_REGULAR_NONE); adc_external_trigger_source_config(ADC1, ADC_REGULAR_CHANNEL, ADC0_1_2_EXTTRIG_REGULAR_NONE); /* ADC data alignment config */ adc_data_alignment_config(ADC0, ADC_DATAALIGN_RIGHT); adc_data_alignment_config(ADC1, ADC_DATAALIGN_RIGHT); /* ADC mode config,使用规则同步模式,ADC0是主,ADC1是从, 同时转换两个通道(同时转换的通道不能相同) */ adc_mode_config(ADC_DAUL_REGULAL_PARALLEL); /* ADC channel length config */ adc_channel_length_config(ADC0, ADC_REGULAR_CHANNEL, 5); adc_channel_length_config(ADC1, ADC_REGULAR_CHANNEL, 5); /* ADC regular channel config,一个通道转换时长是2.06us */ adc_regular_channel_config(ADC0, 0, ADC_CHANNEL_4, ADC_SAMPLETIME_55POINT5); adc_regular_channel_config(ADC0, 1, ADC_CHANNEL_5, ADC_SAMPLETIME_55POINT5); adc_regular_channel_config(ADC0, 2, ADC_CHANNEL_6, ADC_SAMPLETIME_55POINT5); adc_regular_channel_config(ADC0, 3, ADC_CHANNEL_7, ADC_SAMPLETIME_55POINT5); adc_regular_channel_config(ADC0, 4, ADC_CHANNEL_8, ADC_SAMPLETIME_55POINT5); adc_regular_channel_config(ADC1, 0, ADC_CHANNEL_9, ADC_SAMPLETIME_55POINT5); adc_regular_channel_config(ADC1, 1, ADC_CHANNEL_12, ADC_SAMPLETIME_55POINT5); adc_regular_channel_config(ADC1, 2, ADC_CHANNEL_14, ADC_SAMPLETIME_55POINT5); adc_regular_channel_config(ADC1, 3, ADC_CHANNEL_15, ADC_SAMPLETIME_55POINT5); adc_regular_channel_config(ADC1, 4, ADC_CHANNEL_5, ADC_SAMPLETIME_55POINT5); /* ADC external trigger enable */ adc_external_trigger_config(ADC0, ADC_REGULAR_CHANNEL, ENABLE); adc_external_trigger_config(ADC1, ADC_REGULAR_CHANNEL, ENABLE); /* enable ADC0 interface */ adc_enable(ADC0); delay_ms(0xFFFF); /* ADC0 calibration and reset calibration */ adc_calibration_enable(ADC0); /* enable ADC1 interface */ adc_enable(ADC1); delay_ms(0xFFFF); /* ADC1 calibration and reset calibration */ adc_calibration_enable(ADC1); /* ADC DMA function enable */ adc_dma_mode_enable(ADC0); /* ADC0 software trigger enable */ adc_software_trigger_enable(ADC0, ADC_REGULAR_CHANNEL); }
3、ADC的DMA配置
uint32_t adc01_value[5];
/** * @brief ADC01 DMA配置 * @retval none * @author Mr.W * @date 2020-11-10 */ void bsp_adc01_dma_cfg(void) { /* ADC_DMA_channel configuration */ dma_parameter_struct dma_data_parameter; /* enable DMA0 clock */ rcu_periph_clock_enable(RCU_DMA0); /* ADC DMA_channel configuration */ dma_deinit(DMA0, DMA_CH0); /* initialize DMA data mode */ dma_data_parameter.periph_addr = (uint32_t)(&ADC_RDATA(ADC0)); dma_data_parameter.periph_inc = DMA_PERIPH_INCREASE_DISABLE; dma_data_parameter.memory_addr = (uint32_t)(&adc01_value); dma_data_parameter.memory_inc = DMA_MEMORY_INCREASE_ENABLE; dma_data_parameter.periph_width = DMA_PERIPHERAL_WIDTH_32BIT; dma_data_parameter.memory_width = DMA_MEMORY_WIDTH_32BIT; dma_data_parameter.direction = DMA_PERIPHERAL_TO_MEMORY; dma_data_parameter.number = 5; /* ADC通道数量 */ dma_data_parameter.priority = DMA_PRIORITY_HIGH; dma_init(DMA0, DMA_CH0, &dma_data_parameter); dma_circulation_enable(DMA0, DMA_CH0); /* enable DMA channel */ dma_channel_enable(DMA0, DMA_CH0); }
4、ADC初始化
/** * @brief ADC01 初始化 * @retval none * @author Mr.W * @date 2020-11-10 */ void bsp_adc01_init(void) { bsp_adc01_gpio_cfg(); bsp_adc01_dma_cfg(); bsp_adc01_cfg(); }
5、获取各通道ADC值
/** * @brief 获取测量端adc值 * @retval none * @author Mr.W * @date 2020-11-10 */ uint16_t adc01_get_mea_adc_value(void) { return (uint16_t)(adc01_value[0]&0xFFFF); } /** * @brief 获取bat端adc值 * @retval none * @author Mr.W * @date 2020-11-10 */ uint16_t adc01_get_bat_adc_value(void) { return (uint16_t)(adc01_value[1]&0xFFFF); } /** * @brief 获取校准端adc值 * @retval none * @author Mr.W * @date 2020-11-10 */ uint16_t adc01_get_cal_adc_value(void) { return (uint16_t)(adc01_value[2]&0xFFFF); } /** * @brief 获取参比端adc值 * @retval none * @author Mr.W * @date 2020-11-10 */ uint16_t adc01_get_ref_adc_value(void) { return (uint16_t)(adc01_value[3]&0xFFFF); } /** * @brief 获取-5V端adc值 * @retval none * @author Mr.W * @date 2020-11-10 */ uint16_t adc01_get_minus_5v_adc_value(void) { return (uint16_t)(adc01_value[4]&0xFFFF); } /** * @brief 获取+5V端adc值 * @retval none * @author Mr.W * @date 2020-11-10 */ uint16_t adc01_get_pluse_5v_adc_value(void) { return (uint16_t)(adc01_value[0] >> 16);; } /** * @brief 获取8.5V端adc值 * @retval none * @author Mr.W * @date 2020-11-10 */ uint16_t adc01_get_8v5_adc_value(void) { return (uint16_t)(adc01_value[1] >> 16);; } /** * @brief 获取+12V端adc值 * @retval none * @author Mr.W * @date 2020-11-10 */ uint16_t adc01_get_pluse_12v_adc_value(void) { return (uint16_t)(adc01_value[2] >> 16);; } /** * @brief 获取-12V端adc值 * @retval none * @author Mr.W * @date 2020-11-10 */ uint16_t adc01_get_minus_12v_adc_value(void) { return (uint16_t)(adc01_value[3] >> 16);; } /** * @brief 获取bat'端adc值 * @retval none * @author Mr.W * @date 2020-11-19 */ uint16_t adc01_get_batn_adc_value(void) { return (uint16_t)(adc01_value[4] >> 16);; }
#endif