目录
一,定时器的简介
1,什么是定时器(TIM)
定时器就是能够准确规定一段时间的硬件电路。在单片机中,定时器可以对输入的时钟进行计数,并在计数值达到设定值时触发中断。也就是TIM,全名Timer。
在单片机中是16位计数器、预分频器、自动重装寄存器的时基单元,在72MHz计数时钟下可以实现最大59.65s的定时。
2,为什么要用定时器
在上一章节,我写的是关于外部中断(EXTI)这篇文章,大家对中断有了一个概念,像外部中断这种需要物理动作才能触发中断函数执行,但很多情况下我们需要的是中断函数在适当时刻可以自动得去执行,所以就需要定时器来辅助了。
这就像是,你平时想认真学习,但在30分钟以后就要吃饭了,你担心错过吃饭时间,就设置了一个30分钟后会响的闹钟,在这30分钟你想怎么学习就怎么学习,不用担心时间问题。
3,定时器的应用
定时器(Timer)最基本的功能就是定时了,比如定时发送 USART 数据、定时采集 AD数据等等。如果把定时器与 GPIO 结合起来使用的话可以实现非常丰富的功能,可以测量输入信号的脉冲宽度,可以生产输出波形。定时器生产 PWM 控制电机状态是工业控制普遍方法,这方面知识非常有必要深入了解。
4,定时器的功能及分类
功能:
不仅具备基本的定时中断功能,而且还包含内外时钟源选择、输入捕获、输出比较、编码器接口、主从触发模式等多种功能。
分类:
根据复杂度和应用场景分为了高级定时器、通用定时器、基本定时器三种类型。
STM32F103C8T6定时器资源:TIM1、TIM2、TIM3、TIM4
我们一般用不到高级定时器,因为高级定时器特有的功能,我们一般是用不上的,就比如死区生成,以我们目前的水平一般是不会遇到需要解决的这类问题 ,下面我给大家主要介绍通用定时器,顺便说一下什么是死区。 我是绝对不会告诉你们,其实是我不太会用高级定时器的,哈哈哈哈!
高级定时器:
通用定时器:
通用定时器的工作过程如图所示:
大家看了两幅图也知道,通用定时器比高级定时器少了DTG寄存器,极性选择以及时钟失效事件等等。
序号1是时钟发生器,序号2是时基单元,序号3是输入捕获,序号4是输出比较,通用定时器大致分为这4个部分。
在时钟发生器中,由内部时钟或外部TIMx_ETR等经过一系列处理产生时钟源,送入时基单元;在时基单元中经过预分频产生一个时钟,再由CNT进行计数(向上或向下),计数到自动重装载值时可以触发相应的中断;在输入捕获部分中,对TIMx_CHx中的信号进行捕获,再进行滤波等操作,再通过捕获比较寄存器中捕获到两次信号后的计数器的值,就可以得到脉冲宽度等;在输出比较的部分中,可以在捕获比较寄存器中设置一个数值,用计数器中的值与其比较,当高于该数值时,输出高/低电平,低于该数值时,输出低/高电平。
基本定时器:
这个普通定时器,就不多介绍了,大家随便了解即可。
5,死区
完美的理解MOS就只有开与关两个状态, 但现实并没有这种美事, 在状态转完时(很短的时间), 第三种部分导通的状态出现。当控制两MOS转变电流方向时, 在很短的时间内, 两MOS都处于第三种状态, 而后果是, 很短时间内有电流从上面的MOS流经下面的MOS到地,而不是流到负载去。这种不是流到负载去的电流是损耗, 费工这还是小事, 大事是它会让MOS产生很大的功耗, 比起在关与闭的两种状态下大得多得多,而这功耗将会转化为热能。
为了避免第三种状态引起的问题, 就发明了死区这个东东, 意思是上下两管的控制信号不完全是相反的状态, 而是在关闭一MOS的信号后,Delay一段时间, 后才打开另一MOS,这是错开两MOS管的第三种状态的方法,以避免电流直接流经两MOS管,这就是死区的用意。
二,定时中断
1,定时中断的基本结构
定时器中断主要应用到通用定时器的时钟发生器和时基单元两个部分。
2,基本结构的概述
1. 时钟源(TIMxCLK)
定时器时钟 TIMxCLK,即内部时钟 CK_INT,经 APB1 预分频器后分频提供,如果APB1 预分频系数等于 1,则频率不变,否则频率乘以 2,库函数中 APB1 预分频的系数是 2,即 PCLK1=36M,所以定时器时钟 TIMxCLK=36*2=72M。
2. 计数器时钟(CK_CNT)
定时器时钟经过 PSC 预分频器之后,即 CK_CNT,用来驱动计数器计数。PSC 是一个16 位的预分频器,可以对定时器时钟 TIMxCLK 进行 1~65536 之间的任何一个数进行分频。具体计算方式为:CK_CNT=TIMxCLK/(PSC+1)。
3. 计数器(CNT)
计数器 CNT 是一个 16 位的计数器,只能往上计数,最大计数值为 65535。当计数达到自动重装载寄存器的时候产生更新事件,并清零从头开始计数。
计数器模式
通用计数器模式分为三种:向上计数、向下计数、向上向下双向计数。
向上计数模式:计数器从0计数到自动加载值,然后从0重新开始计数并产生一个计数器溢出事件;
向下计数模式:计数器从自动装入值开始向下计数到0,然后从自动装入的值重新开始并产生一个计数器向下溢出事件;
向上/向下计数(中央对齐模式):计数器从0开始计数到自动装入的值-1,产生一个计数器溢出事件,然后向下计数到1并且产生一个计数器溢出事件;然后再从0开始重新计数。
4. 自动重装载寄存器(ARR)
自动重装载寄存器 ARR 是一个 16 位的寄存器,这里面装着计数器能计数的最大数值。当计数到这个值的时候,如果使能了中断的话,定时器就产生溢出中断。
5,输出比较简介
OC(Output Compare)输出比较,输出比较可以通过比较CNT与CCR寄存器值的关系,来对输出电平进行置1、置0或翻转的操作,用于输出一定频率和占空比的PWM波形 每个高级定时器和通用定时器都拥有4个输出比较通道,高级定时器的前3个通道额外拥有死区生成和互补输出的功能。
三,代码的编写步骤及代码
1,定时器中断实现步骤
1,使能定时时钟;
2,初始化定时器,配置自动装载寄存器ARR,预分频器寄存器PSC;
3,开启定时器中断;
4,配置NVIC;
5,使能定时器
6,编写中断服务函数
2,定时器中断代码
Timer.c
#include "stm32f10x.h" // Device header
void Timer_Init(void)
{
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);//开启时钟
TIM_InternalClockConfig(TIM2);//采用内部时钟给 TIM2提供时钟源
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;//结构体定义
TIM_TimeBaseInitStructure.TIM_ClockDivision=TIM_CKD_DIV1;//设置时钟分割,这里是不分割
TIM_TimeBaseInitStructure.TIM_CounterMode=TIM_CounterMode_Up; //TIM向上计数模式
TIM_TimeBaseInitStructure.TIM_Period=10000 - 1;//设置在下一个更新事件装入活动的自动重装载寄存器周期的值
TIM_TimeBaseInitStructure.TIM_Prescaler=7200 - 1;//预分频值
TIM_TimeBaseInitStructure.TIM_RepetitionCounter=0;//定时0.001秒,进入一次中断
TIM_TimeBaseInit(TIM2,&TIM_TimeBaseInitStructure);//初始化TIM2
TIM_ClearFlag(TIM2,TIM_FLAG_Update);//中断清除
TIM_ITConfig(TIM2,TIM_IT_Update,ENABLE);//开启定时器中断
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//中断优先级分组
NVIC_InitTypeDef NVIC_InitStructure;
NVIC_InitStructure.NVIC_IRQChannel=TIM2_IRQn;//设置中断源
NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE;//中断源使能
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=2;//抢占优先级
NVIC_InitStructure.NVIC_IRQChannelSubPriority=1;//响应优先级
NVIC_Init(&NVIC_InitStructure); //调用NVIC初始化函数
TIM_Cmd(TIM2,ENABLE);//定时器2使能
}
main.c
#include "stm32f10x.h" // Device header
#include "Delay.h"
#include "OLED.h"
#include "Timer.h"
uint16_t Num;
int main(void)
{
OLED_Init();
Timer_Init();
OLED_ShowString(1,1,"Num:");
while (1)
{
OLED_ShowNum(1,5,Num,5);//记录发生中断次数
// OLED_ShowNum(2,5,TIM_GetCounter(TIM2),5);
}
}
void TIM2_IRQHandler(void)
{
if(TIM_GetITStatus(TIM2,TIM_IT_Update)==SET)//检查指定的TIM中断发生与否:TIM 中断源
{
Num++;
TIM_ClearITPendingBit(TIM2,TIM_IT_Update);//清除TIMx的中断待处理位:TIM 中断源
}
}
四,总结
这一章是学习PWM的关键,大家一定要好好学习,理解清楚,后面学习理解PWM会更容易一些。如果后面有时间,我给大家讲解一下时钟树,大家记得提醒一下我哈,希望我的文章对大家有用。最近期末忙着备考,以及暑假今年的全国电子设计大赛,我需要好好准备,毕竟咱现在跟绝大多数人一样都是菜鸟,所以我的更新会不定时,下一章,我把PWM给大家讲了,我就打算,教大家红外循迹,再讲解一下PID,再用PID控制的红外循迹,暂时是这样想的,具体后面再说。
希望我的文章对大家有用,能帮到你们就是我最大的快乐,我和你们一起见证自己的成长,大家一起努力学习,一起加油!最后,完整代码我在这里提供给大家:
链接:https://pan.baidu.com/s/1sIAL3S0GIqCJSVS-9hDpWA?pwd=yl66
提取码:yl66