用 STM32 通用定时器做微秒延时函数(STM32CubeMX版本) 4 `, |: [' \+ m# }$ z+ M
概述
X& w9 p$ x0 Q3 ?: G% O 在使用 DHT11 的时候,时序通信需要微秒来操作,STM32CubeMX 自带一个系统时钟,但是实现的是毫秒级别的。因此就自己用通用计时器实现一个。
. _( j/ g) `: Z/ {# G- @; q5 `
, V" E2 T3 S; i0 O) x! p( o. ?& ]4 Z' m2 }; A6 r9 \ o. h
0 K/ Q; `+ `* |
; Q+ d- F4 {4 R: s. N, T
环境:
' f+ _4 v; e. z% i" A+ s J) [- `, u9 k# P: @
8 \1 B F4 U2 z8 K* V$ z
开发板:STM32F4探索者(正点原子)$ [5 ? v" h/ J) z' d9 F9 E
& A, }" D' k/ `- w) h. E2 g3 k1 ?, @/ G5 S/ L
文章目录 e4 Q" v- i9 _* g* o
1.配置定时器时钟
6 x2 @& Z4 p% }8 p2.计数器时钟频率及计数模式. d' \" d- l4 N5 m: A6 @/ t
5 E1 c8 t4 E* ?
) _9 N& ^) [# E" ]
预分频系数
( m2 g* }8 Q3 [6 b5 M& w计数器模式! }/ [1 A1 A# i6 e0 R& U. y
自动重装载值
! E0 A% V0 j* v) S3.打开定时器中断
# Z9 W$ D& U% d! j( _( j- \7 q z# `4 x4.具体实现代码
4 j; s% O+ V7 P9 ~0 a, `, M+ m5.代码测试
9 f9 ?* A/ r0 T* m+ R+ X; s0 S c8 \$ b, ~! e
. `2 d& c. S/ ?9 Y; W
1.配置定时器时钟; k2 { C* M! z# p6 C
·选择时钟源
2 _8 J7 H$ u; M$ G这里选择的是内部时钟,来自 RCC 的TIMxCLK,在通用定时器框图中我们可以看到如下:* J/ M- |* w9 F G. N
而我们可以在 STM32F4xx中文参考手册中找到,TIM2 在外设总线1(APB1上),因此其时钟为 84MHz,如下图所示:( u& L1 N8 i6 N) w* m( C5 ?
2.计数器时钟频率及计数模式
! g0 A0 a1 W5 r+ |9 m1 V除了配置定时器的时钟,还需要配置计数器时钟频率,我们要实现微秒延时,因此计数器时钟频率应该是1MHz,0 d# v5 R# C1 F" ]+ x) \: b- V8 A
- r4 L0 Z+ n" @
) Y$ M5 Y/ `" M1 j( b ^而要实现还需要以下3个参数:0 J7 q& V3 Q) |: v6 i ?5 N
预分频系数
% G7 i' v' q# F( i% }; n3 J根据STM32F4xx中文参考手册中的时钟频率计算,如下图所示:1 g( l$ ]% f2 |1 i; o! A
其中fCK_PSC就是通用定时器框图中的CK_PSC, 即值为84MHz,而我们所要的计数器时钟频率1MHz6 [6 ~( K$ e+ B, \& M O
d- l6 g! L% g
! X7 X3 M4 u8 Z; h% Q: G5 j
因此:
* s& M4 E/ k+ r- PSC[15:0] = (fCK_PSC/CK_CNT) - 1 ; Q; q* D8 w! M
- = (84/1) - 14 k9 Y! `% @+ v- {" b
- = 83
复制代码 因此预分频系数为 83( D" H* s V' ^# E4 @! {1 z
+ T: K, @- | u) }2 X# \* `
7 B$ a, p0 |6 I计数器模式
) W. {- o& j3 d! _% E7 e1 X计数器这里采用向下计数模式,也就是 如设置计数值为 1000,那么每隔一个微秒,就减一,一直减到 0
! \! _; t# c* _5 l9 E1 ~
$ t0 x. L1 u5 C7 o
' s5 Y, \( Q$ v1 B+ d) o1 S自动重装载值
( M* y/ Q9 g, I! ]虽然我们并不使用自动重装载功能,但是,我们还是要对自动重装载寄存器进行赋值且不赋值为0即可,但是我测试时发送,如果为1,延时会出现偏差,因此这里赋值为 2,依据如下:* [6 a/ I) U) J* U, f
% E7 t+ ^8 X2 ]3.打开定时器中断
) S. s- A& c& @5 V- x4.具体实现代码3 N9 L4 S: _/ M1 y4 W6 M- y" B) g; f
- volatile bool elapsed = false; //用于判断设置的计数值是否耗尽(向下计数模式),耗尽时,在中断中奖会设置为true% T8 o' |4 `( D4 p5 h' \
) Z0 ?! q6 F& e& s2 w; F" w- J0 q$ x- void setState(bool state)
/ _+ x8 S- o; m ~5 @0 `5 q8 g - {8 C- X3 `6 m1 {; Y0 L" Z
- elapsed = state;
/ m; g1 B0 |. E5 J( Z# F* s' k0 p - }
& A+ A7 a, X: _. Z- P# V
) d, S/ B" e+ @% @% N- bool getState()
% T0 F' G: q7 K- c* U - {. r H. D) {$ Q+ G& K
- return elapsed;
2 F( ]6 L1 {1 A5 c" n, ? - }) G( D9 j/ m# a: ?: n5 I
5 ^$ x& d7 y) x, Z- void usDelay(uint32_t time)& a t* R7 o/ Z* y
- {
4 H* B% t1 A. p" d9 W; r* d/ r8 E6 Y
$ ]+ d& F3 m* ]* Q- __HAL_TIM_SetCounter(&htim2,time); //设置计数值$ z! ?2 @& H* Z
- setState(false); % M9 R0 X- b k7 m" @
- HAL_TIM_Base_Start_IT(&htim2); //开启定时器
4 L5 }+ \ z0 J+ j - while(!getState()); //判断计数值是否耗尽! j e# a( ^0 k& Q; B
- HAL_TIM_Base_Stop_IT(&htim2); //关闭定时器
+ S1 j6 v* x# A' @/ L -
. ?8 t3 }; i8 m1 ~! L - }
复制代码 计数值耗尽回调函数6 ?2 m6 {8 C: [
- void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
/ C- \% ^. K7 [$ I - {2 v6 d( [0 {6 \; k& M3 \
- if (htim->Instance == TIM2) {
2 z; f3 J; y/ t u$ f - setState(true);7 i- B" X0 d" R# R4 w3 g
- }( S9 U' M6 K+ O8 S- e
-
2 f- ?6 |, a& f3 Y; h - }
复制代码 5.代码测试5 ~, q z+ u. C4 p& o
主函数的主循环中:
8 |! H. b# I3 m) y C# h- while (1)
# a3 } j9 G; m2 E8 b - {) Y, S Y1 |) _" t) u- `
- /* USER CODE END WHILE */5 V' X7 W" F0 F! o4 o6 n
- - A4 w; l6 y& n4 W5 [
- /* USER CODE BEGIN 3 */2 b5 B* n* e3 \3 ^6 b$ W
- printf("hello usDelay");
: ]- [( |. M# e& m1 J - for(int i = 0; i < 1000; i++)
, `8 r& t \8 o" L8 Z - {
9 r" E0 d% x# W# ^0 {# S* o/ Y- c# }& A - usDelay(1000);+ E2 C* s* p' Q" Q }" S
- }$ @- f4 I4 W1 P% T2 `
- 8 w- T* A: J) F8 l/ ]' W+ }
- }
复制代码 在串口调试助手中,可以看到如下,一秒打印一条语句$ U3 S+ X8 Y& H- i2 S
在后台回复“微秒延时”,将获取项目代码!
6 j# j" P0 ~9 U: g/ J* c后续也将分享如何使用该函数读取 DHT11 温湿度传感器的值,欢迎关注我与我交流!
- T# @2 t, i- x/ U. k& P6 Z2 l% S$ }/ @; a8 G3 h% o5 O
|