OpenEdv-开源电子网

 找回密码
 立即注册
正点原子全套STM32/Linux/FPGA开发资料,上千讲STM32视频教程免费下载...
查看: 6968|回复: 0

《MiniPRO H750开发指南》第二十二章 高级定时器实验(上)

[复制链接]

1140

主题

1152

帖子

2

精华

超级版主

Rank: 8Rank: 8

积分
4896
金钱
4896
注册时间
2019-5-8
在线时间
1248 小时
发表于 2022-12-26 15:12:40 | 显示全部楼层 |阅读模式
本帖最后由 正点原子运营 于 2022-12-26 15:13 编辑

第二十二章 高级定时器实验

1)实验平台:正点原子MiniPro STM32H750开发板

2) 章节摘自【正点原子】MiniPro STM32H750 开发指南_V1.1


4)全套实验源码+手册+视频下载地址:http://www.openedv.com/docs/boards/stm32/zdyz_stm32h750_minipro.html

5)正点原子官方B站:https://space.bilibili.com/394620890

6)MiniPro STM32H750技术交流QQ群:170313895

155537c2odj87vz1z9vj6l.jpg

155537nfqovl2gg9faaol9.png

本章我们主要来学习高级定时器, STM32H750有2个高级定时器(TIM1和TIM8)。我们将通过四个实验来学习高级定时器的各个功能,分别是高级定时器输出指定个数PWM实验、高级定时器输出比较模式实验、高级定时器互补输出带死区控制实验和高级定时器PWM输入模式实验。
本章分为如下几个小节:
22.1 高级定时器简介
22.2 高级定时器输出指定个数PWM实验
22.3 高级定时器输出比较模式实验
22.4 高级定时器互补输出带死区控制实验
22.5 高级定时器PWM输入模式实验

22.1 高级定时器简介
高级定时器的框图和通用定时器框图很类似,只是添加了其它的一些功能,如:重复计数器、带死区控制的互补输出通道、断路输入等。这些功能在高级定时器框图的位置如下:
image001.png
图22.1.1 高级定时器框图
上图中,框出来三个部分,这是和通用定时器不同的地方,下面来分别介绍它们。

①  重复计数器
在H7系列中,除了高级定时器TIM1和TIM8之外,通用定时器TIM15、TIM16和TIM17也有重复计数器。通过查看手册可以知道TIM15、TIM16和TIM17虽为通用定时器,但是高级定时器有的功能,它们也基本都有,所以它们是很特殊的。

下面来介绍一下重复计数器有什么作用?在学习基本定时器和通用定时器的时候,我们知道定时器发生上溢或者下溢时,会直接生成更新事件。但是有重复计数器的定时器并不完全是这样的,定时器每次发生上溢或下溢时,重复计数器的值会减一,当重复计数器的值为0时,再发生一次上溢或者下溢才会生成定时器更新事件。如果我们设置重复计数器寄存器RCR的值为N,那么更新事件将在定时器发生N+1次上溢或下溢时发生。
这里需要注意的是重复计数器寄存器是具有影子寄存器的,所以RCR寄存器只是起缓冲的作用。RCR寄存器的值会在更新事件发生时,被转移至其影子寄存器中,从而真正生效。

重复计数器的特性,在控制生成PWM信号时很有用,后面会有相应的实验。

②  输出比较
高级定时器输出比较部分和通用定时器相比,多了带死区控制的互补输出功能。图22.1.1第②部分的TIMx_CH1N、TIMx_CH2N和TIMx_CH3N分别是定时器通道1、通道2和通道3的互补输出通道,通道4是没有互补输出通道的。DTG是死区发生器,死区时间由DTG[7:0]位来配置如果不使用互补通道和死区时间控制,那么高级定时器TIM1/8,以及TIM15/16/17和其它通用定时器的输出比较部分使用方法基本一样,只是要注意MOE位得置1定时器才能输出。

如果使用互补通道,那么就有一定的区别了,具体我们在高级定时器互补输出带死区控制实验小节再来介绍。

③  断路功能
断路功能也称刹车功能,一般用于电机控制的刹车。H7系列有两个断路通道。断路1通道可以收集系统级故障(时钟失效和奇偶校验错误等)和应用故障(来自输入引脚:TIMx_BKIN或者TIMx_BKIN2,还可以来自内置比较器),可以在死区持续时间后将输出强制为预定义的电平(有效或无效)。断路2通道只包含应用故障,能够将输出强制为无效状态。系统复位后,断路功能默认被禁止,MOE位为低。

使能断路功能的方法:将TIMx_BDTR的位BKE(或BK2E)置1。断路输入引脚TIMx_BKIN或者TIMx_BKIN2的输入有效电平可通过TIMx_BDTR寄存器的位BKP(或BK2P)设置。

使能刹车功能后:由TIMx_BDTR的MOE、OSSI、OSSR位,TIMx_CR2的OISx、OISxN位,TIMx_CCER的CCxE、CCxNE位控制OCx和OCxN输出状态。无论何时,OCx和OCxN输出都不能同时处在有效电平。

当发生断路输入后,会怎么样?
1,MOE位被异步地清零,OCx和OCxN为无效、空闲或复位状态(由OSSI位选择)。
2,OCx和OCxN的状态:由相关控制位状态决定,当使用互补输出时:根据情况自动控制输出电平,参考《STM32H7xx参考手册_V3(中文版).pdf》手册1465页的表313.具有断路功能的互补通道Ocx和OcxN的输出控制位。
3,BIF位置1,如果使能了BIE位,还会产生刹车中断;如果使能了TDE位,会产生DMA请求。
4,如果AOE位置 1,在下一个更新事件UEV时,MOE位被自动置 1。

高级定时器框图部分就简单介绍到这里,下面通过实际的实验来学习高级定时器。

22.2 高级定时器输出指定个数PWM实验
声明:本实验虽然名为高级定时器输出指定个数PWM实验,实际上我们以通用定时器15作为例子,前面也说过TIM15/16/17是比较特殊的通用定时器。只要有重复计数器的定时器就可以实现本实验的功能,比如高级定时器TIM1/ 8、通用定时器TIM15/16/17,这个请大家打开官方手册确认。本实验我们使用了通用定时器15
要实现定时器输出指定个数PWM,只需要掌握下面几点内容:
第一,如果大家还不清楚定时器是如何输出PWM的,请回顾通用定时器PWM输出实验的内容,这部分的知识是一样的。但是需要注意的是:本实验,我们需要把MOE位置1,这样定时器15的通道才能输出。
第二,要清楚重复计数器特性,设置重复计数器寄存器RCR的值为N,那么更新事件将在定时器发生N+1次上溢或下溢时发生。换句话来说就是,想要指定输出N个PWM,只需要把N-1写入RCR寄存器。因为在边沿对齐模式下,定时器溢出周期对应着PWM周期,我们只要在更新事件发生时,停止输出PWM就行。
第三,为了保证定时器输出指定个数的PWM后,定时器马上停止继续输出,我们使能更新中断,并在定时器中断里关闭计数器。

原理部分我们就讲到这里,下面直接开始寄存器的介绍。

22.2.1 TIM15寄存器
本实验用到定时器15,在《STM32H7xx参考手册_V3(中文版).pdf》手册的41.5小节(1644页)可以找到TIM15的寄存器描述。这里我们将介绍TIM15寄存器里我们用到的几个重要的寄存器,具体如下:

l  控制寄存器 1(TIM15_CR1
TIM15的控制寄存器1描述如图22.2.1.1所示:
    image003.png
图22.2.1.1 TIM15_CR1寄存器

上图中我们只列出了本章需要用的一些位,其中:位7(APRE)用于控制自动重载寄存器是否具有缓冲作用,在基本定时器的时候已经讲过,请回顾。在本实验中我们把该位要置1,这样就算改变ARR寄存器的值,该值也不会马上生效,而是等待之前设置的PWM完整输出后(发生更新事件)才生效。
因为TIM15只有递增计数模式,所以不用设置计数模式。
CEN位,用于使能计数器的工作,必须要设置该位为1,才可以开始计数。

l  捕获/比较模式寄存器1(TIM15_CCMR1
TIM15的捕获/比较模式寄存器1,只有1个:TIM15 _CCMR1。TIM15_CCMR1刚好可以控制定时器15的两个通道CH1和CH2。TIM15_CCMR1寄存器描述如图22.2.1.2所示:
      image005.png
图22.2.1.2 TIM15_CCMR1寄存器

该寄存器的有些位在不同模式下,功能不一样,我们前面已经说过。比如我们要让TIM15的CH1输出PWM波为例,该寄存器的模式设置位OC1M[3:0]就是对应着通道1的模式设置,此部分由4位组成。TIM15总共可以配置成12种模式,我们使用的是PWM模式,所以这4位必须设置为0110或者0111,分别对应PWM模式1和PWM模式2。这两种PWM模式的区别就是输出有效电平的极性相反,这里我们设置为PWM模式1。位3 OC1PE是输出比较通道1的预装使能,该位需要置1,另外CC1S[1:0]用于设置通道1的方向(输入/输出)默认设置为0,就是设置通道作为输出使用。

l  捕获/比较使能寄存器(TIM15_ CCER
TIM15的捕获/比较使能寄存器,该寄存器控制着各个输入输出通道的开关。TIM15_CCER寄存器描述如图22.2.1.3所示:
    image007.png   
图22.2.1.3 TIM15_CCER寄存器

该寄存器比较简单,要让TIM15的CH1输出PWM波,这里我们要使能CC1E位,该位是通道1输入/输出使能位,要想PWM从IO口输出,这个位必须设置为1。CC1P位是设置通道1的输出极性,我们默认设置0,即OC1 高电平有效。

l  事件产生寄存器(TIM15_ EGR
TIM15的事件产生寄存器,该寄存器作用是让用户用软件方式产生各类事件。TIM15_EGR寄存器描述如图22.2.1.4所示:
image009.png      
图22.2.1.4 TIM15_EGR寄存器

UG位是更新事件的控制位,作用和定时器溢出时产生的更新事件一样,区别是这里是通过软件产生的,而定时器溢出是硬件自己完成的。只有开启了更新中断,这两种方式都可以产更新中断。本实验用到该位去产生软件更新器事件,在需要的时候把UG位置1即可,会由硬件自动清零。

l  重复计数器寄存器(TIM15_ RCR
重复计数器寄存器用于设置重复计数器值,因为它具有影子寄存器,所以它本身只是起缓冲作用。当更新事件发生时,该寄存器的值会转移到其影子寄存器中,从而真正起作用。TIM15_ RCR寄存器描述如图22.2.1.5所示:
image011.png
图22.2.1.5 TIM15_RCR寄存器

该寄存器的REP[7:0]位是低8位有效,即最大值255。因为这个寄存器只是起缓冲作用,如果大家对该寄存器写入值后,想要立即生效,可以通过对UG位写1,产生软件更新事件。

l  捕获/比较寄存器1/2(TIM15_ CCR1/2
捕获/比较寄存器(TIM15_CCR1/2),该寄存器总共有2个,对应TIM15的2个通道CH1和CH2。我们使用的是通道1,所以来看看TIM15_ CCR1寄存器描述如图22.2.1.6所示:
      image013.png
图22.2.1.6 TIM15_ CCR1寄存器

在输出模式下,捕获/比较寄存器影子寄存器的值与CNT的值比较,根据比较结果产生相应动作,利用这点,我们通过修改这个寄存器的值,就可以控制PWM的占空比了。

l  断路和死区寄存器(TIM5_ BDTR
STMH7系列,通用定时器TIM15/16/17,以及高级定时器TIM1/8在输出模式下,还需要配置:断路和死区寄存器(TIMx_BDTR),该寄存器各位描述如图22.2.1.7所示:
image015.png    
图22.2.1.7 TIMx_ BDTR寄存器

本实验,我们只需要关注该寄存器的位15(MOE),要想高级定时器的PWM正常输出,则必须设置MOE位为1,否则不会有输出。

22.2.2 硬件设计
1. 例程功能
通过TIM15_CH1(由PE5复用)输出PWM,来实现PWM输出控制LED0的亮灭。上电默认输出5个PWM控制LED2亮灭五次。之后按一下按键KEY0,就会输出5个PWM控制LED2亮灭五次。

2. 硬件资源
1)RGB灯
   BLUE :LED2 - PE5
1)  独立按键
KEY0 - PA1
3)定时器15,使用TIM15通道1,由PE5复用。

3. 原理图
定时器属于STM32H750的内部资源,只需要软件设置好即可正常工作。我们通过LED2来指示STM32H750的定时器的PWM输出情况,同时还可以用按键KEY0进行控制。

22.2.3 程序设计
本实验用到的HAL库函数介绍请回顾通用定时器PWM输出实验。下面介绍一下定时器输出指定个数PWM的配置步骤。

定时器输出指定个数PWM配置步骤
1)开启TIMx和通道输出的GPIO时钟,配置该IO口的复用功能输出
首先开启TIMx的时钟,然后配置GPIO为复用功能输出。本实验我们默认用到定时器15通道1,对应IO是PE5,它们的时钟开启方法如下:
  1. __HAL_RCC_TIM15_CLK_ENABLE();            /* 使能定时器15 */
  2. __HAL_RCC_GPIOE_CLK_ENABLE();            /* 开启GPIOE时钟 */
复制代码
IO口复用功能是通过函数HAL_GPIO_Init来配置的。

2)初始化TIMx,设置TIMx的ARR和PSC等参数
使用定时器的PWM模式功能时,我们调用的是HAL_TIM_PWM_Init函数来初始化定时器ARR和PSC等参数。
注意:该函数会调用:HAL_TIM_PWM_MspInit函数,我们可以通过后者存放定时器和GPIO时钟使能、GPIO初始化、中断使能以及优先级设置等代码。

3)设置定时器为PWM模式,输出比较极性,比较值等参数
在HAL库中,通过HAL_TIM_PWM_ConfigChannel函数来设置定时器为PWM1模式或者PWM2模式,根据需求设置输出比较的极性,设置比较值(控制占空比)等。
本实验我们设置TIM15的通道1为PWM1模式,因为我们的LED2(连接PE5)是低电平亮,而我们希望输出最后一个PWM波的时候,LED2就灭,所以我们设置输出比较极性为高。捕获/比较寄存器的值(即比较值)设置为自动重装载值的一半,即PWM占空比为50%。

4)使能定时器更新中断,开启定时器并输出PWM,配置定时器中断优先级
通过__HAL_TIM_ENABLE_IT函数使能定时器更新中断。
通过HAL_TIM_PWM_Start函数使能定时器并开启输出PWM。
通过HAL_NVIC_EnableIRQ函数使能定时器中断。
通过HAL_NVIC_SetPriority函数设置中断优先级。

5)编写中断服务函数
定时器中断服务函数为:TIMx_IRQHandler等,当发生中断的时候,程序就会执行中断服务函数。HAL库提供了一个定时器中断公共处理函数HAL_TIM_IRQHandler,该函数会根据中断类型调用相关的中断回调函数。用户根据自己的需要重定义这些中断回调函数来处理中断程序。本实验我们不使用HAL库的中断回调机制,而是把中断程序写在定时器中断服务函数里。详见本章例程源码。

22.2.3.1 程序流程图
image018.png

图22.2.3.2.1高级定时器输出指定个数PWM实验程序流程图

22.2.3.2 程序解析
这里我们只讲解核心代码,详细的源码请大家参考光盘本实验对应源码。本实验使用到TIM15,所以相关的BSP驱动代码还是存放到gtim.c和gtim.h文件中。
首先看gtim.h头文件的几个宏定义:
  1. /*****************************************************************************/
  2. /* TIMX 输出指定个数PWM 定义
  3. * 这里输出的PWM控制LED2(BLUE)的亮灭,亮灭一次表示一个PWM波
  4. * 默认是针对TIM2~TIM5, TIM12~TIM17.
  5. * 注意: 通过修改这8个宏定义,可以支持TIM1~TIM17任意一个定时器,任意一个IO口输出PWM
  6. */
  7. #define GTIM_TIMX_NPWM_CHY_GPIO_PORT        GPIOE
  8. #define GTIM_TIMX_NPWM_CHY_GPIO_PIN         GPIO_PIN_5
  9. #define GTIM_TIMX_NPWM_CHY_GPIO_AF          GPIO_AF4_TIM15   /* AF功能选择 */
  10. #define GTIM_TIMX_NPWM_CHY_GPIO_CLK_ENABLE()
  11. do{ __HAL_RCC_GPIOE_CLK_ENABLE(); }while(0)   /* PE口时钟使能 */
  12. #define GTIM_TIMX_NPWM                     TIM15                       
  13. #define GTIM_TIMX_NPWM_IRQn               TIM15_IRQn
  14. #define GTIM_TIMX_NPWM_IRQHandler        TIM15_IRQHandler
  15. #define GTIM_TIMX_NPWM_CHY                TIM_CHANNEL_1   /* 通道Y,  1<= Y <=4*/
  16. #define GTIM_TIMX_NPWM_CHY_CCRX          TIM15->CCR1     /* 通道Y的输出比较寄存器 */
  17. #define GTIM_TIMX_NPWM_CHY_CLK_ENABLE()   
  18.      do{__HAL_RCC_TIM15_CLK_ENABLE(); }while(0) /* TIMX 时钟使能 */
  19. /*****************************************************************************/
复制代码
可以把上面的宏定义分成两部分,第一部分是定时器15输入通道1对应的IO口的宏定义,第二部分则是定时器15输入通道1的相应宏定义。
下面看gtim.c的程序,首先是输出指定个数PWM初始化函数,其定义如下:
  1. /**
  2. *@brief      通用定时器TIMX 通道Y 输出指定个数PWM 初始化函数
  3. *@note
  4. *             通用定时器的时钟来自APB1,当D2PPRE1≥2分频的时候
  5. *             通用定时器的时钟为APB1时钟的2倍, 而APB1为120M, 所以定时器时钟 = 240Mhz
  6. *             定时器溢出时间计算方法: Tout = ((arr + 1) * (psc + 1)) / Ft us.
  7. *             Ft=定时器工作频率,单位:Mhz
  8. *
  9. *@param       arr: 自动重装值
  10. *@param       psc: 时钟预分频数
  11. *@retval      无
  12. */
  13. voidgtim_timx_npwm_chy_init(uint16_t arr,uint16_t psc)
  14. {
  15.    GPIO_InitTypeDef gpio_init_struct;
  16.    TIM_OC_InitTypeDef timx_oc_npwm_chy = {0};          /* 定时器输出 */
  17.    GTIM_TIMX_NPWM_CHY_GPIO_CLK_ENABLE();                /* 开启通道y的GPIO时钟 */
  18.    GTIM_TIMX_NPWM_CHY_CLK_ENABLE();
  19.    g_timx_npwm_chy_handle.Instance = GTIM_TIMX_NPWM;  /* 定时器x */
  20.    g_timx_npwm_chy_handle.Init.Prescaler = psc;        /* 定时器分频 */
  21.    g_timx_npwm_chy_handle.Init.CounterMode = TIM_COUNTERMODE_UP;/* 递增计数 */
  22.    g_timx_npwm_chy_handle.Init.Period = arr;            /* 自动重装载值 */
  23. g_timx_npwm_chy_handle.Init.AutoReloadPreload =
  24. TIM_AUTORELOAD_PRELOAD_ENABLE; /* 使能TIMx_ARR 寄存器进行缓冲 */
  25.    g_timx_npwm_chy_handle.Init.RepetitionCounter = 0; /* 重复计数器初始值 */
  26.    HAL_TIM_PWM_Init(&g_timx_npwm_chy_handle);          /* 初始化PWM */
  27.    gpio_init_struct.Pin = GTIM_TIMX_NPWM_CHY_GPIO_PIN;/* 通道y的GPIO口 */
  28.    gpio_init_struct.Mode = GPIO_MODE_AF_PP;             /* 复用推挽输出 */
  29.    gpio_init_struct.Pull = GPIO_PULLUP;                       /* 上拉 */
  30.    gpio_init_struct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;      /* 高速 */
  31.    gpio_init_struct.Alternate = GTIM_TIMX_NPWM_CHY_GPIO_AF;/* 复用 */
  32.    HAL_GPIO_Init(GTIM_TIMX_NPWM_CHY_GPIO_PORT, &gpio_init_struct);
  33.    timx_oc_npwm_chy.OCMode = TIM_OCMODE_PWM1;    /* 模式选择PWM1 */
  34.    timx_oc_npwm_chy.Pulse = arr/2;                 /* 设置比较值,此值用来确定占空比 */
  35.    timx_oc_npwm_chy.OCPolarity = TIM_OCPOLARITY_HIGH;  /* 输出比较极性为高 */
  36. HAL_TIM_PWM_ConfigChannel(&g_timx_npwm_chy_handle, &timx_oc_npwm_chy,
  37. GTIM_TIMX_NPWM_CHY);      /* 配置TIMx通道y */
  38. /* 设置中断优先级,抢占优先级1,子优先级3 */
  39.    HAL_NVIC_SetPriority(GTIM_TIMX_NPWM_IRQn, 1, 3);                    
  40.    HAL_NVIC_EnableIRQ(GTIM_TIMX_NPWM_IRQn);            /* 开启ITMx中断 */
  41.    __HAL_TIM_ENABLE_IT(&g_timx_npwm_chy_handle, TIM_IT_UPDATE);/* 使能更新中断 */
  42.    HAL_TIM_PWM_Start(&g_timx_npwm_chy_handle, GTIM_TIMX_NPWM_CHY);/* 使能输出 */
  43. }
复制代码
gtim_timx_npwm_chy_init函数包含了输出通道对应IO的初始代码、NVIC、使能时钟、定时器基础工作参数和输出模式配置的所有代码。下面来看看该函数的代码内容。

第一部分使能定时器和GPIO的时钟。
第二部分调用HAL_TIM_PWM_Init函数初始化定时器基础工作参数,如:ARR和PSC等。
第三部分是定时器输出通道对应的IO的初始化。
第四部分调用HAL_TIM_PWM_ConfigChannel设置PWM模式以及比较值等参数。
第五部分是NVIC的初始化,配置抢占优先级、响应优先级和开启NVIC定时器中断。
最后是使能更新中断和使能通道输出。

为了方便代码的管理和移植性等,这里就没有使用HAL_TIM_PWM_MspInit这个函数来存放使能时钟、GPIO、NVIC相关的代码,而是全部存放在gtim_timx_npwm_chy_init函数中。
下面我们看设置PWM个数的函数,其定义如下:
  1. /*g_npwm_remain表示当前还剩下多少个脉冲要发送
  2. * 每次最多发送256个脉冲
  3. */
  4. static uint32_t g_npwm_remain = 0;
  5. /**
  6. * @brief       通用定时器TIMX NPWM设置PWM个数
  7. * @param       rcr: PWM的个数, 1~2^32次方个
  8. * @retval      无
  9. */
  10. void gtim_timx_npwm_chy_set(uint32_t npwm)
  11. {
  12.     if (npwm == 0)return ;
  13. g_npwm_remain = npwm;                           /* 保存脉冲个数 */
  14. /* 产生一次更新事件,在中断里面处理脉冲输出 */
  15.    HAL_TIM_GenerateEvent(&g_timx_npwm_chy_handle,TIM_EVENTSOURCE_UPDATE);
  16.     __HAL_TIM_ENABLE(&g_timx_npwm_chy_handle);  /* 使能定时器TIMX */
  17. }
复制代码
我们要输出多少个周期的PWM就用这个函数来设置。该函数作用是把我们设置输出的PWM个数的值赋值给静态全局变量g_npwm_remain,该变量会在更新中断服务函数回调函数中发挥作用。最后对TIMx_EGR寄存器UG位写1,产生一次更新事件,并使能定时器。
下面来介绍定时器中断服务函数,其定义如下:
  1. /**
  2. *@brief       定时器中断服务函数
  3. *@param       无
  4. *@retval      无
  5. */
  6. voidGTIM_TIMX_NPWM_IRQHandler(void)
  7. {
  8.     uint16_t npwm = 0;
  9.     /* 以下代码没有使用定时器HAL库共用处理函数来处理,而是直接通过判断中断标志位的方式 */
  10.     if(__HAL_TIM_GET_FLAG(&g_timx_npwm_chy_handle, TIM_FLAG_UPDATE) != RESET)
  11.     {
  12.        if (g_npwm_remain > 256)       /* 还有大于256个脉冲需要发送 */
  13.        {
  14.            g_npwm_remain=g_npwm_remain - 256;
  15.            npwm = 256;
  16.        }
  17.        else if (g_npwm_remain % 256) /* 还有位数(不到256)个脉冲要发送 */
  18.        {
  19.            npwm =g_npwm_remain % 256;
  20.            g_npwm_remain = 0;          /* 没有脉冲了 */
  21.        }
  22.        if (npwm)                        /* 有脉冲要发送 */
  23.        {
  24.            GTIM_TIMX_NPWM->RCR = npwm - 1; /* 设置RCR值为npwm-1, 即npwm个脉冲 */
  25.            HAL_TIM_GenerateEvent(&g_timx_npwm_chy_handle,
  26. TIM_EVENTSOURCE_UPDATE); /* 产生一次更新事件,以更新RCR寄存器 */
  27.            __HAL_TIM_ENABLE(&g_timx_npwm_chy_handle); /* 使能定时器TIMX */
  28.        }
  29.        else
  30.        {
  31. /* 关闭定时器TIMX,使用__HAL_TIM_DISABLE需要失能通道输出,所以不用 */
  32.            GTIM_TIMX_NPWM->CR1 &= ~(1 << 0);           
  33.        }
  34. /* 清除定时器更新中断标志位 */
  35.        __HAL_TIM_CLEAR_IT(&g_timx_npwm_chy_handle, TIM_IT_UPDATE);  
  36.     }
  37. }
复制代码
这里我们没有使用HAL库的中断回调机制,而是想寄存器操作一样,直接通过判断中断标志位处理中断。通过__HAL_TIM_GET_FLAG函数宏判断是否发生更新中断,然后进行更新中断的代码处理,最后通过__HAL_TIM_CLEAR_IT函数宏清除更新中断标志位。

因为重复计数器寄存器 (TIM15_RCR)是8位有效的,所以在定时器中断服务函数中首先对全局变量g_npwm_remain(即我们要输出的PWM个数)进行判断,是否大于256,如果大于256,那就得分次写入重复计数器寄存器。写入重复计数寄存器后,需要产生软件更新事件把RCR寄存器的值更新到RCR影子寄存器中,最后一定不要忘记清除定时器更新中断标志位。

在main函数里面编写如下代码:
  1. int main(void)
  2. {
  3.     uint8_t key = 0;
  4.    sys_cache_enable();                        /* 打开L1-Cache */
  5.    HAL_Init();                                  /* 初始化HAL库 */
  6.    sys_stm32_clock_init(240, 2, 2, 4);     /* 设置时钟, 480Mhz */
  7.    delay_init(480);                            /* 延时初始化 */
  8.    usart_init(115200);                        /* 串口初始化为115200 */
  9.    led_init();                                 /* 初始化LED */
  10.    key_init();                                 /* 初始化按键 */
  11.    gtim_timx_npwm_chy_init(5000 - 1, 24000 - 1);/*10Khz的计数频率,2hz的PWM频率*/
  12. /* 设置PWM占空比,50%,这样可以控制每一个PWM周期,LED2(BLUE) 有一半时间是亮的,
  13. *一半时间是灭的,LED2亮灭一次,表示一个PWM波 */
  14.    GTIM_TIMX_NPWM_CHY_CCRX = 2500;
  15.                                     
  16.    gtim_timx_npwm_chy_set(5);    /* 输出5个PWM波(控制LED2(BLUE)闪烁5次) */
  17.     while (1)
  18.     {
  19.        key = key_scan(0);
  20.        if (key == KEY0_PRES)   /* KEY0按下 */
  21.        {
  22.            gtim_timx_npwm_chy_set(5);  /* 输出5个PWM波(控制LED2(BLUE)闪烁5次) */
  23.        }
  24.        delay_ms(10);
  25.     }
  26. }
复制代码
先看gtim_timx_npwm_chy_init(5000- 1, 24000 - 1);这个语句,这两个形参分别设置自动重载寄存器的值为4999,以及预分频器寄存器的值为23999。按照sys_stm32_clock_init函数的配置,定时器15的时钟频率为2倍的APB2总线时钟频率,即240MHz,可以得到计数器的计数频率是10KHz。自动重载寄存器的值决定的是PWM周期或频率(请回顾21.3小节的内容),计数器计5000个数所用的时间是PWM的周期。在边沿对齐模式下,定时器的溢出周期等于PWM的周期。根据定时器溢出时间计算公式,可得:
Tout= ((arr+1)*(psc+1))/Tclk=((4999+1)*(23999+1))/240000000=0.5s
再由频率是周期的倒数关系得到PWM的频率为2Hz。

占空比则由捕获/比较寄存器(TIMx_CCRx)的值决定,这里就是由TIM15_CCR1寄存器决定。初始化定时器15时我们设置通道输出比较极性为高,GTIM_TIMX_NPWM_CHY_CCRX = 2500,就设置了占空比为50%。因为我们的LED灯是低电平点亮,所以正占空比期间LED灯熄灭,负占空比期间LED灯亮。

22.2.4 下载验证
下载代码后,可以看到LED2亮灭五次,然后我们每按一下按键KEY0,LED2都会亮灭五次。下面我们使用正点原子DS100手持数字示波器,把PE5引脚的波形截获,具体如下:
image019.png
图22.2.4 PE5引脚波形图

由LED的原理图可以知道,PE5引脚输出低电平LED2亮、输出高电平LED2灭。图22.2.4中,从左往右看,可以知道,LED2一开始是熄灭的,然后经过5次亮灭,最后就是一直保持熄灭的状态。PWM频率是2Hz,占空比50%,大家可以自行测量。

22.3 高级定时器输出比较模式实验
本小节我们来学习使用高级定时器输出比较模式下翻转功能,通过定时器4个通道分别输出4个50%占空比、不同相位的PWM。

输出比较模式下翻转功能作用是:当计数器的值等于捕获/比较寄存器影子寄存器的值时,OC1REF 发生翻转,进而控制通道输出(OCx)翻转。通过翻转功能实现输出PWM的具体原理如下:PWM频率由自动重载寄存器(TIMx_ARR)的值决定,在这个过程中,只要自动重载寄存器的值不变,那么PWM占空比就固定为50%。我们可以通过捕获/比较寄存器(TIMx_CCRx)的值改变PWM的相位。生成PWM的原理如图22.3.1所示:
      image020.png
图22.3.1 翻转功能输出PWM原理示意图

本实验就是根据图22.3.1的原理来设计的,具体实验是:我们设置固定的ARR值为999,那么PWM占空比固定为50%,通过改变4个通道的捕获/比较寄存器(TIMx_CCRx)的值使得每个通道输出的PWM的相位都不一样,注意捕获/比较寄存器的值设置范围是:0 ~ ARR。比如:TIMx_CCR1=250-1,TIMx_CCR2=500-1,TIMx_CCR3=750-1,TIMx_CCR4=1000-1,那么可以得到通道1~通道4输出的PWM的相位分别是:25%、50%、75%、100%。翻转功能输出的PWM周期,这里用T表示,其计算公式如下:
T= 2*(arr+1)*((psc+1)/Tclk)
其中:
T:翻转功能输出的PWM周期(单位为s)
Tclk:定时器的时钟源频率(单位为MHz)
arr:自动重装寄存器(TIMx_ARR)的值
psc:预分频器寄存器(TIMx_PSC)的值

22.3.1 TIM1/TIM8寄存器
高级定时器输出比较模式除了用到定时器的时基单元:计数器寄存器(TIMx_CNT)、预分频器寄存器(TIMx_PSC)、自动重载寄存器(TIMx_ARR) 之外。主要还用到以下这些寄存器:
l  控制寄存器 1(TIMx_CR1
TIM1/TIM8的控制寄存器1描述如图22.3.1.1所示:
      image022.png
图22.3.1.1 TIMx_CR1寄存器

上图中我们只列出了本实验需要用的一些位,其中:位7(APRE)用于控制自动重载寄存器是否具有缓冲作用,在基本定时器的时候已经讲过,请回顾。本实验中,我们把该位置1。
CEN位,用于使能计数器的工作,必须要设置该位为1,才可以开始计数。
其它位保持复位值即可。

l  捕获/比较模式寄存器1/2(TIMx_CCMR1/2
TIM1/TIM8的捕获/比较模式寄存器(TIMx_CCMR1/2),该寄存器一般有2个:TIMx_CCMR1和TIMx _CCMR2。TIMx_CCMR1控制CH1和CH2,而TIMx_CCMR2控制CH3和CH4。TIMx_CCMR1寄存器描述如图22.3.1.2所示:
      image024.png
图22.3.1.2 TIMx_CCMR1寄存器

该寄存器的有些位在不同模式下,功能不一样,我们现在用到输出比较模式。关于该寄存器的详细说明,请参考《STM32H7xx参考手册_V3(中文版).pdf》第1458页,38.4.7节。

本实验我们用到了定时器1输出比较的4个通道,所以我们需要配置TIM1_CCMR1和TIM1_CCMR2。以TIM1_CCMR1寄存器为例,模式设置位OC1M[3:0]就是对应着通道1的模式设置,此部分由4位组成,总共可以配置成14种模式,我们使用的是翻转功能,所以这4位必须设置为0011。通道2也是如此,将位OC2M[3:0] 设置为0011。通道3和通道4就要设置TIM1_CCMR2寄存器的位OC3M[3:0]和位OC4M[3:0]。除此之外,我们还要设置输出比较的预装载使能位,如通道1对应输出比较的预装载使能位OC1PE置1,其他通道也要把相应位置1。

l  捕获/比较使能寄存器(TIMx_ CCER
TIM1/TIM8的捕获/比较使能寄存器,该寄存器控制着各个输入输出通道的开关和极性。TIMx_CCER寄存器描述如图22.3.1.3所示:
    image026.png   
图22.3.1.3 TIMx_CCER寄存器

该寄存器比较简单,要让TIM1的4个通道都输出,我们需要把对应的捕获/比较1输出使能位置1。通道1到通道4的使能位分别是:CC1E、CC2E、CC3E、CC4E,我们把这4个位置1,使能通道输出。

l  捕获/比较寄存器1/2/3/4(TIMx_ CCR1/2/3/4
捕获/比较寄存器(TIMx_CCR1/2/3/4),该寄存器总共有4个,对应4个通道CH1~CH4。本实验4个通道都要使用到,以通道1对应的TIMx_ CCR1寄存器为例,其描述如下图所示:
image028.png      
图22.3.1.4 TIMx_ CCR1寄存器
在本实验中,我们通过改变TIMx_ CCR1/2/3/4寄存器的值来改变4个通道输出的PWM的相位。

l  断路和死区寄存器(TIMx_ BDTR
本实验用的是高级定时器,所以我们还需要配置:断路和死区寄存器(TIMx_BDTR),该寄存器各位描述如图22.3.1.5所示:
image030.jpg    
图22.3.1.5 TIMx_ BDTR寄存器

该寄存器,我们只需要关注位15(MOE),要想高级定时器的通道正常输出,则必须设置MOE位为1,否则不会有输出。

22.3.2 硬件设计
1. 例程功能
使用输出比较模式的翻转功能,通过定时器1的4路通道输出占空比固定为50%、相位分别是25%、50%、75%和100%的PWM。

2. 硬件资源
1)RGB灯
    RED :LED0 - PB4
2)PE9复用为TIM1_CH1
   PE11复用为TIM1_CH2
   PE13复用为TIM1_CH3
   PE14复用为TIM1_CH4

3. 原理图
定时器属于STM32H750的内部资源,只需要软件设置好即可正常工作。我们需要通过示波器观察PE9、PE11、PE13和PE14引脚PWM输出的情况。

22.3.3 程序设计
22.3.3.1 定时器的HAL库驱动
定时器在HAL库中的驱动代码在前面已经介绍了部分,请回顾,这里我们再介绍几个本实验用到的函数。

1.HAL_TIM_OC_Init函数
定时器的输出比较模式初始化函数,其声明如下:
  1. HAL_StatusTypeDefHAL_TIM_OC_Init(TIM_HandleTypeDef *htim);
复制代码
l  函数描述:
用于初始化定时器的输出比较模式。

l  函数形参:
形参1是TIM_HandleTypeDef结构体类型指针变量,基本定时器的时候已经介绍。

l  函数返回值:
HAL_StatusTypeDef枚举类型的值。

2. HAL_TIM_OC_ConfigChannel函数
定时器的输出比较通道设置初始化函数。其声明如下:
  1. HAL_StatusTypeDefHAL_TIM_OC_ConfigChannel(TIM_HandleTypeDef *htim,
  2. TIM_OC_InitTypeDef *sConfig, uint32_t Channel);
复制代码
l  函数描述:
该函数用于初始化定时器的输出比较通道。

l  函数形参:
形参1是TIM_HandleTypeDef结构体类型指针变量,用于配置定时器基本参数。
形参2是TIM_OC_InitTypeDef结构体类型指针变量,用于配置定时器的输出比较参数。在通用定时器PWM输出实验已经介绍过TIM_OC_InitTypeDef结构体指针类型。
形参3是定时器通道,范围:TIM_CHANNEL_1到TIM_CHANNEL_6。

l  函数返回值:
HAL_StatusTypeDef枚举类型的值。

3.HAL_TIM_OC_Start函数
定时器的输出比较启动函数,其声明如下:
  1. HAL_StatusTypeDefHAL_TIM_OC_Start(TIM_HandleTypeDef *htim, uint32_t Channel);
复制代码
l  函数描述:
用于启动定时器的输出比较模式。

l  函数形参:
形参1是TIM_HandleTypeDef结构体类型指针变量。
形参2是定时器通道,范围:TIM_CHANNEL_1到TIM_CHANNEL_6。

l  函数返回值:
HAL_StatusTypeDef枚举类型的值。

l  注意事项:
HAL库也同样提供了单独使能定时器的输出通道函数,函数为:
  1. voidTIM_CCxChannelCmd(TIM_TypeDef *TIMx, uint32_t Channel,
  2. uint32_t ChannelState);
复制代码
HAL_TIM_OC_Start函数内部也调用了该函数。

定时器输出比较模式配置步骤
1)开启TIMx和通道输出的GPIO时钟,配置该IO口的复用功能输出
首先开启TIMx的时钟,然后配置GPIO为复用功能输出。本实验我们默认用到定时器1通道1、2、3、4,对应IO是PE9\PE11\PE13\PE14,它们的时钟开启方法如下:
  1. __HAL_RCC_TIM1_CLK_ENABLE();             /* 使能定时器1 */
  2. __HAL_RCC_GPIOE_CLK_ENABLE();            /* 开启GPIOE时钟 */
复制代码
       IO口复用功能是通过函数HAL_GPIO_Init来配置的。

2)初始化TIMx,设置TIMx的ARR和PSC等参数
使用定时器的输出比较模式时,我们调用的是HAL_TIM_OC_Init函数来初始化定时器ARR和PSC等参数。
注意:该函数会调用HAL_TIM_OC_MspInit函数,我们可以通过后者存放定时器和GPIO时钟使能、GPIO初始化、中断使能以及优先级设置等代码。

3)设置定时器为输出比较模式,输出比较极性,输出比较值、翻转功能等参数
在HAL库中,通过HAL_TIM_OC_ConfigChannel函数来设置定时器为输出比较模式,根据需求设置输出比较的极性,设置输出比较值、翻转功能等。
最后我们通过__HAL_TIM_ENABLE_OCxPRELOAD函数使能通道的预装载。

4)开启定时器并输出PWM
通过HAL_TIM_OC_Start函数使能定时器并开启输出。

22.3.3.2 程序流程图
image032.png
图22.3.3.2.1高级定时器输出比较模式实验程序流程图

21.3.3.3 程序解析
这里我们只讲解核心代码,详细的源码请大家参考光盘本实验对应源码。高级定时器驱动源码包括两个文件:atim.c和atim.h。
首先看atim.h头文件的几个宏定义:
  1. /*****************************************************************************/
  2. /* TIMX 输出比较模式 定义
  3. * 这里通过TIM1的输出比较模式,控制PE9,PE11,PE13,PE14输出4路PWM,占空比50%,并且每一路
  4. PWM之间的相位差为25%
  5. * 修改CCRx可以修改相位.
  6. * 默认是针对TIM1/TIM8
  7. * 注意: 通过修改这些宏定义,可以支持TIM1~TIM17任意一个定时器,任意一个IO口使用输出比较模式,
  8. 输出PWM
  9. */
  10. #define ATIM_TIMX_COMP_CH1_GPIO_PORT            GPIOE
  11. #define ATIM_TIMX_COMP_CH1_GPIO_PIN             GPIO_PIN_9
  12. #define ATIM_TIMX_COMP_CH1_GPIO_AF              GPIO_AF1_TIM1    /* AF功能选择 */
  13. #define ATIM_TIMX_COMP_CH1_GPIO_CLK_ENABLE()
  14. do{ __HAL_RCC_GPIOE_CLK_ENABLE(); }while(0)   /* PE口时钟使能 */
  15. #define ATIM_TIMX_COMP_CH2_GPIO_PORT            GPIOE
  16. #define ATIM_TIMX_COMP_CH2_GPIO_PIN             GPIO_PIN_11
  17. #define ATIM_TIMX_COMP_CH2_GPIO_AF              GPIO_AF1_TIM1    /* AF功能选择 */
  18. #defineATIM_TIMX_COMP_CH2_GPIO_CLK_ENABLE()   
  19. do{ __HAL_RCC_GPIOE_CLK_ENABLE(); }while(0)     /* PE口时钟使能 */
  20. #define ATIM_TIMX_COMP_CH3_GPIO_PORT            GPIOE
  21. #define ATIM_TIMX_COMP_CH3_GPIO_PIN             GPIO_PIN_13
  22. #define ATIM_TIMX_COMP_CH3_GPIO_AF              GPIO_AF1_TIM1    /* AF功能选择 */
  23. #defineATIM_TIMX_COMP_CH3_GPIO_CLK_ENABLE()   
  24. do{ __HAL_RCC_GPIOE_CLK_ENABLE(); }while(0)     /* PE口时钟使能 */
  25. #define ATIM_TIMX_COMP_CH4_GPIO_PORT            GPIOE
  26. #define ATIM_TIMX_COMP_CH4_GPIO_PIN             GPIO_PIN_14
  27. #define ATIM_TIMX_COMP_CH4_GPIO_AF              GPIO_AF1_TIM1    /* AF功能选择 */
  28. #defineATIM_TIMX_COMP_CH4_GPIO_CLK_ENABLE()   
  29. do{ __HAL_RCC_GPIOE_CLK_ENABLE(); }while(0)     /* PE口时钟使能 */
  30. #define ATIM_TIMX_COMP              TIM1            
  31. #define ATIM_TIMX_COMP_CH1_CCRX   ATIM_TIMX_COMP->CCR1  /* 通道1的输出比较寄存器 */
  32. #define ATIM_TIMX_COMP_CH2_CCRX   ATIM_TIMX_COMP->CCR2  /* 通道2的输出比较寄存器 */
  33. #define ATIM_TIMX_COMP_CH3_CCRX   ATIM_TIMX_COMP->CCR3  /* 通道3的输出比较寄存器 */
  34. #define ATIM_TIMX_COMP_CH4_CCRX   ATIM_TIMX_COMP->CCR4  /* 通道4的输出比较寄存器 */
  35. #define ATIM_TIMX_COMP_CLK_ENABLE()
  36. do{ __HAL_RCC_TIM1_CLK_ENABLE(); }while(0)   /* TIM1 时钟使能 */
  37. /*****************************************************************************/
  38. 可以把上面的宏定义分成两部分,第一部分是定时器1输出通道1~通道4对应的IO口的宏定义。第二部分则是定时器1的相应宏定义。
  39. 下面来看到atim.c文件的程序,首先是高级定时器输出比较模式初始化函数,其定义如下:
  40. /**
  41. *@brief       高级定时器TIMX 输出比较模式 初始化函数(使用输出比较模式)
  42. *@note
  43. *             配置高级定时器TIMX 4路输出比较模式PWM输出,实现50%占空比,不同相位控制
  44. *             注意,本例程输出比较模式,每2个计数周期才能完成一个PWM输出,因此输出频率减半
  45. *             另外,我们还可以开启中断在中断里面修改CCRx,从而实现不同频率/不同相位的控制
  46. *             但是我们不推荐这么使用,因为这可能导致非常频繁的中断,从而占用大量CPU资源
  47. *
  48. *             高级定时器的时钟来自APB1,当D2PPRE2≥2分频的时候
  49. *             高级定时器的时钟为APB2时钟的2倍, 而APB2为120M, 所以定时器时钟 = 240Mhz
  50. *             定时器溢出时间计算方法: Tout = ((arr + 1) * (psc + 1)) / Ft us.
  51. *             Ft=定时器工作频率,单位:Mhz
  52. * @param       arr:自动重装值。
  53. *@param       psc: 时钟预分频数
  54. *@retval      无
  55. */
  56. voidatim_timx_comp_pwm_init(uint16_t arr, uint16_t psc)
  57. {
  58.    TIM_OC_InitTypeDef tim_oc_handle = {0};
  59.    g_timx_comp_pwm_handle.Instance = ATIM_TIMX_COMP;      /* 定时器1 */
  60.    g_timx_comp_pwm_handle.Init.Prescaler = psc  ;          /* 定时器分频 */
  61.    g_timx_comp_pwm_handle.Init.CounterMode = TIM_COUNTERMODE_UP;/* 递增计数模式*/
  62.    g_timx_comp_pwm_handle.Init.Period = arr;               /* 自动重装载值 */
  63. g_timx_comp_pwm_handle.Init.AutoReloadPreload =
  64. TIM_AUTORELOAD_PRELOAD_ENABLE;       /* 使能TIMx_ARR进行缓冲 */
  65.    HAL_TIM_OC_Init(&g_timx_comp_pwm_handle);       /* 输出比较模式初始化 */
  66.    tim_oc_handle.OCMode =TIM_OCMODE_TOGGLE;        /* 比较输出模式翻转功能 */
  67.    tim_oc_handle.Pulse = 250;                         /* 设置输出比较寄存器的值 */
  68.    tim_oc_handle.OCPolarity = TIM_OCPOLARITY_HIGH; /* 输出比较极性为高 */*/
  69. HAL_TIM_OC_ConfigChannel(&g_timx_comp_pwm_handle, &tim_oc_handle,
  70. TIM_CHANNEL_1); /* 初始化定时器的输出比较通道1 */
  71. /* CCR1寄存器预装载使能 */
  72.     __HAL_TIM_ENABLE_OCxPRELOAD(&g_timx_comp_pwm_handle, TIM_CHANNEL_1);
  73.     tim_oc_handle.Pulse = 500;
  74. HAL_TIM_OC_ConfigChannel(&g_timx_comp_pwm_handle, &tim_oc_handle,
  75. TIM_CHANNEL_2); /* 初始化定时器的输出比较通道2 */
  76. /* CCR2寄存器预装载使能 */
  77.     __HAL_TIM_ENABLE_OCxPRELOAD(&g_timx_comp_pwm_handle, TIM_CHANNEL_2);
  78.     tim_oc_handle.Pulse = 750;
  79. HAL_TIM_OC_ConfigChannel(&g_timx_comp_pwm_handle, &tim_oc_handle,
  80. TIM_CHANNEL_3); /* 初始化定时器的输出比较通道3 */
  81. /* CCR3寄存器预装载使能 */
  82.     __HAL_TIM_ENABLE_OCxPRELOAD(&g_timx_comp_pwm_handle, TIM_CHANNEL_3);
  83.     tim_oc_handle.Pulse = 1000;
  84. HAL_TIM_OC_ConfigChannel(&g_timx_comp_pwm_handle, &tim_oc_handle,
  85. TIM_CHANNEL_4); /* 初始化定时器的输出比较通道4 */
  86. /* CCR4寄存器预装载使能 */   
  87. __HAL_TIM_ENABLE_OCxPRELOAD(&g_timx_comp_pwm_handle, TIM_CHANNEL_4);
  88.    HAL_TIM_OC_Start(&g_timx_comp_pwm_handle,TIM_CHANNEL_1);
  89.    HAL_TIM_OC_Start(&g_timx_comp_pwm_handle,TIM_CHANNEL_2);
  90.    HAL_TIM_OC_Start(&g_timx_comp_pwm_handle,TIM_CHANNEL_3);
  91.    HAL_TIM_OC_Start(&g_timx_comp_pwm_handle,TIM_CHANNEL_4);
  92. }
复制代码
在atim_timx_comp_pwm_init 函数中,首先调用HAL_TIM_OC_Init函数初始化定时器的ARR和PSC等参数。然后通过调用函数HAL_TIM_OC_ConfigChannel设置通道1~通道4的工作参数,包括:输出比较模式功能、输出比较寄存器的值,输出极性等。接着调用__HAL_TIM_ENABLE_OCxPRELOAD函数宏使能CCR1/2/3/4寄存器的预装载。最后通过调用函数HAL_TIM_OC_Start来使能TIM1通道1~通道4输出。
HAL_TIM_OC_Init函数会调用HAL_TIM_OC_MspInit回调函数,我们把使能定时器和通道对应的IO时钟、IO初始化的代码存放到该函数里,其定义如下:
  1. /**
  2. *@brief       定时器输出比较底层驱动
  3.                   HAL库调用的接口,用于配置不同的输入捕获
  4. *@param       htim:定时器句柄
  5. *@note        此函数会被HAL_TIM_OC_Init()调用
  6. *@retval      无
  7. */
  8. voidHAL_TIM_OC_MspInit(TIM_HandleTypeDef* htim)
  9. {
  10.     if(htim->Instance == ATIM_TIMX_COMP)
  11.     {
  12.        GPIO_InitTypeDef gpio_init_struct = {0};
  13.        ATIM_TIMX_COMP_CLK_ENABLE();
  14.        ATIM_TIMX_COMP_CH1_GPIO_CLK_ENABLE();
  15.        ATIM_TIMX_COMP_CH2_GPIO_CLK_ENABLE();
  16.        ATIM_TIMX_COMP_CH3_GPIO_CLK_ENABLE();
  17.        ATIM_TIMX_COMP_CH4_GPIO_CLK_ENABLE();
  18.        gpio_init_struct.Pin = ATIM_TIMX_COMP_CH1_GPIO_PIN;
  19.        gpio_init_struct.Mode = GPIO_MODE_AF_PP;
  20.        gpio_init_struct.Pull = GPIO_NOPULL;
  21.        gpio_init_struct.Speed = GPIO_SPEED_FREQ_HIGH;
  22.        gpio_init_struct.Alternate = ATIM_TIMX_COMP_CH1_GPIO_AF;
  23.        HAL_GPIO_Init(ATIM_TIMX_COMP_CH1_GPIO_PORT, &gpio_init_struct);
  24.        gpio_init_struct.Pin = ATIM_TIMX_COMP_CH2_GPIO_PIN;
  25.        gpio_init_struct.Alternate = ATIM_TIMX_COMP_CH2_GPIO_AF;
  26.        HAL_GPIO_Init(ATIM_TIMX_COMP_CH2_GPIO_PORT, &gpio_init_struct);
  27.        gpio_init_struct.Pin = ATIM_TIMX_COMP_CH3_GPIO_PIN;
  28.        gpio_init_struct.Alternate = ATIM_TIMX_COMP_CH3_GPIO_AF;
  29.        HAL_GPIO_Init(ATIM_TIMX_COMP_CH3_GPIO_PORT, &gpio_init_struct);
  30.         gpio_init_struct.Pin = ATIM_TIMX_COMP_CH4_GPIO_PIN;
  31.        gpio_init_struct.Alternate = ATIM_TIMX_COMP_CH4_GPIO_AF;
  32.        HAL_GPIO_Init(ATIM_TIMX_COMP_CH4_GPIO_PORT, &gpio_init_struct);
  33.     }
  34. }
复制代码
该函数主要是使能定时器和通道对应的IO时钟,初始化IO口。要注意IO口的复用功能一定要选对了。
在main.c里面编写如下代码:
  1. int main(void)
  2. {
  3.     uint8_t t = 0;
  4.    sys_cache_enable();                       /* 打开L1-Cache */
  5.    HAL_Init();                                 /* 初始化HAL库 */
  6.    sys_stm32_clock_init(240, 2, 2, 4);     /* 设置时钟, 480Mhz */
  7.    delay_init(480);                           /* 延时初始化 */
  8.    usart_init(115200);                       /* 串口初始化为115200 */
  9.    led_init();                                 /* 初始化LED */
  10.    atim_timx_comp_pwm_init(1000 - 1, 240 - 1);  /* 1Mhz的计数频率 1Khz的周期 */
  11.     /* atim_timx_comp_pwm_init已经设置过输出比较寄存器,这是寄存器操作的方法 */
  12.    ATIM_TIMX_COMP_CH1_CCRX = 250 - 1;      /* 通道1 相位25% */
  13.    ATIM_TIMX_COMP_CH2_CCRX = 500 - 1;      /* 通道2 相位50% */
  14.    ATIM_TIMX_COMP_CH3_CCRX = 750 - 1;      /* 通道3 相位75% */
  15.    ATIM_TIMX_COMP_CH4_CCRX = 1000 - 1;     /* 通道4 相位100% */
  16.     while (1)
  17.     {
  18.        delay_ms(10);
  19.        t++;
  20.        if (t >= 20)
  21.        {
  22.            LED0_TOGGLE();  /* LED0(RED)闪烁 */
  23.            t = 0;
  24.        }
  25.     }
  26. }
复制代码
本小节开头我们讲解了输出比较模式翻转功能如何产生PWM,下面结合程序一起计算出PWM的周期,频率等参数。
定时器1时钟源的时钟频率为2倍的APB2总线时钟频率,即240MHz,而调用atim_timx_comp_pwm_init(1000- 1, 240 - 1)初始化函数之后,就相当于写入预分频寄存器的值为239,写入自动重载寄存器的值为999。将这些参数代入本小节介绍的翻转功能输出的PWM周期计算公式,可得:
T = 2*(arr+1)*((psc+1)/ Tclk) = 2*(999+1)*((239+1)/240000000) = 0.002s
由上述式子得到PWM周期为2ms,频率为500Hz。ARR值为固定为1000,所以占空比则固定为50%。定时器1通道1~通道4输出的PWM波的相位分别是:25%、50%、75%、100%。

22.3.4 下载验证
下载代码后,可以看到LED0在闪烁,说明程序已经正常在跑了。我们需要借助示波器观察PE9、PE11、PE13和PE14引脚PWM输出的情况,我们已经把示波器的显示内容截图出来,如图22.3.4.1所示:
image033.png
图22.3.4.1 相位为25%、50%、75%、100%的PWM波

图22.3.4.1中,由上到下分别是引脚PE9、PE11、PE13和PE14输出的PWM,即分别对应的是TIM1_CH1、TIM1_CH2、TIM1_CH3和TIM1_CH4输出的相位为25%、50%、75%和100%的PWM。大家可以把其中一个通道的捕获/比较寄存器的值设置为0,那么就可以得到PWM初相位的波形,即相位为0%。
正点原子逻辑分析仪DL16劲爆上市
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则



关闭

原子哥极力推荐上一条 /2 下一条

正点原子公众号

QQ|手机版|OpenEdv-开源电子网 ( 粤ICP备12000418号-1 )

GMT+8, 2025-2-24 20:22

Powered by OpenEdv-开源电子网

© 2001-2030 OpenEdv-开源电子网

快速回复 返回顶部 返回列表