芯片:GD32F350 运行在8M
目标:每10ms读取三个adc通道,并且通过DMA传输
流程
1.配置DMA传输
2.配置ADC模式和通道
3.配置定时器定时触发
4.中断函数和获取足够数据后进行数据处理
说明:本次使用ADC的定时扫描模式,由定时器触发ADC采集转换
ADC扫描模式预先设定好读取的通道,比如ch4 ch5 ch8,当触发adc时候就会连续采集三个通道的数据,通过dma传输到内存,过程(触发->4->5->8->触发->4->5->8->触发->4->5->8->触发)
注意是触发一次即转换三个通道的数据,而不是触发一次转换一个通道
1.配置DMA传输
//adc 的dma是否传输完成标志位
bool flag_DMA_ADC_accomplish = FALSE;
//dma缓冲区
uint16_t battery_ADC_value_arry[ADC_VALUE_ARRY_SIZE] = {0};
/* 使能dma时钟 */
rcu_periph_clock_enable(RCU_DMA);
/* dma初始化结构体 */
dma_parameter_struct dma_data_parameter;
/* 复位DAM通道 */
dma_deinit(DMA_CH0);
//ADC_RDATA是读取adc数据的寄存器
dma_data_parameter.periph_addr = (uint32_t)(&ADC_RDATA);
dma_data_parameter.periph_inc = DMA_PERIPH_INCREASE_DISABLE;
//battery_ADC_value_arry 是存储adc数据的内存数组
dma_data_parameter.memory_addr = (uint32_t)(&battery_ADC_value_arry);
dma_data_parameter.memory_inc = DMA_MEMORY_INCREASE_ENABLE;
dma_data_parameter.periph_width = DMA_PERIPHERAL_WIDTH_16BIT;
dma_data_parameter.memory_width = DMA_MEMORY_WIDTH_16BIT;
//从外设到内存
dma_data_parameter.direction = DMA_PERIPHERAL_TO_MEMORY;
//这里的150是设置dma搬运数据的个数即3个通道共读取150个数据,每个通道读取50个,完成后会触发中断
dma_data_parameter.number = 150;
dma_data_parameter.priority = DMA_PRIORITY_HIGH;
dma_init(DMA_CH0, &dma_data_parameter);
dma_circulation_enable(DMA_CH0);
//设置dma中断优先级
nvic_irq_enable(DMA_Channel0_IRQn, 0, 0);
/* enable DMA transfer complete interrupt */
dma_interrupt_enable(DMA_CH0, DMA_INT_FTF);
/* 使能通道 */
dma_channel_enable(DMA_CH0);
2.配置ADC模式和通道
#define ADC_DMA_OUTPUT_INDEX (0u)
#define ADC_DMA_BATTERY_INDEX (1u)
#define ADC_DMA_USB_INDEX (2u)
//开启时钟ADC的时钟
rcu_osci_on(RCU_IRC28M);
while(ERROR == rcu_osci_stab_wait(RCU_IRC28M));
/* 配置ADC时钟 */
rcu_adc_clock_config(RCU_ADCCK_IRC28M_DIV2);
/* 使能ADC时钟 */
rcu_periph_clock_enable(RCU_ADC);
//=========================================================
/* 端口时钟使能 */
rcu_periph_clock_enable(ADC_GPIO_CLK);
/* 配置读取ADC引脚 */
gpio_mode_set(ADC_GPIO_PORT, GPIO_MODE_ANALOG, GPIO_PUPD_NONE, ADC_GPIO_PIN);
//=========================================================
//连续转换关闭
adc_special_function_config(ADC_CONTINUOUS_MODE, DISABLE);
//打开扫描模式
adc_special_function_config(ADC_SCAN_MODE, ENABLE);
//=========================================================
/* ADC 触发模式 用定时器1通道触发*/
adc_external_trigger_source_config( ADC_REGULAR_CHANNEL, ADC_EXTTRIG_REGULAR_T1_CH1);
/* 数据对齐方式 */
adc_data_alignment_config(ADC_DATAALIGN_RIGHT);
/* ADC分辨率 12B */
adc_resolution_config(ADC_RESOLUTION_12B);
//=========================================================
/* ADC channel 个数配置 */
adc_channel_length_config( ADC_REGULAR_CHANNEL, _ADC_VALUE_NUMBER);
/* 配置ADC通道 */
adc_regular_channel_config(ADC_DMA_OUTPUT_INDEX , ADC_CHANNEL_4, ADC_SAMPLETIME_239POINT5); //输出功率
adc_regular_channel_config(ADC_DMA_BATTERY_INDEX, ADC_CHANNEL_5, ADC_SAMPLETIME_239POINT5); //电池通道
adc_regular_channel_config(ADC_DMA_USB_INDEX , ADC_CHANNEL_8, ADC_SAMPLETIME_239POINT5); //USB电压
//使能adc规则通道外部触发器
adc_external_trigger_config(ADC_REGULAR_CHANNEL, ENABLE);
/* 开启ADC DMA */
adc_dma_mode_enable();
/* ADC使能 */
adc_enable();
delay_1ms(2U);
/* ADC校准复位 */
adc_calibration_enable();
3.配置定时器定时触发
//将定时器配置成周期10ms
timer_oc_parameter_struct timer_ocintpara;
timer_parameter_struct timer_initpara;
rcu_periph_clock_enable(RCU_TIMER1);
/* TIMER1 configuration */
//这里需要注意!!!!!因为我的主频是8M所以定时器这样配置,如果不一样,需要根据实际频率来调节
timer_initpara.prescaler = 8-1;
timer_initpara.alignedmode = TIMER_COUNTER_EDGE;
timer_initpara.counterdirection = TIMER_COUNTER_UP;
timer_initpara.period = 10*1000;
timer_initpara.clockdivision = TIMER_CKDIV_DIV1;
timer_initpara.repetitioncounter = 0U;
timer_init(TIMER1,&timer_initpara);
/* CH0 configuration in PWM mode1 */
timer_ocintpara.ocpolarity = TIMER_OC_POLARITY_HIGH;
timer_ocintpara.outputstate = TIMER_CCX_ENABLE;
timer_channel_output_config(TIMER1, TIMER_CH_1, &timer_ocintpara);
//这里设置比较值是5*1000 5ms是触发点,但是由于是周期性,所以每次采集也是等间距的
timer_channel_output_pulse_value_config(TIMER1, TIMER_CH_1, 5*1000);
timer_channel_output_mode_config(TIMER1, TIMER_CH_1, TIMER_OC_MODE_PWM0);
timer_channel_output_shadow_config(TIMER1, TIMER_CH_1, TIMER_OC_SHADOW_DISABLE);
timer_auto_reload_shadow_enable(TIMER1);
// 可以尝试让他输出PWM用示波器查看配置是否正确
// gpio_mode_set(GPIOA, GPIO_MODE_AF, GPIO_PUPD_NONE, GPIO_PIN_0);
// gpio_output_options_set(GPIOA, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ,GPIO_PIN_0);
// gpio_af_set(GPIOA, GPIO_AF_2, GPIO_PIN_0);
4.中断函数和获取足够数据后进行数据处理
//当传输完成足够的数据后,就关闭定时器,设置标志位,进行处理,可以在中断中处理,
//也可以在其他地方处理,看个人习惯
void DMA_Channel0_IRQHandler(void)
{
//传输完成标志位
if(dma_interrupt_flag_get(DMA_CH0, DMA_INT_FLAG_FTF))
{
//设置标志位 DMA完成了
flag_DMA_ADC_accomplish = TRUE;
//关闭定时器,停止继续读取数据
timer_disable(TIMER1);
//清除全部标志位
dma_interrupt_flag_clear(DMA_CH0, DMA_INT_FLAG_G);
}
}
数据处理,这里我就做了个简单的求平均
//dma平均之后的值
uint32_t voltage_raw_value[3] = {0};
//DMA传输完成 这里进行转换
if(TRUE == flag_DMA_ADC_accomplish)
{
//清除之前的数据
memset(voltage_raw_value,0,sizeof(voltage_raw_value));
for(int i = 0;i < ADC_VALUE_ARRY_SIZE;i += 3)
{
voltage_raw_value[0] += battery_ADC_value_arry[i + 0];
voltage_raw_value[1] += battery_ADC_value_arry[i + 1];
voltage_raw_value[2] += battery_ADC_value_arry[i + 2];
}
}
float tmp = 0;
for(int i = 0;i < _ADC_VALUE_NUMBER;i += 1)
{
tmp = voltage_raw_value[i];
//这里除50是求平均值
tmp = tmp / 50;
voltage_raw_value[i] = (uint16_t)tmp;
}