目录
一、TIM(Timer)定时器简介
- 定时器可以对输入的时钟进行计数,并在计数值达到设定值时触发中断。
- 16位计数器、预分频器、自动重装寄存器的时基单元,在72MHz计数时钟下可以实现最大59.65s的定时。此处计算:2^16=65536 , 65536×65536÷72M=59.65s
原理参见基本定时器框图部分- STM32的定时器支持级联,可以用一个计时器的输出,当做另一个计时器的输入
- 还包含内外时钟源选择、输入捕获、输出比较、编码器接口、主从触发模式等多种功能
1.1 定时器类型
摘要
- STM32F103C8T6定时器资源有:TIM1、TIM2、TIM3、TIM4
1.1.1 基本定时器
- 这里的预分频器是16位的,最大值可以写65535,也就是65536分频;
- 这里的计数器也是16位的,可以从0一直加到65535
- 自动重装寄存器也是16位的,存的是我们写入的计数目标,当计数值等于计数目标时,会产生一个更新信号,并清零计数器,计数器自动开始下一轮的计数计时
- 基本定时器只能选择内部时钟
1.1.2 通用定时器
- 基本定时器只能向上自增计数
- 通用定时器支持向上计数、向下计数和中央对齐计数三种模式
- 通用定时器可以通过TIMx_ETR选择外部时钟
- 通用定时器可以通过ITR0,1,2,3,与其他定时器实现级联,足以丈量宇宙
- TIMx_CH1,2,3,4 用于输入捕获或输出比较,引脚共用
1.1.3 高级定时器
- 高级定时器的重复次数定时器相当于对输出更新信号又做了一次分频,提升了定时时间
- DTG是死区生成电路,防止互补输出的PWM在开关切换瞬间短暂的直通现象
- 输出引脚变为互补输出,可以输出一对互补的PWM波,用于驱动三相无刷电机
- 刹车输入功能
1.2 定时中断基本结构
1.2.1 结构框图
- 左边框图是可选择的时钟源
- 时基单元:由预分频器、计数器、自动重装器构成,高级定时器还多一个重复计数器
- 运行控制:控制寄存器,用来设置运行启动、停止、向上或向下计数等。
- 中断输出控制:有很多地方会申请中断,如果需要就允许,不需要的就禁止。
1.2.2 时序图
- 预分频器时序
- 计数器计数频率:CK_CNT = CK_PSC / (PSC + 1)
- 计数器溢出频率:CK_CNT_OV = CK_CNT / (ARR + 1)
=CK_PSC / (PSC + 1) / (ARR + 1)
二、定时器定时中断&定时器外部时钟
2.1 内部时钟闹钟代码
2.1.1 Timer.c
- 第一步:RCC开启给TIM2的时钟
- 第二步:TIM2的时基单元选择时钟来源为内部时钟
- 前两步相当于双重开关,一个选择给谁;另一个选择接受谁。
- 第三步:配置时基单元
- 第四步:中断输出控制:选择中断源为TIM2的update中断,使能输出
- 第五步:配置NVIC
- 最后不要忘了给TIM2使能
#include "stm32f10x.h" // Device header
//时间hh:mm:ss
uint32_t hh = 12 ;
uint32_t mm = 43 ;
uint32_t ss = 07 ;
//设置闹钟时间
uint32_t h = 0 ;
uint32_t m = 0 ;
uint8_t flag = 0 ; //闹钟复位标记
void Timer_Init(void)
{
//RCC开启时钟给TIM2
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);
//TIM2时基单元选择时钟来源为内部时钟
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; //ARR自动重装器的值
TIM_TimeBaseInitStructure.TIM_Prescaler = 7200-1; //预分频器的值
TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0; //重复计数器的值
TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStructure);
TIM_ClearFlag(TIM2,TIM_FLAG_Update); //清除更新中断标志位
//选择中断源为TIM2的update,使能输出
TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE);
//NVIC优先级分组
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
NVIC_InitTypeDef NVIC_InitStructure;
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()
{
//用取整取余的方式计算时间
ss= 3600*hh + 60*mm + ss;
if(TIM_GetITStatus(TIM2,TIM_IT_Update) == SET)
{
ss++;
if(ss == 86400)
{
ss = 0;
}
uint32_t i = ss % 3600 ;
uint32_t j = i % 60 ;
hh = ss / 3600;
mm = i / 60;
ss = j;
TIM_ClearITPendingBit(TIM2,TIM_IT_Update);
}
//if嵌套的方式进位
// if(TIM_GetITStatus(TIM2,TIM_IT_Update) == SET)
// {
// ss++;
// while(ss == 60)
// {
// mm++;
// ss = 0;
// while(mm == 60)
// {
// hh++;
// mm = 0;
// while(hh == 24)
// {
// hh = 0;
// }
// }
// }
// TIM_ClearITPendingBit(TIM2,TIM_IT_Update);
// }
}
2.1.2 Buzzer.c加入间隔发声函数
//滴滴~~
void Buzzer_Spaced(void)
{
GPIO_ResetBits(GPIOB, GPIO_Pin_12);
Delay_ms(150);
GPIO_SetBits(GPIOB, GPIO_Pin_12);
Delay_ms(150);
GPIO_ResetBits(GPIOB, GPIO_Pin_12);
Delay_ms(150);
GPIO_SetBits(GPIOB, GPIO_Pin_12);
Delay_ms(600);
}
2.1.3 main.c
#include "stm32f10x.h" // Device header
#include "OLED.h"
#include "Timer.h"
#include "Buzzer.h"
extern uint32_t h ;
extern uint32_t m ;
extern uint8_t flag ;
extern uint32_t hh ;
extern uint32_t mm ;
extern uint32_t ss ;
int main(void)
{
OLED_Init();
Timer_Init();
Buzzer_Init();
OLED_ShowString(1,5,"T I M E");
OLED_ShowString(2,4,":");
OLED_ShowString(2,7,":");
OLED_ShowString(2,10,":");
while(1)
{
//时间显示
OLED_ShowNum(2,8,ss,2);
OLED_ShowNum(2,5,mm,2);
OLED_ShowNum(2,2,hh,2);
OLED_ShowNum(2,11,TIM_GetCounter(TIM2),4);
//闹钟开启
if(hh == h && mm == m && flag == 0)
{
Buzzer_Spaced();
}
//闹钟关闭,可定义变量方便更改时间长度
if(hh == h && mm == m && ss == 3 )
{
Buzzer_OFF();
flag = 1;
}
//flag标志复位
if(hh == h && mm != m && flag == 1)
{
flag = 0;
}
}
}
2.1.4 实验视频
Alarm Clock
2.1.5 bug调试
- 现象:初始状态未从0开始
- 归因:影子寄存器需要更新事件才能起作用,为了让其立刻生效,TIM_TimeBaseInit()在最后立即生成一个更新事件。由于更新中断和更新事件是同时发生的,所以同时将更新中断置标志位,初始化结束后立即触发了更新中断。
- 解决方案:在更新中断使能前清除更新中断标志位
- 现象:hh:mm:ss三个数据同时触发跳变,跳变不同步有延迟,但影响不大
- 归因:怀疑时间进位时多重if嵌套拖慢进程,改为取整取余计算时间,无任何变化。
更改三个数据OLED显示顺序,延迟数据有变化,但仍无法同步。- 解决方案:暂时未解决,可能需要修改OLED部分代码
2.2 定时器外部时钟
- 简介:通过对射式红外传感器手动计数
2.2.1 Timer.c
#include "stm32f10x.h" // Device header
extern uint16_t Num ;
void Timer_Init(void)
{
//开启时钟
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
GPIO_InitTypeDef GPIO_InitStructure;
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);
//TIM2选择外部时钟模式2,可设置预分频,极性,滤波
TIM_ETRClockMode2Config(TIM2,TIM_ExtTRGPSC_OFF,TIM_ExtTRGPolarity_NonInverted,0x0F);
//时基单元配置
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;
TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInitStructure.TIM_Period = 10-1;
TIM_TimeBaseInitStructure.TIM_Prescaler = 1-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
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
NVIC_InitTypeDef NVIC_InitStructure;
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);
}
2.2.2 main.c
#include "stm32f10x.h" // Device header
#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,TIM_GetCounter(TIM2),5);
}
}
void TIM2_IRQHandler()
{
Num++;
TIM_ClearITPendingBit(TIM2,TIM_IT_Update);
}
- 记得在中断函数最后清除中断标志位
2.2.3 bug调试
- 现象:遮挡一次,计数值跳变多次
- 归因:传感器电平抖动导致,尝试判断中断是否来自TIM2的update、变更外部时钟预分频都不稳定。
- 解决方案:变更外部时钟滤波方案为0x0f,实测稳定。