1.实验现象如下:
现象:OLED显示Num,每秒数字+1.原理:用定时中断,定时器使用内部时钟定1s的时钟,每隔1s申请中断,自动++,显示Num。
2.代码编写思路:
解释:步骤大致如上图,解释如下:1.RCC开启时钟+2.选择时基单元的时钟源(定时中断选择的是内部时钟源)+3.配置时基单元(包括预分频器+自动重装器+计数模式等,代码中用结构体配置)+4.配置输出中断控制+5.配置NVIC(在NVIC中打开定时器中断通道,分配一个优先级)+6.运行控制+7.中断函数
q:1483078351 v:15136037805, git为@qq1483078351a 已开源,里面有课件和代码。有问题欢迎询问,共同进步.
3.代码部分:
定时器定时代码部分(Timer.c):
#include "stm32f10x.h" // Device header void Timer_Init(void)//初始化定时器---过程查看定时器中断基本结构. //在Library中tim.h定义许多库函数,需要充分学习和使用,此处写几个用到的,重要的 { RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);//第一步:开启APB1时钟,因为TIM2是APB1总线的外设 TIM_InternalClockConfig(TIM2);//第二步:选择时基单元的时钟(此处选的是内部时钟) //第三步:配置时基单元----TIM_TimeBaseInit的第二个参数是结构体,配置结构体 TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure; TIM_TimeBaseInitStructure.TIM_ClockDivision=TIM_CKD_DIV1; //TIM_CKD_DIV1,1分频,不分配;TIM_CKD_DIV2,2分频;TIM_CKD_DIV4,4分频----要求不高时,随意选择 TIM_TimeBaseInitStructure.TIM_CounterMode=TIM_CounterMode_Up; //TIM_CounterMode_Up向上计数;TIM_CounterMode_Down向下计数;TIM_CounterMode_CenterAligned1等为中央对齐 TIM_TimeBaseInitStructure.TIM_Period=10000-1;//资源自动重装器的值,在10K频率下记10K个数,即1s一个。 TIM_TimeBaseInitStructure.TIM_Prescaler=7200-1;//预分频,对72M进行7200分频 TIM_TimeBaseInitStructure.TIM_RepetitionCounter=0;//高级定时器用到,通用定时器选0 TIM_TimeBaseInit(TIM2,&TIM_TimeBaseInitStructure);//结构体 //第四步:使能中断 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); }
Time.h部分:
#ifndef __TIMER_H #define __TIMER_H void Timer_Init(void); #endif
主函数(main.c)部分:
#include "Timer.h" uint16_t Num; int main(void) { OLED_Init(); Timer_Init(); OLED_ShowString(1, 1, "Num:");//在第一行第三列开始显示HelloWorld!, while(1) { OLED_ShowNum(1,5,Num,5); } } //中断函数:当进入中断,自动执行该函数 void TIM2_IRQHandler(void) { if(TIM_GetITStatus(TIM2,TIM_IT_Update)==SET)//获取中断标志位 { Num++; TIM_ClearITPendingBit(TIM2,TIM_IT_Update);//清除标志位 } }
4.定时器常用库函数详解:
以下内容均为需要掌握的Timer里的函数部分,我以注释的形式一点一点打出来,包含函数解释和参数的使用选择。大家没事多看看,多用用。
//TIM_DeInit(TIM_TypeDef* TIMx);//恢复缺省配置 //时基单元:(第三部分) //void TIM_TimeBaseInit(TIM_TypeDef* TIMx, TIM_TimeBaseInitTypeDef* TIM_TimeBaseInitStruct);//时基单元初始化 //配置时基单元,参数1:TIMx选择某个定时器,参数2:TIM_TimeBaseInitStruct结构体,包含配置时基单元的参数 //void TIM_TimeBaseStructInit(TIM_TimeBaseInitTypeDef* TIM_TimeBaseInitStruct);//把结构体变量附一个默认值 //运行控制:(第六部分) //void TIM_Cmd(TIM_TypeDef* TIMx, FunctionalState NewState);//使能计数器。即第六步的运行控制 //参数一:TIMx选择某个定时器,参数2:NewState选择使能还是失能。使能时计数器可以运行,失能不能运行 //中断输出控制:(第四部分) //void TIM_ITConfig(TIM_TypeDef* TIMx, uint16_t TIM_IT, FunctionalState NewState); //使能中断输出信号,即第四步的中断输出控制。 //参数一:TIMx选择某个定时器。参数2:TIM_IT选择要配置哪个中断输出。参数3:NewState选择使能还是失能。 //以下六个函数对应时基单元的时钟源选择部分(第一部分) //void TIM_InternalClockConfig(TIM_TypeDef* TIMx);//选择内部时钟。 //只有一个参数,即RCC内部时钟接外部时钟模式2进入时基单元 //void TIM_ITRxExternalClockConfig(TIM_TypeDef* TIMx, uint16_t TIM_InputTriggerSource);//选择ITR其他定时器的时钟 //参数一:TIMx选择某个定时器。参数2:TIM_InputTriggerSource选择要接入哪个其他的定时器。 //即ITRX接外部时钟模式1进入时基单元 //void TIM_TIxExternalClockConfig(TIM_TypeDef* TIMx, uint16_t TIM_TIxExternalCLKSource, // uint16_t TIM_ICPolarity, uint16_t ICFilter);//选择TIx捕获通道的时钟。 //参数一:TIMx选择某个定时器。参数2:TIM_TIxExternalCLKSource选择TIx具体的某个引脚。 //参数3:TIM_ICPolarity输入极性。参数4:ICFilter输入的滤波器。 //即TIx接外部时钟模式1进入时基单元. //void TIM_ETRClockMode1Config(TIM_TypeDef* TIMx, uint16_t TIM_ExtTRGPrescaler, uint16_t TIM_ExtTRGPolarity, // uint16_t ExtTRGFilter);//选择ETR通过外部时钟模式1输入时钟。 //参数2:TIM_ExtTRGPrescaler对ETR的外部时钟再做一次分频。 //参数3:TIM_ExtTRGPolarity极性。参数4:ExtTRGFilter滤波器。 //即ETR接外部时钟模式1进入时基单元. //void TIM_ETRClockMode2Config(TIM_TypeDef* TIMx, uint16_t TIM_ExtTRGPrescaler, // uint16_t TIM_ExtTRGPolarity, uint16_t ExtTRGFilter); //与上个函数参数一样,用法相似选择ETR通过外部时钟模式1输入时钟。 //void TIM_ETRConfig(TIM_TypeDef* TIMx, uint16_t TIM_ExtTRGPrescaler, uint16_t TIM_ExtTRGPolarity, // uint16_t ExtTRGFilter);//与上两个函数相似,不再赘述 //以下函数为特定场合使用函数 //void TIM_PrescalerConfig(TIM_TypeDef* TIMx, uint16_t Prescaler, uint16_t TIM_PSCReloadMode); //方便改写预分频值。参数一:Prescaler要写入的预分频值。参数2:TIM_PSCReloadMode写入的模式, //此处可选择在更新事件生效还是再写入后手动产生一个更新事件,让这个值立即生效。 //void TIM_CounterModeConfig(TIM_TypeDef* TIMx, uint16_t TIM_CounterMode);//改变计算器的计数模式 //参数2:TIM_CounterMode选择新的计数器模式。 //void TIM_ARRPreloadConfig(TIM_TypeDef* TIMx, FunctionalState NewState);//自动重装器预装功能配置 //有预装还是无预装可选择,NewState使能还是失能。 //void TIM_SetCounter(TIM_TypeDef* TIMx, uint16_t Counter);//给计数器写入一个值,手动给计数器一个值时使用 //void TIM_SetAutoreload(TIM_TypeDef* TIMx, uint16_t Autoreload); //给自动重装器写入一个值,手动给自动重装器一个值时使用 //uint16_t TIM_GetCounter(TIM_TypeDef* TIMx);//获取当前计数器的值 //查看当前计数器记到哪里时调用该函数。返回值就是当前计数器的值 //uint16_t TIM_GetPrescaler(TIM_TypeDef* TIMx);//获取当前预分频器的值 //查看当前预分频器记到哪里时调用该函数。返回值就是当前计数器的值