- 第六章 定时计数器组件
- 学习目标
- 重点内容
- 通用定时器Timer
- 捕获功能——测量脉冲宽度
- 比较功能——定时和输出
- STM32F10x系列定时器TIMx及应用
- 定时器常用寄存器
- 定时器定时步骤
- 实例
- TIM1更新中断应用
- TIM2比较中断应用-4通道定时
- 看门狗定时器
- 独立看门狗IWDG
- 实时钟定时器RTC
- 脉宽调制定时器PWM
- PWM组成
- PWM相关寄存器
- PWM输出周期与占空比
- 占空比
- PWM应用初始化
- 熟悉定时计数器的基本功能
- 熟练掌握通用定时器的原理及应用
- 了解系统定时器SysTick的作用
- 熟练掌握看门狗定时器的原理及典型应用
- 熟悉RTC(实时钟)及其应用
- 熟练掌握PWM原理及应用
- 1、通用定时器更新和比较定时,会计算定时时间及预分频值、重装载值,并编程实现。
- 2、独立看门狗定时器的作用,计算看狗门定时溢出时间。选择溢出时间的一般原则。
- 3、知晓RTC及其功能。
- 4、掌握输出PWM波形的方法及边沿对齐(向上计数)编程应用(周期或频率的改变方法,占空比改变方法)。
通用定时器Timer
定时的实质是计数,只是需要考虑计数的脉冲周期。
所有跟定时有关的组件一个共同的特点就是对特定输入的时钟通过分频后接入计数器进行加1或减1计数,计数达到预定的数值后将引发一个中断并置溢出标志。
如果需要定时,则处理确定计数值外,还要考虑定时计数器计数时钟的周期。如果定时器经过若干分频后的计数时钟频率为
f
f
f ,计数次数为
N
N
N ,则定时的时间为
T
=
N
/
f
T=N/f
T=N/f
假设分频器的值为 PR ,输入时钟频率为
F
P
C
L
K
F_{PCLK}
FPCLK ,定时器的计数频率为
F
=
F
P
C
L
K
/
(
P
R
+
1
)
F=F_{PCLK}/(PR+1)
F=FPCLK/(PR+1)
因此计数值为N时,定时时间为
T
=
N
(
P
R
+
1
)
/
F
P
C
L
K
T=N(PR+1)/F_{PCLK}
T=N(PR+1)/FPCLK
当定时器运行时,在捕获引脚上出现有效外部触发动作,此时定时器计数器的当前的值保存到指定捕获寄存器中,这一功能叫输入捕获。
比较功能——定时和输出 当定时器计数值与预设的比较寄存器的值相等时将产生比较信号并激发一个比较中断。
STM32F10x系列定时器TIMx及应用STM32F10x集成了8个16位定时器(TIM1-TIM8),分为两大类,三个级别:
- (1)高级控制定时器:TIM1和TIM8(连接在APB2快速外设总线上)
- (2)通用定时器:TIM2-TIM5(连接在APB1慢速外设总线上)
- (3)基本定时器:TIM6和TIM7(连接在APB1慢速外设总线上),仅能更新定时
| 定时器类别 | 计数类型 | 连接总线及最高频率 | 中断 | 捕获比较通道 | PWM及互补输出 |
|---|---|---|---|---|---|
| 高级控制定时器TIM1/TIM8 | 向上、向下、上/下 | APB2 72MHz | 刹车、触发、通信COM、捕获/比较1-4、更新 | 4 | PWM、有互补输出 |
| 通用定时器TIM2/TIM3/TIM4/TIM5 | 向上、向下、上/下 | APB1 36MHz | 触发、捕获/比较1-4、更新 | 4 | PWM、无互补 |
| 基本定时器TIM6/TIM7 | 向上 | APB1 36MHz | 更新 | 0 | 全无 |
- 预分频器(TIMx_PSC):决定计数时钟与基准计数时钟的关系
- 自动重装载寄存器(TIMx_ARR) :重新装入计数初始值
- 计数器(TIMx_CNT):当前计数器计数值存放寄存器
- 捕获/ 比较寄存器 1(TIMx_CCR1):存放1通道捕获/比较寄存器值
- 捕获/ 比较寄存器2(TIMx_CCR2):存放2通道捕获/比较寄存器值
- 捕获/ 比较寄存器 3(TIMx_CCR3):存放3通道捕获/比较寄存器值
- 捕获/ 比较寄存器 4(TIMx_CCR4):存放4通道捕获/比较寄存器值
- TIM1 和TIM8 重复计数寄存器(TIMx_RCR) :存放溢出次数
F = F P L C K / ( ( P S C + 1 ) ∗ ( A R R + 1 ) ∗ ( R C R + 1 ) ) F= F_{PLCK}/((PSC + 1)*(ARR + 1)*(RCR + 1)) F=FPLCK/((PSC+1)∗(ARR+1)∗(RCR+1))
定时器定时步骤-
1、初试化定时器
-
用寄存器操作方式使能定时器TIM1/TIM2/TIM6/TIM8时钟
-
TIM1和TIM8是连在APB2总线上的,其他定时器连接在APB1总线上
-
RCC->APB1ENR|=(1<<0)|(1<<4); RCC->APB2ENR|=(1<<11)|(1<<13);
-
-
用CMSIS固件库函数使能定时器TIM1/TIM2/TIM6/TIM8时钟
-
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2|RCC_APB1Periph_TIM6,ENABLE); RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM1|RCC_APB2Periph_TIM8,ENABLE);
-
-
-
2、设置定时时间
- 更新方式定时确定定时参数
T = ( 1 + A R R ) ∗ ( 1 + P S C ) / F T=(1+ARR)*(1+PSC)/F T=(1+ARR)∗(1+PSC)/F
对于TIM1/TIM8
T
=
(
1
+
A
R
R
)
∗
(
1
+
P
S
C
)
∗
(
R
C
R
+
1
)
/
F
T=(1+ARR)*(1+PSC)*(RCR + 1)/F
T=(1+ARR)∗(1+PSC)∗(RCR+1)/F
根据公式可以得到
A
R
R
=
T
∗
F
/
(
1
+
P
S
C
)
−
1
ARR=T*F/(1+PSC)-1
ARR=T∗F/(1+PSC)−1
#define N 10 TIM3->ARR=10*N-1; TIM3->PSC= SystemCoreClock/10000-1 ; ; 当SystemCoreClock=F时,有 ARR=10000*T-1=10*1000T-1,当T=1秒=1000ms时,ARR=10*1000-1 当T=N ms时,有ARR=10*N-1 用固件库函数操作如下: TIM_TimeBaseStructure.TIM_Period =10*N-1; TIM_TimeBaseStructure.TIM_Prescaler = SystemCoreClock/10000-1 ; TIM_TimeBaseInit(TIM3,&TIM_TimeBaseStructure);
比较方式中断定时方式
T
=
C
C
R
∗
(
1
+
P
S
C
)
/
F
T=CCR*(1+PSC)/F
T=CCR∗(1+PSC)/F
TIM3->ARR=65535; TIM3->CCR=10*N; TIM3->PSC= SystemCoreClock/10000-1 ; ; SystemCoreClock=F CCR=10000T=10*1000T,当T=1秒时CCR=10*1000 T=10ms=0.01S时,CCR=10*10 T=N ms时,CCR=10*N
-
3、选择计数模式(默认向上)
-
用寄存器操作
-
TIMx->CR1&=~(1<<4);
-
-
用库函数操作
-
TIM_TimeBaseStructure.TIM_CounterMode=TIM_CounteMode_Up;//向上 TIM_TimeBaseInit(TIMx, &TIM_TimeBaseStructure);
-
-
-
4、设置中断运行寄存器DIER初始化定时中断
-
更新中断
-
用寄存器操作: TIMx->DIER|=1<<0; 用固件库操作 TIM_ITConfig(TIM1, TIM_IT_Update, ENABLE);
-
-
比较中断
-
用寄存器操作 如果采用比较中来定时,则要关闭更新中断,使能比较中断: TIMx->DIER&=~(1<<0); TIMx->DIER|=(1<<1)|(1<<2)|(1<<3)|(1<<4); 用固件库操作 TIM_ITConfig(TIM1, TIM_IT_CC1, ENABLE); //通道1比较 中断使能
-
-
-
5、设置控制寄存器使能定时器
-
用寄存器操作: TIMx->CR1|=1<<0; 用固件库操作 TIM_Cmd(TIMx, ENABLE);
-
-
6、初始化定时器中断
-
先清除中断标志,然后开定时器中断,用固件库函数初试化
-
TIM_ClearITPendingBit(TIMx,TIM_IT_Update); TIM_ITConfig(TIMx,TIM_IT_Update,Enable);
-
可以把(1)~(6)写入TIM_Init()函数中
-
7、设置NVIC进行定时器中断分组
-
void NVIC_Configuration(void)//定时器中断配置 { NVIC_InitTypeDef NVIC_InitStructure; NVIC_SetVectorTable(NVIC_VectTab_FLASH, 0x0); NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); NVIC_InitStructure.NVIC_IRQChannel = TIMx_IRQn;//x为2,3,4,5,6,7 NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0; NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1; NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; NVIC_Init(&NVIC_InitStructure); }
-
-
8、编写定时中断服务函数
- 按照CMSI约定TIMx_IRQHandler为非高级定时器中断服务函数名高级定时器TIM1和TIM8有单独的更新TIMx_UP_IRQHandler高级定时器比较中断函数名TIMx_CC_IRQHandler通常在stm32f10x_it.c或其它指定C文件中。
- 在中断服务函数中要做的处理是判断是哪种中断,然后清除 中断标志,进行中断处理
实例 TIM1更新中断应用
【例1】对于 STM32F10x利用TIM1更新方式定时,让LED1指示灯(LED1发光二极管)每1s为周期闪烁,试编程实现。
分析:1s为周期进行发光管闪亮,意思是0.5s=500ms定时来改变LED1发光状态。初始化TIM1如下
void TIM_Configuration(void)
{
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM1, ENABLE);//使能TIM1时钟
TIM_TimeBaseStructure.TIM_RepetitionCounter = 0 ;//重复计数器
TIM_TimeBaseStructure.TIM_Period = 10*500-1; //500ms
TIM_TimeBaseStructure.TIM_Prescaler = SystemCoreClock/10000-1;
TIM_TimeBaseStructure.TIM_ClockDivision = 0;
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;//向上计数
TIM_TimeBaseInit(TIM1, &TIM_TimeBaseStructure);//写入相关寄存器
TIM_ITConfig(TIM1, TIM_IT_Update, ENABLE);//允许TIM1更新中断
TIM_Cmd(TIM1, ENABLE);//使能定时器TIM1
}
void NVIC_Configuration(void)
{
NVIC_InitTypeDef NVIC_InitStructure;
NVIC_SetVectorTable(NVIC_VectTab_FLASH, 0x0);
NVIC_InitStructure.NVIC_IRQChannel = TIM1_UP_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
}
u8 TP1=0;
void TIM1_UP_IRQHandler(void)
{
if (TIM_GetITStatus(TIM1, TIM_IT_Update) != RESET)
{
TIM_ClearITPendingBit(TIM1, TIM_IT_Update);
TP1++;
if (TP1&1)
GPIO_ResetBits(GPIOD,GPIO_Pin_2);
else
GPIO_SetBits(GPIOD,GPIO_Pin_2);
}
}
int main(void)
{
SystemInit();
GPIO_Configuration();
TIM_Configuration();
NVIC_Configuration();
LCD_Init();
Welcome();
LED1(1);LED2(1);LED3(1);LED4(1);
while(1)
{
}
}
如果需要进行长定时如10秒钟,让LED1每10秒变换一次亮灭(周期20秒)
分析:如果按照前面的预分频值,
则ARR=10*N ms=10*10000=100000>65536,因此有三种方法解决长时间的定时
一种预分频弄大,使计数脉冲周期变长,即可延时定时时间;
二是用变量计中断次数
三是利用高级定时器特有的重新计数寄存器。假设我们让更新中断 定时1秒,则要定时10秒,只需要重复计数器值为10即可。
void TIM_Configuration(void)
{
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM1, ENABLE);//使能TIM1时钟
TIM_TimeBaseStructure.TIM_RepetitionCounter =10 ;//重复计数器为10
TIM_TimeBaseStructure.TIM_Period = 10*1000-1; //1000ms=1s
TIM_TimeBaseStructure.TIM_Prescaler = SystemCoreClock/10000-1;
TIM_TimeBaseStructure.TIM_ClockDivision = 0;
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;//向上计数
TIM_TimeBaseInit(TIM1, &TIM_TimeBaseStructure);//写入相关寄存器
TIM_ITConfig(TIM1, TIM_IT_Update, ENABLE);//允许TIM1更新中断
TIM_Cmd(TIM1, ENABLE);//使能定时器TIM1
}
TIM2比较中断应用-4通道定时
【例2】对于 STM32F10x利用TIM2四个比较通道中断定时,让LED1、LED2、LED3和LED4指示灯,分别定时1s,0.5s,200ms,100ms让四个指示灯改变显示状态。
void TIM_Configuration(void)
{ TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
TIM_OCInitTypeDef TIM_OCInitStructure;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
TIM_TimeBaseStructure.TIM_Period =65535;
TIM_TimeBaseStructure.TIM_Prescaler =(SystemCoreClock/10000)-1;
TIM_TimeBaseStructure.TIM_ClockDivision = 0;
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure);
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_Timing;
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Disable;
TIM_OCInitStructure.TIM_Pulse = CCR1_Val;
TIM_OC1Init(TIM2, &TIM_OCInitStructure);
TIM_OCInitStructure.TIM_Pulse = CCR2_Val;
TIM_OC2Init(TIM2, &TIM_OCInitStructure);
TIM_OCInitStructure.TIM_Pulse = CCR3_Val;
TIM_OC3Init(TIM2, &TIM_OCInitStructure);
TIM_OCInitStructure.TIM_Pulse = CCR4_Val;
TIM_OC4Init(TIM2, &TIM_OCInitStructure);
TIM_ITConfig(TIM2, TIM_IT_CC1 | TIM_IT_CC2 | TIM_IT_CC3 |TIM_IT_CC4, ENABLE);
TIM_Cmd(TIM2, ENABLE);
}
void NVIC_Configuration(void)
{
NVIC_InitTypeDef NVIC_InitStructure;
NVIC_SetVectorTable(NVIC_VectTab_FLASH, 0x0);
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
}
void TIM2_IRQHandler(void) {
uint16_t capture = 0;
if (TIM_GetITStatus(TIM2, TIM_IT_CC1) != RESET)
{
TIM_ClearITPendingBit(TIM2, TIM_IT_CC1);
GPIOD->ODR ^= 1 << 2;
capture = TIM_GetCapture1(TIM2);
TIM_SetCompare1(TIM2, capture + CCR1_Val);//写新比较值到比较寄存器
}
else if (TIM_GetITStatus(TIM2, TIM_IT_CC2) != RESET)
{
TIM_ClearITPendingBit(TIM2, TIM_IT_CC2);
GPIOD->ODR ^= 1 << 3;
capture = TIM_GetCapture2(TIM2);
TIM_SetCompare2(TIM2, capture + CCR2_Val);
}
else if (TIM_GetITStatus(TIM2, TIM_IT_CC3) != RESET)
{
TIM_ClearITPendingBit(TIM2, TIM_IT_CC3);
GPIOD->ODR ^= 1 << 4;
capture = TIM_GetCapture3(TIM2);
TIM_SetCompare3(TIM2, capture + CCR3_Val);
}
else if (TIM_GetITStatus(TIM2, TIM_IT_CC4) != RESET)
{
TIM_ClearITPendingBit(TIM2, TIM_IT_CC4);
GPIOD->ODR ^= 1 << 7;
capture = TIM_GetCapture4(TIM2);
TIM_SetCompare4(TIM2, capture + CCR4_Val);
}
}
看门狗定时器
在嵌入式应用中,微控制器必须可靠工作。但系统由于种种原因(包括环境干扰等),程序运行有时会不按指定指令序列执行,导致死机,系统无法继续工作下去,这时必须使系统复位才能使程序重新投入运行。这个能使系统定时复位的硬件称为看门狗定时器WDG或WDT,简称看门狗或WDG或WDT。
独立看门狗IWDG 溢出时间
T
I
W
D
G
=
4
×
2
I
W
D
G
_
P
R
×
(
1
+
I
W
D
G
_
R
L
R
)
/
40
K
H
z
T_{IWDG}=4times2^{IWDG_{PR}}times(1+IWDG_{RLR})/40KHz
TIWDG=4×2IWDG_PR×(1+IWDG_RLR)/40KHz
让独立看门狗IWDG的溢出时间为2秒,则
假设取IWDG_PR=4,则:
WDG_RLR=40KHz*TIWDG/4/24-1=40000*2/4/16-1=1250-1
因此,初始化程序为:
WDG_WriteAccessCmd(IWDG_WriteAccess_Enable);//使能IWDG写操作
IWDG_SetPrescaler(4); //设置IWDG预分频值:设置IWDG预分频值为4
IWDG_SetReload(1250); //设置IWDG重装载值
IWDG_ReloadCounter(); //重装载IWDG计数器,也叫喂狗
IWDG_Enable(); //使能IWDG,WDG溢出时间为2秒
主函数中的主循环体中要不断喂狗才不至于被复位,当系统死锁时WDG溢出时强行复位。
while(1)
{
: //事务处理
IWDG_ReloadCounter();//喂狗
}
实时钟定时器RTC
RTC(Real Time Clock)实时钟组件是一种能提供日历/时钟、数据存储等功能的专用定时组件,现代嵌入式微控制器片内大都集成了实时时钟(RTC)单元。
操作流程:
脉宽调制定时器PWM
脉冲宽度调制(Pulse Width Modulation, PWM)是对模拟信号电平进行数字编码的一种处理方法。通过高分辨率计数器的使用,方波的占空比被调制用来对一个具体模拟信号的电平进行编码。广泛应用于电子、机械、通信、功率控制等多个领域。
利用PWM可以控制脉冲的周期(频率)以及脉冲的宽度,达到有效控制输出的目的。
PWM组成 在STM32F10x定时器中,除TIM6,TIM7基本定时器外,其它定时器均具有PWM功能。
PWM输出有边沿对齐和中心对齐模式如图所示。
- 1、捕获/比较寄存器TIMx_CCR1/2/3/4
TIMx_CCR1TIMx_CCR4分别对于第TIMx的第1第4个通道的捕获/比较寄存器,它们为16位寄存器,决定各自PWM通道输出PWM波形的占空比。 - 2、重装载寄存器TIMx_ARR1/2/3/4
TIMx_ARR1~TIMx_ARR4为定时器TIMx第1到第4通道的16位重装寄存器。决定输出PWM波形的周期。 - 3、捕获/比较使能寄存器TIMx_CCER
TIMx_CCER控制各通道有开关,它是16位的寄存器,要让PWM从相应引脚输出,必须使相应位设置为1。 - 4、捕获/比较模式寄存器TIMx_CCMR1/2
TIMx_CCMR1和TIMx_CCMR2分别控制定时器TIMx的PWM通道1,2和PWM通道3,4的工作模式。
1、边沿对齐情况
(1)PWM输出频率
F
P
W
M
O
U
T
=
F
P
C
L
K
/
(
(
1
+
T
I
M
x
_
A
R
R
)
∗
(
1
+
T
I
M
x
_
P
S
C
)
)
F_{PWMOUT}=F_{PCLK}/((1+TIMx_{ARR})*(1+TIMx_{PSC}))
FPWMOUT=FPCLK/((1+TIMx_ARR)∗(1+TIMx_PSC))
(2)重装寄存器的值TIMx_ARR决定PWM输出周期/频率
T
I
M
x
_
A
R
R
=
F
P
C
L
K
/
F
P
W
M
O
U
T
/
(
1
+
T
I
M
x
_
P
S
C
)
−
1
TIMx_ARR =F_{PCLK}/ F_{PWMOUT}/(1+TIMx_PSC)-1
TIMx_ARR=FPCLK/FPWMOUT/(1+TIMx_PSC)−1
(3)捕获/比较寄存器TIMx_CCR决定占空比
D
u
t
y
R
a
t
i
o
=
T
I
M
_
C
C
R
i
/
(
T
I
M
x
_
A
R
R
i
+
1
)
∗
100
%
i
=
1
,
2
,
3
,
4
DutyRatio=TIM_CCRi/(TIMx_ARRi+1)*100% space spacespace i=1,2,3,4
DutyRatio=TIM_CCRi/(TIMx_ARRi+1)∗100% i=1,2,3,4
2、中心对齐方式
(1)PWM输出频率
F
P
W
M
O
U
T
=
F
P
C
L
K
/
(
(
T
I
M
x
_
A
R
R
)
∗
(
1
+
T
I
M
x
_
P
S
C
)
)
F_{PWMOUT}=F_{PCLK}/((TIMx_{ARR})*(1+TIMx_{PSC}))
FPWMOUT=FPCLK/((TIMx_ARR)∗(1+TIMx_PSC))
(2)重装寄存器的值TIMx_ARR决定PWM输出周期/频率
T
I
M
x
_
A
R
R
=
F
P
C
L
K
/
F
P
W
M
O
U
T
/
(
1
+
T
I
M
x
_
P
S
C
)
TIMx_ARR =F_{PCLK}/ F_{PWMOUT}/(1+TIMx_PSC)
TIMx_ARR=FPCLK/FPWMOUT/(1+TIMx_PSC)
(3)捕获/比较寄存器TIMx_CCR决定占空比
D
u
t
y
R
a
t
i
o
=
T
I
M
_
C
C
R
i
/
(
T
I
M
x
_
A
R
R
i
)
∗
100
%
i
=
1
,
2
,
3
,
4
DutyRatio=TIM_CCRi/(TIMx_ARRi)*100% space spacespace i=1,2,3,4
DutyRatio=TIM_CCRi/(TIMx_ARRi)∗100% i=1,2,3,4
T
p
T_p
Tp为PWM一个周期的高电平宽度,
T
n
T_n
Tn为PWM一个周期的低电平宽度,一个PWM周期
T
P
W
M
=
T
p
+
T
n
T_{PWM}=T_p+T_n
TPWM=Tp+Tn,占空比为
D
u
t
y
R
a
t
i
o
=
T
p
T
P
W
M
DutyRatio=frac{T_p}{T_{PWM}}
DutyRatio=TPWMTp
- 开启TIMx时钟,配置相关引脚为复用PWM输出
- 设置TIMx_CHi重映射到输出引脚上(见P.206表6.5 PWM引脚关系)
- 设置TIMx的ARR和PSC以确定PWM输出频率
- 通过TIMx_CCMR1/2配置TIMx_CHi的PWM模式
- 使能TIMx_CHi输出,使能TIMx
- 根据占空比要求设置TIMx_CCRi



