文章目录
- 前言
- 一、介绍部分
- TIM简介
- 了解定时器类型
- 基本定时器框图
- 通用定时器框图
- 高级定时器框图
- 定时器级联关系
- 所需简化定时器中断流程图
- 时序部分
- 预分频器时序
- 计数器时序
- 无影子寄存器计数器时序
- 有影子寄存器计数器时序
- 时钟树
- 二、实例部分
- 使用定时器计数
- 使用对射红外传感器来控制计数器
- 电路连接
- 代码部分
- 总结
- 所使用函数总结
- NVIC分组与优先级分配的关系
前言
简介STM32的定时器,主要连接通用定时器的用法,了解定时器中断的原理,以及如何基础的利用定时器中断
一、介绍部分
TIM简介
了解定时器类型
注意定时器的编号以及所在总线
基本定时器框图
计数器加到阈值选择中断会进入NVIC(即框图的向上的箭头),称更新中断,向下的箭头表示产生一个新的事件,触发内部其他电路工作,而不是进入中断,称更新事件。
主模式触发DAC:即通过更新时间,映射到TRGO,这样就可以不需要频繁中断来输出波形
通用定时器框图
基本定时器计数器只有向上计数
通用和高级定时器计数器有三种方法如下图:
高级定时器框图
定时器级联关系
所需简化定时器中断流程图
时序部分
预分频器时序
计数器时序
无影子寄存器计数器时序
有影子寄存器计数器时序
时钟树
二、实例部分
使用定时器计数
硬件电路只需连接STM32最小电路与OLED即可
封装定时器初始化函数Timer.c内容如下:
#include "stm32f10x.h" // Device header
void Timer_Init(void){
// 初始化时钟
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);
// 使用内部时钟(默认)
TIM_InternalClockConfig(TIM2);
// 配置事间基础(时基单元)
TIM_TimeBaseInitTypeDef TIM_InitStructure;
// 时钟分频
TIM_InitStructure.TIM_ClockDivision = TIM_CKD_DIV1;
// 计算模式
TIM_InitStructure.TIM_CounterMode = TIM_CounterMode_Up;
// 重载值(有1的偏差)<计数值> 10000/10000=1
TIM_InitStructure.TIM_Period = 10000-1;
// 预分频(有1的偏差)<频率> 72000000/7200=10000
TIM_InitStructure.TIM_Prescaler = 7200-1;
// 重复计数器(不使用)
TIM_InitStructure.TIM_RepetitionCounter = 0;
TIM_TimeBaseInit(TIM2,&TIM_InitStructure);
// 由于时基初始化后会立即进入中断一次,提前清除以下标志位
TIM_ClearFlag(TIM2,TIM_IT_Update);
// 中断使能,更新中断到NVIC
TIM_ITConfig(TIM2,TIM_IT_Update,ENABLE);
// NVIC分组
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
// 初始化NVIC
NVIC_InitTypeDef NVIC_InitStructure;
// 中断通道
NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn;
// 中断通道使能
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
// 抢占优先级
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
// 响应优先级
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2;
NVIC_Init(&NVIC_InitStructure);
// 启动定时器
TIM_Cmd(TIM2,ENABLE);
}
uint16_t Num;
uint16_t GetNum(void){
return Num;
}
//中断函数
void TIM2_IRQHandler(void){
// 获取中断标志位
if(TIM_GetITStatus(TIM2,TIM_IT_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"
int main(void)
{
OLED_Init();
Timer_Init();
OLED_ShowString(1,1,"Num:");
while (1)
{
OLED_ShowNum(2,1,GetNum(),4);
// 查看计数器的变化
OLED_ShowNum(3,1,TIM_GetCounter(TIM2),4);
}
}
使用对射红外传感器来控制计数器
红外传感器每产生10次上升沿即Num+1
电路连接
代码部分
封装定时器内容Timer.c
#include "stm32f10x.h" // Device header
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);
// 使用外部时钟模式2,不分频,中断方式,滤波
/*
@arg TIM_ExtTRGPolarity_Inverted: active low or falling edge active.
@arg TIM_ExtTRGPolarity_NonInverted: active high or rising edge active.
*/
// 使用红外传感器的上升沿作为计数器+1
TIM_ETRClockMode2Config(TIM2,TIM_ExtTRGPSC_OFF,TIM_ExtTRGPolarity_NonInverted,0x0f);
// 配置事间基础(时基单元)
TIM_TimeBaseInitTypeDef TIM_InitStructure;
// 时钟分频
TIM_InitStructure.TIM_ClockDivision = TIM_CKD_DIV1;
// 计算模式
TIM_InitStructure.TIM_CounterMode = TIM_CounterMode_Up;
// 重载值(有1的偏差)<计数值>
TIM_InitStructure.TIM_Period = 9-1;
// 预分频(有1的偏差)<频率>
TIM_InitStructure.TIM_Prescaler = 1-1;
// 重复计数器(不使用)
TIM_InitStructure.TIM_RepetitionCounter = 0;
TIM_TimeBaseInit(TIM2,&TIM_InitStructure);
// 由于时基初始化后会立即进入中断一次,提前清除以下标志位
TIM_ClearFlag(TIM2,TIM_IT_Update);
// 中断使能,更新中断到NVIC
TIM_ITConfig(TIM2,TIM_IT_Update,ENABLE);
// NVIC分组
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
// 初始化NVIC
NVIC_InitTypeDef NVIC_InitStructure;
// 中断通道
NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn;
// 中断通道使能
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
// 抢占优先级
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
// 响应优先级
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2;
NVIC_Init(&NVIC_InitStructure);
// 启动定时器
TIM_Cmd(TIM2,ENABLE);
}
uint16_t Num;
uint16_t GetNum(void){
return Num;
}
//中断函数
void TIM2_IRQHandler(void){
// 获取中断标志位
if(TIM_GetITStatus(TIM2,TIM_IT_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"
int main(void)
{
OLED_Init();
Timer_Init();
OLED_ShowString(1,1,"Num:");
OLED_ShowString(2,1,"CNT:");
while (1)
{
OLED_ShowNum(1,6,GetNum(),4);
// 查看计数器的变化
OLED_ShowNum(2,6,TIM_GetCounter(TIM2),4);
}
}
总结
所使用函数总结
// 时钟初始化(时钟分频、计数模式、重载值、分频值、重复计数器值)
void TIM_TimeBaseInit(TIM_TypeDef* TIMx, TIM_TimeBaseInitTypeDef* TIM_TimeBaseInitStruct);
// 启动定时器
void TIM_Cmd(TIM_TypeDef* TIMx, FunctionalState NewState);
// 设置外部定时器模式2
void TIM_ETRClockMode2Config(TIM_TypeDef* TIMx, uint16_t TIM_ExtTRGPrescaler,
uint16_t TIM_ExtTRGPolarity, uint16_t ExtTRGFilter);
// 使中断更新到NVIC
TIM_ITConfig(TIM2,TIM_IT_Update,ENABLE);
// 获取定时器计数器的值
uint16_t TIM_GetCounter(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);