当前位置: 首页 > news >正文

74_SysTick滴答定时器中断

文章目录

  • 一、发展历史:操作系统催生的内核定时器
    • 1. 诞生:为时间片而生
    • 2. 嵌入化:从 `RTOS` 到裸机延时的通用工具
    • 3. 技术演进:从简单定时到系统心跳
  • 二、工作原理:一个永远倒计时的数字沙漏
    • 1. 最直白的比喻:沙漏
    • 2. 核心机制:硬件自动重载与标志轮询
    • 3. 关键特性一:`24` 位的限制与溢出处理
    • 4. 关键特性二:两种时钟源可选
  • 三、技术规格:`4` 个寄存器管好一个定时器
    • 1. 寄存器一览
    • 2. `CTRL(控制与状态寄存器)` 寄存器位域详解
    • 3. `LOAD(重装载值寄存器)` 与 `VAL(当前计数值寄存器)` 的关系
    • 4. 初始化 `SysTick`
  • 四、硬件实现:`SysTick` 功能框图
    • 1. 功能框图
    • 2. 特殊机制:`VAL(当前计数值寄存器)` 写操作即时生效
  • 五、实践指南:用 `SysTick` 实现毫秒和微秒延时
    • 1. 配置流程三步走
    • 2. 以 `72MHz` `HCLK` 为例实现毫秒延时
      • (1) 参数确定
      • (2) 初始化代码
      • (3) 毫秒延时函数
      • (4) 微秒延时函数
  • 六、多定时器配合:`SysTick` + `TIM` 的典型分工
  • 七、任意平台纯软件延时 vs 硬件 `SysTick`
    • 1. 核心思想
    • 2. 纯软件微秒延时
    • 3. 软件方式 vs 硬件 `SysTick`
  • 八、提高延时精度与稳定性的技巧
    • 1. 延时期间关总中断
    • 2. 使用 `SysTick_Config` 中断模式替代轮询
    • 3. 校准因子按实际频率重算
  • 九、SysTick这里为什么不初始化GPIO?
    • 核心答案:`SysTick` 是**内核内部**的定时器,不需要也不存在 `GPIO` 引脚
    • 图解:`SysTick` 在内核中的位置
    • 为什么 `SysTick` 不需要 `GPIO` 初始化?三个根本原因
      • 1. 物理上不存在引脚
      • 2. 时钟来源是内核时钟,不是外部引脚
      • 3. 操作方式是对寄存器读写,不涉及引脚状态
    • 一个类比帮助你理解
    • 所以,回到你的问题
    • 验证一下:看 `SysTick` 的寄存器地址
  • 十、结语:内核自带的心跳,最轻量的时钟

你可能每天都在用delay_ms(1000),却不知道是谁在背后帮你精准地数着这1000毫秒。它不是某个外设,而是Cortex-M内核自带的“心跳”——SysTick,全称System Tick Timer(系统节拍定时器)。 今天这篇博客,咱们把它从24位倒计时器的硬件结构,一路拆解到毫秒/微秒延时的代码实现。读完你会发现,原来delay_ms里还藏着计数值溢出的数学游戏。

一、发展历史:操作系统催生的内核定时器

1. 诞生:为时间片而生

在嵌入式实时操作系统(RTOS)中,多个任务并发执行,CPU必须在任务之间快速切换。这就要求有一个精准的定时器来产生固定间隔的“时间片”中断——时间一到,不管当前任务执行到哪,都必须停下来换下一个任务。ARM在设计Cortex-M内核时,直接把这样一个定时器做进了内核里,取名SysTick。它不是挂在APB总线上的“外设”,而是内核的标配组件——任何Cortex-M芯片,不管MCU厂商是谁,SysTick都在那里寄存器地址完全一样。

2. 嵌入化:从RTOS到裸机延时的通用工具

SysTick最初为RTOS而设计,但裸机开发者很快发现它的另一个绝佳用途——精准延时。传统for循环延时受主频、编译器优化影响极大,挪一块板子就得重调循环次数。而SysTick直接挂在系统时钟上,72MHz就每1/72μs减一,精度从“大概”变成了“确切”。STM32F103SysTick是一个24位向下计数器,时钟源可选HCLK72MHz)或HCLK/89MHz),每次计数到0时产生中断或置标志位,自动从LOAD(重装载值寄存器)寄存器重装载,周而复始。

3. 技术演进:从简单定时到系统心跳

年代平台定时器类型核心特性
1990s51单片机8/16位向上计数外设定时器,需手动配置中断
2000sARM7TDMI32位外设定时器向量中断,无内核定时器
2005s 至今Cortex-M3/M424SysTick内核定時器,所有Cortex-M统一地址
未来Cortex-M5564SysTick支持虚拟化和安全扩展

二、工作原理:一个永远倒计时的数字沙漏

1. 最直白的比喻:沙漏

SysTick就像一个倒计时沙漏

  • LOAD(重装载值寄存器)寄存器:你设定沙漏的总沙子量(计数值)。
  • VAL(当前计数值寄存器)寄存器:沙漏里当前剩余的沙子量(每时钟周期减少一粒)。
  • CTRL(控制与状态寄存器)寄存器:沙漏的开关和设置面板。
  • 倒数到0:沙子漏完,沙漏自动翻转(从LOAD(重装载值寄存器)重新加载),同时举手报告“漏完了一次”。

每次沙子漏完,CTRL(控制与状态寄存器)寄存器的第16位(COUNTFLAG)被置1。你只需要读这个标志位,就知道延时到了没有。

2. 核心机制:硬件自动重载与标志轮询

SysTick的工作流程总共三步:

  1. 硬件加载:使能定时器后,硬件自动将LOAD(重装载值寄存器)寄存器的值拷贝到VAL(当前计数值寄存器)寄存器。
  2. 递减计数:每个时钟周期VAL(当前计数值寄存器)1,直到VAL(当前计数值寄存器)变为0
  3. 置标志 + 重载VAL(当前计数值寄存器)回到0的瞬间,CTRL(控制与状态寄存器)的第16位(COUNTFLAG)置1,同时如果定时器未停止,硬件再次将LOAD(重装载值寄存器)的值加载到VAL(当前计数值寄存器),继续下一轮倒数。

你只需要在while循环里反复读CTRL(控制与状态寄存器)的第16位,读到1就表示一轮计数结束。如果配置了中断,读到0CPU自动跳到SysTick_Handler

3. 关键特性一:24位的限制与溢出处理

LOAD(重装载值寄存器)VAL(当前计数值寄存器)寄存器只有24位有效,取值范围0x00000001 ~ 0x00FFFFFF1 ~ 16,777,215)。以HCLK=72MHzHCLK不分频作为时钟源为例,一轮计时最多只能跑16,777,215 / 72MHz ≈ 0.233s,想延时1秒就需要多次循环。这就是毫秒延时函数里“先按最大值循环多次,最后再计剩余值”的数学根源。

4. 关键特性二:两种时钟源可选

CTRL(控制与状态寄存器)寄存器的第2位选择时钟源:

CLKSOURCE时钟源频率(HCLK=72MHz微秒因子fac_us适用场景
1HCLK(不分频)72MHz72高精度短延时
0HCLK/89MHz9低功耗长延时,省时钟功耗

选择HCLK不分频时精度最高,单步1/72μs ≈ 13.9ns,但单轮计时最多0.233s。选择HCLK/8时单步1/9μs ≈ 111ns,但单轮计时可达1.86s

三、技术规格:4个寄存器管好一个定时器

1. 寄存器一览

SysTick只有4个寄存器,地址在Cortex-M3内核中固定不变:

寄存器地址偏移宽度功能
CTRL(控制与状态寄存器)0xE000E01032控制与状态寄存器
LOAD(重装载值寄存器)0xE000E01424位有效重装载值寄存器
VAL(当前计数值寄存器)0xE000E01824位有效当前计数值寄存器
CALIB(校准信息寄存器)0xE000E01C32校准信息寄存器(只读)

2.CTRL(控制与状态寄存器)寄存器位域详解

CTRL(控制与状态寄存器)SysTick的“控制面板”,STM32标准库通过SysTick_Config()函数间接操作它:

名称功能说明
[16]COUNTFLAG到期标志。当VAL(当前计数值寄存器)1减到0时硬件自动置1,读该位或写VAL(当前计数值寄存器)会清零
[2]CLKSOURCE时钟源选择:0=HCLK/81=HCLK
[1]TICKINT中断使能:0= 禁用中断(裸机轮询模式),1= 使能中断(RTOS模式)
[0]ENABLE定时器使能:0= 停止,1= 启动

3.LOAD(重装载值寄存器)VAL(当前计数值寄存器)的关系

LOAD(重装载值寄存器)是预设值,VAL(当前计数值寄存器)是当前值。使能定时器后:

  • 硬件自动将LOAD(重装载值寄存器)的值加载到VAL(当前计数值寄存器)
  • 每个时钟周期VAL(当前计数值寄存器)1
  • VAL(当前计数值寄存器)减到0时,COUNTFLAG1,同时LOAD(重装载值寄存器)的值再次加载到VAL(当前计数值寄存器)
  • VAL(当前计数值寄存器)寄存器会立即清零COUNTFLAG并重新从新值开始计数。

单次计数的配置:LOAD(重装载值寄存器) = NN个时钟周期后置标志)。连续计数的配置:LOAD(重装载值寄存器) = N - 1(因为自动重载后VAL(当前计数值寄存器)N开始递减,实际首次是N个周期,后续都是N-1个周期)。

4. 初始化SysTick

初始化过程非常简单,核心就是配置时钟源并计算微秒/毫秒因子:

uint32_tfac_us=9;/*CN:微秒因子,HCLK/8=9MHz时每微秒9个时钟--EN:Microsecond factor, 9 clocks per μs when HCLK/8=9MHz*/uint32_tfac_ms=9000;/*CN:毫秒因子,HCLK/8=9MHz时每毫秒9000个时钟--EN:Millisecond factor, 9000 clocks per ms when HCLK/8=9MHz*//** * Function: SysTick_Init * Description: CN:初始化SysTick定时器,选择时钟源并计算延时因子--EN:Initialize SysTick timer, select clock source and calculate delay factors * Parameters: SysTick_CLKSource - CN:时钟源选择,SysTick_CLKSource_HCLK_Div8 或 SysTick_CLKSource_HCLK--EN:Clock source, SysTick_CLKSource_HCLK_Div8 or SysTick_CLKSource_HCLK * Return VAL(当前计数值寄存器)ue:无 */voidSysTick_Init(uint32_tSysTick_CLKSource){SysTick->CTRL(控制与状态寄存器)=0;/*CN:清除所有标志,选择HCLK/8时钟,禁用中断,关闭定时器--EN:Clear all flags, select HCLK/8 clock, disable interrupt, stop timer*/if(SysTick_CLKSource_HCLK==SysTick_CLKSource)/*CN:如果选择HCLK不分频--EN:If HCLK selected (no division)*/{SysTick->CTRL(控制与状态寄存器)|=SysTick_CLKSource_HCLK;/*CN:时钟源设为HCLK(72MHz)--EN:Set clock source to HCLK (72MHz)*/fac_us=72;/*CN:72MHz时钟,每微秒72个时钟周期--EN:72MHz clock, 72 cycles per μs*/fac_ms=72000;/*CN:72MHz时钟,每毫秒72000个时钟周期--EN:72MHz clock, 72000 cycles per ms*/}else/*CN:否则选择HCLK/8--EN:Else select HCLK/8*/{fac_us=9;/*CN:9MHz时钟,每微秒9个时钟周期--EN:9MHz clock, 9 cycles per μs*/fac_ms=9000;/*CN:9MHz时钟,每毫秒9000个时钟周期--EN:9MHz clock, 9000 cycles per ms*/}}

四、硬件实现:SysTick功能框图

1. 功能框图

SysTick的内部结构极其精简——时钟源选择器 →24位向下计数器(VAL(当前计数值寄存器))→ 零检测器 →COUNTFLAG标志和中断请求。没有预分频器(因为CLKSOURCE已经提供了HCLKHCLK/8两个选项),没有捕获/比较通道。它是Cortex-M内核里最简单、最纯粹的一个定时器。

2. 特殊机制:VAL(当前计数值寄存器)写操作即时生效

VAL(当前计数值寄存器)写入任意值会立即产生两个效果:COUNTFLAG被硬件清零,同时定时器从新值开始向下计数。这意味着如果你在延时中途写了VAL(当前计数值寄存器),前一段进度直接作废。这是裸机延时函数开头总要写SysTick->VAL(当前计数值寄存器) = 0的原因——确保计时从零开始,不受上一轮遗留值影响。

五、实践指南:用SysTick实现毫秒和微秒延时

1. 配置流程三步走

SysTick的配置是所有外设中最简洁的之一:

  1. 关定时器,清标志:写CTRL(控制与状态寄存器) = 0,确保干净启动。
  2. 选时钟源,设因子:根据精度需求选HCLKHCLK/8
  3. LOAD(重装载值寄存器),清VAL(当前计数值寄存器),启动:写入目标计数值,清零当前计数器,拉高ENABLE位。

2. 以72MHzHCLK为例实现毫秒延时

(1) 参数确定

  • 时钟源:HCLK72MHz,不分频),微秒因子fac_us = 72,毫秒因子fac_ms = 72000
  • LOAD(重装载值寄存器)最大值:0x00FFFFFF = 16,777,215
  • 单轮最大延时:16,777,215 / 72,000 ≈ 233ms。超过233ms需分段循环。

(2) 初始化代码

初始化函数调用一次即可,后续fac_usfac_ms全局生效:

SysTick_Init(SysTick_CLKSource_HCLK);/*CN:选择HCLK不分频,72MHz,最高精度--EN:Select HCLK no division, 72MHz, highest precision*/

(3) 毫秒延时函数

用到的库函数/寄存器操作API及其功能说明如下:

函数名/寄存器操作功能说明
SysTick->CTRL(控制与状态寄存器)读写 SysTick 的控制/状态寄存器(位0=使能, 位1=中断, 位2=时钟源, 位16=到期标志)
SysTick->LOAD(重装载值寄存器)写入定时器重装载值(24 位有效,取值范围 1~16,777,215)
SysTick->VAL(当前计数值寄存器)写入任意值可清零当前计数值和 COUNTFLAG 标志
/** * Function: SysTick_Delay_Ms * Description: CN:SysTick毫秒延时,处理计数值超过24位上限的溢出情况--EN:SysTick millisecond delay, handles overflow when count exceeds 24-bit limit * Parameters: ms - CN:延时的毫秒数--EN:Delay in milliseconds * Return VAL(当前计数值寄存器)ue:无 */voidSysTick_Delay_Ms(uint32_tms){uint32_ttotal=ms*fac_ms;/*CN:总时钟周期数--EN:Total clock cycles*/uint32_tmax_VAL(当前计数值寄存器)=0x00FFFFFF;/*CN:24位最大值16,777,215--EN:24-bit max VAL(当前计数值寄存器)ue 16,777,215*/uint32_tcount=total/max_VAL(当前计数值寄存器);/*CN:需要按最大值计数的轮数--EN:Number of full max-VAL(当前计数值寄存器)ue rounds*/uint32_tremain=total%max_VAL(当前计数值寄存器);/*CN:最后不满一轮的剩余计数值--EN:Remaining count after full rounds*/SysTick->CTRL(控制与状态寄存器)&=~((1<<16)|0x3);/*CN:清除到期标志,关闭定时器--EN:Clear count flag, disable timer*/if(count)/*CN:需要多次按最大值计数--EN:Need multiple max-VAL(当前计数值寄存器)ue rounds*/{SysTick->LOAD(重装载值寄存器)=max_VAL(当前计数值寄存器);/*CN:加载24位最大值--EN:LOAD(重装载值寄存器) 24-bit max VAL(当前计数值寄存器)ue*/SysTick->VAL(当前计数值寄存器)=0;/*CN:清零当前计数值--EN:Clear current VAL(当前计数值寄存器)ue*/SysTick->CTRL(控制与状态寄存器)|=0x1;/*CN:启动定时器--EN:Start timer*/while(count--){while(!(SysTick->CTRL(控制与状态寄存器)&(1<<16)));/*CN:等待一轮到期--EN:Wait for one round to expire*/SysTick->CTRL(控制与状态寄存器)&=~(1<<16);/*CN:写该位清零--EN:Write to clear flag*/}SysTick->CTRL(控制与状态寄存器)&=~0x1;/*CN:停止定时器--EN:Stop timer*/}if(remain)/*CN:处理剩余不满一轮的计数值--EN:Handle remaining count*/{SysTick->LOAD(重装载值寄存器)=remain;/*CN:加载剩余值--EN:LOAD(重装载值寄存器) remaining VAL(当前计数值寄存器)ue*/SysTick->VAL(当前计数值寄存器)=0;SysTick->CTRL(控制与状态寄存器)|=0x1;while(!(SysTick->CTRL(控制与状态寄存器)&(1<<16)));SysTick->CTRL(控制与状态寄存器)&=~((1<<16)|0x1);/*CN:清除标志并停止定时器--EN:Clear flag and stop timer*/}}

以延时5000msHCLK=72MHz为例,总时钟数= 5000 × 72000 = 360,000,000。单轮最大16,777,215,需跑360,000,000 / 16,777,215 ≈ 21轮完整最大值,再加一轮剩余值7,678,485。这就是countremain的由来。

(4) 微秒延时函数

/** * Function: SysTick_Delay_Us * Description: CN:SysTick微秒延时,适用于小于1864ms的短延时(无溢出处理)--EN:SysTick microsecond delay, for short delays under 1864ms (no overflow handling) * Parameters: us - CN:延时的微秒数,注意不要超过 16,777,215 / fac_us--EN:Delay in μs, not exceeding 16,777,215 / fac_us * Return VAL(当前计数值寄存器)ue:无 */voidSysTick_Delay_Us(uint32_tus){SysTick->CTRL(控制与状态寄存器)&=~((1<<16)|0x3);/*CN:清除到期标志,关闭定时器--EN:Clear count flag, disable timer*/SysTick->LOAD(重装载值寄存器)=us*fac_us;/*CN:计算并加载总时钟数--EN:Calculate and LOAD(重装载值寄存器) total clock cycles*/SysTick->VAL(当前计数值寄存器)=0;/*CN:清零当前计数值--EN:Clear current count VAL(当前计数值寄存器)ue*/SysTick->CTRL(控制与状态寄存器)|=0x1;/*CN:启动定时器--EN:Start timer*/while(!(SysTick->CTRL(控制与状态寄存器)&(1<<16)));/*CN:等待到期标志置1--EN:Wait for count flag to be set*/SysTick->CTRL(控制与状态寄存器)&=~((1<<16)|0x1);/*CN:清除标志并关闭定时器--EN:Clear flag and stop timer*/}

注意事项:us × fac_us不能超过16,777,215,否则溢出。HCLK不分频时fac_us = 72,单次最大延时16,777,215 / 72 ≈ 233,000μs ≈ 233ms。延时超过这个值请调用毫秒版本的SysTick_Delay_Ms

六、多定时器配合:SysTick+TIM的典型分工

裸机项目中SysTick通常只干一件事——做delay_ms/delay_us。而TIM则负责PWM输出、输入捕获、编码器模式等复杂功能。两者的典型分工如下:

定时器职责理由
SysTick延时函数、系统心跳配置简单,可直接轮询COUNTFLAG,不占用中断资源
TIM1高级PWM、互补输出、刹车自带死区、刹车、互补通道
TIM2/3/4通用PWM、输入捕获、编码器功能全面,通道多,适合各类通用场景
TIM6/7精简单次或周期中断基本定时器,无输入输出引脚,极致精简

中断优先级方面:SysTick的中断号是-1SysTick_IRQn),几乎是最高的内核异常之一。RTOSSysTick中断通常设最高抢占优先级,确保任务调度不被外设中断阻塞;裸机中如果只用来延时,一般不开中断,靠轮询解决。

七、任意平台纯软件延时 vs 硬件SysTick

如果MCU没有SysTick(比如非Cortex-M内核),可以用NOP汇编宏模拟一个简单的阻塞延时。原理是数NOP个数,但在高频CPU上需要精确计算指令周期。

/*========================================*//*CN:内嵌汇编指令宏--EN:Inline assembly instruction macros*//*========================================*/#defineDISI()_asm{disi}/*CN:关总中断--EN:Disable global interrupt*/#defineWDTC()_asm{wdtc}/*CN:清看门狗--EN:Clear watchdog timer*/#defineSLEP()_asm{slep}/*CN:进入休眠模式--EN:Enter sleep mode*/#defineNOP()_asm{nop}/*CN:空操作,单周期延时--EN:No operation, single-cycle delay*/#defineENI()_asm{eni}/*CN:开总中断--EN:Enable global interrupt*/

1. 核心思想

纯软件延时的本质就是用for循环包NOP指令,用指令周期数凑延时时间。在16MHz主频的MCU上,一个NOP62.5ns16个约1μs16000个约1ms但任何中断打断都会让NOP计数不准,软延时只适合关中断的极短微秒级场景。

2. 纯软件微秒延时

/** * Function: Soft_Delay_Us * Description: CN:纯软件微秒延时,16MHz下约16个NOP=1μs--EN:Pure software μs delay, ~16 NOPs=1μs at 16MHz * Parameters: us - CN:延时的微秒数--EN:Delay in μs * Return VAL(当前计数值寄存器)ue:无 */voidSoft_Delay_Us(uint16_tus){DISI();/*CN:关中断,保护时序--EN:Disable interrupt to protect timing*/while(us--){uint8_ti;for(i=0;i<16;i++)/*CN:16个NOP≈1μs@16MHz--EN:16 NOPs≈1μs@16MHz*/{NOP();}}ENI();}

3. 软件方式 vs 硬件SysTick

维度硬件SysTick纯软件NOP延时
精度时钟级精度(13.9ns@72MHz)受指令周期、中断打断影响,误差大
CPU占用轮询模式下CPU仍被占满,但可用中断解放CPU全阻塞,无法任何其他工作
跨平台移植Cortex-M系列地址一致,直接复用需按主频重新校准NOP个数
适用场景所有Cortex-M裸机与RTOSCortex-M内核、无任何硬件定时器时

八、提高延时精度与稳定性的技巧

1. 延时期间关总中断

微秒级延时对时序极其敏感,如果延时过程中被中断打断,实际延时会拉长。关键通信时序(如软件模拟SPI的时钟边沿)必须在DISI()/ENI()保护下执行。毫秒级延时一般不需要,留给系统响应其他中断。

2. 使用SysTick_Config中断模式替代轮询

裸机中的while(!(SysTick->CTRL(控制与状态寄存器) & (1<<16)))CPU空转等标志,白白浪费处理能力。更推荐用中断模式——SysTick_Config()使能中断,在SysTick_Handler里只做全局tick计数加一,delay_ms改成挂起等待tick到达目标值,CPU在此期间可以去执行其他逻辑或进入休眠。

3. 校准因子按实际频率重算

如果你在SystemInit中修改了系统时钟频率(比如用内部HSI降频到8MHz),fac_usfac_ms必须同步重算,否则所有delay_ms都是错的。不放心的话可以在初始化里用宏自动根据SystemCoreClock计算,避免手动改出bug

九、SysTick这里为什么不初始化GPIO?

这是一个非常好的问题!你观察得非常仔细。

核心答案:SysTick内核内部的定时器,不需要也不存在GPIO引脚

SysTickSPIUSARTGPIO这些外设有一个根本性的区别:

外设类型位置是否需要GPIO初始化原因
SPII2CUSART芯片外设总线(APB/AHB需要这些外设需要通过具体的芯片引脚与外部器件物理连接
GPIO本身芯片边界需要引脚就是GPIO的本质,必须配置方向、模式
SysTickCortex-M内核内部不需要它是一个纯内部计数器没有任何外部引脚

图解:SysTick在内核中的位置

下面是一个简化的STM32芯片内部框图,注意SysTick的位置:

为什么SysTick不需要GPIO初始化?三个根本原因

1. 物理上不存在引脚

GPIO初始化本质上是在配置芯片引脚的电气特性(推挽/开漏、上拉/下拉、速度等)。SysTick既不输入也不输出任何信号到芯片外部,它只是内核内部的一个计数器,因此无引脚可配

2. 时钟来源是内核时钟,不是外部引脚

SysTick的时钟来自内部的HCLKHCLK/8,这是时钟树内部的信号线,不经过GPIO引脚。而SPISCKMOSI等信号必须通过GPIO引脚输出到外部器件,所以需要初始化GPIO的复用功能。

3. 操作方式是对寄存器读写,不涉及引脚状态

所有对SysTick的操作都是通过读写CTRLLOADVAL这几个内存映射寄存器完成的:

  • LOAD→ 设置倒计时初值
  • CTRL→ 查看计数到零标志
  • VAL→ 清零当前计数值

这些操作不经过任何GPIO数据寄存器,因此完全不需要GPIO初始化。

一个类比帮助你理解

外设类比是否需要“接线路”?
SPI从机📬 邮局(需要收发信件的窗口)✅ 需要,窗口就是引脚
GPIO按键🔘 一个按钮(装在面板上)✅ 需要,按钮就是引脚
SysTick⏰ 手机里自带的倒计时闹钟❌ 不需要,完全在手机内部软件运行,没有外接按钮或显示屏

所以,回到你的问题

SysTick不初始化GPIO,不是代码写漏了,而是因为它根本就不是一个“引脚型”外设。

在配置SysTick时,你需要配置的是:

  • ✅ 时钟源(CLKSOURCE
  • ✅ 重装载值(LOAD
  • ✅ 是否产生中断(TICKINT
  • ✅ 使能定时器(ENABLE

但绝对不需要配置任何GPIO_InitTypeDef结构体或调用GPIO_Init函数。这是从物理架构上决定的,不是编程习惯问题。

验证一下:看SysTick的寄存器地址

SysTick的寄存器映射在0xE000E0100xE000E020这个地址范围,这是ARM内核的系统控制空间(SCS),与GPIO外设的地址(0x40010800附近)完全不同。这也从地址空间上证明了:GPIOSysTick是两套完全独立的硬件模块。

十、结语:内核自带的心跳,最轻量的时钟

SysTickCortex-M内核送给你最简单的礼物——4个寄存器、24位倒计时、一条轮询标志位,就能让你的delay_ms从“大概”进化到“精确”。
下一次当你写下delay_ms(1000),愿你能想起那个不停倒数的VAL(当前计数值寄存器)、那个到期就举手报告的COUNTFLAG,还有那行优雅的溢出分段——把360,000,000掰成21 + 1轮的数学之美。

扩展阅读:

  • Cortex-M3 Technical Reference ManualSysTick章节
  • PM0056:STM32F10xxxCortex-M3programming manual
  • 《The Definitive Guide to ARM Cortex-M3 and Cortex-M4 Processors》 – Joseph Yiu
http://www.jsqmd.com/news/811199/

相关文章:

  • 怎么去图片上原有的水印? - 爱上科技热点
  • 有不花钱就可以去除水印的方法吗?干货攻略 - 爱上科技热点
  • DeadLibrary-CLI:自动化识别与管理项目“僵尸依赖”的工程实践
  • 视频链接提取下载工具怎么用?2026最新免费视频链接提取下载工具盘点推荐 - 爱上科技热点
  • Mac用户看过来!保姆级Matlab R2020a安装与激活指南(含断网、补丁替换全流程)
  • 避坑指南:树莓派4B用FFmpeg推USB摄像头流,我踩过的那些编译和权限的坑
  • Arm Cortex-R52调试与性能监控架构详解
  • Hotkey Detective:Windows全局热键冲突检测工具的技术实现与架构解析
  • OBS Advanced Timer:终极直播时间管理解决方案,让专业直播触手可及
  • 告别PWM音频的‘滋滋’声:深入排查定时器更新、RC滤波与功放三大噪声源
  • 深度学习工作站省电降温实战:用nvidia-smi命令行将TITAN RTX功率墙从280W锁到250W
  • 2026年4月第二周AI圈大事件:GPT-6官宣、中国模型称霸、智能体爆发全景解读
  • FanControl:彻底告别电脑噪音,打造个性化风扇控制体验
  • 从零移植Debian到红米2:解锁MSM8916上的主线Linux手机体验
  • 【MM实战解析】特殊采购类型40:跨工厂需求传递与库存优化实战
  • Linux服务器硬盘狂刷‘hard resetting link’错误?别慌,手把手教你用smartctl定位并关闭NCQ避坑
  • NextPy全栈框架:用Python构建AI智能体Web应用
  • 怎么去图片上原有的水印?简单去除方法攻略 - 爱上科技热点
  • MapStruct核心原理与高效应用实践
  • Tessent MBIST Pattern Spec实战:从配置到生成的完整流程解析
  • NoFences:完全免费的Windows桌面分区管理神器
  • 用Fiddler和Proxifier抓包分析易游网络验证API,手把手教你模拟合法请求
  • Nodejs后端服务如何优雅集成Taotoken提供AI对话功能
  • 2026 青岛纹眉哪家口碑好?本地人实测深度测评汇总 - 小艾信息发布
  • STM32模拟I2C驱动TCS34725实现环境光与颜色识别
  • Arm MMU L1 TCU寄存器架构与性能优化解析
  • 从仿真到实战:手把手教你用TINA-TI设计一个可用的窗口比较器电路
  • 观察Taotoken在多模型并发请求下的稳定性与响应表现
  • Mozilla:Mythos发现的271个漏洞“几乎没有误报“
  • Pinching-Antenna系统在B5G/6G网络中的安全通信应用