今日内容:SysTick Timer(系统滴答定时器)
手册:《STM32F10xxx/20xxx/21xxx/L1xxxx Cortex®-M3 programming manual》
Systick简介
Systick是在NVIC中的。
这是一个24位的系统定时器。从重装载值开始向下计数直到为0,然后在下一个时钟边缘从LOAD寄存器中重新读取重装载值继续向下计数。
当处理器被停止以用于调试时,systick将停止计时。
如果不需要的话需要及时关闭,不然会消耗系统资源。
寄存器
Systick控制和状态寄存器(STK_CTRL)
地址偏移:0x00
复位值:0x00
COUNT FLAG:
用于查看systick计数是否溢出。当systick计时到0时,该位会被置1,通过读取该位可以得到是否溢出,以此做出下一步操作。
CLKSOURCE:
用于选择systick的时钟源。
0:AHB/8
1:处理器时钟(AHB)
分频主要是看主频是否较高。
TICKINT:
Systick中断使能位。
0:溢出时不产生中断。
1:溢出时产生中断。
可以通过读取COUNT FLAG位来得到是否已经计数到0。如果使能了中断,需要为其确定中断优先级。
ENABLE:
使能计数器。
- 0:失能。
- 1:使能。
当使能systick之后,会从LOAD寄存器中读取重装载值,然后开始向下计时。计数到0时,将COUNT FLAG置1,依据TICKINT配置选择是否触发中断。最后继续从重装载值开始向下计数。
重装载值寄存器(STK_LOAD)
地址偏移:0x04
复位值:0x0000 0000
整个寄存器保存的都是RELOAD value,有效位占用24位,因此计数上限为 2^24 = 16,777,216 - 1。
从0再向下计数一次,才是溢出。
重装载值的区间在0x00000001 - 0x00FFFFFF。也可以将其设置为0,但是没有意义,因为会直接对COUNTFLAG置1,以及触发中断,起不到任何定时作用。
当前值寄存器(STK_VAL)
地址偏移:0x08
复位值:0x0000 0000
通过查询来实现更短时间的定时。
还要考虑数值回绕的问题。一般不使用于us级别的延时,情况相对复杂。
CURRENT:当前计数值
读取该寄存器可以得到当前计时值。
对该寄存器的任何写入操作都将清空该寄存器并且将COUNTFLAG标志也清空。
校准数值寄存器(STK_CALIB)
地址偏移:0x0C
复位值:0x0002328
NOREF:时钟使用标志位
0:系统提供了参考时钟,其频率为HCLK/8.
1:没有提供参考时钟,无法使用基于时钟的功能。
SKEW:用于表明TENMS的值是否精确。
0:精确。
1:不够精确。
TENMS:校准值。
当systick允许在HCLK/8的时钟下时,其校准值。
当HLCK以最大频率运行时,systick周期为1ms。
下图表明,校准值一般被设置为9000,当输入时钟信号为9Mhz时,提供1 ms的计时。
严格计时一般都使用外部晶振来实现。
关于Systick的建议
- Systick计时器运行在处理器时钟信号的基础上,若该信号在低功耗下停止运转,那么systick计数也会停止。
- 确保在访问systick相关寄存器时按4字节对齐的方式访问。防止出错。
如何计算自己需要的定时间隔:
假设当前为72mhz,SysTick Timer使用的时钟HCLK/8 = 72/8 = 9Mhz。由此可得,一个时钟周期为 1 / 9000000 s = 1 / 9 us,9次后计时1 us。由此可以得到计时ms,10us等情况下如何定义重装载值。
常用:
1 ms:Sysfrequency / 1000。(常用)
一般用72mhz直接输入,若用于us的话也有较高的容错率。
10 us = Sysfrequency / 100000。
1 us = Sysfrequency / 1000000。
NVIC:嵌套向量中断控制器
特性:
- 高达68个可屏蔽中断通道。
- 16个可编程的中断优先级(4位)
- 低延迟
NVIC管理包括内核中断等多种中断。用于管理中断优先级以及中断的使能等操作。
中断向量表
STM32F103RCT6属于大容量产品,其中断向量表在204页。
中断向量表的起始地址一般在启动汇编文件当中,中断向量表中具体外设的中断入口地址都是从起始地址进行偏移得到的,因此在编程之前必须保证中断向量表的入口地址正确。
由于外设的中断入口地址是确定的,因此一般来说,某个外设的中断处理函数也是被定义好的,只需要调用然后填充自己所需要的功能代码即可。
外部中断控制器
外部中断控制器最多可以控制19路的中断/事件边沿检测器。每条中断线都可以独立配置输入类型和对于的触发边沿,独立被屏蔽。
外部中断框图
通过中断唤醒内核
可以通过外部或者内部时间来唤醒内核。
使能某个外设寄存器中的中断,但是不使能NVIC中的中断。使能M3系统控制寄存器中的SEVONPEND位。当CPU从WFE恢复后,需要清除相应的中断标志位和NVIC的中断清除挂起寄存器。
或者配置一个外部或者内部EXTI线为事件模式,当CPU从WFR恢复之后,就无需清除对应的中断挂起位和NVIC中断挂起位。
中断功能描述
产生中断:
中断线必须配置好且使能。根据需要的边沿触发来设置两个相关的触发寄存器,当外部中断线产生目标边沿变化时,产生中断请求,对于的外设挂起为会被置 1。在挂起寄存器中的对应位写 1 便可以清除该位。
产生事件:
基本上同上。
直接在软件中断/事件寄存器中写 1,也可以通过软件产生中断或者事件。
中断源选择:
- 配置屏蔽位:EXTI_IMR
- 配置边沿触发选项:EXTI_RTSR, EXTI_FTSR
- 配置对应的EXTI和NVIC的使能位和屏蔽位。
事件源选择:
- 配置屏蔽位:EXTI_EMR
- 配置事件的边沿触发选项:EXTI_RTSR, EXTI_FTSR
软件实现:
- 配置屏蔽位:EXTI_IMR, EXTI_EMR
- 设置请求位:EXTI_SWIER
外部中断线I/O映射
内部中断与外部中断
内部中断指的是内核产生的中断。例如systick发生的中断。
外部中断指的是CPU外部接入的外设产生的中断信号。
NVIC与EXTI:为什么要分这么多个控制器?
可以作为中断使能影响的分级。EXTI单纯控制外部中断,NVIC管理全部中断,涉及的内容大小不同,在使/失能时所造成的影响也不同。
在EXTI控制器的寄存器当中,可以具体屏蔽某个中断,精确屏蔽不需要产生的外部中断。
NVIC的一条中断线上肯定会挂载多个中断信号,那么当某个中断触发时,可以通过关闭NVIC上的对应位,来保证不被同信号线上的其他中断源打扰。
当某些不想要中断介入的场合,比如系统调度,操作共享资源时,可以通过关闭全局中断来保证操作的原子性以及安全性。在完成操作后记得打开中断,这两个操作一般是配套的。
中断向量表的第0项:
中断向量表的前几项一般都是固定的。而一般来说第一位都是其栈地址。中断向量表存放在栈的位置,通过这个地址可以跳转到中断向量表所在栈空间。
主栈和任务栈:
主栈的容量比较大,任务栈一般只存放能让任务继续运行的内容。因为任务栈容量小,万一溢出任务就崩溃了。
任务的主要内容存放与主栈当中,当涉及到任务调度时,切换更加方便。
时钟频率与中断:
假设我当前的中断1ms触发一次,时钟主频越高,在这1ms中能做的事就越多,也就是能执行的指令会更多。
同时要注意,如果中断1ms便触发一次,后台的任务如果其执行时间超过1ms,必然会被中断打断,在其他中断中处理同理,会被更高优先级的中断频繁打断。
因此在选择中断处理的内容时要谨慎对待,也就是对中断处理所占用的时间是相对确定的。
中断标志位的清除:
一般来说,中断标志位都是在刚刚进入中断处理函数进行清除。如果放在最后,是有可能导致标志还未清除,而中断处理函数已经结束的情况,导致循环进入该中断处理函数导致的系统卡死。
例如定时器中断,如果定时的时间较短,导致中断处理函数未能执行到清除标志,重复进入。或者中断触发条件是持续存在的,如果没有第一时间清除标志位,会一直进入。有的手册会明确标注清除中断所需要的指令周期。
Systick实现1ms的中断,然后进行计数
假设当前系统时钟为systemclk = 72Mhz, 定义全局变量counter用于计数。
- 通过STK_LOAD寄存器填入重装载值,定时1ms为 (systemclk / 1000) - 1。
- 确保STK_VAL寄存器值为空,因此对其清空。
- 通过STK_CTRL寄存器,使能systick定时器以及中断,默认时钟源为AHB,因此一般无需配置。
- Systick属于内部中断,并无抢占优先级和子优先级之分,在手册中可以看到,systick中断默认是最低优先级的。
- 中断处理函数:SysTick_Handler。
- 在该中断处理函数中,直接对counter进行累加操作。