一、介绍
也就是定一个时间,每隔一段时间触发一次中断,然后去处理事情
基本定时器:
下边三个构成了时基单元(基本计数计时单元){时钟72M(将72M(时间就是计72个就是1us)计72000也就是1ms}
- 内部时钟首先到PSC预分频器,然后到CNT进行计数,当定时器计数值等与自动重装载的值相等时就会产生一个UI或U,然后NVIC进行通道设置后,CPU就可以相应更新中断了
PSC:预分频器的时钟分割操作,PSC写1就是2分频就是36M,写3就是4分频就是18M;{PSC是16位的,因此最大可以65535就是65536分频}))
CNT计数器:每来一个脉冲,计数值加一,16位所以最大到65535,再加重新从0开始,
自动重装载寄存器:也就是设定的目标时间,当CNT记到这个时间之后就会产生一个中断,然后会清除计数器,开始下一次计数,
UI(更新中断):指定时器计数值等与自动重装载的事件产生的中断,指向NVIC,配置好NVIC通道后,就能得到CPU的响应了。
U(更新事件)
主从DAC(通过U触发扳机(TRGO)进行 DAC采样,避免重复使用中断)
通用计时器还支持 1向下计数和中央对齐计数,2 外部时钟
二、时序电路
预分频时序
计数器时序
时钟树
三、实战 1
1、目标:
每隔一秒计数1,在oled上显示
2、硬件:仅需要一个oled,
3、软件:
(1)在系统文件夹编写库文件.c .h文件
.h:
#ifndef __TIMER_H
#define __TIMER_H
void Timer_Init(void);
#endif
(2)根据下边结构图,完.c文件
第一步,RCC开启时钟(这里选择TIM2通用定时器在APB1总线上)
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
第二步,选择时基单元的时钟源(这里选择内部时钟)不写也行,默认为内部时钟
TIM_InternalClockConfig(TIM2);
第三步,配置时基单元(包括预分频器、自动重装器、计数模式)
对TIM2定时器的初始化和配置,用于生成定时中断。通过设置计数器的周期值和预分频器的值,可以确定定时器的计时周期和频率。通过使能更新中断,可以在计数器溢出时触发中断事件。
TIM_InternalClockConfig(TIM2); // 配置TIM2为内部时钟模式,使用内部时钟源
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure; // 定义TIM_TimeBaseInitStructure结构体,用于配置TIM的基本参数
TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1; // 设置时钟分频系数为1,不分频
TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up; // 设置计数器为向上计数模式
TIM_TimeBaseInitStructure.TIM_Period = 10000 - 1; // 设置计数器的周期值为10000,即计数器从0到9999
TIM_TimeBaseInitStructure.TIM_Prescaler = 7200 - 1; // 设置预分频器的值为7200-1,将输入时钟频率分频为1MHz
TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0; // 设置重复计数器的值为0,不使用重复计数功能
TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStructure); // 根据TIM_TimeBaseInitStructure的配置初始化TIM2的时间基准参数
TIM_ClearFlag(TIM2, TIM_FLAG_Update); // 清除TIM2的更新标志位
TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE); // 使能TIM2的更新中断,当计数器溢出时会触发更新中断
第四步,配置输出中断控制,允许更新中断输出到NVIC
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); // 配置中断优先级分组为2,即4位抢占优先级和0位响应优先级
第五步,配置NVIC,在NVIC中打开定时器中断的通道,并分配一个优先级
对中断控制器(NVIC)的初始化和配置,用于使能TIM2定时器的中断。通过设置中断通道、抢占优先级和响应优先级,可以确定中断的优先级顺序。通过使能中断通道,可以在满足触发条件时触发相应的中断事件。最后,通过使能TIM2定时器,开始计数。
NVIC_InitTypeDef NVIC_InitStructure; // 定义NVIC_InitStructure结构体,用于配置NVIC的参数
NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn; // 设置中断通道为TIM2_IRQn,即TIM2的中断通道
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; // 使能TIM2的中断通道
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2; // 设置抢占优先级为2
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1; // 设置响应优先级为1
NVIC_Init(&NVIC_InitStructure); // 根据NVIC_InitStructure的配置初始化NVIC
TIM_Cmd(TIM2, ENABLE); // 使能TIM2定时器,开始计数
以上5步初始化结束
------------------------------------------------------------------------------------------------------------------------------
第六步,中断函数(找到定时器2的中断函数)
void TIM2_IRQHandler(void)
{
if (TIM_GetITStatus(TIM2, TIM_IT_Update) == SET)
{
// 检查是否发生了TIM2的更新中断
// 如果更新中断已经发生
// 进入下面的代码块
TIM_ClearITPendingBit(TIM2, TIM_IT_Update);
// 清除TIM2的更新中断标志位
// 这样可以重置中断状态,以允许下一次中断发生
}
}
中断处理函数,它用于处理TIM2定时器的更新中断。首先,它检查TIM2的更新中断标志位是否被设置(即中断是否发生)。如果更新中断已经发生,它会执行清除中断标志位的操作,以允许下一次中断触发。这是一个典型的中断处理程序,用于确保及时响应和处理特定的中断事件。
第七步、主函数
#include "stm32f10x.h" // 引入STM32标准库的头文件
#include "Delay.h" // 引入延迟函数的头文件
#include "OLED.h" // 引入OLED显示库的头文件
#include "Timer.h" // 引入定时器库的头文件
uint16_t Num; // 声明一个无符号16位整数变量Num
int main(void)
{
OLED_Init(); // 初始化OLED显示屏
Timer_Init(); // 初始化定时器
OLED_ShowString(1, 1, "Num:"); // 在OLED上显示字符串 "Num:"
while (1)
{
OLED_ShowNum(1, 5, Num, 5); // 在OLED上显示变量Num的值
}
}
void TIM2_IRQHandler(void)
{
if (TIM_GetITStatus(TIM2, TIM_IT_Update) == SET)
{
Num++; // 如果TIM2的更新中断发生,增加Num的值
TIM_ClearITPendingBit(TIM2, TIM_IT_Update); // 清除TIM2的更新中断标志位,以准备下一次中断
}
}
四、实战2 定时器外部时钟
目标:将外部时钟信号来触发事件,在oled上显示事件变化(这里采用对射式红外传感器作为时钟产生装置,当触发时,产生一个时钟被捕获)
硬件:
DO接PA0
软件思路;
就是将实战1里边的.c文件中将选择为内部时钟改为选择外部时钟
TIM_ETRClockMode2Config(TIM2, TIM_ExtTRGPSC_OFF, TIM_ExtTRGPolarity_NonInverted, 0x0F);
GPIO引脚初始化
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);
更改自动重装值,因为手动触发开关,所以不需要分频来一次算一次就行,自动重装就设置9,从0-9之后就重置 ,其他的和内部时钟的一样
TIM_TimeBaseInitStructure.TIM_Period = 10 - 1;
TIM_TimeBaseInitStructure.TIM_Prescaler = 1 - 1;
观察显示CNT函数 (记得外部声明)
uint16_t Timer_GetCounter(void)
{
return TIM_GetCounter(TIM2);
}
主函数
#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);
}
}
现象:每次触发对射管,CNT数值加一,到9后清零,申请中断num++