完美解决STM32F407ZGT6使用Systic定时器实现延时

预备知识

        STM32F4的系统滴答计时器的介绍及其说明。时间有限,这里点到为止,详情自行百度。

        延时的原理:

                        因为在 ucos 下 systick 不能再被随意更改,如果我们还想利用 systick 来做 delay_us 或者
                delay_ms 的延时,就必须想点办法了,这里我们利用的是时钟摘取法。

                                ---这里摘自正点原子探索者F4,详细原理请自行百度。

实验目标

        使用Systic计时器实现精准延时,且不占用OS中断

        延时系统具有通用性,可适用多种系统频率

重点分析

        本程序流程如下:

                初始化其它相关硬件->获取HCLK时钟频率->用HCLK时钟频率初始化Systic计时器->其它操作实现延时

问题诊断

       刚开始测试的时候发现了一个问题,如下描述:

                由于该程序修改自正点原子的源码,源码中系统时钟是预先定义好的,使用起来没有问题,但这里系统频率是调用库函数实现的,虽然具有通用信,但却存在问题。

                库函数中获取时钟频率的函数RCC_GetClocksFreq正确使用有一个条件,就是外部时钟25MHz

                我们看一看该 函数的部分说明:

                        HSE_VALUE is a constant defined in stm32f4xx.h file (default value

                        25 MHz), user has to ensure that HSE_VALUE is same as the real
                      frequency of the crystal used. Otherwise, this function may

                      have wrong result.

                意思就是外部晶振不为25MHz时算出的频率会出错。

                表现在该工程中就是延时的时间是标准时间的三倍左右,即25/8

        那么如何解决这个问题?很简单

            stm32f4xx.h做以下修改

                源代码:
123行:   #define HSE_VALUE    ((uint32_t)25000000) /*!< Value of the External oscillator in Hz */

改为: 123行:   #define HSE_VALUE    ((uint32_t)8000000) /*!< Value of the External oscillator in Hz */

                这里是指默认的外部高速时钟25Mhz,实际为8MHz,这里为了使库函数获取的时钟更准确

        修改后,测试基本符合。

工程源码:

Delay.h

/**
  ******************************************************************************
  * @file    Delay.h
  * @author  李航兵
  * @version V1.2
  * @date    18-June-2018
  * @brief   这个头文件包含了STM32F407ZGT6开发板的延时相关的函数. 
  ******************************************************************************
  * @attention
  *		官方库文件里有参考代码
  *		为保证延时系统的精准,要求系统主频率越高越好
  *		为保证延时系统的完整支持,系统主频率至少8MHz
  *		使用CM4系统定时器,中断优先级最低
  *
  * @更新说明
  *		支持us、ms级延时,且精度大大提高
  *		支持OS,基本不影响OS的使用(下个版本添加)
  *
  ******************************************************************************
  */

/* Define to prevent recursive inclusion -------------------------------------*/
#ifndef __DELAY_H
#define __DELAY_H


/* Includes ------------------------------------------------------------------*/
#include "stm32f4xx_rcc.h"
#include "core_cm4.h"
#include "stm32f4xx_it.h"
#include "misc.h"



/* Exported types ------------------------------------------------------------*/
/* Exported constants --------------------------------------------------------*/
/* Exported macro ------------------------------------------------------------*/
/* Exported functions ------------------------------------------------------- */

void Delay_Init(void);					//延时系统初始化
void Delay_Us(u16 us);				//微秒延时
void Delay_Ms(u16 ms);				//毫秒延时
void Delay_Us_2(u32 us);				//微秒延时,范围更大




#endif /* __DELAY_H */
/************************ (C) COPYRIGHT 李航兵 *****END OF FILE****/

Led.h

/**
  ******************************************************************************
  * @file    Led.h
  * @author  李航兵
  * @version V1.0
  * @date    14-June-2018
  * @brief   这个头文件包含了STM32F407ZGT6开发板的Led相关的函数. 
  ******************************************************************************
  * @attention
  *硬件连接:
  *		Led1:PC0-VCC
  *		Led1:PD3-VCC
  *
  ******************************************************************************
  */

/* Define to prevent recursive inclusion -------------------------------------*/
#ifndef __LED_H
#define __LED_H


/* Includes ------------------------------------------------------------------*/
#include "stm32f4xx_gpio.h"
#include "stm32f4xx_rcc.h"


/* Exported types ------------------------------------------------------------*/
/* Exported constants --------------------------------------------------------*/
/* Exported macro ------------------------------------------------------------*/
/* Exported functions ------------------------------------------------------- */


void Led_Init(void);              //Led初始化
void Led_On(u8 led);           //开灯
void Led_Off(u8 led);           //关灯


#endif /* __LED_H */
/************************ (C) COPYRIGHT 李航兵 *****END OF FILE****/

Delay.c

/**
  ******************************************************************************
  * @file    Delay.c
  * @author  李航兵
  * @version V1.2
  * @date    18-June-2018
  * @brief   这个文件包含以下函数用于操作STM32F407ZGT6开发板的延时功能
  *           + 延时系统初始化
  *           + 微秒延时
  *           + 毫秒延时
  *
  *  @verbatim
  *
  *
    ===========================================================================
                         ##### How to use this driver #####
    ===========================================================================
      [..]
      (#) 初始化延时系统,调用Delay_Init()
      (#) 延时
         (++) 毫秒: Delay_Ms()
         (++) 微秒: Delay_Init()
    @endverbatim
  ******************************************************************************
  * @attention
  *		官方库文件里有参考代码
  *		为保证延时系统的精准,要求系统主频率越高越好
  *		为保证延时系统的完整支持,系统主频率至少8MHz
  *		使用CM4系统定时器,中断优先级最低
  *		使用此文件后,禁止再修改SysTick计时器及其设置
  *
  *
  *
  *
  *
  *
  * <h2><center>&copy; COPYRIGHT 2018 李航兵</center></h2>
  * @更新说明
  *		支持us、ms级延时,且精度大大提高
  *		支持OS,基本不影响OS的使用(下个版本添加)
  *
  *
  *
  *
  *
  *
  ******************************************************************************
  */
  
  /* Includes ------------------------------------------------------------------*/
#include "Delay.h"


/* Private typedef -----------------------------------------------------------*/
/* Private define ------------------------------------------------------------*/
/* Private macro -------------------------------------------------------------*/
/* Private variables ---------------------------------------------------------*/


u8 per_us;				//每1us定时器节拍
u32 per_ms;				//每1ms节拍,注意168MHz下值为168000,需要32位,移植自STM32F0,此处谨慎



/* Private function prototypes -----------------------------------------------*/
/* Private functions ---------------------------------------------------------*/


/**
  * @brief  延时系统初始化.
  * @param  None.
  * @retval None
  */
void Delay_Init()
{
	RCC_ClocksTypeDef RCC_Clocks;
	RCC_GetClocksFreq(&RCC_Clocks);
		//这里有个问题,本想自动化获取时钟频率的,不料在该函数中有如下声明
	/*
	HSE_VALUE is a constant defined in stm32f4xx.h file (default value
  *                25 MHz), user has to ensure that HSE_VALUE is same as the real
  *                frequency of the crystal used. Otherwise, this function may
  *                have wrong result.
	*/
	//也就是后来测试时发现延时总是为输入的3倍左右
	//后修改了库文件,可参看对应目录的readme文件获取修改记录
	if(SysTick_Config(RCC_Clocks.HCLK_Frequency / 1000))		//1ms一次中断
		while(1);	
	
	per_ms=SysTick->LOAD;							//每1ms节拍,亦即重载值
	per_us=per_ms/1000;		//每1us定时器节拍

}



/**
  * @brief  微秒延时.
  * @param  延时的微秒数,约定范围1~390,“禁止其他值”.
  * @note   存在一定误差,主要是函数调用+部分计算.
  * @retval None
  */
void Delay_Us(u16 us)				//微秒延时
{
	u32 ticks_old=SysTick->VAL;		//前一个计数值
	u32 ticks_new;					//后一个计数值
	u16 ticks_sum=0;				//已经经过的节拍
	u16 ticks_delta=us*per_us;		//需要经过的节拍
	if(us>390) return;				//计时不允许超过390us,超过390us请使用Delay_Us_2
	while(1)
	{
		ticks_new=SysTick->VAL;	
		if(ticks_new!=ticks_old)
		{
			if(ticks_new<ticks_old)ticks_sum+=ticks_old-ticks_new;	//这里注意一下SYSTICK是一个递减的计数器就可以了.
			else ticks_sum+=per_ms-ticks_new+ticks_old;	    
			ticks_old=ticks_new;
			if(ticks_sum>=ticks_delta)break;			//时间超过/等于要延迟的时间,则退出.
		}  
	}
}








/**
  * @brief  毫秒延时.
  * @param  延时的毫秒数,约定范围1~25000,“禁止其他值”.
  * @note   存在一定误差,主要是函数调用+部分计算.
  * @retval None
  */
void Delay_Ms(u16 ms)				//毫秒延时
{
	u32 ticks_old=SysTick->VAL;		//前一个计数值
	u32 ticks_new;					//后一个计数值
	u32 ticks_sum=0;				//已经经过的节拍
	u32 ticks_delta=ms*per_ms;			//需要经过的节拍
	if(ms>25000) return;				//计时不允许超过25000ms,超过25000ms请多次使用
	while(1)
	{
		ticks_new=SysTick->VAL;	
		if(ticks_new!=ticks_old)
		{
			if(ticks_new<ticks_old)ticks_sum+=ticks_old-ticks_new;	//这里注意一下SYSTICK是一个递减的计数器就可以了.
			else ticks_sum+=per_ms-ticks_new+ticks_old;	    
			ticks_old=ticks_new;
			if(ticks_sum>=ticks_delta)break;			//时间超过/等于要延迟的时间,则退出.
		}
	}
}




/**
  * @brief  微秒延时,范围更大.
  * @param  延时的微秒数,约定范围1~25000000,“禁止其他值”.
  * @note   存在一定误差,主要是函数调用+部分计算.
  * @retval None
  */
void Delay_Us_2(u32 us)				//微秒延时,范围更大
{
	u32 ticks_old=SysTick->VAL;		//前一个计数值
	u32 ticks_new;					//后一个计数值
	u32 ticks_sum=0;				//已经经过的节拍
	u32 ticks_delta=us*per_us;		//需要经过的节拍
	if(us>25000000) return;				//计时不允许超过25000000us
	while(1)
	{
		ticks_new=SysTick->VAL;	
		if(ticks_new!=ticks_old)
		{
			if(ticks_new<ticks_old)ticks_sum+=ticks_old-ticks_new;	//这里注意一下SYSTICK是一个递减的计数器就可以了.
			else ticks_sum+=per_ms-ticks_new+ticks_old;	    
			ticks_old=ticks_new;
			if(ticks_sum>=ticks_delta)break;			//时间超过/等于要延迟的时间,则退出.
		}  
	}
}



/**
  * @brief  系统定时器中断.
  * @param  None.
  * @note   一般给OS用.
  * @retval None
  */
void SysTick_Handler(void)		//系统定时器中断
{
	
}


Led.c

/**
  ******************************************************************************
  * @file    Led.c
  * @author  李航兵
  * @version V1.0
  * @date    14-June-2018
  * @brief   这个文档包含了Led功能相关的函数.
  ******************************************************************************
  * @brief   这个文件包含以下函数用于操作STM32F407ZGT6开发板的Led灯
  *           + 初始化Led
  *           + 开灯
  *           + 关灯
  *
  *  @verbatim
  *
  *
    ===========================================================================
                         ##### How to use this driver #####
    ===========================================================================
      [..]
      (#) 初始化Led,调用Led_Init()
      (#) 控制灯的亮灭
         (++) 开灯: Led_On
         (++) 关灯: Led_Off
    @endverbatim
  ******************************************************************************
  * @attention
  *
  * <h2><center>&copy; COPYRIGHT 2018 李航兵</center></h2>
  *硬件连接:
  *		Led1:PC0-VCC
  *		Led1:PD3-VCC
  *使用该文件后,PC0和PD3将会被占用
  *
  ******************************************************************************
  */


/* Includes ------------------------------------------------------------------*/
#include "Led.h"


/* Private typedef -----------------------------------------------------------*/
/* Private define ------------------------------------------------------------*/
/* Private macro -------------------------------------------------------------*/
/* Private variables ---------------------------------------------------------*/
/* Private function prototypes -----------------------------------------------*/
/* Private functions ---------------------------------------------------------*/



/**
  * @brief  Led初始化.
  * @note   PC0和PD3将会被占用
  * @note   调用此函数会开启GPIOC和启GPIOD时钟.    
  * @param  None
  * @retval None
  */
void Led_Init()              //Led初始化
{
    GPIO_InitTypeDef GPIO_Led;
	
	RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOC,ENABLE);
	RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOD,ENABLE);
	
	
	GPIO_Led.GPIO_Mode=GPIO_Mode_OUT;
	GPIO_Led.GPIO_OType=GPIO_OType_PP;
	GPIO_Led.GPIO_Pin=GPIO_Pin_0;
	GPIO_Led.GPIO_PuPd=GPIO_PuPd_NOPULL;
	GPIO_Led.GPIO_Speed=GPIO_High_Speed;
	
	GPIO_Init(GPIOC,&GPIO_Led);
	
	GPIO_Led.GPIO_Pin=GPIO_Pin_3;
	
	GPIO_Init(GPIOD,&GPIO_Led);
	
	Led_Off(1);
	Led_Off(2);
}




/**
  * @brief  开灯.
  * @note   
  * @param  led:Led的编号,可取1、2
  * @retval None
  */
void Led_On(u8 led)           //开灯
{
    switch(led)
    {
    case 1:
	  	GPIO_ResetBits(GPIOC,GPIO_Pin_0);
		break;
    case 2:
	  	GPIO_ResetBits(GPIOD,GPIO_Pin_3);
		break;
    }
}



/**
  * @brief  关灯.
  * @note   
  * @param  led:Led的编号,可取1、2
  * @retval None
  */
void Led_Off(u8 led)           //关灯
{
  	switch(led)
    {
    case 1:
	  	GPIO_SetBits(GPIOC,GPIO_Pin_0);
		break;
    case 2:
	  	GPIO_SetBits(GPIOD,GPIO_Pin_3);
		break;
    }
}


/************************ (C) COPYRIGHT 李航兵 *****END OF FILE****/

Delay_Test.c



#include "Delay.h"
#include "Led.h"












void main()
{
	int i;
	Led_Init();
	Delay_Init();
	while(1)
	{
		Led_On(1);
		for(i=0;i<1000;i++)
			Delay_Us(300);
		Led_Off(1);
		Delay_Ms(1000);
		Led_On(2);
		Delay_Us_2(1000000);
		Led_Off(2);
		Delay_Ms(1000);
	}
}



//void main()
//{
//	int i;
//	Led_Init();
//	Delay_Init();
//	while(1)
//	{
//		Led_Off(1);
//		for(i=0;i<1000;i++)
//		{
//			Delay_Us(300);
//			Delay_Us(300);
//			Delay_Us(300);
//			Delay_Us(100);
//		}
//		Led_On(1);
//		
//		for(i=0;i<1000;i++)
//		{
//			Delay_Us(300);
//			Delay_Us(300);
//			Delay_Us(300);
//			Delay_Us(100);
//		}
//	}
//}





//void main()
//{
//	int i;
//	Led_Init();
//	Delay_Init();
//	while(1)
//	{
//		Led_Off(1);
//		Delay_Ms(1000);
//		Led_On(1);
//		
//		Delay_Ms(1000);
//	}
//}
stm32f4xx.h
    这里修改的地方前面已经说明

结果分析

        经过测试,延时1s的时间基本准确

        
  • 3
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值