下面这张图为我们初始化定时器的顺序图:

以下为我们使用定时器时,主要会用到的函数:

void TIM_DeInit(TIM_TypeDef* TIMx);//恢复缺损配置

 

void TIM_TimeBaseInit(TIM_TypeDef* TIMx, TIM_TimeBaseInitTypeDef* TIM_TimeBaseInitStruct);//用来配置时基单元

TIMx:选择某个定时器、TIM_TimeBaseInitStruct:结构体包含配置时基单元的参数

 

void TIM_TimeBaseStructInit(TIM_TimeBaseInitTypeDef* TIM_TimeBaseInitStruct);//可以把结构体的变量赋为一个默认值

 

void TIM_Cmd(TIM_TypeDef* TIMx, FunctionalState NewState);//用来使能计数器,对应上图的运行控制

TIMx:选择定时器、NewState:对计数器使能或者失能

 

void TIM_ITConfig(TIM_TypeDef* TIMx, uint16_t TIM_IT, FunctionalState NewState);//用来使能中断输出信号,对应上图的中断输出控制

TIMx:选择定时器、 TIM_IT:选择配置哪个终端输出、NewState:选择使能或失能

 

以下函数对应上图时基单元的时钟选择部分

void TIM_InternalClockConfig(TIM_TypeDef* TIMx);//选择内部时钟
void TIM_ITRxExternalClockConfig(TIM_TypeDef* TIMx, uint16_t TIM_InputTriggerSource);//选择ITRx其他定时器的时钟,在上图对应的就是从ITRx其他定时器连接到外部时钟模式1再连入时基单元
void TIM_TIxExternalClockConfig(TIM_TypeDef* TIMx, uint16_t TIM_TIxExternalCLKSource,
uint16_t TIM_ICPolarity, uint16_t ICFilter);//选择TIx捕获通道的时钟,TIM_TIxExternalCLKSource:选择TIx具体某个引脚,在上图对应就是从TIx捕获通道连到外部时钟模式1再连入时基单元
void TIM_ETRClockMode1Config(TIM_TypeDef* TIMx, uint16_t TIM_ExtTRGPrescaler, uint16_t TIM_ExtTRGPolarity,
uint16_t ExtTRGFilter);//选择ETR通过外部时钟模式1输入,TIM_ExtTRGPrescaler:外部触发预分频器,可以对ETR的外部时钟再提前做一个分频
void TIM_ETRClockMode2Config(TIM_TypeDef* TIMx, uint16_t TIM_ExtTRGPrescaler,
uint16_t TIM_ExtTRGPolarity, uint16_t ExtTRGFilter);//选择ETR通过外部时钟模式2输入
void TIM_ETRConfig(TIM_TypeDef* TIMx, uint16_t TIM_ExtTRGPrescaler, uint16_t TIM_ExtTRGPolarity,
uint16_t ExtTRGFilter);//单独用来配置ETR引脚的预分频器、极性、滤波器参数

 

void TIM_PrescalerConfig(TIM_TypeDef* TIMx, uint16_t Prescaler, uint16_t TIM_PSCReloadMode);//单独用来填写预分频值

Prescaler:写入的预分频值、TIM_PSCReloadMode:写入的模式,可以等待下一次更新事件后生效,还是手动产生一个更新事件,使其立即生效

 

void TIM_CounterModeConfig(TIM_TypeDef* TIMx, uint16_t TIM_CounterMode);//改变计数器的技术模式

 

void TIM_ARRPreloadConfig(TIM_TypeDef* TIMx, FunctionalState 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);//获取当前预分频器的值

 

预分频器有一个缓冲寄存器,我们写的值只有在更新事件时,才会真正的起作用,所以这里为了让值立刻起作用,就会手动生成一个更新事件这样预分频器的值就有效了,但同时,副作用就是,更新事件和更新中断是同时发生的,更新中断会置更新中断标志位,当我们一旦初始化完了,更新中断就会立刻进入,这就是我们刚一上点就立刻进中断的原因。解决方法就是TimeBase设定预分频器的后面,开启中断的前面,手动调用一下TIM_ClearFlag。清楚中断标志位。

首先是依靠内部时钟来使用定时器,由上图的顺序来我们可以写出代码:

#include "stm32f10x.h"                  // Device header
void Timer_Init(void)
{
 RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);//使能内部时钟
 TIM_InternalClockConfig(TIM2);//定时器默认使用内部时钟,这句不写也能通过编译
 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自动重装器的值
 TIM_TimeBaseInitStructure.TIM_Prescaler = 7200 - 1;//预分频数
 TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;//重复计数器,只有高级计数器会用
 TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStructure);//时基单元初始化
 TIM_ClearFlag(TIM2, TIM_FLAG_Update);
 TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE);//使能更新中断
 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);
}
/*以下为中断函数,为了使用方便,可以直接复制粘贴到主函数文件中主函数后使用
void TIM2_IRQHandler(void)
{
if (TIM_GetITStatus(TIM2, TIM_IT_Update) == SET)
{

TIM_ClearITPendingBit(TIM2, TIM_IT_Update);
}
}
*/

代码中在时基单元初始化后,使能更新中断之前有一句TIM_ClearFlag(TIM2, TIM_FLAG_Update);的代码。原因如下:
预分频器有一个缓冲寄存器,我们写的值只有在更新事件时,才会真正的起作用,所以这里为了让值立刻起作用,就会手动生成一个更新事件这样预分频器的值就有效了,但同时,副作用就是,更新事件和更新中断是同时发生的,更新中断会置更新中断标志位,当我们一旦初始化完了,更新中断就会立刻进入,这就是我们刚一上点就立刻进中断的原因。解决方法就是TimeBase设定预分频器的后面,开启中断的前面,手动调用一下TIM_ClearFlag。清楚中断标志位。
注意时基单元初始化时重装器数值和预分频数的分配,要根据以下公式来分配:
CK_CNT_OV = CK_CNT / (ARR + 1) = CK_PSC / (PSC + 1) / (ARR + 1)
CK_PSC指的是时钟频率,PSC通常指的是预分频数。在公式CK_CNT_OV = CK_PSC / (PSC + 1) / (ARR + 1)中,CK_PSC代表时钟频率,PSC代表预分频数,ARR代表自动重装载寄存器的值。
接下来就是主函数了
#include "stm32f10x.h"                  // Device header
#include "OLED.h"
#include "Timer.h"
uint16_t Num;
int main(void)
{
 OLED_Init();
 Timer_Init();
 OLED_ShowString(1, 1, "Num:");
 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);
 }
}
本节主要就是有很多函数,第一次面对头文件里密密麻麻一堆函数的时候实在是有些头疼,但潜下心来,多看看别人的解读,多做做笔记其实很快就能够熟悉了,平时有空也自己多改改参数熟悉一下。
接下来还有利用外部时钟来控制定时器的方法:
这里使用的是红外对射器来控制定时器,所以重装器值和预分频值调小一点以便观察现象。
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);//因为用到外部设备控制定时器,所以需要使能GPIO时钟

GPIO_InitTypeDef GPIO_InitStructure;//GPIO结构体
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);

TIM_ETRClockMode2Config(TIM2, TIM_ExtTRGPSC_OFF, TIM_ExtTRGPolarity_NonInverted, 0x00);//使用外部时钟模式2

TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;//采样滤波分频用的,可随填
TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInitStructure.TIM_Period = 10 - 1;//ARR自动重装器的值
TIM_TimeBaseInitStructure.TIM_Prescaler = 1 - 1;
TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;//重复计数器,只有高级计数器会用
TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStructure);

再往后的代码和前面没有区别了,这里主要展示一下使用外部时钟的初始化代码,主要就是要加上GPIO初始化的代码。

今天听这个课,试着直接在这里写笔记,前面函数的解释就是边听老师讲边记的,放在这里留到以后复习应该挺不错的。以后感觉也可以这样记笔记,而且也挺轻松的,复制一下代码,再打字解析一下就好,比直接写在笔记本上轻松很多,但听理论课的时候感觉还是比较想直接卸载笔记本上。今天还试着看了PCB的课,感觉挺好的,之前上单片机课老师就有时候会有额外要求画PCB图,但都没怎么学过,学校也没怎么交过,感觉可以在这段时间学起来。明天试试看吧。