用 STM32 通用定时器做微秒延时函数(STM32CubeMX版本) ) h* @# `1 H, U* h
概述9 N" A2 ^% M+ Z5 G% J
在使用 DHT11 的时候,时序通信需要微秒来操作,STM32CubeMX 自带一个系统时钟,但是实现的是毫秒级别的。因此就自己用通用计时器实现一个。
/ p5 n' ~6 ~7 }# [1 n6 L9 K0 D# ?3 w3 I1 O5 }" L: \
7 _; K" j4 ?7 V! G8 w/ n& M% D
- |+ {0 O% t8 c# m% d
: F4 M( u( ?5 |6 ^* r+ a 环境:7 g0 g# o# s0 u" f* V7 L
5 }$ } D) S1 y$ ^: z
; e E6 J; M$ J4 W& Q! s9 o. B开发板:STM32F4探索者(正点原子)2 h- i: i2 Q$ r" r9 W' R* m
" @' y6 d! {( _8 ~8 ^ z- V7 w0 q' [$ f1 b3 Z
文章目录 c% K+ X8 c3 G- o
1.配置定时器时钟
8 `& Q+ }4 D8 i8 D2.计数器时钟频率及计数模式! A, p, y, \' ?8 z: G. e
) j1 q+ d7 E7 B9 X+ o6 j# e% @$ r* g8 r' n$ ]! l, U% |( {
预分频系数
$ ?5 E5 V9 \; y7 H$ z7 f计数器模式) F4 { }8 z8 _' I& h
自动重装载值8 W8 Q5 |8 W2 V _0 ^1 k$ o; K
3.打开定时器中断; c; H+ P* S( X; Y& W
4.具体实现代码
; |) K8 B1 G6 _6 l/ w/ t) z$ u5.代码测试: Q6 ^( [ p2 j9 r1 R( P
/ g8 T- \: ?4 N- T! ~9 V- m& ]' z* s C; Y& a: @) d
1.配置定时器时钟
9 c- ?0 l+ `8 m' V8 }9 s [$ t·选择时钟源
6 U) y* _! g% T, g$ f+ R这里选择的是内部时钟,来自 RCC 的TIMxCLK,在通用定时器框图中我们可以看到如下:
4 K4 f6 {% e& A, S5 j$ H. Q而我们可以在 STM32F4xx中文参考手册中找到,TIM2 在外设总线1(APB1上),因此其时钟为 84MHz,如下图所示:
* h D" r' o4 Q0 p2 w2.计数器时钟频率及计数模式
9 s* k6 `. s& z除了配置定时器的时钟,还需要配置计数器时钟频率,我们要实现微秒延时,因此计数器时钟频率应该是1MHz,
! M& x" b# @$ a6 \ r0 e7 s
0 x+ S6 F4 H3 R
! u- q5 E/ ~% K5 K; [. r; Z3 ^3 h而要实现还需要以下3个参数:; P6 f5 m9 n; I: {" [. `) I
预分频系数
! ] [3 ]. X- _; B% B5 T2 o根据STM32F4xx中文参考手册中的时钟频率计算,如下图所示:
0 K3 `7 p2 @2 B2 y+ v其中fCK_PSC就是通用定时器框图中的CK_PSC, 即值为84MHz,而我们所要的计数器时钟频率1MHz
- |& {2 Q; F5 D7 I: \3 n/ H
( d1 K- I. h) B1 c; }. J/ L. }3 h* m/ H
因此:
' H& \# }; I3 i; o+ w2 w* F- PSC[15:0] = (fCK_PSC/CK_CNT) - 1 ) o; x2 a& D/ h% y' a& j
- = (84/1) - 1; z n* G9 J P4 W" c+ o( _' T% j
- = 83
复制代码 因此预分频系数为 83' Y9 c# `! @. I- {$ G, T
+ s& F7 l. z5 u0 K9 P
" D" D/ F% a+ ]( Z" G5 ?+ M/ S
计数器模式8 p: A( D2 k7 e6 J# R T
计数器这里采用向下计数模式,也就是 如设置计数值为 1000,那么每隔一个微秒,就减一,一直减到 0
) ~8 b: [ i' X
M# N: y2 d8 P f! r6 l, V7 }! K
自动重装载值
8 N) m, ]' f8 t5 L4 q, m虽然我们并不使用自动重装载功能,但是,我们还是要对自动重装载寄存器进行赋值且不赋值为0即可,但是我测试时发送,如果为1,延时会出现偏差,因此这里赋值为 2,依据如下:% V: k: D: [ G: r7 }
8 s6 B) C! K4 J
3.打开定时器中断
$ I8 w0 o* L& H9 M4.具体实现代码+ }) v u: f m! G# I- B
- volatile bool elapsed = false; //用于判断设置的计数值是否耗尽(向下计数模式),耗尽时,在中断中奖会设置为true
! Y7 {1 e; z, R$ y; | - " k, Q# W6 L" {. w
- void setState(bool state)
+ X, K5 v4 t# X0 h7 u9 Z8 p8 H - {
8 U$ R- w( M% b - elapsed = state;& T0 Q3 x/ `/ g
- }
, }( H8 ^ C& z+ C" m - ) ~; k' }9 o( q" p6 Q
- bool getState()
8 o6 t3 e2 O' X3 O3 ?+ h - {
* |& i+ d2 _* c1 M3 ^ - return elapsed;1 e' L6 f0 l7 R3 z
- }
5 T8 L/ g# n! c; [9 v - ; Q# f% @$ D8 |* x
- void usDelay(uint32_t time)
! Y' p5 ~% B. Q* z- Y! r1 o - {0 @- G% ?5 p) G7 M& D! o g
7 D3 ]- b: Z. T9 v- __HAL_TIM_SetCounter(&htim2,time); //设置计数值( I4 k0 k3 Q [! j5 _
- setState(false); 5 S* ^/ t W! [" F% A
- HAL_TIM_Base_Start_IT(&htim2); //开启定时器
- k9 }0 S% o% s) ]2 h - while(!getState()); //判断计数值是否耗尽
2 `& ]4 C0 ~- v, j1 } - HAL_TIM_Base_Stop_IT(&htim2); //关闭定时器7 w( z3 a0 ?" r5 r
-
: ?( {6 N8 X' M s+ `7 e6 Y7 Y - }
复制代码 计数值耗尽回调函数- b* k. S: ~9 H1 K/ D
- void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
! e: k# B6 R& \; ~+ |: r$ q3 u - {
; o6 L9 w8 h Y - if (htim->Instance == TIM2) {
5 }6 u5 v; D2 o% p+ D( w/ [1 T - setState(true);
: x. r& A! h8 E% u( K% ?! U - }
) e: t* ~1 |; s" l -
6 n, r5 G/ l5 w! Z" j5 Z; h - }
复制代码 5.代码测试
- v$ g2 V& m3 U' l! L6 n* W) k! j主函数的主循环中:' K0 G/ R, Z, a$ q9 q
- while (1)! i5 s$ b5 ]9 O3 ?$ w) I$ W; I0 }
- {
; @$ W6 C+ {6 Q+ b: V0 M! a& `5 u+ K - /* USER CODE END WHILE */
& F) t( I, m" _, w/ [
; W. i9 T9 h! o! l7 K. x% u8 P- /* USER CODE BEGIN 3 */0 O& p1 ^4 U# l- d. O" b. T
- printf("hello usDelay");
9 q+ P% a5 s9 ~1 g' [1 ` - for(int i = 0; i < 1000; i++)
9 G; ]3 }/ u0 p9 n1 O" ^5 r - {
4 Z7 g$ t& q4 y- D - usDelay(1000);* M# E' Y0 N! L0 r
- }
1 \, l' v& G# _8 L - & ~1 Z3 O P7 ^
- }
复制代码 在串口调试助手中,可以看到如下,一秒打印一条语句" [# @1 G1 t1 u# h- C# Y& T+ \
在后台回复“微秒延时”,将获取项目代码!' M d" J1 f$ y5 E! b
后续也将分享如何使用该函数读取 DHT11 温湿度传感器的值,欢迎关注我与我交流!
! f1 t9 b* A( H8 G$ i* z/ g. Y; j% q: K7 n5 J" M2 p B7 }
|