【STM32系列文章】
STM32-01-认识单片机
STM32-02-基础知识
STM32-03-HAL库
STM32-04-时钟树
STM32-05-SYSTEM文件夹
STM32-06-GPIO
STM32-07-外部中断
STM32-08-串口
STM32-09-IWDG和WWDG
STM32-10-定时器
STM32-11-电容触摸按键
STM32-12-OLED模块
STM32-13-MPU
STM32-14-FSMC_LCD
STM32-15-DMA
STM32-16-ADC
STM32-17-DAC
文章目录
一、STM32 基础定时器
STM32定时器分类:
STM32定时器特性表:
主要功能:
定时器类型 | 主要功能 |
---|---|
基本定时器 | 没有输入输出通道,常用作时基,即定时功能 |
通用定时器 | 具有多路独立通道,可用于输入捕获/输出比较,也可用作时基 |
高级定时器 | 除具备通用定时器所有功能外,还具备带死区控制的互补信号输出、刹车输入等功能(可用于电机控制、数字电源设计等) |
1. 基本定时器简介
-
STM32F103有两个基本定时器
TIM6
和TIM7
,它们的功能完全相同,资源是完全独立的,可以同时使用。 -
主要特性:
- 16位自动重载递增计数器;
- 16位可编程预分频器,预分频系数
1~65536
,用于对计数器时钟频率进行分频; - 触发DAC的同步电路;
- 生成中断 和DMA请求。
-
定时原理:
2. 基本定时器框图
-
影子寄存器: 实际起作用的寄存器,用户不可直接访问。举个例子:我们可以把预分频系数写入预分频器寄存器(
TIMx_PSC
),但是预分频器寄存器只是起到缓存数据的作用,只有等到更新事件发生时,预分频器寄存器的值才会被自动写入其影子寄存器中,这时才真正起作用。 -
自动重载寄存器:自动重载寄存器及其影子寄存器的作用在于自动重载寄存器是否具有缓冲作用还受到
ARPE
位的控制。- 当该位置0时,ARR寄存器不进行缓冲,写入新ARR值会立即传送到ARR影子寄存器从而直接生效;
- 当该位置1时,ARR寄存器进行缓冲,写入新ARR值时不会立即被写入ARR影子寄存器,而是要等到更新事件发生才会被写入ARR影子寄存器,这时才生效。预分频器寄存器则没有这样相关的控制位,这就是它们不同点。
-
更新事件的产生:
- 软件产生,将
TIMx_EGR
寄存器的位UG
置 1,产生更新事件后,硬件会自动将UG
位清零。 - 硬件产生,基本定时器的计数器(
CNT
)是一个递增的计数器,当寄存器(TIMx_CR1
)的CEN
位置 1,即使能定时器,每来一个CK_CNT脉冲,TIMx_CNT
的值就会递增加1。当TIMx_CNT
值与TIMx_ARR
的设定值相等时,TIMx_CNT
的值就会被自动清零并且会生成更新事件(如果开启相应的功能,就会产生DMA请求、产生中断信号或者触发DAC同步电路),然后下一个 CK_CNT脉冲到来,TIMx_CNT
的值就会递增加1,如此循环。在此过程中,TIMx_CNT
等于TIMx_ARR
时,我们称之为定时器溢出,因为是递增计数,故而又称为定时器上溢。定时器溢出就伴随着更新事件的发生。
- 软件产生,将
-
计数模式与溢出条件:
-
递增计数模式实例说明:
-
递减计数模式实例说明:
-
中心对齐计数模式实例说明:
-
3. 基本定时器相关寄存器
-
控制寄存器 1(
TIMx_CR1
)
-
DMA/中断使能寄存器(
TIMx_DIER
)
-
状态寄存器(
TIMx_SR
)
-
计数器(
TIMx_CNT
)
-
预分频器(
TIMx_PSC
)
-
自动重装载寄存器(
TIMx_ARR
)
4. 定时器溢出时间计算
5. 基本定时器中断配置步骤
-
配置定时器基础工作参数
HAL_TIM_Base_Init()
-
定时器基础MSP初始化
HAL_TIM_Base_MspInit()
-
使能更新中断并启动计数器
HAL_TIM_Base_Start_IT()
-
设置优先级,使能中断
HAL_NVIC_SetPriority() HAL_NVIC_EnableIRQ()
-
编写中断服务函数
TIMx_IRQHandler() HAL_TIM_IRQHandler()
-
编写定时器更新中断回调函数
HAL_TIM_PeriodElapsedCallback()
-
程序流程
6. 代码实现
-
功能:使用定时器中断实现LED灯闪烁.
-
定时器中断初始化
void btim_timx_int_init(uint16_t psc, uint16_t per) { g_timx_handle.Instance = TIM6; g_timx_handle.Init.Prescaler = psc; g_timx_handle.Init.Period = per; HAL_TIM_Base_Init(&g_timx_handle); HAL_TIM_Base_Start_IT(&g_timx_handle); }
-
定时器基础MSP初始化函数
void HAL_TIM_Base_MspInit(TIM_HandleTypeDef *htim) { if (htim->Instance == TIM6) { __HAL_RCC_TIM6_CLK_ENABLE(); HAL_NVIC_SetPriority(TIM6_IRQn, 1, 3); HAL_NVIC_EnableIRQ(TIM6_IRQn); } }
-
定时器6中断服务函数
void TIM6_IRQHandler() { HAL_TIM_IRQHandler(&g_timx_handle); }
-
定时器溢出中断回调函数
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) { if(htim->Instance == TIM6) { LED0_TOGGLE(); } }
-
主函数
#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/BEEP/beep.h" #include "./BSP/EXTI/exti.h" #include "./BSP/WDG/wdg.h" #include "./BSP/TIMER/btim.h" int main(void) { HAL_Init(); /* 初始化HAL库 */ sys_stm32_clock_init(RCC_PLL_MUL9); /* 设置时钟, 72Mhz */ delay_init(72); /* 延时初始化 */ usart_init(115200); /* 串口初始化为115200 */ led_init(); /* 初始化LED */ btim_timx_int_init(7199, 4999); while(1) { } }
二、STM32通用定时器
1. 通用定时器简介
- 通用定时器是一个通过可编程预分频器驱动的16位自动装载计数器构成。
- 它适用于多种场合,包括测量输入信号的脉冲宽度(输入捕获)或者产生输出波形(输出比较和PWM)。
- 使用定时器预分频器和RCC时钟控制器预分频器,脉冲长度和波形周期可以在几个微秒到几个毫秒间调整。
- 每个定时器都是完全独立的,没有互相共享任何资源,并且可以一起同步操作。
-
主要特性
-
16位递增、递减、中心对齐计数器(计数值:0~65535)
-
16位预分频器(分频系数:1~65536)
-
4个独立通道:输入捕获,输出比较,PWM生成,单脉冲模式输出
-
使用外部信号控制定时器且可实现多个定时器互连的同步电路
-
支持针对定位的增量编码器和霍尔传感器电路
-
触发输入作为外部时钟或者按周期的电流管理
-
2. 通用定时器框图
1. 时钟源
计数器时钟选择类型 | 设置方法 |
---|---|
内部时钟(CK_INT) | 设置TIMx_SMCR的SMS=000 |
外部时钟模式1:外部输入引脚(TIx) | 设置TIMx_SMCR的SMS=111 |
外部时钟模式2:外部触发输入(ETR) | 设置TIMx_SMCR的ECE=1 |
内部触发输入(ITRx) |
1. 内部时钟(CK_INT
)
- 定时器TIM2~TIM7挂接在APB1总线上,这些定时器的内部时钟(CK_INT)实际上来自APB1总线提供的时钟,然后经过一个倍频器之后,输出的时钟频率为72MHz。
2. 外部时钟模式1(TI1, TI2)
3. 外部时钟模式2(ETR)
4. 内部触发输入(ITRx)
2. 控制器
-
控制器包括:触发控制器,从模式控制器,编码器接口
-
触发控制器:用来提供触发信号给别的外设
-
从模式控制器:可以控制计数器复位、启动、递增/递减、计数
-
编码器接口:针对编码器计数
-
3. 时基单元
4. 输入捕获
TIMx_CH1
~TIMx_CH4
表示定时器的4个通道,4个通道独立工作。IO端口通过复用功能与这些通道相连。配置好IO端口的复用功能后,将需要测量的信号输入到相应的 IO端口,输入捕获部分可以对输入的信号的上升沿,下降沿或者双边沿进行捕获,常见的测量有:测量输入信号的脉冲宽度、测量PWM输入信号的频率和占空比等。
5. 捕获/比较(公共)
以通道1为例,对应CCR1
寄存器,CC1G
位可以产生软件捕获事件,那么硬件捕获事件如何产生的?以通道1输入为例,CC1S[1:0]=01,即IC1映射到TI1上;CC1E位置1,使能输入捕获;比如不滤波、不分频,ICF[3:0]=00,ICPS[1:0]=00;比如检测上升沿,CC1P位置0:接着就是等待测量信号的上升沿到来。当上升沿到来时,IC1PS信号就会触发输入捕获事件发生,计数器的值就会被锁存到捕获/比较影子寄存器里。当CCR1寄存器没有被进行读操作的时候,捕获/比较影子寄存器里的值就会锁存到CCR1寄存器中,那么程序员就可以读取CCR1寄存器,得到计数器的计数值。检测下降沿同理。
6. 输出比较
首先程序员写CCR1寄存器,即写入比较值。这个比较值需要转移到对应的捕获比较影子寄存器后才会真正生效。什么条件下才能转移?compare transfer旁边的与门,需要满足三个条件:CCR1不在写入操作期间、CC1S[1:0]=0配置为输出、OC1PE位置 0(或者OC1PE位置1,并且需要发生更新事件,这个更新事件可以软件产生或者硬件产生)。当CCR1寄存器的值转移到其影子寄存器后,新的值就会和计数器的值进行比较,它们的比较结果将会通过输出比较影响定时器的输出。
3. 通用定时器相关寄存器
-
控制寄存器1(
TIMx_CR1
)
-
从模式控制寄存器(
TIMx_SMCR
)
-
DMA/中断使能寄存器(
TIMx_DIER
)
-
状态寄存器(
TIMx_SR
)
-
计数寄存器(
TIMx_CNT
)
-
预分频寄存器(
TIMx_PSC
)
-
自动重载寄存器(
TIMx_ARR
)
三、 通用定时器输出PWM波
-
通用定时器输出模式
PWM,即脉冲宽度调制,是利用微处理器的数字输出对模拟电路进行控制的一种非常有效的技术。让定时器产生PWM,在计数器频率固定时,PWM频率自动重载寄存器(TIMx_ARR)的值决定,其占空比由捕获/比较寄存器(TIMx_CCRx)的值决定。
-
PWM模式
-
PWM控制寄存器
-
捕获/比较模式寄存器1/2(
TIMx_CCMR1/2
)
-
捕获/比较使能寄存器(
TIMx_CCER
)
-
捕获/比较寄存器(
TIMx_CCR1/2/3/4
)
-
1. 通用定时器PWM输出实验配置步骤
-
配置定时器基础工作参数
HAL_TIM_PWM_Init()
-
定时器PWM输出MSP初始化
HAL_TIM_PWM_MspInit()
-
配置PWM模式/比较值等
HAL_TIM_PWM_ConfigChannel()
-
使能输出并启动计数器
HAL_TIM_PWM_Start()
-
修改比较值控制占空比(可选)
__HAL_TIM_SET_COMPARE()
-
使能通道预装载(可选)
__HAL_TIM_ENABLE_OCxPRELOAD()
-
相关库函数介绍
函数 主要寄存器 主要功能 HAL_TIM_PWM_Init() CR1、ARR、PSC 初始化定时器基础参数 HAL_TIM_PWM_MspInit() 无 存放NVIC、CLOCK、GPIO初始化代码 HAL_TIM_PWM_ConfigChannel() CCMRx、CCRx、CCER 配置PWM模式、比较值、输出极性等 HAL_TIM_PWM_Start() CCER、CR1 使能输出比较并启动计数器 __HAL_TIM_SET_COMPARE() CCRx 修改比较值 __HAL_TIM_ENABLE_OCxPRELOAD() CCER 使能通道预装载
2. 代码实现
-
功能:使用TM3通道2(由PB5复用)输出PWM,PB5引脚连接了LED0,从而实现PWM输出控制LED0亮度。
-
程序流程:
-
通用定时器PWM输出初始化函数
void gtim_timx_pwm_chy_init(uint16_t arr, uint16_t psc) { TIM_OC_InitTypeDef timx_oc_pwm_chy; g_timx_pwm_chy_handle.Instance = TIM3; //定时器选择 g_timx_pwm_chy_handle.Init.Prescaler = psc; //定时器分频 g_timx_pwm_chy_handle.Init.Period = arr; g_timx_pwm_chy_handle.Init.CounterMode = TIM_COUNTERMODE_UP; //定时器计数模式 HAL_TIM_PWM_Init(&g_timx_pwm_chy_handle); //初始化PWM timx_oc_pwm_chy.OCMode = TIM_OCMODE_PWM1; //模式选择PWM1 timx_oc_pwm_chy.Pulse = arr/2; //占空比为50% timx_oc_pwm_chy.OCNPolarity = TIM_OCNPOLARITY_LOW; //输出比较极性为低 HAL_TIM_PWM_ConfigChannel(&g_timx_pwm_chy_handle, &timx_oc_pwm_chy, TIM_CHANNEL_2); //配置定时器3通道2 HAL_TIM_PWM_Start(&g_timx_pwm_chy_handle, TIM_CHANNEL_2); //开启PWM通道 }
-
定时器输出PWM MSP初始化函数
void HAL_TIM_PWM_MspInit(TIM_HandleTypeDef *htim) { if(htim->Instance == TIM3) { GPIO_InitTypeDef gpio_init_struct; __HAL_RCC_GPIOB_CLK_ENABLE(); __HAL_RCC_TIM3_CLK_ENABLE(); gpio_init_struct.Pin = GPIO_PIN_5; 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(GPIOB, &gpio_init_struct); __HAL_RCC_AFIO_CLK_ENABLE(); __HAL_AFIO_REMAP_TIM3_PARTIAL(); //设置重映射,把TIM3通道2映射到PB5 } }
-
主函数
int main(void) { uint16_t ledrpwmval = 0; uint8_t dir = 1; HAL_Init(); /* 初始化HAL库 */ sys_stm32_clock_init(RCC_PLL_MUL9); /* 设置时钟, 72Mhz */ delay_init(72); /* 延时初始化 */ usart_init(115200); /* 串口初始化为115200 */ led_init(); /* 初始化LED */ gtim_timx_pwm_chy_init(500 - 1, 72 - 1); while