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

基于STM32串口的经验总结

[复制链接]
攻城狮Melo 发布时间:2023-3-18 12:11
在日常中,串口经常作为和上位机通信的接口,进行打印信息方便调试程序,有时也会作为模块的驱动接口,所以总结一下串口的几种使用方法对以后的开发还是很有帮助的。6 U" }1 s% K  k3 y% C
3 t1 M5 F. T- Z2 b
一.仅向上位机打印调试信息8 W2 P* N( i% q+ N: R4 j; E7 t
单纯利用串口向上位机打印调试信息,程序如下:
/ Z  G1 G8 D$ T" y  `
  1. void USART1_Init( uint32_t btl )' c0 Q4 @1 w) h! |1 [. f0 N+ k) c
  2. {  J3 c- |! `' ^" A) u9 n
  3.         GPIO_InitTypeDef  GPIO_InitStruct;
    2 |* l2 ^: e. l, O# r  P3 q
  4.         USART_InitTypeDef USART_InitStruct;
    $ v% S8 ~: }2 l
  5.        
    7 N4 E7 f9 j, l% a
  6.         RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOA|RCC_APB2Periph_USART1, ENABLE );
    % G" U- N$ ^5 H, `4 W* B+ n
  7.         7 _4 B1 G+ A8 p, v- L, m/ N& l
  8.         GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_PP;5 u. T1 u$ Z1 @! h* ^
  9.         GPIO_InitStruct.GPIO_Pin = GPIO_Pin_9;//Tx: W  G$ w8 g/ i& r; _( Z2 {
  10.         GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;! O% S* @1 w. ^+ @  H4 [
  11.         GPIO_Init( GPIOA, &GPIO_InitStruct );+ ~/ @7 c2 O( F  a" j3 y9 N9 V
  12.         GPIO_InitStruct.GPIO_Pin = GPIO_Pin_10;//Rx
    . z% S) x! n$ I
  13.         GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IN_FLOATING;
    , v7 p7 J9 t2 {/ c" A
  14.         GPIO_Init( GPIOA, &GPIO_InitStruct );/ x4 J5 W. a, e+ J+ E! K$ K$ V
  15.        
    6 s4 e& O9 p! W# U: d+ r
  16.         USART_InitStruct.USART_BaudRate = btl;" Q' K) H) {3 O# w1 i! l$ ]0 s
  17.         USART_InitStruct.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
    $ d6 b, X; e: I
  18.         USART_InitStruct.USART_Mode = USART_Mode_Tx;2 Q: Z2 d8 t/ e: p/ D9 o$ h4 i
  19.         USART_InitStruct.USART_Parity = USART_Parity_No;
    & \. g1 J2 ]; C7 C$ T, \
  20.         USART_InitStruct.USART_StopBits = USART_StopBits_1;
    7 A, Y5 `& [+ ~4 h/ v* w
  21.         USART_InitStruct.USART_WordLength = USART_WordLength_8b;
    4 O, w5 D6 O  |# s- f
  22.         USART_Init( USART1, &USART_InitStruct );% a2 }; P+ ^) W. O. s5 g
  23.        
    1 b  _! g5 q. s! G
  24.         USART_Cmd( USART1, ENABLE );
    9 w' A& b  G$ c$ l/ C
  25.         + a" E! ^3 `, Q1 a- r
  26. }/ h' a' f8 C2 i, j4 S/ d, W2 T* T

  27. + K1 r& o' M) {1 {. K; r2 P0 s
  28. //串口重定向,直接利用printf函数输出调试信息
    4 N. P9 }  p. ^' U4 }
  29. int fputc( int ch, FILE *f )
    4 P; Y# J7 e' U4 O* {9 ^  u( T
  30. {
    1 m6 |) E+ G7 f5 i3 J& a' u, i
  31.         USART_SendData( USART1, ( uint8_t )ch );
    6 K  ~: ?: w2 t4 v0 f
  32.         while( USART_GetFlagStatus( USART1, USART_FLAG_TXE )!=SET );% @2 t; D8 L& P" {& Q* L
  33.         return ch;, m) B4 m5 b# s% U4 J- _
  34. }
      `* c) A0 i0 r- l0 l

  35. : v- X: G8 V  C5 z

  36. & ^* r. A, g* [/ ?
复制代码
; Q1 ~0 A8 p- i8 J( `
记得包含头文件,勾选Use MicroLIB,以使用printf等函数
) l/ R2 N8 @/ \) S
' M( Q: L1 d8 x% d
2020042220264754.png / S' x2 L. A# T

1 B0 s# l) D* O* u& \2 d' P

+ }+ P- Q) \  _; {. S' {二.与上位机交互信息8 b" I3 y' m, X% j' K" x/ e
相比于上面的单向通信,有时候需要从上位机接收信息,然后进行反馈,这个时候就使用到串口的中断了。
9 W/ K& o7 q. C: y2 Y  }上位机向单片机发送字符串,接收后再发送给上位机:# }/ O4 @( ?$ h) y# A
  1. 9 A( [9 W; q% N$ t% D, w# g8 K! @& C
  2. void USART1_Init( uint32_t btl )7 N( [5 c; v' p8 F) O& N2 }5 F% K
  3. {3 G$ |4 O: Y! O7 Y# G' V9 |
  4.         /* 结构体声明 */. B' J9 n$ s& Y& L
  5.         GPIO_InitTypeDef  GPIO_InitStruct;9 q. Y+ W) r) o5 T( D- ^: S+ ^
  6.         USART_InitTypeDef USART_InitStruct;+ s0 a# t- Y6 ~
  7.         NVIC_InitTypeDef NVIC_InitStruct;, B+ R7 K/ y8 D( ?5 m
  8.         , ~' F' F8 X* O
  9.         /* 打开时钟 */. V( x; C  d' _. N" a  F9 `. G
  10.         RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOA, ENABLE );' C- b7 @' H- e
  11.         RCC_APB2PeriphClockCmd( RCC_APB2Periph_USART1, ENABLE );
    . F' p7 R1 k, O9 k: K6 w% }
  12.         7 X, d  F" s; d! W5 Y
  13.         /* GPIO配置 */; S; U8 |1 b6 k1 R( H" ]4 H
  14.         GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_PP;" Q0 z" {+ K1 e  P3 E1 H
  15.         GPIO_InitStruct.GPIO_Pin = GPIO_Pin_9;//Tx1 L) \) Z/ Q" N0 j
  16.         GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;3 ~: Y7 o, {% f9 J6 L
  17.         GPIO_Init( GPIOA, &GPIO_InitStruct );0 ~+ g* J# z/ n  ^& H
  18.         GPIO_InitStruct.GPIO_Pin = GPIO_Pin_10;//Rx; D# e) }0 g# ~/ s5 Q" O
  19.         GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IN_FLOATING;
      a/ ^( S0 q  N7 j1 M; f
  20.         GPIO_Init( GPIOA, &GPIO_InitStruct );0 Y* o1 h: k! L2 H
  21.        
    : R! K& t# c! }6 X7 G6 o
  22.         /* 串口配置 */
    , c) @) h2 L. W0 V% m
  23.         USART_InitStruct.USART_BaudRate = btl;; ?9 _. F: I* t# U4 |
  24.         USART_InitStruct.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
    " q7 p+ ~) h/ ~4 I1 s) P
  25.         USART_InitStruct.USART_Mode = USART_Mode_Tx|USART_Mode_Rx;//收发模式
    4 p4 i* q9 ?# w  M# ^' h% d7 `
  26.         USART_InitStruct.USART_Parity = USART_Parity_No;" d9 |6 x  d% P: g% I& w
  27.         USART_InitStruct.USART_StopBits = USART_StopBits_1;
    % |' Q6 M* s  n+ E, D- D# b6 m) X
  28.         USART_InitStruct.USART_WordLength = USART_WordLength_8b;
    2 r% _) N3 z, S! }. M$ I0 H7 K
  29.         USART_Init( USART1, &USART_InitStruct );
    6 ~0 [1 Z" q4 B
  30.        
    & g  x2 c3 B; i* m0 Z9 Q
  31.         /* 中断配置 */  ?/ L% v. v  P& z+ G
  32.         NVIC_InitStruct.NVIC_IRQChannel = USART1_IRQn;5 |' f1 s. k$ \% t+ D1 d1 V) G( w
  33.         NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority=3 ;. J! O3 q; l3 r) C0 Y$ k  I& _; B
  34.         NVIC_InitStruct.NVIC_IRQChannelSubPriority = 3;                5 \# p. n- ?/ p) y
  35.         NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;                        4 n9 w7 x' Y5 N5 H$ Y( |1 B
  36.         NVIC_Init(&NVIC_InitStruct);        / V) U9 g0 N# J+ d
  37.         USART_ITConfig( USART1, USART_IT_RXNE, ENABLE );//接收寄存器非空触发中断
    , N! C  c$ j) i8 J" H' U; z# y
  38.        
    , P, x  Y5 }, `# u. U/ ^( ?. e; Q
  39.         /* 使能串口 */# F1 M# R$ H# l
  40.         USART_Cmd( USART1, ENABLE );: ?3 X3 }. u. f- m' k, @9 c. v0 E
  41.         0 Y9 Z( }% N' g) `8 t( }! [! W3 _
  42. }
    & |3 K" ], Z4 R

  43. : x4 [- ^  g- a+ ~9 L* N" U

  44. ! D7 T. y. Y# n& S# A9 a) p
  45. volatile uint8_t n=0;) W( X9 B+ i: Z4 s+ c
  46. //接收缓冲区- u: K. I( ]0 P8 o. ]
  47. uint8_t USART1_Rx_Buf[100];% ~2 z$ \  B. K/ Q& X8 {. ?, @0 J6 J
  48. void USART1_IRQHandler( void )
    4 S& ]! Q- u# ~7 u7 ?
  49. {
    # e2 A$ o+ y6 ^
  50.         if( USART_GetITStatus( USART1, USART_IT_RXNE )==SET )
    0 W: C7 T) P$ N- v) j
  51.         {/ D9 v1 v% g4 D' E
  52.                         USART1_Rx_Buf[n]=USART_ReceiveData( USART1 );
    * Q  e9 O: c1 F  k; {! P/ a
  53.                         n++;
    & T' t3 U5 ~9 ^: ^) k
  54.         }        7 T& }- ]: \2 Z5 J! |
  55.         USART_ClearFlag( USART1, USART_IT_RXNE );+ j) K+ S7 Y8 u  n& R- q' V
  56. }; ]  ~: M5 f8 b7 q$ p: _& d
复制代码
$ [$ U) n. A$ {, Q
每从上位机中接收一字节的数据,都将数据存储在串口的接收缓冲区USART1_Rx_Buf[100]中。4 Y  n8 `6 N6 |( G  Z- s* A' b

( V* k) _) w2 G, a0 Y6 J  _# G

5 U$ t) ?) }0 F三.作为驱动接口+ M0 C3 l3 ]* x" K) W
一些模块的驱动接口就是串口,这个时候就需要单片机从模块中读取指定格式的数据,比如GPS模块,将定位信息从串口发出,单片机解析串口数据,显示在上位机中。
& g/ S3 C  G, ^' M3 c8 D- A  husart3用来与GPS模块通信,从GPS模块接收数据,认为10ms内的数据属于一次数据,所以就需要定时器来控制时间。
; B# j, d. _  E+ ?6 L$ Musart3:* X4 K) H9 Q3 g2 [1 O
  1. //串口1中断服务程序/ l1 T7 {7 K; ]1 _7 X, @! A: d
  2. //注意,读取USARTx->SR能避免莫名其妙的错误           : I, z9 \/ [. e/ a, [# C
  3. u8 USART_RX_BUF[USART_REC_LEN];     //接收缓冲,最大USART_REC_LEN个字节.. n8 w9 K, m/ y" m: P8 C3 E
  4. //接收状态: A& S; G3 t6 M3 C. T! \
  5. //bit15,        接收完成标志
      W3 ]* S7 X! |) j
  6. //bit14,        接收到0x0d
    0 t1 {2 _7 Y! r* n+ _. i0 S
  7. //bit13~0,        接收到的有效字节数目- c; _& _1 r8 c
  8. u16 USART_RX_STA=0;       //接收状态标记          
    . S" s: D, [6 F2 s/ h$ f
  9.   
    - x6 H* M, d* \+ E# k
  10. void uart_init(u32 bound){9 c" W) o  f8 X+ C
  11.   //GPIO端口设置5 c4 e! q! e+ l+ k6 ]2 ^; r) H' ~
  12.   GPIO_InitTypeDef GPIO_InitStructure;2 N' N1 _0 b* k$ @; _& D
  13.         USART_InitTypeDef USART_InitStructure;
    / p1 x6 {, \, x0 n9 W6 H
  14.         NVIC_InitTypeDef NVIC_InitStructure;7 s. t8 r/ q! Q8 f$ B* L. n+ |6 L
  15.        
    0 X, w- B; l# g  _. C+ Y: U
  16.         RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1|RCC_APB2Periph_GPIOA, ENABLE);        //使能USART1,GPIOA时钟! L! ]; [! L% s1 L  q
  17.   4 C: ^6 S! @" f3 Y5 P/ W* S
  18.         //USART1_TX   GPIOA.9# a& m8 I$ N  ~' n' \2 E; e
  19.   GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9; //PA.9' M8 S/ m: _! _! w
  20.   GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;9 [& o% l6 _; N
  21.   GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;        //复用推挽输出
    ; m( `( B- h, T) |3 L+ r0 T1 E8 c7 _$ q
  22.   GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化GPIOA.9; L  G! n) ^4 S# C
  23.    . _. K, h) C# i1 Y% ~( k4 ?
  24.   //USART1_RX          GPIOA.10初始化3 ~: \) T$ z8 k0 T4 u; \( f/ H
  25.   GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;//PA10/ v) E2 G1 b% {
  26.   GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;//浮空输入! Z; y; z0 @" Y" T( N' c8 j: u
  27.   GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化GPIOA.10  " a/ [5 J. v# Z) J

  28. & A2 |$ W% Z$ x7 D, L' t4 w
  29.   //Usart1 NVIC 配置
    % H2 v6 y- t3 t* I
  30.   NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
    ! y5 x' y9 }& N7 y
  31.         NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=3 ;//抢占优先级3( h& _6 h) e' X/ P7 I
  32.         NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3;                //子优先级3
    / G+ U: g" n" E
  33.         NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;                        //IRQ通道使能, e) w. ~8 a6 q. o5 k" f' C" k5 H
  34.         NVIC_Init(&NVIC_InitStructure);        //根据指定的参数初始化VIC寄存器
    ! I" s% L/ N7 m$ R9 R1 ?( K
  35.   
    4 O; d  M8 ]% ]9 }6 T, B
  36.    //USART 初始化设置
    % R* b' M/ m, ?1 b/ }2 N2 U4 X
  37. - n6 P6 H' v6 V* m7 n  y9 o. I8 ~9 D, {
  38.         USART_InitStructure.USART_BaudRate = bound;//串口波特率
    ' R# O6 k' |& i, e. y0 f& {9 h4 Y
  39.         USART_InitStructure.USART_WordLength = USART_WordLength_8b;//字长为8位数据格式0 m5 W  Y) I7 D# b. X  C
  40.         USART_InitStructure.USART_StopBits = USART_StopBits_1;//一个停止位
    - J# W, s" j" D& G8 F; e6 Z4 R
  41.         USART_InitStructure.USART_Parity = USART_Parity_No;//无奇偶校验位( t  F% B( i8 L  H, }
  42.         USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;//无硬件数据流控制' |. A- F4 |  X
  43.         USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;        //收发模式
    9 j) y0 d. l) O+ S" O( w4 h

  44. * \$ w* T: i+ z2 U# s% x9 u
  45.   USART_Init(USART1, &USART_InitStructure); //初始化串口17 |) h/ Z6 u0 n" c! S. F5 _. b
  46.   USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);//开启串口接受中断" V$ N- H: y* ^% u" ~; J: \# |
  47.   USART_Cmd(USART1, ENABLE);                    //使能串口1
    ) Y7 {( M+ h- g! S9 Z. b6 L! E
  48. * u. H: O4 J: W  m$ ~( w  ~5 O& C
  49. }
    1 ?- u+ w) N0 ?- o1 @% l
  50. 7 _& M5 S3 Y+ M" D/ s% X7 k: G
  51. void USART1_IRQHandler(void)                        //串口1中断服务程序! u5 m0 j' V4 K9 e! Z% v
  52.         {
    + \. y+ o3 o) q; U3 K
  53.         u8 Res;
      s+ u8 Q  Z& u5 Q# P

  54. % e- y# k) M  a
  55.         if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET)  //接收中断(接收到的数据必须是0x0d 0x0a结尾)7 T$ \/ k! N" x, h, v
  56.                 {
    8 u# D( p* U, b+ u2 {# o
  57.                 Res =USART_ReceiveData(USART1);        //读取接收到的数据
    * l' Y4 r5 T; T8 `& f2 m& x
  58.                 & ~; O0 m7 v( k; ~0 ^# n/ w
  59.                 if((USART_RX_STA&0x8000)==0)//接收未完成/ O7 g% n) A2 ]. J1 L* M* r( V
  60.                         {
    7 ]6 x7 f7 M3 l3 u1 f
  61.                         if(USART_RX_STA&0x4000)//接收到了0x0d
    . ~6 }0 Y+ m  C
  62.                                 {; i& m  a% H) P2 t/ ?, x
  63.                                 if(Res!=0x0a)USART_RX_STA=0;//接收错误,重新开始' |" M9 T5 r  S$ S5 }# N
  64.                                 else USART_RX_STA|=0x8000;        //接收完成了
    ! l# j; e  [4 x( n
  65.                                 }
    4 \8 d' \0 D" i( H
  66.                         else //还没收到0X0D
    , t; l5 I" L6 A) X
  67.                                 {        : @7 l9 f% u& U1 W$ p8 H% q
  68.                                 if(Res==0x0d)USART_RX_STA|=0x4000;
    + H5 a8 o1 M. T. V; r
  69.                                 else" X# }* S( a+ m6 R6 b
  70.                                         {5 S5 T3 E* c7 b  H
  71.                                         USART_RX_BUF[USART_RX_STA&0X3FFF]=Res ;; z+ t: ?) a5 }6 F) V) w
  72.                                         USART_RX_STA++;
    $ T3 _$ J2 @0 L  P9 [6 G
  73.                                         if(USART_RX_STA>(USART_REC_LEN-1))USART_RX_STA=0;//接收数据错误,重新开始接收          
    8 }8 `! j* ?. t+ X' o* `" ?% U' [
  74.                                         }                 
    " y5 C9 V4 f4 e  P! H8 L
  75.                                 }
    # |  |" y: l) V6 U
  76.                         }                    
    : D0 Y1 o# Q+ g( d0 L! H
  77.      }
    + b; c6 x5 k5 K8 q/ p) d( G
  78. } 7 g4 l' c2 q9 C5 m0 e

  79. $ K" R" |8 a4 A, B! R9 \" y. h
复制代码
" A% H! U( h8 q7 P. G6 U
time7:1 }& o9 q6 y; h9 N8 G: U% w
  1. . `9 e! Q1 T4 o: q! ^
  2. extern vu16 USART3_RX_STA;( r) v& M' ?. F( k' |
  3. ) S; E( M; {/ c& r, T/ \
  4. //定时器7中断服务程序                    2 H: `9 M- S  r; k! m3 l2 L
  5. void TIM7_IRQHandler(void)* ^. ^9 G1 _1 \* g5 p* {- Z
  6. {         # k. p. U9 c% ]6 c
  7.         if (TIM_GetITStatus(TIM7, TIM_IT_Update) != RESET)//是更新中断* Y3 V& u' z7 t
  8.         {                                    5 I# n1 z* Y; m" c
  9.                 USART3_RX_STA|=1<<15;        //标记接收完成
    3 y( p0 u& A2 ^- }$ b: Y; B
  10.                 TIM_ClearITPendingBit(TIM7, TIM_IT_Update  );  //清除TIM7更新中断标志    : H6 K4 U: q' A1 F3 h- L' e
  11.                 TIM_Cmd(TIM7, DISABLE);  //关闭TIM7 0 W, ]  _9 d8 }
  12.         }            ! d; ^+ W2 T% n. P7 A
  13. }
    ) t' |9 L3 y' e9 h: W5 p: e
  14. 3 F% P: _8 @1 F  A! Y4 W
  15. //通用定时器7中断初始化4 B1 I, G( C6 q' h" o
  16. //这里时钟选择为APB1的2倍,而APB1为42M
    . X/ \9 V. r- O* m
  17. //arr:自动重装值。
    0 X$ ~- j% L; x) P
  18. //psc:时钟预分频数- x2 y  o, K9 t) C& |
  19. //定时器溢出时间计算方法:Tout=((arr+1)*(psc+1))/Ft us.
    1 i4 N8 t7 K* E1 }
  20. //Ft=定时器工作频率,单位:Mhz
    # c  l2 |# a1 ?& h3 ~; Z
  21. //通用定时器中断初始化
    6 U0 N& p" Q" k9 V
  22. //这里始终选择为APB1的2倍,而APB1为36M' _% o, T. `% {# ]9 r/ ?$ |0 T0 f
  23. //arr:自动重装值。
    + ~. p' V: a0 C& _3 r5 o4 ]
  24. //psc:时钟预分频数                 
    4 c; C# F, _4 B, n
  25. void TIM7_Int_Init(u16 arr,u16 psc)* f2 j2 d5 X' F2 A4 Z
  26. {        5 h, X4 D/ B% \/ w' N, j
  27.         NVIC_InitTypeDef NVIC_InitStructure;
    8 O, J( r% g( a6 M
  28.         TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;) u4 }9 u# c( d4 \6 k6 ]3 o
  29. ' z8 A9 X9 h: o' q1 C
  30.         RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM7, ENABLE);//TIM7时钟使能    6 l; i0 e% ^, i3 _$ v5 o3 A" O& S+ H; A
  31.        
    : a& m( w5 _! x7 D# u
  32.         //定时器TIM7初始化* |0 n! U! {, ?+ i$ X# f
  33.         TIM_TimeBaseStructure.TIM_Period = arr; //设置在下一个更新事件装入活动的自动重装载寄存器周期的值        : F5 N( ]! i' E7 h- W- [, u
  34.         TIM_TimeBaseStructure.TIM_Prescaler =psc; //设置用来作为TIMx时钟频率除数的预分频值3 s/ z! L$ M" ~- L, M, y
  35.         TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; //设置时钟分割:TDTS = Tck_tim0 p+ Y3 m* l/ P1 n/ K
  36.         TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;  //TIM向上计数模式5 M+ `# A8 H/ W2 H: E4 ~4 T: {
  37.         TIM_TimeBaseInit(TIM7, &TIM_TimeBaseStructure); //根据指定的参数初始化TIMx的时间基数单位8 R2 g$ D- }7 P  v2 b7 f5 F; a

  38. ( U+ ~: N8 U  V0 s" C1 J
  39.         TIM_ITConfig(TIM7,TIM_IT_Update,ENABLE ); //使能指定的TIM7中断,允许更新中断2 |4 R! P3 a; m! y* N9 T
  40.         9 K5 j2 o) v. d. _' n! s
  41.         TIM_Cmd(TIM7,ENABLE);//开启定时器7  _! J5 X- x+ G6 t0 p5 B
  42.         0 m, p& k- I1 r! x/ a& P2 G% P- I
  43.         NVIC_InitStructure.NVIC_IRQChannel = TIM7_IRQn;
    3 r1 g; l' i; `" g& m; L1 [
  44.         NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=0 ;//抢占优先级0
    0 \' q! `) D/ _& b
  45.         NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2;                //子优先级2
    " B/ K+ Y1 {$ A) M
  46.         NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;                        //IRQ通道使能  D( F+ K+ d1 j
  47.         NVIC_Init(&NVIC_InitStructure);        //根据指定的参数初始化VIC寄存器, a" b- V& V; [& l& `( U
  48.         - w9 f# L0 L- c6 G1 ~+ }
  49. }3 P* V& e5 N/ [# r( U5 v" Q
  50.          / t8 f6 h$ m; c2 x9 b3 i9 u; w
复制代码
8 Q1 s# g# j" P& g/ A5 {: R( w
四.结合DMA接收数据帧" }+ a7 {" ]4 f7 L% [
当单片机从串口上接收数据时,一般我们都是接收一个字节数据,进入一次中断,在中断中处理数据或者做标记,这种方法虽然简单,但是对于大量数据的情况,频繁地进入中断处理数据就占有了CPU的宝贵资源。  g; F0 E, a1 _) u- k' D5 w
这个时候我们可以使用DMA来接收数据,DMA接收数据的好处就是省CPU资源,DMA仅仅在初始化的时候占用一下CPU资源,其他操作都是在DMA的控制器来完成的。
$ S" L& u7 {5 K  X# Q( N
0 _4 z6 d* f: b' e0 I使用背景:单片机从USART2中接收传感器传回的数据帧(12字节),传感器每秒上报一次数据,要求单片机可以收到完整数据并且可以对数据进行处理。* `: i+ M( W9 d
1 U3 b7 u2 M8 t5 W  T( _% t. P
程序框架:以往串口接收数据都是判断数据寄存器非空的,一字节一字节地接收,现在使用DMA,使每一次数据寄存器的值都自动传给内存指定地址(也就是指定的数据缓冲区),当串口接收完一帧数据后,会触发空闲中断,这意味着一帧数据的接收完成,我们只需要在数据缓冲区中对数据进行处理即可。7 R! M( N7 W$ G) Z; i

/ ~, s7 |# }0 @代码:
8 B( B2 [4 c2 U' V8 G1 `+ k3 i7 t

  1. ; t2 G) h. u' p
  2. uint8_t USART2_Rx_Buf[12];
    % r1 |6 r5 j$ U; Z, r
  3. - x3 B% ]& ^  ^% P% W, j
  4. void USART2_Config( void )0 W. E5 s' H% Y* I! _$ |, Z- Z
  5. {* @$ _) z% k4 s! M1 [1 d
  6.         /* 声明各结构体 */
    2 N* B4 _3 k0 ?1 v& J
  7.         GPIO_InitTypeDef GPIO_InitStruct;
      ]0 K! |8 U7 l! V% Y; K
  8.         USART_InitTypeDef USART_InitStruct;
    3 c/ Q* b* s1 S4 u8 o2 {! |* r
  9.         NVIC_InitTypeDef NVIC_InitStruct;
    7 R% b3 ~* P0 s$ s! I
  10.         DMA_InitTypeDef DMA_InitStruct;- K( \5 c3 v5 I" ^' Z; n9 y2 B; c
  11.         $ I* h3 s" E/ @: g! r7 _% g2 u
  12.         /* 打开时钟 */) j: c/ j  A* v( z! V! b
  13.         RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOA, ENABLE );
    3 x/ F2 S$ Q. v' b1 ]4 K: v! E: t0 y
  14.         RCC_APB1PeriphClockCmd( RCC_APB1Periph_USART2, ENABLE );
    - l. e. D- Y+ |# g% ~
  15.         RCC_AHBPeriphClockCmd( RCC_AHBPeriph_DMA1, ENABLE );0 s8 F5 U  K: b% k- U
  16.         3 E! ]$ B0 t: J5 M
  17.         /* GPIO配置 */
    1 i0 }4 E4 n1 \) e
  18.         GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_PP;4 Y3 f7 M9 L# c" F9 ^5 D
  19.         GPIO_InitStruct.GPIO_Pin = GPIO_Pin_2;                 //USART2_Tx:PA2
    ) O8 ^0 \% t5 b4 ^; ?0 m  {8 ?# L
  20.         GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;% W2 E; ^5 L4 o( ^$ E- y
  21.         GPIO_Init( GPIOA, &GPIO_InitStruct );
    # c) E! c' T! Q, a0 J2 n3 O
  22.         GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IN_FLOATING;
    0 Y5 K( l# N+ x9 Y
  23.         GPIO_InitStruct.GPIO_Pin = GPIO_Pin_3;                //USART2_Rx:PA3& N# }/ p4 }, f. M' e
  24.         GPIO_Init( GPIOA, &GPIO_InitStruct );
    9 B8 G' P5 p" L0 B, I5 r3 `
  25.        
    : L; e. \; g! g
  26.         /* 串口配置 */
    4 p3 F' x6 I8 U# l0 ?+ V$ Z
  27.         USART_DeInit( USART2 );
    ' w. O! N* [6 ]3 Z2 z9 M, {' ]% N- f
  28.         USART_InitStruct.USART_BaudRate = 9600;                //波特率:9600
    ) P8 O' U- n, a" W" w
  29.         USART_InitStruct.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
    ( {" G) D4 S9 L. w1 Q1 U, L
  30.         USART_InitStruct.USART_Mode = USART_Mode_Tx|USART_Mode_Rx;
    4 ^! X8 f2 w" J: j
  31.         USART_InitStruct.USART_Parity = USART_Parity_No;
    7 I1 ?2 |" L# [/ J) l
  32.         USART_InitStruct.USART_StopBits = USART_StopBits_1;/ _' |1 L$ a6 }# F* p6 A! K
  33.         USART_InitStruct.USART_WordLength = USART_WordLength_8b;
    ( ~% B, f. H/ R' D0 F
  34.         USART_Init( USART2, &USART_InitStruct);
    ' E" m% t" J2 n7 x: D! s! q- o
  35.         % k/ {2 P9 D, h  `) ]% n1 j1 {: f
  36.         /* 中断配置 */! n: x; i' ^4 b% G9 R# ]
  37.         NVIC_PriorityGroupConfig( NVIC_PriorityGroup_2 );" ~( k% y9 \  q3 H6 H% n1 C8 p
  38.         NVIC_InitStruct.NVIC_IRQChannel = USART2_IRQn;
    ( Y3 I# E* T" L" y) c( Y9 T; I
  39.         NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;- p4 Z- Q* T5 C; ?# D
  40.         NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 1;
    2 a0 k# o% p% C1 t! [$ d% X' S! }
  41.         NVIC_InitStruct.NVIC_IRQChannelSubPriority = 0;( \% {9 C2 J/ m' A% S
  42.         NVIC_Init( &NVIC_InitStruct );
    * z4 f9 _7 E3 C# |/ p$ L* ]9 n
  43.         USART_ITConfig( USART2, USART_IT_IDLE, ENABLE );      //空闲中断- ~5 p) `$ B+ X1 \, F
  44.         USART_DMACmd( USART2, USART_DMAReq_Rx, ENABLE );   //开启DMA接收6 N. F4 ~( e5 r- J- s. i
  45.        
    + b% h6 z0 t6 C9 p- x" n
  46.        
    0 \1 H, S; W8 P/ Z/ F1 }2 E) Q
  47.         /* 配置DMA传输数据 USART2对应DMA1的通道6 方向:外设到存储器*/          Z& n# G8 d. Q1 \
  48.     DMA_InitStruct.DMA_PeripheralBaseAddr = USART2_BASE+0x04;              // 设置DMA源地址:串口数据寄存器地址*/        : j; H- s- C% c" g" t8 x: r
  49.         DMA_InitStruct.DMA_MemoryBaseAddr = (uint32_t)USART2_Rx_Buf;           // 内存地址(要传输的变量的指针)       
    ; M  l& _( e% J# E' ^2 H/ F
  50.         DMA_InitStruct.DMA_DIR = DMA_DIR_PeripheralSRC;                        // 方向:从外设到内存                9 a% G( s* R4 t" V  W: B- |
  51.         DMA_InitStruct.DMA_BufferSize = 12;                                    // 传输大小        一帧数据12字节                            2 s8 _3 x- ~2 ]: o4 U
  52.         DMA_InitStruct.DMA_PeripheralInc = DMA_PeripheralInc_Disable;          // 外设地址不增                + H/ ]" z4 S# ~
  53.         DMA_InitStruct.DMA_MemoryInc = DMA_MemoryInc_Enable;                                    // 内存地址自增               
    6 {4 t; ~9 c% g& y$ ~3 V
  54.         DMA_InitStruct.DMA_PeripheralDataSize =         DMA_PeripheralDataSize_Byte; // 外设数据单位               
    1 S/ a0 w' b9 ]: ?- m- G+ Q
  55.         DMA_InitStruct.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;           // 内存数据单位         . {; k8 ^0 g$ a/ Z# c
  56.         DMA_InitStruct.DMA_Mode = DMA_Mode_Circular;                               //正常 DMA模式,一次或者循环模式
    . P0 n% I  g5 Y: @) j- [: k
  57.         DMA_InitStruct.DMA_Priority = DMA_Priority_Medium;                     // 优先级:中
    7 o& a0 r3 |1 B0 C$ d
  58.         DMA_InitStruct.DMA_M2M = DMA_M2M_Disable;                              // 禁止内存到内存的传输
      i, e( Q, \7 Z6 T$ M, f! Q
  59.         DMA_Init( DMA1_Channel6, &DMA_InitStruct );& ~  P4 c) f) ~0 e! U
  60.         DMA_ClearFlag(DMA1_FLAG_TC5);
    5 H9 y! Z+ H; G, ]& z6 s% f
  61.         DMA_Cmd( DMA1_Channel6, ENABLE );//使能DMA. s4 d+ P  x3 D0 a5 W6 S" E
  62.         ) k' f- D; x- ]
  63.        
    ; V: b& s- {- f
  64.         /* 使能串口 容易忽略 */$ E: u2 ~4 w2 n: e" h4 K& D
  65.         USART_Cmd( USART2, ENABLE);3 O3 K. L$ `/ ^) N' D
  66.        
    ; ]$ {" L. ?  ^6 }: j1 j, ]
  67. }
    " T) j0 R! l: \

  68. 4 Q$ @" \- z  o
  69. 1 q0 \+ U! q. q$ R

  70. 8 n/ X! O; U( y* O* }% C
  71. /* 串口空闲中断服务函数 */
    9 j" m) l+ d$ T; l, R, i
  72. void USART2_IRQHandler( void )
    " s7 P& B0 n6 N7 Q* g# I' g
  73. {6 v  k4 y) f6 [( e8 d$ W; @8 f) V1 K
  74.         if( USART_GetITStatus( USART2, USART_IT_IDLE ) != RESET )
    & Q2 T6 V+ d6 V# ?: k7 [
  75.         {
    : e5 q( |5 o% y6 v
  76.                 USART_ReceiveData( USART2 );//象征性接收数据& d4 ?# j$ ?/ v' F; [
  77.                 USART_ClearITPendingBit( USART2, USART_IT_IDLE );//清除标志位( x- c3 \' v5 V$ ^7 B1 n
  78.                
    , h8 a" B7 d- b
  79.         }
    - i% [  t$ k, t$ D# Y9 R
  80. }' C' H, K: L2 x# R5 v
  81. //也可以在中断服务函数中扩展更多的功能3 Q. c: i5 [7 ?
复制代码

$ Q* f' x9 t* w9 Z- H; I9 Y5 R" q, ?要点:4 C& B* ~, b& D5 p- K
要开启串口的空闲中断
3 o! i5 E4 U1 N( C" A串口的空闲中断中要象征性读取数据和清除标志位
" a( I% q& P$ S) @* D3 z0 r要开启串口的DMA接收请求
) o6 q' m) f: f; O+ M9 ?4 h
————————————————
- t8 a9 I! s( X3 b  r- l5 g版权声明:Aspirant-GQ
! `. Q; ~* \% G' ]如有侵权请联系删除
0 N1 ?% C4 Z: X: L7 Q
% i: c* b  g6 `3 s3 v! W2 x& }& H, n' Y9 P1 \
: X$ Z; R* ~1 H. B, o9 C$ k
收藏 评论0 发布时间:2023-3-18 12:11

举报

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