无需另配定时器在STM32 HAL下实现微秒级延时(兼容FreeRTOS)
目錄
- 前言
- 一、代碼部分
- 二、使用和驗證
- 1.引入頭文件
- 2.初始化
- 3.使用和驗證
- 三、可移植性
- 總結
前言
接觸HAL庫差不多兩年了,一直苦于HAL庫沒有自帶微秒級的延時,網上的前輩們給出的解決方案要么是改寫HAL_Delay的延時時間,要么就是額外占用一個定時器來實現,不太方便移植,以下是我給出的解決方案。
軟件平臺:STM32 Cube IDE 1.5.0
一、代碼部分
Delay.c 代碼如下
#include "main.h"#define USE_HAL_LEGACY #include "stm32_hal_legacy.h"#define Timebase_Source_is_SysTick 1 //當Timebase Source為SysTick時改為1 //#define Timebase_Source_is_SysTick 0 //當使用FreeRTOS,Timebase Source為其他定時器時改為0#if (!Timebase_Source_is_SysTick)extern TIM_HandleTypeDef htimx; //當使用FreeRTOS,Timebase Source為其他定時器時,修改為對應的定時器#define Timebase_htim htimx#define Delay_GetCounter() __HAL_TIM_GetCounter(&Timebase_htim)#define Delay_GetAutoreload() __HAL_TIM_GetAutoreload(&Timebase_htim) #else#define Delay_GetCounter() (SysTick->VAL)#define Delay_GetAutoreload() (SysTick->LOAD) #endifstatic uint16_t fac_us = 0; static uint32_t fac_ms = 0;/*初始化*/ void delay_init(void) { #if (!Timebase_Source_is_SysTick)fac_ms = 1000000; //作為時基的計數器時鐘頻率在HAL_InitTick()中被設為了1MHzfac_us = fac_ms / 1000; #elsefac_ms = SystemCoreClock / 1000;fac_us = fac_ms / 1000; #endif }/*微秒級延時*/ void delay_us(uint32_t nus) {uint32_t ticks = 0;uint32_t told = 0;uint32_t tnow = 0;uint32_t tcnt = 0;uint32_t reload = 0;reload = Delay_GetAutoreload();ticks = nus * fac_us;told = Delay_GetCounter();while (1){tnow = Delay_GetCounter();if (tnow != told){if (tnow < told){tcnt += told - tnow;}else{tcnt += reload - tnow + told;}told = tnow;if (tcnt >= ticks){break;}}} }/*毫秒級延時*/ void delay_ms(uint32_t nms) {uint32_t ticks = 0;uint32_t told = 0;uint32_t tnow = 0;uint32_t tcnt = 0;uint32_t reload = 0;reload = Delay_GetAutoreload();ticks = nms * fac_ms;told = Delay_GetCounter();while (1){tnow = Delay_GetCounter();if (tnow != told){if (tnow < told){tcnt += told - tnow;}else{tcnt += reload - tnow + told;}told = tnow;if (tcnt >= ticks){break;}}} }/*重寫HAL_Delay*/ void HAL_Delay(uint32_t Delay) {uint32_t tickstart = HAL_GetTick();uint32_t wait = Delay;/*不太明白官方源碼為啥這么寫,會多延時1ms,注釋掉后更準*/ // /* Add a freq to guarantee minimum wait */ // if (wait < HAL_MAX_DELAY) // { // wait += (uint32_t)(uwTickFreq); // }while ((HAL_GetTick() - tickstart) < wait){} }Delay.h 代碼如下
#ifndef DELAY_H #define DELAY_H#include "main.h"extern void delay_init(void); extern void delay_us(uint32_t nus); extern void delay_ms(uint32_t nms);#endif二、使用和驗證
1.引入頭文件
代碼如下(示例):
/* USER CODE END Header */ /* Includes ------------------------------------------------------------------*/ #include "main.h"/* Private includes ----------------------------------------------------------*/ /* USER CODE BEGIN Includes */ #include "../Delay/Delay.h" //引入頭文件 /* USER CODE END Includes *///...2.初始化
代碼如下(示例):
//.../* Initialize all configured peripherals */MX_GPIO_Init();/* USER CODE BEGIN 2 */delay_init(); //初始化/* USER CODE END 2 *//* Infinite loop *//* USER CODE BEGIN WHILE *///...3.使用和驗證
(示例)
所用開發板為 野火指南者 STM32F103VET6 開發板。
工程優化等級為默認的None,所測輸出引腳為PC13和PC4
先測試什么也不做時輸出的脈沖周期(此時系統時鐘為72M)
測得此時的脈沖周期
延時500us時
延時50us時
延時10us時
加入FreeRTOS
Cube建議我們更改時基源
更改Timebase Source為其他定時器
更新工程后修改Delay.c文件
修改測試任務
//... /* USER CODE BEGIN Header_StartDefaultTask */ /*** @brief Function implementing the defaultTask thread.* @param argument: Not used* @retval None*/ /* USER CODE END Header_StartDefaultTask */ void StartDefaultTask(void *argument) {/* USER CODE BEGIN 5 *//* Infinite loop */for(;;){GPIOC->BSRR = GPIO_PIN_13;delay_us(50); //PC13高電平時間為50usGPIOC->BSRR = (uint32_t)GPIO_PIN_13 << 16u;}/* USER CODE END 5 */ }/* USER CODE BEGIN Header_StartTask02 */ /** * @brief Function implementing the myTask02 thread. * @param argument: Not used * @retval None */ /* USER CODE END Header_StartTask02 */ void StartTask02(void *argument) {/* USER CODE BEGIN StartTask02 *//* Infinite loop */for(;;){GPIOC->BSRR = GPIO_PIN_4;osDelay(1); //PC4高電平時間為1msGPIOC->BSRR = (uint32_t)GPIO_PIN_4 << 16u;}/* USER CODE END StartTask02 */ } //...如圖所示微秒級延時仍然工作正常
三、可移植性
//... /*初始化*/ void delay_init(void) { #if (!Timebase_Source_is_SysTick)fac_ms = 1000000; //作為時基的計數器時鐘頻率在HAL_InitTick()中被設為了1MHzfac_us = fac_ms / 1000; #elsefac_ms = SystemCoreClock / 1000;fac_us = fac_ms / 1000; #endif } //...使用滴答定時器作為時基時自然不用多說,當使用其他定時器作為時基時(如本文的例子),Src目錄下會自動生成一個stm32f1xx_hal_timebase_tim.c文件,其中的HAL_InitTick函數重構了在stm32f1xx_hal.c中的、__weak修飾的同名函數,它設置了所選定時器的時鐘頻率為1MHz:
//.../* Compute TIM7 clock */uwTimclock = 2*HAL_RCC_GetPCLK1Freq();/* Compute the prescaler value to have TIM7 counter clock equal to 1MHz */uwPrescalerValue = (uint32_t) ((uwTimclock / 1000000U) - 1U); //...因此本方案在絕大多數由cube生成工程的情況下應該是通用的。
總結
經過實驗,我們發現本方案實現了精度還算可以接受的微秒級延時,不過本方案的延時方式和HAL_Delay差不多,不建議在任務中過多地調用。
不同時基下的初始化過程建議參閱HongAndYi大佬寫的
《HAL和FreeRTOS的基礎時鐘》
總結
以上是生活随笔為你收集整理的无需另配定时器在STM32 HAL下实现微秒级延时(兼容FreeRTOS)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 磁盘和文件系统管理一
- 下一篇: Makefile的写法