stm32HAL库时钟tick定时函数分析与应用代码

本章目标

深入解析HAL_Delay()函数

分析使用的时候会出现的问题

精确定时的自实现方法案例代码

HAL_Delay()原理分析

HAL_Delay(1000);大家都很熟悉了,嵌入式开发阶段玛卡巴卡级别的元老了。

但是终归是神秘了一点,初次见面人际关系还不错,后面社交复杂了就变心了。

__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)
  {
  }
}

你可能会遇到卡死、时序错误、失灵等等bug.

下面来具体分析一下这个函数,首先找出名字不太好辨认的:

获得当前tick:

__weak uint32_t HAL_GetTick(void)//获取tick
{
  return uwTick;
}

延时的最大值 :

#define HAL_MAX_DELAY      0xFFFFFFFFU

累次叠加时的步进值,可选: 

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;


HAL_TickFreqTypeDef uwTickFreq = HAL_TICK_FREQ_DEFAULT;  /* 1KHz */

抽象了,用人话说就是,获取当前的时间,然后逐步加一点去逼近(当前时间+delay)这个数,每次前进的步伐大小根据需要选一个。

至于步伐HAL_TickFreqTypeDef为啥要这么定义,咱们这么想,在最终值大小一样的时候,频率越大,每次前进的值越小。

再通俗一点,跑800米,同样是3min到达,你步子小,就得迈得快。

HAL_Delay()引发的问题

平时使用这个函数的时候,大概都是在main函数里面while里面写HAL_Dekay(1000);

这就可能会出现刚刚前面说过的各种bug,别想了,你老板肯定不高兴。

仔细一看,好家伙,这巨大一个while死循环可,真是棒啊。

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

还记得自己是个单片机吧,也就是说,一旦程序进入这个流程,就得卡到while条件不符合退出,否则就每次都是执行一条空指令,返回while,再来,再返回……

对于实时性要求特别高的硬件,比如按钮、ADC、RS485通讯等等,就比较无力了。

问题解决方案

如果想要实现更灵活的延时可以参考上回用定时器实现按键检测

自实现精确定时

那HAL_Delay()怎么看怎么不对劲,那毕竟人家姓HAL啊,咱还是得自己有活计才行。

per_count=SystemCoreClock/1000000;   
            
void delay_us(uint16_t us)
{
    uint32_t ctrl;                               //工具人
	SysTick->LOAD=per_count*us;                  //装载值,每微秒的时钟脉冲数*需要us微秒
	SysTick->VAL=0x00;        			         //清零当前值寄存器
	SysTick->CTRL=0x00000005;		             //控制和状态寄存器,启动SysTick定时器	
	do
	{
		ctrl=SysTick->CTRL;
	}
	while(temp&0x01&&!(temp&(1<<16)));	         //SysTick->CTRL的位[0] - ENABLE(使能位),位[1] - TICKINT(中断使能位),位[2] - CLKSOURCE(时钟源选择位),位[16] - COUNTFLAG(计数标志位)
	SysTick->CTRL=0x00000004;			
	SysTick->VAL=0x00;        			
}

有了us计数,相信ms和s就不需要多说了。多来几个1000遍就可以。

下面解释一下per_count这个变量的来历:

以STM32F103C8举例:

最大72MHz,也就是说,计数一次1/72Ms,那么如果计数72次,就是1/1000000s=1us

假设我们需要延时10us,那么,就需要计数72*10次即可

而SystemCoreClock这个变量,是在系统初始化的时候改变的,根据你的芯片型号和最初在cubemax里面配置的systick来确定数值。

以下是FreeModbus STM32 HAL库中porttimer文件的示例代码,其中包含了中文注释: ``` /* * FreeModbus Libary: STM32Fxxx Port Timer For Modbus * * Copyright (C) 2013 Real Time Engineers ltd. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * * File: porttimer.c * * Description: STM32Fxxx port timer implementation for FreeModbus. */ /* ----------------------- Modbus includes ----------------------------------*/ #include "mb.h" #include "mbport.h" /* ----------------------- STM32 includes ----------------------------------*/ #include "stm32f10x.h" /* ----------------------- Defines ------------------------------------------*/ #define MB_TIMER_PRESCALER ( ( uint16_t )( ( SystemCoreClock / 2 ) / 1000000 ) - 1 ) // 定时器预分频器,用于将系统时钟分频到1MHz #define MB_TIMER_TICKS ( 20000UL ) // 定时器重装值,用于定时20ms /* ----------------------- Static variables ---------------------------------*/ static USHORT usTimerOCRADelta; /* ----------------------- Function prototypes ------------------------------*/ static void prvvTIMERExpiredISR( void ); /* ----------------------- Start implementation -----------------------------*/ BOOL xMBPortTimersInit( USHORT usTim1Timerout50us ) { TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure; NVIC_InitTypeDef NVIC_InitStructure; /* Enable the TIM2 global Interrupt */ NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn; NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 3; NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; NVIC_Init( &NVIC_InitStructure ); /* Time base configuration */ TIM_TimeBaseStructInit( &TIM_TimeBaseStructure ); TIM_TimeBaseStructure.TIM_Period = ( uint16_t )( MB_TIMER_TICKS - 1 ); TIM_TimeBaseStructure.TIM_Prescaler = MB_TIMER_PRESCALER; TIM_TimeBaseStructure.TIM_ClockDivision = 0; TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; TIM_TimeBaseInit( TIM2, &TIM_TimeBaseStructure ); /* Clear TIM2 update flag */ TIM_ClearFlag( TIM2, TIM_FLAG_Update ); /* Enable TIM2 Update interrupt */ TIM_ITConfig( TIM2, TIM_IT_Update, ENABLE ); /* Preload enable */ TIM_OC1PreloadConfig( TIM2, TIM_OCPreload_Disable ); TIM_ARRPreloadConfig( TIM2, ENABLE ); /* TIM2 enable counter */ TIM_Cmd( TIM2, ENABLE ); /* Timer delta value for Modbus tick */ usTimerOCRADelta = ( uint16_t )( ( ( uint32_t )usTim1Timerout50us * ( uint32_t )( MB_TIMER_TICKS ) ) / ( uint32_t )20000 ); return TRUE; } void vMBPortTimersEnable( ) { /* Enable the timer with the timeout passed to xMBPortTimersInit( ) */ TIM_SetAutoreload( TIM2, ( uint16_t )( MB_TIMER_TICKS - 1 ) ); TIM_SetCompare1( TIM2, TIM_GetCounter( TIM2 ) + usTimerOCRADelta ); TIM_Cmd( TIM2, ENABLE ); } void vMBPortTimersDisable( ) { /* Disable any pending timers. */ TIM_Cmd( TIM2, DISABLE ); TIM_SetCounter( TIM2, 0 ); } /* ----------------------- Timers functions ---------------------------------*/ void prvvTIMERExpiredISR( void ) { ( void )pxMBPortCBTimerExpired( ); } void TIM2_IRQHandler( void ) { if( TIM_GetITStatus( TIM2, TIM_IT_Update ) != RESET ) { TIM_ClearITPendingBit( TIM2, TIM_IT_Update ); prvvTIMERExpiredISR( ); } } ``` 这段代码实现了FreeModbus STM32 HAL库定时器功能。其中,`MB_TIMER_PRESCALER`和`MB_TIMER_TICKS`分别是定时器的预分频器和重装值,用于将定时时钟分频到1MHz并定时20ms。在`xMBPortTimersInit()`函数中,首先配置了定时器的各项参数,并设置了定时器中断的优先级和使能。在`vMBPortTimersEnable()`函数中,设置了定时器到期时间,并使能了定时器。而在`vMBPortTimersDisable()`函数中,则禁用了定时器。 此外,代码还实现了定时器到期时的中断处理函数`prvvTIMERExpiredISR()`和定时器中断服务函数`TIM2_IRQHandler()`,以便在定时器到期时触发Modbus超时处理。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值