【STM32+HAL】微秒级延时函数汇总

一、调用Arduino中的micros函数

特点:

  • 在等待的过程中可以执行其他代码。

1、micros.c
#include "micros.h"


__STATIC_INLINE uint32_t GXT_SYSTICK_IsActiveCounterFlag(void)
{
  return ((SysTick->CTRL & SysTick_CTRL_COUNTFLAG_Msk) == (SysTick_CTRL_COUNTFLAG_Msk));
}


static uint32_t getCurrentMicros(void)
{
  /* Ensure COUNTFLAG is reset by reading SysTick control and status register */
  GXT_SYSTICK_IsActiveCounterFlag();
  uint32_t m = HAL_GetTick();
  const uint32_t tms = SysTick->LOAD + 1;
  __IO uint32_t u = tms - SysTick->VAL;
  if (GXT_SYSTICK_IsActiveCounterFlag()) {
    m = HAL_GetTick();
    u = tms - SysTick->VAL;
  }
  return (m * 1000 + (u * 1000) / tms);
}


//获取系统时间,单位us
uint32_t micros(void)
{
  return getCurrentMicros();
}



//微秒级延时函数
void Delay_us(uint16_t nus)
{
    int temp = micros();
    while(micros() - temp < nus){}
}
2、micros.h
#ifndef __MICROS_H__
#define __MICROS_H__

#include "stm32f4xx_hal.h"

uint32_t micros(void);
void Delay_us(uint16_t nus);

#endif

3、测试结果

波形较不稳定,周期准确度较高

 

二、定时器中断延时函数

1、HAL库开启定时器

已知TIM1挂载在APB2总线上

配置参数值 

2、延时函数
/**
  * @brief  定时器延时us,Prescaler -> 168-1
  * @param  us: <= 65535
  * @retval None
  */

void Delay_us(uint16_t nus)
{
	__HAL_TIM_SetCounter(&htim1,0);
	__HAL_TIM_ENABLE(&htim1);
	while (__HAL_TIM_GET_COUNTER(&htim1) < nus)
	{
	}
	__HAL_TIM_DISABLE(&htim1);
}

3、测试结果

波形较稳定,周期稳定地偏长0.3-0.4us

三、纯代码1

1、Delay_us.c
__IO float usDelayBase;

void PY_usDelayTest(void)
{
  __IO uint32_t firstms, secondms;
  __IO uint32_t counter = 0;

  firstms = HAL_GetTick()+1;
  secondms = firstms+1;

  while(uwTick!=firstms) ;

  while(uwTick!=secondms) counter++;

  usDelayBase = ((float)counter)/1000;
}



void PY_Delay_us_t(uint32_t Delay)
{
  __IO uint32_t delayReg;
  __IO uint32_t usNum = (uint32_t)(Delay*usDelayBase);

  delayReg = 0;
  while(delayReg!=usNum) delayReg++;
}



void PY_usDelayOptimize(void)
{
  __IO uint32_t firstms, secondms;
  __IO float coe = 1.0;

  firstms = HAL_GetTick();
  PY_Delay_us_t(1000000) ;
  secondms = HAL_GetTick();

  coe = ((float)1000)/(secondms-firstms);
  usDelayBase = coe*usDelayBase;
}



void Delay_us(uint16_t nus)
{
  __IO uint32_t delayReg;

  __IO uint32_t msNum = nus/1000;
  __IO uint32_t usNum = (uint32_t)((nus%1000)*usDelayBase);

  if(msNum>0) HAL_Delay(msNum);

  delayReg = 0;
  while(delayReg!=usNum) delayReg++;
}

2、测试结果

波形较稳定,周期稳定地偏长0.3-0.4us

四、纯代码2

1、Delay_us.c
#define CPU_FREQUENCY_MHZ    168        //STM32主频

void Delay_us(__IO uint32_t delay)
{
    int last, curr, val;
    int temp;
    while (delay != 0)
    {
        temp = delay > 900 ? 900 : delay;
        last = SysTick->VAL;
        curr = last - CPU_FREQUENCY_MHZ * temp;
        if (curr >= 0){
            do{
                val = SysTick->VAL;
            }
            while ((val < last) && (val >= curr));
        }
        else{
            curr += CPU_FREQUENCY_MHZ * 1000;
            do{
                val = SysTick->VAL;
            }
            while ((val <= last) || (val > curr));
        }
        delay -= temp;
    }
}

 2、测试结果

波形较稳定,偏差稳定0.5us

五、HAL库延时函数

1、Delay_us.c
#define CPU_FREQUENCY_MHZ 168				/* CPU主频,根据实际进行修改 */


/**
 * 此延时函数代码适用于HAL库
 */
void delay_us(uint32_t delay)
{
    int last, curr, val;
    int temp;
    while (delay != 0){
        temp = delay > 900 ? 900 : delay;
        last = SysTick->VAL;
        curr = last - CPU_FREQUENCY_MHZ * temp;
        if (curr >= 0){
            do{
                val = SysTick->VAL;
            }
            while ((val < last) && (val >= curr));
        }
        else{
            curr += CPU_FREQUENCY_MHZ * 1000;
            do{
                val = SysTick->VAL;
            }
            while ((val <= last) || (val > curr));
        }
        delay -= temp;
    }
}

测试结果与方式一相似

<think>好的,用户现在想用STM32HAL实现微秒级延时。之前他们问过关于W5500和SPI驱动的问题,现在转向了定时器相关的功能。首先,我需要确定用户的需求是什么。他们可能需要精确的微秒级延时,用于外设的时序控制,比如SPI通信中的等待时间,或者传感器读取时的精确时序。 接下来,我得考虑HAL本身提供的延时函数HAL_Delay()是基于SysTick的毫秒级延时,无法满足微秒需求。所以用户需要自己实现更细粒度的延时。常见的实现方法有几种:使用定时器、使用SysTick、或者使用NOP指令的空循环。但NOP的方法受编译器优化和CPU频率影响大,可能不准。所以更可靠的是定时器或SysTick。 用户可能已经知道这些方法,但需要具体的代码实现。我需要提供基于定时器和DWT的两种方法,因为这两种比较常见。使用定时器的话,需要配置一个基本定时器,比如TIM2,设置时钟为1MHz,这样每个计数就是1微秒。然后编写启动、延时、停止的函数。这种方法需要用户根据自己MCU的时钟配置调整定时器的预分频值。 另一种方法是使用DWT(Data Watchpoint and Trace)的Cycle Counter,这需要启用DWT模块,通过读取时钟周期数来实现精确延时。这种方法不需要占用定时器资源,但需要确认MCU支持DWT,并且时钟配置正确。比如,如果系统主频是72MHz,那么每微秒有72个周期,这样计算起来方便。 用户可能关心代码的可移植性和准确性,所以需要给出注意事项,比如时钟配置、中断的影响等。此外,他们可能在实际使用中遇到问题,比如延时不准,这时候需要提示检查系统时钟和定时器配置是否正确。 还要考虑到代码的兼容性,不同STM32型号可能有不同的定时器或DWT支持情况,所以需要用户根据具体情况调整。最后,示例代码需要简洁明了,直接给出关键部分,让用户能够快速集成到自己的项目中。</think>以下提供两种基于STM32 HAL微秒级延时实现方案(推荐方案1): --- ### **方案1:定时器实现(高精度)** ```c // timer.h #define DELAY_TIM TIM2 #define DELAY_TIM_CLK_ENABLE() __HAL_RCC_TIM2_CLK_ENABLE() // 初始化函数(时钟配置为1MHz) void Delay_TIM_Init(void) { TIM_HandleTypeDef htim; htim.Instance = DELAY_TIM; htim.Init.Prescaler = HAL_RCC_GetPCLK1Freq()/(2*1000000) - 1; // 1MHz时钟 htim.Init.CounterMode = TIM_COUNTERMODE_UP; htim.Init.Period = 0xFFFF-1; htim.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1; HAL_TIM_Base_Init(&htim); HAL_TIM_Base_Start(&htim); } // 微秒延时函数 void delay_us(uint16_t us) { __HAL_TIM_SET_COUNTER(&htim, 0); while(__HAL_TIM_GET_COUNTER(&htim) < us); } ``` --- ### **方案2:DWT周期计数器实现(无需硬件定时器)** ```c // dwt.h #include "core_cm4.h" // 根据内核型号选择 void Delay_DWT_Init(void) { CoreDebug->DEMCR |= CoreDebug_DEMCR_TRCENA_Msk; DWT->CYCCNT = 0; DWT->CTRL |= DWT_CTRL_CYCCNTENA_Msk; } void delay_us(uint32_t us) { uint32_t cycles = SystemCoreClock / 1000000 * us; uint32_t start = DWT->CYCCNT; while((DWT->CYCCNT - start) < cycles); } ``` --- ### **使用示例** ```c int main(void) { HAL_Init(); SystemClock_Config(); // 选择其中一种初始化方式 Delay_TIM_Init(); // 或者 Delay_DWT_Init(); while(1) { HAL_GPIO_TogglePin(GPIOA, GPIO_PIN_5); delay_us(500); // 500微秒延时 } } ``` --- ### **关键参数说明** | 参数 | 方案1要求 | 方案2要求 | |--------------|-------------------------|-------------------------| | 时钟精度 | 依赖定时器配置 | 依赖系统主频 | | 适用场景 | 需要严格时序控制 | 通用延时 | | 误差范围 | ±0.5us(72MHz主频) | ±1us(无中断干扰) | | 资源占用 | 占用一个定时器 | 无需硬件资源 | --- ### **注意事项** 1. 方案1需根据实际APB时钟调整预分频值 2. 方案2要求: - 内核必须支持DWT(Cortex-M3/M4/M7) - 系统时钟需准确配置(通过`SystemCoreClock`变量反映) 3. 两种方案均需关闭全局中断才能保证最高精度 4. 对于100us以上延时,建议结合HAL_Delay()使用
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值