一次关于定时Tick的调试经历

如何使用定时触发某一时间

1.先看一个简单的例子,实现LED定时1ms翻转

void RefreshLED(void)
{
    HAL_Delay(1000);
    /*< Do something */
	HAL_GPIO_TogglePin(GPIOA,GPIO_PIN_0);
}
void RefreshLED(void)
{
	static uint32_t TickBase=0;
	
	if( HAL_GetTick() - TickBase >= 1000){
		TickBase = HAL_GetTick();
		/*< Do something */
        HAL_GPIO_TogglePin(GPIOA,GPIO_PIN_0);
	}
}
  • 第一种方式,阻塞式等待,CPU在HAL_Delay()空转

  • 第二种方式,非阻塞等待,判断当前tick与基准tick之间的差值是否大于目标阈值,使用这种结构相比较阻塞式等待能大幅提升效率,CPU执行到此处判断当前系统时间与系统时间基准的差值,不满足直接执行后续其他操作

2.分析HAL_GetTick源码

__IO uint32_t uwTick;
HAL_TickFreqTypeDef uwTickFreq = HAL_TICK_FREQ_DEFAULT;  /* 1KHz */

/**
  * @brief Provides a tick value in millisecond.
  * @note This function is declared as __weak to be overwritten in case of other 
  *       implementations in user file.
  * @retval tick value
  */
__weak uint32_t HAL_GetTick(void)
{
  return uwTick;
}


/**
  * @brief This function is called to increment  a global variable "uwTick"
  *        used as application time base.
  * @note In the default implementation, this variable is incremented each 1ms
  *       in SysTick ISR.
 * @note This function is declared as __weak to be overwritten in case of other 
  *      implementations in user file.
  * @retval None
  */
__weak void HAL_IncTick(void)
{
  uwTick += uwTickFreq;
}

/**
  * @brief This function handles System tick timer.
  */
void SysTick_Handler(void)
{
  /* USER CODE BEGIN SysTick_IRQn 0 */

  /* USER CODE END SysTick_IRQn 0 */
  HAL_IncTick();
  /* USER CODE BEGIN SysTick_IRQn 1 */

  /* USER CODE END SysTick_IRQn 1 */
}
  • HAL_GetTick源码,本质是返回一个自增的系统Tick值,值递增,在SysTick_Handler中断中自增

3.HAL_GetTick() - TickBase

  • HAL_GetTick返回的是unsigend int类型,TickBase也是unsigend int类型,
  • uint32_t - uint32_t = uint32_t
  • 5 - 0xFFFFFFFF = 6;
uint32_t        uint32_t
( HAL_GetTick() - TickBase > 1000)
    

需要关心的是TickBase,如果TickBase只在当前位置使用则没有关系,但当TickBase被使用在中断内如果出现运算过程中外部中断触发之后HAL_GetTick获取的是之前的值的状态,就会出现,0-1 = 0xFFFFFFFF的状态,这种情况很少见但会出现,使用时一定要注意

uint32_t TickBase=0;

void interrupt_handler(void)
{
    //3.
    TickBase = HAL_GetTick();
}

void SysTick_Handler(void)
{
    //2.
    HAL_IncTick();
}

//1.
if( HAL_GetTick() - TickBase >= 1000){
    TickBase = HAL_GetTick();
    /*< Do something */
    HAL_GPIO_TogglePin(GPIOA,GPIO_PIN_0);
}

//HAL_GetTick()返回的是uint32_t 返回当前的系统tick
//1.如果HAL_GetTick()完之后,执行过程中触发SysTick_Handler中断
//2.tick++
//3.触发中断interrupt_handler,读取当前tick为TickBase
//会出现什么状况呢?假设步骤1暂存的值位1000,在步骤3之后有概率为1001 在1000-1001之后值为-1也为0xFFFFFFFF满足>=1000条件,触发条件

总结

if( HAL_GetTick() - TickBase >= 1000){
    TickBase = HAL_GetTick();
}
  • 使用这种方式时基只能在这个地方修改或在非中断位置修改时基,需要在中断中修改时基的不建议采用这种方式

使用另一种方式延时

extern uint32_t gPowerTick=0;

void SysTick_Handler(void)
{
    HAL_IncTick();
    if(gPowerTick) gPowerTick--;
}

void interrupt_handler(void)
{
    //3.
    gPowerTick = 1000;
}

if(gPowerTick);
else{
    gPowerTick = 1000;
    /*< Do something */
}
//借助于 if(gPowerTick) gPowerTick--; 
//即使在运行过程中触发中断,也不会满足条件,
//if(gPowerTick),做判断的只有一条指令,在执行前打断,不会执行事件处理分支,在执行分支后打断已经不影响

你可能感兴趣的:(C语言知识)