STM32G0开发笔记:多通道ADC与DMA的使用

发布时间:2023-08-03  

使用Platformio平台的libopencm3开发框架来开发STM32G0,以下为多通道ADC与DMA的使用。


1 新建项目

建立adc_dma项目

在PIO的Home页面新建项目,项目名称adc_dma,选择开发板为 MonkeyPi_STM32_G070RB,开发框架选择libopencm3;


项目建立完成后在src目录下新建main.c主程序文件;

修改下载和调试方式,这里开发板使用的是DAPLink仿真器,因此修改platformio.ini文件如下:

1upload_protocol = cmsis-dap

2debug_tool = cmsis-dap

2 编写程序

2.1 ADC 设置

这里设置PA0、PA1、PA2、PA3四个引脚为ADC:


1static void adc_setup(void)

 2{

 3    rcc_periph_clock_enable(RCC_ADC);

 4    rcc_periph_clock_enable(RCC_GPIOA);

 5

 6    gpio_mode_setup(GPIOA, GPIO_MODE_ANALOG, GPIO_PUPD_NONE, GPIO0);

 7    gpio_mode_setup(GPIOA, GPIO_MODE_ANALOG, GPIO_PUPD_NONE, GPIO1);

 8    gpio_mode_setup(GPIOA, GPIO_MODE_ANALOG, GPIO_PUPD_NONE, GPIO2);

 9    gpio_mode_setup(GPIOA, GPIO_MODE_ANALOG, GPIO_PUPD_NONE, GPIO3);

10

11    adc_power_off(ADC1);

12    adc_set_clk_prescale(ADC1, ADC_CCR_PRESC_DIV2);

13    adc_set_single_conversion_mode(ADC1);

14    adc_set_right_aligned(ADC1);

15    adc_set_sample_time_on_all_channels(ADC1, ADC_SMPTIME_160DOT5);

16

17    uint8_t channel_array[16] = {0};

18    channel_array[0] = 0;

19    channel_array[1] = 1;

20    channel_array[2] = 2;

21    channel_array[3] = 3;

22    adc_set_regular_sequence(ADC1, ADC_CHAN_CNT, channel_array);

23    adc_enable_dma_circular_mode(ADC1);

24    adc_set_resolution(ADC1, ADC_CFGR1_RES_12_BIT);

25    adc_power_on(ADC1);

26

27    /* Wait for ADC starting up. */

28    delay_ms(10); 

29}

2.2 DMA配置

1static void dma_setup(void *data, int size)

 2{

 3    dma_channel_reset(DMA1, DMA_CHANNEL1);

 4    dma_set_peripheral_address(DMA1, DMA_CHANNEL1, (uint32_t)&ADC_DR(ADC1));

 5    dma_set_memory_address(DMA1, DMA_CHANNEL1, (uint32_t)data);

 6    dma_set_number_of_data(DMA1, DMA_CHANNEL1, size);

 7    dma_set_read_from_peripheral(DMA1, DMA_CHANNEL1);

 8    dma_enable_memory_increment_mode(DMA1, DMA_CHANNEL1);

 9    dma_set_peripheral_size(DMA1, DMA_CHANNEL1, DMA_CCR_PSIZE_16BIT);

10    dma_set_memory_size(DMA1, DMA_CHANNEL1, DMA_CCR_MSIZE_16BIT);

11    dma_enable_circular_mode(DMA1, DMA_CHANNEL1);

12    dma_enable_transfer_complete_interrupt(DMA1, DMA_CHANNEL1);

13    dma_enable_channel(DMA1, DMA_CHANNEL1);

14

15    dmamux_reset_dma_channel(DMAMUX1, DMA_CHANNEL1);

16    dmamux_set_dma_channel_request(DMAMUX1, DMA_CHANNEL1, DMAMUX_CxCR_DMAREQ_ID_ADC);

17}

主要是设置DMA的外设地址为ADC数据寄存器 ADC_DR;并设置内存地址为定义的buff,size为需要缓存的数据大小:


1#define ADC_CHAN_CNT        4

2#define ADC_FILETER_SIZE    32

3

4int16_t adc_values[ADC_FILETER_SIZE*ADC_CHAN_CNT];

2.3 ADC配置为DMA读取和Timer触发

定时器设置

1void tim3_setup(void)

 2{

 3    /* Enable TIM3 clock. */

 4    rcc_periph_clock_enable(RCC_TIM3);

 5

 6    /* Enable TIM3 interrupt. */

 7    nvic_enable_irq(NVIC_TIM3_IRQ);

 8

 9    /* Reset TIM3 peripheral to defaults. */

10    rcc_periph_reset_pulse(RST_TIM3);

11

12    /* Timer global mode:

13     * - No divider

14     * - Alignment edge

15     * - Direction up

16     * (These are actually default values after reset above, so this call

17     * is strictly unnecessary, but demos the api for alternative settings)

18     */

19    timer_set_mode(TIM3, TIM_CR1_CKD_CK_INT,

20                   TIM_CR1_CMS_EDGE, TIM_CR1_DIR_UP);

21

22    /*

23     * Please take note that the clock source for STM32 timers

24     * might not be the raw APB1/APB2 clocks.  In various conditions they

25     * are doubled.  See the Reference Manual for full details!

26     * In our case, TIM3 on APB1 is running at double frequency, so this

27     * sets the prescaler to have the timer run at 5kHz

28     */

29    timer_set_prescaler(TIM3, 64-1);

30

31    /* Disable preload. */

32    timer_disable_preload(TIM3);

33    timer_continuous_mode(TIM3);

34

35    timer_set_period(TIM3, 20000-1); //100Hz

36

37    timer_set_master_mode(TIM3, TIM_CR2_MMS_UPDATE);

38

39    timer_enable_irq(TIM3, TIM_DIER_UIE);

40}

41

42void tim3_enable_counter(bool en)

43{

44    if(en){

45        timer_enable_counter(TIM3);

46    }else{

47        timer_disable_counter(TIM3);

48    }

49}

50

51void tim3_isr(void)

52{

53    if (timer_get_flag(TIM3, TIM_SR_UIF)) {

54

55        /* Clear compare interrupt flag. */

56        timer_clear_flag(TIM3, TIM_SR_UIF);

57    }

58}

DMA设置和Timer触发

1    rcc_periph_clock_enable(RCC_DMA);

 2    nvic_set_priority(NVIC_DMA1_CHANNEL1_IRQ, 3);

 3    nvic_enable_irq(NVIC_DMA1_CHANNEL1_IRQ);

 4

 5    adc_setup();

 6

 7    dma_setup(adc_values, ADC_CHAN_CNT*ADC_FILETER_SIZE);

 8

 9    adc_enable_overrun_interrupt(ADC1);

10

11    adc_enable_dma(ADC1);

12

13    ADC_CFGR1(ADC1) = (ADC_CFGR1(ADC1) & ~(0x3<<10)) | (0x1<<10);    // Hardware trigger detection on the rising edge

14    ADC_CFGR1(ADC1) = (ADC_CFGR1(ADC1) & ~ADC_CFGR1_EXTSEL) | (3<1_EXTSEL_SHIFT);    // toggle by tim3

15

16    tim3_setup();

17

18    adc_start_conversion_regular(ADC1);

19

20

21    tim3_enable_counter(true);

22

23    delay_ms(100);

DMA中断时候即准备好读取ADC数据,因此在DMA中断中先把定时器关闭,读取数据后再次打开:


1void dma1_channel1_isr(void)

 2{

 3

 4    if ((DMA1_ISR &DMA_ISR_TCIF1) != 0) {

 5        DMA1_IFCR |= DMA_IFCR_CTCIF1;

 6    }

 7

 8    tim3_enable_counter(false);

 9

10}

2.4 ADC读取

1void adc_sample(void)

 2{

 3    uint32_t sum_val1 = 0;

 4    uint32_t sum_val2 = 0;

 5    uint32_t sum_val3 = 0;

 6    uint32_t sum_val4 = 0;

 7

 8    for(int i=0; i9        sum_val1 += adc_values[ADC_CHAN_CNT*i + 0];

10        sum_val2 += adc_values[ADC_CHAN_CNT*i + 1];

11        sum_val3 += adc_values[ADC_CHAN_CNT*i + 2];

12        sum_val4 += adc_values[ADC_CHAN_CNT*i + 3];

13    }

14

15    uint32_t filter_val1 = sum_val1/ADC_FILETER_SIZE;

16    uint32_t filter_val2 = sum_val2/ADC_FILETER_SIZE;

17    uint32_t filter_val3 = sum_val3/ADC_FILETER_SIZE;

18    uint32_t filter_val4 = sum_val4/ADC_FILETER_SIZE;

19

20    printf("adc:%d  %d  %d  %d ", filter_val1, filter_val2, filter_val3, filter_val4);

21

22    tim3_enable_counter(true);

23}

读取时候按照通道的顺序从buff中取出,这里做了简单的过滤;


3 烧写测试

将程序烧写到开发板,然后打开串口可以看到四个ADC通道的数据,在PA0/PA1/PA3/PA4四个引脚连接不同电压可以看到变化:

文章来源于:电子工程世界    原文链接
本站所有转载文章系出于传递更多信息之目的,且明确注明来源,不希望被转载的媒体或个人可与我们联系,我们将立即进行删除处理。

我们与500+贴片厂合作,完美满足客户的定制需求。为品牌提供定制化的推广方案、专属产品特色页,多渠道推广,SEM/SEO精准营销以及与公众号的联合推广...详细>>

利用葫芦芯平台的卓越技术服务和新产品推广能力,原厂代理能轻松打入消费物联网(IOT)、信息与通信(ICT)、汽车及新能源汽车、工业自动化及工业物联网、装备及功率电子...详细>>

充分利用其强大的电子元器件采购流量,创新性地为这些物料提供了一个全新的窗口。我们的高效数字营销技术,不仅可以助你轻松识别与连接到需求方,更能够极大地提高“闲置物料”的处理能力,通过葫芦芯平台...详细>>

我们的目标很明确:构建一个全方位的半导体产业生态系统。成为一家全球领先的半导体互联网生态公司。目前,我们已成功打造了智能汽车、智能家居、大健康医疗、机器人和材料等五大生态领域。更为重要的是...详细>>

我们深知加工与定制类服务商的价值和重要性,因此,我们倾力为您提供最顶尖的营销资源。在我们的平台上,您可以直接接触到100万的研发工程师和采购工程师,以及10万的活跃客户群体...详细>>

凭借我们强大的专业流量和尖端的互联网数字营销技术,我们承诺为原厂提供免费的产品资料推广服务。无论是最新的资讯、技术动态还是创新产品,都可以通过我们的平台迅速传达给目标客户...详细>>

我们不止于将线索转化为潜在客户。葫芦芯平台致力于形成业务闭环,从引流、宣传到最终销售,全程跟进,确保每一个potential lead都得到妥善处理,从而大幅提高转化率。不仅如此...详细>>