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

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

[复制链接]
STMCU小助手 发布时间:2022-4-13 11:00
背景
. Z2 G& p/ H; H' G延时函数在STM32单片机开发的项目中有广泛的应用,微秒级延时在一些时间要求严格的场景下(例如软件模拟I2C通讯)是必不可少的。由于FreeRTOS默认采用了system tick作为时间片分配的时基定时器,可能与利用system tick设计的延时函数出现冲突。再加上FreeRTOS提供的延时函数void vTaskDelay( const TickType_t xTicksToDelay )最小的延时时间等于FreeRTOS的tick时间(一般设置为1ms),因此需要重新设计一套不基于system tick的微秒级延时函数。利用CM3/4内核中的数据观察点与跟踪(DWT)寄存器,可以在不占用硬件外设定时器的情况下实现微秒级的精准延时。1 Q  f3 b) V$ W& D) \1 T. u
5 r) `: _  [" o7 _6 M- P
源码
/ b" r9 K. Y1 t$ i/ J. g/ s通过选择#define USE_SYS_TICK, #define USE_DWT, #define USE_NOP_DELAY其中一个宏定义来决定delay函数的实现方式。需要特别注意的是,由于函数调用是需要时间的,因此使用延时函数时,一般会额外多出一些时间(1微秒左右)。: ?+ p; P2 W# ^1 f9 y" E2 E
& a+ `1 p' S- O
#define USE_SYS_TICK是利用system tick设计的延时函数,在没有使用实时操作系统的时候,它是一个比较适合实现微秒级延时的通用方法。1 R7 v. P1 `, ~5 t) L; i$ x
#define USE_DWT是利用CM3/4内核DWT寄存器设计的延时函数,它具有不占用system tick与外设定时器,延时精度高的特点。% E% \& M( v# I+ u8 E
#define USE_NOP_DELAY是利用了空指令设计的延时函数,它主要实现微秒以下的超短时间延时。6 M( m8 R( k) T. m2 H
头文件( |2 A  ~' i( H  X
  1. #ifndef __DELAY_H__% v( Y+ V. t  q) l' A9 o. }
  2. #define __DELAY_H__                           
    # J  d! Z6 H: c' H2 k
  3. 8 F- B; p' e1 ~1 m! j2 N: y# [; Y
  4. #include "stm32f10x_conf.h"8 H- a- A1 O) Y" r( e! T
  5. #include "stm32f10x.h"
    ' V% E1 c+ B) n3 p8 K
  6. 7 G" K7 t( ]5 o
  7. //#define USE_SYS_TICK
    + ?7 n/ R- i4 ]  m3 K7 I& `& L
  8. #define USE_DWT
    ) {' G$ K2 z$ d5 r6 c: ]) i
  9. //#define USE_NOP_DELAY
    # Y+ j% N. P: c! Q* |7 ?

  10. * j* c$ c9 L4 D: _# H
  11. void delay_init(void);4 I. t& e& a. D( B9 m
  12. void delay_ms(uint16_t ms);5 m) F$ j0 z) c8 K- G, O+ |
  13. void delay_us(u32 us);
    $ }. [5 {/ i$ @) G

  14. 7 O) Z" d% a0 z$ e" [4 b& K) F
  15. /**
    # Q% B: ?4 N: {$ V
  16. * 定义ns级延时,72M主频下,每一条空指令大约14ns- _& J1 J% m( ~% }0 W- Z
  17. */
    - H2 v/ t2 }4 K. C3 q/ p8 G
  18. //#define DELAY_6ns()                                        __NOP()1 y9 j6 k- @& S% b
  19. //#define DELAY_12ns()                                DELAY_6ns(); __NOP()% W  f1 A1 O* P1 n, w
  20. //#define DELAY_18ns()                                DELAY_12ns(); __NOP()
    ; K$ w9 d7 ~2 d3 T
  21. //#define DELAY_24ns()                                DELAY_18ns(); __NOP()
    / t8 p1 S) [9 c/ j
  22. //#define DELAY_30ns()                                DELAY_24ns(); __NOP()( ~- h2 ?# _" q* [7 T
  23. //#define DELAY_60ns()                                DELAY_30ns(); DELAY_30ns(), N! Y2 @# m$ F( X; k. J: k
  24. //#define DELAY_90ns()                                DELAY_60ns(); DELAY_30ns()9 s1 O$ A2 ^( `' W# Z) o2 V
  25. , {9 ?& N# U' h) M  ^# n" C( t7 B+ s1 s5 y
  26. - W' k7 B7 U7 t
  27. #endif: j) p; G7 q# {
  28. % ^6 B. ~; R  |6 K9 a! x
复制代码

, J1 A# |' f0 A源文件& j: Z. t8 B9 ?; {; N# g9 a/ s
  1. #include "delay.h"! J  s, k2 b5 W1 R, V+ t6 i( [
  2. 9 ?8 Y; ~. i0 A1 p. R2 n
  3. #ifdef USE_SYS_TICK2 h' `' _1 o1 U  s% c- g
  4. & L6 z' H, @" a' e9 y  ]( q- K
  5. static uint8_t  fac_us = 0;                          //us延时倍乘数
    ) F8 z1 ]# g; A
  6. static uint16_t fac_ms = 0;                          //ms延时倍乘数,在ucos下,代表每个节拍的ms数
    ' H( o% m9 Y* ~4 T# _# e( K, C
  7. - b( g; O5 v7 k7 L# o+ O
  8. //初始化延迟函数
    ( R  ~& |4 s. X/ Z& [
  9. //当使用OS的时候,此函数会初始化OS的时钟节拍0 R, k$ P0 l# ~( o3 x
  10. //SYSTICK的时钟固定为HCLK时钟的1/8
    1 R) g+ V' z7 D7 a$ P2 U
  11. //SYSCLK:系统时钟7 v0 i* @9 Z7 p" r: v
  12. void delay_init(); |! e3 y- P/ y
  13. {8 y7 s3 f" u" `/ P4 w4 K
  14.         SysTick_CLKSourceConfig ( SysTick_CLKSource_HCLK_Div8 ); //选择外部时钟  HCLK/85 l2 J/ Q( U+ |2 w
  15.         fac_us = SystemCoreClock / 8000000;         //为系统时钟的1/89 ~" d2 V8 F4 D  V& w6 e: ^
  16.         fac_ms = ( uint16_t ) fac_us * 1000;             //非OS下,代表每个ms需要的systick时钟数
    % l, U. a; k3 r
  17. }+ B0 y* q: h5 w
  18. + l3 ~7 ]' K. J+ R. I
  19. //延时nus
    . Z2 p! b! N% l- S" @% Q
  20. //nus为要延时的us数.
    $ o# k! G2 z, p: h( [* u. O6 }
  21. void delay_us ( u32 us ), F/ P# B6 W; ]0 ]- ?( P  F
  22. {: f' _! r  I7 K
  23.         u32 temp;
    $ L, r- {7 w) I7 K
  24.         SysTick->LOAD = us * fac_us;               //时间加载+ j+ B2 |5 T, Q8 n
  25.         SysTick->VAL = 0x00;                        //清空计数器
    + p1 R+ f: m  I) N& W
  26.         SysTick->CTRL |= SysTick_CTRL_ENABLE_Msk ;  //开始倒数
    5 A. ~- H; U6 ~) ]: D, ?/ S- @
  27. 3 z; I# @/ ]# u! h( B
  28.         do- O! s9 F. n+ u  R
  29.         {1 j) l- t) @- j( [% }1 ^' i  @" v. W
  30.                 temp = SysTick->CTRL;
    : F) k: G% |3 \* v7 S
  31.         }
      i8 H% L% l% X: l* p8 ?
  32.         while ( ( temp & 0x01 ) && ! ( temp & ( 1 << 16 ) ) ); //等待时间到达( S4 u- P1 w5 O: `5 l) a
  33. 6 ]) c! d' k) j( m% j
  34.         SysTick->CTRL &= ~SysTick_CTRL_ENABLE_Msk;  //关闭计数器0 \* U5 N: ?2 }$ K
  35.         SysTick->VAL = 0X00;                         //清空计数器9 A2 Q% w" {# s0 q" D$ _* F
  36. }
    # V# \* j2 u6 p6 I  D3 _
  37. //延时nms
    # c5 X3 n! v) v) @4 d: I+ o
  38. //注意nms的范围; \+ j  Y2 V/ @; J9 M: w
  39. //SysTick->LOAD为24位寄存器,所以,最大延时为:
    4 w2 s* _" ]' F" S
  40. //nms<=0xffffff*8*1000/SYSCLK
    - C. b5 W: ?& d+ N3 J
  41. //SYSCLK单位为Hz,nms单位为ms+ V: n% l$ r7 k! k9 P7 S
  42. //对72M条件下,nms<=1864
    + i) S6 I  Q2 ]; V% y! f+ ?7 y* D
  43. void delay_ms ( uint16_t ms )& G4 O' m3 A1 z0 g# A5 T
  44. {
    ' h3 q. b+ L; ?* l
  45.         u32 temp;
    5 o4 ~( [$ `, p
  46.         SysTick->LOAD = ( u32 ) ms * fac_ms;       //时间加载(SysTick->LOAD为24bit)7 L3 x, y4 [& A0 i+ N
  47.         SysTick->VAL = 0x00;                        //清空计数器. f4 v! b7 j9 W$ n4 B  k, b9 v
  48.         SysTick->CTRL |= SysTick_CTRL_ENABLE_Msk ;  //开始倒数8 c' T( N9 Y$ M5 v. Z% w3 ?" C

  49. 5 x$ e+ h. Y6 P9 X% }
  50.         do* f/ E" H, I& F
  51.         {
    , {% d6 ^. {3 |9 g6 N' ]* ^" w% x
  52.                 temp = SysTick->CTRL;
    1 p: J3 {, {, Q$ [
  53.         }0 n. b. [0 x) k
  54.         while ( ( temp & 0x01 ) && ! ( temp & ( 1 << 16 ) ) ); //等待时间到达2 b1 p/ e6 T  X( f8 R
  55. % f* r0 e0 U0 h+ p7 s
  56.         SysTick->CTRL &= ~SysTick_CTRL_ENABLE_Msk;  //关闭计数器
    3 s( I" R% R; b; A1 D( Z
  57.         SysTick->VAL = 0X00;                        //清空计数器4 v; x7 b+ l! v& K- ?0 U
  58. }! N" m' P/ N- h

  59. 0 C3 l7 Q' U9 e0 {8 j2 Z
  60. #elif defined USE_DWT
    1 q4 B7 p" _2 Y) d  m

  61. 8 [$ W8 z7 z4 O" |$ E2 r
  62. // 0xE000EDFC DEMCR RW Debug Exception and Monitor Control Register.
    4 p5 d, A* F% H$ S; f8 q
  63. #define DEMCR           ( *(__IO uint32_t *)0xE000EDFC )7 s0 h) J3 {2 F) n- L
  64. #define TRCENA          ( 0x01 << 24) // DEMCR的DWT使能位/ I; i' b9 X, b# V: w! K
  65. $ B5 D9 @$ m, U, A4 P6 X1 j" X, g
  66. #define  DBGMCU_CR   *(__IO uint32_t *)0xE0042004                        //MCU调试模块控制寄存器,详细内容参考《stm32中文参考手册》调试支持(DBG)章节,747页
    5 [0 e) }# a1 D

  67. 9 V. E  |/ w& |
  68. // 0xE0001000 DWT_CTRL RW The Debug Watchpoint and Trace (DWT) unit
    2 ?: `6 a! J, K) L' T1 @  h
  69. #define DWT_CTRL        ( *(__IO uint32_t *)0xE0001000 )
    4 p# p* {1 q9 h) g  P
  70. #define DWT_CTRL_CYCCNTENA       ( 0x01 << 0 ) // DWT的SYCCNT使能位' z! G7 }8 H$ i

  71. % T" Z1 B& ?3 V+ [9 @( m8 n' k
  72. // 0xE0001004 DWT_CYCCNT RW Cycle Count register,6 P1 J- @" L' x  f" S: ?9 a
  73. #define DWT_CYCCNT      ( *(__IO uint32_t *)0xE0001004) // 显示或设置处理器的周期计数值
    9 Z: l3 ]: v5 k3 Z" V0 \% T

  74. . V* s5 m7 I: K9 t" e' i
  75. //#define DWT_DELAY_mS(mSec)    DWT_DELAY_uS(mSec*1000)
    . H6 a  w/ Y# I- v" A, M' I0 F3 _$ A

  76. - P4 v% B; H2 q2 [+ [' {
  77. void delay_init(). [& h1 @$ J5 Q, a
  78. {9 V/ e' V3 Y. u3 Q
  79.         //使能DWT外设
    % W) i: X: r! f2 i% T. M) N
  80.         DEMCR |= (uint32_t)TRCENA;$ R' e  ]% ~! S' \

  81. ; _/ i1 h1 }1 W! Z) f$ F$ U
  82.         //DWT CYCCNT寄存器计数清0
    2 W. D  v! U" ~5 `3 c
  83.         DWT_CYCCNT = (uint32_t)0u;, ]1 K$ ~# k: c1 w* F5 u1 b; ]% }  N
  84. - w* I5 F- R6 }, f! w* j
  85.         //使能Cortex-M3 DWT CYCCNT寄存器: l; P# @6 {9 s
  86.         DWT_CTRL |= (uint32_t)DWT_CTRL_CYCCNTENA;+ E2 \  Y. ]) a, S% j5 F
  87. }; q8 [9 X# U* z9 H7 _
  88. * o: l( l' ]6 @# [
  89. 8 S$ S" W( X+ ?9 A5 W" r
  90. // 微秒延时# H' S0 S- q9 Q4 d+ L5 N' n! \
  91. void delay_us(uint16_t uSec)5 [+ U6 N3 \1 |0 u
  92. {
    ; j/ E; i; {8 B) V7 A* j# p8 {4 d
  93.         if(uSec > 10000) uSec = 10000;( Z7 @8 M: |+ a1 i" G
  94. . {" k, g- l6 X  ?5 ~% t' O3 H
  95.     uint32_t ticks_start, ticks_end, ticks_delay;
    7 m; r$ j: p" _( G. Q
  96. 4 Q! E0 P  M5 b$ K8 A. z" y! E
  97.     ticks_start = DWT_CYCCNT;9 O& Y: w: L/ m/ @
  98.     ticks_delay = ( uSec * ( SystemCoreClock / (1000000) ) ); // 将微秒数换算成滴答数
    + d, t( p0 c: M
  99.     ticks_end = ticks_start + ticks_delay;. U6 [3 T/ J& ]' P. x, x6 n

  100. " n. N( n! M. V3 w; M  b  Z
  101.     // ticks_end没有溢出8 J9 R- a: R" h: x4 ]- g7 ]7 c6 `
  102.     if ( ticks_end >= ticks_start )
    - `8 _2 C( V, s
  103.     {8 _0 E7 n+ ~3 o6 p
  104.         // DWT_CYCCNT在上述计算的这段时间中没有溢出' f1 ^; w% D, N
  105.         if(DWT_CYCCNT > ticks_start)0 n' t6 W- j, T5 |+ R0 z2 D
  106.         {
    ' b/ ]( K; M) U  M- M% I. [; O, {
  107.             while( DWT_CYCCNT < ticks_end );
    3 \. Z9 }5 P, J' w  s  A' S
  108.         }
      b0 r7 D. j1 n" v4 q9 Y3 i9 M7 h
  109.         // DWT_CYCCNT溢出
    9 q4 c* Q( z( ^+ S4 `+ F) W
  110.         else! `% \6 U$ N. x$ P6 [# ~, V
  111.         {
    1 y& e& j( V0 F' I
  112.             // 已经超时,直接退出
    ( v, V: b/ b9 e# ^, A) ?
  113.             return;, |5 }& G1 q. I4 G. o+ L2 r
  114.         }
    . Q8 D4 W9 Q3 }8 w( @  M4 O
  115.     }& f+ Q! v" z) ~2 Q
  116.     else // ticks_end溢出
    9 }3 C3 b# Z" q  R
  117.     {, u+ w, g  V8 D" M" Q  M5 h, k
  118.         // DWT_CYCCNT在上述计算的这段时间中没有溢出
    0 u$ J- D6 T' h4 J- J
  119.         if(DWT_CYCCNT > ticks_start)
    - P' M; K+ t# |! v7 G- L( C
  120.         {' w  {  p/ c' T9 y, {
  121.             // 等待DWT_CYCCNT的值溢出* _. R8 V- y7 M/ ]7 T
  122.             while( DWT_CYCCNT > ticks_end );! \1 S- n$ Y6 S$ {
  123.         }
    9 N8 d8 e2 o5 X
  124.         // 等待溢出后的DWT_CYCCNT到达ticks_end
    # Z6 k0 V1 q6 j( S
  125.         while( DWT_CYCCNT < ticks_end );
    # V" N% R8 N0 g  ?5 b
  126.     }
    : B9 i/ L) G, U" g2 u3 M
  127. }9 ?' v3 y1 n( p- R7 ?
  128. - p4 w4 y; k/ t$ T* Y) Y' f- r. d& [
  129. void delay_ms(uint16_t ms)9 o: {; O* Z+ O' F0 p" i: o. }0 ?
  130. {! `9 m  [1 t% `( L/ {. `' H' G
  131.                 for(uint16_t i = 0; i < ms; i++)
    3 p- R) d0 O% Z0 ^; C
  132.     {2 i  A2 K. f" H. _, s+ v9 J, E; X
  133.         // delay 1 ms
    / K2 p( M/ O9 h3 e
  134.         delay_us(1000);
    # o' x& @5 J, u
  135.     }
    ) o- P: H% l- N2 r# O
  136. }
    " N' n0 C* A5 @6 S2 G+ x. _7 \( {
  137. 6 q1 k8 m$ P6 h2 J% h
  138. #elif defined USE_NOP_DELAY
    & Y+ l. c6 |& r3 \- M8 X" I
  139. " Z6 N# A# d5 O
  140. void delay_init(void)% e0 r* {0 }0 _; J! t
  141. {
    0 j" l2 A- a" R: ]2 R
  142. , A# b* Z  Q  u, O7 C: I' K
  143. }. w, j8 D( _& F- y% U1 x1 |

  144. ' ^3 O' P$ ?- ~+ ?% {- E+ v6 j% f
  145. void delay_ms(uint16_t ms)
    ( K0 Z7 k( f3 L& N
  146. {5 ^$ C: _: }/ o4 f0 k' E+ G

  147. 6 s; M7 H+ T. C% u$ n+ r: y# c
  148. }
    * T- J: P$ I+ L1 y6 r

  149. " W, U3 I4 E0 B$ @: w
  150. void delay_us(u32 us)3 q1 c0 I& U; [
  151. {+ U4 b# h* I8 t2 T1 V. g
  152. 2 m) {1 V  w" L- z% S( t& v9 M
  153. }
    8 @  C: u2 M" M" C
  154. 7 Z, g( U* _! H2 k8 K- @& O( [- p8 v
  155. #else
    8 ~& G/ S* ~  D9 X' [

  156.   Q: J: e5 o  q1 n' j  a! M: }
  157. #endif
复制代码

( b3 |$ t* A( d2 e2 T特别说明
9 ~$ A2 e$ H! ^5 C9 ^( D' \) x
使用DWT内核的延时函数中,涉及了两次数据溢出处理,第一次数据溢出处理是指计算结束时间(ticks_end = ticks_start + ticks_delay)发生溢出,如下图所示:
6 E: l# P% _( A5 u3 n) c/ O1 @( A# E+ v3 B* A
这里有个限制条件就是延时长度(ticks_delay)不能超过uint32_t的最大值,但考虑到这是一个极大的数值,几乎不可能发生,因此可以简化处理限制微秒延时函数的输入参数小于10k,以提高延时精度。对于这个溢出判断就比较明确了:
9 _" @, [* O9 w- F
$ o% P8 b: H2 ]( ^5 S! }
  1. if ( ticks_end >= ticks_start )% y) K/ W: ?. B5 g" y1 W# x$ V" j
  2. {2 M% `' r+ ~% l- k% L
  3.         // 计算未溢出3 D, X9 o. m7 e: C
  4. }5 O: S. j" K; L, e. C
  5. else
    2 Z3 x# {3 a" O. j( V4 K
  6. {) p% D5 D1 @9 @* N
  7.         // 计算溢出& \6 r1 F8 l* |2 `# J7 T# N
  8. }
复制代码

$ q' }% q9 N- c5 O, g5 i" @第二个溢出相对比较复杂,处理不当会导致严重问题,尤其是使用了RTOS的系统中有很可能遇到。其核心原因是从标记初始化时间戳ticks_start = DWT_CYCCNT到准备比较时间戳例如while( DWT_CYCCNT < ticks_end );的这段时间的耗时是不确定的。即使没有使用RTOS,如下代码(使用绿色底色标注)在执行时也可能被硬中断暂停,从而导致DWT_CYCCNT时间戳可能已经越过了ticks_end ,或者也发生了溢出重置。
3 ]; f" r' g! T1 G" W" n! N* i% k# k6 X7 @% k- c3 c: Q
  1. ticks_start = DWT_CYCCNT;# t$ |3 D3 m  ~5 o; W5 q
  2. ticks_delay = ( uSec * ( SystemCoreClock / (1000000) ) ); // 将微秒数换算成滴答数" X- i, Q1 S2 c+ [
  3. ticks_end = ticks_start + ticks_delay;" s- \1 ]1 {' r0 y
  4. 0 f' T' h! @& P# K
  5. if ( ticks_end >= ticks_start )4 M. H! y9 p1 E" O% ]
  6. {
    & y: E: _2 ?. C9 r
  7.         // DWT_CYCCNT在上述计算的这段时间中没有溢出
    + {+ y. `* y, i. _( E+ D. [+ j
  8.         if(DWT_CYCCNT > ticks_start); ?. ^8 j* F0 Q  F& i: H! m
  9.         {
    . X/ c/ @2 [9 C% _! r

  10. 7 k/ ^! Y2 ?) O' p( v2 H5 j+ P
  11.         }0 b' |# ]/ h% ?6 y
  12.         else
    * z3 y1 e6 V) \
  13.         {
    / m1 J- O) R9 _& m+ b( J* T
  14.         
    * v+ z- L: I+ k" W
  15.         }
    * A' ]3 ]. C3 |0 D# L" d
  16. }+ v8 \( B& q+ ^8 }" W  \
  17. else ( T, u. ]/ i7 A4 K( v# H) z/ M
  18. {! t: k3 d( K6 G% G; s
  19.         // DWT_CYCCNT在上述计算的这段时间中没有溢出
    6 Z. }1 G; B" D' w& f+ q4 N
  20.         if(DWT_CYCCNT > ticks_start)/ w+ V& |( ^- l
  21.         {
    1 L6 `0 F0 D; Z* t5 f# v

  22. : R3 F3 T+ {7 K6 F: z  {
  23.         }
    - \+ ~$ J4 G, k3 ?
  24.         else
    % ~0 Y4 c# s  p% H6 S
  25.         {0 r* v; O6 r/ D5 q' N4 D0 u$ x3 n/ g
  26.         ( }  e) s0 z, T3 L  ^
  27.         }
    " A7 }* L% S' b9 P
  28. }
复制代码
$ b0 ^% n" q% O. w0 I9 |
因此需要根据延时等待操作时DWT_CYCCNT所处位置再进行一次判断:
0 X  N# E& H; q9 z0 {6 J3 h/ k* m
/ ]" l+ j0 v; ]8 p6 K 7d3cd7bd163a4f0a812be51734efb715.png ! Z; L5 n. G" j! i" p
3 ?) l) A/ Y+ d; o1 f
ticks_end未溢出时(ticks_end1):" w$ {5 s/ w. r+ ~2 ]
DWT_CYCCNT未溢出(DWT_CYCCNT2 & DWT_CYCCNT3)时:while( DWT_CYCCNT < ticks_end );
4 v9 p  I- p8 Q/ T, O* F. J1 e2 b' B- kDWT_CYCCNT溢出(DWT_CYCCNT4)时:直接返回。5 r7 H) n) ~: z" n8 F7 P- O' `
ticks_end溢出时(ticks_end2):* Y. e2 W7 ]3 j4 b- m7 h
DWT_CYCCNT未溢出(DWT_CYCCNT2)时:while( DWT_CYCCNT > ticks_end ); while( DWT_CYCCNT < ticks_end );
; z( N0 W0 l2 B6 {DWT_CYCCNT溢出(DWT_CYCCNT4 & DWT_CYCCNT5)时:while( DWT_CYCCNT < ticks_end );( _. U1 U! C0 ~: r' Z
/ T& K4 I8 V" i+ K' j. y
UR7)6S95828HELIL(I$Y$TG.png
/ n/ }; _% X. H3 p
) L  H" X. x/ }( V/ X% p使用指南
' h# g, t* D9 i# ]5 P在程序开始的地方初始化延时函数:delay_init();- o5 r% X# `  d! @! f4 S+ o
在需要延时的地方调用延时函数:delay_us(10);' r/ d' u4 ?0 e3 }  f9 [
需要特别说明的是,在FreeRTOS的任务中调用本文介绍的例如void delay_us(uint32_t us)等延时函数,任务会阻塞等待延时结束。如果是调用FreeRTOS提供的延时函数void vTaskDelay( const TickType_t xTicksToDelay ),系统会执行低优先级任务直到延时结束。; M: V0 c4 X' e, X; j9 I, ~

1 R5 [! }2 @9 \) j8 w- v  v. L1 J- S* j

, q" q3 S' o4 p" V% Q: ?- q
收藏 评论0 发布时间:2022-4-13 11:00

举报

0个回答

所属标签

相似分享

官网相关资源

关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
STM32N6 AI生态系统
STM32MCU,MPU高性能GUI
ST ACEPACK电源模块
意法半导体生物传感器
STM32Cube扩展软件包
关注我们
st-img 微信公众号
st-img 手机版