STM32F407ZGT6使用Systic定时器实现延时
2018-07-20 来源:eefocus
预备知识
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计时器及其设置
*
*
*
*
*
*
*
© COPYRIGHT 2018 李航兵
* @更新说明
* 支持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 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 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 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 * * *硬件连接: * 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的时间基本准确