|
用 STM32 通用定时器做微秒延时函数(STM32CubeMX版本)
0 p" h& V2 w7 b概述) o1 _# x; g6 c. v
在使用 DHT11 的时候,时序通信需要微秒来操作,STM32CubeMX 自带一个系统时钟,但是实现的是毫秒级别的。因此就自己用通用计时器实现一个。* a# s0 X+ r! s! ^9 c' V
& s$ ?6 X" t N" v3 s" I+ h: w1 Y2 e; ^* w# X0 M
: p8 G. ^' L/ F
( o& C. m0 \+ t2 A1 |& c1 j4 b. c! @
环境:
4 c! M) w! `% [3 W4 Z$ M% Y& v$ a5 {9 x) J0 W
& ^. g: o+ ^( \& z# A* o
开发板:STM32F4探索者(正点原子)
( A% Q) K$ \) W# C, F5 O- l3 \3 J+ Y% ?) P8 k( T
- m) z3 J9 E6 i; E
文章目录3 r; M) b6 F# k q4 O
1.配置定时器时钟
6 d% `& k9 P. x7 L- }2.计数器时钟频率及计数模式. k5 g ?" ?4 p
1 V. ]) U5 {, v3 G* }0 Y# e8 \' @* Q/ f4 s+ q3 I+ K9 C l
预分频系数
; ~+ D$ a2 l1 L( t3 _# v计数器模式
+ o, Q. c) C% O' g& ?6 R自动重装载值( ^3 X8 L3 d6 P$ K. q3 H
3.打开定时器中断
8 O! t' i$ g, x/ q. W4.具体实现代码5 L* t g5 |5 x$ q: E$ I* l: `
5.代码测试
9 h6 b; W& r5 C2 y$ Q2 G) d2 x% ~% n1 D* j2 a
- |* U* v* _8 J8 a5 Y8 W
1.配置定时器时钟" ?/ d/ X) x& I! H- ]5 Y! }
·选择时钟源; w8 J, [4 l9 Q; L& T1 O
这里选择的是内部时钟,来自 RCC 的TIMxCLK,在通用定时器框图中我们可以看到如下:
2 q/ l" u; C" p3 Q而我们可以在 STM32F4xx中文参考手册中找到,TIM2 在外设总线1(APB1上),因此其时钟为 84MHz,如下图所示:
$ a; ]/ r* _ h5 }/ @! c7 A+ b+ g: s2.计数器时钟频率及计数模式; ?# ^' e3 h( [8 O3 M3 l0 O( C
除了配置定时器的时钟,还需要配置计数器时钟频率,我们要实现微秒延时,因此计数器时钟频率应该是1MHz,
3 A* C, Q' s7 D& D" E9 q5 @" B; M) H- R# x/ y' ]
4 d I q' h6 y, g6 W而要实现还需要以下3个参数:
& B6 Z6 [: Q( t预分频系数
* }# y# V/ |, k根据STM32F4xx中文参考手册中的时钟频率计算,如下图所示:
7 G9 W$ \2 h& A3 I% g0 f其中fCK_PSC就是通用定时器框图中的CK_PSC, 即值为84MHz,而我们所要的计数器时钟频率1MHz9 P6 C9 l- D' |* J$ X) k t. E
9 i. H6 }) q+ J1 P) s1 j" z
4 u8 C C$ L q8 Q# z! y因此:
/ O s' d T: c) r2 Z9 ]. g- PSC[15:0] = (fCK_PSC/CK_CNT) - 1 3 e+ V+ d$ F& `' {
- = (84/1) - 1
; u1 x" u: W2 ?/ Y3 T& |: y - = 83
复制代码 因此预分频系数为 83
* v& m: E. C# ^0 J9 p- K% y7 E3 ]7 C/ w
' q( g7 c& K. W5 `$ c计数器模式- @6 g' k* d# f% ]: r/ s
计数器这里采用向下计数模式,也就是 如设置计数值为 1000,那么每隔一个微秒,就减一,一直减到 0 E' E) J/ ]6 w
+ [, m o' N" J- v" M
Y5 f- a$ I- i% d" {5 v
自动重装载值- u. G/ Y. Z' ~" i0 C w7 z, e
虽然我们并不使用自动重装载功能,但是,我们还是要对自动重装载寄存器进行赋值且不赋值为0即可,但是我测试时发送,如果为1,延时会出现偏差,因此这里赋值为 2,依据如下:
/ a9 _: o) R# X3 M! F# b! {8 E
5 G0 I/ U; Q t2 H/ b3.打开定时器中断
" {: b! p2 M# q4 G7 V. L4.具体实现代码
5 K# O: \0 f z$ V [$ C5 I- volatile bool elapsed = false; //用于判断设置的计数值是否耗尽(向下计数模式),耗尽时,在中断中奖会设置为true
; D* O/ O$ `4 p w$ O V - & c: _' _, A; z) Z
- void setState(bool state)* V$ `1 T; r. ^6 ^) h4 x
- {
9 }; N) U! O; v9 B" _. `8 q8 f - elapsed = state;* o1 s F3 ]: |+ ^& i) E
- }
2 [6 i& ]' v! b - ; ?# k! q! W8 t; p
- bool getState()
/ o# ]' ]# H. T4 z - {
7 [& Z9 B0 L; U- l - return elapsed;' R8 P. p! [5 ?; }4 d; Q- J
- }8 q. y4 U/ |3 s9 p
- ; g2 m& O2 F& J+ L1 h- o
- void usDelay(uint32_t time)% J# N0 Q+ [* U; B$ x- d
- {% v% B/ ^2 V+ i
7 N' s1 _" Z# g a* [, _) ~- __HAL_TIM_SetCounter(&htim2,time); //设置计数值 [) u' b: ]5 a$ T' d# n
- setState(false); # h3 q5 e* ]: ^4 C/ x8 c2 e
- HAL_TIM_Base_Start_IT(&htim2); //开启定时器% D3 H6 ^% z% @" c' U# F
- while(!getState()); //判断计数值是否耗尽" G7 o4 A! l: i
- HAL_TIM_Base_Stop_IT(&htim2); //关闭定时器
: _+ y4 } \$ n5 X$ Y! w5 c. R) U -
: v7 Q) I: f+ [ - }
复制代码 计数值耗尽回调函数
$ z( C5 s! M8 m0 C' r0 V8 {6 H! Z- void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)4 M- ^) _0 A/ ]4 ^ P G6 ^# S3 O
- {& b, @) e2 M9 ~+ m( s
- if (htim->Instance == TIM2) {- ^& f' o( r! C7 _2 @ x8 T
- setState(true);
, m1 W. ~: J7 F R4 g1 \! J - }; Q+ a3 Q" y! G* u& Q; ~5 d
- 7 U0 f1 }, T2 T B
- }
复制代码 5.代码测试
0 ?! I9 y' z5 o9 R! E主函数的主循环中:2 _( `! `* S; y5 p* P
- while (1)2 L2 E6 v# i; h! i1 b9 ~
- {
' H7 _7 s, m: W; d/ { - /* USER CODE END WHILE */& ?( o; n- d7 n* f/ _
- . x& ]: S, j- E# f" D; `2 F
- /* USER CODE BEGIN 3 */
4 y! i F7 C/ h8 D - printf("hello usDelay");
9 c8 J) Q* l, a" W r& [8 ~: y; i - for(int i = 0; i < 1000; i++)# C7 z e( X7 [
- {% [4 l! B6 z3 j! f
- usDelay(1000);* o5 N& y/ q+ T# M. u9 {3 L; J
- }$ Z) Z' x: P$ \+ A
-
4 Y8 s. O! [& C - }
复制代码 在串口调试助手中,可以看到如下,一秒打印一条语句8 V2 l: c4 y' O' k- S
在后台回复“微秒延时”,将获取项目代码!6 ~* g! B0 h2 I+ d
后续也将分享如何使用该函数读取 DHT11 温湿度传感器的值,欢迎关注我与我交流!
! s4 Z4 _! X/ b0 _; e' ^* A! B: L; ^* S! i- |" H ?8 w
|