目录1.SysTick介绍2.芯片架构2.1 M3系统架构图3.SysTick使用4.readme5.定时器中断配置5.1 core_cm3.h文件5.1.1 函数头注释部分5.1.2 函数名5.1.3 判断重装值是否超过 24 位5.1.4 设置重装载寄存器5.1.5 设置中断优先级5.1.6 清空当前计数器5.1.7 配置 CTRL 寄存器最关键5.1.8 返回成功5.1.9 这个函数干了什么事情6.寄存器6.1 SysTick里面的寄存器6.1.2 LOAD —— 重装值6.1.2.1 寄存器全名6.1.2.2 寄存器位定义6.1.2.3 手册核心含义6.1.2.4 手册超级重点为什么代码里要 -16.1.2.5 手册另外两个重要规则6.1.2.6 把 LOAD、VAL、CTRL 三个寄存器连起来完整工作流程6.1.2.7 一句话总结本寄存器背下来6.1.3 VAL —— 当前计数值6.1.3.1 寄存器基础信息6.1.3.2 寄存器位定义6.1.3.3 官方原文逐句大白话翻译最核心重点6.1.3.4 完全对应你之前的 SysTick_Config 函数代码6.1.3.5 VAL、LOAD、CTRL 三个寄存器分工总结6.1.3.6 SysTick 完整工作流程结合三个官方寄存器6.1.3.7 一句话总结本 VAL 寄存器6.1.4 CTRL —— 控制 / 开关 / 标志6.1.4.1 寄存器基础信息6.1.4.2 Bit 16COUNTFLAG你 while 循环检测的计时完成标志6.1.4.3 Bit 2CLKSOURCE 时钟源选择6.1.4.4 Bit 1TICKINT SysTick 中断请求使能6.1.4.5 Bit 0ENABLE 定时器总使能开关6.1.4.5 Bits 31:17、Bits15:3保留位 Reserved6.2 NVIC 优先级寄存器 —— 中断优先级1.SysTick介绍SysTick—系统定时器是属于CM3内核中的一个外设内嵌在NVIC中。系统定时器是一个24bit的向下递减的计数器计数器每计数一次的时间为1/SYSCLK一般我们设置系统时钟SYSCLK等于72M。当重装载数值寄存器的值递减到0的时候系统定时器就产生一次中断以此循环往复。因为SysTick是属于CM3内核的外设所以所有基于CM3内核的单片机都具有这个系统定时器使得软件在CM3单片机中可以很容易的移植。系统定时器一般用于操作系统用于产生时基维持操作系统的心跳。意味着1/72M得到秒计数一次的时间 1 / 72M 13.89ns纳秒计数一次就是相当于延时13.89ns纳秒但是又说了系统定时器是一个24bit的向下递减的计数器。24个1最大的值是16777215最大计数值 2^24 - 1 16,777,215最大定时时间 16777215 × 13.89ns ≈ 233ms也就是说SysTick 最大只能定时 233ms超过这个时间必须用多次中断累加。例如 如果我定时50ms已知条件系统时钟72MHz目标定时50ms 0.05s公式开始计算重装值 0.05 × 72,000,000重装值 3,600,000结果要定时 50ms重装值 3600000但这里有一个超级重要的点SysTick 是 24 位计数器最大值只有 167772153600000 小于 16777215✅ 完全装得下✅ 可以直接一次定时 50ms最终结论直接用50ms 定时72MHz 时钟重装值 3600000计数器从 3600000 往下减每 13.89ns 减 1减到 0 刚好 50ms触发中断。还有一个通用速算公式比如定时时间计算的重载值1ms7200010ms72000050ms3600000100ms7200000全部都小于 16777215都能直接用2.芯片架构2.1 M3系统架构图3.SysTick使用函数首先先判断形参IRQn的大小如果是小于0则表示这个是系统异常系统异常的优先级由内核外设SCB的寄存器SHPRx控制如果大于0则是外部中断外部中断的优先级由内核外设NVIC中的IPx寄存器控制。因为SysTick属于内核外设跟普通外设的中断优先级有些区别并没有抢占优先级和子优先级的说法。在STM32F103中内核外设的中断优先级由内核SCB这个外设的寄存器SHPRxx1.2.3来配置。有关SHPRx寄存器的详细描述可参考《Cortex-M3内核编程手册》4.4.8章节。下面我们简单介绍下这个寄存器。SPRH1-SPRH3是一个32位的寄存器但是只能通过字节访问每8个字段控制着一个内核外设的中断优先级的配置。在STM32F103中只有位7:4这高四位有效低四位没有用到所以内核外设的中断优先级可编程为0~15只有16个可编程优先级数值越小优先级越高。如果软件优先级配置相同那就根据他们在中断向量表里面的位置编号来决定优先级大小编号越小优先级越高。用到SysTick Config这个函数这个函数定义在core_cm3.h。将函数定义写在头文件会有什么问题呢通常情况下因为头文件可能会被多个C文件include相当于多个C文件都包含了已定义的同名函数点编译后会报重复定义错误。然而在这里它加上了inline这个关键字让编译器处理好让多个C文件即包含了相同定义的函数又避免了重复定义报错。额外提示加inline函数通常叫“内联函数”但实际不一定“内联”可以确定的是先记住上一句的标红结论就行。4.readme/*********************************************************************************************/【 *】程序简介-工程名称 系统定时器基础延时与流水灯-实验平台 野火 STM32F103C8T6-STM32开发板【 】功能简介使用系统节拍定时器来控制板载灯的流水亮灭【 】实验操作1.编译并下载程序到开发板即可观察现象 2.观察板载灯的间隔是否均匀。/*********************************************************************************************/【*】 引脚分配LED_R-LED1-PA1 核心板板载LED灯低电平亮LED_G-LED2-PA2 核心板板载LED灯低电平亮LED_B-LED3-PA3 核心板板载LED灯低电平亮/*********************************************************************************************/【*】程序描述 main.c 1.初始化LED对应的GPIO初始化对应端口 2.控制LED的亮灭 3.使用系统节拍定时器完成延时函数的建立/*********************************************************************************************/5.定时器中断配置5.1 core_cm3.h文件在官方提供的内核文件中提供了一个定时器函数static__INLINEuint32_tSysTick_Config(uint32_tticks)/** * brief Initialize and start the SysTick counter and its interrupt. * * param ticks number of ticks between two interrupts * return 1 failed, 0 successful * * Initialise the system tick timer and its interrupt and start the * system tick timer / counter in free running mode to generate * periodical interrupts. */static__INLINEuint32_tSysTick_Config(uint32_tticks){if(ticksSysTick_LOAD_RELOAD_Msk)return(1);/* Reload value impossible */SysTick-LOAD(ticksSysTick_LOAD_RELOAD_Msk)-1;/* set reload register */NVIC_SetPriority(SysTick_IRQn,(1__NVIC_PRIO_BITS)-1);/* set Priority for Cortex-M0 System Interrupts */SysTick-VAL0;/* Load the SysTick Counter Value */SysTick-CTRLSysTick_CTRL_CLKSOURCE_Msk|SysTick_CTRL_TICKINT_Msk|SysTick_CTRL_ENABLE_Msk;/* Enable SysTick IRQ and SysTick Timer */return(0);/* Function successful */}5.1.1 函数头注释部分/**brief Initialize and start the SysTick counter and its interrupt.param ticks 两次中断之间的时钟周期个数return 1失败, 0成功*/这个函数用来初始化 SysTick 定时器并启动定时中断。你传一个ticks计数值定时器就会每隔 ticks 个时钟周期触发一次中断。传入我要的定时周期 ticks自动配置好 SysTick启动定时器开启中断返回是否成功。5.1.2 函数名static__INLINEuint32_tSysTick_Config(uint32_tticks)static __INLINE静态内联函数编译器优化uint32_t ticks你要设置的重装载值比如72000 → 1ms 中断一次返回值0 成功1 失败这个函数加了inline内联函数因为这个函数在core_cm3.h头文件写的一般情况下函数体部分在.c文件写而.h文件写函数的声明部分但是这个函数的实现功能也都在.h中写就会出现一个问题当多个源文件.c文件调用这个函数的时候会出现重复定义使用的报错问题就像比如有3个文件a.c源文件b.c源文件c.h头文件在c.h头文件写了定义语句int i。然后此时a.c和b.c源文件都同时包含了c.h头文件就会报错int i重复定义问题类似于一个函数体多次被其他的源文件调用的重复定义问题。此时就是inline 内联函数起到作用解决这种重复性定义报错问题。5.1.3 判断重装值是否超过 24 位if(ticksSysTick_LOAD_RELOAD_Msk)return(1);SysTick_LOAD_RELOAD_Msk 0xFFFFFF24 位最大值如果传入的 ticks 超过 2²⁴-1 → 非法直接返回 1配置失败5.1.4 设置重装载寄存器SysTick-LOAD(ticksSysTick_LOAD_RELOAD_Msk)-1;把 ticks 写入 LOAD 寄存器-1 原因从 X 数到 0总次数是 X1 次所以要减 1 才能得到准确的定时例子ticks72000 → LOAD71999从 71999 减到 0正好 72000 次 1ms5.1.5 设置中断优先级NVIC_SetPriority(SysTick_IRQn,(1__NVIC_PRIO_BITS)-1);设置 SysTick 中断为最低优先级不影响其他重要中断CM3、CM4 内核标准配置5.1.6 清空当前计数器SysTick-VAL0;把当前计数值清零让定时器立刻从 LOAD 值重新开始计数5.1.7 配置 CTRL 寄存器最关键SysTick-CTRLSysTick_CTRL_CLKSOURCE_Msk|// Bit2 1 → 使用内核时钟72MSysTick_CTRL_TICKINT_Msk|// Bit1 1 → 使能中断SysTick_CTRL_ENABLE_Msk;// Bit0 1 → 启动定时器Bit2时钟源 系统时钟 72MHzBit1开启中断Bit0启动定时器5.1.8 返回成功return(0);5.1.9 这个函数干了什么事情1.检查定时值是否合法2.设置重装载值3.设置中断优先级4.清空计数器5.选择 72MHz 时钟6.开启中断7.启动定时器8.返回成功6.寄存器STM32F10xxx Cortex-M3编程手册-英文版以下寄存器都在这个手册里面的6.1 SysTick里面的寄存器代码里寄存器手册官方名称手册精确页码手册小节代码里用途SysTick-CTRLSTK_CTRL 控制状态寄存器151 页4.5.1开关、时钟、中断、bit16 标志位SysTick-LOADSTK_LOAD 重装载寄存器152 页4.5.2设置倒计时初值SysTick-VALSTK_VAL 当前计数值寄存器153 页4.5.3清空当前计数6.1.2 LOAD —— 重装值6.1.2.1 寄存器全名SysTick reload value register (STK_LOAD)中文SysTick 重装载数值寄存器就是代码里的SysTick-LOAD地址偏移0x04 复位值06.1.2.2 寄存器位定义位 31~24保留 Reserved必须写 0位 23~0RELOAD [23:0] 24 位重装载值可读写 rw正好对应 SysTick 是24 位向下计数器最大值0xFFFFFF 167772156.1.2.3 手册核心含义The LOAD register specifies the start value to load into the VAL register when the counter is enabled and when it reaches 0.翻译当计数器减到 0 的时候硬件会自动把 LOAD 寄存器里面的值重新加载到 VAL 当前计数器寄存器然后重新开始向下递减计数。简单说LOAD 倒计时初始值VAL 当前正在数的值数到 0 → VAL 自动变回 LOAD 的值 → 重新倒计时6.1.2.4 手册超级重点为什么代码里要 -1手册原文原话To generate a multi-shot timer with a period of N processor clockcycles, use a RELOAD value of N-1.例如每 100 个时钟脉冲中断一次设置 RELOAD99手册明确规定想要定时 N 个时钟周期LOAD 必须设置为N − 1对应我的代码SysTick-LOAD(ticksSysTick_LOAD_RELOAD_Msk)-1;1ms 72000 个时钟周期所以手册要求LOAD 72000 − 1 71999原理从 71999 → 0一共正好递减 72000 次刚好 1ms6.1.2.5 手册另外两个重要规则1.RELOAD 范围1 ~ 0xFFFFFF不能写 0写 0 定时器就不动了2.LOAD 只存初始值不参与实时计数实时计数看 VAL 寄存器6.1.2.6 把 LOAD、VAL、CTRL 三个寄存器连起来完整工作流程1.LOAD存倒计时初始值719992.VAL当前正在往下数的值3.CTRL开关、时钟、中断、COUNTFLAG 标志工作流程1.配置 LOAD719992.CTRL 打开定时器 ENABLE13.VAL 从 LOAD 加载初始值开始向下减4.VAL 一直减到 05.硬件自动COUNTFLAG 置 1、VAL 重新加载 LOAD、重新倒计时6.1.2.7 一句话总结本寄存器背下来STK_LOAD (LOAD) 24 位重装载初始值定时 N 次时钟 → LOAD 填 N-1计数器减到 0 自动把 LOAD 加载进 VAL 重新计时6.1.3 VAL —— 当前计数值6.1.3.1 寄存器基础信息官方全名SysTick current value register (STK_VAL)代码里名字SysTick-VAL中文SysTick 当前实时计数值寄存器地址偏移0x08复位默认值06.1.3.2 寄存器位定义位 31~24保留位 Reserved必须写 0不用管位 23~0CURRENT [23:0]24 位可读写和 LOAD 寄存器位宽完全一致就是 SysTick实时正在往下数的数字6.1.3.3 官方原文逐句大白话翻译最核心重点原文The VAL register contains the current value of the SysTick counter.Reads return the current value of the SysTick counter.A write of any value clears the field to 0, and also clears the COUNTFLAG bit in the STK_CTRL register to 0.逐句翻译VAL 寄存器存放 SysTick 计数器当前实时的计数值这个寄存器读到的就是此时此刻计数器正在倒计时的数字读取操作正常返回当前倒计时数值超级重要写入操作往 VAL 里写任意数值哪怕写 0都会自动做两件事① 立刻把 VAL 当前计数值清零② 同时自动把 CTRL 寄存器里的 Bit16 COUNTFLAG 标志位也清零6.1.3.4 完全对应你之前的 SysTick_Config 函数代码SysTick-VAL0;现在对照官方手册就彻底懂了往 VAL 写 0 → 立刻清空当前正在倒计时的数值同时自动清空 COUNTFLAG 标志位避免上一次计时残留标志误触发保证定时器从零开始、干净地重新倒计时不会出现计时偏移、时间不准的问题6.1.3.5 VAL、LOAD、CTRL 三个寄存器分工总结寄存器手册章节核心作用对应你代码操作STK_CTRLSysTick-CTRL4.5.1控制开关、时钟源、中断使能、Bit16 计时完成标志 COUNTFLAG配置时钟、开中断、启动定时器while 循环检测 COUNTFLAGSTK_LOADSysTick-LOAD4.5.2存放倒计时初始重装值计数器到 0 自动加载进 VAL设置定时周期手册要求定时 N 次填 N-1STK_VALSysTick-VAL4.5.3存放当前实时倒计时数值写 0 清空计数器 清空标志位保证重新开始计时6.1.3.6 SysTick 完整工作流程结合三个官方寄存器1.配置 LOAD写入定时初始值71999对应 1ms2.配置 VAL写 0 清空当前计数 清空标志位3.配置 CTRL打开时钟、中断、总开关 ENABLE启动定时器4.VAL 从 LOAD 加载初始值每个时钟周期自动 - 1 向下递减5.VAL 减到 0硬件自动置位 CTRL 的 COUNTFLAG、自动把 LOAD 重新加载进 VAL、重新开始倒计时6.while 循环检测 COUNTFLAG判断 1ms 时间是否到达6.1.3.7 一句话总结本 VAL 寄存器VAL 计数器当前正在数的实时数字往 VAL 写任意数 立刻清零计数 清零计时完成标志配合 LOAD 初始值、CTRL 控制位完成完整 24 位向下递减定时。6.1.4 CTRL —— 控制 / 开关 / 标志6.1.4.1 寄存器基础信息官方全名SysTick control and status register (STK_CTRL)代码里名字SysTick-CTRL中文SysTick 控制 状态寄存器地址偏移0x00上电复位默认值0x00000000全部关闭官方原文Returns 1 if timer counted to 0 since last time this was read.6.1.4.2 Bit 16COUNTFLAG你 while 循环检测的计时完成标志大白话解释从上一次读取这个位之后如果定时器 VAL 计数器递减到了 0硬件自动把这一位置1。2 个官方硬性规则必须记住VAL 减到 0 → Bit16 自动硬件置 1只要你读一次这个位代码里做 运算读取硬件自动把它清 0对应你的代码while(!((SysTick-CTRL)(116)));116 就是专门读取这第 16 位 COUNTFLAG时间没到VAL 没减到 0→ Bit160 → while 死等时间到了VAL 减到 0→ Bit161 → 跳出循环6.1.4.3 Bit 2CLKSOURCE 时钟源选择官方原文Selects the clock source.0: AHB/81: Processor clock (AHB内核系统时钟)大白话解释选择 SysTick 计数器用哪个时钟向下计数写 0时钟 系统时钟 ÷8写 1时钟 72MHz 内核系统时钟AHB你工程里用的就是这个对应的代码SysTick_CTRL_CLKSOURCE_Msk就是把这一位置 1所以你的定时器计数速度是 13.89ns / 次。6.1.4.4 Bit 1TICKINT SysTick 中断请求使能官方原文0: Counting down to zero does not assert the SysTick exception request1: Counting down to zero to asserts the SysTick exception request.Note: Software can use COUNTFLAG to determine if SysTick has ever counted to zero.大白话解释控制 VAL 减到 0 的时候要不要触发中断服务函数0关闭中断只用 COUNTFLAG 标志位查询延时你延时函数的用法1打开中断计数到 0 自动进 SysTick 中断函数对应我的代码SysTick_CTRL_TICKINT_Msk把这一位置 1开启了中断使能。注意你延时函数是查询标志位不是进中断但初始化函数默认打开了中断不影响查询用法。6.1.4.5 Bit 0ENABLE 定时器总使能开关官方原文Enables the counter. When ENABLE is set to 1, the counter loads the RELOAD value from theLOAD register and then counts down. On reaching 0, it sets the COUNTFLAG to 1 andoptionally asserts the SysTick depending on the value of TICKINT. It then loads the RELOADvalue again, and begins counting.0: Counter disabled1: Counter enabled1: Counter enabled计数器开机运行0: Counter disabled计数器关闭停止当 ENABLE1计数器会从 LOAD 寄存器加载重装值开始向下递减减到 0 后自动置 1 COUNTFLAG、根据 TICKINT 决定是否发中断、自动重新加载 LOAD 值、重新开始倒计时。大白话解释SysTick 定时器的总电源开关1开机VAL 开始从 LOAD 值往下数数0关机VAL 停止不动不再计数对应你的代码SysTick_CTRL_ENABLE_Msk把这一位置 1 → 启动定时器开始计数延时结束代码SysTick-CTRL~SysTick_CTRL_ENABLE_Msk→ 把 Bit0 清 0关闭定时器6.1.4.5 Bits 31:17、Bits15:3保留位 Reserved官方要求必须保持为 0不能修改、不用管。6.2 NVIC 优先级寄存器 —— 中断优先级NVIC 优先级在 4.3 章节章节标题翻译4.3 Nested vectored interrupt controller (NVIC)中文嵌套向量中断控制器NVIC它是 ARM Cortex-M3 内核自带的、统一管理芯片所有中断的核心单元你 SysTick 的中断优先级、开关、响应全部由它控制逐条翻译官方特性 对应你的代码讲解原文 1up to 81 interrupts (depends on the STM32 device type)翻译最多支持 81 个中断通道数量由具体 STM32 芯片型号决定你的 STM32F103C8T6 只用其中很小一部分SysTick 就是其中一个内核系统中断。原文 2最重要和你代码直接相关A programmable priority level of 0-15 for each interrupt.A higherlevel corresponds to a lower priority, so level 0 is the highestinterrupt priority逐句大白话翻译1.每个中断都可以单独设置优先级范围0~152.优先级数字越大 → 优先级越低越不重要3.数字 0 全系统最高优先级完全对应你 SysTick_Config 函数里这句代码NVIC_SetPriority(SysTick_IRQn,(1__NVIC_PRIO_BITS)-1);CM3 内核优先级位宽__NVIC_PRIO_BITS4计算(14)-1 15意思把 SysTick 滴答定时器中断设置为优先级 15全系统最低优先级官方规则验证数字越大优先级越低 → 15 是最低完全符合手册这么设计的原因SysTick 只是系统时基延时绝对不能抢占串口、按键等重要中断所以固定设为最低优先级。原文 3Level and pulse detection of interrupt signals翻译支持电平触发、脉冲触发两种中断检测方式STM32 外部中断常用脉冲边沿触发SysTick 是内核内部自动触发中断。原文 4Dynamic reprioritization of interrupts翻译支持运行时动态修改中断优先级程序跑起来之后也可以随时重新设置某个中断的优先级。原文 5Grouping of priority values into group priority and subpriority fields翻译支持把优先级拆分成抢占优先级 响应子优先级这就是 STM32 著名的中断优先级分组抢占优先级能不能打断正在运行的中断响应子优先级同时触发时先响应哪个SysTick 是内核异常默认不参与分组固定最低优先级。原文 6Interrupt tail-chaining翻译支持中断尾链技术CM3 内核硬件加速上一个中断结束立刻直接进下一个中断不用重复保存现场中断响应更快。原文 7An external Non-maskable interrupt (NMI)翻译支持外部不可屏蔽中断 NMI最高优先级、关不掉的中断电源故障等致命错误才会用SysTick 和它无关。
stm32F103C8T6标准库定时器应用流水灯1——相关的寄存器
发布时间:2026/5/22 9:20:17
目录1.SysTick介绍2.芯片架构2.1 M3系统架构图3.SysTick使用4.readme5.定时器中断配置5.1 core_cm3.h文件5.1.1 函数头注释部分5.1.2 函数名5.1.3 判断重装值是否超过 24 位5.1.4 设置重装载寄存器5.1.5 设置中断优先级5.1.6 清空当前计数器5.1.7 配置 CTRL 寄存器最关键5.1.8 返回成功5.1.9 这个函数干了什么事情6.寄存器6.1 SysTick里面的寄存器6.1.2 LOAD —— 重装值6.1.2.1 寄存器全名6.1.2.2 寄存器位定义6.1.2.3 手册核心含义6.1.2.4 手册超级重点为什么代码里要 -16.1.2.5 手册另外两个重要规则6.1.2.6 把 LOAD、VAL、CTRL 三个寄存器连起来完整工作流程6.1.2.7 一句话总结本寄存器背下来6.1.3 VAL —— 当前计数值6.1.3.1 寄存器基础信息6.1.3.2 寄存器位定义6.1.3.3 官方原文逐句大白话翻译最核心重点6.1.3.4 完全对应你之前的 SysTick_Config 函数代码6.1.3.5 VAL、LOAD、CTRL 三个寄存器分工总结6.1.3.6 SysTick 完整工作流程结合三个官方寄存器6.1.3.7 一句话总结本 VAL 寄存器6.1.4 CTRL —— 控制 / 开关 / 标志6.1.4.1 寄存器基础信息6.1.4.2 Bit 16COUNTFLAG你 while 循环检测的计时完成标志6.1.4.3 Bit 2CLKSOURCE 时钟源选择6.1.4.4 Bit 1TICKINT SysTick 中断请求使能6.1.4.5 Bit 0ENABLE 定时器总使能开关6.1.4.5 Bits 31:17、Bits15:3保留位 Reserved6.2 NVIC 优先级寄存器 —— 中断优先级1.SysTick介绍SysTick—系统定时器是属于CM3内核中的一个外设内嵌在NVIC中。系统定时器是一个24bit的向下递减的计数器计数器每计数一次的时间为1/SYSCLK一般我们设置系统时钟SYSCLK等于72M。当重装载数值寄存器的值递减到0的时候系统定时器就产生一次中断以此循环往复。因为SysTick是属于CM3内核的外设所以所有基于CM3内核的单片机都具有这个系统定时器使得软件在CM3单片机中可以很容易的移植。系统定时器一般用于操作系统用于产生时基维持操作系统的心跳。意味着1/72M得到秒计数一次的时间 1 / 72M 13.89ns纳秒计数一次就是相当于延时13.89ns纳秒但是又说了系统定时器是一个24bit的向下递减的计数器。24个1最大的值是16777215最大计数值 2^24 - 1 16,777,215最大定时时间 16777215 × 13.89ns ≈ 233ms也就是说SysTick 最大只能定时 233ms超过这个时间必须用多次中断累加。例如 如果我定时50ms已知条件系统时钟72MHz目标定时50ms 0.05s公式开始计算重装值 0.05 × 72,000,000重装值 3,600,000结果要定时 50ms重装值 3600000但这里有一个超级重要的点SysTick 是 24 位计数器最大值只有 167772153600000 小于 16777215✅ 完全装得下✅ 可以直接一次定时 50ms最终结论直接用50ms 定时72MHz 时钟重装值 3600000计数器从 3600000 往下减每 13.89ns 减 1减到 0 刚好 50ms触发中断。还有一个通用速算公式比如定时时间计算的重载值1ms7200010ms72000050ms3600000100ms7200000全部都小于 16777215都能直接用2.芯片架构2.1 M3系统架构图3.SysTick使用函数首先先判断形参IRQn的大小如果是小于0则表示这个是系统异常系统异常的优先级由内核外设SCB的寄存器SHPRx控制如果大于0则是外部中断外部中断的优先级由内核外设NVIC中的IPx寄存器控制。因为SysTick属于内核外设跟普通外设的中断优先级有些区别并没有抢占优先级和子优先级的说法。在STM32F103中内核外设的中断优先级由内核SCB这个外设的寄存器SHPRxx1.2.3来配置。有关SHPRx寄存器的详细描述可参考《Cortex-M3内核编程手册》4.4.8章节。下面我们简单介绍下这个寄存器。SPRH1-SPRH3是一个32位的寄存器但是只能通过字节访问每8个字段控制着一个内核外设的中断优先级的配置。在STM32F103中只有位7:4这高四位有效低四位没有用到所以内核外设的中断优先级可编程为0~15只有16个可编程优先级数值越小优先级越高。如果软件优先级配置相同那就根据他们在中断向量表里面的位置编号来决定优先级大小编号越小优先级越高。用到SysTick Config这个函数这个函数定义在core_cm3.h。将函数定义写在头文件会有什么问题呢通常情况下因为头文件可能会被多个C文件include相当于多个C文件都包含了已定义的同名函数点编译后会报重复定义错误。然而在这里它加上了inline这个关键字让编译器处理好让多个C文件即包含了相同定义的函数又避免了重复定义报错。额外提示加inline函数通常叫“内联函数”但实际不一定“内联”可以确定的是先记住上一句的标红结论就行。4.readme/*********************************************************************************************/【 *】程序简介-工程名称 系统定时器基础延时与流水灯-实验平台 野火 STM32F103C8T6-STM32开发板【 】功能简介使用系统节拍定时器来控制板载灯的流水亮灭【 】实验操作1.编译并下载程序到开发板即可观察现象 2.观察板载灯的间隔是否均匀。/*********************************************************************************************/【*】 引脚分配LED_R-LED1-PA1 核心板板载LED灯低电平亮LED_G-LED2-PA2 核心板板载LED灯低电平亮LED_B-LED3-PA3 核心板板载LED灯低电平亮/*********************************************************************************************/【*】程序描述 main.c 1.初始化LED对应的GPIO初始化对应端口 2.控制LED的亮灭 3.使用系统节拍定时器完成延时函数的建立/*********************************************************************************************/5.定时器中断配置5.1 core_cm3.h文件在官方提供的内核文件中提供了一个定时器函数static__INLINEuint32_tSysTick_Config(uint32_tticks)/** * brief Initialize and start the SysTick counter and its interrupt. * * param ticks number of ticks between two interrupts * return 1 failed, 0 successful * * Initialise the system tick timer and its interrupt and start the * system tick timer / counter in free running mode to generate * periodical interrupts. */static__INLINEuint32_tSysTick_Config(uint32_tticks){if(ticksSysTick_LOAD_RELOAD_Msk)return(1);/* Reload value impossible */SysTick-LOAD(ticksSysTick_LOAD_RELOAD_Msk)-1;/* set reload register */NVIC_SetPriority(SysTick_IRQn,(1__NVIC_PRIO_BITS)-1);/* set Priority for Cortex-M0 System Interrupts */SysTick-VAL0;/* Load the SysTick Counter Value */SysTick-CTRLSysTick_CTRL_CLKSOURCE_Msk|SysTick_CTRL_TICKINT_Msk|SysTick_CTRL_ENABLE_Msk;/* Enable SysTick IRQ and SysTick Timer */return(0);/* Function successful */}5.1.1 函数头注释部分/**brief Initialize and start the SysTick counter and its interrupt.param ticks 两次中断之间的时钟周期个数return 1失败, 0成功*/这个函数用来初始化 SysTick 定时器并启动定时中断。你传一个ticks计数值定时器就会每隔 ticks 个时钟周期触发一次中断。传入我要的定时周期 ticks自动配置好 SysTick启动定时器开启中断返回是否成功。5.1.2 函数名static__INLINEuint32_tSysTick_Config(uint32_tticks)static __INLINE静态内联函数编译器优化uint32_t ticks你要设置的重装载值比如72000 → 1ms 中断一次返回值0 成功1 失败这个函数加了inline内联函数因为这个函数在core_cm3.h头文件写的一般情况下函数体部分在.c文件写而.h文件写函数的声明部分但是这个函数的实现功能也都在.h中写就会出现一个问题当多个源文件.c文件调用这个函数的时候会出现重复定义使用的报错问题就像比如有3个文件a.c源文件b.c源文件c.h头文件在c.h头文件写了定义语句int i。然后此时a.c和b.c源文件都同时包含了c.h头文件就会报错int i重复定义问题类似于一个函数体多次被其他的源文件调用的重复定义问题。此时就是inline 内联函数起到作用解决这种重复性定义报错问题。5.1.3 判断重装值是否超过 24 位if(ticksSysTick_LOAD_RELOAD_Msk)return(1);SysTick_LOAD_RELOAD_Msk 0xFFFFFF24 位最大值如果传入的 ticks 超过 2²⁴-1 → 非法直接返回 1配置失败5.1.4 设置重装载寄存器SysTick-LOAD(ticksSysTick_LOAD_RELOAD_Msk)-1;把 ticks 写入 LOAD 寄存器-1 原因从 X 数到 0总次数是 X1 次所以要减 1 才能得到准确的定时例子ticks72000 → LOAD71999从 71999 减到 0正好 72000 次 1ms5.1.5 设置中断优先级NVIC_SetPriority(SysTick_IRQn,(1__NVIC_PRIO_BITS)-1);设置 SysTick 中断为最低优先级不影响其他重要中断CM3、CM4 内核标准配置5.1.6 清空当前计数器SysTick-VAL0;把当前计数值清零让定时器立刻从 LOAD 值重新开始计数5.1.7 配置 CTRL 寄存器最关键SysTick-CTRLSysTick_CTRL_CLKSOURCE_Msk|// Bit2 1 → 使用内核时钟72MSysTick_CTRL_TICKINT_Msk|// Bit1 1 → 使能中断SysTick_CTRL_ENABLE_Msk;// Bit0 1 → 启动定时器Bit2时钟源 系统时钟 72MHzBit1开启中断Bit0启动定时器5.1.8 返回成功return(0);5.1.9 这个函数干了什么事情1.检查定时值是否合法2.设置重装载值3.设置中断优先级4.清空计数器5.选择 72MHz 时钟6.开启中断7.启动定时器8.返回成功6.寄存器STM32F10xxx Cortex-M3编程手册-英文版以下寄存器都在这个手册里面的6.1 SysTick里面的寄存器代码里寄存器手册官方名称手册精确页码手册小节代码里用途SysTick-CTRLSTK_CTRL 控制状态寄存器151 页4.5.1开关、时钟、中断、bit16 标志位SysTick-LOADSTK_LOAD 重装载寄存器152 页4.5.2设置倒计时初值SysTick-VALSTK_VAL 当前计数值寄存器153 页4.5.3清空当前计数6.1.2 LOAD —— 重装值6.1.2.1 寄存器全名SysTick reload value register (STK_LOAD)中文SysTick 重装载数值寄存器就是代码里的SysTick-LOAD地址偏移0x04 复位值06.1.2.2 寄存器位定义位 31~24保留 Reserved必须写 0位 23~0RELOAD [23:0] 24 位重装载值可读写 rw正好对应 SysTick 是24 位向下计数器最大值0xFFFFFF 167772156.1.2.3 手册核心含义The LOAD register specifies the start value to load into the VAL register when the counter is enabled and when it reaches 0.翻译当计数器减到 0 的时候硬件会自动把 LOAD 寄存器里面的值重新加载到 VAL 当前计数器寄存器然后重新开始向下递减计数。简单说LOAD 倒计时初始值VAL 当前正在数的值数到 0 → VAL 自动变回 LOAD 的值 → 重新倒计时6.1.2.4 手册超级重点为什么代码里要 -1手册原文原话To generate a multi-shot timer with a period of N processor clockcycles, use a RELOAD value of N-1.例如每 100 个时钟脉冲中断一次设置 RELOAD99手册明确规定想要定时 N 个时钟周期LOAD 必须设置为N − 1对应我的代码SysTick-LOAD(ticksSysTick_LOAD_RELOAD_Msk)-1;1ms 72000 个时钟周期所以手册要求LOAD 72000 − 1 71999原理从 71999 → 0一共正好递减 72000 次刚好 1ms6.1.2.5 手册另外两个重要规则1.RELOAD 范围1 ~ 0xFFFFFF不能写 0写 0 定时器就不动了2.LOAD 只存初始值不参与实时计数实时计数看 VAL 寄存器6.1.2.6 把 LOAD、VAL、CTRL 三个寄存器连起来完整工作流程1.LOAD存倒计时初始值719992.VAL当前正在往下数的值3.CTRL开关、时钟、中断、COUNTFLAG 标志工作流程1.配置 LOAD719992.CTRL 打开定时器 ENABLE13.VAL 从 LOAD 加载初始值开始向下减4.VAL 一直减到 05.硬件自动COUNTFLAG 置 1、VAL 重新加载 LOAD、重新倒计时6.1.2.7 一句话总结本寄存器背下来STK_LOAD (LOAD) 24 位重装载初始值定时 N 次时钟 → LOAD 填 N-1计数器减到 0 自动把 LOAD 加载进 VAL 重新计时6.1.3 VAL —— 当前计数值6.1.3.1 寄存器基础信息官方全名SysTick current value register (STK_VAL)代码里名字SysTick-VAL中文SysTick 当前实时计数值寄存器地址偏移0x08复位默认值06.1.3.2 寄存器位定义位 31~24保留位 Reserved必须写 0不用管位 23~0CURRENT [23:0]24 位可读写和 LOAD 寄存器位宽完全一致就是 SysTick实时正在往下数的数字6.1.3.3 官方原文逐句大白话翻译最核心重点原文The VAL register contains the current value of the SysTick counter.Reads return the current value of the SysTick counter.A write of any value clears the field to 0, and also clears the COUNTFLAG bit in the STK_CTRL register to 0.逐句翻译VAL 寄存器存放 SysTick 计数器当前实时的计数值这个寄存器读到的就是此时此刻计数器正在倒计时的数字读取操作正常返回当前倒计时数值超级重要写入操作往 VAL 里写任意数值哪怕写 0都会自动做两件事① 立刻把 VAL 当前计数值清零② 同时自动把 CTRL 寄存器里的 Bit16 COUNTFLAG 标志位也清零6.1.3.4 完全对应你之前的 SysTick_Config 函数代码SysTick-VAL0;现在对照官方手册就彻底懂了往 VAL 写 0 → 立刻清空当前正在倒计时的数值同时自动清空 COUNTFLAG 标志位避免上一次计时残留标志误触发保证定时器从零开始、干净地重新倒计时不会出现计时偏移、时间不准的问题6.1.3.5 VAL、LOAD、CTRL 三个寄存器分工总结寄存器手册章节核心作用对应你代码操作STK_CTRLSysTick-CTRL4.5.1控制开关、时钟源、中断使能、Bit16 计时完成标志 COUNTFLAG配置时钟、开中断、启动定时器while 循环检测 COUNTFLAGSTK_LOADSysTick-LOAD4.5.2存放倒计时初始重装值计数器到 0 自动加载进 VAL设置定时周期手册要求定时 N 次填 N-1STK_VALSysTick-VAL4.5.3存放当前实时倒计时数值写 0 清空计数器 清空标志位保证重新开始计时6.1.3.6 SysTick 完整工作流程结合三个官方寄存器1.配置 LOAD写入定时初始值71999对应 1ms2.配置 VAL写 0 清空当前计数 清空标志位3.配置 CTRL打开时钟、中断、总开关 ENABLE启动定时器4.VAL 从 LOAD 加载初始值每个时钟周期自动 - 1 向下递减5.VAL 减到 0硬件自动置位 CTRL 的 COUNTFLAG、自动把 LOAD 重新加载进 VAL、重新开始倒计时6.while 循环检测 COUNTFLAG判断 1ms 时间是否到达6.1.3.7 一句话总结本 VAL 寄存器VAL 计数器当前正在数的实时数字往 VAL 写任意数 立刻清零计数 清零计时完成标志配合 LOAD 初始值、CTRL 控制位完成完整 24 位向下递减定时。6.1.4 CTRL —— 控制 / 开关 / 标志6.1.4.1 寄存器基础信息官方全名SysTick control and status register (STK_CTRL)代码里名字SysTick-CTRL中文SysTick 控制 状态寄存器地址偏移0x00上电复位默认值0x00000000全部关闭官方原文Returns 1 if timer counted to 0 since last time this was read.6.1.4.2 Bit 16COUNTFLAG你 while 循环检测的计时完成标志大白话解释从上一次读取这个位之后如果定时器 VAL 计数器递减到了 0硬件自动把这一位置1。2 个官方硬性规则必须记住VAL 减到 0 → Bit16 自动硬件置 1只要你读一次这个位代码里做 运算读取硬件自动把它清 0对应你的代码while(!((SysTick-CTRL)(116)));116 就是专门读取这第 16 位 COUNTFLAG时间没到VAL 没减到 0→ Bit160 → while 死等时间到了VAL 减到 0→ Bit161 → 跳出循环6.1.4.3 Bit 2CLKSOURCE 时钟源选择官方原文Selects the clock source.0: AHB/81: Processor clock (AHB内核系统时钟)大白话解释选择 SysTick 计数器用哪个时钟向下计数写 0时钟 系统时钟 ÷8写 1时钟 72MHz 内核系统时钟AHB你工程里用的就是这个对应的代码SysTick_CTRL_CLKSOURCE_Msk就是把这一位置 1所以你的定时器计数速度是 13.89ns / 次。6.1.4.4 Bit 1TICKINT SysTick 中断请求使能官方原文0: Counting down to zero does not assert the SysTick exception request1: Counting down to zero to asserts the SysTick exception request.Note: Software can use COUNTFLAG to determine if SysTick has ever counted to zero.大白话解释控制 VAL 减到 0 的时候要不要触发中断服务函数0关闭中断只用 COUNTFLAG 标志位查询延时你延时函数的用法1打开中断计数到 0 自动进 SysTick 中断函数对应我的代码SysTick_CTRL_TICKINT_Msk把这一位置 1开启了中断使能。注意你延时函数是查询标志位不是进中断但初始化函数默认打开了中断不影响查询用法。6.1.4.5 Bit 0ENABLE 定时器总使能开关官方原文Enables the counter. When ENABLE is set to 1, the counter loads the RELOAD value from theLOAD register and then counts down. On reaching 0, it sets the COUNTFLAG to 1 andoptionally asserts the SysTick depending on the value of TICKINT. It then loads the RELOADvalue again, and begins counting.0: Counter disabled1: Counter enabled1: Counter enabled计数器开机运行0: Counter disabled计数器关闭停止当 ENABLE1计数器会从 LOAD 寄存器加载重装值开始向下递减减到 0 后自动置 1 COUNTFLAG、根据 TICKINT 决定是否发中断、自动重新加载 LOAD 值、重新开始倒计时。大白话解释SysTick 定时器的总电源开关1开机VAL 开始从 LOAD 值往下数数0关机VAL 停止不动不再计数对应你的代码SysTick_CTRL_ENABLE_Msk把这一位置 1 → 启动定时器开始计数延时结束代码SysTick-CTRL~SysTick_CTRL_ENABLE_Msk→ 把 Bit0 清 0关闭定时器6.1.4.5 Bits 31:17、Bits15:3保留位 Reserved官方要求必须保持为 0不能修改、不用管。6.2 NVIC 优先级寄存器 —— 中断优先级NVIC 优先级在 4.3 章节章节标题翻译4.3 Nested vectored interrupt controller (NVIC)中文嵌套向量中断控制器NVIC它是 ARM Cortex-M3 内核自带的、统一管理芯片所有中断的核心单元你 SysTick 的中断优先级、开关、响应全部由它控制逐条翻译官方特性 对应你的代码讲解原文 1up to 81 interrupts (depends on the STM32 device type)翻译最多支持 81 个中断通道数量由具体 STM32 芯片型号决定你的 STM32F103C8T6 只用其中很小一部分SysTick 就是其中一个内核系统中断。原文 2最重要和你代码直接相关A programmable priority level of 0-15 for each interrupt.A higherlevel corresponds to a lower priority, so level 0 is the highestinterrupt priority逐句大白话翻译1.每个中断都可以单独设置优先级范围0~152.优先级数字越大 → 优先级越低越不重要3.数字 0 全系统最高优先级完全对应你 SysTick_Config 函数里这句代码NVIC_SetPriority(SysTick_IRQn,(1__NVIC_PRIO_BITS)-1);CM3 内核优先级位宽__NVIC_PRIO_BITS4计算(14)-1 15意思把 SysTick 滴答定时器中断设置为优先级 15全系统最低优先级官方规则验证数字越大优先级越低 → 15 是最低完全符合手册这么设计的原因SysTick 只是系统时基延时绝对不能抢占串口、按键等重要中断所以固定设为最低优先级。原文 3Level and pulse detection of interrupt signals翻译支持电平触发、脉冲触发两种中断检测方式STM32 外部中断常用脉冲边沿触发SysTick 是内核内部自动触发中断。原文 4Dynamic reprioritization of interrupts翻译支持运行时动态修改中断优先级程序跑起来之后也可以随时重新设置某个中断的优先级。原文 5Grouping of priority values into group priority and subpriority fields翻译支持把优先级拆分成抢占优先级 响应子优先级这就是 STM32 著名的中断优先级分组抢占优先级能不能打断正在运行的中断响应子优先级同时触发时先响应哪个SysTick 是内核异常默认不参与分组固定最低优先级。原文 6Interrupt tail-chaining翻译支持中断尾链技术CM3 内核硬件加速上一个中断结束立刻直接进下一个中断不用重复保存现场中断响应更快。原文 7An external Non-maskable interrupt (NMI)翻译支持外部不可屏蔽中断 NMI最高优先级、关不掉的中断电源故障等致命错误才会用SysTick 和它无关。