网站建设实训心得 总结/关键词优化案例
STM32定时器使用
- STM32定时器
- 计数模式
- 定时器工作原理
- 基本定时器:TIM6、TIM7
- 示例代码
- 代码讲解
- 通用定时器:TIM2、TIM3、TIM4、TIM5
- 高级定时器:TIM1、TIM8
- 定时器PWM输出原理
- 通用定时器3控制通道1输出PWM脉冲
- 代码
- 总结
- 参考资料
STM32定时器
STM32F103系列芯片拥有多种定时器,包括基本定时器、通用定时器和高级定时器,每种定时器都具有一些特定的功能。
计数模式
向上计数:计数器从0计数到自动重装载值(ARR),然后重新从0开始计数并且产生一个计数器溢出事件。
向下计数:计数器从自动重装载值(ARR)开始向下计数到0,然后重新从自动重装载值(ARR)开始向下计数,并且产生一个计数器溢出事件。
向上向下双向计数(中央对齐模式):计数器从0开始计数到自动重装载值-1,产生一个计数器溢出事件,然后计数器从ARR开始向下计数,计数到1再次产生一个计数器溢出事件,以此往复。
定时器工作原理
STM32F103系统构架知:TIM1、8在APB2上,TIM2、3、4、5、6、7在APB1上,APB1操作速度限于36MHz,APB2操作于全速72MHz。
基本定时器:TIM6、TIM7
TIM6、TIM7定时器主要功能:16位自动重装载累加计算器,16位可编程预分频器(1~65536),触发DAC的同步电路、产生DMA中断请求。
基本定时器框图
时钟源: STM32F103基本定时器的时钟源,可以使用内部时钟源或外部时钟源。当使用内部时钟源(CN_INT)时,计时器的时钟频率由STM32内部时钟提供。当使用外部时钟源时,计时器的时钟频率由外部时钟信号提供,可以使用外部晶体振荡器、RC振荡器等外部时钟源。
预分频器:PSC预分频寄存器,用于设定定时器时钟的分频系数。
CNT计数器:用于记录定时器的当前值,从0开始计数,当CNT到达ARR时计数器重新从0开始计数(向上计数模式)。
自动重装载寄存器:ARR用于设定计时器的计时时长,当CNT到达该值时自动重新计数。
如何定时呢?
首先确定TIM6\TIM7时钟源(内部时钟CK_INT)经APB1预分频后分频提供,如果APB1预分频系数是1则频率不变,在库函数中APB1预分频的系数是2,即PCLK1=36MHz,所以定时器的时钟频率TIMxCLK为72Mhz(36*2)
计数器计数的计算方式:
CK_CNT = TIMxCLK/(PSC+1) //CK_CNT最大65536
PSC是预分频器值
定时时间计算(计数器的中断周期*中断的次数):
Tout = ((ARR+1)*(PSC+1))/TIMxCLK
假设需要计时1s产生一次中断(已知TIMxCLk为72MHz):
先设ARR值为9999(ARR+1就是10000)
1 = (10000(PSC+1))/72000000*
因此PSC=7199
所以CK_CNT计算有什么用?
假设PSC是7199 所以CN_CNK=1000(hz)的时钟 计数一次花费时间1/1000 = 0.0001s = 0.1ms
所以Tout = (ARR+1)*(0.0001) 已知定时1s = Tout 所以ARR = 9999
示例代码
使用定时器TIM6中断方法控制LED灯闪烁,设置定时器1s中断一次,中断服务函数中控制LED实现LED闪烁功能。
定时器头文件定义
#ifndef __TIME_H
#define __TIME_H#include "stm32f10x.h"#define TIM TIM4
#define TIM_CLK RCC_APB1Periph_TIM4 //RCC时钟
#define TIM_ARR 10000-1 //定义ARR值
#define TIM_PSC 7200-1 //定义PSC值
#define TIM_IRQ TIM4_IRQn //定义中断编号
#define TIM_IRQHandler TIM4_IRQHandler //重定义中断函数void TIM_Init_Config(void); #endif /* __TIME_H */
定时器源文件
#include "bsp_time.h"//中断优先级配置
void TIM_NVIC_Config(void){NVIC_InitTypeDef NVIC_InitStructure;NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1); //设置优先级分组NVIC_InitStructure.NVIC_IRQChannel = TIM_IRQ; //指定需要配置的中断信号NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1; //先占优先级 1NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1; //从优先级 1NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //使能NVICNVIC_Init(&NVIC_InitStructure); //初始化NVIC寄存器
}//定时器配置
void TIM_Init_Config(void){TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStruct;RCC_APB1PeriphClockCmd(TIM_CLK, ENABLE); //开启TIM4定时器所对应的APB1总线时钟TIM_TimeBaseInitStruct.TIM_Prescaler = TIM_PSC; //设置TIM4的预分频系数TIM_TimeBaseInitStruct.TIM_CounterMode = TIM_CounterMode_Up;//计数模式设置为递增模式TIM_TimeBaseInitStruct.TIM_Period = TIM_ARR; //设置TIM4自动重载寄存器的值,用于设定定时器的定时时长TIM_TimeBaseInitStruct.TIM_ClockDivision = TIM_CKD_DIV1; //设置TIM4的时钟分割比,即分割细度
// TIM_TimeBaseInitStruct.TIM_RepetitionCounter = 0;TIM_TimeBaseInit(TIM, &TIM_TimeBaseInitStruct);TIM_ITConfig(TIM, TIM_IT_Update, ENABLE);//使能TIM4的溢出中断,将TIM_IT_Update设置为ENABLE即可。这一步非常重要,否则定时器无法产生中断信号,不会触发相应的中断服务程序。TIM_NVIC_Config();TIM_Cmd(TIM, ENABLE); //使能定时器 -- 开启定时器
}
中断函数
//"stm32f10x_it.c"源文件
void TIM_IRQHandler(void){if(TIM_GetITStatus(TIM, TIM_IT_Update)!= RESET){LED_TOGGLE;TIM_ClearITPendingBit(TIM, TIM_IT_Update);}
}
代码讲解
定时器结构体成员
/*** @brief TIM Time Base Init structure definition* @note This structure is used with all TIMx except for TIM6 and TIM7. */typedef struct
{uint16_t TIM_Prescaler; /*!< Specifies the prescaler value used to divide the TIM clock.This parameter can be a number between 0x0000 and 0xFFFF */uint16_t TIM_CounterMode; /*!< Specifies the counter mode.This parameter can be a value of @ref TIM_Counter_Mode */uint16_t TIM_Period; /*!< Specifies the period value to be loaded into the activeAuto-Reload Register at the next update event.This parameter must be a number between 0x0000 and 0xFFFF. */uint16_t TIM_ClockDivision; /*!< Specifies the clock division.This parameter can be a value of @ref TIM_Clock_Division_CKD */uint8_t TIM_RepetitionCounter; /*!< Specifies the repetition counter value. Each time the RCR downcounterreaches zero, an update event is generated and counting restartsfrom the RCR value (N).This means in PWM mode that (N+1) corresponds to:- the number of PWM periods in edge-aligned mode- the number of half PWM period in center-aligned modeThis parameter must be a number between 0x00 and 0xFF.@note This parameter is valid only for TIM1 and TIM8. */
} TIM_TimeBaseInitTypeDef;
TIM_Prescaler:指分配器的预分频值,一般为一定的预定值,大小范围为0到65536
TIM_CounterMode:指定时器的计数模式,一般有递增和递减模式
#define TIM_CounterMode_Up ((uint16_t)0x0000)
#define TIM_CounterMode_Down ((uint16_t)0x0010)
#define TIM_CounterMode_CenterAligned1 ((uint16_t)0x0020)
#define TIM_CounterMode_CenterAligned2 ((uint16_t)0x0040)
#define TIM_CounterMode_CenterAligned3 ((uint16_t)0x0060)
TIM_Period:定时器的自动重装载值,大小范围为0到65536
TIM_ClockDivision:TIM的时钟分割比,即分割细度
TIM_RepetitionCounter: 指定TIM1和TIM8定时器的重复计数器值。当重复计数器的值减少到0时,将会产生一个溢出事件,并将TIMx的计数器重新从重复计数器的值开始计数。
定时器初始化函数
void TIM_TimeBaseInit(TIM_TypeDef* TIMx, TIM_TimeBaseInitTypeDef* TIM_TimeBaseInitStruct) //x从1到17
TIMx:定时器选择,可从1到17
使能/失能中断溢出函数
void TIM_ITConfig(TIM_TypeDef* TIMx, uint16_t TIM_IT, FunctionalState NewState) //x从1到17
开启/关闭定时器函数
void TIM_Cmd(TIM_TypeDef* TIMx, FunctionalState NewState) //x从1到17
定时器中断标志位获取和清除
FlagStatus TIM_GetFlagStatus(TIM_TypeDef* TIMx, uint16_t TIM_FLAG); //获取定时器中断标志状态,检查x定时器是否发生中断事件//TIM_FLAG是需要检测的中断标志
void TIM_ClearFlag(TIM_TypeDef* TIMx, uint16_t TIM_FLAG); //清除定时器中断标志,将x定时器指定的中断标志清零ITStatus TIM_GetITStatus(TIM_TypeDef* TIMx, uint16_t TIM_IT); //获取定时器中断状态,检查x定时器是否产生中断信号//TIM_IT用于检测需要的中断源void TIM_ClearITPendingBit(TIM_TypeDef* TIMx, uint16_t TIM_IT); //清除定时器的中断状态
定时器中断标志状态函数一般先于定时器中断状态函数使用,并且在启用中断之前使用它们以确保不会丢失年中断信号;定时器中断状态函数则用于在ISR(中断服务程序)中检查中断状态。
通用定时器:TIM2、TIM3、TIM4、TIM5
定时器时钟来源:
- 内部定时器(CK_INT)
- 外部时钟模式1:外部输入脚(TIx)
- 外部时钟模式2:外部触发输入(ETR)
- 内部触发输入(ITRx):使用一个定时器作为另一个定时器的预分频器,如可以配置一个定时器作为另一个定时器的预分频器。
编程步骤:
- 配置系统时钟
- 配置NVIC
- 配置定时器
- 配置定时器中断服务函数
高级定时器:TIM1、TIM8
待补写
定时器PWM输出原理
PWM输出就是对外输出脉宽(占空比)可调的方波信号,信号频率由自动重装载寄存器ARR的值决定,占空比由比较寄存器CCR的值决定。
通用定时器3控制通道1输出PWM脉冲
从上图通用定时器框图可以看出一个定时器只有一个计数器,所以同一个定时器输出的PWM频率是一样的,可以改变的是占空比大小(可以通过改变比较寄存器改变占空比大小)。
查看定时器TIM3复用的引脚图,如下所示
代码
PWM头文件定义
#ifndef __PWM_H
#define __PWM_H
#include "stm32f10x.h"#define PWM_TIM TIM3
#define PWM_TIM_CLK RCC_APB1Periph_TIM3#define PWM_TIM_GPIP_PORT GPIOA
#define PWM_TIM_GPIO_CLK RCC_APB2Periph_GPIOA
#define PWM_TIM_GPIO_Pin GPIO_Pin_6void PWM_TIM_Mode_Init(uint16_t freq, uint16_t duty);#endif /* __PWM_H */
PWM源文件
#include "bsp_pwm.h"/**
* @brief 初始化PWM的GPIO为复用模式
*
* @retval None
*/
void PWM_TIM_GPIO_Init(void){GPIO_InitTypeDef GPIO_InitStruct;RCC_APB2PeriphClockCmd(PWM_TIM_GPIO_CLK, ENABLE); //开启GPIOA时钟GPIO_InitStruct.GPIO_Pin = PWM_TIM_GPIO_Pin;GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_PP; //设置引脚为复用推挽输出模式GPIO_Init(PWM_TIM_GPIP_PORT, &GPIO_InitStruct); //初始化函数
}/**
* @brief 初始化TIM为PWM输出模式
*
* @param freq PWM的频率(kHz)
* @param duty PWM的占空比(0~100)
* @retval None
*/
void PWM_TIM_Mode_Init(uint16_t freq, uint16_t duty){uint16_t arr = 36000/freq; //freq是频率,单位是khz //将频率PWM_TIM_GPIO_Init();//配置定时器TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStruct;TIM_OCInitTypeDef TIM_OCInitStruct;//开启定时器时钟RCC_APB1PeriphClockCmd(PWM_TIM_CLK, ENABLE);TIM_TimeBaseInitStruct.TIM_Period = arr-1;TIM_TimeBaseInitStruct.TIM_Prescaler =1; //时钟预分频数 设置频率f = 36mhz 因为 t = TIMx_CLK/(PSC+1) --> t = 1/(36Mhz) --> f = 1/t --> f = 1/t = 36mhzTIM_TimeBaseInitStruct.TIM_ClockDivision = 0; //采样分频TIM_TimeBaseInitStruct.TIM_CounterMode = TIM_CounterMode_Up; //计数模式TIM_TimeBaseInit(PWM_TIM, &TIM_TimeBaseInitStruct);TIM_OCInitStruct.TIM_OCMode = TIM_OCMode_PWM1; //设置为PWM1模式TIM_OCInitStruct.TIM_OutputState = TIM_OutputState_Enable; //输出使能TIM_OCInitStruct.TIM_Pulse = (arr+1)*duty/100-1; //设置占空比TIM_OCInitStruct.TIM_OCPolarity = TIM_OCPolarity_High; //输出极性//初始化PWM_TIM输出比较通道1TIM_OC1Init(PWM_TIM, &TIM_OCInitStruct);//使能OC1输出比较多预装载以及TIMx的配置寄存器TIM_OC1PreloadConfig(PWM_TIM, TIM_OCPreload_Enable);//开启定时器TIM_Cmd(PWM_TIM,ENABLE);
}
主程序
PWM_TIM_Mode_Init(20, 80); //初始化时调用一次即可
TIM_OCInitTypeDef结构体讲解
typedef struct
{uint16_t TIM_OCMode; /*!< 指定TIM输出比较的模式 */uint16_t TIM_OutputState; /*!< 指定TIM输出比较通道的输出状态(开启或关闭) */uint16_t TIM_OutputNState; /*!< 指定TIM互补输出比较通道的输出状态(开启或者关闭),只对定时器TIM1、TIM8有效 */uint16_t TIM_Pulse; /*!< 指定TIM输出比较通道的占空比 */uint16_t TIM_OCPolarity; /*!< 指定TIM输出比较通道的极性 */uint16_t TIM_OCNPolarity; /*!< 指定TIM互补输出比较通道的极性 */uint16_t TIM_OCIdleState; /*!< 指定TIM输出比较通道在闲置或者空档时的输出电平状态 */uint16_t TIM_OCNIdleState; /*!< 指定TIM互补输出比较通道在空档状态下输出的电平状态 */
} TIM_OCInitTypeDef;
ARR决定PWM周期
CCRx决定PWM占空比大小
ARR计算:
由Tout = ((ARR+1)*(PSC+1))/TIMxCLK -- 已知TIMxCLK = 72Mhz且PSC设置为1
因此T=(arr+1)/36 -- 已知 t = 1/f
所以1/f = (arr+1)/36
得 arr = 36000/f
总结
要多用,要多用!
参考资料
https://www.bilibili.com/video/BV1b54y1m71K/?spm_id_from=333.337.search-card.all.click&vd_source=101ae2594bcc6dbee11cc6670869bc2ehttps://blog.csdn.net/qq_44016222/article/details/123507270?spm=1001.2100.3001.7377&utm_medium=distribute.pc_feed_blog_category.none-task-blog-classify_tag-20-123507270-null-null.nonecase&depth_1-utm_source=distribute.pc_feed_blog_category.none-task-blog-classify_tag-20-123507270-null-null.nonecasehttps://www.guyuehome.com/39703#%E4%B8%89%E3%80%81%E5%AE%9A%E6%97%B6%E5%99%A8%E8%BE%93%E5%87%BAPWMhttps://blog.csdn.net/qq_38410730/article/details/79996222