当前位置:网站首页>学习记录:TIM—基本定时器
学习记录:TIM—基本定时器
2022-07-06 09:25:00 【Bitter tea seeds】
目录
前言
定时器分类
STM32F1系列中,除了互联型的产品,共有 8 个定时器,分为基本定时器,通用定时器和高级定时器。
基本定时器TIM6 和 TIM7 是一个 16 位的只能向上计数的定时器,只能定时,没有外部 IO。
通用定时器TIM2、3、4、5 是一个 16 位的可以向上/下计数的定时器,可以定时,可以输出比较,可以输入捕捉,每个定时器有四个外部 IO。
高级定时器TIM1、8 是一个 16 位的可以向上/下计数的定时器,可以定时,可以输出比较,可以输入捕捉,还可以有三相电机互补输出信号,每个定时器有 8 个外部 IO。
一、STM32 基本定时器
1.1、基本定时器功能框图讲解
基本定时器的核心是时基,不仅基本定时器有,通用定时器和高级定时器也有。
可编程通用定时器的主要部分是一个16位计数器和与其相关的自动装载寄存器。这个计数器可以向上计数、向下计数或者向上向下双向计数。此计数器时钟由预分频器分频得到。 计数器、自动装载寄存器和预分频器寄存器可以由软件读写,在计数器运行时仍可以读写。
时基单元包含:
● 计数器寄存器(TIMx_CNT)
● 预分频器寄存器 (TIMx_PSC)
● 自动装载寄存器 (TIMx_ARR)
1.2、时钟源
定时器时钟 TIMxCLK,即内部时钟 CK_INT,经 APB1 预分频器后分频提供,如果 APB1 预分频 系数等于 1,则频率不变,否则频率乘以 2,库函数中 APB1 预分频的系数是 2,即 PCLK1=36M, 所以定时器时钟 TIMxCLK=36*2=72M。
1.3、计数器时钟
定时器时钟经过 PSC 预分频器之后,即 CK_CNT,用来驱动计数器计数。PSC 是一个 16 位的预分频器,可以对定时器时钟 TIMxCLK 进行 1~65536 之间的任何一个数进行分频。具体计算方式 为:CK_CNT=TIMxCLK/(PSC+1)。
1.4、计数器
计数器 CNT 是一个 16 位的计数器,只能往上计数,最大计数值为 65535。当计数达到自动重装载寄存器的时候产生更新事件,并清零从头开始计数。
1.5、自动重装载寄存器
自动重装载寄存器 ARR 是一个 16 位的寄存器,这里面装着计数器能计数的最大数值。当计数到这个值的时候,如果使能了中断的话,定时器就产生溢出中断。
1.6、定时时间的计算
定时器的定时时间等于计数器的中断周期乘以中断的次数。计数器在 CK_CNT 的驱动下,计一 个数的时间则是 CK_CLK 的倒数,等于:1/(TIMxCLK/(PSC+1)),产生一次中断的时间则等于: 1/(CK_CLK * ARR)。如果在中断服务程序里面设置一个变量 time,用来记录中断的次数,那么就可以计算出我们需要的定时时间等于:1/CK_CLK* (ARR+1)*time。
二、STM32 通用定时器
2.1、通用定时器简介
STM32F1的通用定时器是一个通过可编程预分频器(PSC)驱动的 16 位自动装载计数器 (CNT)构成。
STM32 的通用定时器可以被用于:测量输入信号的脉冲长度(输入捕获)或者产生输出波形(输出比较和 PWM)等。 使用定时器预分频器和 RCC 时钟控制器预分频器,脉冲长度和波形周期可以在几个微秒到几个毫秒间调整。
STM32 的每个通用定时器都是完全独立的, 没有互相共享的任何资源。
2.2、TIMx主要功能
通用TIMx (TIM2、TIM3、TIM4和TIM5)定时器功能包括:
● 16位向上、向下、向上/向下自动装载计数器
自动重装载寄存器 ARR 是一个 16 位的寄存器,这里面装着计数器能计数的最大数值。当计数到 这个值的时候,如果使能了中断的话,定时器就产生溢出中断。
● 16位可编程(可以实时修改)预分频器,计数器时钟频率的分频系数为1~65536之间的任意 数值
● 4个独立通道:
─ 输入捕获
─ 输出比较
─ PWM生成(边缘或中间对齐模式)
─ 单脉冲模式输出
● 使用外部信号控制定时器和定时器互连的同步电路
● 如下事件发生时产生中断/DMA:
─ 更新:计数器向上溢出/向下溢出,计数器初始化(通过软件或者内部/外部触发)
─ 触发事件(计数器启动、停止、初始化或者由内部/外部触发计数)
─ 输入捕获
─ 输出比较
● 支持针对定位的增量(正交)编码器和霍尔传感器电路
● 触发输入作为外部时钟或者按周期的电流管理
2.3、STM32 的通用寄存器
控制寄存器 1(TIMx_CR1)
TIMx_CR1 的最低位,也就是计数器使能位,该位必须置 1,才能让定时器开始计数。
从第 4 位 DIR 可以看出默认的计数方式是向上计数,同时也可以向下计数。
第 5,6 位是设置计数对齐方式的。
第 8 和第 9 位可以看出,可以设置定时器的时钟分频因子为 1,2,4。
DMA/中断使能寄存器 (TIMx_DIER),该寄存器是一个 16 位的寄存器。
它的第 0 位,该位是更新中断允许位,用到的是定时器的更新中断,所以该位要设置为 1,来允许由于更新事件所产生的中断。
预分频寄存器(TIMx_PSC),该寄存器用设置对时钟进行分频,然后提供给计数器,作为计数器的时钟。
通过 TIMx_SMCR 寄存器的相关位来设置时钟:1.内部时钟(CK_INT)2.外部时钟模式 1:外部输入脚(TIx)3.外部时钟模式 2:外部触发输入(ETR)4.内部触发输入(ITRx):使用 A 定时器作为 B 定时器的预分频器(A 为 B 提供时钟)。
这里的 CK_INT 时钟是从 APB1 倍频的来的,除非 APB1 的时钟分频数设置为 1,否则通用定时器 TIMx 的时钟是 APB1 时钟的 2 倍,当 APB1 的时钟不分频的时候,通用定时器 TIMx 的时钟就等于 APB1 的时钟。这里还要注意的就是高级定时器的时钟不是来自 APB1,而是来自 APB2 的。
TIMx_CNT 寄存器,该寄存器是定时器的计数器,该寄存器存储了当前定时器的计数值。
自动重装载寄存器(TIMx_ARR)在物理上实际对应着 2 个寄存器。 一个是程序员可以直接操作的,另外一个是影子寄存器。
真正起作用的是影子寄存器。根据 TIMx_CR1 寄存器中 APRE 位的设置:APRE=0 时,预装载寄存器的内容可以随时传送到影子寄存器,此时 2 者是连通的;而 APRE=1 时,在每一次更新事件(UEV)时,才把预装在寄存器的内容传送到影子寄存器。
状态寄存器(TIMx_SR)用来标记当前与定时器相关的各种事件/中断是否发生。
三、实现程序的步骤
使用定时器产生中断,然后在中断服务函数里面翻转 DS1 上的电平,来指示定时器中断的产生。
3.1、基本定时器实现程序步骤
(1) TIM_Prescaler:定时器预分频器设置,时钟源经该预分频器才是定时器时钟,它设定 TIMx_PSC 寄存器的值。可设置范围为 0 至 65535,实现 1 至 65536 分频。
(2) TIM_CounterMode:定时器计数方式,可是在为向上计数、向下计数以及三种中心对齐模式。 基本定时器只能是向上计数,即 TIMx_CNT 只能从 0 开始递增,并且无需初始化。
(3) TIM_Period:定时器周期,实际就是设定自动重载寄存器的值,在事件生成时更新到影子寄 存器。可设置范围为 0 至 65535。
(4) TIM_ClockDivision:时钟分频,设置定时器时钟 CK_INT 频率与数字滤波器采样时钟频率分 频比,基本定时器没有此功能,不用设置。
(5) TIM_RepetitionCounter:重复计数器,属于高级控制寄存器专用寄存器位,利用它可以非常容易控制输出 PWM 的个数。这里不用设置。
LED.h代码如下所示:
#ifndef __LED_H
#define __LED_H
#include "stm32f10x.h"
/* 定义LED连接的GPIO端口, 用户只需要修改下面的代码即可改变控制的LED引脚 */
// R-红色
#define LED1_GPIO_PORT GPIOB /* GPIO端口 */
#define LED1_GPIO_CLK RCC_APB2Periph_GPIOB /* GPIO端口时钟 */
#define LED1_GPIO_PIN GPIO_Pin_5 /* 连接到SCL时钟线的GPIO */
#define ON 0
#define OFF 1
/* 使用标准的固件库控制IO*/
#define LED1(a) if (a) \
GPIO_SetBits(LED1_GPIO_PORT,LED1_GPIO_PIN);\
else \
GPIO_ResetBits(LED1_GPIO_PORT,LED1_GPIO_PIN)
/* 直接操作寄存器的方法控制IO */
#define digitalHi(p,i) {p->BSRR=i;} //输出为高电平
#define digitalLo(p,i) {p->BRR=i;} //输出低电平
#define digitalToggle(p,i) {p->ODR ^=i;} //输出反转状态
/* 定义控制IO的宏 */
#define LED1_TOGGLE digitalToggle(LED1_GPIO_PORT,LED1_GPIO_PIN)
#define LED1_OFF digitalHi(LED1_GPIO_PORT,LED1_GPIO_PIN)
#define LED1_ON digitalLo(LED1_GPIO_PORT,LED1_GPIO_PIN)
void LED_GPIO_Config(void);
#endif /* __LED_H */
LED.c程序如下所示:
#include "bsp_led.h"
void LED_GPIO_Config(void)
{
/*定义一个GPIO_InitTypeDef类型的结构体*/
GPIO_InitTypeDef GPIO_InitStructure;
/*开启LED相关的GPIO外设时钟*/
RCC_APB2PeriphClockCmd( LED1_GPIO_CLK , ENABLE);
/*选择要控制的GPIO引脚*/
GPIO_InitStructure.GPIO_Pin = LED1_GPIO_PIN;
/*设置引脚模式为通用推挽输出*/
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
/*设置引脚速率为50MHz */
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
/*调用库函数,初始化GPIO*/
GPIO_Init(LED1_GPIO_PORT, &GPIO_InitStructure);
/* 关闭所有led灯 */
GPIO_SetBits(LED1_GPIO_PORT, LED1_GPIO_PIN);
}
TIMEBASE.h程序如下所示:
#ifndef __BSP_TIMEBASE_H
#define __BSP_TIMEBASE_H
#include "stm32f10x.h"
#define BASIC_TIM6 // 如果使用TIM7,注释掉这个宏即可
#ifdef BASIC_TIM6 // 使用基本定时器TIM6
#define BASIC_TIM TIM6
#define BASIC_TIM_APBxClock_FUN RCC_APB1PeriphClockCmd
#define BASIC_TIM_CLK RCC_APB1Periph_TIM6
#define BASIC_TIM_Period (1000-1)
#define BASIC_TIM_Prescaler 71
#define BASIC_TIM_IRQ TIM6_IRQn
#define BASIC_TIM_IRQHandler TIM6_IRQHandler
#else // 使用基本定时器TIM7
#define BASIC_TIM TIM7
#define BASIC_TIM_APBxClock_FUN RCC_APB1PeriphClockCmd
#define BASIC_TIM_CLK RCC_APB1Periph_TIM7
#define BASIC_TIM_Period 1000-1
#define BASIC_TIM_Prescaler 71
#define BASIC_TIM_IRQ TIM7_IRQn
#define BASIC_TIM_IRQHandler TIM7_IRQHandler
#endif
void BASIC_TIM_Init(void);
#endif /* __BSP_TIMEBASE_H */
TIMEBASE.c程序如下所示:
// 基本定时器TIMx,x[6,7]定时初始化函数
#include "bsp_TiMbase.h"
// 中断优先级配置
static void BASIC_TIM_NVIC_Config(void)
{
NVIC_InitTypeDef NVIC_InitStructure;
// 设置中断组为0
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_0);
// 设置中断来源
NVIC_InitStructure.NVIC_IRQChannel = BASIC_TIM_IRQ ;
// 设置主优先级为 0
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
// 设置抢占优先级为3
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
}
/*
* 注意:TIM_TimeBaseInitTypeDef结构体里面有5个成员,TIM6和TIM7的寄存器里面只有
* TIM_Prescaler和TIM_Period,所以使用TIM6和TIM7的时候只需初始化这两个成员即可,
* 另外三个成员是通用定时器和高级定时器才有.
*-----------------------------------------------------------------------------
*typedef struct
*{ TIM_Prescaler 都有
* TIM_CounterMode TIMx,x[6,7]没有,其他都有
* TIM_Period 都有
* TIM_ClockDivision TIMx,x[6,7]没有,其他都有
* TIM_RepetitionCounter TIMx,x[1,8,15,16,17]才有
*}TIM_TimeBaseInitTypeDef;
*-----------------------------------------------------------------------------
*/
static void BASIC_TIM_Mode_Config(void)
{
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
// 开启定时器时钟,即内部时钟CK_INT=72M
BASIC_TIM_APBxClock_FUN(BASIC_TIM_CLK, ENABLE);
// 自动重装载寄存器的值,累计TIM_Period+1个频率后产生一个更新或者中断
TIM_TimeBaseStructure.TIM_Period = BASIC_TIM_Period;
// 时钟预分频数为
TIM_TimeBaseStructure.TIM_Prescaler= BASIC_TIM_Prescaler;
// 时钟分频因子 ,基本定时器没有,不用管
//TIM_TimeBaseStructure.TIM_ClockDivision=TIM_CKD_DIV1;
// 计数器计数模式,基本定时器只能向上计数,没有计数模式的设置
//TIM_TimeBaseStructure.TIM_CounterMode=TIM_CounterMode_Up;
// 重复计数器的值,基本定时器没有,不用管
//TIM_TimeBaseStructure.TIM_RepetitionCounter=0;
// 初始化定时器
TIM_TimeBaseInit(BASIC_TIM, &TIM_TimeBaseStructure);
// 清除计数器中断标志位
TIM_ClearFlag(BASIC_TIM, TIM_FLAG_Update);
// 开启计数器中断
TIM_ITConfig(BASIC_TIM,TIM_IT_Update,ENABLE);
// 使能计数器
TIM_Cmd(BASIC_TIM, ENABLE);
}
void BASIC_TIM_Init(void)
{
BASIC_TIM_NVIC_Config();
BASIC_TIM_Mode_Config();
}
main.c程序如下所示:
// 基本定时器TIMx,x[6,7]定时应用
#include "stm32f10x.h"
#include "bsp_led.h"
#include "bsp_TiMbase.h"
volatile uint32_t time = 0; // ms 计时变量
int main(void)
{
/* led 端口配置 */
LED_GPIO_Config();
BASIC_TIM_Init();
while(1)
{
if ( time == 1000 ) /* 1000 * 1 ms = 1s 时间到 */
{
time = 0;
/* LED1 取反 */
LED1_TOGGLE;
}
}
}
3.2、基本定时器实验现象
正点原子精英开发板LED0红灯每隔一秒闪烁一次;
3.3、通用定时器实现程序步骤
time.h程序如下所示:
#ifndef __TIMER_H
#define __TIMER_H
#include "sys.h"
void TIM3_Int_Init(u16 arr,u16 psc);
#endif
time.c程序如下所示:
#include "timer.h"
#include "led.h"
//通用定时器3中断初始化
//这里时钟选择为APB1的2倍,而APB1为36M
//arr:自动重装值。
//psc:时钟预分频数
//这里使用的是定时器3!
void TIM3_Int_Init(u16 arr,u16 psc)
{
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
NVIC_InitTypeDef NVIC_InitStructure;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE); //时钟使能
//定时器TIM3初始化
TIM_TimeBaseStructure.TIM_Period = arr; //设置在下一个更新事件装入活动的自动重装载寄存器周期的值
TIM_TimeBaseStructure.TIM_Prescaler =psc; //设置用来作为TIMx时钟频率除数的预分频值
TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; //设置时钟分割:TDTS = Tck_tim
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; //TIM向上计数模式
TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure); //根据指定的参数初始化TIMx的时间基数单位
TIM_ITConfig(TIM3,TIM_IT_Update,ENABLE ); //使能指定的TIM3中断,允许更新中断
//中断优先级NVIC设置
NVIC_InitStructure.NVIC_IRQChannel = TIM3_IRQn; //TIM3中断
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0; //先占优先级0级
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3; //从优先级3级
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道被使能
NVIC_Init(&NVIC_InitStructure); //初始化NVIC寄存器
TIM_Cmd(TIM3, ENABLE); //使能TIMx
}
//定时器3中断服务程序
void TIM3_IRQHandler(void) //TIM3中断
{
if (TIM_GetITStatus(TIM3, TIM_IT_Update) != RESET) //检查TIM3更新中断发生与否
{
TIM_ClearITPendingBit(TIM3, TIM_IT_Update ); //清除TIMx更新中断标志
LED1=!LED1;
}
}
main.c程序如下所示
#include "led.h"
#include "delay.h"
#include "key.h"
#include "sys.h"
#include "usart.h"
#include "timer.h"
int main(void)
{
delay_init(); //延时函数初始化
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //设置NVIC中断分组2:2位抢占优先级,2位响应优先级
uart_init(115200); //串口初始化为115200
LED_Init(); //LED端口初始化
TIM3_Int_Init(4999,7199);//10Khz的计数频率,计数到5000为500ms
while(1)
{
LED0=!LED0;
delay_ms(200);
}
}
3.4、通用定时器实验现象
正点原子精英开发板LED0红灯闪烁的比LED1绿灯快;
边栏推荐
- Want to change jobs? Do you know the seven skills you need to master in the interview software test
- How to build a nail robot that can automatically reply
- Rearrange spaces between words in leetcode simple questions
- UCORE LaB6 scheduler experiment report
- CSAPP Shell Lab 实验报告
- MySQL development - advanced query - take a good look at how it suits you
- Flex --- detailed explanation of flex layout attributes
- Crawling cat's eye movie review, data visualization analysis source code operation instructions
- JS --- detailed explanation of JS facing objects (VI)
- LeetCode#118. Yanghui triangle
猜你喜欢
Word macro operation: convert the automatic number in the document into editable text type
Sorting odd and even subscripts respectively for leetcode simple problem
ucore lab5
CSAPP homework answers chapter 789
In Oracle, start with connect by prior recursive query is used to query multi-level subordinate employees.
Maximum nesting depth of parentheses in leetcode simple questions
CSAPP shell lab experiment report
UCORE lab8 file system experiment report
MySQL development - advanced query - take a good look at how it suits you
ucore lab 2
随机推荐
MySQL transactions
pytest
Your wechat nickname may be betraying you
UCORE lab2 physical memory management experiment report
Servlet
Leetcode notes - dynamic planning -day7
Introduction to safety testing
Do you know the performance testing terms to be asked in the software testing interview?
LeetCode#268. Missing numbers
Practical cases, hand-in-hand teaching you to build e-commerce user portraits | with code
学习记录:TIM—电容按键检测
Portapack application development tutorial (XVII) nRF24L01 launch B
How to rename multiple folders and add unified new content to folder names
Should wildcard import be avoided- Should wildcard import be avoided?
遇到程序员不修改bug时怎么办?我教你
Sleep quality today 81 points
LeetCode#237. Delete nodes in the linked list
How to build a nail robot that can automatically reply
LeetCode#53. Maximum subarray sum
Nest and merge new videos, and preset new video titles