文章目录
- 1、简介
- 2、寄存器
- 2.1 SysTick控制和状态寄存器(STK_CTRL)
- 2.2 重载值寄存器(STK_LOAD)
- 2.3 SysTick当前值寄存器 (STK_VAL)
- 2.4 校准寄存器(STK_CALIB)
- 3、SysTick 定时实验
- 3.1 在标准库中
- 3.2 在HAL库中的初始化
- 4、SysTick 定时再RTX系统中延时的使用
1、简介
SysTick—系统定时器是属于 CM4 内核中的一个外设,内嵌在 NVIC 中。系统定时器是一个 24bit 的向下递减的计数器,计数器每计数一次的时间为 1/SYSCLK,一般我们设置系统时钟 SYSCLK 等于 180M。当重装载数值寄存器的值递减到 0 的时候,系统定时器就产生一次中断,以此循环往复。
因为 SysTick 是属于 CM4 内核的外设,所以所有基于 CM4 内核的单片机都具有这个系统定时器,使得软件在 CM4 单片机中可以很容易的移植。系统定时器一般用于操作系统,用于产生时基,维持操作系统的心跳。
2、寄存器
寄存器名称 | 寄存器描述 |
---|---|
CTRL | SysTick 控制及状态寄存器 |
LOAD | SysTick 重装载数值寄存器 |
VAL | SysTick 当前数值寄存器 |
CALIB | SysTick 校准数值寄存器 |
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