正点原子STM32(基于HAL库)3
Posted 行稳方能走远
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了正点原子STM32(基于HAL库)3相关的知识,希望对你有一定的参考价值。
目录
高级定时器实验
本章我们主要来学习高级定时器,STM32F103 有2 个高级定时器(TIM1 和TIM8)。我们
将通过四个实验来学习高级定时器的各个功能,分别是高级定时器输出指定个数PWM 实验、
高级定时器输出比较模式实验、高级定时器互补输出带死区控制实验和高级定时器PWM 输入
模式实验。
高级定时器简介
高级定时器的框图和通用定时器框图很类似,只是添加了其它的一些功能,如:重复计数器、带死区控制的互补输出通道、断路输入等。这些功能在高级定时器框图的位置如下:
上图中,框出来三个部分,这是和通用定时器不同的地方,下面来分别介绍它们。
① 重复计数器
在F1 系列中,高级定时器TIM1 和TIM8 都有重复计数器。下面来介绍一下重复计数器有
什么作用?在学习基本定时器和通用定时器的时候,我们知道定时器发生上溢或者下溢时,会直接生成更新事件。但是有重复计数器的定时器并不完全是这样的,定时器每次发生上溢或下溢时,重复计数器的值会减一,当重复计数器的值为0 时,再发生一次上溢或者下溢才会生成定时器更新事件。如果我们设置重复计数器寄存器RCR 的值为N,那么更新事件将在定时器发生N+1 次上溢或下溢时发生。
这里需要注意的是重复计数器寄存器是具有影子寄存器的,所以RCR 寄存器只是起缓冲
的作用。RCR 寄存器的值会在更新事件发生时,被转移至其影子寄存器中,从而真正生效。重复计数器的特性,在控制生成PWM 信号时很有用,后面会有相应的实验。
② 输出比较
高级定时器输出比较部分和通用定时器相比,多了带死区控制的互补输出功能。图2 2.1.1
第②部分的TIMx_CH1N、TIMx_CH2N 和TIMx_CH3N 分别是定时器通道1、通道2 和通道3
的互补输出通道,通道4 是没有互补输出通道的。
DTG 是死区发生器,死区时间由DTG[7:0]位来配置。如果不使用互补通道和死区时间控制,那么高级定时器TIM1 和TIM8 和通用定时器的输出比较部分使用方法基本一样,只是要注意MOE 位得置1 定时器才能输出。
如果使用互补通道,那么就有一定的区别了,具体我们在高级定时器互补输出带死区控制
实验小节再来介绍。
③ 断路功能
断路功能也称刹车功能,一般用于电机控制的刹车。F1 系列有一个断路通道,断路源可以
是刹车输入引脚(TIMx_BKIN),也可以是一个时钟失败事件。时钟失败事件由复位时钟控制器中的时钟安全系统产生。系统复位后,断路功能默认被禁止,MOE 位为低。
使能断路功能的方法:将TIMx_BDTR 的位BKE 置1。断路输入引脚TIMx_BKIN 的输入
有效电平可通过TIMx_BDTR 寄存器的位BKP 设置。
使能刹车功能后:由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 的状态:由相关控制位状态决定,当使用互补输出时:根据情况自动控
制输出电平,参考《STM32F10xxx 参考手册_V10(中文版).pdf》手册第245 页的表75 带刹车功能的互补通道Ocx 和OcxN 的控制位。
3,BIF 位置1,如果使能了BIE 位,还会产生刹车中断;如果使能了TDE 位,会产生DMA
请求。
4,如果AOE 位置1,在下一个更新事件UEV 时,MOE 位被自动置1。
高级定时器框图部分就简单介绍到这里,下面通过实际的实验来学习高级定时器。
高级定时器输出指定个数PWM 实验
要实现定时器输出指定个数PWM,只需要掌握下面几点内容:
第一,如果大家还不清楚定时器是如何输出PWM 的,请回顾通用定时器PWM 输出实验
的内容,这部分的知识是一样的。但是需要注意的是:我们需要把MOE 位置1,这样高级定时器的通道才能输出。
第二,要清楚重复计数器特性,设置重复计数器寄存器RCR 的值为N,那么更新事件将在
定时器发生N+1 次上溢或下溢时发生。换句话来说就是,想要指定输出N 个PWM,只需要把N-1 写入RCR 寄存器。因为在边沿对齐模式下,定时器溢出周期对应着PWM 周期,我们只要在更新事件发生时,停止输出PWM 就行。如下图:
第三,为了保证定时器输出指定个数的PWM 后,定时器马上停止继续输出,我们使能更
新中断,并在定时器中断里关闭计数器。
高级定时器输出指定个数PWM原理
TIM1/TIM8 寄存器
下面介绍TIM1/TIM8 这些高级定时器中使用到的几个重要的寄存器,其他更多关于定时器
的资料可以参考《STM32F10xxx 参考手册_V10(中文版).pdf》的第13 章。
⚫ 控制寄存器1(TIMx_CR1)
TIM1/TIM8 的控制寄存器1 描述如图22.2.1.1 所示:
上图中我们只列出了本章需要用的一些位,其中:位7(APRE)用于控制自动重载寄存器
是否具有缓冲作用,在基本定时器的时候已经讲过,请回顾。在本实验中我们把该位要置1,这样就算改变ARR 寄存器的值,该值也不会马上生效,而是等待之前设置的PWM 完整输出后(发生更新事件)才生效。位4(DIR)用于配置计数器的计数方向,这里我们默认置0。位0(CEN),用于使能计数器的工作,必须要设置该位为1,才可以开始计数。
⚫ 捕获/比较模式寄存器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.2.1.2 所示:
该寄存器的有些位在不同模式下,功能不一样,我们前面已经说过。比如我们要让TIM1 的
CH1 输出PWM 波为例,该寄存器的模式设置位OC1M[2:0]就是对应着通道1 的模式设置,此部分由3 位组成,总共可以配置成8 种模式,我们使用的是PWM 模式,所以这3 位必须设置为110 或者111,分别对应PWM 模式1 和PWM 模式2。这两种PWM 模式的区别就是输出有效电平的极性相反,这里我们设置为PWM 模式1。位3 OC1PE 是输出比较通道1 的预装使能,该位需要置1,另外CC1S[1:0]用于设置通道1 的方向(输入/输出)默认设置为0,就是设置通道作为输出使用。
⚫ 捕获/比较使能寄存器(TIMx_ CCER)
TIM1/TIM8 的捕获/比较使能寄存器,该寄存器控制着各个输入输出通道的开关。
TIMx_CCER 寄存器描述如图22.2.1.3 所示:
该寄存器比较简单,要让TIM1 的CH1 输出PWM 波,这里我们要使能CC1E 位,该位是
通道1 输入/输出使能位,要想PWM 从IO 口输出,这个位必须设置为1。CC1P 位是设置通道
1 的输出极性,我们设置0,即OC1 高电平有效。
⚫ 事件产生寄存器(TIMx_ EGR)
TIM1/TIM8 的事件产生寄存器,该寄存器作用是让用户用软件方式产生各类事件。
TIMx_EGR 寄存器描述如图22.2.1.4 所示:
UG 位是更新事件的控制位,作用和定时器溢出时产生的更新事件一样,区别是这里是通
过软件产生的,而定时器溢出是硬件自己完成的。只有开启了更新中断,这两种方式都可以产更新中断。本实验用到该位去产生软件更新器事件,在需要的时候把UG 位置1 即可,会由硬件自动清零。
⚫ 重复计数器寄存器(TIMx_ RCR)
重复计数器寄存器用于设置重复计数器值,因为它具有影子寄存器,所以它本身只是起缓
冲作用。当更新事件发生时,该寄存器的值会转移到其影子寄存器中,从而真正起作用。TIMx_ RCR 寄存器描述如图22.2.1.5 所示:
该寄存器的REP[7:0]位是低8 位有效,即最大值255。因为这个寄存器只是起缓冲作用,
如果大家对该寄存器写入值后,想要立即生效,可以通过对UG 位写1,产生软件更新事件。
⚫ 捕获/比较寄存器1/2/3/4(TIMx_CCR1/2/3/4)
捕获/比较寄存器(TIMx_CCR1/2/3/4),该寄存器总共有4 个,对应4 个通道CH1~CH4。
我们使用的是通道1,所以来看看TIMx_CCR1 寄存器的描述,如图22.2.1.6 所示:
在输出模式下,捕获/比较寄存器影子寄存器的值与CNT 的值比较,根据比较结果产生相
应动作,利用这点,我们通过修改这个寄存器的值,就可以控制PWM 的占空比了。
⚫ 断路和死区寄存器(TIMx_ BDTR)
高级定时器TIM1/8 的通道用作输出时,还必须配置断路和死区寄存器(TIMx_BDTR)的
位MOE,该寄存器各位描述如图22.3.1.7 所示:
本实验,我们只需要关注该寄存器的位15(MOE),要想高级定时器的PWM 正常输出,
则必须设置MOE 位为1,否则不会有输出。
硬件设计
- 例程功能
通过TIM8_CH1(由PC6 复用)输出PWM,然后为了指示PWM 的输出情况,我们用杜
邦线将PC6 和PE5 引脚的排针连接起来,从而实现PWM 输出控制LED1(硬件已连接在PPE5引脚上)的亮灭。注意的点是:PE5 要设置成浮空输入,避免引脚冲突,我们在main 函数中设置好了,请看源码。上电默认输出5 个PWM 波,连接好杜邦线后可以看见LED1 亮灭五次。
之后按一下按键KEY0,就会输出5 个PWM 波控制LED1 亮灭五次。LED0 闪烁提示系统正在运行。 - 硬件资源
1)LED 灯:
LED0 – PB5
LED1 – PE5
2)独立按键:
KEY0 – PE4
3)定时器8,使用TIM8 通道1,由PC6 复用。用杜邦线将PC6 和PE5 引脚连接起来。 - 原理图
定时器属于STM32F103 的内部资源,只需要软件设置好即可正常工作。我们通过LED1 来指示STM32F103 的定时器的PWM 输出情况,所以需要用一根杜邦线连接PC6 和PE5,同时还用按键KEY0 进行控制。
课堂源码(输出指定个数PWM灯就闪几次)
本实验用到的HAL 库函数介绍请回顾通用定时器PWM 输出实验。下面介绍一下定时器
输出指定个数PWM 的配置步骤。
定时器输出指定个数PWM 配置步骤
1)开启TIMx 和通道输出的GPIO 时钟,配置该IO 口的复用功能输出
首先开启TIMx 的时钟,然后配置GPIO 为复用功能输出。本实验我们默认用到定时器8 通
道1,对应IO 是PC6,它们的时钟开启方法如下:
__HAL_RCC_TIM8_CLK_ENABLE(); /* 使能定时器8 */
__HAL_RCC_GPIOC_CLK_ENABLE(); /* 开启GPIOC时钟*/
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 模式,根据需求设置输出比较的极性,设置比较值(控制占空比)等。
本实验我们设置TIM8 的通道1 为PWM1 模式,使用杜邦线把PC6 与PE5 进行连接,因
为我们的LED1(连接PE5)是低电平亮,而我们希望输出最后一个PWM 波的时候,LED1 就灭,所以我们设置输出比较极性为高。捕获/比较寄存器的值(即比较值)设置为自动重装载值的一半,即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 库的中断回调机制,而是把中断程序写在定时器中断服务函数里。
atim.h
#ifndef __ATIM_H
#define __ATIM_H
#include "./SYSTEM/sys/sys.h"
void atim_timx_npwm_chy_init(uint16_t arr, uint16_t psc);
void atim_timx_npwm_chy_set(uint8_t npwm);
#endif
atim.c
#include "./BSP/TIMER/atim.h"
TIM_HandleTypeDef g_timx_npwm_chy_handle; /* 定时器x句柄 */
static uint8_t g_npwm_remain = 0;
/* 高级定时器TIMX 通道Y 输出指定个数PWM 初始化函数 */
void atim_timx_npwm_chy_init(uint16_t arr, uint16_t psc)
TIM_OC_InitTypeDef timx_oc_npwm_chy = 0;
g_timx_npwm_chy_handle.Instance = TIM8; /* 定时器x */
g_timx_npwm_chy_handle.Init.Prescaler = psc; /* 定时器分频 */
g_timx_npwm_chy_handle.Init.CounterMode = TIM_COUNTERMODE_UP; /* 递增计数模式 */
g_timx_npwm_chy_handle.Init.Period = arr; /* 自动重装载值 */
g_timx_npwm_chy_handle.Init.RepetitionCounter = 0; /* 重复计数器初始值 */
HAL_TIM_PWM_Init(&g_timx_npwm_chy_handle); /* 初始化PWM */
timx_oc_npwm_chy.OCMode = TIM_OCMODE_PWM1; /* 模式选择PWM 1*/
timx_oc_npwm_chy.Pulse = arr / 2; /* 设置比较值,此值用来确定占空比50% */
/* 这里默认设置比较值为自动重装载值的一半,即占空比为50% */
timx_oc_npwm_chy.OCPolarity = TIM_OCPOLARITY_HIGH; /* 输出比较极性为高 */
HAL_TIM_PWM_ConfigChannel(&g_timx_npwm_chy_handle, &timx_oc_npwm_chy, TIM_CHANNEL_1);
__HAL_TIM_ENABLE_IT(&g_timx_npwm_chy_handle, TIM_IT_UPDATE);
HAL_TIM_PWM_Start(&g_timx_npwm_chy_handle, TIM_CHANNEL_1);
/* 定时器 PWM输出 MSP初始化函数 */
void HAL_TIM_PWM_MspInit(TIM_HandleTypeDef *htim)
if (htim->Instance == TIM8)
GPIO_InitTypeDef gpio_init_struct;
__HAL_RCC_GPIOC_CLK_ENABLE();
__HAL_RCC_TIM8_CLK_ENABLE();
gpio_init_struct.Pin = GPIO_PIN_6;
gpio_init_struct.Mode = GPIO_MODE_AF_PP; /* 推挽式复用功能 */
gpio_init_struct.Pull = GPIO_PULLUP; /* 上拉 */
gpio_init_struct.Speed = GPIO_SPEED_FREQ_HIGH; /* 高速 */
HAL_GPIO_Init(GPIOC, &gpio_init_struct);
HAL_NVIC_SetPriority(TIM8_UP_IRQn, 1, 3);
HAL_NVIC_EnableIRQ(TIM8_UP_IRQn);
/* 高级定时器TIMX NPWM设置PWM个数函数 */
void atim_timx_npwm_chy_set(uint8_t npwm)
if(npwm == 0) return;
g_npwm_remain = npwm;
HAL_TIM_GenerateEvent(&g_timx_npwm_chy_handle, TIM_EVENTSOURCE_UPDATE);
__HAL_TIM_ENABLE(&g_timx_npwm_chy_handle);
/* 定时器8中断服务函数 */
void TIM8_UP_IRQHandler(void)
HAL_TIM_IRQHandler(&g_timx_npwm_chy_handle);
/* 定时器更新中断回调函数 */
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
if (htim->Instance == TIM8)
if(g_npwm_remain)
TIM8->RCR = g_npwm_remain - 1;//N-1写入到RCR寄存器
HAL_TIM_GenerateEvent(&g_timx_npwm_chy_handle, TIM_EVENTSOURCE_UPDATE);//RCR寄存器的值缓冲到影子寄存器里,通过软件更新事件方式
__HAL_TIM_ENABLE(&g_timx_npwm_chy_handle);//启动计数器
g_npwm_remain = 0;
else
TIM8->CR1 &= ~(1 << 0);//关闭计数器,这里也可以直接调用HAL库函数
main.c
#include "./SYSTEM/sys/sys.h"
#include "./SYSTEM/usart/usart.h"
#include "./SYSTEM/delay/delay.h"
#include "./BSP/LED/led.h"
#include "./BSP/KEY/key.h"
#include "./BSP/TIMER/atim.h"
int main(void)
uint8_t key;
uint8_t t = 0;
GPIO_InitTypeDef gpio_init_struct;
HAL_Init(); /* 初始化HAL库 */
sys_stm32_clock_init(RCC_PLL_MUL9); /* 设置时钟, 72Mhz */
delay_init(72); /* 延时初始化 */
usart_init(115200); /* 串口初始化为115200 */
led_init(); /* 初始化LED */
key_init(); /* 初始化按键 */
/* 把PE5设置为输入,避免与 PC6 冲突(杜邦线连接两引脚) */
__HAL_RCC_GPIOE_CLK_ENABLE();
gpio_init_struct.Pin = GPIO_PIN_5;
gpio_init_struct.Mode = GPIO_MODE_INPUT; /* 输入 */
gpio_init_struct.Pull = GPIO_PULLUP; /* 上拉 */
gpio_init_struct.Speed = GPIO_SPEED_FREQ_HIGH; /* 高速 */
HAL_GPIO_Init(GPIOE, &gpio_init_struct);
atim_timx_npwm_chy_init(5000 - 1, 7200 - 1);
atim_timx_npwm_chy_set(5);//灯闪5次
while (1)
key = key_scan(0);
if(key == KEY0_PRES)//按键 0 按下
atim_timx_npwm_chy_set(6);//灯闪6次
t++;
delay_ms(10);
if (t > 50) /* 控制LED0闪烁, 提示程序运行状态 */
t = 0;
LED0_TOGGLE();
程序设计
程序流程图
程序解析
这里我们只讲解核心代码,详细的源码请大家参考光盘本实验对应源码。高级定时器驱动
源码包括两个文件:atim.c 和atim.h。本章节的四个实验源码都是存放在atim.c 和atim.h 中,源码中也有明确的注释。
首先看atim.h 头文件的几个宏定义:
/* TIMX 输出指定个数PWM 定义
* 这里输出的PWM通过PC6(TIM8_CH1)输出, 我们用杜邦线连接PC6和PE5, 然后在程序里面将PE5设
* 置成浮空输入就可以看到TIM8_CH1控制LED1(GREEN)的亮灭, 亮灭一次表示一个PWM波
* 默认使用的是TIM8_CH1.
* 注意: 通过修改这几个宏定义, 可以支持TIM1/TIM8定时器, 任意一个IO口输出指定个数的PWM
*/
#define ATIM_TIMX_NPWM_CHY_GPIO_PORT GPIOC
#define ATIM_TIMX_NPWM_CHY_GPIO_PIN GPIO_PIN_6
#define ATIM_TIMX_NPWM_CHY_GPIO_CLK_ENABLE() do__HAL_RCC_GPIOC_CLK_ENABLE();\\
while(0) /* PC口时钟使能*/
#define ATIM_TIMX_NPWM TIM8
#define ATIM_TIMX_NPWM_IRQn TIM8_UP_IRQn
#define ATIM_TIMX_NPWM_IRQHandler TIM8_UP_IRQHandler
#define ATIM_TIMX_NPWM_CHY TIM_CHANNEL_1 /* 通道Y, 1<= Y <=4 */
#define ATIM_TIMX_NPWM_CHY_CCRX TIM8->CCR1/* 通道Y的输出比较寄存器*/
#define ATIM_TIMX_NPWM_CHY_CLK_ENABLE() do __HAL_RCC_TIM8_CLK_ENABLE(); \\
while(0)
可以把上面的宏定义分成两部分,第一部分是定时器8 输入通道1 对应的IO 口的宏定义,
第二部分则是定时器8 输入通道1 的相应宏定义。
下面看atim.c 的程序,首先是输出指定个数PWM 初始化函数,其定义如下:
/**
* @brief 高级定时器TIMX 通道Y 输出指定个数PWM 初始化函数
* @note
* 高级定时器的时钟来自APB2, 而PCLK2 = 72Mhz, 我们设置PPRE2不分频, 因此
* 高级定时器时钟= 72Mhz
* 定时器溢出时间计算方法: Tout = ((arr + 1) * (psc + 1)) / Ft us.
* Ft=定时器工作频率,单位:Mhz
* @param arr: 自动重装值
* @param psc: 时钟预分频数
* @retval 无
*/
void atim_timx_npwm_chy_init(uint16_t arr, uint16_t psc)
GPIO_InitTypeDef gpio_init_struct;
TIM_OC_InitTypeDef timx_oc_npwm_chy; /* 定时器输出*/
ATIM_TIMX_NPWM_CHY_GPIO_CLK_ENABLE(); /* TIMX 通道IO口时钟使能*/
ATIM_TIMX_NPWM_CHY_CLK_ENABLE(); /* TIMX 时钟使能*/
g_timx_npwm_chy_handle.Instance = ATIM_TIMX_NPWM; /* 定时器x */
g_timx_npwm_chy_handle.Init.Prescaler = psc; /* 定时器分频*/
g_timx_npwm_chy_handle.Init.CounterMode = TIM_COUNTERMODE_UP;/* 递增计数*/
g_timx_npwm_chy_handle.Init.Period = arr; /* 自动重装载值*/
g_timx_npwm_chy_handle.Init.AutoReloadPreload =
TIM_AUTORELOAD_PRELOAD_ENABLE; /*使能TIMx_ARR进行缓冲*/
g_timx_npwm_chy_handle.Init.RepetitionCounter = 0; /* 重复计数器初始值*/
HAL_TIM_PWM_Init(&g_timx_npwm_chy_handle); /* 初始化PWM */
gpio_init_struct.Pin = ATIM_TIMX_NPWM_CHY_GPIO_PIN;/* 通道y的CPIO口*/
gpio_init_struct.Mode = GPIO_MODE_AF_PP; /* 复用推完输出*/
gpio_init_struct.Pull = GPIO_PULLUP; /* 上拉*/
gpio_init_struct.Speed = GPIO_SPEED_FREQ_HIGH; /* 高速*/
HAL_GPIO_Init(ATIM_TIMX_NPWM_CHY_GPIO_PORT, &gpio_init_struct);
timx_oc_npwm_chy.OCMode = TIM_OCMODE_PWM1; /* 模式选择PWM 1*/
timx_oc_npwm_chy.Pulse = arr / 2; /* 设置比较值,此值用来确定占空比*/
timx_oc_npwm_chy.OCPolarity = TIM_OCPOLARITY_HIGH; /* 输出比较极性为高*/
HAL_TIM_PWM_ConfigChannel(&g_timx_npwm_chy_handle, &timx_oc_npwm_chy,
ATIM_TIMX_NPWM_CHY); /* 配置TIMx通道y */
/* 设置中断优先级,抢占优先级1,子优先级3 */
HAL_NVIC_SetPriority(ATIM_TIMX_NPWM_IRQn, 1, 3);
HAL_NVIC_EnableIRQ(ATIM_TIMX_NPWM_IRQn); /* 开启ITMx中断*/
__HAL_TIM_ENABLE_IT(&g_timx_npwm_chy_handle, TIM_IT_UPDATE);/* 允许更新中断*/
HAL_TIM_PWM_Start(&g_timx_npwm_chy_handle, ATIM_TIMX_NPWM_CHY);/* 使能输出*/
atim_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 个数的函数,其定义如下:
/* g_npwm_remain表示当前还剩下多少个脉冲要发送
* 每次最多发送256个脉冲
*/
static uint32_t g_npwm_remain = 0;
/**
* @brief 高级定时器TIMX NPWM设置PWM个数
* @param rcr: PWM的个数, 1~2^32次方个
* @retval 无
*/
void atim_timx_npwm_chy_set(uint32_t npwm)
if (npwm == 0)return ;
g_npwm_remain = npwm; /* 保存脉冲个数*/
/* 产生一次更新事件,在中断里面处理脉冲输出*/
HAL_TIM_GenerateEvent(&g_timx_npwm_chy_handle, TIM_EVENTSOURCE_UPDATE);
__HAL_TIM_ENABLE(&g_timx_npwm_chy_handle); /* 使能定时器TIMX */
我们要输出多少个周期的PWM 就用这个函数来设置。该函数作用是把我们设置输出的
PWM 个数的值赋值给静态全局变量g_npwm_remain,该变量会在更新中断服务函数回调函数中发挥作用。最后对TIMx_EGR 寄存器UG 位写1,产生一次更新事件,并使能定时器。
下面来介绍定时器中断服务函数,其定义如下:
/**
* @brief 定时器中断服务函数
* @param 无
* @retval 无
*/
void ATIM_TIMX_NPWM_IRQHandler(void)
uint16_t npwm = 0;
/* 以下代码没有使用定时器HAL库共用处理函数来处理,而是直接通过判断中断标志位的方式*/
if(__HAL_TIM_GET_FLAG(&g_timx_npwm_chy_handle, TIM_FLAG_UPDATE) != RESET)
if (g_npwm_remain >= 256) /* 还有大于256个脉冲需要发送*/
g_npwm_remain=g_npwm_remain - 256;
npwm = 256;
else if (g_npwm_remain % 256) /* 还有位数(不到256)个脉冲要发送*/
npwm = g_npwm_remain % 256;
g_npwm_remain = 0; /* 没有脉冲了*/
if (npwm) /* 有脉冲要发送*/
ATIM_TIMX_NPWM->RCR = npwm - 1; /* 设置RCR值为npwm-1, 即npwm个脉冲*/
HAL_TIM_GenerateEvent(&g_timx_npwm_chy_handle,
TIM_EVENTSOURCE_UPDATE); /* 产生一次更新事件,以更新RCR寄存器*/
__HAL_TIM_ENABLE(&g_timx_npwm_chy_handle); /* 使能定时器TIMX */
else
/* 关闭定时器TIMX,使用__HAL_TIM_DISABLE需要失能通道输出,所以不用*/
ATIM_TIMX_NPWM->CR1 &= ~(1 << 0);
/* 清除定时器更新中断标志位*/
__HAL_TIM_CLEAR_IT(&g_timx_npwm_chy_handle, TIM_I以上是关于正点原子STM32(基于HAL库)3的主要内容,如果未能解决你的问题,请参考以下文章