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

【经验分享】STM32开发项目:微秒级的精准延时 - 使用system tick/DWT寄存器/NOP命令

[复制链接]
STMCU小助手 发布时间:2022-4-13 11:00
背景; h$ o% k" \( j8 f  D" K
延时函数在STM32单片机开发的项目中有广泛的应用,微秒级延时在一些时间要求严格的场景下(例如软件模拟I2C通讯)是必不可少的。由于FreeRTOS默认采用了system tick作为时间片分配的时基定时器,可能与利用system tick设计的延时函数出现冲突。再加上FreeRTOS提供的延时函数void vTaskDelay( const TickType_t xTicksToDelay )最小的延时时间等于FreeRTOS的tick时间(一般设置为1ms),因此需要重新设计一套不基于system tick的微秒级延时函数。利用CM3/4内核中的数据观察点与跟踪(DWT)寄存器,可以在不占用硬件外设定时器的情况下实现微秒级的精准延时。
3 S1 {5 a; r, J" H) y' B5 ]9 y3 u+ e' T$ O3 k. N: V
源码# |8 s4 Y& c- A6 ]- }) d2 f
通过选择#define USE_SYS_TICK, #define USE_DWT, #define USE_NOP_DELAY其中一个宏定义来决定delay函数的实现方式。需要特别注意的是,由于函数调用是需要时间的,因此使用延时函数时,一般会额外多出一些时间(1微秒左右)。9 {% y3 I1 m4 I9 ~, O
* c% q/ q, e8 P
#define USE_SYS_TICK是利用system tick设计的延时函数,在没有使用实时操作系统的时候,它是一个比较适合实现微秒级延时的通用方法。0 g! A* J' z8 X& k
#define USE_DWT是利用CM3/4内核DWT寄存器设计的延时函数,它具有不占用system tick与外设定时器,延时精度高的特点。
" h3 X* |' E  b9 Y% ^9 ]#define USE_NOP_DELAY是利用了空指令设计的延时函数,它主要实现微秒以下的超短时间延时。8 j/ q  e! w4 a( E0 {% A% r% v2 ?5 h% @
头文件
/ r) U8 z/ m( H! ]8 \8 q6 \
  1. #ifndef __DELAY_H__
    , a) l8 D- [# R3 C
  2. #define __DELAY_H__                           
    - r' ~$ a- K5 R3 }
  3. . m; k; W% u' G9 z6 i
  4. #include "stm32f10x_conf.h"
    % Y, H' N+ C( Z. h6 o
  5. #include "stm32f10x.h"9 K8 u+ p- o4 I! W' W

  6. 9 U' r+ R( }. v9 I8 B; Y3 `9 O
  7. //#define USE_SYS_TICK
    : c' ?4 x7 K- e0 G/ m# `1 }7 j
  8. #define USE_DWT: r, }% P, D  q$ q. c' N- S6 O
  9. //#define USE_NOP_DELAY
    * M! c/ O' H. F- w7 R
  10. 0 Z* M' @. l0 r. W# I, }3 I+ i: H
  11. void delay_init(void);9 j5 w- a: ~4 s4 H2 \4 g
  12. void delay_ms(uint16_t ms);5 e1 j6 s8 V0 ~
  13. void delay_us(u32 us);# j$ U& Z' J5 G! j# H
  14. ' N7 m* Q7 W. q
  15. /**
    8 R6 }- k+ P# j1 z, T4 M2 t1 {
  16. * 定义ns级延时,72M主频下,每一条空指令大约14ns
    $ C, }! I- h- `; D" J+ Q" A6 a
  17. */' N3 {% g/ H: z# y' |; H# R
  18. //#define DELAY_6ns()                                        __NOP()5 V! n5 N3 X  [% L) w6 f6 y
  19. //#define DELAY_12ns()                                DELAY_6ns(); __NOP()
    ( H# A( T( P  b# y# j0 l/ |
  20. //#define DELAY_18ns()                                DELAY_12ns(); __NOP()
    & ~5 I2 `: H) B& ^6 |$ k
  21. //#define DELAY_24ns()                                DELAY_18ns(); __NOP()1 u$ W' J' o5 J
  22. //#define DELAY_30ns()                                DELAY_24ns(); __NOP()
    : B% u0 A' I& _! C" b
  23. //#define DELAY_60ns()                                DELAY_30ns(); DELAY_30ns()0 \. |( R& ^/ C" f9 K* n3 H# K  t9 O2 o
  24. //#define DELAY_90ns()                                DELAY_60ns(); DELAY_30ns()
    % K+ R( C: n$ h

  25. 9 \% d+ L' `- N: |* j% X1 g7 y
  26. : K4 w5 j7 K& U. a, O; P
  27. #endif( a' ?' D3 ~6 y. A$ w4 Z1 j  E

  28. ! s2 s0 o& k& n% h
复制代码

/ Z3 @, e1 [  y9 _7 _源文件
4 K0 F+ d$ S, y/ e* C$ m
  1. #include "delay.h"
    1 B. B4 {. z- q& ?' Q, c5 t

  2. 5 y7 ?8 y' L4 Q; g
  3. #ifdef USE_SYS_TICK8 @# w" j1 \/ R3 R1 s/ ^+ Q# Z! G
  4. 9 M) @& ]. M0 m% v" F
  5. static uint8_t  fac_us = 0;                          //us延时倍乘数
    5 ^9 X  @4 k% M4 u  i/ F+ U
  6. static uint16_t fac_ms = 0;                          //ms延时倍乘数,在ucos下,代表每个节拍的ms数
    $ n* v% }; v( g+ c. A! A
  7. : e8 [% L/ F7 W5 _
  8. //初始化延迟函数/ ~/ w9 @4 b" S( j
  9. //当使用OS的时候,此函数会初始化OS的时钟节拍4 B# L" j+ n3 i
  10. //SYSTICK的时钟固定为HCLK时钟的1/8
    * \3 S5 z0 ^3 B* ]8 ]+ l
  11. //SYSCLK:系统时钟* j) ^: m0 d% P) b% n1 h1 b
  12. void delay_init()8 h7 r6 P2 r0 @' j7 ], @
  13. {
    1 E* G0 N% C' N- C
  14.         SysTick_CLKSourceConfig ( SysTick_CLKSource_HCLK_Div8 ); //选择外部时钟  HCLK/8
    # d- V" }3 _8 O3 j  d1 h# M
  15.         fac_us = SystemCoreClock / 8000000;         //为系统时钟的1/8
    6 m, Q9 U& q, I" `6 ^9 K
  16.         fac_ms = ( uint16_t ) fac_us * 1000;             //非OS下,代表每个ms需要的systick时钟数
    / S  L. C# Y! Z! ?5 j
  17. }- N' D& I( b) H; f$ G5 I7 _

  18. / D# Z, C! I+ A  S& S$ e& l) D
  19. //延时nus
    7 q7 F3 }7 `: C" F" a, o+ S0 p
  20. //nus为要延时的us数.5 a$ t4 M2 @9 b8 f6 P* n3 |" l7 P
  21. void delay_us ( u32 us )1 w$ i; p& Z) `2 f1 H
  22. {
    , j9 B1 l! z, y0 M* H4 `( v
  23.         u32 temp;
    4 ^5 u; J9 D" r; n* H* D- f% g- ^
  24.         SysTick->LOAD = us * fac_us;               //时间加载
    4 r$ h5 m, o1 ]7 o9 Y( C1 h
  25.         SysTick->VAL = 0x00;                        //清空计数器/ t3 h8 Q& h- X, u7 y1 b
  26.         SysTick->CTRL |= SysTick_CTRL_ENABLE_Msk ;  //开始倒数
    + H0 O( |8 x: K5 L! d
  27. $ A: J8 V! b1 I" K
  28.         do+ h9 z  V. r. ^/ M5 F
  29.         {4 o, M. P. b4 q4 D
  30.                 temp = SysTick->CTRL;5 k6 O* O& n4 `3 @  I, h
  31.         }
    7 B4 z3 x' h' Q. W
  32.         while ( ( temp & 0x01 ) && ! ( temp & ( 1 << 16 ) ) ); //等待时间到达; g$ V1 b2 T6 A7 ?

  33. 8 O+ T& G; l0 }8 [
  34.         SysTick->CTRL &= ~SysTick_CTRL_ENABLE_Msk;  //关闭计数器  K1 o  C  ~1 W
  35.         SysTick->VAL = 0X00;                         //清空计数器5 a2 T# O- Z6 Y7 ]: d
  36. }
    ' }* ^0 a7 v! ^8 D1 g( V9 u
  37. //延时nms0 h& B- V, c0 R
  38. //注意nms的范围
    " g$ `; u6 @: B  s, @9 Q
  39. //SysTick->LOAD为24位寄存器,所以,最大延时为:
    & y' }+ a* M, K$ B: ?
  40. //nms<=0xffffff*8*1000/SYSCLK
    7 T" t( @; L& l! `& y4 Y
  41. //SYSCLK单位为Hz,nms单位为ms
    - `, ^/ B/ g" K2 C$ ]
  42. //对72M条件下,nms<=1864( K6 r3 b9 Y# u3 m( w( _8 b, G
  43. void delay_ms ( uint16_t ms ), U3 A+ ~$ j+ [8 n
  44. {' {# [+ s! R% n5 F' x
  45.         u32 temp;
    ; }: w$ w1 W( W; g
  46.         SysTick->LOAD = ( u32 ) ms * fac_ms;       //时间加载(SysTick->LOAD为24bit)& E/ ]/ v9 e5 k0 _. b2 @: J1 o
  47.         SysTick->VAL = 0x00;                        //清空计数器
    7 O5 c: m/ x. z7 G
  48.         SysTick->CTRL |= SysTick_CTRL_ENABLE_Msk ;  //开始倒数
    , r. h' ]+ B  i/ B
  49. $ i0 R& r5 e9 \0 E# U
  50.         do
    2 S6 Q+ p# R. }+ d( Z- z  p
  51.         {
    8 [2 u  b: E# F; N. _% N
  52.                 temp = SysTick->CTRL;
    & E6 w' L8 h' u' b' A
  53.         }
    / h7 @, Z( }+ G# f( h# ^+ m
  54.         while ( ( temp & 0x01 ) && ! ( temp & ( 1 << 16 ) ) ); //等待时间到达
    - V1 f1 O, Z; ?! D+ W6 C& C* I$ q

  55. 5 l  C5 x1 D6 @
  56.         SysTick->CTRL &= ~SysTick_CTRL_ENABLE_Msk;  //关闭计数器6 o: O* f) T* P' T/ D/ }( m
  57.         SysTick->VAL = 0X00;                        //清空计数器
    * l' _3 B5 A! {& d
  58. }4 L, _2 |+ O, o: s1 T* g

  59. 9 u" {9 l# ]6 A; a5 N
  60. #elif defined USE_DWT5 Y, M# `7 p" R8 g+ I4 }

  61. 9 L" V. S. \3 }& r7 x
  62. // 0xE000EDFC DEMCR RW Debug Exception and Monitor Control Register.
    : h, Y0 Q( r% [+ m2 M
  63. #define DEMCR           ( *(__IO uint32_t *)0xE000EDFC )
    ; o  Z# D& R1 ]
  64. #define TRCENA          ( 0x01 << 24) // DEMCR的DWT使能位
    / g+ l! u5 X1 @! x5 N# I6 m  ^

  65. 7 v  J' H6 ]! z) c
  66. #define  DBGMCU_CR   *(__IO uint32_t *)0xE0042004                        //MCU调试模块控制寄存器,详细内容参考《stm32中文参考手册》调试支持(DBG)章节,747页
    5 j& y7 M' H, x
  67. ! f) i3 d- m( Y! ~9 b
  68. // 0xE0001000 DWT_CTRL RW The Debug Watchpoint and Trace (DWT) unit
    0 T3 Q( U5 \/ v. a$ W4 `
  69. #define DWT_CTRL        ( *(__IO uint32_t *)0xE0001000 )
    ; p$ |7 c  `) l' C! X
  70. #define DWT_CTRL_CYCCNTENA       ( 0x01 << 0 ) // DWT的SYCCNT使能位
    0 o' k& N. J& d# B& ^3 X) F  v

  71. 6 R- N8 {9 @  c3 @2 h: A- A
  72. // 0xE0001004 DWT_CYCCNT RW Cycle Count register,
    ; [# t4 [. I& Y9 z$ w! C  I
  73. #define DWT_CYCCNT      ( *(__IO uint32_t *)0xE0001004) // 显示或设置处理器的周期计数值
    ! w7 S1 N5 |" R2 ^% J3 d

  74. 2 b7 S, l, v' J5 q( n
  75. //#define DWT_DELAY_mS(mSec)    DWT_DELAY_uS(mSec*1000)4 U4 G2 r1 o, _4 v- m) V
  76. ) }: O( T% \/ S! s# @% A( j/ R
  77. void delay_init()
    5 W5 U' j. @8 i. w& ~* K; \
  78. {) f) ?$ n, s: D! o) H/ m* c1 H% S- [% k
  79.         //使能DWT外设: k3 d* }' I9 C% n1 C/ f; Z
  80.         DEMCR |= (uint32_t)TRCENA;
    # h5 k0 V3 m1 Q
  81. / T! u6 u9 q- V# k0 S# ]$ _8 M
  82.         //DWT CYCCNT寄存器计数清02 m3 ~$ ~3 [- T7 p! k3 f- ~
  83.         DWT_CYCCNT = (uint32_t)0u;; n: F+ g# b6 d4 p; e
  84. $ ], E, [4 a8 k5 }* a
  85.         //使能Cortex-M3 DWT CYCCNT寄存器
    2 T2 V+ p4 s# ?, Z
  86.         DWT_CTRL |= (uint32_t)DWT_CTRL_CYCCNTENA;
    1 D) T1 n  t1 K% M
  87. }& m$ {" Y" q0 E  Y* b- U

  88.   s* X/ s2 W2 S  e
  89. 1 U6 W  v2 ~5 q
  90. // 微秒延时3 s3 n8 v! ?/ }" `
  91. void delay_us(uint16_t uSec)
    9 m# e: {" C6 A
  92. {! J  ^1 _, a8 }9 B( E  N
  93.         if(uSec > 10000) uSec = 10000;
    ; y6 P9 k' V6 ?9 f9 b5 G' A. K

  94. 5 V1 {1 `$ J- [0 T+ I; D
  95.     uint32_t ticks_start, ticks_end, ticks_delay;
    0 d. m: c, ~9 C( P( _3 y, ~: Z+ ]2 R
  96. 9 o% x" y# X7 `% A
  97.     ticks_start = DWT_CYCCNT;
    / n5 a, j- S* D. q. b0 y/ ~/ ^
  98.     ticks_delay = ( uSec * ( SystemCoreClock / (1000000) ) ); // 将微秒数换算成滴答数
    7 ~, j: H7 Z( l4 A9 h
  99.     ticks_end = ticks_start + ticks_delay;
    8 V1 J  f6 G0 h' n% ?/ Y
  100. * B: g: x# W5 g8 G$ W* m
  101.     // ticks_end没有溢出9 W; ?, b# O, z0 }
  102.     if ( ticks_end >= ticks_start )9 }" C, s# c( [" Y, ~+ L
  103.     {/ B" B9 u  P9 F) {4 F
  104.         // DWT_CYCCNT在上述计算的这段时间中没有溢出
    5 h! o' H+ C& P$ h) B
  105.         if(DWT_CYCCNT > ticks_start)
    , c' o* u* H8 o/ P0 D' ^: L
  106.         {
    ! q. {& `$ f0 Y+ E4 ~
  107.             while( DWT_CYCCNT < ticks_end );
    $ }% j$ k# H. h. L& }
  108.         }# H9 j' _2 m. e* c1 V  i8 P7 {
  109.         // DWT_CYCCNT溢出
    ) I1 n9 I- N$ T1 J) R5 o; }
  110.         else0 ?- H* ]$ R- P2 c/ r) w0 _
  111.         {+ Z" S9 u1 u+ w: P3 o
  112.             // 已经超时,直接退出' r  L& \0 k6 J8 \9 p+ @$ b
  113.             return;
    & l' r. Y$ A( Z. z
  114.         }3 t: K/ M8 g$ b' P0 @
  115.     }+ c' O+ e! }& X' z4 H
  116.     else // ticks_end溢出
    ( K, _& P8 u9 R2 g0 D" _
  117.     {
    2 k/ j' z$ Q' a" U% C& _' }
  118.         // DWT_CYCCNT在上述计算的这段时间中没有溢出, ?( n1 h- g: ]9 f* ], x+ P" M
  119.         if(DWT_CYCCNT > ticks_start)& p7 z8 `  q1 H2 G$ }8 l
  120.         {$ J# ~! ~# {$ ?
  121.             // 等待DWT_CYCCNT的值溢出  X  i2 I# L$ d3 L8 G$ I2 `
  122.             while( DWT_CYCCNT > ticks_end );5 a% {6 a4 D& L
  123.         }8 U2 E" @. A) X( C( b. ?
  124.         // 等待溢出后的DWT_CYCCNT到达ticks_end4 X% n, N/ T4 C. z  f, l
  125.         while( DWT_CYCCNT < ticks_end );
    6 F  ^# b& g/ x9 ~8 ]4 P. F; i
  126.     }
    ' _7 O9 h+ k, f5 f# Z
  127. }+ `# }! q4 M- K9 D$ k
  128. - L* d) k7 J/ N2 n5 T
  129. void delay_ms(uint16_t ms)4 I/ s% T/ ?5 }3 X) ^
  130. {) @7 W% A  ^: x) J8 v6 m5 p$ B
  131.                 for(uint16_t i = 0; i < ms; i++)
    ( p: ~6 I+ h6 \2 I" r2 Z
  132.     {
    ; G. M- f4 n' k' O
  133.         // delay 1 ms
    % Y9 }/ |6 d2 w/ q6 B- \
  134.         delay_us(1000);
    ( |% a% h7 N+ z5 L* x4 U4 I- y& h
  135.     }. V% V  q6 h# M9 d5 A. \& T
  136. }0 a# B1 T) b2 n8 }
  137. , g, I) A# v4 b$ ]  r& z# T& z
  138. #elif defined USE_NOP_DELAY1 u' i! `2 j7 a+ H

  139. 5 C3 I" Z" a& y) b# p
  140. void delay_init(void)) |$ [7 [0 P5 J( F9 R3 c7 G
  141. {
      j$ q& f* n. D

  142. - s2 L7 f( M/ _. q5 p0 y
  143. }
    & h9 c* L/ G6 _: G% O  R

  144. 7 s2 V6 p' d% \: O( }" d
  145. void delay_ms(uint16_t ms)( Z5 R8 C, L. s. N5 E. p% Y
  146. {
    4 Z% W" _7 O- l( B- r- w# {8 t
  147. 1 T1 v& W) s# k/ P# V9 ~) P) s/ }
  148. }$ u! `, d. F, z- q; T

  149. - ~" Y2 ^2 h/ O
  150. void delay_us(u32 us)) k8 O7 c" c0 h$ D2 H
  151. {
    ! k# e9 w1 N4 H$ L1 a# m5 p
  152. ! L2 T+ J& N+ l5 }6 W- l0 F
  153. }, x+ H) a' Y, \

  154. 7 V0 w( B. x: n$ E% W( C
  155. #else9 ^" ?* C% Z$ F5 U3 |  ~! T
  156. 8 m% ]' n4 P  P1 C
  157. #endif
复制代码

2 w9 c5 l9 f: |6 V/ t2 p) x特别说明

' m. U# Y) Z/ I; l4 W, m3 ]使用DWT内核的延时函数中,涉及了两次数据溢出处理,第一次数据溢出处理是指计算结束时间(ticks_end = ticks_start + ticks_delay)发生溢出,如下图所示:7 v2 H/ y0 J/ H3 o9 ~

4 x7 d1 r  R% z$ A& @+ `6 t这里有个限制条件就是延时长度(ticks_delay)不能超过uint32_t的最大值,但考虑到这是一个极大的数值,几乎不可能发生,因此可以简化处理限制微秒延时函数的输入参数小于10k,以提高延时精度。对于这个溢出判断就比较明确了:' J. f$ p' ~, M- E: f

7 J6 U. I2 k& {6 ^3 o
  1. if ( ticks_end >= ticks_start )
    4 k* `* Z% ^9 J) W# O
  2. {
    ; w3 s( M! ^0 V  B
  3.         // 计算未溢出
    ) S: {  j8 H# k
  4. }
    ) |1 F! h  |' X4 p, g
  5. else 7 _0 D4 s5 ^- w8 ]
  6. {+ Q9 ^% m9 P6 ~  a5 P2 H
  7.         // 计算溢出& T9 F% {% I# _+ U1 h& Y  P( t
  8. }
复制代码

# y# C4 E0 ^/ B; s- ^" b. ~% M2 I第二个溢出相对比较复杂,处理不当会导致严重问题,尤其是使用了RTOS的系统中有很可能遇到。其核心原因是从标记初始化时间戳ticks_start = DWT_CYCCNT到准备比较时间戳例如while( DWT_CYCCNT < ticks_end );的这段时间的耗时是不确定的。即使没有使用RTOS,如下代码(使用绿色底色标注)在执行时也可能被硬中断暂停,从而导致DWT_CYCCNT时间戳可能已经越过了ticks_end ,或者也发生了溢出重置。$ @( d4 D) {/ m% Z$ \) w
. H! \8 _  Z' m
  1. ticks_start = DWT_CYCCNT;1 H) C) r, Z% J+ Q  \, n
  2. ticks_delay = ( uSec * ( SystemCoreClock / (1000000) ) ); // 将微秒数换算成滴答数
    7 U3 {9 x  S5 g/ O& k1 a
  3. ticks_end = ticks_start + ticks_delay;' k) h* z* d, e7 q9 e

  4. ! _3 G  W" [& J5 F6 ], ^' @
  5. if ( ticks_end >= ticks_start )3 I) }& S: s' `( A  Z- ?! o
  6. {9 Z- O  ?8 r5 u4 D
  7.         // DWT_CYCCNT在上述计算的这段时间中没有溢出) G8 F- b$ U  C$ r( E1 G
  8.         if(DWT_CYCCNT > ticks_start)
    ' U6 ]6 j& R0 h2 x5 N6 E# f3 u8 Q
  9.         {  b' @# [3 Z3 \3 P6 Y9 @* V( ]8 l
  10. 6 m. u# J9 R: {  |, ~$ d
  11.         }
    % {3 M, `! u3 Z7 c% O+ x# f  V% _
  12.         else6 T0 g: B8 V# S. s
  13.         {
    6 H9 N. _: D0 X7 j
  14.         * R% M0 s, m; ?
  15.         }
    # R0 ~( l2 h2 [+ Y
  16. }& u- m/ _2 ^2 y
  17. else # U( w! ?; ?9 H3 x" B
  18. {
    $ m+ N) ]0 b) t6 A# y
  19.         // DWT_CYCCNT在上述计算的这段时间中没有溢出
    + C5 v" K6 f0 n; I8 ]: V$ u
  20.         if(DWT_CYCCNT > ticks_start)
    * V, X& ^! c( w9 S
  21.         {
    : v3 v" s3 g: N/ W

  22. 3 X" d' |2 @+ s$ @
  23.         }3 y% Y/ S0 Z7 }7 z
  24.         else
    / }: |9 j0 D% \5 b. B
  25.         {
    - _' m' k: f$ ^$ ]" `
  26.         2 I" Z2 I2 E6 F" v' H& D2 D$ K
  27.         }
    " j; V* ], R, U' t; A8 @: j! U2 J7 a
  28. }
复制代码

* k0 I" _; b/ H9 f2 H$ I7 Y因此需要根据延时等待操作时DWT_CYCCNT所处位置再进行一次判断:: ~/ T) e0 N  B- m. @) o

4 l1 v, L1 B% m/ c/ @ 7d3cd7bd163a4f0a812be51734efb715.png
& p7 ?/ a* A0 X; G7 }: Q2 i4 U. g8 E8 j6 ~  ]& e/ X2 Y0 I9 A% U) @
ticks_end未溢出时(ticks_end1):8 i. v" h9 u( f+ o9 p+ n1 t
DWT_CYCCNT未溢出(DWT_CYCCNT2 & DWT_CYCCNT3)时:while( DWT_CYCCNT < ticks_end );) K( q! r5 V1 U0 I" r7 |* i
DWT_CYCCNT溢出(DWT_CYCCNT4)时:直接返回。
8 x9 L; l: h0 I$ p+ Rticks_end溢出时(ticks_end2):* G; L$ \# J; |2 n
DWT_CYCCNT未溢出(DWT_CYCCNT2)时:while( DWT_CYCCNT > ticks_end ); while( DWT_CYCCNT < ticks_end );
- _' I9 E4 }. Y" d& V( W6 g( F6 e( n# eDWT_CYCCNT溢出(DWT_CYCCNT4 & DWT_CYCCNT5)时:while( DWT_CYCCNT < ticks_end );
8 O; s$ s5 E, ?3 ?
& u5 a" l8 y+ u1 T, L7 w5 ^ UR7)6S95828HELIL(I$Y$TG.png
! q9 ]! K, p6 c! k4 v; S  p' I; t7 O) o' I5 D% b; V* ~
使用指南$ S0 M9 [% e% g* p4 h' h  e
在程序开始的地方初始化延时函数:delay_init();" Y0 I& M9 V# J
在需要延时的地方调用延时函数:delay_us(10);9 D$ h% {4 B8 d
需要特别说明的是,在FreeRTOS的任务中调用本文介绍的例如void delay_us(uint32_t us)等延时函数,任务会阻塞等待延时结束。如果是调用FreeRTOS提供的延时函数void vTaskDelay( const TickType_t xTicksToDelay ),系统会执行低优先级任务直到延时结束。
3 _- P3 x8 s4 ?1 w/ l8 }1 X' |9 u& [# b" W
8 i  _% @4 |5 g$ H

' q% p* y; ~! q. q
赞 收藏 评论0 发布时间:2022-4-13 11:00

举报

0个回答

所属标签

相似技术帖

官网相关资源

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