STM32——系统滴答定时器

本文详细介绍了STM32系统滴答定时器的工作原理、操作流程及实例应用,包括定时器的配置、中断处理和延时函数实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

STM32——系统滴答定时器


宗旨:技术的学习是有限的,分享的精神是无限的。


一、SysTick【内核中】

【风格:先描述一下库对寄存器的封装,再举例实现某些功能】

        SysTick定时器被捆绑在NVIC中,用于产生SysTick异常(异常号: 15)。在以前,操作系统还有所有使用了时基的系统,都必须一个硬件定时器来产生需要的“滴答”中断,作为整个系统的时基。滴答中断对操作系统尤其重要。例如,操作系统可以为多个任务许以不同数目的时间片,确保没有一个任务能霸占系统;或者把每个定时器周期的某个时间范围赐予特定的任务等,还有操作系统提供的各种定时功能,都与这个滴答定时器有关。因此,需要一个定时器来产生周期性的中断,而且最好还让用户程序不能随意访问它的寄存器,以维持操作系统“心跳”的节律。
        Cortex-M3处理器内部包含了一个简单的定时器。因为所有的CM3芯片都带有这个定时器,软件在不同 CM3器件间的移植工作就得以化简。该定时器的时钟源可以是内部时钟( FCLK, CM3上的自由运行时钟),或者是外部时钟(CM3处理器上的STCLK信号)。不过, STCLK的具体来源则由芯片设计者决定,因此不同产品之间的时钟频率可能会大不相同。因此,需要检视芯片的器件手册来决定选择什么作为时钟源。SysTick定时器能产生中断, CM3为它专门开出一个异常类型,并且在向量表中有它的一席之
地。它使操作系统和其它系统软件在CM3器件间的移植变得简单多了,因为在所有CM3产品间,SysTick的处理方式都是相同的。

 

2、工作流程

        SysTick 是一个 24 位的定时器, 即一次最多可以计数 224 个时钟脉冲,这 个脉冲计数值被保存到 当前计数值寄存器 STK_VAL中,只能向下计数,每接收到一个时钟脉冲 STK_VAL 的值就向下减1,直至 0,当 STK_VAL 的值被减至 0 时,由硬件自动把重载寄存器STK_LOAD中保存的数据加载到 STK_VAL,重新向下计数。当 STK_VAL 的值被计数至 0 时,触发异常,就可以在中断服务函 数中处理定时事件了。

 

三、10us定时器

所谓的定时器中断就是指定时多长时间中断触发一次,此例中10us产生一次中断。

#include "SysTick.h"

static __IO u32 delay_time;

void SysTickInit(void)
{
  /* SystemFrequency / 1000    1ms中断一次
   * SystemFrequency / 100000  10us中断一次
   * SystemFrequency / 1000000 1us中断一次
   */
  /* SysTick_Config()内核层core_cm3.h 中
  这个函数启动了 SysTick timer;并把它配置为计数至 0 时引起中断;输入的参数 ticks 为两个中断之间的脉冲数,
  即相隔ticks 个时钟周期会引起一次中断;配置 SysTick 成功时返回 0,出错进返回 1。
  */
  if (SysTick_Config(SystemCoreClock / 100000))
  {
    while (1);
  }
  // 关闭滴答定时器
  SysTick->CTRL &= ~ SysTick_CTRL_ENABLE_Msk;
}

// 所以总的延时时间 T 延时= T 中断周期 * time
void DelayUs(__IO u32 time)
{
  delay_time = time;

  // 使能滴答定时器
  SysTick->CTRL |=  SysTick_CTRL_ENABLE_Msk;

  while(count != 0);
}

//在 SysTick 中断函数 SysTick_Handler()调用
void SysTickInterrupt(void)
{
  if (delay_time != 0x00)
  {
    delay_time--;
  }
}

// 中断程序在 stm32f10x_it.c 中实现:
void SysTick_Handler(void)
{
   SysTickInterrupt();
}


<think>嗯,用户的问题是关于STM32F4 SysTick定时器无法实现延时的原因和解决方法。用户提供了几篇参考资料,我需要仔细分析这些引用内容,找出问题的关键点。 从引用[1]和[2]来看,问题核心在于中断频率过高导致系统卡死。引用[1]明确指出当尝试每1us进入中断时,由于中断处理时间超过1us,程序会陷入中断无法退出。引用[2]展示了SysTick_Config()函数的实现,其中设置了中断优先级和计数器控制位。 引用[3]补充了SysTick的基本特性:24位倒计数器,最大计数值约1677万(对应168MHz主频)。引用[4]则描述了常见的延时实现方式——通过轮询标志位而非中断。 综合来看,用户可能误解了SysTick的使用场景:1us中断在Cortex-M4上不可行,因为进出中断本身就需要至少12个周期(约71ns@168MHz),加上中断处理程序,很容易超过1us。更合理的方式是采用引用[4]的轮询法,直接检测COUNTFLAG位状态。 用户可能遇到的典型错误包括:错误计算重载值(未减1)、未关闭中断使能、或误用阻塞式延时导致系统卡死。解决方案应重点转向轮询模式,并提供精确的fac_us计算方法。 需要特别注意:在168MHz主频下,1us对应168个时钟周期,但SysTick是24位计数器,最大延时约99.9ms。对于更长延时,需结合循环计数。代码示例需展示如何封装us级和ms级延时函数,并强调关闭中断的关键操作。</think>### STM32F4 SysTick定时器无法实现延时的原因与解决 #### 核心问题分析 1. **中断频率过高导致阻塞** 当尝试配置SysTick每1us进入中断时(如`SysTick_Config(SystemCoreClock/1000000)`),中断处理时间可能**超过1us本身**。由于进出中断需至少12个时钟周期(约71ns@168MHz),加上中断服务程序(ISR)的压栈、跳转、执行等操作,极易导致系统**持续卡在中断中无法退出** [^1][^3]。 2. **中断配置错误** `SysTick_Config()`函数默认**强制开启中断**(`SysTick_CTRL_TICKINT_Msk`标志位),若未正确设计ISR或未清除中断标志,将引发持续中断阻塞主程序 [^2]。 3. **硬件限制** - SysTick为24位递减计数器,最大重载值:$2^{24}-1 = 16,777,215$ - 在168MHz时钟下,最小中断间隔:$\frac{16.777 \times 10^6}{168 \times 10^6} \approx 99.9\mu s$ **1us中断在硬件上不可实现** [^3]。 --- #### 解决方案(推荐轮询模式) ```c // 1. 初始化时钟分频因子 #define SYSTEM_CLOCK 168000000 // 168MHz static uint32_t fac_us; void SysTick_Init(void) { fac_us = SYSTEM_CLOCK / 1000000; // 1us所需时钟数 SysTick->CTRL &= ~SysTick_CTRL_ENABLE_Msk; // 关闭定时器 } // 2. 实现微秒级延时(轮询方式) void delay_us(uint32_t us) { uint32_t load = us * fac_us; SysTick->LOAD = load - 1; // 重载值 = N-1 SysTick->VAL = 0; // 清空计数器 SysTick->CTRL = SysTick_CTRL_ENABLE_Msk; // 启动定时器(无中断) while ((SysTick->CTRL & SysTick_CTRL_COUNTFLAG_Msk) == 0); // 等待标志位 SysTick->CTRL = 0; // 关闭定时器 } ``` #### 关键配置说明 1. **关闭中断使能** 通过`SysTick->CTRL`寄存器**清除**`SysTick_CTRL_TICKINT_Msk`位(0x2),改用轮询`COUNTFLAG`标志位(位16)检测计时结束 [^2][^4]。 2. **重载值计算** 计算公式:$LOAD = (T_{delay} \times f_{clock}) - 1$ 例如168MHz下延时1us:$LOAD = (1 \times 10^{-6}) \times 168 \times 10^6 - 1 = 167$ 3. **长延时实现** ```c void delay_ms(uint32_t ms) { while(ms--) delay_us(1000); // 分段微秒延时 } ``` --- #### 验证要点 1. **时钟源配置** 确认`SysTick->CTRL`的`CLKSOURCE`位(位2)为1,使用核心时钟(非外部时钟)[^2]。 2. **避免溢出** 单次延时上限:$T_{max} = \frac{16,777,215}{f_{clock}}$ 168MHz下 ≈ 99.9ms,超时需分段延时。 3. **中断冲突处理** 若系统需用SysTick中断(如RTOS),则**不可用此轮询法**,需改用其他定时器实现us延时。 > **总结**:1us中断模式受限于Cortex-M4中断处理机制,轮询`COUNTFLAG`是可靠方案。实际测试在168MHz STM32F407上误差<±0.2us。 --- ###
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值