你的浏览器版本过低,可能导致网站不能正常访问!
为了你能正常使用网站功能,请使用这些浏览器。

STM32G0-Platformio+libopencm3-多通道ADC与DMA使用

[复制链接]
STMCU小助手 发布时间:2023-2-22 18:36
使用Platformio平台的libopencm3开发框架来开发STM32G0,以下为多通道ADC与DMA的使用。

1 新建项目
  • 建立adc_dma项目

在PIO的Home页面新建项目,项目名称adc_dma,选择开发板为 MonkeyPi_STM32_G070RB,开发框架选择libopencm3;
  • 项目建立完成后在src目录下新建main.c主程序文件;
  • 修改下载和调试方式,这里开发板使用的是DAPLink仿真器,因此修改platformio.ini文件如下:

  1. 1upload_protocol = cmsis-dap
  2. 2debug_tool = cmsis-dap
复制代码


2 编写程序2.1 ADC 设置
这里设置PA0、PA1、PA2、PA3四个引脚为ADC:
  1. 1static void adc_setup(void)
  2. 2{
  3. 3    rcc_periph_clock_enable(RCC_ADC);
  4. 4    rcc_periph_clock_enable(RCC_GPIOA);
  5. 5
  6. 6    gpio_mode_setup(GPIOA, GPIO_MODE_ANALOG, GPIO_PUPD_NONE, GPIO0);
  7. 7    gpio_mode_setup(GPIOA, GPIO_MODE_ANALOG, GPIO_PUPD_NONE, GPIO1);
  8. 8    gpio_mode_setup(GPIOA, GPIO_MODE_ANALOG, GPIO_PUPD_NONE, GPIO2);
  9. 9    gpio_mode_setup(GPIOA, GPIO_MODE_ANALOG, GPIO_PUPD_NONE, GPIO3);
  10. 10
  11. 11    adc_power_off(ADC1);
  12. 12    adc_set_clk_prescale(ADC1, ADC_CCR_PRESC_DIV2);
  13. 13    adc_set_single_conversion_mode(ADC1);
  14. 14    adc_set_right_aligned(ADC1);
  15. 15    adc_set_sample_time_on_all_channels(ADC1, ADC_SMPTIME_160DOT5);
  16. 16
  17. 17    uint8_t channel_array[16] = {0};
  18. 18    channel_array[0] = 0;
  19. 19    channel_array[1] = 1;
  20. 20    channel_array[2] = 2;
  21. 21    channel_array[3] = 3;
  22. 22    adc_set_regular_sequence(ADC1, ADC_CHAN_CNT, channel_array);
  23. 23    adc_enable_dma_circular_mode(ADC1);
  24. 24    adc_set_resolution(ADC1, ADC_CFGR1_RES_12_BIT);
  25. 25    adc_power_on(ADC1);
  26. 26
  27. 27    /* Wait for ADC starting up. */
  28. 28    delay_ms(10);
  29. 29}
复制代码

2.2 DMA配置
  1. 1static void dma_setup(void *data, int size)
  2. 2{
  3. 3    dma_channel_reset(DMA1, DMA_CHANNEL1);
  4. 4    dma_set_peripheral_address(DMA1, DMA_CHANNEL1, (uint32_t)&ADC_DR(ADC1));
  5. 5    dma_set_memory_address(DMA1, DMA_CHANNEL1, (uint32_t)data);
  6. 6    dma_set_number_of_data(DMA1, DMA_CHANNEL1, size);
  7. 7    dma_set_read_from_peripheral(DMA1, DMA_CHANNEL1);
  8. 8    dma_enable_memory_increment_mode(DMA1, DMA_CHANNEL1);
  9. 9    dma_set_peripheral_size(DMA1, DMA_CHANNEL1, DMA_CCR_PSIZE_16BIT);
  10. 10    dma_set_memory_size(DMA1, DMA_CHANNEL1, DMA_CCR_MSIZE_16BIT);
  11. 11    dma_enable_circular_mode(DMA1, DMA_CHANNEL1);
  12. 12    dma_enable_transfer_complete_interrupt(DMA1, DMA_CHANNEL1);
  13. 13    dma_enable_channel(DMA1, DMA_CHANNEL1);
  14. 14
  15. 15    dmamux_reset_dma_channel(DMAMUX1, DMA_CHANNEL1);
  16. 16    dmamux_set_dma_channel_request(DMAMUX1, DMA_CHANNEL1, DMAMUX_CxCR_DMAREQ_ID_ADC);
  17. 17}
复制代码


主要是设置DMA的外设地址为ADC数据寄存器 ADC_DR;并设置内存地址为定义的buff,size为需要缓存的数据大小:
  1. 1#define ADC_CHAN_CNT        4
  2. 2#define ADC_FILETER_SIZE    32
  3. 3
  4. 4int16_t adc_values[ADC_FILETER_SIZE*ADC_CHAN_CNT];
复制代码

2.3 ADC配置为DMA读取和Timer触发
  • 定时器设置

  1. 1void tim3_setup(void)
  2. 2{
  3. 3    /* Enable TIM3 clock. */
  4. 4    rcc_periph_clock_enable(RCC_TIM3);
  5. 5
  6. 6    /* Enable TIM3 interrupt. */
  7. 7    nvic_enable_irq(NVIC_TIM3_IRQ);
  8. 8
  9. 9    /* Reset TIM3 peripheral to defaults. */
  10. 10    rcc_periph_reset_pulse(RST_TIM3);
  11. 11
  12. 12    /* Timer global mode:
  13. 13     * - No divider
  14. 14     * - Alignment edge
  15. 15     * - Direction up
  16. 16     * (These are actually default values after reset above, so this call
  17. 17     * is strictly unnecessary, but demos the api for alternative settings)
  18. 18     */
  19. 19    timer_set_mode(TIM3, TIM_CR1_CKD_CK_INT,
  20. 20                   TIM_CR1_CMS_EDGE, TIM_CR1_DIR_UP);
  21. 21
  22. 22    /*
  23. 23     * Please take note that the clock source for STM32 timers
  24. 24     * might not be the raw APB1/APB2 clocks.  In various conditions they
  25. 25     * are doubled.  See the Reference Manual for full details!
  26. 26     * In our case, TIM3 on APB1 is running at double frequency, so this
  27. 27     * sets the prescaler to have the timer run at 5kHz
  28. 28     */
  29. 29    timer_set_prescaler(TIM3, 64-1);
  30. 30
  31. 31    /* Disable preload. */
  32. 32    timer_disable_preload(TIM3);
  33. 33    timer_continuous_mode(TIM3);
  34. 34
  35. 35    timer_set_period(TIM3, 20000-1); //100Hz
  36. 36
  37. 37    timer_set_master_mode(TIM3, TIM_CR2_MMS_UPDATE);
  38. 38
  39. 39    timer_enable_irq(TIM3, TIM_DIER_UIE);
  40. 40}
  41. 41
  42. 42void tim3_enable_counter(bool en)
  43. 43{
  44. 44    if(en){
  45. 45        timer_enable_counter(TIM3);
  46. 46    }else{
  47. 47        timer_disable_counter(TIM3);
  48. 48    }
  49. 49}
  50. 50
  51. 51void tim3_isr(void)
  52. 52{
  53. 53    if (timer_get_flag(TIM3, TIM_SR_UIF)) {
  54. 54
  55. 55        /* Clear compare interrupt flag. */
  56. 56        timer_clear_flag(TIM3, TIM_SR_UIF);
  57. 57    }
  58. 58}
复制代码


  • DMA设置和Timer触发

  1. 1    rcc_periph_clock_enable(RCC_DMA);
  2. 2    nvic_set_priority(NVIC_DMA1_CHANNEL1_IRQ, 3);
  3. 3    nvic_enable_irq(NVIC_DMA1_CHANNEL1_IRQ);
  4. 4
  5. 5    adc_setup();
  6. 6
  7. 7    dma_setup(adc_values, ADC_CHAN_CNT*ADC_FILETER_SIZE);
  8. 8
  9. 9    adc_enable_overrun_interrupt(ADC1);
  10. 10
  11. 11    adc_enable_dma(ADC1);
  12. 12
  13. 13    ADC_CFGR1(ADC1) = (ADC_CFGR1(ADC1) & ~(0x3<<10)) | (0x1<<10);    // Hardware trigger detection on the rising edge
  14. 14    ADC_CFGR1(ADC1) = (ADC_CFGR1(ADC1) & ~ADC_CFGR1_EXTSEL) | (3<<ADC_CFGR1_EXTSEL_SHIFT);    // toggle by tim3
  15. 15
  16. 16    tim3_setup();
  17. 17
  18. 18    adc_start_conversion_regular(ADC1);
  19. 19
  20. 20
  21. 21    tim3_enable_counter(true);
  22. 22
  23. 23    delay_ms(100);
复制代码


DMA中断时候即准备好读取ADC数据,因此在DMA中断中先把定时器关闭,读取数据后再次打开:
  1. 1void dma1_channel1_isr(void)
  2. 2{
  3. 3
  4. 4    if ((DMA1_ISR &DMA_ISR_TCIF1) != 0) {
  5. 5        DMA1_IFCR |= DMA_IFCR_CTCIF1;
  6. 6    }
  7. 7
  8. 8    tim3_enable_counter(false);
  9. 9
  10. 10}
复制代码

2.4 ADC读取

  1. 1void adc_sample(void)
  2. 2{
  3. 3    uint32_t sum_val1 = 0;
  4. 4    uint32_t sum_val2 = 0;
  5. 5    uint32_t sum_val3 = 0;
  6. 6    uint32_t sum_val4 = 0;
  7. 7
  8. 8    for(int i=0; i<ADC_FILETER_SIZE; i++){
  9. 9        sum_val1 += adc_values[ADC_CHAN_CNT*i + 0];
  10. 10        sum_val2 += adc_values[ADC_CHAN_CNT*i + 1];
  11. 11        sum_val3 += adc_values[ADC_CHAN_CNT*i + 2];
  12. 12        sum_val4 += adc_values[ADC_CHAN_CNT*i + 3];
  13. 13    }
  14. 14
  15. 15    uint32_t filter_val1 = sum_val1/ADC_FILETER_SIZE;
  16. 16    uint32_t filter_val2 = sum_val2/ADC_FILETER_SIZE;
  17. 17    uint32_t filter_val3 = sum_val3/ADC_FILETER_SIZE;
  18. 18    uint32_t filter_val4 = sum_val4/ADC_FILETER_SIZE;
  19. 19
  20. 20    printf("adc:%d  %d  %d  %d\r\n", filter_val1, filter_val2, filter_val3, filter_val4);
  21. 21
  22. 22    tim3_enable_counter(true);
  23. 23}
复制代码


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

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

转载自:MakerInChina.cn


收藏 评论0 发布时间:2023-2-22 18:36

举报

0个回答

所属标签

相似分享

官网相关资源

关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
STM32N6 AI生态系统
STM32MCU,MPU高性能GUI
ST ACEPACK电源模块
意法半导体生物传感器
STM32Cube扩展软件包
关注我们
st-img 微信公众号
st-img 手机版