【STM32H7教程】第46章 STM32H7的ADC应用之DMA方式多通道采样

发布时间:2023-04-07  

第1阶段,上电启动阶段:

  • 这部分在第14章进行了详细说明。

第2阶段,进入main函数:

  • 第1步,硬件初始化,主要是MPU,Cache,HAL库,系统时钟,滴答定时器,LED,串口和ADC。

  • 第2步,周期性的打印ADC采集的多通道数据。

46.7 实验例程说明(MDK)

配套例子:

V7-024-ADC+DMA的多通道采集

实验目的:

  1. 学习ADC + DMA的多通道采集实现。

实验内容:

  1. 例子默认用的PLL时钟供ADC使用,大家可以通过bsp_adc.c文件开头宏定义切换到AHB时钟。

  2. 采用DMA方式进行多通道采样,采集了PC0, Vbat/4, VrefInt和温度。

  3. 每隔500ms,串口会打印一次。

  4. 板子正常运行时LED2闪烁。

PC0引脚位置(稳压基准要短接3.3V):

上电后串口打印的信息:

波特率 115200,数据位 8,奇偶校验位无,停止位 1

程序设计:

系统栈大小分配:

RAM空间用的DTCM:

硬件外设初始化

硬件外设的初始化是在 bsp.c 文件实现:

46.1 初学者重要提示

  1. 学习本章节前,务必优先学习第44章,需要对ADC的基础知识和HAL库的几个常用API有个认识。

  2. 开发板右上角有个跳线帽,可以让ADC的稳压基准接3.3V或者2.5V,本章例子是接到3.3V。

  3. STM32H7的ADC支持偏移校准和线性度校准。如果使用线性度校准的话,特别要注意此贴的问题:armbbs.cn/forum.php? 。

  4. ADC的专业术语诠释文档,推荐大家看看:armbbs.cn/forum.php? 。

  5. STM32H7的ADC多通道并不是同步采样的,本质上是通过内部的多路选择器不断切换实现的,一个采集完毕了才会采集另一个。

46.2 ADC稳压基准硬件设计

注:学习前务必优先看第14章的2.1小节,对电源供电框架有个了解。

ADC要采集的准确,就需要有一个稳定的稳压基准源,V7开发板使用的LM285D-2.5,即2.5V的基准源。硬件设计如下:

关于这个原理图要注意以下问题:

LM285D-2.5输出的是2.5V的稳压基准,原理图这里做了一个特别的处理,同时接了一个上拉电阻到VDDA(3.3V),然后用户可以使用开发板右上角的跳线帽设置Vref选择3.3V稳压还是2.5V稳压。

下面再来了解下LM285的电气特性:

通过这个表,我们要了解以下几点知识:

  1. LM285的典型值是2.5V,支持的最小值2.462V,最大值2.538V。工作电流是20uA到20mA,温飘是±20ppm/℃

  2. Iz是Reference current参考电流的意思:

    • 参考电流是20uA到1mA,温度25℃,参考电压最大变化1mV。

    • 参考电流是20uA到1mA,全范围温度(−40°C to 85°C),参考电压最大变化1.5mV。

    • 参考电流是1mA到20mA,温度25℃,参考电压最大变化10mV。

    • 参考电流是1mA到20mA,全范围温度(−40°C to 85°C),参考电压最大变化30mV。


那么问题来了,V7开发板上LM285的参考电流是多少? 简单计算就是:

(VDDA – 2.5V) / 1K =(3.3 – 2.5V) / 1K = 0.8mA。

46.3 ADC驱动设计

ADC做DMA数据传输的实现思路框图如下:

下面将程序设计中的相关问题逐一为大家做个说明。

46.3.1 ADC软件触发

ADC转换既可以选择外部触发也可以选择软件触发。我们这里选择的是软件触发方式的多通道转换,即连续转换序列,软件触发。对应的时序如下(在第44章的2.7小节有详细讲解软件触发和硬件触发的时序):。

ADSTART表示软件启动转换。

ADSTP表示停止转换。

EOC表示一个通道转换结束。

EOS表示所有通道转换结束。

关于这个时序图的解读:

  • 配置为连续转换的话,软件启动ADSTART会开启所有通道转换,全部转换完毕后,继续进行下一轮转换。调用了停止转换ADSTP后,会停止转换。

  • 每个通过转换完毕有个EOC标志,所有通道转换完毕有个EOS标志。

46.3.2 ADC时钟源选择

根据第44章2.2小节的讲解,我们知道ADC有两种时钟源可供选择,可以使用来自AHB总线的系统时钟,也可以使用PLL2,PLL3,HSE,HSI或者CSI时钟。

如果采用AHB时钟,不需要做专门的配置,而采用PLL2,PLL3时钟需要特别的配置,下面是使用AHB或者PLL2时钟的配置。

  • 通过宏定义设置选择的时钟源

使用哪个时钟源,将另一个注释掉即可:

/* 选择ADC的时钟源 */ #define ADC_CLOCK_SOURCE_AHB /* 选择AHB时钟源 */ //

#define ADC_CLOCK_SOURCE_PLL   /* 选择PLL时钟源 */

PLL2或者AHB时钟源配置

#if defined (ADC_CLOCK_SOURCE_PLL)

    /* 配置PLL2时钟为的72MHz,方便分频产生ADC最高时钟36MHz */

    RCC_PeriphCLKInitTypeDef PeriphClkInitStruct = {0};

    PeriphClkInitStruct.PeriphClockSelection = RCC_PERIPHCLK_ADC;

    PeriphClkInitStruct.PLL2.PLL2M = 25;

    PeriphClkInitStruct.PLL2.PLL2N = 504;

    PeriphClkInitStruct.PLL2.PLL2P = 7;

    PeriphClkInitStruct.PLL2.PLL2Q = 7;

    PeriphClkInitStruct.PLL2.PLL2R = 7;

    PeriphClkInitStruct.PLL2.PLL2RGE = RCC_PLL2VCIRANGE_0;

    PeriphClkInitStruct.PLL2.PLL2VCOSEL = RCC_PLL2VCOWIDE;

    PeriphClkInitStruct.PLL2.PLL2FRACN = 0;

    PeriphClkInitStruct.AdcClockSelection = RCC_ADCCLKSOURCE_PLL2;

    if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInitStruct) != HAL_OK)

    {

        Error_Handler(__FILE__, __LINE__);  

    }

#elif defined (ADC_CLOCK_SOURCE_AHB)

  

  /* 使用AHB时钟的话,无需配置,默认选择*/

  

#endif

对于PLL2的时钟输出,直接使用STM32CubeMX里面的时钟树配置即可,效果如下:

选择PLL2P输出作为ADC时钟源:

  • ADC分频设置

无论是使用AHB时钟还是PLL2时钟都支持分频设置:

AHB支持下面三种分频设置:


#define ADC_CLOCK_SYNC_PCLK_DIV1   ((uint32_t)ADC_CCR_CKMODE_0)  

#define ADC_CLOCK_SYNC_PCLK_DIV2   ((uint32_t)ADC_CCR_CKMODE_1) 

#define ADC_CLOCK_SYNC_PCLK_DIV4   ((uint32_t)ADC_CCR_CKMODE)   


#define ADC_CLOCKPRESCALER_PCLK_DIV1   ADC_CLOCK_SYNC_PCLK_DIV1   /* 这三个仅仅是为了兼容,已经不推荐使用 */

#define ADC_CLOCKPRESCALER_PCLK_DIV2   ADC_CLOCK_SYNC_PCLK_DIV2   

#define ADC_CLOCKPRESCALER_PCLK_DIV4   ADC_CLOCK_SYNC_PCLK_DIV4    

PLL2支持下面几种分频设置:


#define ADC_CLOCK_ASYNC_DIV1       ((uint32_t)0x00000000)                                       

#define ADC_CLOCK_ASYNC_DIV2       ((uint32_t)ADC_CCR_PRESC_0)                                  

#define ADC_CLOCK_ASYNC_DIV4       ((uint32_t)ADC_CCR_PRESC_1)                                   

#define ADC_CLOCK_ASYNC_DIV6       ((uint32_t)(ADC_CCR_PRESC_1|ADC_CCR_PRESC_0))                 

#define ADC_CLOCK_ASYNC_DIV8       ((uint32_t)(ADC_CCR_PRESC_2))                                

#define ADC_CLOCK_ASYNC_DIV10      ((uint32_t)(ADC_CCR_PRESC_2|ADC_CCR_PRESC_0))                 

#define ADC_CLOCK_ASYNC_DIV12      ((uint32_t)(ADC_CCR_PRESC_2|ADC_CCR_PRESC_1))                 

#define ADC_CLOCK_ASYNC_DIV16      ((uint32_t)(ADC_CCR_PRESC_2|ADC_CCR_PRESC_1|ADC_CCR_PRESC_0)) 

#define ADC_CLOCK_ASYNC_DIV32      ((uint32_t)(ADC_CCR_PRESC_3))                                

#define ADC_CLOCK_ASYNC_DIV64      ((uint32_t)(ADC_CCR_PRESC_3|ADC_CCR_PRESC_0))                 

#define ADC_CLOCK_ASYNC_DIV128     ((uint32_t)(ADC_CCR_PRESC_3|ADC_CCR_PRESC_1))                

#define ADC_CLOCK_ASYNC_DIV256     ((uint32_t)(ADC_CCR_PRESC_3|ADC_CCR_PRESC_1|ADC_CCR_PRESC_0)) 

有了这些认识后再看实际的分频配置就好理解了:


#if defined (ADC_CLOCK_SOURCE_PLL)

/* 采用PLL异步时钟,2分频,即72MHz/2 = 36MHz */

    AdcHandle.Init.ClockPrescaler        = ADC_CLOCK_ASYNC_DIV2;     

/* 采用AHB同步时钟,4分频,即200MHz/4 = 50MHz */     

#elif defined (ADC_CLOCK_SOURCE_AHB)

    AdcHandle.Init.ClockPrescaler        = ADC_CLOCK_SYNC_PCLK_DIV4;      

#endif

46.3.3 ADC的DMA配置

由于函数HAL_ADC_Start_DMA封装的DMA传输函数是HAL_DMA_Start_IT。而我们这里仅需要用到DMA传输,而用不到中断,所以不开启对应的NVIC即可,这里使用的是DMA1_Stream1,测量了PC0,Vbat/4,VrefInt和温度四个通道。


1.    /*

2.    ******************************************************************************************************

3.    *    函 数 名: bsp_InitADC

4.    *    功能说明: 初始化ADC,采用DMA方式进行多通道采样,采集了PC0, Vbat/4, VrefInt和温度

5.    *    形    参: 无

6.    *    返 回 值: 无

7.    ******************************************************************************************************

8.    */

9.    void bsp_InitADC(void)

10.    {

11.        ADC_HandleTypeDef   AdcHandle = {0};

12.        DMA_HandleTypeDef   DMA_Handle = {0};

13.        ADC_ChannelConfTypeDef   sConfig = {0};

14.        GPIO_InitTypeDef          GPIO_InitStruct;

15.    

16.      /* ## - 1 - 配置ADC采样的时钟 ####################################### */

17.    #if defined (ADC_CLOCK_SOURCE_PLL)

18.        /* 配置PLL2时钟为的72MHz,方便分频产生ADC最高时钟36MHz */

19.         RCC_PeriphCLKInitTypeDef PeriphClkInitStruct = {0};

20.        PeriphClkInitStruct.PeriphClockSelection = RCC_PERIPHCLK_ADC;

21.        PeriphClkInitStruct.PLL2.PLL2M = 25;

22.        PeriphClkInitStruct.PLL2.PLL2N = 504;

23.        PeriphClkInitStruct.PLL2.PLL2P = 7;

24.        PeriphClkInitStruct.PLL2.PLL2Q = 7;

25.        PeriphClkInitStruct.PLL2.PLL2R = 7;

26.        PeriphClkInitStruct.PLL2.PLL2RGE = RCC_PLL2VCIRANGE_0;

27.        PeriphClkInitStruct.PLL2.PLL2VCOSEL = RCC_PLL2VCOWIDE;

28.        PeriphClkInitStruct.PLL2.PLL2FRACN = 0;

29.        PeriphClkInitStruct.AdcClockSelection = RCC_ADCCLKSOURCE_PLL2;

30.        if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInitStruct) != HAL_OK)

31.        {

32.            Error_Handler(__FILE__, __LINE__);  

33.        }

34.    #elif defined (ADC_CLOCK_SOURCE_AHB)

35.      

36.      /* 使用AHB时钟的话,无需配置,默认选择*/

37.      

38.    #endif

39.    

40.        /* ## - 2 - 配置ADC采样使用的时钟 ####################################### */

41.        __HAL_RCC_GPIOC_CLK_ENABLE();

42.    

43.        GPIO_InitStruct.Pin = GPIO_PIN_0;

44.        GPIO_InitStruct.Mode = GPIO_MODE_ANALOG;

45.        GPIO_InitStruct.Pull = GPIO_NOPULL;

46.        HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);

47.      

48.        /* ## - 3 - 配置ADC采样使用的时钟 ####################################### */

49.        __HAL_RCC_DMA1_CLK_ENABLE();

50.        DMA_Handle.Instance                 = DMA1_Stream1;            /* 使用的DMA1 Stream1 */

51.        DMA_Handle.Init.Request             = DMA_REQUEST_ADC3;         /* 请求类型采用DMA_REQUEST_ADC3 */  

52.        DMA_Handle.Init.Direction           = DMA_PERIPH_TO_MEMORY;    /* 传输方向是从外设到存储器*/  

53.        DMA_Handle.Init.PeriphInc           = DMA_PINC_DISABLE;        /* 外设地址自增禁止 */ 

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

相关文章

    STM32入门编程总结(时钟+GPIO);时钟,单片机的时钟好比人的心脏,时钟歇了,单片机也就停止工作了,51单片机就一个时钟,12M、11.0592M居多,11.0592M这么......
    滤除传输过程中的高频干扰信号,在PCB布局时电阻电容应靠近单片机ADC管脚。 二极管D1为钳位二极管,用于保证在电路故障时(比如R2虚焊或者R2,R1电阻焊错位置等),或出现尖峰浪涌电压时,VF1可以......
    基于单片机实现多通道数据综合采集系统的应用方案;1.前言 在工业控制中需要对各种参量进行采集,即利用信号采集系统将各种数据采集到计算机中进行实时处理。传感器起着中间桥梁的作用,但是......
    ADC管脚。二极管D1为钳位二极管,用于保证在电路故障时(比如R2虚焊或者R2,R1电阻焊错位置等),或出现尖峰浪涌电压时,VF1可以保持在一个安全电压,不至于损坏单片机。电路中D1应选......
    频率应用。相较于Avago前一代产品,ACPL-P/W345和ACPL-P/W346在传播延迟上减低了一半。 产品特点 120ns最大传播延迟 2.5A最大尖峰输出电流(ACPL-P/W346......
    篇幅关系没有写完,这一期把它们补上。同时我还要继续介绍单片机的多个重要功能。之所以说“重要”,是因为单片机如果没有这些功能,虽然可以正常工作,但其性能和所发挥的作用会大大减弱。重要功能包括:低功耗模式、ADC、DMA、I......
    的外设:STM32单片机集成了丰富的外设,如GPIO、UART、SPI、I2C、ADC、DAC、PWM、RTC等,能够满足各种应用的需求。 易于开发:STM32单片机提供了丰富的开发工具和软件库,如Keil......
    少,因此大多数 GPIO 都有很多复用功能。出厂就集成蓝牙、WiFi 等物联网必备功能,板子也很小,适合物联网。 01 GD32 和 STM32 的区别 GD32 是国产单片机,据说开发人员来自ST......
    少,因此大多数 GPIO 都有很多复用功能。出厂就集成蓝牙、WiFi 等物联网必备功能,板子也很小,适合物联网。 GD32 和 STM32 的区别 GD32 是国产单片机,据说开发人员来自ST公司......
    脚很少,因此大多数 GPIO 都有很多复用功能。出厂就集成蓝牙、WiFi 等物联网必备功能,板子也很小,适合物联网。 02  GD32 和 STM32 的区别 GD32 是国产单片机, 和 STM32......

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

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

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

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

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

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

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