好记性不如铅笔头

ARM, FreeRTOS, STM32, 操作系统

STM32CubeMX简单使用笔记:us延迟以及systick

因为项目需要,最近重新拾起来STM32,正好趁着这个机会好好的梳理下遇到的知识细节。

参考链接【 https://www.cnblogs.com/pheye/p/5630938.html

由于STM32系列的主频非常高,而且有指令流水线和代码缓存,如果要高精度定时,一般使用硬件定时任务来处理,这里简单笔记下如何使用cortexM内核自带的SysTick定时器来进行高精度延迟。

SysTick定时器简单笔记

Cortex‐Mx处理器内部包含的定时器。SysTick定时器被捆绑在NVIC中,用于产生SYSTICK异常(异常号:15)。该定时器的时钟源可以是内部时钟(FCLK,CMx上的自由运行时钟),或者是外部时钟( CMx处理器上的STCLK信号)
有4个寄存器控制SysTick定时器,如下图:

简单总结来看:
1.SysTick定时器是一个cortexMx内部自带的定时器,时钟来源为FCLK或者STCLK
2.SysTick定时器以递减方式来对当前数值(0xE000_E018)处理,当倒数为0时,会产生SYSTICK异常,同时当前数值会重置为默认值(0xE000_E014)。

STM32如何提供SysTick时钟

打开STM32时钟树,可以看到:

SYSCLK经过AHB预分配器降频后:
1路给到了HCLK
1路在经过可选8分频后给到了 Cortex系统定时器
1路给到了FCLK Cortex自由运行时钟

STM32CubeMX中,我们可以看到如下配置:

1 HCLK作为AHB总线频率,就是cortexMx的主频
2 FCLK = HCLK,所谓”自由运行时钟(free running clock)”。“自由”表现在它不来自系统时钟HCLK,因此在系统时钟停止时FCLK也继续运行。FCLK和HCLK相等。
3 Cortex系统定时器 即为STCLK,由于STCLK在内部是通过FCLK来采样的,因此在STCLK有效使用的情况下,周期必须至少是 FCLK 的两倍。一般情况下,我们都不是用STCLK,因此该值一般都没有配置。

SysTick如何配置和us延迟实现

FreeRTOS版本

在使用FreeRTOS时,FreeRTOS使用SysTick作为时基,使用SysTick异常来启动线程切换,默认情况下,SysTick超时时间设置为1ms。代码入口如下:

//在启动Schedule时,调用了如下函数:
__attribute__(( weak )) void vPortSetupTimerInterrupt( void )
{
。。。。
。。。。
	/* Stop and clear the SysTick. */

/*
#define portNVIC_SYSTICK_CTRL_REG			( * ( ( volatile uint32_t * ) 0xe000e010 ) )
#define portNVIC_SYSTICK_LOAD_REG			( * ( ( volatile uint32_t * ) 0xe000e014 ) )
#define portNVIC_SYSTICK_CURRENT_VALUE_REG	( * ( ( volatile uint32_t * ) 0xe000e018 ) )
*/
	portNVIC_SYSTICK_CTRL_REG = 0UL;
	portNVIC_SYSTICK_CURRENT_VALUE_REG = 0UL;

	/* Configure SysTick to interrupt at the requested rate. */
    /*
    configTICK_RATE_HZ = 1000,表示1秒钟内触发中断1000次
    configSYSTICK_CLOCK_HZ为CPU主频
    */
	portNVIC_SYSTICK_LOAD_REG = ( configSYSTICK_CLOCK_HZ / configTICK_RATE_HZ ) - 1UL;

    /*
    portNVIC_SYSTICK_CLK_BIT = 1<<2 表示使用FCLK
    portNVIC_SYSTICK_INT_BIT = 1<<1 表示使用倒数到0时,产生异常
    portNVIC_SYSTICK_ENABLE_BIT = 1<<0 表示使能SysTick定时器
    */    
	portNVIC_SYSTICK_CTRL_REG = ( portNVIC_SYSTICK_CLK_BIT | portNVIC_SYSTICK_INT_BIT | portNVIC_SYSTICK_ENABLE_BIT );
}

STM32CubeMX生成代码默认都实现cmsis标准,cmsis版本的us延迟可以如下实现:

#define MY_MS_TO_TICKS(ms)     ((ms) * osKernelGetTickFreq() / 1000)
#define my_delay_ms(delay_ms)  osDelay(MY_MS_TO_TICKS((delay_ms)))

void my_delay_us(uint32_t delay_us)
{
/*
 *  tick_irq_freq:sys_tick 中断超时频率
 *  sys_tick_freq:sys_tick 嘀嗒频率
 * portNVIC_SYSTICK_CTRL_REG = ( portNVIC_SYSTICK_CLK_BIT ...) 说明systick的来源为FCLK
 * FCLK = HCLK = SYS_CLK
 */
	uint32_t tick_irq_freq = osKernelGetTickFreq();
	uint32_t sys_tick_freq = osKernelGetSysTimerFreq();

	if(delay_us >= tick_irq_freq)
	{
		my_delay_ms(delay_us / tick_irq_freq);
		delay_us = delay_us - delay_us / tick_irq_freq * tick_irq_freq;
	}

#define portNVIC_SYSTICK_CURRENT_VALUE_REG	( * ( ( volatile uint32_t * ) 0xe000e018 ) )
#define portNVIC_SYSTICK_LOAD_REG			( * ( ( volatile uint32_t * ) 0xe000e014 ) )

	uint32_t tick_reload_val = portNVIC_SYSTICK_LOAD_REG;
	uint32_t delay_tick = sys_tick_freq / 1000000 * delay_us;

//可选关闭外部中断
//__disable_irq();
	uint32_t old_tick = portNVIC_SYSTICK_CURRENT_VALUE_REG;
	uint32_t curr_tick = 0;
	while(1)
	{
		curr_tick = portNVIC_SYSTICK_CURRENT_VALUE_REG;
		if ( ( curr_tick < old_tick ? (old_tick - curr_tick) : (tick_reload_val - curr_tick + old_tick) ) >= delay_tick )
		{
			break;
		}
	}

//可选打开外部中断
//__enable_irq();
}

无系统版本

STM32CubeMX在不选用操作系统时,STM32CubeMX默认使用SysTick作为时基,使用SysTick异常来进行HAL层ticks维护,默认情况下,SysTick超时时间设置为1ms。代码入口如下:

//在RCC设置完成之后,会调用如下函数:
__weak HAL_StatusTypeDef HAL_InitTick(uint32_t TickPriority)
{
  /* Configure the SysTick to have interrupt in 1ms time basis*/
  /*
  SystemCoreClock为CPU主频
  HAL层默认为SYSTICK超时为1ms
  */
  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;
}
。。。。。。
。。。。。。
__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 */

/*
表示使用FCLK, 使用倒数到0时,产生异常, 使能SysTick定时器
*/
  SysTick->CTRL  = SysTick_CTRL_CLKSOURCE_Msk |
                   SysTick_CTRL_TICKINT_Msk   |
                   SysTick_CTRL_ENABLE_Msk;                         /* Enable SysTick IRQ and SysTick Timer */
  return (0UL);                                                     /* Function successful */
}

STM32CubeMX在不选用操作系统时,不支持cmsis标准,

void hal_delay_us(uint32_t delay_us)
{
/*
 *  tick_irq_freq:sys_tick 中断超时频率
 *  sys_tick_freq:sys_tick 嘀嗒频率
 *  根据 SysTick->CTRL  = SysTick_CTRL_CLKSOURCE_Msk | ... 说明systick的来源为FCLK
 *  FCLK = HCLK = SYS_CLK
 */
	uint32_t tick_irq_freq = 1000;
	uint32_t sys_tick_freq = HAL_RCC_GetHCLKFreq();

	if(delay_us >= tick_irq_freq)
	{
		HAL_Delay(delay_us / tick_irq_freq);
		delay_us = delay_us - delay_us/tick_irq_freq*tick_irq_freq;
	}

#define SYSTICK_CURRENT_VALUE_REG	( * ( ( volatile uint32_t * ) 0xe000e018 ) )  // 也可以使用寄存器定义:SysTick->VAL
#define SYSTICK_LOAD_REG			( * ( ( volatile uint32_t * ) 0xe000e014 ) )  // 也可以使用寄存器定义:SysTick->LOAD
	uint32_t tick_reload_val = SYSTICK_LOAD_REG;
	uint32_t delay_tick = delay_us * sys_tick_freq / 1000000;

	uint32_t old_tick = SYSTICK_CURRENT_VALUE_REG;
	uint32_t curr_tick = 0;
	while(1)
	{
		curr_tick = SYSTICK_CURRENT_VALUE_REG;
		if ( ( curr_tick < old_tick ? (old_tick - curr_tick) : (tick_reload_val - curr_tick + old_tick) ) >= delay_tick )
		{
			break;
		}
	}
}

 

us延迟可以如下实现:

Leave a Reply

7 − 5 =

此站点使用Akismet来减少垃圾评论。了解我们如何处理您的评论数据