2.17 STM32 SysTick—系统定时器

1、简介

SysTick—系统定时器是属于 CM4 内核中的一个外设,内嵌在 NVIC 中。系统定时器是一个 24bit 的向下递减的计数器,计数器每计数一次的时间为 1/SYSCLK,一般我们设置系统时钟 SYSCLK 等于 180M。当重装载数值寄存器的值递减到 0 的时候,系统定时器就产生一次中断,以此循环往复。

因为 SysTick 是属于 CM4 内核的外设,所以所有基于 CM4 内核的单片机都具有这个系统定时器,使得软件在 CM4 单片机中可以很容易的移植。系统定时器一般用于操作系统,用于产生时基,维持操作系统的心跳。

2、寄存器

寄存器名称寄存器描述
CTRLSysTick 控制及状态寄存器
LOADSysTick 重装载数值寄存器
VALSysTick 当前数值寄存器
CALIBSysTick 校准数值寄存器

2.1 SysTick控制和状态寄存器(STK_CTRL)

偏移地址:0x00
复位值: 0x0000 0000
在这里插入图片描述
COUNTFLAG:如果在上次读取本寄存器后, SysTick 已经计到了 0,则该位为 1。
CLKSOURCE:时钟源选择位,0=AHB/8,1=处理器时钟 AHB
TICKINT:1=SysTick 倒数计数到 0 时产生 SysTick 异常请求,0=数到 0 时无动作。也可以通过读取COUNTFLAG 标志位来确定计数器是否递减到0
ENABLE:SysTick 定时器的使能位

2.2 重载值寄存器(STK_LOAD)

偏移地址:0x04
复位值: 0x0000 0000
在这里插入图片描述
当计算器使能且计数值到0时,LOAD寄存器的值重加载到STK_VAL寄存器。

2.3 SysTick当前值寄存器 (STK_VAL)

偏移地址:0x08
复位值: 0x0000 0000
在这里插入图片描述
VAL寄存器包含SysTick计数器的当前值

2.4 校准寄存器(STK_CALIB)

偏移地址:0x0C
复位值: 0x0000 0000
在这里插入图片描述

3、SysTick 定时实验

3.1 在标准库中

利用 SysTick 产生 1s 的时基,LED 以 1s 的频率闪烁。

int main(void)
{
	/* LED 端口初始化 */
	LED_GPIO_Config();	 
 
  /* 配置SysTick 为10us中断一次,时间到后触发定时中断,
	*进入stm32fxx_it.c文件的SysTick_Handler处理,通过数中断次数计时
	*/
	SysTick_Init();
  
	while(1)
	{

		LED_RED; 
	  Delay_us(100000);    	// 10000 * 10us = 1000ms
		//Delay_ms(1000);
	  
		LED_GREEN;
	  Delay_us(100000);		// 10000 * 10us = 1000ms
		//Delay_ms(1000);
	
		LED_BLUE;
	  Delay_us(100000);		// 10000 * 10us = 1000ms
		//Delay_ms(1000);


	}   
}

/**
  * @brief  启动系统滴答定时器 SysTick
  * @param  无
  * @retval 无
  */
void SysTick_Init(void)
{
	/* SystemFrequency / 1000    1ms中断一次
	 * SystemFrequency / 100000	 10us中断一次
	 * SystemFrequency / 1000000 1us中断一次
	 */
	 //uint32_t SystemCoreClock = 180000000;
	if (SysTick_Config(SystemCoreClock / 100000))
	{ 
		/* Capture error */ 
		while (1);
	}
}
/** \brief  System Tick Configuration

    The function initializes the System Timer and its interrupt, and starts the System Tick Timer.
    Counter is in free running mode to generate periodic interrupts.

    \param [in]  ticks  Number of ticks between two interrupts.

    \return          0  Function succeeded.
    \return          1  Function failed.

    \note     When the variable <b>__Vendor_SysTickConfig</b> is set to 1, then the
    function <b>SysTick_Config</b> is not included. In this case, the file <b><i>device</i>.h</b>
    must contain a vendor-specific implementation of this function.

 */
__STATIC_INLINE uint32_t SysTick_Config(uint32_t ticks)
{
  if ((ticks - 1) > SysTick_LOAD_RELOAD_Msk)  return (1);      /* Reload value impossible */
// 设置重装载寄存器
  SysTick->LOAD  = ticks - 1;                                  /* set reload register */
  // 设置中断优先级
  NVIC_SetPriority (SysTick_IRQn, (1<<__NVIC_PRIO_BITS) - 1);  /* set Priority for Systick Interrupt */
  SysTick->VAL   = 0;                                          /* Load the SysTick Counter Value */
 // 设置系统定时器的时钟源为 AHBCLK=180M
 // 使能系统定时器中断
 // 使能定时器
  SysTick->CTRL
  SysTick->CTRL  = SysTick_CTRL_CLKSOURCE_Msk |
                   SysTick_CTRL_TICKINT_Msk   |
                   SysTick_CTRL_ENABLE_Msk;                    /* Enable SysTick IRQ and SysTick Timer */
  return (0);                                                  /* Function successful */
}

用固件库编程的时候我们只需要调用库函数 SysTick_Config()即可,形参 ticks 用来设置重装载寄存器的值,最大不能超过重装载寄存器的值 224,当重装载寄存器的值递减到 0的时候产生中断,然后重装载寄存器的值又重新装载往下递减计数,以此循环往复。紧随其后设置好中断优先级,最后配置系统定时器的时钟为 180M,使能定时器和定时器中断,这样系统定时器就配置好了,一个库函数搞定。

3.2 在HAL库中的初始化

/**
  * @brief This function configures the source of the time base:
  *        The time source is configured to have 1ms time base with a dedicated
  *        Tick interrupt priority.
  * @note This function is called  automatically at the beginning of program after
  *       reset by HAL_Init() or at any time when clock is reconfigured  by HAL_RCC_ClockConfig().
  * @note In the default implementation, SysTick timer is the source of time base.
  *       It is used to generate interrupts at regular time intervals.
  *       Care must be taken if HAL_Delay() is called from a peripheral ISR process,
  *       The SysTick interrupt must have higher priority (numerically lower)
  *       than the peripheral interrupt. Otherwise the caller ISR process will be blocked.
  *       The function is declared as __weak  to be overwritten  in case of other
  *       implementation  in user file.
  * @param TickPriority: Tick interrupt priority.
  * @retval HAL status
  */
__weak HAL_StatusTypeDef HAL_InitTick(uint32_t TickPriority)
{
  HAL_StatusTypeDef  status = HAL_OK;

  if (uwTickFreq != 0U)
  {
    /* Configure the SysTick to have interrupt in 1ms time basis*/
    //SystemCoreClock  = 16000000UL,uwTickFreq=1
    if (HAL_SYSTICK_Config(SystemCoreClock / (1000U / uwTickFreq)) == 0U)
    {
      /* Configure the SysTick IRQ priority */
      if (TickPriority < (1UL << __NVIC_PRIO_BITS))
      {
        HAL_NVIC_SetPriority(SysTick_IRQn, TickPriority, 0U);
        uwTickPrio = TickPriority;
      }
      else
      {
        status = HAL_ERROR;
      }
    }
    else
    {
      status = HAL_ERROR;
    }
  }
  else
  {
    status = HAL_ERROR;
  }

  /* Return function status */
  return status;
}

4、SysTick 定时再RTX系统中延时的使用

#define delay_us(us)			Delay_Us(us)
#define delay_ms(us)			Delay_Us(us*1000)

#include "delay.h"
#include "os_tick.h"
void Delay_Us(uint32_t u32Us)
{
	uint32_t u32Last = SysTick->VAL;//SysTick当前值寄存器 
	uint32_t u32Cnt = 0;
	uint32_t u32Period = OS_Tick_GetInterval();//SysTick->LOAD + 1U,重载值寄存器的值。如果在跑系统,设置的是计时1ms LOAD的值,即SystemCoreClock / (1000U);
	u32Us *= (SystemCoreClock / 1000000);//计时u32Us uS的计数值
	while(1)
	{
		uint32_t u32Now = SysTick->VAL;//SysTick当前值寄存器(递减)
		if(u32Now != u32Last)
		{
			if(u32Now < u32Last)
				u32Cnt += (u32Last - u32Now);//SYSTICK是递减的计数器就可以.
			else
				u32Cnt += (u32Period - u32Now + u32Last);
			if(u32Cnt >= u32Us)
				break; //时间超过/等于要延迟的时间,则退出.
			u32Last = u32Now;
		}
	}
}
/*
OS Tick API
OS_Tick_Setup : 建立 OS Tick 。
OS_Tick_Enable : 启用 OS Tick 。
OS_Tick_Disable : 禁用 OS Tick 。
OS_Tick_AcknowledgeIRQ : 确认 OS Tick IRQ 。
OS_Tick_GetIRQn : 获取 OS Tick IRQ 编号。
OS_Tick_GetClock : 获取 OS Tick 时钟。
OS_Tick_GetInterval : 获取 OS Tick 间隔。
OS_Tick_GetCount : 获取 OS Tick 计数值。
OS_Tick_GetOverflow : 获取 OS Tick 溢出状态。
*/

以下是RTX里,os_systick.c里的代码

/**************************************************************************//**
 * @file     os_systick.c
 * @brief    CMSIS OS Tick SysTick implementation
 * @version  V1.0.3
 * @date     19. March 2021
 ******************************************************************************/
/*
 * Copyright (c) 2017-2021 ARM Limited. All rights reserved.
 *
 * SPDX-License-Identifier: Apache-2.0
 *
 * Licensed under the Apache License, Version 2.0 (the License); you may
 * not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an AS IS BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "os_tick.h"

//lint -emacro((923,9078),SCB,SysTick) "cast from unsigned long to pointer"
#include "RTE_Components.h"
#include CMSIS_device_header

#ifdef  SysTick

#ifndef SYSTICK_IRQ_PRIORITY
#define SYSTICK_IRQ_PRIORITY    0xFFU
#endif

static uint8_t PendST __attribute__((section(".bss.os")));

// Setup OS Tick.
__WEAK int32_t OS_Tick_Setup (uint32_t freq, IRQHandler_t handler) {
  uint32_t load;
  (void)handler;

  if (freq == 0U) {
    //lint -e{904} "Return statement before end of function"
    return (-1);
  }

  load = (SystemCoreClock / freq) - 1U;
  if (load > 0x00FFFFFFU) {
    //lint -e{904} "Return statement before end of function"
    return (-1);
  }

  // Set SysTick Interrupt Priority
#if   ((defined(__ARM_ARCH_8M_MAIN__)   && (__ARM_ARCH_8M_MAIN__   != 0)) || \
       (defined(__ARM_ARCH_8_1M_MAIN__) && (__ARM_ARCH_8_1M_MAIN__ != 0)) || \
       (defined(__CORTEX_M)             && (__CORTEX_M             == 7U)))
  SCB->SHPR[11] = SYSTICK_IRQ_PRIORITY;
#elif  (defined(__ARM_ARCH_8M_BASE__)   && (__ARM_ARCH_8M_BASE__   != 0))
  SCB->SHPR[1] |= ((uint32_t)SYSTICK_IRQ_PRIORITY << 24);
#elif ((defined(__ARM_ARCH_7M__)        && (__ARM_ARCH_7M__        != 0)) || \
       (defined(__ARM_ARCH_7EM__)       && (__ARM_ARCH_7EM__       != 0)))
  SCB->SHP[11]  = SYSTICK_IRQ_PRIORITY;
#elif  (defined(__ARM_ARCH_6M__)        && (__ARM_ARCH_6M__        != 0))
  SCB->SHP[1]  |= ((uint32_t)SYSTICK_IRQ_PRIORITY << 24);
#else
#error "Unknown ARM Core!"
#endif

  SysTick->CTRL =  SysTick_CTRL_CLKSOURCE_Msk | SysTick_CTRL_TICKINT_Msk;
  SysTick->LOAD =  load;
  SysTick->VAL  =  0U;

  PendST = 0U;

  return (0);
}

/// Enable OS Tick.
__WEAK void OS_Tick_Enable (void) {

  if (PendST != 0U) {
    PendST = 0U;
    SCB->ICSR = SCB_ICSR_PENDSTSET_Msk;
  }

  SysTick->CTRL |=  SysTick_CTRL_ENABLE_Msk;
}

/// Disable OS Tick.
__WEAK void OS_Tick_Disable (void) {

  SysTick->CTRL &= ~SysTick_CTRL_ENABLE_Msk;

  if ((SCB->ICSR & SCB_ICSR_PENDSTSET_Msk) != 0U) {
    SCB->ICSR = SCB_ICSR_PENDSTCLR_Msk;
    PendST = 1U;
  }
}

// Acknowledge OS Tick IRQ.
__WEAK void OS_Tick_AcknowledgeIRQ (void) {
  (void)SysTick->CTRL;
}

// Get OS Tick IRQ number.
__WEAK int32_t  OS_Tick_GetIRQn (void) {
  return ((int32_t)SysTick_IRQn);
}

// Get OS Tick clock.
__WEAK uint32_t OS_Tick_GetClock (void) {
  return (SystemCoreClock);
}

// Get OS Tick interval.
__WEAK uint32_t OS_Tick_GetInterval (void) {
  return (SysTick->LOAD + 1U);
}

// Get OS Tick count value.
__WEAK uint32_t OS_Tick_GetCount (void) {
  uint32_t load = SysTick->LOAD;
  return  (load - SysTick->VAL);
}

// Get OS Tick overflow status.
__WEAK uint32_t OS_Tick_GetOverflow (void) {
  return ((SCB->ICSR & SCB_ICSR_PENDSTSET_Msk) >> SCB_ICSR_PENDSTSET_Pos);
}

#endif  // SysTick

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值