嵌入式——定时器(TIM)
作用:恢复缺省配置参数一:TIMx,选择某个定时器参数二:结构体,里面包含配置时基单元的一些参数作用:时基单元初始化参数1:TIMx参数2:结构体作用:配置输出比较模块1参数1:TIMx参数2:结构体作用:配置输出比较模块2参数1:TIMx参数2:结构体作用:配置输出比较模块3参数1:TIMx参数2:结构体作用:配置输出比较模块4作用:输入捕获的初始化,输入捕获的四个通道配置在一个函数中,在结构体
一、定时器(TIM)简介
1.定时器(TIM)基本概念
(1)定时器也叫计数器,可以对输入的时钟进行计数,并在计数值达到设定值时触发中断(定时触发中断)。
(2)STM32有16位计数器、预分频器、自动重装寄存器的时基单元,在72MHz计数时钟下可以实现最大59.65s的定时。
16位计数器:执行计数定时的一个寄存器,每来一个时钟,计数器加1
预分频器:可以对计数器的时钟进行分频,让这个计数更灵活
实际分频系数=预分频器的值+1
自动重装寄存器:计数的目标值,就是我想要计多少个时钟申请中断
计数器、预分频器、自动重装寄存器是定时器最核心的部分,我们把这一块电路称为时基单元。(时基单元中的计数器、预分频器、自动重装寄存器都是16位的)。
(3)定时器不仅具备基本的定时中断功能,而且还包含内外时钟源选择、输入捕获、输出比较、编码器接口、主从触发模式等多种功能。
(4)不仅具备基本的定时中断功能,而且还包含内外时钟源选择、输入捕获、输出比较、编码器接口、主从触发模式等多种功能。
(5)定时器的编码器接口,可以读取正交编码器的输出波形。
2.定时器类型

图中可以看出各种类型的定时器所具有的定时资源,以及对应的总线,并还有相应功能介绍 。我所用的STM32F103C8T6定时器资源:TIM1、TIM2、TIM3、TIM4。
3.定时器的基本结构
(1)基本定时器

折线向上箭头UI:代表会产生一个中断信号,CNT计数值等于自动重装值产生的中断称为“更新中断”,更新中断后通往NVIC,配置好NVIC的定时器通道,那么定时器的更新中断就能得到CPU的响应。
折线向下箭头U:代表会产生一个事件,这里对应的事件叫做“更新事件”,更新事件不会触发中断,但会触发内部其他电路工作。
总流程:从基准时钟(内部时钟),到PSC预分频器,再到计数器,计数器计数自增,同时不断地与自动重装寄存器进行比较,它俩值相等时,即计时时间到,这时会产生一个更新中断和更新事件,CPU响应更新中断,就完成了我们定时中断的任务。
主模式触发DAC:将更新事件通过主模式映射到TRGO(触发输出),然后TRGO就会直接去触发DAC。
(2)通用定时器

我们可以将通用定时器结构图分为上、中、下三部分,中间就是最基本的时基单元,上部分就是内外时钟源选择和主从触发模式的结构,对于基本定时器而言,定时只能选择内部时钟,也就是系统频率的72MHz,但是通用定时器,时钟源不仅可以选择内部的72MHz时钟,还可以选择外部时钟。第一个外设时钟就是来自TIMx_ETR引脚的外部时钟,ETR引脚的位置可以参考引脚定义表,那么我们可以在TIM2的ETR引脚,也就是PA0上接一个外部方波时钟,然后配置一下内部的极性选择、边沿检测和预分频器电路,再配置一下输入滤波电路,这些电路可以对外部时钟进行一定的整形,对输入的波形进行一定的滤波,接下来,滤波后的信号兵分两路,上面一路ETRF进入触发控制器,紧跟着就可以选择作为时基单元的时钟了(“外部时钟模式2“)。下面一路TRGI(触发输入)作为外部时钟的输入,此时这一路称为“外部时钟模式1”,通过这一路的外部时钟有①ETR引脚的信号②ITR信号,这一部分的时钟信号来自其它定时器,不同定时器之间通过TGRO触发输出,输出通道被称为ITR,这一部分就实现了定时器级联的功能,如图4所示③还可以选择TI1F_ED,这里连接的是输入捕获单元的CH1引脚,也就是从CH1引脚获取时钟,_ED指沿边沿④这个时钟还能通过TI1FP1和TI2FP2获得,其中TI1FP1连接到了CH1引脚的时钟,TI2FP2连接到了CH2引脚的时钟。
总结:外部时钟模式1的输入有①ETR引脚②其他定时器③CH1引脚的边沿④CH1引脚⑤CH2引脚

图四 定时器级联
下部分主要包含两块电路,右边是输出比较电路,总共有四个通道,分别对于CH1到CH4的引脚,可以用于输出PWM波形,驱动电机,左边是输入捕获电路,也是有四个通道,也分别对于CH1到CH4的引脚,可以用于测量输入方波的频率等。中间的寄存器为捕获比较寄存器,是输入捕获和输出比较电路共用的。
(3)高级定时器

在申请中断的地方加入了一个重复次数计数器,有了这个计数器就可以实现每隔几个计数周期才发生一次更新事件和更新中断。下面所增加的就是高级定时器对输出比较模块的升级,DTG是死区生成电路,右边的输出引脚由原来的一个变为了两个互补的输出,可以输出一对互补的PWM波,这些电路是为了驱动三相无刷电机。最后一部分是刹车输入的功能,是为了给电机驱动提供安全保障。
4.定时中断基本结构图

接下来我们按照定时中断的基本结构图来完成两个具体的实例介绍,在介绍实例之前,我们参考二,学习一下定时器的基本库函数。
5.代码1——定时器定时中断(内部时钟)
①RCC开启时钟
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE); // RCC开启时钟
②选择时基单元的时钟源(也就是图6的黄色部分)
TIM_InternalClockConfig(TIM2); // 选择内部时钟模式
③配置时基单元
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure; // 配置时基单元
TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV2;
这里是指定时钟分频参数,可以选三个分别为
TIM_CKD_DIV1——1分频(不分频)
TIM_CKD_DIV2——2分频
TIM_CKD_DIV4——4分频
TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;
这里是指计数器模式,可以选择四个
TIM_CounterMode_Up——向上计数
TIM_CounterMode_Down——向下计数
TIM_CounterMode_CenterAligned1——中央对齐模式
TIM_CounterMode_CenterAligned2——中央对齐模式
IM_CounterMode_CenterAligned3——中央对齐模式
TIM_TimeBaseInitStructure.TIM_Period = 10000; // ARR自动重装器的值
TIM_TimeBaseInitStructure.TIM_Prescaler = 7200; //PSC预分频器的值
总结:计数时间长短由这两个参数决定
公式:CK_CMT_OV = CK_PSC / (PSC+1) / (ARR+1)
定时频率=72M/(Prescaler +1)/(Period +1)(注意单位换算1MHz=1000000Hz)
频率取倒数就是秒
预分频高,自动重装少:以一个比较低的频率计比较少的数
预分频少,自动重装高:以一个比较高的频率计比较多的数
TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;
重复计数器的值——高级计数器才有
TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStructure);
④配置输出中断控制,允许更新中断输出到NVIC
TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE); // 中断控制输出配置
⑤配置NVIC,在NVIC中打开定时器中断的通道,并分配一个优先级
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); // 中断分组
NVIC_InitTypeDef NVIC_Initstructure; // 配置NVIC
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);
⑥运行控制
TIM_Cmd(TIM2, ENABLE); //配置运行控制
整个模块配置成功后,需要使能计数器,不然计数器不会运行。
当定时器使能后,计数器就开始计数,当计数器更新时,触发中断,最后写一个定时器的中断函数,这样中断函数每隔一段时间就能自动执行一次。
我们配置一个Timer.c模块进行定时器配置,然后在主函数中调用。当定时到指定时间时Num+1。
Timer.c
#include "stm32f10x.h" // Device header
void Timer_Init(void)
{
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE); // RCC开启时钟
TIM_InternalClockConfig(TIM2); // 选择内部时钟模式
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure; // 配置时基单元
TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;
TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInitStructure.TIM_Period = 10000 - 1;
TIM_TimeBaseInitStructure.TIM_Prescaler = 7200 - 1;
TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;
TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStructure);
TIM_ClearFlag(TIM2, TIM_FLAG_Update);
TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE); // 中断控制输出配置
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); // 中断分组
NVIC_InitTypeDef NVIC_Initstructure; // 配置NVIC
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);
TIM_Cmd(TIM2, ENABLE); //配置运行控制
}
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), 6);
}
}
void TIM2_IRQHandler(void)
{
if (TIM_GetITStatus(TIM2, TIM_IT_Update) == SET)
{
Num++;
TIM_ClearITPendingBit(TIM2, TIM_IT_Update);
}
}
6.代码2——定时器定时中断(定时器外部时钟)
定时器初始化步骤:
①RCC开启时钟
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE); // RCC开启时钟
②配置GPIO
GPIO_InitTypeDef GPIO_InitStructure; // 配置GPIO
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
③选择时基单元的时钟源(也就是图6的黄色部分)
TIM_ETRClockMode2Config(TIM2,TIM_ExtTRGPSC_OFF,TIM_ExtTRGPolarity_Inverted, 0x00); // 选择外部时钟模式
第一个参数:TIMx
第二个参数:TIM_ExtTRGPSC_OFF(不分频)、TIM_ExtTRGPSC_DIV2、 TIM_ExtTRGPSC_DIV4、 TIM_ExtTRGPSC_DIV8
第三个参数:TIM_ExtTRGPolarity_Inverted(反向,低电平或下降沿有效)
TIM_ExtTRGPolarity_NonInverted(不反向,高电平或上升沿有效)
第四个参数:滤波器,以一个采样频率f采用N个点,如果N个点都一样,才会有效输出,这个参数就是决定f和N,具体对应关系见手册
④配置时基单元
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure; // 配置时基单元
TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV2;
TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInitStructure.TIM_Period = 10-1; // Num何时加1
TIM_TimeBaseInitStructure.TIM_Prescaler = 1-1; // CNT何时加1
TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0x00;
TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStructure);
⑤配置输出中断控制,允许更新中断输出到NVIC
TIM_ITConfig(TIM2, TIM_IT_CC2, ENABLE); // 中断控制输出配置
⑥配置NVIC,在NVIC中打开定时器中断的通道,并分配一个优先级
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); // 中断分组
NVIC_InitTypeDef NVIC_Initstructure; // 配置NVIC
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);
⑦运行控制
TIM_Cmd(TIM2, ENABLE); //配置运行控制
这里使用的是对外式红外传感器,当红外光被遮挡时CNT+1,但CNT与ARR的值相同时Num+1。
Timer.c
#include "stm32f10x.h" // Device header
void Timer_Init(void)
{
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE); // RCC开启时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
GPIO_InitTypeDef GPIO_InitStructure; // 配置GPIO
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
TIM_ETRClockMode2Config(TIM2, TIM_ExtTRGPSC_OFF,TIM_ExtTRGPolarity_NonInverted, 0x00); // 选择外部时钟模式
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure; // 配置时基单元
TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;
TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInitStructure.TIM_Period = 10 - 1; // Num何时加1
TIM_TimeBaseInitStructure.TIM_Prescaler = 1 - 1; // CNT何时加1
TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;
TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStructure);
TIM_ClearFlag(TIM2, TIM_FLAG_Update);
TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE); // 中断控制输出配置
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); // 中断分组
NVIC_InitTypeDef NVIC_Initstructure; // 配置NVIC
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);
TIM_Cmd(TIM2, ENABLE); //配置运行控制
}
uint16_t Timer_GetCOunter(void) // 实时观看CNT计数器的值
{
return TIM_GetCounter(TIM2);
}
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:");
OLED_ShowString(2, 1, "CNT:");
while (1)
{
OLED_ShowNum(1, 5, Num, 5);
OLED_ShowNum(2, 5, Timer_GetCOunter(), 5);
}
}
void TIM2_IRQHandler(void)
{
if (TIM_GetITStatus(TIM2, TIM_IT_Update) == SET)
{
Num++;
TIM_ClearITPendingBit(TIM2, TIM_IT_Update);
}
}
二、TIM库函数介绍
void TIM_DeInit(TIM_TypeDef* TIMx);
作用:恢复缺省配置
void TIM_TimeBaseInit(TIM_TypeDef* TIMx,TIM_TimeBaseInitTypeDef*TIM_TimeBaseInitStruct);
参数一:TIMx,选择某个定时器
参数二:结构体,里面包含配置时基单元的一些参数
作用:时基单元初始化
void TIM_OC1Init(TIM_TypeDef* TIMx, TIM_OCInitTypeDef* TIM_OCInitStruct);
参数1:TIMx
参数2:结构体
作用:配置输出比较模块1
void TIM_OC2Init(TIM_TypeDef* TIMx, TIM_OCInitTypeDef* TIM_OCInitStruct);
参数1:TIMx
参数2:结构体
作用:配置输出比较模块2
void TIM_OC3Init(TIM_TypeDef* TIMx, TIM_OCInitTypeDef* TIM_OCInitStruct);
参数1:TIMx
参数2:结构体
作用:配置输出比较模块3
void TIM_OC4Init(TIM_TypeDef* TIMx, TIM_OCInitTypeDef* TIM_OCInitStruct);
参数1:TIMx
参数2:结构体
作用:配置输出比较模块4
void TIM_ICInit(TIM_TypeDef* TIMx, TIM_ICInitTypeDef* TIM_ICInitStruct);
作用:输入捕获的初始化,输入捕获的四个通道配置在一个函数中,在结构体里面会有额外的一个参数,选择具体配置哪个通道
void TIM_PWMIConfig(TIM_TypeDef* TIMx, TIM_ICInitTypeDef* TIM_ICInitStruct);
作用:初始化输入捕获单元,快速配置两个通道
void TIM_ICStructInit(TIM_ICInitTypeDef* TIM_ICInitStruct);
作用:可以给输入捕获结构体赋一个初始值
void TIM_SelectInputTrigger(TIM_TypeDef* TIMx, uint16_t TIM_InputTriggerSource);
作用:选择输入触发源TRGI,从模式的触发源选择
void TIM_SelectOutputTrigger(TIM_TypeDef* TIMx, uint16_t TIM_TRGOSource);
作用:选择输出触发源TRGO,主模式输出的触发源
void TIM_SelectSlaveMode(TIM_TypeDef* TIMx, uint16_t TIM_SlaveMode);
作用:选择从模式
void TIM_SetIC1Prescaler(TIM_TypeDef* TIMx, uint16_t TIM_ICPSC);
作用:单独配置通道1的分频器
void TIM_SetIC2Prescaler(TIM_TypeDef* TIMx, uint16_t TIM_ICPSC);
作用:单独配置通道2的分频器
void TIM_SetIC3Prescaler(TIM_TypeDef* TIMx, uint16_t TIM_ICPSC);
作用:单独配置通道3的分频器
void TIM_SetIC4Prescaler(TIM_TypeDef* TIMx, uint16_t TIM_ICPSC);
作用:单独配置通道4的分频器
uint16_t TIM_GetCapture1(TIM_TypeDef* TIMx);
作用:读取通道1的CCR值
uint16_t TIM_GetCapture2(TIM_TypeDef* TIMx);
作用:读取通道2的CCR值
uint16_t TIM_GetCapture3(TIM_TypeDef* TIMx);
作用:读取通道3的CCR值
uint16_t TIM_GetCapture4(TIM_TypeDef* TIMx);
作用:读取通道4的CCR值
void TIM_TimeBaseStructInit(TIM_TimeBaseInitTypeDef* TIM_TimeBaseInitStruct);
作用:把结构体变量赋一个默认值
void TIM_OCStructInit(TIM_OCInitTypeDef* TIM_OCInitStruct);
作用:给输出比较结构体赋一个默认值
void TIM_Cmd(TIM_TypeDef* TIMx, FunctionalState NewState);
参数一:TIMx,选择某个定时器
参数二:ENABLE或DISENABLE
作用:使能计数器(对应图6的运行控制)
void TIM_ITConfig(TIM_TypeDef* TIMx, uint16_t TIM_IT, FunctionalState NewState);
参数一:TIMx,选择某个定时器
参数二:选择要配置哪个中断输出
TIM_IT_Update(更新中断)、TIM_IT_CC1、TIM_IT_CC2、TIM_IT_CC3、TIM_IT_CC4、TIM_IT_COM、TIM_IT_Trigger、TIM_IT_Break
参数三:ENABLE或DISENABLE
作用:图六的中断控制输出
void TIM_InternalClockConfig(TIM_TypeDef* TIMx);
参数一:TIMx,选择某个定时器
作用:选择内部时钟
void TIM_ITRxExternalClockConfig(TIM_TypeDef* TIMx, uint16_t TIM_InputTriggerSource);
参数一:TIMx,选择某个定时器
参数二:选择要接入哪个其他的定时器
作用:选择ITRx其他定时器的时钟
void TIM_TIxExternalClockConfig(TIM_TypeDef* TIMx, uint16_t TIM_TIxExternalCLKSource,
uint16_t TIM_ICPolarity, uint16_t ICFilter);
参数一:TIMx,选择某个定时器
参数二:选择TIx具体的某个引脚
参数三:输入的极性
参数四:输入的滤波器
作用:选择TIx捕获通道的时钟
void TIM_ETRClockMode1Config(TIM_TypeDef* TIMx, uint16_t TIM_ExtTRGPrescaler, uint16_t TIM_ExtTRGPolarity, uint16_t ExtTRGFilter);
参数一:TIMx,选择某个定时器
参数二:外部触发预分频器
参数三:输入的极性
参数四:输入的滤波器
作用:选择ETR通过外部时钟模式1输入的时钟
void TIM_ETRClockMode2Config(TIM_TypeDef* TIMx, uint16_t TIM_ExtTRGPrescaler,
uint16_t TIM_ExtTRGPolarity, uint16_t ExtTRGFilter);
参数一:TIMx,选择某个定时器
参数二:外部触发预分频器
参数三:输入的极性
参数四:输入的滤波器
作用:选择ETR通过外部时钟模式2输入的时钟
void TIM_ETRConfig(TIM_TypeDef* TIMx, uint16_t TIM_ExtTRGPrescaler, uint16_t TIM_ExtTRGPolarity, uint16_t ExtTRGFilter);
作用:单独的用来配置ETR引脚的预分频器、极性、滤波器这些参数
对应图六的黄色也就是时钟选择部分
作用:使能中断输出信号(对应图6的中断输出控制)
void TIM_ForcedOC1Config(TIM_TypeDef* TIMx, uint16_t TIM_ForcedAction);
作用:配置强制输出模式(输出比较单元1)
void TIM_ForcedOC2Config(TIM_TypeDef* TIMx, uint16_t TIM_ForcedAction);
作用:配置强制输出模式(输出比较单元2)
void TIM_ForcedOC3Config(TIM_TypeDef* TIMx, uint16_t TIM_ForcedAction);
作用:配置强制输出模式(输出比较单元3)
void TIM_ForcedOC4Config(TIM_TypeDef* TIMx, uint16_t TIM_ForcedAction);
作用:配置强制输出模式(输出比较单元4)
void TIM_OC1PreloadConfig(TIM_TypeDef* TIMx, uint16_t TIM_OCPreload);
作用:配置CCR寄存器的预装功能(写入的值不会立即生效,而是在更新事件生效)
void TIM_OC2PreloadConfig(TIM_TypeDef* TIMx, uint16_t TIM_OCPreload);
作用:配置CCR寄存器的预装功能(写入的值不会立即生效,而是在更新事件生效)
void TIM_OC3PreloadConfig(TIM_TypeDef* TIMx, uint16_t TIM_OCPreload);
作用:配置CCR寄存器的预装功能(写入的值不会立即生效,而是在更新事件生效)
void TIM_OC4PreloadConfig(TIM_TypeDef* TIMx, uint16_t TIM_OCPreload);
作用:配置CCR寄存器的预装功能(写入的值不会立即生效,而是在更新事件生效)
void TIM_OC1FastConfig(TIM_TypeDef* TIMx, uint16_t TIM_OCFast);
作用:配置快速使能
void TIM_OC2FastConfig(TIM_TypeDef* TIMx, uint16_t TIM_OCFast);
作用:配置快速使能
void TIM_OC3FastConfig(TIM_TypeDef* TIMx, uint16_t TIM_OCFast);
作用:配置快速使能
void TIM_OC4FastConfig(TIM_TypeDef* TIMx, uint16_t TIM_OCFast);
作用:配置快速使能
void TIM_ClearOC1Ref(TIM_TypeDef* TIMx, uint16_t TIM_OCClear);
作用:外部事件清除REF信号
void TIM_ClearOC2Ref(TIM_TypeDef* TIMx, uint16_t TIM_OCClear);
作用:外部事件清除REF信号
void TIM_ClearOC3Ref(TIM_TypeDef* TIMx, uint16_t TIM_OCClear);
作用:外部事件清除REF信号
void TIM_ClearOC4Ref(TIM_TypeDef* TIMx, uint16_t TIM_OCClear);
作用:外部事件清除REF信号
void TIM_OC1PolarityConfig(TIM_TypeDef* TIMx, uint16_t TIM_OCPolarity);
作用:单独设置输出比较的极性
void TIM_OC1NPolarityConfig(TIM_TypeDef* TIMx, uint16_t TIM_OCNPolarity);
作用:单独设置输出比较的极性(高级计时器里互补通道的配置)
void TIM_OC2PolarityConfig(TIM_TypeDef* TIMx, uint16_t TIM_OCPolarity);
作用:单独设置输出比较的极性
void TIM_OC2NPolarityConfig(TIM_TypeDef* TIMx, uint16_t TIM_OCNPolarity);
作用:单独设置输出比较的极性(高级计时器里互补通道的配置)
void TIM_OC3PolarityConfig(TIM_TypeDef* TIMx, uint16_t TIM_OCPolarity);
作用:单独设置输出比较的极性
void TIM_OC3NPolarityConfig(TIM_TypeDef* TIMx, uint16_t TIM_OCNPolarity);
作用:单独设置输出比较的极性(高级计时器里互补通道的配置)
void TIM_OC4PolarityConfig(TIM_TypeDef* TIMx, uint16_t TIM_OCPolarity);
作用:单独设置输出比较的极性
OC4没有互补通道
void TIM_CCxCmd(TIM_TypeDef* TIMx, uint16_t TIM_Channel, uint16_t TIM_CCx);
作用:单独修改输出使能参数
void TIM_CCxNCmd(TIM_TypeDef* TIMx, uint16_t TIM_Channel, uint16_t TIM_CCxN);
作用:单独修改输出使能参数(高级计时器里互补通道的配置)
void TIM_SelectOCxM(TIM_TypeDef* TIMx, uint16_t TIM_Channel, uint16_t TIM_OCMode);
作用:单独更改输出比较模式
void TIM_SetCompare1(TIM_TypeDef* TIMx, uint16_t Compare1);
作用:单独更改CCR寄存器值的函数(更改占空比)
void TIM_SetCompare2(TIM_TypeDef* TIMx, uint16_t Compare2);
作用:单独更改CCR寄存器值的函数
void TIM_SetCompare3(TIM_TypeDef* TIMx, uint16_t Compare3);
作用:单独更改CCR寄存器值的函数
void TIM_SetCompare4(TIM_TypeDef* TIMx, uint16_t Compare4);
作用:单独更改CCR寄存器值的函数
void TIM_CtrlPWMOutputs(TIM_TypeDef* TIMx, FunctionalState NewState);
作用:仅高级定时器使用,在使用高级定时器输出PWM时,需要调用这个函数,使能主输出,否则PWM将不能正常输出
void TIM_PrescalerConfig(TIM_TypeDef* TIMx, uint16_t Prescaler, uint16_t TIM_PSCReloadMode);
参数一:TIMx,选择某个定时器
参数二:写入的预分配值
参数三:写入的模式
TIM_PSCReloadMode_Update——预分频器在更新事件重装
TIM_PSCReloadMode_Immediate——预分频器立即重装
作用:单独写预分频值
void TIM_CounterModeConfig(TIM_TypeDef* TIMx, uint16_t TIM_CounterMode);
参数一:TIMx,选择某个定时器
参数二:选择新的计数器模式
作用:用来改变计数器的计数模式
void TIM_ARRPreloadConfig(TIM_TypeDef* TIMx, FunctionalState NewState);
参数一:TIMx,选择某个定时器
参数二:ENABLE或DISENABLE
作用:自动重装器预装功能配置
void TIM_SetCounter(TIM_TypeDef* TIMx, uint16_t Counter);
作用:给计数器写入一个值
void TIM_SetAutoreload(TIM_TypeDef* TIMx, uint16_t Autoreload);
作用:给自动重装器写入一个值
uint16_t TIM_GetCounter(TIM_TypeDef* TIMx);
作用:获取当前计数器的值
uint16_t TIM_GetPrescaler(TIM_TypeDef* TIMx);
作用:获取当前的预分频器的值
FlagStatus TIM_GetFlagStatus(TIM_TypeDef* TIMx, uint16_t TIM_FLAG);
void TIM_ClearFlag(TIM_TypeDef* TIMx, uint16_t TIM_FLAG);
ITStatus TIM_GetITStatus(TIM_TypeDef* TIMx, uint16_t TIM_IT);
void TIM_ClearITPendingBit(TIM_TypeDef* TIMx, uint16_t TIM_IT);
获取标志位和清除标志位
void TIM_EncoderInterfaceConfig(TIM_TypeDef* TIMx, uint16_t TIM_EncoderMode,
uint16_t TIM_IC1Polarity, uint16_t TIM_IC2Polarity);
第一个参数:TIMx
第二个参数:编码器模式
第三个参数:通道1的电平极性
第四个参数:通道2的电平极性
作用:定时器编码器接口配置
三、定时器输出比较的功能
输出比较模块常见用途就是产生PWM波形,用于驱动电机等设备。
举例:使用STM32输出的PWM波形来驱动舵机和直流电机。
1.输出比较简介
OC(Output Compare)输出比较
输出比较可以通过比较CNT计数器与CCR比较/捕获寄存器值的关系,来对输出电平进行置1、置0或翻转的操作,用于输出一定频率和占空比的PWM波形
见通用定时器结构图,当CNT大于CCR,小于CCR或者等于CCR时,输出会对应的置1,置0、置1、置0这样就可以输出一个电平不断跳变的PWM波形
每个高级定时器和通用定时器都拥有4个输出比较通道
高级定时器的前3个通道额外拥有死区生成和互补输出的功能
2.PWM简介
PWM(Pulse Width Modulation)脉冲宽度调制(数字输出信号)
在具有惯性的系统中,可以通过对一系列脉冲的宽度进行调制,来等效地获得所需要的模拟参量,常应用于电机控速等领域
使用PWM波形是等效地实现一个模拟信号的输出
PWM参数:
频率 = 1 / TS 占空比 = TON / TS 分辨率 = 占空比变化步距
占空比决定了PWM等效出来的模拟电压的大小,占空比越大,等效出来的模拟电压就越趋近于高电平,占空比越小,等效出来的模拟电压就越趋近于低电平,占空比是线性的。
分辨率:占空比变化的精细程度


3.定时器的输出比较模块是如何输出PWM波形的?
高级定时器前三个通道的输出比较部分电路

通用定时器的输出比较部分电路

首先CNT计数器与CCR1第一路的捕获/比较寄存器,它俩进行比较,当CNT>CCR1或CNT=CCR1时,就会给输出模式控制器传一个信号,然后输出模式控制器就会改变它输出oc1ref的高低电平(REF信号就是指这里的高低电平,意思时参考信号),接下来REF可以传到主模式控制器,也就是把这个REF映射到主模式的TRGO输出上去,REF主要去往下一路,通过下一路进入极性选择,给这个寄存器写0,信号就会往上走,就是信号电平不翻转,写1的话,信号就会往下走,信号通过一个非门取反,输出的信号就是输入信号高低电平反转的信号,接着就是输出使能电路,选择要不要输出,最后就是OC1引脚,这个引脚就是CH1通道的引脚,在引脚定义表就知道时哪个GPIO了。
输出模式控制器的执行原理

输出模式可以通过OC1M寄存器来进行配置
有效电平——高电平 无效电平——低电平
3.只使用PWM模式1,向上计数这一种模式,这种模式是怎么输出频率和占空比都 可调的PWM波形的呢?

蓝色线——CNT 黄色线——ARR 红色线——CCR 绿色线——PWM输出
首先CNT向上自增,当CNT=30时,由于是PWM1模式1,所以置无效电平也就是低电平,然后CNT继续自增,当等于ARR时,CNT清零。
4.PWM参数计算

PWM频率=计数器的更新频率
5.代码1 ——PWM驱动LED呼吸灯
PWM初始化步骤:
①RCC开启时钟,把要用到的TIM外设和GPIO外设的时钟打开。
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
②选择时基单元的时钟源(也就是图6的黄色部分)
TIM_InternalClockConfig(TIM2); // 选择时钟源
③配置时基单元
TIM_TimeBaseInitTypeDef TIM_TimerBaseInitStructure; // 配置时基单元
TIM_TimerBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;
TIM_TimerBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimerBaseInitStructure.TIM_Period = 100 - 1;
TIM_TimerBaseInitStructure.TIM_Prescaler = 720 -1;
TIM_TimerBaseInitStructure.TIM_RepetitionCounter = 0;
TIM_TimeBaseInit(TIM2, &TIM_TimerBaseInitStructure);
④配置输出比较单元(CCR的值、输出比较模式、极性选择、输出使能)
TIM_OCInitTypeDef TIM_OCInitStructure; // 配置(初始化)输出比较单元
TIM_OCStructInit(&TIM_OCInitStructure);// 给结构体赋默认初始值
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;
设置输出比较模式:
TIM_OCMode_Timing——冻结模式
TIM_OCMode_Active——相等时置有效电平
TIM_OCMode_Inactive——相等时置无效电平
TIM_OCMode_Toggle——相等时电平翻转转
TIM_OCMode_PWM1——PWM模式1
TIM_OCMode_PWM2——PWM模式2
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;
设置输出比较的极性:
TIM_OCPolarity_High——高极性,极性不翻转,REF波形直接输出,或者说有效电平为高电平
TIM_OCPolarity_Low——低极性,极性翻转,REF电平取反,或者说有效电平为低电平
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
设置输出使能:
TIM_OutputState_Disable——失能
TIM_OutputState_Enable——使能
TIM_OCInitStructure.TIM_Pulse = 0x0000; //设置CCR
TIM_OC1Init(TIM2, &TIM_OCInitStructure);
⑤配置GPIO(复用推挽输出)
GPIO_InitTypeDef GPIO_InitStructure; // 配置GPIO
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
⑥运行控制
TIM_Cmd(TIM2, ENABLE); // 配置运行控制
完成呼吸灯需要不断改变占空比因此我们需要函数TIM_SetCompare1();
写一个函数来不断改变占空比
void PWM_SetCompare1(uint16_t Compare)
{
TIM_SetCompare1(TIM2, Compare);
}
PWM.c
#include "stm32f10x.h" // Device header
void PWM_Init(void) // PWM初始化
{
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE); //开启TIM和GPIO外设时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
TIM_InternalClockConfig(TIM2); // 选择时钟源
TIM_TimeBaseInitTypeDef TIM_TimerBaseInitStructure; // 配置时基单元
TIM_TimerBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;
TIM_TimerBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimerBaseInitStructure.TIM_Period = 100 - 1; // ARR
TIM_TimerBaseInitStructure.TIM_Prescaler = 720 -1; // PSC
TIM_TimerBaseInitStructure.TIM_RepetitionCounter = 0;
TIM_TimeBaseInit(TIM2, &TIM_TimerBaseInitStructure);
TIM_OCInitTypeDef TIM_OCInitStructure; // 配置(初始化)输出比较单元
TIM_OCStructInit(&TIM_OCInitStructure);
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
TIM_OC1Init(TIM2, &TIM_OCInitStructure);
GPIO_InitTypeDef GPIO_InitStructure; // 配置GPIO
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
TIM_Cmd(TIM2, ENABLE); // 配置运行控制
}
void PWM_SetCompare1(uint16_t Compare)
{
TIM_SetCompare1(TIM2, Compare);
}
main.c
#include "stm32f10x.h" // Device header
#include "Delay.h"
#include "OLED.h"
#include "PWM.h"
uint8_t i;
int main(void)
{
OLED_Init();
PWM_Init();
while (1)
{
for(i=0; i<=100; i++)
{
PWM_SetCompare1(i);
Delay_ms(10);
}
for(i=0; i<=100; i++)
{
PWM_SetCompare1(100-i);
Delay_ms(10);
}
}
}
6.代码2——PWM驱动舵机
同一个定时器的不同通道输出的PWM
频率必须一样、占空比由各自的CCR决定、相位同步
舵机是一种根据输入PWM信号占空比来控制输出角度的装置
输入PWM信号要求:周期为20ms,高电平宽度为0.5ms~2.5ms
这里有三个模块,PWM.c进行PWM初始化,以及更改占空比。Servo.c进行舵机角度设置,以及舵机初始,舵机初始化其实就是PWM初始化。还有一个就是main.c
Servo.c
#include "stm32f10x.h" // Device header
#include "PWM.h"
void Servo_Init(void)
{
PWM_Init();
}
void Servo_SetAngle(float Angle) // 设置舵机角度
{
PWM_SetCompare2(Angle / 180 * 2000 + 500);
}
PWM.c——这里更换了定时器通道
#include "stm32f10x.h" // Device header
void PWM_Init(void) // PWM初始化
{
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE); //开启TIM和GPIO外设时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
TIM_InternalClockConfig(TIM2); // 选择时钟源
TIM_TimeBaseInitTypeDef TIM_TimerBaseInitStructure; // 配置时基单元
TIM_TimerBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;
TIM_TimerBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimerBaseInitStructure.TIM_Period = 20000 - 1; // ARR
TIM_TimerBaseInitStructure.TIM_Prescaler = 72 -1; // PSC
TIM_TimerBaseInitStructure.TIM_RepetitionCounter = 0;
TIM_TimeBaseInit(TIM2, &TIM_TimerBaseInitStructure);
TIM_OCInitTypeDef TIM_OCInitStructure; // 配置(初始化)输出比较单元
TIM_OCStructInit(&TIM_OCInitStructure);
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
TIM_OC2Init(TIM2, &TIM_OCInitStructure);
GPIO_InitTypeDef GPIO_InitStructure; // 配置GPIO
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
TIM_Cmd(TIM2, ENABLE); // 配置运行控制
}
void PWM_SetCompare2(uint16_t Compare) // 改变占空比
{
TIM_SetCompare2(TIM2, Compare);
}
main.c
#include "stm32f10x.h" // Device header
#include "Delay.h"
#include "OLED.h"
#include "Servo.h"
#include "Key.h"
uint8_t KeyNum; //按键键码
float Angle; // 角度变量
int main(void)
{
OLED_Init();
Servo_Init();
Key_Init();
OLED_ShowString(1, 1, "Angle:");
while (1)
{
KeyNum = Key_GetNum();
if(KeyNum == 1)
{
Angle += 30;
if(Angle > 180)
{
Angle = 0;
}
}
Servo_SetAngle(Angle);
OLED_ShowNum(1, 7, Angle, 3);
}
}
每按下一次按键后,舵机角度增加30。当为180°是,再按一次,返回0°。
7.代码3——PWM驱动直流电机
PWM.c——这里更换了定时器通道
#include "stm32f10x.h" // Device header
void PWM_Init(void) // PWM初始化
{
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE); //开启TIM和GPIO外设时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
TIM_InternalClockConfig(TIM2); // 选择时钟源
TIM_TimeBaseInitTypeDef TIM_TimerBaseInitStructure; // 配置时基单元
TIM_TimerBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;
TIM_TimerBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimerBaseInitStructure.TIM_Period = 100 - 1; // ARR
TIM_TimerBaseInitStructure.TIM_Prescaler = 36 -1; // PSC
TIM_TimerBaseInitStructure.TIM_RepetitionCounter = 0;
TIM_TimeBaseInit(TIM2, &TIM_TimerBaseInitStructure);
TIM_OCInitTypeDef TIM_OCInitStructure; // 配置(初始化)输出比较单元
TIM_OCStructInit(&TIM_OCInitStructure);
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
TIM_OC3Init(TIM2, &TIM_OCInitStructure);
GPIO_InitTypeDef GPIO_InitStructure; // 配置GPIO
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
TIM_Cmd(TIM2, ENABLE); // 配置运行控制
}
void PWM_SetCompare3(uint16_t Compare)
{
TIM_SetCompare3(TIM2, Compare);
}
Motor.c——直流电机模块,因为这里需要一个直流电机驱动,所以在初始化增加了GPIO配置
#include "stm32f10x.h" // Device header
#include "PWM.h"
void Motor_Init(void)
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4 | GPIO_Pin_5;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
PWM_Init();
}
void Motor_SetSpeed(int8_t Speed)
{
if(Speed > 0)
{
GPIO_SetBits(GPIOA, GPIO_Pin_4);
GPIO_ResetBits(GPIOA, GPIO_Pin_5);
PWM_SetCompare3(Speed);
}
else
{
GPIO_ResetBits(GPIOA, GPIO_Pin_4);
GPIO_SetBits(GPIOA, GPIO_Pin_5);
PWM_SetCompare3(-Speed);
}
}
main.c
#include "stm32f10x.h" // Device header
#include "Delay.h"
#include "OLED.h"
#include "Motor.h"
#include "Key.h"
uint8_t KeyNum;
int8_t Speed;
int main(void)
{
OLED_Init();
Motor_Init();
Key_Init();
OLED_ShowString(1, 1, "Speed:");
while (1)
{
KeyNum = Key_GetNum();
if(KeyNum == 1)
{
Speed += 20;
if(Speed > 100)
{
Speed = -100;
}
}
Motor_SetSpeed(Speed);
OLED_ShowSignedNum(1, 7, Speed, 3);
}
}
每按下一次按键,电机速度增加20,当为100时,再次按下按键为-100,然后以20速度降低。
四、定时器输入捕获的功能
举例:使用输入捕获模块实现测量方波频率。
五、定时器输入捕获
1.输入捕获简介
IC(Input Capture)输入捕获
输入捕获模式下,当通道输入引脚出现指定电平跳变时,当前CNT的值将被锁存到CCR中,可用于测量PWM波形的频率、占空比、脉冲间隔、电平持续时间等参数
每个高级定时器和通用定时器都拥有4个输入捕获通道
可配置为PWMI模式,同时测量频率和占空比
可配合主从触发模式,实现硬件全自动测量
2.频率测量

越往左,频率越高,越往右,频率越低
测频法:在闸门时间T内,对上升沿计次,得到N,则频率
适合高频信号,测量结果更新缓慢,数值相对稳定
测周法:两个上升沿内,以标准频率fc计次,得到N ,则频率
适合低频信号,测量结果更新快,数据跳变也非常快
中界频率:测频法与测周法误差相等的频率点
3.输入捕获电路 
从左到右,首先是四个引脚,参考引脚定义表查看复用到哪个位置,然后引脚进来,有一个三输入的异或门,异或门的输入接到了通道1,2,3端口,异或门的执行逻辑(当三个输入引脚的任意一个电平翻转时,输出引脚就产生一次电平翻转),之后输出通过数据选择器,到达输入捕获通道1,数据选择器如果选择上面一个,那么输入捕获通道1的输入,就是3个引脚的异或值,如果选择下面一个,异或门不起作用,4个通道各用各的引脚,然后输入信号进入输入滤波器(对信号滤波)和边沿检测器(选择高/低电平触发,当出现指定的电平时,边沿检测电路就会触发后续电路执行动作),此外这里其实设计了两套滤波和边沿检测电路,第一套电路,经过滤波和极性选择,得到TI1FP1,输入给通道1的后续电路。第二套电路,经过另一个滤波和极性选择,得到TI1FP2,输入给下面通道2的后续电路。同理,下面TI2信号进来,也经过两套滤波和极性选择,得到TI2FP1和TI2FP2,其中,TI2FP1输入给上面,TI2FP2输入给下面。这里两个信号可以各走各的,也可以选择交叉,让CH2引脚输入给通道1,或者CH1引脚输入给通道2。交叉连接的目的:①可以灵活切换后续捕获电路的输入②将一个引脚的输入,同时映射到两个捕获单元。接下来,第一个通道使用上升沿触发,用来捕获周期,第二个通道使用下降沿触发,用来捕获占空比,两个通道同时对一个引脚进行捕获,就可以同时测量频率和占空比。输入信号进行滤波和极性选择后,进入预分频器,对前面的信号进行分频,分频之后的触发信号,就可以触发捕获电路进行工作。
每来一个触发信号,CNT的值,就会向CCR转运一次,转运的同时,会发生一个捕获事件,这个事件会在状态寄存器置标志位,同时也可以产生中断。
4.主从触发模式

主模式将定时器内部的信号,映射到TRGO引脚,用于触发别的外设。
从模式:接收其他外设或自身外设的一些信号,用于控制自身定时器的运行
触发源选择:选择从模式的触发信号源
5.输入捕获基本结构

右上角为时基单元,配置好时基单元,启动定时器,那么CNT就会在预分频之后的时钟驱动下,不断自增,这个CNT就是测周法用来计数计时的东西,经过预分频之后这个给位置的时钟频率,就是驱动CNT的标准频率fc。然后下面输入捕获通道1的GPIO口,输入一个图中方波信号,经过滤波器和边沿检测,选择TI1FP1为上升沿触发,之后输入选择直连的通道,分频器选择不分频,当TI1FP1出现上升沿之后,CNT的当前计数值转运到CCR1,同时触发源选择,选中TI1FP1为触发信号,从模式选择复位操作,这样TI1FP1的上升沿也会通过上面一路,触发CNT清零。
然后分析左上角的图:首先出现一个上升沿,将CNT的值传入CCR1(输入捕获执行),同时清零CNT(从模式执行),然后在一个周期内CNT在标准时钟的驱动下,不断自增,然后直到下一次上升沿出现,CNT清零,第二次捕获是CCR1=CNT,这里的CCR1就是一个周期的计数值,然后当运行到下一个周期时,CCR1的值更新,所以CCR1的值始终为最新一个周期的计数值,这里的计数值就是测周法中的N。
6.PWMI基本结构

首先,TI1FP1配置上升沿触发,触发捕获和清零CNT,正常捕获周期,这时再来一个TI1FP2,配置为下降沿触发,通过交叉通道,触发通道2的捕获单元。
分析左上角图,最开始上升沿,CCR1捕获,同时清零CNT,之后CNT++,然后在下降沿时刻,触发CCR2捕获,所以这里CCR2的值就是高电平时的计数值,CCR2捕获,并不触发CNT清零,然后CNT继续++,直到下一个上升沿触发CCR1捕获,此时CCR1的值为一个周期的计数值,然后CNT清零,所以CCR2/CCR1就是占空比。
7.代码1——输入捕获模拟测频率
调节PSC来更新PWM频率,调节CCR更新占空比,所以这里ARR给固定值。
PWM.c
#include "stm32f10x.h" // Device header
void PWM_Init(void) // PWM初始化
{
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE); //开启TIM和GPIO外设时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
TIM_InternalClockConfig(TIM2); // 选择时钟源
TIM_TimeBaseInitTypeDef TIM_TimerBaseInitStructure; // 配置时基单元
TIM_TimerBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;
TIM_TimerBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimerBaseInitStructure.TIM_Period = 100 - 1; // ARR
TIM_TimerBaseInitStructure.TIM_Prescaler = 720 -1; // PSC
TIM_TimerBaseInitStructure.TIM_RepetitionCounter = 0;
TIM_TimeBaseInit(TIM2, &TIM_TimerBaseInitStructure);
TIM_OCInitTypeDef TIM_OCInitStructure; // 配置(初始化)输出比较单元
TIM_OCStructInit(&TIM_OCInitStructure);
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
TIM_OC1Init(TIM2, &TIM_OCInitStructure);
GPIO_InitTypeDef GPIO_InitStructure; // 配置GPIO
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
TIM_Cmd(TIM2, ENABLE); // 配置运行控制
}
void PWM_SetCompare1(uint16_t Compare) // 调节CCR1寄存器的值 改变占空比
{
TIM_SetCompare1(TIM2, Compare);
}
void PWM_SetPrescaler(uint16_t Prescaler) // 调节PSC分配器的值 改变频率
{
TIM_PrescalerConfig(TIM2, Prescaler, TIM_PSCReloadMode_Immediate);
}
输入捕获的步骤:
①RCC开启时钟,把要用到的TIM外设和GPIO外设的时钟打开。
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
②配置GPIO(输入模式)
GPIO_InitTypeDef GPIO_InitStructure; // 配置GPIO
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
③选择时基单元的时钟源(也就是图6的黄色部分)
TIM_InternalClockConfig(TIM2); // 选择时钟源
④配置时基单元
TIM_TimeBaseInitTypeDef TIM_TimerBaseInitStructure; // 配置时基单元
TIM_TimerBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;
TIM_TimerBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimerBaseInitStructure.TIM_Period = 65536 - 1;
TIM_TimerBaseInitStructure.TIM_Prescaler = 72 -1;
TIM_TimerBaseInitStructure.TIM_RepetitionCounter = 0;
TIM_TimeBaseInit(TIM2, &TIM_TimerBaseInitStructure);
⑤配置输入捕获单元(滤波器、极性、直连通道、分频器)
TIM_ICInitTypeDef TIM_ICInitStructure; // 配置(初始化)输入捕获单元
TIM_ICInitStructure.TIM_Channel = TIM_Channel_1;
输入通道:TIM_Channel_1、TIM_Channel_2、TIM_Channel_3、TIM_Channel_4
TIM_ICInitStructure.TIM_ICFilter = 0xF;
输入捕获的滤波器:between 0x0 and 0xF
TIM_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Rising;
极性:TIM_ICPolarity_Rising——上升沿触发
TIM_ICPolarity_Falling——下降沿触发
TIM_ICPolarity_BothEdge——上升沿下降沿都触发
TIM_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1;
分频器:TIM_ICPSC_DIV1、TIM_ICPSC_DIV2、TIM_ICPSC_DIV4、TIM_ICPSC_DIV8
TIM_ICInitStructure.TIM_ICSelection = ;
触发信号从哪个引脚输入:TIM_ICSelection_DirectTI——直连通道
TIM_ICSelection_IndirectTI——交叉通道
TIM_ICInit(TIM3, &TIM_ICInitStructure);
⑥配置TRGI的触发源为TIFP1(从模式的触发源)
TIM_SelectInputTrigger(TIM3, TIM_TS_TI1FP1);
⑦配置从模式为Reset
TIM_SelectSlaveMode(TIM3, TIM_SlaveMode_Reset);
⑧配置运行控制
TIM_Cmd(TIM2, ENABLE); // 配置运行控制
IC.c(输入捕获模块)
#include "stm32f10x.h" // Device header
// PA0口的占空比和频率是多少
void IC_Init(void)
{
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE); //开启TIM和GPIO外设时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
GPIO_InitTypeDef GPIO_InitStructure; // 配置GPIO
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
TIM_InternalClockConfig(TIM3); // 选择时钟源
TIM_TimeBaseInitTypeDef TIM_TimerBaseInitStructure; // 配置时基单元
TIM_TimerBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;
TIM_TimerBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimerBaseInitStructure.TIM_Period = 65536 - 1; // ARR
TIM_TimerBaseInitStructure.TIM_Prescaler = 72 -1; // PSC
TIM_TimerBaseInitStructure.TIM_RepetitionCounter = 0;
TIM_TimeBaseInit(TIM3, &TIM_TimerBaseInitStructure);
TIM_ICInitTypeDef TIM_ICInitStructure; // 配置(初始化)输入捕获单元
TIM_ICInitStructure.TIM_Channel = TIM_Channel_1;
TIM_ICInitStructure.TIM_ICFilter = 0xF;
TIM_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Rising;
TIM_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1;
TIM_ICInitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI;
TIM_ICInit(TIM3, &TIM_ICInitStructure);
TIM_SelectInputTrigger(TIM3, TIM_TS_TI1FP1); // 配置TRGI的触发源为TIFP1
TIM_SelectSlaveMode(TIM3, TIM_SlaveMode_Reset); //配置从模式为Reset
TIM_Cmd(TIM3, ENABLE); // 配置运行控制
}
uint32_t IC_GetFreq(void) // 获取频率
{
return 1000000 / (TIM_GetCapture1(TIM3) + 1);
}
main.c
#include "stm32f10x.h" // Device header
#include "Delay.h"
#include "OLED.h"
#include "PWM.h"
#include "IC.h"
int main(void)
{
OLED_Init();
PWM_Init();
IC_Init();
OLED_ShowString(1, 1, "Freq:00000Hz");
PWM_SetPrescaler(720 - 1); // 更新PSC
PWM_SetCompare1(50); // 更新CCR
while (1)
{
OLED_ShowNum(1, 6, IC_GetFreq(), 5);
}
}
8.代码2——PWMI模式测频率占空比
PWM.c
#include "stm32f10x.h" // Device header
void PWM_Init(void) // PWM初始化
{
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE); //开启TIM和GPIO外设时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
TIM_InternalClockConfig(TIM2); // 选择时钟源
TIM_TimeBaseInitTypeDef TIM_TimerBaseInitStructure; // 配置时基单元
TIM_TimerBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;
TIM_TimerBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimerBaseInitStructure.TIM_Period = 100 - 1; // ARR
TIM_TimerBaseInitStructure.TIM_Prescaler = 720 -1; // PSC
TIM_TimerBaseInitStructure.TIM_RepetitionCounter = 0;
TIM_TimeBaseInit(TIM2, &TIM_TimerBaseInitStructure);
TIM_OCInitTypeDef TIM_OCInitStructure; // 配置(初始化)输出比较单元
TIM_OCStructInit(&TIM_OCInitStructure);
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
TIM_OC1Init(TIM2, &TIM_OCInitStructure);
GPIO_InitTypeDef GPIO_InitStructure; // 配置GPIO
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
TIM_Cmd(TIM2, ENABLE); // 配置运行控制
}
void PWM_SetCompare1(uint16_t Compare) // 调节CCR1寄存器的值 改变占空比
{
TIM_SetCompare1(TIM2, Compare);
}
void PWM_SetPrescaler(uint16_t Prescaler) // 调节PSC分配器的值 改变频率
{
TIM_PrescalerConfig(TIM2, Prescaler, TIM_PSCReloadMode_Immediate);
}
IC.c(输入捕获模块)
#include "stm32f10x.h" // Device header
// PA0口的占空比和频率是多少
void IC_Init(void)
{
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE); //开启TIM和GPIO外设时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
GPIO_InitTypeDef GPIO_InitStructure; // 配置GPIO
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
TIM_InternalClockConfig(TIM3); // 选择时钟源
TIM_TimeBaseInitTypeDef TIM_TimerBaseInitStructure; // 配置时基单元
TIM_TimerBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;
TIM_TimerBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimerBaseInitStructure.TIM_Period = 65536 - 1; // ARR
TIM_TimerBaseInitStructure.TIM_Prescaler = 72 -1; // PSC
TIM_TimerBaseInitStructure.TIM_RepetitionCounter = 0;
TIM_TimeBaseInit(TIM3, &TIM_TimerBaseInitStructure);
TIM_ICInitTypeDef TIM_ICInitStructure; // 配置(初始化)输入捕获单元
TIM_ICInitStructure.TIM_Channel = TIM_Channel_1;
TIM_ICInitStructure.TIM_ICFilter = 0xF;
TIM_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Rising;
TIM_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1;
TIM_ICInitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI;
TIM_ICInit(TIM3, &TIM_ICInitStructure);
TIM_PWMIConfig(TIM3, &TIM_ICInitStructure);
TIM_SelectInputTrigger(TIM3, TIM_TS_TI1FP1); // 配置TRGI的触发源为TIFP1
TIM_SelectSlaveMode(TIM3, TIM_SlaveMode_Reset); //配置从模式为Reset
TIM_Cmd(TIM3, ENABLE); // 配置运行控制
}
uint32_t IC_GetFreq(void) // 获取频率
{
return 1000000 / (TIM_GetCapture1(TIM3) + 1);
}
uint32_t IC_GetDuty(void) // 获取占空比
{
return (TIM_GetCapture2(TIM3) + 1)* 100 / (TIM_GetCapture1(TIM3) + 1); // CCR2/CCR1
}
main.c
#include "stm32f10x.h" // Device header
#include "Delay.h"
#include "OLED.h"
#include "PWM.h"
#include "IC.h"
int main(void)
{
OLED_Init();
PWM_Init();
IC_Init();
OLED_ShowString(1, 1, "Freq:00000Hz");
OLED_ShowString(2, 1, "Duty:00%");
PWM_SetPrescaler(7200 - 1); // 更新PSC
PWM_SetCompare1(80); // 更新CCR
while (1)
{
OLED_ShowNum(1, 6, IC_GetFreq(), 5);
OLED_ShowNum(2, 6, IC_GetDuty(), 2);
}
}
六、定时器的编码器接口
1.编码器接口简介
Encoder Interface 编码器接口
相当于一个带有方向选择的外部时钟
工作流程:编码器接口可接收增量(正交)编码器的信号,根据编码器旋转产生的正交信号脉冲,自动控制CNT自增或自减,从而指示编码器的位置、旋转方向和旋转速度
每个高级定时器和通用定时器都拥有1个编码器接口
两个输入引脚借用了输入捕获的通道1和通道2
2.正交编码器

正传之后90°,反转提前90°,通过A相的上升还是下降,观察B相来查看正传还是反转。
3.编码器接口基本结构

4,代码——编码器接口测速
编码器测速初始化:
①RCC开启时钟,开启GPIO和定时器时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);
②配置GPIO
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
③配置时基单元
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;
TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInitStructure.TIM_Period = 65536 - 1;
TIM_TimeBaseInitStructure.TIM_Prescaler = 1 - 1;
TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;
TIM_TimeBaseInit(TIM3, &TIM_TimeBaseInitStructure);
④配置输入捕获单元
TIM_ICInitTypeDef TIM_ICInitStructure;
TIM_ICStructInit(&TIM_ICInitStructure);
TIM_ICInitStructure.TIM_Channel = TIM_Channel_1;
TIM_ICInitStructure.TIM_ICFilter = 0xF;
TIM_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Rising; // 极性不反转
TIM_ICInit(TIM3, &TIM_ICInitStructure);
TIM_ICInitStructure.TIM_Channel = TIM_Channel_2;
TIM_ICInitStructure.TIM_ICFilter = 0xF;
TIM_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Rising; // 极性不反转
TIM_ICInit(TIM3, &TIM_ICInitStructure);
⑤配置编码器接口模式
TIM_EncoderInterfaceConfig(TIM3,TIM_EncoderMode_TI1,TIM_ICPolarity_Rising,TIM_ICPolarity_Falling);
参数二:TIM_EncoderMode_TI1——TI1计数
TIM_EncoderMode_TI2——TI2计数
TIM_EncoderMode_TI12——TI1TI2都计数
参数3,4:TIM_ICPolarity_Rising——通道反向
TIM_ICPolarity_Falling——通道不反向
⑥TIM_Cmd启动定时器
TIM_Cmd(TIM3, ENABLE);
Enconder.c——编码器模块
#include "stm32f10x.h" // Device header
void Enconder_Init(void)
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;
TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInitStructure.TIM_Period = 65536 - 1;
TIM_TimeBaseInitStructure.TIM_Prescaler = 1 - 1;
TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;
TIM_TimeBaseInit(TIM3, &TIM_TimeBaseInitStructure);
TIM_ICInitTypeDef TIM_ICInitStructure;
TIM_ICStructInit(&TIM_ICInitStructure);
TIM_ICInitStructure.TIM_Channel = TIM_Channel_1;
TIM_ICInitStructure.TIM_ICFilter = 0xF;
TIM_ICInit(TIM3, &TIM_ICInitStructure);
TIM_ICInitStructure.TIM_Channel = TIM_Channel_2;
TIM_ICInitStructure.TIM_ICFilter = 0xF;
TIM_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Rising; // 极性不反转
TIM_ICInit(TIM3, &TIM_ICInitStructure);
TIM_EncoderInterfaceConfig(TIM3, TIM_EncoderMode_TI12, TIM_ICPolarity_Rising, TIM_ICPolarity_Rising);
TIM_Cmd(TIM3, ENABLE);
}
int16_t Enconder_Get(void)
{
int16_t Temp;
Temp = TIM_GetCounter(TIM3);
TIM_SetCounter(TIM3, 0); //CNT清零
return Temp;
}
Timer.c
#include "stm32f10x.h" // Device header
void Timer_Init(void)
{
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE); // RCC开启时钟
TIM_InternalClockConfig(TIM2); // 选择内部时钟模式
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure; // 配置时基单元
TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;
TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInitStructure.TIM_Period = 10000 - 1;
TIM_TimeBaseInitStructure.TIM_Prescaler = 7200 - 1;
TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;
TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStructure);
TIM_ClearFlag(TIM2, TIM_FLAG_Update);
TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE); // 中断控制输出配置
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); // 中断分组
NVIC_InitTypeDef NVIC_Initstructure; // 配置NVIC
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);
TIM_Cmd(TIM2, ENABLE); //配置运行控制
}
main.c
#include "stm32f10x.h" // Device header
#include "Delay.h"
#include "OLED.h"
#include "Timer.h"
#include "Enconder.h"
int16_t Speed;
int main(void)
{
OLED_Init();
Timer_Init();
Enconder_Init();
OLED_ShowString(1, 1, "Speed:");
while (1)
{
OLED_ShowSignedNum(1, 5, Speed, 5);
}
}
void TIM2_IRQHandler(void)
{
if (TIM_GetITStatus(TIM2, TIM_IT_Update) == SET)
{
Speed = Enconder_Get();
TIM_ClearITPendingBit(TIM2, TIM_IT_Update);
}
}
七、结语
嵌入式定时器是微控制器的核心外设,通过对时钟源计数实现精确时序控制。其核心组件包括计数器、预分频器和自动重载寄存器,可配置为定时计数、PWM 输出、输入捕获或输出比较等模式。常用于实现延时任务、电机控制、频率测量及通信协议时序管理,是嵌入式系统中实现精准时间管理和外设驱动的基础模块。
更多推荐



所有评论(0)