定时器本质
定时器的本质:计数器
定时器的构成:时钟源+计数器+重载值
1、stm32的定时器
stm32定时器数量较多、功能比较强;不同的定时器功能有不同。
stm32的定时器进行类型划分。分为三类:①基本定时器;②通用定时器;③高级定时器
基本定时器:用于定时器,还可以用于触发DAC(数模转换器)、ADC(模数转换器)工作。
通用定时器:具备基本定时器所有的功能;捕获输入、比较输出、捕获PWM波、生成PWM波、支持霍尔元件
高级定时器:具备通用定时器所有的功能:支持死区、刹车功能。
STM32F407的基本定时器有TIM6、TIM7.
STM32F407的通用定时器有TIM2~TIM5,TIM9~TIM14.
STM32F407的高级定时器有TIM1、TIM8.
基本定时器定时功能。
2、基本定时器定时功能介绍。
基本定时器 TIM6 和 TIM7 包含一个 16 位自动重载计数器,该计数器由可编程预分频器驱动。
此类定时器不仅可用作通用定时器以生成时基(可以作为通用定时器的时钟源), 还可以专门用于驱动数模转换器 (DAC)。实
际上,此类定时器内部连接到 DAC 并能够通过其触发输出驱动 DAC。
这些定时器彼此完全独立,不共享任何资源(每个定时器都可以单独工作,不需要其他定时器的资源)。
3、基本定时器的特征
基本定时器(TIM6 和 TIM7)的特性:
● 16 位自动重载递增计数器
● 16 位可编程预分频器,用于对计数器时钟频率进行分频(即运行时修改),分频系数介于 1 和 65536 之间
● 用于触发 DAC 的同步电路
● 发生如下更新事件时会生成中断/DMA 请求:计数器上溢(计数器上溢会产生更新事件,更新事件能够产生中断/DMA请求)。
例:
假设 168MHz 10000分频
本身在168MHz的情况下1秒钟,只需要记168M个数。
如果是10000分频,那么就要记:168MHz/10000 = 16800个数
公式套用:
计数时钟 = 时钟源 / PSC + 1
时钟周期 = 1/计数时钟
时钟周期 * arr = 得出多少时间才溢出,触发中断
4、基本定时器框架
工作原理在框图上有体现:
什么时候用自动重载值寄存器带缓冲功能? 什么时候用自动重载值寄存器不带缓冲功能?
不带缓冲功能:定时器只需要固定一个时间的定时,这个项目中不需要改变这个定时器的定时。
例如:这个定时器1s中点亮LED灯,1s中熄灭LED灯
带缓冲功能:定时一个时间后要换时间。 例如:这个定时器500ms中点亮LED灯,100ms中熄灭LED灯
UG位:可以让计数器清0,自动重载值写入到影子寄存器,清除分频器的值,不会改变分频比。
配置流程:
1、选择一个定时时长;确定预分频与重载值。
2、确定是否使用缓存功能(UG位操作)
3、使能定时器
4、等待时间到
计算预分频去与自动重载值如何设定?TIM6/7定时器5s钟。
5、基本定时器的相关寄存器
5.1 TIM6 和 TIM7 控制寄存器 1 (TIMx_CR1)
位 7 ARPE:自动重载预装载使能 (Auto-reload preload enable)
0: TIMx_ARR 寄存器不进行缓冲。
1: TIMx_ARR 寄存器进行缓冲。
位 3 OPM:单脉冲模式 (One-pulse mode)
0:计数器在发生更新事件时不会停止计数(循环功能)
1:计数器在发生下一更新事件时停止计数(将 CEN 位清零)(单次功能)。
位 2 URS:更新请求源 (Update request source)
此位由软件置 1 和清零,用以选择 UEV 事件源。
0:使能时(UEV使能),所有以下事件都会生成更新中断或 DMA 请求。此类事件包括:
— 计数器上溢/下溢
— 将 UG 位置 1
— 通过从模式控制器生成的更新事件
1:使能时(UEV使能),只有计数器上溢/下溢会生成更新中断或 DMA 请求。
位 1 UDIS:更新禁止 (Update disable)
此位由软件置 1 和清零,用以使能/禁止 UEV 事件生成。
0:使能 UEV。更新 (UEV) 事件可通过以下事件之一生成:
— 计数器上溢/下溢
— 将 UG 位置 1
— 通过从模式控制器生成的更新事件然后更新影子寄存器的值。
1:禁止 UEV。不会生成更新事件,各影子寄存器的值(ARR 和 PSC)保持不变。但如果将
UG 位置 1,或者从从模式控制器接收到硬件复位,则会重新初始化计数器和预分频器。
位 0 CEN:计数器使能 (Counter enable)
0:禁止计数器
1:使能计数器
注意: 只有事先通过软件将 CEN 位置 1,才可以使用门控模式。而触发模式可通过硬件自动将
CEN 位置 1。
在单脉冲模式下,当发生更新事件时会自动将 CEN 位清零。
5.2 TIM6 和 TIM7 控制寄存器 2 (TIMx_CR2)
这个寄存器与基本定时器功能无关,只有需要使用到TRGO才去关心该位。
5.3TIM6 和 TIM7 DMA/中断使能寄存器 (TIMx_DIER)
位 8 UDE:更新 DMA 请求使能 (Update DMA request enable)
0:禁止更新 DMA 请求。
1:使能更新 DMA 请求。
位 0 UIE:更新中断使能 (Update interrupt enable) (模块级使能中断位)
0:禁止更新中断。
1:使能更新中断。
5.4 TIM6 和 TIM7 状态寄存器 (TIMx_SR)
位 0 UIF:更新中断标志 (Update interrupt flag)
该位在发生更新事件时通过硬件置 1。但需要通过软件清零。
0:未发生更新。
1:更新中断挂起。该位在以下情况下更新寄存器时由硬件置 1:(定时时间到)
— 上溢或下溢并且当 TIMx_CR1 寄存器中 UDIS = 0 时。
— 当由于 TIMx_CR1 寄存器中 URS = 0 且 UDIS = 0 而通过软件使用 TIMx_EGR 寄存器中
的 UG 位重新初始化 CNT 时。
5.5 TIM6 和 TIM7 事件生成寄存器 (TIMx_EGR)
位 0 UG:更新生成 (Update generation)
该位可通过软件置 1,并由硬件自动清零。
0:不执行任何操作。
1:重新初始化定时器计数器并生成寄存器更新事件。请注意,预分频器计数器也将清零(但预分频比不受影响)。
5.6 TIM6 和 TIM7 计数器 (TIMx_CNT)
位 15:0 CNT[15:0]:计数器值 (Counter value)
5.7 TIM6 和 TIM7 预分频器 (TIMx_PSC)
位 15:0 PSC[15:0]:预分频器值 (Prescaler value)
计数器时钟频率 CK_CNT 等于 fCK_PSC / (PSC[15:0] + 1)。注意: 该位会自动加1,在设置的时候要减1。
例如:8400分频 只需写入8399即可。
PSC 包含在每次发生更新事件时要装载到实际预分频器寄存器的值。
5.8 TIM6 和 TIM7 自动重载寄存器 (TIMx_ARR)
位 15:0 ARR[15:0]:自动重载值 (Auto-reload value)
ARR 为要装载到实际自动重载寄存器的值。注意:自动重载值写入10000;真实写入的值只能是9999。原因看下图便知。
有关 ARR 更新和行为的详细信息,请参见第 17.3.1 节:第 484 页的时基单元。
当自动重载值为空时,计数器不工作。
配置流程:
案例:实现1s LED亮,1s LED灭
6、使用定时器6
1、初始化
开启时钟、清空计数器的值、设置预分频、设置重载值、设置无缓冲功能、采用循环模式、控制寄存器1、开启中断使能(模块级中断使能、核心级中断使能)、开启计数器使能。
2、编写中断服务函数
清标志位、灯亮与灯灭。
程序:
timer6.h
#ifndef __TIMER6_H__
#define __TIMER6_H__
#include
void timer6_init(u16 arr, u16 psc);
#endif
timer6.c
#include "timer6.h"
#include "led.h"
/*
函数功能:基本定时器初始化
返回值:无
形参:arr:重装载值 psc:预分频值
*/
void timer6_init(u16 arr, u16 psc)
{
//开启基本定时器6(TIM6)的时钟
RCC->APB1ENR |= 1<<4;
/*清空计数器的值*/
TIM6->CNT = 0;
TIM6->PSC = psc; //预分频值 (8400)
TIM6->ARR = arr; //重装载值 (1s钟定时)
TIM6->CR1 = 0; //将控制寄存器1清空
TIM6->CR1 |= 1<<2; //设置事件更新请求源
/*设置无缓冲功能*/
TIM6->CR1 &= ~(1<<7);
/*设置循环模式*/
TIM6->CR1 &= ~(1<<3);
/*允许更新中断使能*/
TIM6->DIER |= 1<<0;
/*使能基本定时器中断*/
NVIC_EnableIRQ(TIM6_DAC_IRQn);
/*开启计数器使能*/
TIM6->CR1 |= 1<<0;
}
/*
函数功能:基本定时器终端服务函数
返回值:无
形参:无
*/
void TIM6_DAC_IRQHandler(void)
{
if(TIM6->SR & (1<<0)) //溢出中断
{
TIM6->SR &= ~(1<<0); //清除中断标志位
//执行相应操作
LED2 = !LED2;
}
}
main.c
#include "led.h"
#include "tim6.h"
int main(void)
{
led_init();
timer6_init(5000-1, 8400-1); //重装载值:5000-1, 分频8400-1
}