STM32F4 32位定时器TIM2、TIM5的使用(附STM32F407----STM32F401的移植说明)
STM32F4 32位定时器TIM2、TIM5的使用(附STM32F407----STM32F401的移植说明)
STM32F4是一款性价比极高的MCU,撇开强大的FPU(硬件浮点运算单元),单从定时器来讲,STM32F4比STM32F1多了两个32位定时器(TIM2、TIM5),而,可以看到32位定时器可以计时达到us级别,单次计时最长可以达到57s左右,而16位计时器只能达到65ms,这对于一些需要高精度且需要有较长的时间支持的计时场合很重要。比如需要积分、微分的时候。
看到网上有一些关于如何使用STM32F4的32位定时器的博客,很多都有一些问题,这里我将我使用的方法分享出来,精度非常高,且基本没有任何误差:(MCU采用的STM32F401 ,根据正点原子STM32F407移植过来的,其中需要改动的地方都注释了的,在这里要非常感谢原子哥)
其中需要特别注意的地方有以下几个:
① arr 装载值由于是从 0-1-2-3-…arr-0 ,所以循环周期是arr+1,所以无论是采用向上计数还是向下计数还是*对齐计数,是一样的(3种计数方式)。
②分频系数从0-1-2-3-…psc,分别对应1-2-3-4…psc+1分频。
③TIM2和TIM5可以被作为16位计数器,也可以被作为32位计数器,原理很简单,这里是需要特别注意的:
装载值被设定为0xffff及以下的值时候,TIM2和TIM5是16位计数器。
装载值被设定为0xffff-0xffffffff之间的时候,TIM2和TIM5是32位计数器。
我在测试中使用TIM3定时中断观测TIM2的计数器的值,为了让计数精度更高,在使能TIM2和TIM3时最好同时使能,不然会有误差(不过这个误差也比较小并且误差大小是固定的us级别的误差,这个误差是由于不是同时开启定时器,有机器运行周期的差异导致的)
④虽然TIM2、TIM3挂载在APB1(42Mhz)上,但是由于采用的PLCK1是二分频得到,因此定时器会 2 倍频到 84Mhz 作为定时器时钟。
测试的平台:
MCU: STM32F401CCU6 (核心板某宝不到20RMB,WeAct2.1)
晶振 : 25MHz (官方晶振,正点原子使用的是8Mhz,移植时候需要注意,如果你没注意,不幸超频将MCU锁死,看这篇博客)
OLED:中景园1.3寸7针OLED 1106驱动 (某宝20RMB以内)
下面是部分源码:
tiner32bit.c文件
#include "timer32bit.h"
#include "led.h"
#include "oled.h"
u32 counter = 0;
u16 times = 0;
void TIM3_Int_Init(u16 arr,u16 psc)
{
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3,ENABLE); ///使能TIM3时钟
TIM_TimeBaseInitStructure.TIM_Period = arr; //自动重装载值
TIM_TimeBaseInitStructure.TIM_Prescaler=psc; //定时器分频
TIM_TimeBaseInitStructure.TIM_CounterMode=TIM_CounterMode_Up; //向下计数模式
TIM_TimeBaseInitStructure.TIM_ClockDivision=TIM_CKD_DIV1;
TIM_TimeBaseInit(TIM3,&TIM_TimeBaseInitStructure);//初始化TIM3
TIM_ITConfig(TIM3,TIM_IT_Update,ENABLE); //允许定时器3更新中断
TIM_Cmd(TIM3,ENABLE); //使能定时器3
TIM_Cmd(TIM2,ENABLE); //使能定时器2
NVIC_InitStructure.NVIC_IRQChannel=TIM3_IRQn; //定时器3中断
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=0x01; //抢占优先级1
NVIC_InitStructure.NVIC_IRQChannelSubPriority=0x03; //子优先级3
NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE;
NVIC_Init(&NVIC_InitStructure);
}
//定时器3中断服务函数
void TIM3_IRQHandler(void)
{
if(TIM_GetITStatus(TIM3,TIM_IT_Update)==SET) //溢出中断
{
LED0=!LED0;//LED0 翻转
counter = TIM2->CNT;//读取计数值
OLED_ShowString(5,10,"Now Count:",8,1);
OLED_ShowNum(25,25,counter,10,8,1);
OLED_ShowString(100,25,"us",8,1);
OLED_ShowString(5,35,"Now Times:",8,1);
OLED_ShowNum(25,50,times++,10,8,1);
OLED_ShowString(100,50,"10ms",8,1);
OLED_Refresh();
}
TIM_ClearITPendingBit(TIM3,TIM_IT_Update); //清除中断标志位
}
void TIM2_Int_Init(u32 arr,u16 psc)
{
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);
TIM_TimeBaseInitStructure.TIM_Period = arr; //0xffffffff 可以计数到 2^32 次方
TIM_TimeBaseInitStructure.TIM_Prescaler = psc; //84分频 us 级别计数
TIM_TimeBaseInitStructure.TIM_CounterMode=TIM_CounterMode_Up;
TIM_TimeBaseInitStructure.TIM_ClockDivision=TIM_CKD_DIV1;
TIM_TimeBaseInit(TIM2,&TIM_TimeBaseInitStructure);
}
timer32bit.h文件
#ifndef __TIMER32BIT__
#define __TIMER32BIT__
#include "sys.h"
void TIM3_Int_Init(u16 arr,u16 psc);
void TIM2_Int_Init(u32 arr,u16 psc);
#endif
main.c
#include "sys.h"
#include "delay.h"
#include "usart.h"
#include "led.h"
#include "key.h"
#include "oled.h"
#include "bmp.h"
#include "timer32bit.h"
/*====================================================================================================================*/
/*====================================================================================================================*/
/*====================================================================================================================*/
/*****************************************************程序改动说明*****************************************************/
//根据采用不同晶振 STM32F401 改动的地方有:
//OptionForTarget xtal(改为和实际晶振大小一致)
//delay.c 121、187、206行(降低计时误差 不改动定时误差为5% 改动后延时为偶数无误差,为奇数误差为1/n *100%)
//system_stm32f4xx.c 317行(根据system_stm32f4xx.c 137-181行的说明改动)
//stm32f4xx.h 123行(将HSE晶振数值更新为与实际一样)
/*====================================================================================================================*/
/*====================================================================================================================*/
/*====================================================================================================================*/
int main(void)
{
delay_init(84); //初始化延时函数
LED_Init(); //初始化LED端口
KEY_Init();
OLED_Init();
OLED_ShowPicture(0,0,128,64,BMP4,1);//LOGO
delay_ms(1000);
ShowFrequence();//显示时钟频率
delay_ms(1000);
OLED_Clear();
TIM2_Int_Init(0xffffffff,84-1);//us
TIM3_Int_Init(8400-1,100-1);//ms
/**下面是通过直接操作库函数的方式实现IO控制**/
while(1)
{
if(KEY_Scan(0)==KEY0_PRES)
{
LED_Shine(100,3);
}
}
}
实验效果:
显示时钟
定时计数比较
Keil工程链接
提取码: v9ha
二维码链接: