用到的TIM配置函数
-
TIM_TimeBaseInit();时基单元初始化
-
TIM_TimeBaseStructInit();给结构体变量赋一个默认值
-
TIM_Cmd();使能计数器
-
TIM_ITConfig();使能中断输出控制
///时钟源选择用这里的六个参数
-
TIM_InternalClockConfig();选择内部时钟
-
TIM_ITRxExternalClockConfig();选择ITRx其他定时器的时钟
-
TIM_TIxExternalClockConfig();选择TIx捕获通道的时钟
-
TIM_ETRClockMode1Config();选择ETR通过外部时钟模式1输入的时钟
-
TIM_ETRClockMode2Config(); 选择ETR通过外部时钟模式2输入的时钟
-
TIM_ETRConfig();配置ETR引脚的预分频器、极性、滤波器等参数
-
TIM_PrescalerConfig();用来单独写预分频值
-
TIM_CounterModeConfig();用来改变计数器的计数模式
-
TIM_ARRPreloadConfig();自动重装器预装功能配置
-
TIM_SetCounter();手动写一个计数值
-
TIM_SetAutoreload(); 手动写一个自动重装值
-
TIM_GetCounter();获取当前计数器的值
-
TIM_GetPrescaler();获取当前预分频器的值
编写TIM定时器定时中断代码:以【定时器定时中断】为例
初始化定时器时从左至右参考该图流程
1、完成驱动文件导入操作和编写驱动程序基本代码(参考之前文章)
//TIM定时器不涉及外部硬件,因此将配置文件放在【System】文件夹里
2、在Timer.c中初始化函数
Timer_Init
void Timer_Init(void){//第一步:开启时钟(注意使用APB1的开启时钟函数,因为TIM2是APB1总线的外设RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);//第二部:选择时基单元的时钟源(在stm32f10x_tim.h文件中查找函数)TIM_InternalClockConfig(TIM2);//对于定时中断,这里选择为内部时钟源//时基单元就由内部时钟驱动了//第三步:配置时基单元(在stm32f10x_tim.h文件中查找函数)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自动重新加载寄存器中的值//Period【周期】 //ARR取值[0,65535]TIM_TimeBaseInitStructure.TIM_Prescaler = 7200 - 1;//指定用于划分TIM时钟的预分频器值 PSC取值[0,65535]TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;//指定重复计数器的值(高级定时器才用得上,本项目给0)TIM_TimeBaseInit(TIM2,&TIM_TimeBaseInitStructure);//初始化时基单元//TIM_TimeBaseInit会手动生成一个更新事件,导致刚一上电就立刻进入中断(现象:Num一开始不是00000而是00001)TIM_ClearFlag(TIM2,TIM_FLAG_Update);//手动清除更新标志位,避免刚初始化完成就进中断//第四步:配置输出中断控制,使能更新中断输出到NVIC(在stm32f10x_tim.h文件中查找函数)TIM_ITConfig(TIM2,TIM_IT_Update,ENABLE);//开启了更新中断到NVIC的通路//第五步:配置NVIC,在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 = 2;//指定IRQ通道的抢占优先级NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;//指定IRQ通道的响应优先级NVIC_Init(&NVIC_InitStructure);//第六步:启动定时器(在stm32f10x_tim.h文件中查找函数)TIM_Cmd(TIM2,ENABLE);//至此定时器就可以开始工作了,当产生更新时,就会触发中断}
3、在Timer.c中编写中断服务程序函数
TIM2_IRQHandler
在STM32中,中断函数的名字都是固定的,每个中断通道都对应一个中断函数(名字参考启动文件startup_stm32f10x_md.s)
文件中以
IRQHandler结尾的字符串就是中断函数的名字
中断函数都是无参无返回值的
void TIM2_IRQHandler(void)//当定时器产生更新中断时,这个函数就会被自动执行{if(TIM_GetITStatus(TIM2,TIM_IT_Update) == SET)//检查中断标志位{Num ++;TIM_ClearITPendingBit(TIM2,TIM_IT_Update);//清除中断标志位}}
4、实现
定时器每秒自动加Num功能
//在maic.c文件中定义一个全局变量uint16_t Num//中断函数是在Timer模块里,如果在中断函数里写Num++,那么Num就是跨越不同.c文件的变量了-----------------解决方法一如果想跨文件使用变量,可以在使用变量的那个文件顶部用【extern】声明要用的变量extern uint16_t Num;extern声明变量就是告诉编译器我现在有Num这个变量,请求编译器帮忙在全文件中查找-----------------解决方法二直接将Timer.c文件中的中断服务程序函数TIM2_IRQHandler放在main.c的主函数外定义使用,注释掉原来Timer.c的中断函数//对于定时器而言,这个中断函数就是为别的文件服务的,所以中断函数可以放在使用它的地方
5、在Timer.h中声明初始化函数
Timer_In
it
void Timer_Init(void);//中断函数不用声明,因为中断函数不需要调用,会自动执行
6、在主程序main.c中
#include "Timer.h"
#include "Timer.h"
7、在主循环之前先初始化外部中断
8、在主循环中编写程序主体
int main(void){OLED_Init();Timer_Init();OLED_ShowString(1,1,"Num:");while(1){OLED_ShowNum(1,5,Num,5);OLED_ShowNum(2,5,TIM_GetCounter(TIM2),5);//观察CNT计数器的变化情况}}void TIM2_IRQHandler(void)//当定时器产生更新中断时,这个函数就会被自动执行{if(TIM_GetITStatus(TIM2,TIM_IT_Update) == SET)//检查中断标志位{Num ++;TIM_ClearITPendingBit(TIM2,TIM_IT_Update);//清除中断标志位}}
实现功能:上电后OLED显示从0每隔一秒计数Num+1,CNT显示计数器值