欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

STM32F4 32位定时器TIM2、TIM5的使用(附STM32F407----STM32F401的移植说明)

程序员文章站 2022-06-09 11:22:02
...

STM32F4 32位定时器TIM2、TIM5的使用(附STM32F407----STM32F401的移植说明)

STM32F4是一款性价比极高的MCU,撇开强大的FPU(硬件浮点运算单元),单从定时器来讲,STM32F4比STM32F1多了两个32位定时器(TIM2、TIM5),而232=4.291092^{32}=4.29*10^9,可以看到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 作为定时器时钟。
STM32F4 32位定时器TIM2、TIM5的使用(附STM32F407----STM32F401的移植说明)

测试的平台:
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);
		}
	}
}

实验效果:

显示时钟
STM32F4 32位定时器TIM2、TIM5的使用(附STM32F407----STM32F401的移植说明)
定时计数比较
STM32F4 32位定时器TIM2、TIM5的使用(附STM32F407----STM32F401的移植说明)
Keil工程链接
提取码: v9ha
二维码链接:
STM32F4 32位定时器TIM2、TIM5的使用(附STM32F407----STM32F401的移植说明)