【STM32H7教程】第45章 STM32H7的ADC应用之定时器触发配合DMA双缓冲

发布时间:2023-04-13  

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

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

第2阶段,进入main函数:

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

  • 第2步,应用程序设计部分,周期性的打印数据,方便查看。

  • 第3步,DMA中断,以双缓冲方式存储ADC数据。

45.7 实验例程说明(MDK)

配套例子:

V7-019_ADC定时器触发+DMA双缓冲实现

实验目的:

  1. 学习ADC定时器触发 + DMA双缓冲的实现。

实验内容:

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

  2. 使用的TIM1的OC1作为ADC的外部触发源,触发速度是100KHz,即ADC的采样率也是100KHz。

  3. 使用DMA的半传输完成中断和传输完成中断实现数据的双缓冲更新。

  4. 采集引脚使用的PC0,另外特别注意开发板上的Vref稳压基准跳线帽短接的3.3V。

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

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

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

上电后串口打印的信息:

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

程序设计:

系统栈大小分配:

RAM空间用的DTCM:

硬件外设初始化

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

45.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? 。

45.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。

45.3 ADC驱动设计

定时器触发ADC做DMA数据传输的实现思路框图如下:

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

45.3.1 触发ADC的定时器选择和配置

ADC转换既可以选择外部触发也可以选择软件触发。定时器属于外部触发方式,使用定时器触发的好处是可以设置任何ADC能够支持的转换频率。

对于ADC1,ADC2,ADC3来说,规则通道支持的外部触发源如下:

#define ADC_EXTERNALTRIG_T1_CC1           ((uint32_t)0x00000000)

#define ADC_EXTERNALTRIG_T1_CC2           ((uint32_t)ADC_CFGR_EXTSEL_0)

#define ADC_EXTERNALTRIG_T1_CC3           ((uint32_t)ADC_CFGR_EXTSEL_1)

#define ADC_EXTERNALTRIG_T2_CC2           ((uint32_t)(ADC_CFGR_EXTSEL_1 | ADC_CFGR_EXTSEL_0))

#define ADC_EXTERNALTRIG_T3_TRGO          ((uint32_t)ADC_CFGR_EXTSEL_2)

#define ADC_EXTERNALTRIG_T4_CC4           ((uint32_t)(ADC_CFGR_EXTSEL_2 | ADC_CFGR_EXTSEL_0))

#define ADC_EXTERNALTRIG_EXT_IT11         ((uint32_t)(ADC_CFGR_EXTSEL_2 | ADC_CFGR_EXTSEL_1))

#define ADC_EXTERNALTRIG_T8_TRGO          ((uint32_t)(ADC_CFGR_EXTSEL_2 | ADC_CFGR_EXTSEL_1 |

 ADC_CFGR_EXTSEL_0))

#define ADC_EXTERNALTRIG_T8_TRGO2         ((uint32_t) ADC_CFGR_EXTSEL_3)

#define ADC_EXTERNALTRIG_T1_TRGO          ((uint32_t)(ADC_CFGR_EXTSEL_3 | ADC_CFGR_EXTSEL_0))

#define ADC_EXTERNALTRIG_T1_TRGO2         ((uint32_t)(ADC_CFGR_EXTSEL_3 | ADC_CFGR_EXTSEL_1))

#define ADC_EXTERNALTRIG_T2_TRGO          ((uint32_t)(ADC_CFGR_EXTSEL_3 | ADC_CFGR_EXTSEL_1 | ADC_CFGR_EXTSEL_0))

#define ADC_EXTERNALTRIG_T4_TRGO          ((uint32_t)(ADC_CFGR_EXTSEL_3 | ADC_CFGR_EXTSEL_2))

#define ADC_EXTERNALTRIG_T6_TRGO          ((uint32_t)(ADC_CFGR_EXTSEL_3 | ADC_CFGR_EXTSEL_2 | ADC_CFGR_EXTSEL_0))

#define ADC_EXTERNALTRIG_T15_TRGO         ((uint32_t)(ADC_CFGR_EXTSEL_3 | ADC_CFGR_EXTSEL_2 | ADC_CFGR_EXTSEL_1))

#define ADC_EXTERNALTRIG_T3_CC4           ((uint32_t)(ADC_CFGR_EXTSEL_3 | ADC_CFGR_EXTSEL_2 | ADC_CFGR_EXTSEL_1 | ADC_CFGR_EXTSEL_0))

#define ADC_EXTERNALTRIG_HR1_ADCTRG1      ((uint32_t) ADC_CFGR_EXTSEL_4)

#define ADC_EXTERNALTRIG_HR1_ADCTRG3      ((uint32_t) (ADC_CFGR_EXTSEL_4 | ADC_CFGR_EXTSEL_0))

#define ADC_EXTERNALTRIG_LPTIM1_OUT       ((uint32_t) (ADC_CFGR_EXTSEL_4 | ADC_CFGR_EXTSEL_1))

#define ADC_EXTERNALTRIG_LPTIM2_OUT       ((uint32_t) (ADC_CFGR_EXTSEL_4 | ADC_CFGR_EXTSEL_1| ADC_CFGR_EXTSEL_0))

#define ADC_EXTERNALTRIG_LPTIM3_OUT       ((uint32_t) (ADC_CFGR_EXTSEL_4 | ADC_CFGR_EXTSEL_2))

我们这里使用的是TIM1_CC1。


接下来就是TIM1的时钟配置问题,代码如下:


1.    /*

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

3.    *    函 数 名: TIM1_Config

4.    *    功能说明: 配置TIM1,用于触发ADC,当前配置的100KHz触发频率

5.    *    形    参: 无                                      

6.    *    返 回 值: 无

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

8.    */

9.    static void TIM1_Config(void)

10.    {

11.        TIM_HandleTypeDef  htim ={0};

12.        TIM_OC_InitTypeDef sConfig = {0};

13.    

14.    

15.        /* 使能时钟 */  

16.        __HAL_RCC_TIM1_CLK_ENABLE();

17.          

18.        /*-----------------------------------------------------------------------

19.            bsp.c 文件中 void SystemClock_Config(void) 函数对时钟的配置如下: 

20.    

21.            System Clock source       = PLL (HSE)

22.            SYSCLK(Hz)                = 400000000 (CPU Clock)

23.            HCLK(Hz)                  = 200000000 (AXI and AHBs Clock)

24.            AHB Prescaler             = 2

25.            D1 APB3 Prescaler         = 2 (APB3 Clock  100MHz)

26.            D2 APB1 Prescaler         = 2 (APB1 Clock  100MHz)

27.            D2 APB2 Prescaler         = 2 (APB2 Clock  100MHz)

28.            D3 APB4 Prescaler         = 2 (APB4 Clock  100MHz)

29.    

30.            因为APB1 prescaler != 1, 所以 APB1上的TIMxCLK = APB1 x 2 = 200MHz;

31.            因为APB2 prescaler != 1, 所以 APB2上的TIMxCLK = APB2 x 2 = 200MHz;

32.            APB4上面的TIMxCLK没有分频,所以就是100MHz;

33.    

34.            APB1 定时器有 TIM2, TIM3 ,TIM4, TIM5, TIM6, TIM7, TIM12, TIM13, TIM14,LPTIM1

35.            APB2 定时器有 TIM1, TIM8 , TIM15, TIM16,TIM17

36.    

37.            APB4 定时器有 LPTIM2,LPTIM3,LPTIM4,LPTIM5

38.    

39.        TIM12CLK = 200MHz/(Period + 1) / (Prescaler + 1) = 200MHz / 2000 / 1 = 100KHz

40.        ----------------------------------------------------------------------- */  

41.         HAL_TIM_Base_DeInit(&htim);

42.        

43.         htim.Instance = TIM1;

44.        htim.Init.Period            = 1999;

45.        htim.Init.Prescaler         = 0;

46.        htim.Init.ClockDivision     = 0;

47.        htim.Init.CounterMode       = TIM_COUNTERMODE_UP;

48.        htim.Init.RepetitionCounter = 0;

49.        HAL_TIM_Base_Init(&htim);

50.        

51.        sConfig.OCMode     = TIM_OCMODE_PWM1;

52.        sConfig.OCPolarity = TIM_OCPOLARITY_LOW;

53.    

54.        /* 占空比50% */

55.        sConfig.Pulse = 1000;  

56.        if(HAL_TIM_OC_ConfigChannel(&htim, &sConfig, TIM_CHANNEL_1) != HAL_OK)

57.        {

58.            Error_Handler(__FILE__, __LINE__);

59.        }

60.    

61.        /* 启动OC1 */

62.        if(HAL_TIM_OC_Start(&htim, TIM_CHANNEL_1) != HAL_OK)

63.        {

64.            Error_Handler(__FILE__, __LINE__);

65.        }

66.    }

这里把几个关键的地方阐释下:


第11 – 12行,对作为局部变量的HAL库结构体做初始化,防止不确定值配置时出问题。

第18 – 65行,注释已经比较详细,配置TIM1的频率是100KHz,这个速度就是ADC的触发频率。

TIM1CLK = 200MHz / (Period + 1) / (Prescaler + 1) = 200MHz/(1999+1)/(0+1) = 100KHz


占空比 = Pulse / (Period + 1) = 1000 / (1999+1)= 50%


这些知识点在前面的定时器章节有更详细的说明。


45.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;

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

相关文章

    将程序烧录到STM32单片机上,并运行程序,实现STM32单片机与外部设备之间的数据传输。 使用STM32单片机通信协议时,需要注意以下几点: 1. 首先,需要......
    ,还是应用最广泛的一种。需要注意的是52系列的单片机一般不具备自编程能力。   当前常用的51系列单片机主要产品有:   *Intel的:80C31、80C51、87C51,80C32......
    STM32单片机学习笔记(5):ADC模数转换器;项目简介 利用CubMX生成基于32单片机的HAl库工程,然后编写程序在proteus上仿真验证。本项目最适合没有开发板的同学学习,零成......
    很长的一段时间内将占有大量市场。51单片机是基础入门的一个单片机,还是应用最广泛的一种。需要注意的是51系列的单片机一般不具备自编程能力。   单片机的串口通信看起来是很复杂的,主要是因为他用到了更多的寄存器,与前......
    升级程序则进入bootloader区进行代码更新;若不需要则继续运行功能函数代码即可。 根据运行流程,我们可以总结出简单几条bootloader设计过程中需要注意的地方: 精简、程序尽可能精简。在单片机Flash有限......
    基于AT89C51单片机的自行车仪表系统设计;一.系统概述 系统使用的模块有AT89C51单片机+小灯+按键+ADC0832+DS1302时钟模块。 系统内使用AT89C51单片机作为主控,检测......
    及后续的波形显示和分析。笔者提出了ADC0832与压力传感器(PTB710)相结合,并利用单片机AT89S52的程序控制转换时钟脉冲方法,对真空度数据进行检测。 1、A/D转换电路 1.1、ADC0832芯片......
    区进行代码更新;若不需要则继续运行功能函数代码即可。 因此IAP编程下的单片机运行流程如下图: 根据运行流程,我们可以总结出简单几条bootloader设计过程中需要注意的地方: 精简、程序......
    端口与单片机的接口是双向的,所以在设计电路中可以用一根线将DO端和DI端连接到一起。 (2)ADC0832的外部连接采用SPI总线结构,这样便把它的连接方式与其他设备统一起来了。ADC0832采用......
    使用的晶振都是11.0592MHz的了吧!虽然使用12MHz的晶振进行通信计算机也能捕获到数据,但为了保险起见,若日后你使用51单片机开发项目需要使用到串口时要注意晶振的选择,当然如果使用其他单片机......

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

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

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

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

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

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

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