STM32之定时器

文章目录

    • STM32的时钟系统
    • 定时器之输出PWM
    • 定时器之输入捕获
    • 定时器之中断

STM32的时钟系统

由于定时器和时钟,听起来总有那么一点相似之处。所以作为本文也简略阐述一下关于STM32定时器的相关内容,有了这部分基础,再去学习定时器或许更清晰。
1、时钟源
STM32有四个时钟源和一个PLL。分别HSI、HSE、LSI、LSE。其英文全称大概是High Speed External之类的,所以顾名思义也能知道它们分别对应,内部高速时钟、外部告诉时钟、内部低速时钟、外部高速时钟。
HSI:STM32内置的时钟,为8Mhz,常给STM32的AHB总线提供时钟源。优点是内置的,不需要外接晶振,缺点则是不稳定且误差大。
HSE:STM32外部时钟,为8Mhz,常给STM32AHB总线提供时钟源。优点是稳定且准确,缺点是需要外接晶振。但我们一般买的最小系统板子都有这个晶振,故也没啥缺点,我们一般默认的也是用这个时钟,而不是HSI。
LSI、LSE:一个内部一个外部,分别是40kHz和32.768kHz。与高速时钟相同,内部的不稳定,外部的稳定。一般给看门狗之类的外设使用。
2、分频和倍频
这里以STM32F193系列的单片机为例,这个系列的单片机最大的时钟频率是72Mhz,但上文提到,我们一般是用8Mhz的HSE提供时钟源,那如何上升到72Mhz呢?
这里PLL就出场了,至于PLL具体原理是什么,这里按下不表,有兴趣的同学自行了解。总之,HSE通过PLL乘以9则变成了72Mhz的主频了,即AHB的频率。
而AHB继续被用于各个总线和外设中,较为经典的是APB1、APB2两条总线。其中APB2完全继承了72Mhz的频率,而APB1最高限制只能是36Mhz的频率,这使得挂载在APB1总线上的设备大都只能有36Mhz的频率。但是,挂载在APB1上的高级定时器,TIM1和TIM8依旧有72Mhz的频率。因为到了定时器,又被默认倍频到72Mhz了。
3、总结
说了一大堆,绕了几个圈,最终还是得出了总所周知的结论。所有定时器,无论是通用定时器还是高级定时器,其时钟频率都是比较准确的72Mhz。

定时器之输出PWM

1、什么是pwm
简而言之就是,在一个固定的周期内,对有效电平的占比的调节。如给一个电器接点1s,其中0.6s是高电平(有效电平),剩下0.4s是低电平,则占空比为%60。
而在stm32中,有关PWM的寄存器有TIMx_CCRx,TIMx_ARR。
ARR:计数周期
CCR:计数阈值
以常规的向上计数为例:ARR=1000,CCR=600。则有效电平占空比为%60。具体设置参考下文
2、GPIO设置
PWM模式下的端口需要设置为复用推挽输出,其他和普通IO设置一样即可。

GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;

注意:GPIO不是普通的任何一个GPIO,是定时器对应通道的指定的GPIO。
3、频率设置
频率设置设计到两个关键的参数,其一则是上文提到的ARR(自动重转载值),另一个就是PSC(预分配系数),这两个参数共同决定了定时器的计数周期。

公式:周期=(PSC+1)*(ARR+1)/72Mhz

在stm32中的设置参考如下:

	TIM_BaseInitStructure.TIM_Period = 7199; 
	TIM_BaseInitStructure.TIM_Prescaler = 0;

或寄存器版本的

TIMX->ARR=7199;
TIMX->PSC=0;

注:上文的PWM周期是100us,X是指代任意一个定时器。读者可根据自身需要灵活设置。
4、其他设置
设置参考如下

	TIM_BaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;  //时钟分割,不分割
	TIM_BaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;  //计数模式,向上计数

5、通道设置
每个定时器都有四路PWM输出通道,以其中一路设置参考如下

	TIM_OCInitTypeDef  TIM_OCInitStructure;     //重命名结构体
	TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;   //PWM模式,模式一(从0到CCR)为有效电平
	TIM_OCInitStructure.TIM_OCPolarity= TIM_OCPolarity_High;  //有效电平是高电平
	TIM_OCInitStructure.TIM_OutputState= TIM_OutputState_Enable;  //输出使能
	TIM_OC1Init(TIM3, &TIM_OCInitStructure);  //通道一使能
	TIM_OC1PreloadConfig(TIM3,TIM_OCPreload_Enable);  //预装载使能
	//TIM_CtrlPWMOutputs(TIM1, ENABLE);  //TIM1和TIM8时候药打开
	
	TIM_Cmd(TIMx,ENABLE);  //最后定时器使能

6、调用

uint16_t PWM;     //输入你给定的PWM
TIMx->CCRx=PWM;   //和TIM_SetCompareX(TIMx,PWM)一样,其中x是1、2、4之类的数字

定时器之输入捕获

1、什么是输入捕获
通俗的讲就是当电平跃变的时候,由定时器检测到,每检测到一个TIMx_CNT加一或减一。跃变通常指上升沿或下降压沿,即电压由低变高或由高变低的过程。
当TIMx_CNT寄存器的值放到TIMx_CCRx中的时候视为捕获完成。(此句了解即可)
STM32之定时器_第1张图片

2、GPIO设置

GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;  //上拉输入
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPD;   //下拉输入
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;  //浮空输入

以上三种输入方式任选一种即可。
3、计数设置

TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
TIM_TimeBaseStructure.TIM_Period = arr;   //设定计数器自动重装值
TIM_TimeBaseStructure.TIM_Prescaler =psc; //触发psc+1次,TIMx_CNT+1。如无特殊要求,设置为0即可
TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; //时钟分割,不分割
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; //设置向上计数或者向下计数
TIM_TimeBaseInit(TIMx, &TIM_TimeBaseStructure); 

4、通道设置
和PWM模式不同,这里是ICInitStructure,而PWM模式是OCInitStructure,大概对应的是英文Input Channel和Output Channel吧。

	TIM_ICInitTypeDef  TIM_ICInitStructure;   //重命名结构体
	TIMx_ICInitStructure.TIM_Channel = TIM_Channel_x; //设置通道
  	TIMx_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Rising;	//设置上升沿捕获或下降沿捕获(Falling)
  	//TIMX_ICInitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI; //如果用的是channel1则直接映射到TI1上
  	TIMx_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1;	 //配置输入分频,不分频 
  	TIMx_ICInitStructure.TIM_ICFilter = 0x00;// 配置输入滤波器,0x00表示不滤波
  	TIM_ICInit(TIMx, &TIMx_ICInitStructure);
  	
  	TIMx->CNT=0;         //初始化清空CNT计数器
	TIM_Cmd(TIMx, ENABLE);     //定时器使能

5、编码器模式设置
编码器比较复杂,这里不展开讲了,总之按照一下设置,将电机的AB相分别接到定时器的一二通道上,再将结果除以4则是电机转动的圈数。

TIM_EncoderInterfaceConfig(TIMx, TIM_EncoderMode_TI12, TIM_ICPolarity_Rising ,TIM_ICPolarity_Rising); 

备注:编码器模式下只能是一二通道输入捕获,且如果设置为编码器模式,则通道设置这一部分可以完全省略掉。
6、输入捕获中断

TIM_ITConfig(TIMX,TIM_IT_Update|TIM_IT_CC1,ENABLE);   //允许中断和允许更新中断

到这里想要中断成功还是缺乏一个中断嵌套设置(NVIC)和中断服务函数的调用的,放后面讲。

定时器之中断

1、什么是中断
摘自百度词条是这样的,但是本人认为在使用中断的过程更多的不是“意外”情况需要主机干预,而是自己”希望“它中断。

中断是指计算机运行过程中,出现某些意外情况需主机干预时,机器能自动停止正在运行的程序并转入处理新情况的程序,处理完毕后又返回原被暂停的程序继续运行。

2、定时时间设置
定时器中断不需要设置时间,只需要参考以上的计数设置即可。

TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
TIM_TimeBaseStructure.TIM_Period = arr;  
TIM_TimeBaseStructure.TIM_Prescaler =psc; 
TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; //时钟分割,不分割
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; //设置向上计数或者向下计数
TIM_TimeBaseInit(TIMx, &TIM_TimeBaseStructure); 

其中,psc和arr两个值的组合,构成了定时器中断的时间。例如我arr=7199,psc=49。则中断时间为5ms。

((7199+1)*(49+1))/72M=5ms

值得一提的是,定时器的计数设置之后,使能该定时器则定时器立刻会计数。计数这个功能你便可以用来卡PWM,卡时间中断等等。
3、其他设置

TIM_ITConfig(TIMX,TIM_IT_Update,ENABLE);	   //使能中断

4、中断设置
中断设置也是比较见简单的了,设置中断组、中断线、子优先级、抢占优先级最后使能。

	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);		
	NVIC_InitStructure.NVIC_IRQChannel = TIMX_IRQn;  //TIM2中断
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x01;  //先占优先级0级
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x01;  //从优先级3级
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道被使能
	NVIC_Init(&NVIC_InitStructure);  //初始化NVIC寄存器

5、中断服务函数
定时器的中断服务函数大概长这样,分别是进入中断服务函数之后再次确认是否中断标志位置位了,然后就清空标志位。

void TIMX_IRQHandler()
{
	if (TIM_GetITStatus(TIMX,TIM_IT_Update)!=RESET)  
	{	
		TIM_ClearITPendingBit(TIMX,TIM_IT_Update);
	}
}

再次提醒:上文所有提到的X都是1,2,3,4之类的整数。自行替换才可以使用。

你可能感兴趣的:(stm32(单片机),stm32,单片机,arm)