目录
前言
本期接着上一期的内容继续学习stm32的定时器中断(上一期链接:stm32入门-----TIM定时器(定时与中断------上)-CSDN博客)本期主要分为两个项目实操,分别是内部定时器中断实现一秒计一个数,另外一个就是对射红外线传感器外部定时中断实现计数。(视频:[6-2] 定时器定时中断&定时器外部时钟_哔哩哔哩_bilibili)
一、C语言编程步骤
上图展示的就是定时器中断的过程,实际上我们编程只需要把这些箭头指向的通路打开即可。这里我选择通用定时器内部定时中断作为示例。
1.开启定时器时钟
|--------|---------------------|--------|----------------------------------------------------|
| 类型 | 编号 | 总线 | 功能 |
| 高级定时器 | TIM1、TIM8 | APB2 | 拥有通用定时器全部功能,并额外具有重复计数器、死区生成、互补输出、刹车输入等功能 |
| 通用定时器 | TIM2、TIM3、TIM4、TIM5 | APB1 | 拥有基本定时器全部功能,并额外具有内外时钟源选择、输入捕获、输出比较、编码器接口、主从触发模式等功能 |
| 基本定时器 | TIM6、TIM7 | APB1 | 拥有定时中断、主模式触发DAC的功能 |
根据上表我们可以看到通用定时器的时钟总线为APB1,所以要去打开APB1的时钟,然后去启用编号为TIM2的定时器(其他三个也是没问题的,都是通用定时器),代码示例如下:
cpp
//1.开启定时器时钟,TIM2总线是为APB1的
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);
TIM_InternalClockConfig(TIM2); //给TIM2选择时钟 为内部时钟,定时器默认是使用内部的时钟,不写这一行也行的
2.配置时基单元
这个是定时器的核心部分,配置定时器的时基单元就基本上设置好了定时的时间以及计数方式,代码示例如下:
cpp
//2.配置时基单元
TIM_TimeBaseInitTypeDef TIM_timebasestruct;
//下面两个是运行控制操作值
TIM_timebasestruct.TIM_ClockDivision=TIM_CKD_DIV1;//对输入信号进行初步分频,内部时钟72Mhz信号
TIM_timebasestruct.TIM_CounterMode=TIM_CounterMode_Up;//计数方式选择向上计数
//以下三个是时基单元里面的实际参数值
/* 计数器溢出频率:CK_CNT_OV = CK_CNT / (ARR + 1)
= CK_PSC / (PSC + 1) / (ARR + 1)
根据公式下面去配置重装值和分频器值要进行减一操作,因为在定时器计算的时候会自动加上1的,要抵
消掉*/
TIM_timebasestruct.TIM_Period=10000-1; //计数器的重装值,目标值
TIM_timebasestruct.TIM_Prescaler=7200-1; //预分频器的值
TIM_timebasestruct.TIM_RepetitionCounter=0;//重复计数功能,这个是高级计数器才有的,当前选择的是通用计数器,设置0即可
TIM_TimeBaseInit(TIM2,&TIM_timebasestruct);
TIM_ClearITPendingBit(TIM2,TIM_IT_Update);//因为TIM_TimeBaseInit初始化时基单元的时候更新了相关配置此时,就会自动取进入到中断,这里设置清除中断就行了
最后对于为什么要去清除掉这个定时中断的指示位,看下图就可以知道,因为在初始化后定时器会自动执行一次中断,这时候我们就要对这个中断指示位进行清除。
3.配置NVIC
cpp
//3.配置NVIC
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //分组
NVIC_InitTypeDef NVIC_initstruct;
NVIC_initstruct.NVIC_IRQChannel=TIM2_IRQn; //中断通道为定时器TIM2
NVIC_initstruct.NVIC_IRQChannelCmd=ENABLE; //对通道进行使能
NVIC_initstruct.NVIC_IRQChannelPreemptionPriority=2; //抢占优先级
NVIC_initstruct.NVIC_IRQChannelSubPriority=1; //响应优先级
NVIC_Init(&NVIC_initstruct);
4.开启定时器和开启定时器中断
cpp
//5.开启定时器、开启定时器中断
TIM_ITConfig(TIM2,TIM_IT_Update,ENABLE);//开启定时器中断,设置为TIM_IT_Update 更新进入中断
TIM_Cmd(TIM2,ENABLE);//开启定时器
以上就是C语言去初始化定时器的过程了,这是内部定时器中断的操作示例,要改为其他中断的话,我们只需要把中断的方式修改即可。整体代码如下:
cpp
#include "stm32f10x.h" // Device header
void Timer_init(){
//1.开启定时器时钟,TIM2总线是为APB1的
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);
TIM_InternalClockConfig(TIM2); //给TIM2选择时钟 为内部时钟,定时器默认是使用内部的时钟,不写这一行也行的
//2.配置时基单元
TIM_TimeBaseInitTypeDef TIM_timebasestruct;
//下面两个是运行控制操作值
TIM_timebasestruct.TIM_ClockDivision=TIM_CKD_DIV1;//对输入信号进行初步分频,内部时钟72Mhz信号
TIM_timebasestruct.TIM_CounterMode=TIM_CounterMode_Up;//计数方式选择向上计数
//以下三个是时基单元里面的实际参数值
/* 计数器溢出频率:CK_CNT_OV = CK_CNT / (ARR + 1)
= CK_PSC / (PSC + 1) / (ARR + 1) */
TIM_timebasestruct.TIM_Period=10000-1; //计数器的重装值,目标值
TIM_timebasestruct.TIM_Prescaler=7200-1; //预分配器的值
TIM_timebasestruct.TIM_RepetitionCounter=0;//重复计数功能,这个是高级计数器才有的,当前选择的是通用计数器,设置0即可
TIM_TimeBaseInit(TIM2,&TIM_timebasestruct);
TIM_ClearITPendingBit(TIM2,TIM_IT_Update);//因为TIM_TimeBaseInit初始化时基单元的时候更新了相关配置此时,就会自动取进入到中断,这里设置清除中断就行了
//3.配置NVIC
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //分组
NVIC_InitTypeDef NVIC_initstruct;
NVIC_initstruct.NVIC_IRQChannel=TIM2_IRQn; //中断通道为定时器TIM2
NVIC_initstruct.NVIC_IRQChannelCmd=ENABLE; //对通道进行使能
NVIC_initstruct.NVIC_IRQChannelPreemptionPriority=2; //抢占优先级
NVIC_initstruct.NVIC_IRQChannelSubPriority=1; //响应优先级
NVIC_Init(&NVIC_initstruct);
//5.开启定时器、开启定时器中断
TIM_ITConfig(TIM2,TIM_IT_Update,ENABLE);//开启定时器中断,设置为TIM_IT_Update 更新进入中断
TIM_Cmd(TIM2,ENABLE);//开启定时器
}
三、项目实操
OLED显示屏的驱动代码可在这里获取。
链接:https://pan.baidu.com/s/1yq5HwFcvqHVBlBTqzlQYxg?pwd=8985
提取码:8985
1.内部定时器中断计数
先看效果:
stm32外部定时器中断
硬件电路连接图:
很简单哈,只需要连这个oled屏幕就行了。
定时器是stm32自身有的,这里就是属于系统部分的资源,所以文件要去添加到system这边,项目工程文件如下:
C语言代码:
Timer.c文件:
cpp
#include "stm32f10x.h" // Device header
//extern uint16_t num;//跨文件变量
void Timer_init(){
//1.开启定时器时钟,TIM2总线是为APB1的
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);
TIM_InternalClockConfig(TIM2); //给TIM2选择时钟 为内部时钟,定时器默认是使用内部的时钟,不写这一行也行的
//2.配置时基单元
TIM_TimeBaseInitTypeDef TIM_timebasestruct;
//下面两个是运行控制操作值
TIM_timebasestruct.TIM_ClockDivision=TIM_CKD_DIV1;//对输入信号进行初步分频,内部时钟72Mhz信号
TIM_timebasestruct.TIM_CounterMode=TIM_CounterMode_Up;//计数方式选择向上计数
//以下三个是时基单元里面的实际参数值
/* 计数器溢出频率:CK_CNT_OV = CK_CNT / (ARR + 1)
= CK_PSC / (PSC + 1) / (ARR + 1) */
TIM_timebasestruct.TIM_Period=10000-1; //计数器的重装值,目标值
TIM_timebasestruct.TIM_Prescaler=7200-1; //预分配器的值
TIM_timebasestruct.TIM_RepetitionCounter=0;//重复计数功能,这个是高级计数器才有的,当前选择的是通用计数器,设置0即可
TIM_TimeBaseInit(TIM2,&TIM_timebasestruct);
TIM_ClearITPendingBit(TIM2,TIM_IT_Update);//因为TIM_TimeBaseInit初始化时基单元的时候更新了相关配置此时,就会自动取进入到中断,这里设置清除中断就行了
//3.配置NVIC
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //分组
NVIC_InitTypeDef NVIC_initstruct;
NVIC_initstruct.NVIC_IRQChannel=TIM2_IRQn; //中断通道为定时器TIM2
NVIC_initstruct.NVIC_IRQChannelCmd=ENABLE; //对通道进行使能
NVIC_initstruct.NVIC_IRQChannelPreemptionPriority=2; //抢占优先级
NVIC_initstruct.NVIC_IRQChannelSubPriority=1; //响应优先级
NVIC_Init(&NVIC_initstruct);
//5.开启定时器、开启定时器中断
TIM_ITConfig(TIM2,TIM_IT_Update,ENABLE);//开启定时器中断,设置为TIM_IT_Update 更新进入中断
TIM_Cmd(TIM2,ENABLE);//开启定时器
}
Timer.h文件
cpp
#ifndef __TIMER_H
#define __TIMER_H
void Timer_init();
#endif // !__TIMER_H
主函数main.c文件代码:
cpp
#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,"count:");
while(1){
OLED_ShowNum(1,7,num,3);
OLED_ShowNum(2,7,TIM_GetCounter(TIM2),5);
}
}
//定时器2中断
void TIM2_IRQHandler(){
if(TIM_GetITStatus(TIM2,TIM_IT_Update)==SET){
num++;
TIM_ClearITPendingBit(TIM2,TIM_IT_Update);
}
}
2.外部定时器中断计数
先看效果:
stm32外部定时器中断
硬件电路连接图:
工程文件同上:
Timer.c代码:
cpp
#include "stm32f10x.h" // Device header
//extern uint16_t num;//跨文件变量
void Timer_init(){
//1.开启定时器,TIM2总线是为APB1的
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);
//配置GPIOA
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
GPIO_InitTypeDef GPIO_initstruct;
GPIO_initstruct.GPIO_Mode=GPIO_Mode_IPU;
GPIO_initstruct.GPIO_Pin=GPIO_Pin_0;
GPIO_initstruct.GPIO_Speed=GPIO_Speed_50MHz;
GPIO_Init(GPIOA,&GPIO_initstruct);
// 选择外部时间输入信号
TIM_ETRClockMode2Config(TIM2,TIM_ExtTRGPSC_OFF,TIM_ExtTRGPolarity_NonInverted,0x0F);
//2.配置时基单元
TIM_TimeBaseInitTypeDef TIM_timebasestruct;
//下面两个是运行控制操作值
TIM_timebasestruct.TIM_ClockDivision=TIM_CKD_DIV1;//对输入信号进行初步分频,内部时钟72Mhz信号,这里就没有分频
TIM_timebasestruct.TIM_CounterMode=TIM_CounterMode_Up;//计数方式选择向上计数
//以下三个是时基单元里面的实际参数值
/* 计数器溢出频率:CK_CNT_OV = CK_CNT / (ARR + 1)
= CK_PSC / (PSC + 1) / (ARR + 1) */
TIM_timebasestruct.TIM_Period=10-1; //计数器的重装值,目标值
TIM_timebasestruct.TIM_Prescaler=1-1; //预分频器的值
TIM_timebasestruct.TIM_RepetitionCounter=0;//这个是高级计数器才有的,当前选择的是通用计数器,设置0即可
TIM_TimeBaseInit(TIM2,&TIM_timebasestruct);
TIM_ClearITPendingBit(TIM2,TIM_IT_Update);//因为TIM_TimeBaseInit初始化时基单元的时候更新了相关配置此时,就会自动取进入到中断,这里设置清除中断就行了
//3.计数完成后使能中断,开启中断到NVIC的通路
//4.配置NVIC
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
NVIC_InitTypeDef NVIC_initstruct;
NVIC_initstruct.NVIC_IRQChannel=TIM2_IRQn;
NVIC_initstruct.NVIC_IRQChannelCmd=ENABLE;
NVIC_initstruct.NVIC_IRQChannelPreemptionPriority=2;
NVIC_initstruct.NVIC_IRQChannelSubPriority=1;
NVIC_Init(&NVIC_initstruct);
//5.开启定时器、开启定时器中断
TIM_ITConfig(TIM2,TIM_IT_Update,ENABLE);//开启定时器中断,设置为TIM_IT_Update 更新进入中断
TIM_Cmd(TIM2,ENABLE);//开启定时器
}
//返回定时器时钟输入的值
uint16_t Timer_Getcounter(){
return TIM_GetCounter(TIM2);
}
Timer.h代码:
cpp
#ifndef __TIMER_H
#define __TIMER_H
void Timer_init();
uint16_t Timer_Getcounter();
#endif // !__TIMER_H
主函数main.c代码:
cpp
#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,"count:");
OLED_ShowString(2,1,"CNT:");
while(1){
OLED_ShowNum(1,7,num,3);
OLED_ShowNum(2,7,Timer_Getcounter(),5);
}
}
//定时器2中断
void TIM2_IRQHandler(){
if(TIM_GetITStatus(TIM2,TIM_IT_Update)==SET){
num++;
TIM_ClearITPendingBit(TIM2,TIM_IT_Update);
}
}
以上就是本期的全部内容了,我们下次见!
每日壁纸: