[APM32E0] APM32E030的ADC驱动(DMA)

[复制链接]
390|2
口天土立口 发表于 2025-9-15 17:28 | 显示全部楼层 |阅读模式
6059768c7dbad82c4c.png

APM32E030为12位ADC,同时有16个外部通道和2个内部通道。
同时,ADC支持与DMA的联动,实现更高效的数据采集。

ADC与DMA的驱动代码如下:
  1. /*
  2. *  PA0 -> ADC_IN0
  3. *  PA1 -> ADC_IN1
  4. *  PA2 -> ADC_IN2
  5. *  PA3 -> ADC_IN3
  6. *  PA4 -> ADC_IN4
  7. *  PA5 -> ADC_IN5
  8. *  PA6 -> ADC_IN6
  9. *  PA7 -> ADC_IN7
  10. *  PB0 -> ADC_IN8
  11. *  PB1 -> ADC_IN9
  12. *  PC0 -> ADC_IN10
  13. *  PC1 -> ADC_IN11
  14. *  PC2 -> ADC_IN12
  15. *  PC3 -> ADC_IN13
  16. *  PC4 -> ADC_IN14
  17. *  PC5 -> ADC_IN15
  18. */
  19. typedef struct {
  20.     GPIO_T      *port;           
  21.     uint16_t    pin;
  22.     uint32_t    AHBPeriph;
  23.     uint32_t    adc_channel;
  24. } adc_ch_info_t;
  25. /* ADC信息 */
  26. static adc_ch_info_t adc_ch_info[] = {
  27.     {GPIOA,     GPIO_PIN_0,    RCM_AHB_PERIPH_GPIOA,    ADC_CHANNEL_0 },
  28.     {GPIOA,     GPIO_PIN_1,    RCM_AHB_PERIPH_GPIOA,    ADC_CHANNEL_1 },
  29.     {GPIOA,     GPIO_PIN_2,    RCM_AHB_PERIPH_GPIOA,    ADC_CHANNEL_2 },
  30.     {GPIOA,     GPIO_PIN_3,    RCM_AHB_PERIPH_GPIOA,    ADC_CHANNEL_3 },
  31.     {GPIOA,     GPIO_PIN_4,    RCM_AHB_PERIPH_GPIOA,    ADC_CHANNEL_4 },
  32.     {GPIOA,     GPIO_PIN_5,    RCM_AHB_PERIPH_GPIOA,    ADC_CHANNEL_5 },
  33.     {GPIOA,     GPIO_PIN_6,    RCM_AHB_PERIPH_GPIOA,    ADC_CHANNEL_6 },
  34.     {GPIOA,     GPIO_PIN_7,    RCM_AHB_PERIPH_GPIOA,    ADC_CHANNEL_7 },
  35.     {GPIOB,     GPIO_PIN_0,    RCM_AHB_PERIPH_GPIOB,    ADC_CHANNEL_8 },
  36.     {GPIOB,     GPIO_PIN_1,    RCM_AHB_PERIPH_GPIOB,    ADC_CHANNEL_9 },
  37.     {GPIOC,     GPIO_PIN_0,    RCM_AHB_PERIPH_GPIOC,    ADC_CHANNEL_10 },
  38.     {GPIOC,     GPIO_PIN_1,    RCM_AHB_PERIPH_GPIOC,    ADC_CHANNEL_11 },
  39.     {GPIOC,     GPIO_PIN_2,    RCM_AHB_PERIPH_GPIOC,    ADC_CHANNEL_12 },
  40.     {GPIOC,     GPIO_PIN_3,    RCM_AHB_PERIPH_GPIOC,    ADC_CHANNEL_13 },
  41.     {GPIOC,     GPIO_PIN_4,    RCM_AHB_PERIPH_GPIOC,    ADC_CHANNEL_14 },
  42.     {GPIOC,     GPIO_PIN_5,    RCM_AHB_PERIPH_GPIOC,    ADC_CHANNEL_15 },
  43.     {NULL,     0,               0,                      ADC_CHANNEL_16 },
  44.     {NULL,     0,               0,                      ADC_CHANNEL_17 },
  45. };
  1. /*
  2. * [url=/u/brief]@brief[/url]       引脚初始化
  3. *
  4. * @param       None
  5. *
  6. * @retval      None
  7. *
  8. */
  9. void bsp_adc_gpio_init(void)
  10. {
  11.         GPIO_Config_T gpioConfig;
  12.     uint8_t ch = 0;
  13.    
  14.     for (ch = 0; ch < (sizeof(adc_ch_info) / sizeof(adc_ch_info[0])); ch++) {
  15.         RCM_EnableAHBPeriphClock(adc_ch_info[ch].AHBPeriph);
  16.         GPIO_ConfigStructInit(&gpioConfig);
  17.         gpioConfig.pin     = adc_ch_info[ch].pin;
  18.         gpioConfig.mode    = GPIO_MODE_AN;
  19.         gpioConfig.outtype = GPIO_OUT_TYPE_PP;
  20.         gpioConfig.speed   = GPIO_SPEED_50MHz;
  21.         gpioConfig.pupd    = GPIO_PUPD_NO;
  22.         GPIO_Config(adc_ch_info[ch].port, &gpioConfig);
  23.     }
  24. }
  1. /*
  2. * @brief       ADC初始化
  3. *
  4. * @param       membuf: 存储地址
  5. *
  6. * @retval      None
  7. *
  8. */
  9. void bsp_adc_init(uint16_t *membuf)
  10. {
  11.     ADC_Config_T adcConfig;
  12.     DMA_Config_T dmaConfig;
  13.    
  14.     uint32_t ch = ADC_CHANNEL_0 | ADC_CHANNEL_1 | ADC_CHANNEL_2 | ADC_CHANNEL_3 | ADC_CHANNEL_4 | \
  15.             ADC_CHANNEL_5 | ADC_CHANNEL_6 | ADC_CHANNEL_7 | ADC_CHANNEL_8 | ADC_CHANNEL_9 | \
  16.             ADC_CHANNEL_10 | ADC_CHANNEL_11 | ADC_CHANNEL_12 | ADC_CHANNEL_13 | ADC_CHANNEL_14 | \
  17.             ADC_CHANNEL_15 | ADC_CHANNEL_16 | ADC_CHANNEL_17;
  18.    
  19.     RCM_EnableAPB2PeriphClock(RCM_APB2_PERIPH_ADC1);
  20.     RCM_EnableAHBPeriphClock(RCM_AHB_PERIPH_DMA1);
  21.    
  22.     /* DMA配置 */
  23.     DMA_ConfigStructInit(&dmaConfig);
  24.     dmaConfig.direction = DMA_DIR_PERIPHERAL;
  25.     dmaConfig.circular = DMA_CIRCULAR_DISABLE;
  26.     dmaConfig.memoryTomemory = DMA_M2M_DISABLE;
  27.     dmaConfig.priority = DMA_PRIORITY_LEVEL_LOW;
  28.     dmaConfig.memoryInc = DMA_MEMORY_INC_ENABLE;
  29.     dmaConfig.peripheralInc = DMA_PERIPHERAL_INC_DISABLE;
  30.     dmaConfig.memoryDataSize = DMA_MEMORY_DATASIZE_HALFWORD;
  31.     dmaConfig.peripheralDataSize = DMA_PERIPHERAL_DATASIZE_HALFWORD;
  32.     dmaConfig.bufferSize = ADC_CH_NUM;
  33.     dmaConfig.memoryAddress = (uint32_t)membuf;
  34.     dmaConfig.peripheralAddress = (uint32_t)&ADC->DATA;
  35.     DMA_Config(DMA1_CHANNEL_1, &dmaConfig);
  36.     DMA_Enable(DMA1_CHANNEL_1);
  37.    
  38.     /* ADC Configuration */
  39.     ADC_Reset();
  40.     ADC_ConfigStructInit(&adcConfig);
  41.     /* Set resolution*/
  42.     adcConfig.resolution = ADC_RESOLUTION_12B;
  43.     /* Set dataAlign*/
  44.     adcConfig.dataAlign  = ADC_DATA_ALIGN_RIGHT;
  45.     /* Set scanDir*/
  46.     adcConfig.scanDir    = ADC_SCAN_DIR_UPWARD;
  47.     /* Set single */
  48.     adcConfig.convMode   = ADC_CONVERSION_SINGLE;
  49.     /* Set extTrigConv*/
  50.     adcConfig.extTrigConv  = ADC_EXT_TRIG_CONV_TRG0;
  51.     /* Set TrigEdge*/
  52.     adcConfig.extTrigEdge    = ADC_EXT_TRIG_EDGE_NONE;
  53.     ADC_Config(&adcConfig);
  54.     ADC_EnableTempSensor();
  55.     ADC_EnableVrefint();
  56.     ADC_ConfigChannel(ch, ADC_SAMPLE_TIME_41_5);
  57.     /* Calibration*/
  58.     ADC_ReadCalibrationFactor();
  59.     /* Enable ADC*/
  60.     ADC_Enable();   
  61.     ADC_EnableDMA();
  62. }
  1. /*
  2. * @brief       ADC启动
  3. *
  4. * @param       None
  5. *
  6. * @retval      None
  7. *
  8. */
  9. void bsp_adc_start(void)
  10. {
  11.     DMA_Disable(DMA1_CHANNEL_1);
  12.     DMA_SetDataNumber(DMA1_CHANNEL_1, ADC_CH_NUM);
  13.     DMA_Enable(DMA1_CHANNEL_1);
  14.     /* Wait until ADC is ready */
  15.     while (!ADC_ReadStatusFlag(ADC_FLAG_ADRDY));
  16.     ADC_StartConversion();
  17. }
  1. /*
  2. * @brief       ADC停止
  3. *
  4. * @param       None
  5. *
  6. * @retval      None
  7. *
  8. */
  9. void bsp_adc_stop(void)
  10. {
  11.     ADC_StopConversion();
  12. }
  1. /*
  2. * @brief       ADC完成
  3. *
  4. * @param       None
  5. *
  6. * @retval      完成结果
  7. *
  8. */
  9. uint8_t bsp_adc_complete(void)
  10. {
  11.     uint8_t ret = 0;
  12.    
  13.     if (DMA_ReadStatusFlag(DMA1_FLAG_TF1) != RESET) {
  14.         DMA_ClearStatusFlag(DMA1_FLAG_TF1);
  15.         ret = 1;
  16.     }
  17.    
  18.     return ret;
  19. }
  1. enum ADC_CH {
  2.     ADC_CH0,
  3.     ADC_CH1,
  4.     ADC_CH2,
  5.     ADC_CH3,
  6.     ADC_CH4,
  7.     ADC_CH5,
  8.     ADC_CH6,
  9.     ADC_CH7,
  10.     ADC_CH8,
  11.     ADC_CH9,
  12.     ADC_CH10,
  13.     ADC_CH11,
  14.     ADC_CH12,
  15.     ADC_CH13,
  16.     ADC_CH14,
  17.     ADC_CH15,
  18.     ADC_CH16,
  19.     ADC_CH17,
  20.    
  21.     ADC_CH_NUM
  22. };



测试代码如下:
  1. uint16_t adc_ch_value[ADC_CH_NUM];
  2. float adc_ch_voltage[ADC_CH_NUM];

  3. // 应用初始化
  4. void app_init(void)
  5. {
  6.     bsp_adc_gpio_init();
  7.     bsp_adc_init(adc_ch_value);
  8.     bsp_adc_start();
  9. }

  10. // 应用任务
  11. void app_task(void)
  12. {
  13.     if (bsp_adc_complete() != 0) {
  14.         for (uint8_t i = 0; i < ADC_CH_NUM; i++) {
  15.             adc_ch_voltage[i] = ((float)adc_ch_value[i]) / 4095 * 3.3f;
  16.         }
  17.         bsp_adc_start();
  18.     }
  19. }


详细代码,请查看附件!
DMA.zip (2.37 MB, 下载次数: 0)






复古留声机 发表于 2025-9-27 09:55 | 显示全部楼层
代码中使用了DMA来提高ADC的数据采集效率,这是一个很好的做法
夜幕叙事曲 发表于 2025-10-4 10:07 | 显示全部楼层
启动一次,ADC通道转换与剩下的操作就都由DMA来实现了。
比一次一次读取方便多了
您需要登录后才可以回帖 登录 | 注册

本版积分规则

19

主题

45

帖子

0

粉丝
快速回复 在线客服 返回列表 返回顶部