返回列表 发新帖我要提问本帖赏金: 50.00元(功能说明)

[APM32F4] APM32F407 3ADC+DMA连续采样示例

[复制链接]
 楼主| Alden 发表于 2025-7-17 11:30 | 显示全部楼层 |阅读模式
<
#技术资源# #申请原创#
APM32F407微控制器中的模数转换器(ADC)是一种12位逐次逼近型转换器,用于将模拟信号(如电压)转换为数字值,支持高精度数据采集。该模块具备以下核心特性:
主要特性
  • 分辨率与精度:支持12位、10位、8位或6位可配置分辨率,最小量化误差低,可通过自校准提升精度。输入电压范围由VREF-至VREF+(典型值VREF+连接VDDA=3.3V)。
  • 通道配置:
    • 最多24个外部通道:每个 ADC 最多有 16 个外部通道、3个内部通道(温度传感器、内部参考电压VREFINT和备份电压)、以及VBAT电池监控通道。
    • 通道分为规则组(最多16通道,用于常规转换)和注入组(最多4通道,支持高优先级中断式转换)。
  • 转换模式:
    • 支持单次转换、连续转换、扫描模式(自动顺序转换多个通道)和间断模式(分组触发)。
    • 双重或三重ADC模式(ADC1与ADC2和ADC3可协同工作)。
  • 数据处理:结果以左对齐或右对齐16位数据寄存器存储,支持DMA传输(减少CPU开销)和中断机制(用于转换结束或看门狗事件)。
  • 其他功能:软件或外部触发启动转换,采样时间按通道可编程,模拟看门狗监控输入阈值超限。
  • 采样速率:支持最高2.4 MSPS(每秒百万次采样)的单通道速度;通过交替模式,可提升至7.2 MSPS,满足高速数据应用需求。

APM32F407的有三个ADC,除了可以每个ADC单独工作外,还有双重ADC和三重ADC模式,可以实现更丰富的功能。接下来就测试下三重ADC的同时规则模式,三个ADC同时采各种的通道,实现多通道ADC采样有更快的速度。
首先是ADC和DMA的初始化
  1. void ADC_Init(void)
  2. {
  3.     GPIO_Config_T gpioConfig;
  4.     ADC_Config_T  adcConfig;
  5.     ADC_CommonConfig_T adcCommonConfig;

  6.     /* RCM Enable*/
  7.     RCM_EnableAHB1PeriphClock(RCM_AHB1_PERIPH_GPIOA|RCM_AHB1_PERIPH_GPIOB|RCM_AHB1_PERIPH_GPIOC);

  8.     /* GPIO Configuration */
  9.     GPIO_ConfigStructInit(&gpioConfig);
  10.     gpioConfig.pin = GPIO_PIN_0|GPIO_PIN_1|GPIO_PIN_2|GPIO_PIN_4|GPIO_PIN_5|GPIO_PIN_6;
  11.     gpioConfig.mode = GPIO_MODE_AN;
  12.     gpioConfig.pupd = GPIO_PUPD_NOPULL;
  13.     GPIO_Config(GPIOA, &gpioConfig);
  14.                
  15.     GPIO_ConfigStructInit(&gpioConfig);
  16.     gpioConfig.pin = GPIO_PIN_0|GPIO_PIN_1;
  17.     gpioConfig.mode = GPIO_MODE_AN;
  18.     gpioConfig.pupd = GPIO_PUPD_NOPULL;
  19.     GPIO_Config(GPIOB, &gpioConfig);
  20.         
  21.      GPIO_ConfigStructInit(&gpioConfig);
  22.     gpioConfig.pin = GPIO_PIN_0|GPIO_PIN_1|GPIO_PIN_2|GPIO_PIN_3;
  23.     gpioConfig.mode = GPIO_MODE_AN;
  24.     gpioConfig.pupd = GPIO_PUPD_NOPULL;
  25.     GPIO_Config(GPIOC, &gpioConfig);

  26.     RCM_EnableAPB2PeriphClock(RCM_APB2_PERIPH_ADC1);
  27.     RCM_EnableAPB2PeriphClock(RCM_APB2_PERIPH_ADC2);
  28.     RCM_EnableAPB2PeriphClock(RCM_APB2_PERIPH_ADC3);
  29.     ADC_Reset();

  30.     /* ADC Common Configuration */
  31.     adcCommonConfig.mode        = ADC_MODE_TRIPLE_REGSIMULT;
  32.     adcCommonConfig.prescaler   = ADC_PRESCALER_DIV6;
  33.     adcCommonConfig.accessMode  = ADC_ACCESS_MODE_1;
  34.     adcCommonConfig.twoSampling = ADC_TWO_SAMPLING_10CYCLES;
  35.     ADC_CommonConfig(&adcCommonConfig);

  36.     /* ADC Configuration */
  37.     ADC_ConfigStructInit(&adcConfig);

  38.     /* Set resolution*/
  39.     adcConfig.resolution = ADC_RESOLUTION_12BIT;
  40.     /* Set dataAlign*/
  41.     adcConfig.dataAlign = ADC_DATA_ALIGN_RIGHT;
  42.     /* Set scanDir*/
  43.     adcConfig.scanConvMode = ENABLE;
  44.     /* Set convMode continous*/
  45.     adcConfig.continuousConvMode = ENABLE;
  46.     /* Set extTrigEdge*/
  47.     adcConfig.extTrigEdge = ADC_EXT_TRIG_EDGE_NONE;
  48.     /* Set nbrOfConversion*/
  49.     adcConfig.nbrOfChannel = 4;

  50.     ADC_Config(ADC1, &adcConfig);
  51.     ADC_ConfigRegularChannel(ADC1, ADC_CHANNEL_0, 1, ADC_SAMPLETIME_3CYCLES);
  52.     ADC_ConfigRegularChannel(ADC1, ADC_CHANNEL_1, 2, ADC_SAMPLETIME_3CYCLES);
  53.                 ADC_ConfigRegularChannel(ADC1, ADC_CHANNEL_2, 3, ADC_SAMPLETIME_3CYCLES);
  54.     ADC_ConfigRegularChannel(ADC1, ADC_CHANNEL_4, 4, ADC_SAMPLETIME_3CYCLES);

  55.     ADC_Config(ADC2, &adcConfig);
  56.     ADC_ConfigRegularChannel(ADC2, ADC_CHANNEL_5, 1, ADC_SAMPLETIME_3CYCLES);
  57.     ADC_ConfigRegularChannel(ADC2, ADC_CHANNEL_6, 2, ADC_SAMPLETIME_3CYCLES);
  58.     ADC_ConfigRegularChannel(ADC2, ADC_CHANNEL_8, 3, ADC_SAMPLETIME_3CYCLES);
  59.     ADC_ConfigRegularChannel(ADC2, ADC_CHANNEL_9, 4, ADC_SAMPLETIME_3CYCLES);
  60.                
  61.      ADC_Config(ADC3, &adcConfig);
  62.     ADC_ConfigRegularChannel(ADC3, ADC_CHANNEL_10, 1, ADC_SAMPLETIME_3CYCLES);
  63.     ADC_ConfigRegularChannel(ADC3, ADC_CHANNEL_11, 2, ADC_SAMPLETIME_3CYCLES);
  64.     ADC_ConfigRegularChannel(ADC3, ADC_CHANNEL_12, 3, ADC_SAMPLETIME_3CYCLES);
  65.     ADC_ConfigRegularChannel(ADC3, ADC_CHANNEL_13, 4, ADC_SAMPLETIME_3CYCLES);
  66.                
  67.                
  68.     ADC_EnableMultiModeDMARequest();

  69.     /* Enable ADC*/
  70.     ADC_Enable(ADC1);
  71.     ADC_Enable(ADC2);
  72.     ADC_Enable(ADC3);
  73.     ADC_SoftwareStartConv(ADC1);
  74. }
  • 主要的配置点:配置为3ADC同步规则模式
    adcCommonConfig.mode        = ADC_MODE_TRIPLE_REGSIMULT;
  • 使能连续扫描模式,不使能触发
    /* Set scanDir*/
    adcConfig.scanConvMode = ENABLE;
    /* Set convMode continous*/
    adcConfig.continuousConvMode = ENABLE;
    /* Set extTrigEdge*/
    adcConfig.extTrigEdge = ADC_EXT_TRIG_EDGE_NONE;
  • 配置各ADC的通道顺序
    ADC_Config(ADC1, &adcConfig);
    ADC_ConfigRegularChannel(ADC1, ADC_CHANNEL_0, 1, ADC_SAMPLETIME_3CYCLES);
    ADC_ConfigRegularChannel(ADC1, ADC_CHANNEL_1, 2, ADC_SAMPLETIME_3CYCLES);
    ADC_ConfigRegularChannel(ADC1, ADC_CHANNEL_2, 3, ADC_SAMPLETIME_3CYCLES);
    ADC_ConfigRegularChannel(ADC1, ADC_CHANNEL_4, 4, ADC_SAMPLETIME_3CYCLES);
    ADC_Config(ADC2, &adcConfig);
    ADC_ConfigRegularChannel(ADC2, ADC_CHANNEL_5, 1, ADC_SAMPLETIME_3CYCLES);
    ADC_ConfigRegularChannel(ADC2, ADC_CHANNEL_6, 2, ADC_SAMPLETIME_3CYCLES);
    ADC_ConfigRegularChannel(ADC2, ADC_CHANNEL_8, 3, ADC_SAMPLETIME_3CYCLES);
    ADC_ConfigRegularChannel(ADC2, ADC_CHANNEL_9, 4, ADC_SAMPLETIME_3CYCLES);
               
    ADC_Config(ADC3, &adcConfig);
    ADC_ConfigRegularChannel(ADC3, ADC_CHANNEL_10, 1, ADC_SAMPLETIME_3CYCLES);
    ADC_ConfigRegularChannel(ADC3, ADC_CHANNEL_11, 2, ADC_SAMPLETIME_3CYCLES);
    ADC_ConfigRegularChannel(ADC3, ADC_CHANNEL_12, 3, ADC_SAMPLETIME_3CYCLES);
    ADC_ConfigRegularChannel(ADC3, ADC_CHANNEL_13, 4, ADC_SAMPLETIME_3CYCLES);
双重或三重 ADC 模式,ADC1 默认为主ADC,其他默认为从 ADC,只需要ADC1开始采样,其他ADC就也能工作。接下来是DMA的配置
查看用户手册,ADC1在DMA2的通道0 数据流0.根据这个进行配置
Snipaste_2025-07-16_18-50-46.png
  1. void DMA_Init(uint32_t* Buf)
  2. {
  3.     /* DMA Configure */
  4.     DMA_Config_T dmaConfig;

  5.     /* Enable DMA clock */
  6.     RCM_EnableAHB1PeriphClock(RCM_AHB1_PERIPH_DMA2);

  7.     /* size of buffer*/
  8.     dmaConfig.bufferSize = 12;
  9.     /* set memory Data Size*/
  10.     dmaConfig.memoryDataSize = DMA_MEMORY_DATA_SIZE_HALFWORD;
  11.     /* Set peripheral Data Size*/
  12.     dmaConfig.peripheralDataSize = DMA_PERIPHERAL_DATA_SIZE_HALFWORD;
  13.     /* Enable Memory Address increase*/
  14.     dmaConfig.memoryInc = DMA_MEMORY_INC_ENABLE;
  15.     /* Disable Peripheral Address increase*/
  16.     dmaConfig.peripheralInc = DMA_PERIPHERAL_INC_DISABLE;
  17.     /* Reset Circular Mode*/
  18.     dmaConfig.loopMode = DMA_MODE_CIRCULAR;
  19.     /* set priority*/
  20.     dmaConfig.priority = DMA_PRIORITY_HIGH;
  21.     /* read from peripheral*/
  22.     dmaConfig.dir = DMA_DIR_PERIPHERALTOMEMORY;
  23.     /* Set memory Address*/
  24.     dmaConfig.memoryBaseAddr = (uint32_t)Buf;
  25.     /* Set Peripheral Address*/
  26.     dmaConfig.peripheralBaseAddr = (uint32_t)&ADC->CDATA;

  27.     dmaConfig.channel          = DMA_CHANNEL_0;
  28.     dmaConfig.fifoMode         = DMA_FIFOMODE_ENABLE;
  29.     dmaConfig.fifoThreshold    = DMA_FIFOTHRESHOLD_HALFFULL;
  30.     dmaConfig.peripheralBurst  = DMA_PERIPHERALBURST_SINGLE;
  31.     dmaConfig.memoryBurst      = DMA_MEMORYBURST_SINGLE;

  32.     DMA_Config(DMA2_Stream0, &dmaConfig);
  33.     NVIC_EnableIRQRequest(DMA2_STR0_IRQn, 0x01, 0x01);
  34.     /* Clear DMA TF flag*/
  35.     DMA_ClearIntFlag(DMA2_Stream0, DMA_INT_TCI**0);

  36.     /* Enable DMA Interrupt*/
  37.     DMA_EnableInterrupt(DMA2_Stream0,DMA_INT_TCI**);
  38.     DMA_Enable(DMA2_Stream0);
  39. }
开启DMA中断,可以在每轮采样完成后进行数据处理
  1. void DMA_IRQHandler(void)
  2. {
  3.         if(DMA_ReadIntFlag(DMA2_Stream0,DMA_INT_TCI**0) != RESET)
  4.         {
  5.                                 APM_MINI_LEDToggle(LED2);
  6.                                         ADC1_CH0_ConvertedValue = (float)DMA_DualConvertedValue[0]/4095*3.3f;
  7.                                         ADC2_CH5_ConvertedValue = (float)DMA_DualConvertedValue[1]/4095*3.3f;
  8.                                         ADC3_CH10_ConvertedValue = (float)DMA_DualConvertedValue[2]/4095*3.3f;
  9.                                        
  10.                                         ADC1_CH1_ConvertedValue = (float)DMA_DualConvertedValue[3]/4095*3.3f;
  11.                                         ADC2_CH6_ConvertedValue = (float)DMA_DualConvertedValue[4]/4095*3.3f;
  12.                                         ADC3_CH11_ConvertedValue = (float)DMA_DualConvertedValue[5]/4095*3.3f;
  13.                                        
  14.                                         ADC1_CH2_ConvertedValue = (float)DMA_DualConvertedValue[6]/4095*3.3f;
  15.                                         ADC2_CH8_ConvertedValue = (float)DMA_DualConvertedValue[7]/4095*3.3f;
  16.                                         ADC3_CH12_ConvertedValue = (float)DMA_DualConvertedValue[8]/4095*3.3f;
  17.                                        
  18.                                         ADC1_CH4_ConvertedValue = (float)DMA_DualConvertedValue[9]/4095*3.3f;
  19.                                         ADC2_CH9_ConvertedValue = (float)DMA_DualConvertedValue[10]/4095*3.3f;
  20.                                         ADC3_CH13_ConvertedValue = (float)DMA_DualConvertedValue[11]/4095*3.3f;
  21.         DMA_ClearIntFlag(DMA2_Stream0,DMA_INT_TCI**0);
  22.         }
  23. }
最后可以使用串口打印,输出采的电压。
  1. int main(void)
  2. {


  3.     USART_Config_T usartConfigStruct;

  4.     /* USART configuration */
  5.     USART_ConfigStructInit(&usartConfigStruct);
  6.     usartConfigStruct.baudRate      = 115200;
  7.     usartConfigStruct.mode          = USART_MODE_TX_RX;
  8.     usartConfigStruct.parity        = USART_PARITY_NONE;
  9.     usartConfigStruct.stopBits      = USART_STOP_BIT_1;
  10.     usartConfigStruct.wordLength    = USART_WORD_LEN_8B;
  11.     usartConfigStruct.hardwareFlow  = USART_HARDWARE_FLOW_NONE;
  12.     APM_MINI_LEDInit(LED2);
  13.     /* COM1 init*/
  14.     APM_MINI_COMInit(COM1, &usartConfigStruct);

  15.     DMA_Init((uint32_t*)DMA_DualConvertedValue);
  16.     ADC_Init();

  17.     while (1)
  18.     {
  19.       Delay(0x1FFFFF);

  20.             printf("\r\nADC CH0: ADC1 Voltage    = %f V \r\n", ADC1_CH0_ConvertedValue);
  21.             printf("ADC CH1: ADC1 Voltage    = %f V \r\n", ADC1_CH1_ConvertedValue);
  22.             printf("ADC CH2: ADC1 Voltage    = %f V \r\n", ADC1_CH2_ConvertedValue);
  23.             printf("ADC CH4: ADC1 Voltage    = %f V \r\n", ADC1_CH4_ConvertedValue);
  24.                         
  25.             printf("ADC CH5: ADC2 Voltage    = %f V \r\n", ADC2_CH5_ConvertedValue);
  26.             printf("ADC CH6: ADC2 Voltage    = %f V \r\n", ADC2_CH6_ConvertedValue);
  27.             printf("ADC CH8: ADC2 Voltage    = %f V \r\n", ADC2_CH8_ConvertedValue);
  28.             printf("ADC CH9: ADC2 Voltage    = %f V \r\n", ADC2_CH9_ConvertedValue);
  29.                         
  30.                               printf("ADC CH10: ADC2 Voltage    = %f V \r\n", ADC3_CH10_ConvertedValue);
  31.             printf("ADC CH11: ADC2 Voltage    = %f V \r\n", ADC3_CH11_ConvertedValue);
  32.             printf("ADC CH12: ADC2 Voltage    = %f V \r\n", ADC3_CH12_ConvertedValue);
  33.             printf("ADC CH13: ADC2 Voltage    = %f V \r\n", ADC3_CH13_ConvertedValue);
  34.       
  35.      
  36.     }
  37. }
将PA0接到高电平,查看串口打印的就是正确的了。
微信图片_20250716185638.png
APM32F4xx_SDK_V1.4-3ADC.zip (9.83 MB, 下载次数: 1)

打赏榜单

21小跑堂 打赏了 50.00 元 2025-07-28
理由:恭喜通过原创审核!期待您更多的原创作品~~

评论

基于APM32F407 的三个ADC多通道DMA采样实例,通过三个ADC结合DMA,实现高效的ADC数据采集,满足高速数据应用需求。  发表于 2025-7-28 17:47
 楼主| Alden 发表于 2025-7-17 11:31 | 显示全部楼层
zjk103 发表于 2025-7-18 10:34 | 显示全部楼层
之前我们用极海的407有发生采样不准的情况,你有没有碰到啊?
 楼主| Alden 发表于 2025-7-18 11:14 | 显示全部楼层
zjk103 发表于 2025-7-18 10:34
之前我们用极海的407有发生采样不准的情况,你有没有碰到啊?

注意电源的纹波和稳定性,注意ADC的时钟频率,没分频跑在超频的状态精度会受很大影响,正常测试精度是没问题的
zjsx8192 发表于 2025-7-19 08:52 | 显示全部楼层
不错的例程
zjk103 发表于 2025-7-19 23:02 | 显示全部楼层
Alden 发表于 2025-7-18 11:14
注意电源的纹波和稳定性,注意ADC的时钟频率,没分频跑在超频的状态精度会受很大影响,正常测试精度是没 ...

我们再试试
天体书记 发表于 2025-7-21 14:35 | 显示全部楼层
刚刚在另一个帖子我还说,我超级喜欢DMA + 多通道 周期触发采集。
楼主这就这么完美实现并分享了项目
谢谢楼主了
作业粉碎机 发表于 2025-7-23 13:07 | 显示全部楼层
这个示例代码非常详细,涵盖了ADC和DMA的初始化配置,对于多通道数据采集很有帮助。
 楼主| Alden 发表于 2025-7-29 14:17 | 显示全部楼层
本帖最后由 Alden 于 2025-7-29 14:18 编辑
zjk103 发表于 2025-7-18 10:34
之前我们用极海的407有发生采样不准的情况,你有没有碰到啊?

目前测试精度是没问题的,也可以排查VDDA/VREF的供电纹波情况,ADC时钟配置是否在36M以下,是否有开ADC校准,采样的电压输出能力比较弱需要将ADC采样周期加大。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

49

主题

113

帖子

2

粉丝