使用Platformio平台的libopencm3开发框架来开发STM32G0,以下为多通道ADC与DMA的使用。
1 新建项目
在PIO的Home页面新建项目,项目名称adc_dma,选择开发板为 MonkeyPi_STM32_G070RB,开发框架选择libopencm3;
项目建立完成后在src目录下新建main.c主程序文件;
修改下载和调试方式,这里开发板使用的是DAPLink仿真器,因此修改platformio.ini文件如下:
1 2 upload_protocol = cmsis-dap debug_tool = cmsis-dap
2 编写程序 2.1 ADC 设置 这里设置PA0、PA1、PA2、PA3四个引脚为ADC:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 static void adc_setup (void ) { rcc_periph_clock_enable(RCC_ADC); rcc_periph_clock_enable(RCC_GPIOA); gpio_mode_setup(GPIOA, GPIO_MODE_ANALOG, GPIO_PUPD_NONE, GPIO0); gpio_mode_setup(GPIOA, GPIO_MODE_ANALOG, GPIO_PUPD_NONE, GPIO1); gpio_mode_setup(GPIOA, GPIO_MODE_ANALOG, GPIO_PUPD_NONE, GPIO2); gpio_mode_setup(GPIOA, GPIO_MODE_ANALOG, GPIO_PUPD_NONE, GPIO3); adc_power_off(ADC1); adc_set_clk_prescale(ADC1, ADC_CCR_PRESC_DIV2); adc_set_single_conversion_mode(ADC1); adc_set_right_aligned(ADC1); adc_set_sample_time_on_all_channels(ADC1, ADC_SMPTIME_160DOT5); uint8_t channel_array[16 ] = {0 }; channel_array[0 ] = 0 ; channel_array[1 ] = 1 ; channel_array[2 ] = 2 ; channel_array[3 ] = 3 ; adc_set_regular_sequence(ADC1, ADC_CHAN_CNT, channel_array); adc_enable_dma_circular_mode(ADC1); adc_set_resolution(ADC1, ADC_CFGR1_RES_12_BIT); adc_power_on(ADC1); delay_ms(10 ); }
2.2 DMA配置 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 static void dma_setup (void *data, int size) { dma_channel_reset(DMA1, DMA_CHANNEL1); dma_set_peripheral_address(DMA1, DMA_CHANNEL1, (uint32_t )&ADC_DR(ADC1)); dma_set_memory_address(DMA1, DMA_CHANNEL1, (uint32_t )data); dma_set_number_of_data(DMA1, DMA_CHANNEL1, size); dma_set_read_from_peripheral(DMA1, DMA_CHANNEL1); dma_enable_memory_increment_mode(DMA1, DMA_CHANNEL1); dma_set_peripheral_size(DMA1, DMA_CHANNEL1, DMA_CCR_PSIZE_16BIT); dma_set_memory_size(DMA1, DMA_CHANNEL1, DMA_CCR_MSIZE_16BIT); dma_enable_circular_mode(DMA1, DMA_CHANNEL1); dma_enable_transfer_complete_interrupt(DMA1, DMA_CHANNEL1); dma_enable_channel(DMA1, DMA_CHANNEL1); dmamux_reset_dma_channel(DMAMUX1, DMA_CHANNEL1); dmamux_set_dma_channel_request(DMAMUX1, DMA_CHANNEL1, DMAMUX_CxCR_DMAREQ_ID_ADC); }
主要是设置DMA的外设地址为ADC数据寄存器 ADC_DR;并设置内存地址为定义的buff,size为需要缓存的数据大小:
1 2 3 4 #define ADC_CHAN_CNT 4 #define ADC_FILETER_SIZE 32 int16_t adc_values[ADC_FILETER_SIZE*ADC_CHAN_CNT];
2.3 ADC配置为DMA读取和Timer触发
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 void tim3_setup (void ) { rcc_periph_clock_enable(RCC_TIM3); nvic_enable_irq(NVIC_TIM3_IRQ); rcc_periph_reset_pulse(RST_TIM3); timer_set_mode(TIM3, TIM_CR1_CKD_CK_INT, TIM_CR1_CMS_EDGE, TIM_CR1_DIR_UP); timer_set_prescaler(TIM3, 64 -1 ); timer_disable_preload(TIM3); timer_continuous_mode(TIM3); timer_set_period(TIM3, 20000 -1 ); timer_set_master_mode(TIM3, TIM_CR2_MMS_UPDATE); timer_enable_irq(TIM3, TIM_DIER_UIE); } void tim3_enable_counter (bool en) { if (en){ timer_enable_counter(TIM3); }else { timer_disable_counter(TIM3); } } void tim3_isr (void ) { if (timer_get_flag(TIM3, TIM_SR_UIF)) { timer_clear_flag(TIM3, TIM_SR_UIF); } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 rcc_periph_clock_enable(RCC_DMA); nvic_set_priority(NVIC_DMA1_CHANNEL1_IRQ, 3 ); nvic_enable_irq(NVIC_DMA1_CHANNEL1_IRQ); adc_setup(); dma_setup(adc_values, ADC_CHAN_CNT*ADC_FILETER_SIZE); adc_enable_overrun_interrupt(ADC1); adc_enable_dma(ADC1); ADC_CFGR1(ADC1) = (ADC_CFGR1(ADC1) & ~(0x3 <<10 )) | (0x1 <<10 ); ADC_CFGR1(ADC1) = (ADC_CFGR1(ADC1) & ~ADC_CFGR1_EXTSEL) | (3 <<ADC_CFGR1_EXTSEL_SHIFT); tim3_setup(); adc_start_conversion_regular(ADC1); tim3_enable_counter(true ); delay_ms(100 );
DMA中断时候即准备好读取ADC数据,因此在DMA中断中先把定时器关闭,读取数据后再次打开:
1 2 3 4 5 6 7 8 9 10 void dma1_channel1_isr (void ) { if ((DMA1_ISR &DMA_ISR_TCIF1) != 0 ) { DMA1_IFCR |= DMA_IFCR_CTCIF1; } tim3_enable_counter(false ); }
2.4 ADC读取 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 void adc_sample (void ) { uint32_t sum_val1 = 0 ; uint32_t sum_val2 = 0 ; uint32_t sum_val3 = 0 ; uint32_t sum_val4 = 0 ; for (int i=0 ; i<ADC_FILETER_SIZE; i++){ sum_val1 += adc_values[ADC_CHAN_CNT*i + 0 ]; sum_val2 += adc_values[ADC_CHAN_CNT*i + 1 ]; sum_val3 += adc_values[ADC_CHAN_CNT*i + 2 ]; sum_val4 += adc_values[ADC_CHAN_CNT*i + 3 ]; } uint32_t filter_val1 = sum_val1/ADC_FILETER_SIZE; uint32_t filter_val2 = sum_val2/ADC_FILETER_SIZE; uint32_t filter_val3 = sum_val3/ADC_FILETER_SIZE; uint32_t filter_val4 = sum_val4/ADC_FILETER_SIZE; printf ("adc:%d %d %d %d\r\n" , filter_val1, filter_val2, filter_val3, filter_val4); tim3_enable_counter(true ); }
读取时候按照通道的顺序从buff中取出,这里做了简单的过滤;
3 烧写测试 将程序烧写到开发板,然后打开串口可以看到四个ADC通道的数据,在PA0/PA1/PA3/PA4四个引脚连接不同电压可以看到变化: