目录
一.定时器简介
1.基本定时器
2.通用定时器
3.高级定时器
4.定时器基本结构
编辑编辑 5.计数器时序
5.时钟树
二.定时器定时
代码实现
<1>显示屏自动计时
(1)跨文件使用变量
(2) 将中断函数放在主函数里面
<2>对射式红外计数
三.定时器输出比较
一.输出比较简介
二.PWM波形
三.输出模式控制器的工作原理
四.外部设备
五.PWM相关函数
六.代码实现
滤波器可以滤掉信号的抖动和干扰,其工作原理:在一个固定的时钟频率f下进行采样,如果连续n隔采样点都为相同的电平,那就代表输入的信号稳定了。如果采样值不全都相同,那就说明信号有抖动,这时就保证上一次的输出,或者直接输出低电平也行,这样就能保证输出信号在一定程度上的滤波,采样频率f和采样点数N都时滤波器的参数
频率越低,采样点数越多,滤波效果就越好,信号延迟就越大
采样频率f可以由内部时钟分频而来,由参数ClockDivision决定
上图对应函数
timer.c
#include "stm32f10x.h" // Device header
extern uint16_t Num;//告诉编译器,在其他地方已经定义了Num的值,自己去找
void Timer_Init(void)
{
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);
/*选择时钟函数*/
TIM_InternalClockConfig(TIM2);//也可以不写,因为定时器上电以后默认就是内部时钟
/*配置时基单元*/
TIM_TimeBaseInitStructure.TIM_ClockDivision=TIM_CKD_DIV1;//指定时钟分频,这里不进行分频
TIM_TimeBaseInitStructure.TIM_CounterMode=TIM_CounterMode_Up;
TIM_TimeBaseInitStructure.TIM_Period=10000-1;//周期,就是ARR自动重装器的值
TIM_TimeBaseInitStructure.TIM_Prescaler=7200-1;//PSC预分频器的值
TIM_TimeBaseInitStructure.TIM_RepetitionCounter=0;//重复计数器的值,高级定时器才有
TIM_TimeBaseInit(TIM2,&TIM_TimeBaseInitStructure);//为将初始化的值由缓冲寄存器写入,会立刻更新一个中断,所以下面的计数会从1开始而不是0
TIM_ClearFlag(TIM2,TIM_FLAG_Update);//手动清除更新中断标志位,避免刚初始化完就进入中断
TIM_ITConfig(TIM2,TIM_IT_Update,ENABLE);//使能中断,更新中断
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
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);//启动定时器
}
void TIM2_IRQHandler(void)
{
if(TIM_GetFlagStatus(TIM2,TIM_FLAG_Update)==SET)
{
Num++;
TIM_ClearITPendingBit(TIM2,TIM_IT_Update);
}
}
main.c
#include "stm32f10x.h" // Device header
#include "Delay.h"
#include "OLED.h"
#include "Timer.h"
uint16_t Num;
int main()
{
OLED_Init();
OLED_ShowString(1,1,"NUM:");
Timer_Init();
while(1)
{
OLED_ShowNum(1,5,Num,3);
OLED_ShowNum(2,5,TIM_GetCounter(TIM2),5);
}
}
main.c
#include "stm32f10x.h" // Device header
#include "Delay.h"
#include "OLED.h"
#include "Timer.h"
uint16_t Num;
int main()
{
OLED_Init();
OLED_ShowString(1,1,"NUM:");
Timer_Init();
while(1)
{
OLED_ShowNum(1,5,Num,3);
OLED_ShowNum(2,5,TIM_GetCounter(TIM2),5);
}
}
void TIM2_IRQHandler(void)
{
if(TIM_GetFlagStatus(TIM2,TIM_FLAG_Update)==SET)
{
Num++;
TIM_ClearITPendingBit(TIM2,TIM_IT_Update);
}
}
Timer.c
#include "stm32f10x.h" // Device header
extern uint16_t Num;//告诉编译器,在其他地方已经定义了Num的值,自己去找
void Timer_Init(void)
{
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);
/*选择时钟函数*/
TIM_InternalClockConfig(TIM2);//也可以不写,因为定时器上电以后默认就是内部时钟
/*配置时基单元*/
TIM_TimeBaseInitStructure.TIM_ClockDivision=TIM_CKD_DIV1;//指定时钟分频,这里不进行分频
TIM_TimeBaseInitStructure.TIM_CounterMode=TIM_CounterMode_Up;
TIM_TimeBaseInitStructure.TIM_Period=10000-1;//周期,就是ARR自动重装器的值
TIM_TimeBaseInitStructure.TIM_Prescaler=7200-1;//PSC预分频器的值
TIM_TimeBaseInitStructure.TIM_RepetitionCounter=0;//重复计数器的值,高级定时器才有
TIM_TimeBaseInit(TIM2,&TIM_TimeBaseInitStructure);//为将初始化的值由缓冲寄存器写入,会立刻更新一个中断,所以下面的计数会从1开始而不是0
TIM_ClearFlag(TIM2,TIM_FLAG_Update);//手动清除更新中断标志位,避免刚初始化完就进入中断
TIM_ITConfig(TIM2,TIM_IT_Update,ENABLE);//使能中断,更新中断
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
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);//启动定时器
}
//void TIM2_IRQHandler(void)
//{
// if(TIM_GetFlagStatus(TIM2,TIM_FLAG_Update)==SET)
// {
//
// TIM_ClearITPendingBit(TIM2,TIM_IT_Update);
// }
//}
Timer.c
#include "stm32f10x.h" // Device header
extern uint16_t Num;//告诉编译器,在其他地方已经定义了Num的值,自己去找
void Timer_Init(void)
{
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
/*初始化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);
/*选择时钟函数*/
//第二个参数:外部触发预分频器;第三:外部触发极性(上升或下降触发);第四:外部触发滤波器(这里启动滤波防止CNT计数时发生跳变)
TIM_ETRClockMode2Config(TIM2,TIM_ExtTRGPSC_OFF,TIM_ExtTRGPolarity_NonInverted,0x0f);
/*配置时基单元*/
TIM_TimeBaseInitStructure.TIM_ClockDivision=TIM_CKD_DIV1;//指定时钟分频,这里不进行分频
TIM_TimeBaseInitStructure.TIM_CounterMode=TIM_CounterMode_Up;
TIM_TimeBaseInitStructure.TIM_Period=10-1;//周期,就是ARR自动重装器的值
TIM_TimeBaseInitStructure.TIM_Prescaler=1-1;//PSC预分频器的值
TIM_TimeBaseInitStructure.TIM_RepetitionCounter=0;//重复计数器的值,高级定时器才有
TIM_TimeBaseInit(TIM2,&TIM_TimeBaseInitStructure);//为将初始化的值由缓冲寄存器写入,会立刻更新一个中断,所以下面的计数会从1开始而不是0
TIM_ClearFlag(TIM2,TIM_FLAG_Update);//手动清除更新中断标志位,避免刚初始化完就进入中断
TIM_ITConfig(TIM2,TIM_IT_Update,ENABLE);//使能中断,更新中断
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
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 Count(void)//返回计数器的值
{
return TIM_GetCounter(TIM2);
}
//void TIM2_IRQHandler(void)
//{
// if(TIM_GetFlagStatus(TIM2,TIM_FLAG_Update)==SET)
// {
//
// TIM_ClearITPendingBit(TIM2,TIM_IT_Update);
// }
//}
main.c
#include "stm32f10x.h" // Device header
#include "Delay.h"
#include "OLED.h"
#include "Timer.h"
uint16_t Num;
int main()
{
OLED_Init();
OLED_ShowString(1,1,"NUM:");
OLED_ShowString(2,1,"CNT:");
Timer_Init();
while(1)
{
OLED_ShowNum(1,5,Num,3);
OLED_ShowNum(2,5,Count(),5);
}
}
void TIM2_IRQHandler(void)
{
if(TIM_GetFlagStatus(TIM2,TIM_FLAG_Update)==SET)
{
Num++;
TIM_ClearITPendingBit(TIM2,TIM_IT_Update);
}
}
当外部输入信号功率很小,而IO内部的上拉电阻可能会影响到输入信号,可以使用浮空输入来防止影响外部输入的电平
IC(input Capture)输入捕获
CC(Capture/Compare)输入捕获和输出比较单元,当使用输入捕获时就是捕获寄存器,当使用输出比较时就是比较寄存器
PWM是数字输出信号
惯性,举例LED在熄灭的时候,由于余晖和人眼视觉暂留现象,LED不会立马熄灭而会过一小段时间才会熄灭,电机同理
参数:
频率:1/Ts,Ts代表一个高低电平变换周期的时间,频率越快,等效模拟的信号就越平稳,性能开销就越大
占空比:一次高电平/一个高低电平变换周期的时间,决定了PWM等效出来的模拟电压的大小
分辨率:如1%,2%,3%......步距为1%,就是占空比变化的精细程度
注
1.
该结构体的部分参数是在高级定时器才会用到,但最好用函数将其所有的参数全部初始化,便于后期使用高级定时器。
2.ARR,PSC,CCR共同决定PWM的周期和占空比
3.
4.
(1)LED呼吸灯
PWM.c
#include "stm32f10x.h" // Device header
void PWM_Init(void)
{
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
GPIO_InitTypeDef GPIO_InitStructure;
TIM_OCInitTypeDef TIM_OCInitStructure;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
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_InternalClockConfig(TIM2);//也可以不写,因为定时器上电以后默认就是内部时钟
/*配置时基单元*/
TIM_TimeBaseInitStructure.TIM_ClockDivision=TIM_CKD_DIV1;//指定时钟分频,这里不进行分频
TIM_TimeBaseInitStructure.TIM_CounterMode=TIM_CounterMode_Up;
TIM_TimeBaseInitStructure.TIM_Period=100-1;//周期,就是ARR自动重装器的值
TIM_TimeBaseInitStructure.TIM_Prescaler=720-1;//PSC预分频器的值
TIM_TimeBaseInitStructure.TIM_RepetitionCounter=0;//重复计数器的值,高级定时器才有
TIM_TimeBaseInit(TIM2,&TIM_TimeBaseInitStructure);//为将初始化的值由缓冲寄存器写入,会立刻更新一个中断,所以下面的计数会从1开始而不是0
TIM_OCStructInit(&TIM_OCInitStructure);//初始化结构体所有参数,防止修改报错
TIM_OCInitStructure.TIM_OCMode=TIM_OCMode_PWM1;//输出比较模式
TIM_OCInitStructure.TIM_OCPolarity=TIM_OCPolarity_High;//输出比较极性,High极性不翻转,Low极性翻转
TIM_OCInitStructure.TIM_OutputState=TIM_OutputState_Enable;//输出使能
TIM_OCInitStructure.TIM_Pulse=0;//CCR
TIM_OC1Init(TIM2,&TIM_OCInitStructure);
TIM_Cmd(TIM2,ENABLE);//启动定时器
}
void PWM_CCR(uint16_t CCR_count)
{
TIM_SetCompare1(TIM2,CCR_count);//单独更改通道1的CCR值
}
main.c
#include "stm32f10x.h" // Device header
#include "Delay.h"
#include "OLED.h"
#include "PWM.h"
uint16_t i;
int main()
{
OLED_Init();
PWM_Init();
while(1)
{
for(i=0;i<=100;i++)
{
PWM_CCR(i);
Delay_ms(10);
}
for(;i>0;i--)
{
PWM_CCR(i);
Delay_ms(10);
}
}
}
------------------------------------------
将TIM2_CH1通道重映射到PA15IO
PWM.c
#include "stm32f10x.h" // Device header
void PWM_Init(void)
{
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
GPIO_InitTypeDef GPIO_InitStructure;
TIM_OCInitTypeDef TIM_OCInitStructure;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);
GPIO_PinRemapConfig(GPIO_PartialRemap1_TIM2,ENABLE);
GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable,ENABLE);
GPIO_InitStructure.GPIO_Mode=GPIO_Mode_AF_PP;//复用推挽输出模式
GPIO_InitStructure.GPIO_Pin=GPIO_Pin_15;
GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
GPIO_Init(GPIOA,&GPIO_InitStructure);
/*选择时钟函数*/
TIM_InternalClockConfig(TIM2);//也可以不写,因为定时器上电以后默认就是内部时钟
/*配置时基单元*/
TIM_TimeBaseInitStructure.TIM_ClockDivision=TIM_CKD_DIV1;//指定时钟分频,这里不进行分频
TIM_TimeBaseInitStructure.TIM_CounterMode=TIM_CounterMode_Up;
TIM_TimeBaseInitStructure.TIM_Period=100-1;//周期,就是ARR自动重装器的值
TIM_TimeBaseInitStructure.TIM_Prescaler=720-1;//PSC预分频器的值
TIM_TimeBaseInitStructure.TIM_RepetitionCounter=0;//重复计数器的值,高级定时器才有
TIM_TimeBaseInit(TIM2,&TIM_TimeBaseInitStructure);//为将初始化的值由缓冲寄存器写入,会立刻更新一个中断,所以下面的计数会从1开始而不是0
TIM_OCStructInit(&TIM_OCInitStructure);//初始化结构体所有参数,防止修改报错
TIM_OCInitStructure.TIM_OCMode=TIM_OCMode_PWM1;//输出比较模式
TIM_OCInitStructure.TIM_OCPolarity=TIM_OCPolarity_High;//输出比较极性,High极性不翻转,Low极性翻转
TIM_OCInitStructure.TIM_OutputState=TIM_OutputState_Enable;//输出使能
TIM_OCInitStructure.TIM_Pulse=0;//CCR
TIM_OC1Init(TIM2,&TIM_OCInitStructure);
TIM_Cmd(TIM2,ENABLE);//启动定时器
}
void PWM_CCR(uint16_t CCR_count)
{
TIM_SetCompare1(TIM2,CCR_count);//单独更改通道1的CCR值
}
(2)舵机
PWM.c
#include "stm32f10x.h" // Device header
void PWM_Init(void)
{
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
GPIO_InitTypeDef GPIO_InitStructure;
TIM_OCInitTypeDef TIM_OCInitStructure;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
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_InternalClockConfig(TIM2);//也可以不写,因为定时器上电以后默认就是内部时钟
/*配置时基单元*/
TIM_TimeBaseInitStructure.TIM_ClockDivision=TIM_CKD_DIV1;//指定时钟分频,这里不进行分频
TIM_TimeBaseInitStructure.TIM_CounterMode=TIM_CounterMode_Up;
TIM_TimeBaseInitStructure.TIM_Period=20000-1;//周期,就是ARR自动重装器的值
TIM_TimeBaseInitStructure.TIM_Prescaler=72-1;//PSC预分频器的值
TIM_TimeBaseInitStructure.TIM_RepetitionCounter=0;//重复计数器的值,高级定时器才有
TIM_TimeBaseInit(TIM2,&TIM_TimeBaseInitStructure);//为将初始化的值由缓冲寄存器写入,会立刻更新一个中断,所以下面的计数会从1开始而不是0
TIM_OCStructInit(&TIM_OCInitStructure);//初始化结构体所有参数,防止修改报错
TIM_OCInitStructure.TIM_OCMode=TIM_OCMode_PWM1;//输出比较模式
TIM_OCInitStructure.TIM_OCPolarity=TIM_OCPolarity_High;//输出比较极性,High极性不翻转,Low极性翻转
TIM_OCInitStructure.TIM_OutputState=TIM_OutputState_Enable;//输出使能
TIM_OCInitStructure.TIM_Pulse=0;//CCR
TIM_OC2Init(TIM2,&TIM_OCInitStructure);
TIM_Cmd(TIM2,ENABLE);//启动定时器
}
void PWM_CCR(uint16_t CCR_count)
{
TIM_SetCompare2(TIM2,CCR_count);//单独更改通道2的CCR值
}
Servo.c
#include "stm32f10x.h" // Device header
#include "PWM.h"
float Angle;
void Servo_Init()
{
PWM_Init();
}
void Servo_Angle(float Angle)
{
PWM_CCR(Angle*2000/180+500);
}
main.c
#include "stm32f10x.h" // Device header
#include "Delay.h"
#include "OLED.h"
#include "Servo.h"
#include "Key.h"
uint8_t num;
float angle;
int main()
{
OLED_Init();
Servo_Init();
Key_Init();
OLED_ShowString(1,1,"Angle:");
while(1)
{
num=Keynum();
if(num==1)
{
angle+=30;
if(angle>180)
{
angle=0;
}
Servo_Angle(angle);
OLED_ShowNum(1,7,angle,3);
}
}
}
注
主函数中的变量定义不要与模块中的变量定义一样
PWMI模式是专门为PWM频率和占空比设计的
在一秒时间出现周期的个数就是频率
测频法适合测量高频信号,测量结果更新得慢一些(取决闸门时间),数值相对稳定,平滑,自带均值滤波,如果在闸门时间内波形频率有变化,得到的其实是闸门时间的平均频率
测周法适合测量低频信号,测量结果更新得快一些(取决与待测信号的频率),数据跳变也快,由于只测量一个周期,结果值受噪声的影响,波动较大
两种测量方法都存在正负1误差,N越大,正负1误差的影响就越小
频率>Fm:用测频法;频率<Fm:用测周法
主从触发模式
可以用于定时器的级联
四.定时器的编码器 接口