你的浏览器版本过低,可能导致网站不能正常访问!
为了你能正常使用网站功能,请使用这些浏览器。

献给新新新手——SysTick定时器的一个简单应用

[复制链接]
挺子啊 发布时间:2017-6-5 21:10
本帖最后由 挺子啊 于 2017-6-5 21:56 编辑 $ ?4 U+ A! _" }8 }

( W) c" j1 l' R========================: k, P6 y+ A2 p* R  v* p  `
文章转载自知乎专栏 - stm32
' O; N! g2 L3 F+ O6 M========================
: r4 r' w9 U7 K* L% l! m' T1 X" ]  i! Q6 W
7 n! b3 ~0 }4 m  X# U: g+ p9 c
SysTick即为系统定时器,又称嘀嗒定时器,是Cortex-M3内核的一个外设,集成在NVIC中。SysTick是一个24bit的向下递减的计数器,每计数一次的时间为1/SYSCLK(SYSCLK一般为72MHz)。
操作系统需要执行多任务管理,用SysTick产生中断,确保单个任务不会锁定整个系统。同时SysTick还可用于闹钟定时、时间测量等。
由于Cortex-M3芯片都有SysTick,所以软件可以很容易地在Cortex-M3的产品间移植。
我们待会儿将利用SysTick产生1s的时基,让LED一秒钟闪烁一次,以完成SysTick的定时实验。
注:本文所用芯片为stm32f103。
9 {, T, \/ O, a* u
SysTick寄存器
SysTick定时器由四个寄存器控制,如图7-1所示。9 ?4 Z& ?9 d- U; p
QQ截图20170605215247.png
图7-1" K! N9 Z& Q) S
上图是对四个寄存器各个位的描述。其中最后一个校准值寄存器(STK_CALIB)在定时实验中并没有用到,这里暂且不介绍,有兴趣的读者可以自行查找其他资料阅读。

, ]- f0 v+ A2 f3 Z
编程要点1 L6 g" [" [4 M+ ]
  • 向SysTick重装载值寄存器(STK_LOAD)写入新的重装载值;
  • 配置中断优先级;
  • 写SysTick当前值寄存器(STK_VAL),将当前值清0;
  • 写SysTick控制及状态寄存器(STK_CTRL),启动SysTick。
    ; m: g9 v2 [) g2 e8 O$ |; w" L6 Q9 A

* o# Z2 f" y- q( l* w
SysTick属于内核外设,其相关的寄存器定义和库函数都在core_cm3.h中。如图7-2为core_cm3.h头文件里配置SysTick的源码截图。
QQ截图20170605212635.png
图7-2

2 X; S4 J. U; y6 m( T( J: K
SysTick_Config()库函数即是按照上述的编程要点完成SysTick配置的。我们在实际项目中时可以直接调用这个库函数来完成配置。而形参ticks用来设置STK_LOAD的值,最大不超过2^24。然后配置中断优先级,清空STK_VAL,最后配置STK_CTRL。其中,在配置中断优先级时调用了NVIC_SetPriority()库函数,传入的优先级实参为(1<<__NVIC_PRIO_BITS) - 1,其中宏__NVIC_PRIO_BITS为4,则可得其优先级为15,则可以看出其默认设置的优先级在内核外设中是最低的。
& m3 n, `4 U  I. F
那么如果我们同时设置了内核外设和片上外设的优先级,该如何判断孰高孰低呢?
在专栏(stm32):stm32中断初识与实践(上)里说过,在配置外设中断优先级时,需要先分组,再设置抢占优先级及子优先级,那么我们把内核外设的优先级也用同一配置方式分解为这三个部分。
+ N# C; X, J" e/ _1 l2 {- C9 `8 M
假设配置一个外设的中断优先级分组为3,抢占优先级为2,子优先级为1,而SysTick优先级为4。则将SysTick优先级4转换为二进制,为0100(0b),在分组为3时,SysTick抢占优先级为010(0b),即为2,子优先级为0。我们先比较抢占优先级,发现相同,那么比较子优先级,此时SysTick子优先级为0,而我们假设的外部中断子优先级为1,所以SysTick优先级大于假设的外设优先级(数值越小,优先级越高)。

( J, k$ T& i: Z. F: v5 _, W
中断时间计算
& _0 }3 p* v0 X
SysTick的计数器执行的是倒计时,我们要计算中断计数时间,需要知道计数总量(STK_LOAD的值)、时钟源频率等两个参数。打个比方,这相当于我们要计算运动时间,需要知道距离和速度,那么STK_LOAD的值即为距离,时钟源频率即为速度。则中断计数时间为(假设STK_LOAD的值为VALUE,时钟源频率为CLK,中断计数时间为T):
T = VALUE / CLK(其中,CLK为72MHz)
0 S0 M- z: p8 ?& c( a, j" ~6 A8 q
当STK_LOAD的值VALUE减到0时,即可产生中断。如果设置VALUE=72000,那么中断一次的时间T = 72000 / 72MHz = 1ms。

# ?- \% Z8 y5 U: Z0 J/ g8 |/ @
定时时间计算
, R  _  j7 y- ^( D$ B1 N
得出中断一次的时间后,我们可以设置一个变量n,用来记录中断次数,那么最终的定时时间即为T*n。
回到开头的实验说明中,我们需要产生1s时基,来实现LED灯1s闪烁一次,则n为1000时满足要求。
6 p2 n* x0 r' a, W
QQ截图20170605212538.png
图7-3
5 L4 I/ o$ e4 O/ O8 C7 c
由于SysTick不会自动停止,所以我们需要在异常/中断处理中将其停止,即失能SysTick。
到这里为止,一个简单的SysTick定时实验完成了,之后只要在main函数中调用SysTick_Delay_ms(1000)函数,即可实现1s的精确SysTick定时,而不是使用普通的不精确延时函数。

4 c( L' [& E# r; ]$ I' U! I! `; T/ t
专栏(stm32):stm32的“Hello World”——LED流水灯使用的则是普通延时函数实现流水灯效果。

9 @$ n8 H- i) R6 T  n$ m. U# O
--------------------------------------------------------------------
文章首发于知乎专栏 - stm32,转载请私信,并注明原文出处。
4 z+ ^/ n% Q* h' X# E, `! r" C0 s+ F

% B4 k) \2 \5 Y1 [! o8 l5 e! W* P. {/ H" ?- {6 v

0 N; ?. `! m8 j1 Z( n1 E1 E5 Y7 ^7 g2 L5 {: z  G
  f. C& p7 M& ?& z! i2 P9 a

! u. g/ O8 w. D
) C# a' x4 E7 g6 L- @2 Q" h- f2 A) o0 e" }

, Y' b& d/ `3 K$ i2 B2 c4 @$ C, T
赞 收藏 3 评论2 发布时间:2017-6-5 21:10

举报

2个回答
Inc_brza 回答时间:2017-6-26 14:44:30
其实这是一个比较浪费资源的方法。不太建议使用!2 X, n1 I& J0 H- r2 c1 Y
systick能引起中断,可以通过在中断中使用变量自增的方法来达到延时,而延时函数只需要判断当前的
9 R% ^! q# l0 a1 L3 O8 @+ y) Nsystickcount和刚进入延时获得的count做对比就能达到延时作用
valetang-126879 回答时间:2017-6-26 16:57:04
帮顶一下

所属标签

相似技术帖

关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
人形机器人运动控制、感知与智能配电
半导体创新技术与应用方向
EE架构与软件定义汽车
12V/48V 汽车智能配电(SPD)
区域控制单元(ZCU)与分区架构
关注我们
st-img 微信公众号
st-img 手机版