配置定时器
定时器分类
(STM32F103RCT6)
定时器相关结构
①时钟源
定时器时钟 TIMxCLK,即内部时钟 CK_INT,经 APB1 预分频器后分频提供,如果APB1 预分频系数等于 1,则频率不变,否则频率乘2,库函数中 APB1 预分频的系数是 2,即 PCLK1=36M,所以定时器时钟 TIMxCLK=36*2=72M。
②计数器时钟
定时器时钟经过 PSC 预分频器之后,即 CK_CNT,用来驱动计数器计数。PSC 是一个16 位的预分频器,可以对定时器时钟 TIMxCLK 进行 1~65536 之间的任何一个数进行分频。具体计算方式为:CK_CNT=TIMxCLK/(PSC+1)。
③计数器
计数器 CNT 是一个 16 位的计数器,只能往上计数,最大计数值为 65535。当计数达到自动重装载寄存器的时候产生更新事件,并清零从头开始计数。
④自动重装载寄存器
自动重装载寄存器 ARR 是一个 16 位的寄存器,这里面装着计数器能计数的最大数值。当计数到这个值的时候,如果使能了中断的话,定时器就产生溢出中断。
⑤定时时间的计算
定时器的定时时间等于计数器的中断周期乘以中断的次数。计数器在 CK_CNT 的驱动下,计一个数的时间则是 CK_CLK 的倒数,等于:1/(TIMxCLK/(PSC+1)),产生一次中断的时间则等于:1/(CK_CLK * (ARR+1))。如果在中断服务程序里面设置一个变量 timer,用来记录中断的次数,那么就可以计算出我们需要的定时时间等于 : [1/(CK_CLK * (ARR+1))]*time。
ARR:自动重装载寄存器数值。 PSC:定时器时钟预分频数值。 TIMxCLK:主频。
时基单元包含:● 计数器寄存器(TIMx_CNT) ● 预分频器寄存器 (TIMx_PSC) ● 自动装载寄存器 (TIMx_ARR) ● 重复次数寄存器 (TIMx_RCR)
例如:主频为72M,预分频为72,则计一个数的时间为[(1/(72M/72))秒],即1us;自动重装载寄存器数值设置为1000-1,则利用记一个数的时间乘上该数值,即是产生一次中断的时间:1us * 1000 = 1ms。再用一个timer来记录产生中断的时间,那么可以知道,如果timer等于1000,则表示此时定时的时间为1s。
基本定时器
初始化结构体(3种)
基本定时器 | 输出比较 | 输入捕获 |
---|
TIM Time Base Init structure | TIM Output Compare Init structure | TIM Input Capture Init structure |
①基本定时器TimeBase
typedef struct
{
uint16_t TIM_Prescaler;
uint16_t TIM_CounterMode;
uint16_t TIM_Period;
uint16_t TIM_ClockDivision;
uint8_t TIM_RepetitionCounter;
} TIM_TimeBaseInitTypeDef;
②输出比较OutputCompare
typedef struct
{
uint16_t TIM_OCMode;
uint16_t TIM_OutputState;
uint16_t TIM_OutputNState;
uint16_t TIM_Pulse;
uint16_t TIM_OCPolarity;
uint16_t TIM_OCNPolarity;
uint16_t TIM_OCIdleState;
uint16_t TIM_OCNIdleState;
} TIM_OCInitTypeDef;
③输入捕获InputCapture
typedef struct
{
uint16_t TIM_Channel;
uint16_t TIM_ICPolarity;
uint16_t TIM_ICSelection;
uint16_t TIM_ICPrescaler;
uint16_t TIM_ICFilter;
} TIM_ICInitTypeDef;
④TIM1,8高级定时器死区和刹车
typedef struct
{
uint16_t TIM_OSSRState;
uint16_t TIM_OSSIState;
uint16_t TIM_LOCKLevel;
uint16_t TIM_DeadTime;
uint16_t TIM_Break;
uint16_t TIM_BreakPolarity;
uint16_t TIM_AutomaticOutput;
} TIM_BDTRInitTypeDef;
定时器分为基本定时器(TIM6和TIM7)、高级定时器(TIM1和TIM8)、通用定时器(TIM2、TIM3、TIM4、TIM5)
基本定时器配置
? 注意:TIM_TimeBaseInitTypeDef结构体里面有5个成员,TIM6和TIM7的寄存器里面只有TIM_Prescaler和TIM_Period,所以使用TIM6和TIM7的时候只需初始化这两个成员即可,另外三个成员是通用定时器和高级定时器才有。 然后编写中断服务函数,在stm32f10x_it.c文件中编写 由于基本定时器只能向上计数,所以检测的中断标志位为TIM_IT_Update,清除的中断未决位:TIM_FLAG_Update (定时器每计时一次,检测一次标志位,如果定时完毕则time++,然后给该标志位写1)
在主函数中编写用户代码
int main(void)
{
LED_GPIO_Config();
BASIC_TIM_Init();
while(1)
{
if ( time == 1000 )
{
time = 0;
......
}
}
}
高级定时器
①定时
和基本定时器的配置和使用基本一致,结构体中多了几个要配置的参数,使用也和基本定时器一致,都要在中断中编写中断回调函数。
②PWM互补输出带死区时间
? 高级控制和通用定时器通道引脚分布:
? 结构体初始化:对应的GPIO、时基、输出比较模式、死区和刹车配置
PWM输出时的Dead Zone(死区)作用是在电平翻转时插入一个时间间隔,避免关闭前一个设备和打开后一个设备时因为开关速度的问题出现同时开启状态而增加负荷的情况(在没有彻底关闭前打开了后一个设备),尤其是电流过大时容易造成短路等损坏设备,如:互补PWM波输出在逆变器(直流转交流)中的应用。
刹车则是指当BKIN引脚检测到高电平或低电平的时候,输出比较信号被禁止输出,就像刹车一样。
利用PWM做电机调速时,可通过修改占空比来调节。
输出比较通道的互补通道,即电平和输出比较通道反相。
③输出4路占空比不同的PWM
? 配置PWM输出通道,4个输出比较通道
? 配置输出比较结构体以及初始化
? 利用虚拟示波器观察结果,也可用来做呼吸灯,控制电机速度等,但要用其它方法提高占空比的分辨率
④PWM输入捕获
(外部输入PWM波,stm32接收并进行数据处理)
输入捕获模式配置:时基、输入捕获模式
输入捕获模式和PWM输入模式的区别:
1、输入捕获模式 stm32的通用TIM2、3、4、5 都具有输入捕捉的功能,每个定时器具有四个通道,并且每一个通道都可以单独配置为输入捕捉式,主要用于测量输入信号的高电平时间,也可测量信号的频率(可能不太精确,尤其对于频率很高的信号),如果用于测量信号的高电平时间,配置时需要注意定时器的时基频率。
需要注意的问题: 定时器的时基频率不能太高,如果定时器工作于36M,采集50Hz的信号就会出现偏差(实测),所以程序中将定时器的时基频率配置为1M;如果存在两个以上中断,需要设置中断优先级,否则容易出问题。
2、PWM输入模式 PWM输入模式是输入捕捉模式的高级应用,对于测量频率较高的输入信号的频率特别精确,当然,为了实现这个模式,也得做出一点牺牲。相比于基本输入捕捉功能的实现来说,PWM输入模式中,一路输入信号同时映射到两个引脚,而且只有第一和第二通道可以配置为这种模式,换句话说,每个通用定时器只能测量一路输入信号。
通用定时器
①定时
与高级定时器的配置和使用完全相同,先配置定时器初始化结构体,清除中断标志位,开中断,使能计数器;然后编写中断回调函数;最后在主函数中编写用户代码。
②4路PWM输出
与高级定时器中的输出4路占空比不同的PWM的配置一致,可用示波器观察。
③测量脉冲宽度
? 先进行输入捕获参数配置,注意输入捕获模式和PWM输入模式的区别
? 别忘了配置输入捕获的GPIO口和中断优先级的配置
? PWM信号 周期和占空比的计算
在中断中记录一个脉冲的时间,先确定上升沿,再确定下降沿,两者之间的时间也就是一个脉冲高电平宽度。乘以2则是一个PWM波的总时间。具体实现过程如下
typedef struct
{
uint8_t Capture_FinishFlag;
uint8_t Capture_StartFlag;
uint16_t Capture_CcrValue;
uint16_t Capture_Period;
}TIM_ICUserValueTypeDef;
void TIM5_IRQHandler(void)
{
if ( TIM_GetITStatus ( GENERAL_TIM, TIM_IT_Update) != RESET )
{
TIM_ICUserValueStructure.Capture_Period ++;
TIM_ClearITPendingBit ( GENERAL_TIM, TIM_FLAG_Update );
}
if ( TIM_GetITStatus (GENERAL_TIM, GENERAL_TIM_IT_CCx ) != RESET)
{
if ( TIM_ICUserValueStructure.Capture_StartFlag == 0 )
{
TIM_SetCounter ( GENERAL_TIM, 0 );
TIM_ICUserValueStructure.Capture_Period = 0;
TIM_ICUserValueStructure.Capture_CcrValue = 0;
GENERAL_TIM_OCxPolarityConfig_FUN(GENERAL_TIM, TIM_ICPolarity_Falling);
TIM_ICUserValueStructure.Capture_StartFlag = 1;
}
else
{
TIM_ICUserValueStructure.Capture_CcrValue = GENERAL_TIM_GetCapturex_FUN (GENERAL_TIM);
GENERAL_TIM_OCxPolarityConfig_FUN(GENERAL_TIM, TIM_ICPolarity_Rising);
TIM_ICUserValueStructure.Capture_StartFlag = 0;
TIM_ICUserValueStructure.Capture_FinishFlag = 1;
}
TIM_ClearITPendingBit (GENERAL_TIM,GENERAL_TIM_IT_CCx);
}
}
int main(void)
{
uint32_t time;
uint32_t TIM_PscCLK = 72000000 / (GENERAL_TIM_PSC+1);
USART_Config();
GENERAL_TIM_Init();
while ( 1 )
{
if(TIM_ICUserValueStructure.Capture_FinishFlag == 1)
{
time = TIM_ICUserValueStructure.Capture_Period
* (GENERAL_TIM_PERIOD+1)
+ (TIM_ICUserValueStructure.Capture_CcrValue+1);
printf ( "\r\n测得高电平脉宽时间:%d.%d s\r\n",time/TIM_PscCLK,time%TIM_PscCLK );
TIM_ICUserValueStructure.Capture_FinishFlag = 0;
}
}
}
|