1. 程式人生 > >完美解決STM32F407ZGT6使用Systic定時器實現延時

完美解決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的時間基本準確