如何快速简易的修改使用STM32CUBEMX生成工程文件的延时函数,得到一个尽可能低的延时

# 前言

------------

我们使用STM32CUBEMX软件生成的工程里,有一个官方提供的延时函数HAL_Delay(uint32_t Delay),这个函数提供一个1ms的延时,并且不占用系统,因为这是一个利用滴答时钟实现的。但是在我们实际使用中,1ms的延时真是太久了,有些外设还真的是需要使用到us级别的延时,那怎么办!官方早想到了,提供了让你重写这个函数的办法。但是,我说但是,小白刚学啥都不懂怎么办?重写完一堆error会不会奔溃?
这里笔者提供了一个思路和办法,用最简单的办法实现。
## 首先 

__weak void HAL_Delay(uint32_t Delay)
{
  uint32_t tickstart = HAL_GetTick();
  uint32_t wait = Delay;

  /* Add a freq to guarantee minimum wait */
  if (wait < HAL_MAX_DELAY)
  {
    wait += (uint32_t)(uwTickFreq);
  }

  while ((HAL_GetTick() - tickstart) < wait)
  {
  }
}

这个函数的作用是提供了1ms的延迟,以SysTick timer作为time base时间源。其中uwTickFreq为设置的频率,频率封装在结构体中,分为10HZ\100HZ以及1KHZ,系统默认是1KHZ。

HAL_TickFreqTypeDef uwTickFreq = HAL_TICK_FREQ_DEFAULT;  /* 1KHz */

typedef enum
{
  HAL_TICK_FREQ_10HZ         = 100U,
  HAL_TICK_FREQ_100HZ        = 10U,
  HAL_TICK_FREQ_1KHZ         = 1U,
  HAL_TICK_FREQ_DEFAULT      = HAL_TICK_FREQ_1KHZ
} HAL_TickFreqTypeDef;

我们通过查手册,可以知道,外部晶振为8MHZ,软件设置了72MHZ主时钟的条件下,滴答时钟SysTick只要计数9次就是1us,那么可以得到计数1次就是1/9us,理论上是可以实现伪纳秒级别的延时。现在系统默认设置了1KHZ的时钟频率,那我们怎么才能修改呢?

下面我们找到滴答时钟的初始化函数,用于初始化time base。

__weak HAL_StatusTypeDef HAL_InitTick(uint32_t TickPriority)
{
  /* Configure the SysTick to have interrupt in 1ms time basis*/
  if (HAL_SYSTICK_Config(SystemCoreClock / (1000U / uwTickFreq)) > 0U)
  {
    return HAL_ERROR;
  }

  /* Configure the SysTick IRQ priority */
  if (TickPriority < (1UL << __NVIC_PRIO_BITS))
  {
    HAL_NVIC_SetPriority(SysTick_IRQn, TickPriority, 0U);
    uwTickPrio = TickPriority;
  }
  else
  {
    return HAL_ERROR;
  }

  /* Return function status */
  return HAL_OK;
}

其中HAL_SYSTICK_Config(SystemCoreClock / (1000U / uwTickFreq)函数的作用是初始化滴答时钟和中断。

函数内的参数用到了主时钟和频率做运算,按照系统默认的设置,参数最后的值应为72000

  uint32_t SystemCoreClock         = 72000000U;        /*!< System Clock Frequency (Core Clock) */

接下来我们再来看看配置函数拿这个72000做了什么

__STATIC_INLINE uint32_t SysTick_Config(uint32_t ticks)
{
  if ((ticks - 1UL) > SysTick_LOAD_RELOAD_Msk)
  {
    return (1UL);                                                   /* Reload value impossible */
  }

  SysTick->LOAD  = (uint32_t)(ticks - 1UL);                         /* set reload register */
  NVIC_SetPriority (SysTick_IRQn, (1UL << __NVIC_PRIO_BITS) - 1UL); /* set Priority for Systick Interrupt */
  SysTick->VAL   = 0UL;                                             /* Load the SysTick Counter Value */
  SysTick->CTRL  = SysTick_CTRL_CLKSOURCE_Msk |
                   SysTick_CTRL_TICKINT_Msk   |
                   SysTick_CTRL_ENABLE_Msk;                         /* Enable SysTick IRQ and SysTick Timer */
  return (0UL);                                                     /* Function successful */
}

从这里可以知道,滴答时钟的重载值和72000直接相关,作为两次相邻中断间隔中滴答时钟的滴答次数。

事情看到这里是不是已经很明朗了,主时钟为72MHZ,滴答时钟的重载值为72000(赋值为72000-1),得到1KHZ,那么两次滴答时钟的中断间隔为1ms了,这样就能得到1ms的延时。现在我们只需要增大ticks这个参数的值就行了,因为这个参数是由

SystemCoreClock / (1000U / uwTickFreq)

计算得到的,那么我们修改为

SystemCoreClock / (10000U / uwTickFreq)

就能暗改1KHZ为10KHZ了。

下载验证

验证一

    HAL_Delay(1);
    HAL_GPIO_WritePin(GPIOB, GPIO_PIN_0, GPIO_PIN_RESET);
    HAL_Delay(1);
    HAL_GPIO_WritePin(GPIOB, GPIO_PIN_0, GPIO_PIN_SET);

示波器波形,这里因为是用了hal库,本来延时1ms的波,理论上一个周期应该为2ms,但是实际上一个周期为4ms,但是这个不打紧,精确延时是需要自己拿着示波器一点一点的调出来的,我们粗略延时用hal库就行了

然后我们暗改了公式后的示波器波形,很明显的,实际一个周期为400us了

试验成功,说明这个办法是可行的,但是对于一些外设来说,1ms和几百us的延时不够用啊,那么我们下面来试一下,能不能做到10us级别延时,修改公式SystemCoreClock / (100000U / uwTickFreq),得到下图,试验成功。

那么我们能通过修改hal库得到的最小延时是多少呢?这就要知道重载值最小可以是多少而系统不报错。

根据重载寄存器的赋值可知,理论上参数ticks最小值为1(赋值为0),此时公式为SystemCoreClock / (72000000U / uwTickFreq)

 SysTick->LOAD  = (uint32_t)(ticks - 1UL);                         /* set reload register */

但是在赋值语句的上面有一个if判断,如果参数ticks大于了某个值就会系统出错

(ticks - 1UL) > SysTick_LOAD_RELOAD_Msk

其中,SysTick_LOAD_RELOAD_Msk的最大值为0XFFFFFF,在手册中,重载寄存器为一个24位的寄存器,等于16777215远远小于72MHZ,所以这里我就将公式改为得到的值接近0xffffff并且比它小

SystemCoreClock / (16777215U / uwTickFreq)

结果呢,没有波形,看来这个已经超过极限

接下来经过我的多次反复的测试,得到的最小的延时公式为SystemCoreClock / (167000U / uwTickFreq),约为41.6KHZ左右,经过实测,170000U延时反而增加了,具体什么原因,本人暂时不知道,在这里也就不解答这个问题了。

## 最后一句 

------------
13us不到的延时相对于大部分情况来说已经够用了,本人也不建议继续探究极限值在哪里,浪费时间。另外需要提醒的地方是,因为我们是用STM32CUBEMX生成的工程,并且我们的修改不在软件的保护代码范围区,所以下次修改了STM32CUBEMX的内容并且重新生成了工程,还需要手动去修改一下那个公式

 

 

  • 2
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值