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

基于STM32串口的经验总结

[复制链接]
攻城狮Melo 发布时间:2023-3-18 12:11
在日常中,串口经常作为和上位机通信的接口,进行打印信息方便调试程序,有时也会作为模块的驱动接口,所以总结一下串口的几种使用方法对以后的开发还是很有帮助的。/ p& g1 G4 y3 K) n/ P
+ v- L  b; Z8 E0 B$ \2 I
一.仅向上位机打印调试信息
5 l$ y5 `& Q0 @3 _6 ~9 L2 {2 ]7 z单纯利用串口向上位机打印调试信息,程序如下:
) s3 A- I9 K5 _3 G- p3 i0 e0 @! ]
  1. void USART1_Init( uint32_t btl )0 p( z- s  W7 Z8 M
  2. {6 m8 S" k: m# {6 L
  3.         GPIO_InitTypeDef  GPIO_InitStruct;0 W+ Y! Z6 D! l# q6 ]
  4.         USART_InitTypeDef USART_InitStruct;# M7 V4 c1 i: c4 Q
  5.         2 I8 S0 u# l3 d7 ^5 v! @
  6.         RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOA|RCC_APB2Periph_USART1, ENABLE );
    ) z/ N; H+ V% y2 n, X( `
  7.         # L# @! R0 q# e+ |3 ~! }% h
  8.         GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_PP;& q" c( t' u9 L% i
  9.         GPIO_InitStruct.GPIO_Pin = GPIO_Pin_9;//Tx( @' v( f8 H; J
  10.         GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
    9 t1 H1 \8 ?) I; h
  11.         GPIO_Init( GPIOA, &GPIO_InitStruct );4 r4 ]) r# ]3 [7 g$ @
  12.         GPIO_InitStruct.GPIO_Pin = GPIO_Pin_10;//Rx
    , y$ ]! B8 B1 s5 B0 z
  13.         GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IN_FLOATING;; W. z6 f5 S0 @3 u. P- W; t
  14.         GPIO_Init( GPIOA, &GPIO_InitStruct );
    0 ~4 G2 d- T; _, b0 G# F
  15.        
    . Q% G) @& W7 O0 j6 u. b
  16.         USART_InitStruct.USART_BaudRate = btl;4 B0 V& A: t  u, p* ^
  17.         USART_InitStruct.USART_HardwareFlowControl = USART_HardwareFlowControl_None;* O7 i2 U4 _6 D# g
  18.         USART_InitStruct.USART_Mode = USART_Mode_Tx;
    2 [' T4 B8 ?9 W2 f2 m, L' c
  19.         USART_InitStruct.USART_Parity = USART_Parity_No;
      q4 \, d- E) ^/ c& I0 x
  20.         USART_InitStruct.USART_StopBits = USART_StopBits_1;& m" w& b9 d9 v+ U+ M& T! ?
  21.         USART_InitStruct.USART_WordLength = USART_WordLength_8b;( h  _, g8 f/ L5 T6 G( z
  22.         USART_Init( USART1, &USART_InitStruct );& s9 z* S3 `$ i
  23.        
    3 j9 ~0 Q2 U  U8 z
  24.         USART_Cmd( USART1, ENABLE );
    $ O2 Y2 f& w3 y
  25.         . J; {) \6 U" g0 z
  26. }
    + J* D- A& P& c# @$ q5 z
  27. 4 |, X1 v! }0 h! b$ ~# ^
  28. //串口重定向,直接利用printf函数输出调试信息
    $ C! O: y) W, |. b
  29. int fputc( int ch, FILE *f )0 N# o5 {( k. D) [. x1 k
  30. {
    ) u( X( o& |; Z' F6 q4 i* q
  31.         USART_SendData( USART1, ( uint8_t )ch );
    0 _, Q. ^8 `* P/ A
  32.         while( USART_GetFlagStatus( USART1, USART_FLAG_TXE )!=SET );  P% s: d) m$ k6 I& k5 x
  33.         return ch;
    , {3 w- c+ {: Z- z
  34. }: [0 R6 t1 [3 `" Y3 a: q
  35. * J& w7 L9 B9 m% x9 Z' z4 f

  36. * Y" H+ g7 X9 d( O2 l# w' d# \
复制代码

6 Q+ g# t% I& L记得包含头文件,勾选Use MicroLIB,以使用printf等函数
; d* ^5 Q% g4 l7 a# U# c4 M* `4 `* p
2020042220264754.png
7 m9 v! M) w4 X0 G3 ]) i
2 E, d5 n% v* g# T# Q& N
# S7 r- Z$ s) j
二.与上位机交互信息( a$ e5 L3 p* V8 Q, F& l# x$ {
相比于上面的单向通信,有时候需要从上位机接收信息,然后进行反馈,这个时候就使用到串口的中断了。
9 |( _8 J6 P  x$ C上位机向单片机发送字符串,接收后再发送给上位机:+ u. b4 i6 A( I) X+ {' }( p* h

  1. 9 r# k" N3 a0 W! Z
  2. void USART1_Init( uint32_t btl )1 k7 z, _" ]: ?- F" U) u6 m6 a
  3. {
    6 e2 A& M6 `  K4 h1 W* C. h
  4.         /* 结构体声明 */
    ( ]& z6 R  w0 [, F! L$ s
  5.         GPIO_InitTypeDef  GPIO_InitStruct;
    . Y4 _" \# \3 ?6 i* Z' y" v7 o$ Z
  6.         USART_InitTypeDef USART_InitStruct;# Y3 P' m$ _& ^
  7.         NVIC_InitTypeDef NVIC_InitStruct;
    ! S# {0 t3 M- U" _1 t* d. {% M
  8.        
    ' C, ^! T9 ?8 u0 [- N3 P. v9 r5 h
  9.         /* 打开时钟 */, b5 {( b( R2 X" p
  10.         RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOA, ENABLE );, O. K8 L; z- B
  11.         RCC_APB2PeriphClockCmd( RCC_APB2Periph_USART1, ENABLE );% s+ b5 Y' W. O" @
  12.        
    " S: _0 t; l" P! |
  13.         /* GPIO配置 */- H- D6 m' {! i. D
  14.         GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_PP;8 s; A0 ]7 ~3 f- I# Y
  15.         GPIO_InitStruct.GPIO_Pin = GPIO_Pin_9;//Tx
    0 X$ P) H% j& B+ w0 A: C5 Z! t
  16.         GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;) b9 @& m+ C* j/ u
  17.         GPIO_Init( GPIOA, &GPIO_InitStruct );
    3 `' r6 t6 W$ M; @+ r
  18.         GPIO_InitStruct.GPIO_Pin = GPIO_Pin_10;//Rx; p- z! @" b4 W( H% k5 t9 J* F
  19.         GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IN_FLOATING;
    ' `4 ~7 c$ D4 ?$ T; n
  20.         GPIO_Init( GPIOA, &GPIO_InitStruct );
    3 p: D6 @7 q/ D% I* e
  21.         : K% P- _* f  p- ^! I7 ?, ~
  22.         /* 串口配置 */  {$ Q" H0 j; X0 B
  23.         USART_InitStruct.USART_BaudRate = btl;
    , T3 x# m& f; A/ j8 g
  24.         USART_InitStruct.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
    2 [/ a7 |9 b% I# h
  25.         USART_InitStruct.USART_Mode = USART_Mode_Tx|USART_Mode_Rx;//收发模式2 U- |7 ^0 Z3 h+ u9 H+ ~
  26.         USART_InitStruct.USART_Parity = USART_Parity_No;( G6 X/ z8 \9 @( h0 k$ T
  27.         USART_InitStruct.USART_StopBits = USART_StopBits_1;/ N' f% g& C9 V& y
  28.         USART_InitStruct.USART_WordLength = USART_WordLength_8b;& N' F) p# N  V% {7 Y, Y
  29.         USART_Init( USART1, &USART_InitStruct );9 N2 G0 e) n* U' C8 B) D
  30.        
    4 L3 _# e7 a6 x
  31.         /* 中断配置 */' N( c3 g) Y5 D- L. o/ N2 e9 d
  32.         NVIC_InitStruct.NVIC_IRQChannel = USART1_IRQn;1 ?3 s' i/ T; q. K
  33.         NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority=3 ;+ {  S- _6 a$ y; z
  34.         NVIC_InitStruct.NVIC_IRQChannelSubPriority = 3;               
    % n& `! ^' e2 b: W& I, R, e
  35.         NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;                       
    2 q1 f7 E3 X3 a, G4 y
  36.         NVIC_Init(&NVIC_InitStruct);       
    % X% O+ f  e0 J/ D1 c
  37.         USART_ITConfig( USART1, USART_IT_RXNE, ENABLE );//接收寄存器非空触发中断  a' }) ?* G0 u1 A7 v6 h! g/ z
  38.        
    , f# M& w3 i" u  E' n5 @0 m+ x
  39.         /* 使能串口 */$ D/ {/ i  \8 ]- x% g1 c
  40.         USART_Cmd( USART1, ENABLE );4 |2 \. i- M" {5 w+ P5 O; B3 J
  41.         2 G& u) @' ?5 c3 Z
  42. }) z8 U- Z0 Q; u& N0 v
  43. ; S% [1 `; Y9 F7 [: g, |( J
  44. ( D* V- M9 `) Y, l4 z
  45. volatile uint8_t n=0;! V8 S6 j4 m' a, M4 }# u' ]
  46. //接收缓冲区: n7 R6 u% M2 N2 q% Q7 J
  47. uint8_t USART1_Rx_Buf[100];4 d/ T/ ]8 U1 G% G- |1 O: v. r- U
  48. void USART1_IRQHandler( void )( _: k6 V  y5 Q1 ^
  49. {" _- f3 x' Q4 h: V- ]3 z
  50.         if( USART_GetITStatus( USART1, USART_IT_RXNE )==SET )- O$ {" x4 i, W1 D1 ]
  51.         {) X4 Q4 V+ G# w3 S0 r7 u1 G
  52.                         USART1_Rx_Buf[n]=USART_ReceiveData( USART1 );* e( a5 Z7 r# c: q+ U3 D
  53.                         n++;
    " u) R; l/ `! v! |# y
  54.         }        & h$ y2 n% L6 ^+ ?0 z9 E/ q; [, E9 {
  55.         USART_ClearFlag( USART1, USART_IT_RXNE );
    5 q& p: t% @7 n9 ?
  56. }1 z% o! Q: O% e& S
复制代码
% X+ k' e3 e4 B7 P8 I
每从上位机中接收一字节的数据,都将数据存储在串口的接收缓冲区USART1_Rx_Buf[100]中。  N2 A2 r: L% m' T$ d0 l8 u- [

; J# M( U, d( s. G3 M# {

; I# H6 N( {9 W三.作为驱动接口3 N0 m' G/ T1 ^2 _7 i& O
一些模块的驱动接口就是串口,这个时候就需要单片机从模块中读取指定格式的数据,比如GPS模块,将定位信息从串口发出,单片机解析串口数据,显示在上位机中。( h5 |" f7 O+ p5 ~
usart3用来与GPS模块通信,从GPS模块接收数据,认为10ms内的数据属于一次数据,所以就需要定时器来控制时间。
, K+ M$ r% r2 rusart3:
' A4 T! S( v2 ?3 U! S! e
  1. //串口1中断服务程序; I' K& C! H. E2 q0 T
  2. //注意,读取USARTx->SR能避免莫名其妙的错误          
    ) p. O4 s7 f+ z2 f+ S( J' A
  3. u8 USART_RX_BUF[USART_REC_LEN];     //接收缓冲,最大USART_REC_LEN个字节.
    2 y: U* l% e8 U% b4 _
  4. //接收状态: ~+ a1 I( H' y- b' O
  5. //bit15,        接收完成标志
    9 A4 y3 k  T% h7 Q5 ]
  6. //bit14,        接收到0x0d2 O* v. ~2 s+ W* D" F
  7. //bit13~0,        接收到的有效字节数目' J; f8 [- z. r1 Q6 U. _: G
  8. u16 USART_RX_STA=0;       //接收状态标记          
    # X9 @- ~4 `* n8 S7 k8 K" U: l' @5 @
  9.     h, F7 I' S* x4 W2 e, n3 Q* O0 `
  10. void uart_init(u32 bound){
    4 d7 `4 s$ {6 J, w. l7 ^
  11.   //GPIO端口设置8 e, r. y. [8 S* y8 r
  12.   GPIO_InitTypeDef GPIO_InitStructure;+ D4 x' n# z" K6 S6 j1 m& ]  l* e& M
  13.         USART_InitTypeDef USART_InitStructure;
    6 k( K6 F( B2 ]# f. s2 O& }: |# x( Z! }
  14.         NVIC_InitTypeDef NVIC_InitStructure;1 F6 f2 g0 p1 r; B+ e
  15.         1 d; ?, `, R' {! U8 Z. }
  16.         RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1|RCC_APB2Periph_GPIOA, ENABLE);        //使能USART1,GPIOA时钟; g4 o) B: f; e/ X7 S
  17.   9 I& I' P2 r' c! \+ P
  18.         //USART1_TX   GPIOA.9
    ) l% h# }, X9 _' ]+ @& t
  19.   GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9; //PA.9
    9 o, Z7 G6 A. X
  20.   GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;, i. u4 m4 l: j/ m( F
  21.   GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;        //复用推挽输出% C8 T) y9 {: X- s
  22.   GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化GPIOA.9
    6 ]1 b  G" g! Y3 H/ z% s
  23.    ( A' O6 j) P  `
  24.   //USART1_RX          GPIOA.10初始化( o  [6 {) {, M, G8 G
  25.   GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;//PA10; @" }+ \7 A% F1 a* b$ _! C9 L* P! V
  26.   GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;//浮空输入
    6 Q5 `2 c2 A, ?! v/ G2 R: H
  27.   GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化GPIOA.10  * C" `7 h9 o& A2 k
  28. ' j9 U3 ~- ~& L5 }' [7 e/ |5 [
  29.   //Usart1 NVIC 配置
    : q( z* ]5 x' c1 X( W( r4 Y
  30.   NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
    # p( ~/ v( k. n8 |
  31.         NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=3 ;//抢占优先级3
    2 c$ J) O) l  @" A; @
  32.         NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3;                //子优先级3
    2 y/ x- ?9 U/ C: x6 |& b% K
  33.         NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;                        //IRQ通道使能
      A: C" |5 _# q  |- ?
  34.         NVIC_Init(&NVIC_InitStructure);        //根据指定的参数初始化VIC寄存器7 e/ J5 {1 \* j& V
  35.   
    0 Z% G7 [2 _0 P( I$ P/ a) I+ `4 }
  36.    //USART 初始化设置
    5 ~$ i& P0 V% O+ X
  37. ' ]; H7 [9 j7 ?7 h8 d
  38.         USART_InitStructure.USART_BaudRate = bound;//串口波特率
    + o0 ^. `9 X, Z- c; c; t
  39.         USART_InitStructure.USART_WordLength = USART_WordLength_8b;//字长为8位数据格式
    ( A3 `0 `0 r7 l- `: c% H9 f
  40.         USART_InitStructure.USART_StopBits = USART_StopBits_1;//一个停止位' }2 |8 K# s) T
  41.         USART_InitStructure.USART_Parity = USART_Parity_No;//无奇偶校验位, D- ^- Z( ]4 J' Y4 [% `$ _
  42.         USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;//无硬件数据流控制
    + `8 F9 Y% y. S4 Z  ?
  43.         USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;        //收发模式
    $ ]4 j, m+ `: V' X

  44. - N4 \5 r7 M; H! u. b
  45.   USART_Init(USART1, &USART_InitStructure); //初始化串口15 r, g+ |' [; U6 U: g- T
  46.   USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);//开启串口接受中断
    + I0 v/ e. {" u; {6 x0 _
  47.   USART_Cmd(USART1, ENABLE);                    //使能串口1
    : _6 f6 K1 g* `, u( k8 V
  48. 5 o( h4 T! [! T. Q! Q
  49. }2 V! L3 U6 ]$ S9 ~
  50. $ m; F7 M9 D" W1 y0 L8 ^
  51. void USART1_IRQHandler(void)                        //串口1中断服务程序2 X( W7 I$ }' g5 W- n6 ?- e
  52.         {
    6 F; }# p7 E7 ^" k4 u
  53.         u8 Res;/ Z5 Y8 z8 U- j; \$ F

  54. 6 V+ ~3 K1 T% Z4 P( e# v
  55.         if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET)  //接收中断(接收到的数据必须是0x0d 0x0a结尾), P# _6 [, p% K3 H- z
  56.                 {
    , q& |$ V% e5 b* }5 @
  57.                 Res =USART_ReceiveData(USART1);        //读取接收到的数据
    4 o( Q5 p; k" ~3 S! m# \
  58.                
    ; u/ u$ P) m& i" W
  59.                 if((USART_RX_STA&0x8000)==0)//接收未完成
      X: }7 [/ P; K7 Z
  60.                         {  N$ G1 H+ |; {. G
  61.                         if(USART_RX_STA&0x4000)//接收到了0x0d
    / U) x$ r' `: k6 b( p
  62.                                 {+ h- i" ~2 e0 u2 y
  63.                                 if(Res!=0x0a)USART_RX_STA=0;//接收错误,重新开始2 Y: u8 ~/ @  |8 f, m) B4 H4 h, N
  64.                                 else USART_RX_STA|=0x8000;        //接收完成了
    8 P" z; d) u8 q. ?! `
  65.                                 }8 e. s9 Y( T7 o4 ?5 t- C  t8 ?
  66.                         else //还没收到0X0D3 D; b3 U" j/ S+ C6 ?
  67.                                 {        ! G& M. C# H( r5 b9 K: t
  68.                                 if(Res==0x0d)USART_RX_STA|=0x4000;0 a' B( a- W6 N# J4 P7 Z
  69.                                 else  ?5 v) G  m4 @7 a) y: ?
  70.                                         {; W6 V+ M0 y& m, G
  71.                                         USART_RX_BUF[USART_RX_STA&0X3FFF]=Res ;8 n# p8 ~( p9 r
  72.                                         USART_RX_STA++;, s6 n( V% m) e: \* P) n
  73.                                         if(USART_RX_STA>(USART_REC_LEN-1))USART_RX_STA=0;//接收数据错误,重新开始接收          
    ' R1 e3 G1 m$ E  c
  74.                                         }                 5 U% p, I6 x7 g6 E- P; a' b
  75.                                 }7 v: q% y* ~) @' p. E
  76.                         }                      S( f* D4 i" O- |' I8 d5 d  ?! R5 w
  77.      } , D& y6 b' K# j: v1 h2 \: X- ?
  78. } $ T2 \2 f7 |' m6 p; v
  79. 6 j4 M# l8 N# m  C$ m/ p
复制代码

6 ?7 x/ {& @9 D. T7 l% o; U  Btime7:( D; h# R* S' Q( \
  1. 2 F% K/ c" M. g; ]' W
  2. extern vu16 USART3_RX_STA;
    ; y  e# ^" p# Y5 w  Q+ `' \1 e

  3. $ G+ Y6 ]# i( z5 ~( j7 I( o9 G: m
  4. //定时器7中断服务程序                    
    2 s5 j% {. i% ^, A+ Z" T
  5. void TIM7_IRQHandler(void)
    * T1 I7 J, b7 D/ V; r/ R; y
  6. {        
    6 h4 r3 G/ Q: F9 E2 f4 ?
  7.         if (TIM_GetITStatus(TIM7, TIM_IT_Update) != RESET)//是更新中断
    4 C  M1 w9 g: K' I! `
  8.         {                                    1 ?2 ]" |+ {% b- {
  9.                 USART3_RX_STA|=1<<15;        //标记接收完成& O9 f& M- b% H2 E) F  J
  10.                 TIM_ClearITPendingBit(TIM7, TIM_IT_Update  );  //清除TIM7更新中断标志    & R7 R5 J- ?0 x2 v. J3 [- p
  11.                 TIM_Cmd(TIM7, DISABLE);  //关闭TIM7 + E  J3 ?# `" h7 e6 _! n; R
  12.         }            
    * }# I% `' V. a0 B3 Y4 l
  13. }3 l  Q2 F0 G- }2 F+ C3 x8 P  R( |

  14. " O7 n+ u  P4 o0 `1 B: `
  15. //通用定时器7中断初始化, y* J! [9 N, s+ h9 m
  16. //这里时钟选择为APB1的2倍,而APB1为42M
    8 y& k- l/ X" b* a( e2 ]
  17. //arr:自动重装值。5 I: a) ~# r6 Q/ x
  18. //psc:时钟预分频数6 }5 r0 S; s& X( v' D4 M( u
  19. //定时器溢出时间计算方法:Tout=((arr+1)*(psc+1))/Ft us.. z+ p& D/ H, N
  20. //Ft=定时器工作频率,单位:Mhz
    * m: C3 X8 I1 C& D+ B  b( ~
  21. //通用定时器中断初始化
    ; J5 C9 d% A: z
  22. //这里始终选择为APB1的2倍,而APB1为36M9 C0 e  t9 \6 ~- _+ P/ L9 X- j
  23. //arr:自动重装值。$ Q0 c2 r3 D" Z) j/ h1 m
  24. //psc:时钟预分频数                 % X% z2 B  M: K7 n
  25. void TIM7_Int_Init(u16 arr,u16 psc)
      L- ]$ `7 L% H
  26. {       
    6 C4 P6 P+ v5 E
  27.         NVIC_InitTypeDef NVIC_InitStructure;
    $ q- W8 e% D' U/ j" a
  28.         TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;
    " C8 r' Q; _9 \
  29. . w4 E1 f6 k: Q4 ^' A' Z
  30.         RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM7, ENABLE);//TIM7时钟使能    3 J* e! W# z! a- ~4 r
  31.        
    + |: r+ ]2 |. G
  32.         //定时器TIM7初始化
    0 @' P# }) A. i2 X8 N
  33.         TIM_TimeBaseStructure.TIM_Period = arr; //设置在下一个更新事件装入活动的自动重装载寄存器周期的值        ( z# I! t5 A( q8 j$ Z  G
  34.         TIM_TimeBaseStructure.TIM_Prescaler =psc; //设置用来作为TIMx时钟频率除数的预分频值
    / \0 ~4 V) j$ X* W7 g
  35.         TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; //设置时钟分割:TDTS = Tck_tim
    " n5 I( z. V* y* T2 o1 {4 K
  36.         TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;  //TIM向上计数模式
    + }8 M5 F6 y9 U+ I, {1 }/ y
  37.         TIM_TimeBaseInit(TIM7, &TIM_TimeBaseStructure); //根据指定的参数初始化TIMx的时间基数单位
    # J0 J; ?* r! _  H# z) U8 M2 W' T! T
  38. 4 d4 J+ ^% |* ~% h
  39.         TIM_ITConfig(TIM7,TIM_IT_Update,ENABLE ); //使能指定的TIM7中断,允许更新中断3 ^" f( t& W8 e) w* N. F
  40.         ' t6 S& h. C9 M2 s
  41.         TIM_Cmd(TIM7,ENABLE);//开启定时器7
    7 ~/ }, C) x$ ^1 A7 h
  42.        
    # v1 T' }6 w5 R" J
  43.         NVIC_InitStructure.NVIC_IRQChannel = TIM7_IRQn;- }! S* ~7 `7 Q& `! I& R# q* `
  44.         NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=0 ;//抢占优先级0( W! c5 D+ l$ Z
  45.         NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2;                //子优先级2
      `. X# s8 q  h4 Q1 }. Q/ v2 k
  46.         NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;                        //IRQ通道使能
    2 Z. w- m6 B- n2 f
  47.         NVIC_Init(&NVIC_InitStructure);        //根据指定的参数初始化VIC寄存器0 Z1 y4 C( ^% E; U7 C/ Y
  48.        
    6 ~! c  L) w5 V) D
  49. }
    0 q* T4 X1 k% e& I4 c# t# G
  50.          
    ' _, e5 g& \+ M4 E( ]
复制代码
& l2 D5 X$ e( d
四.结合DMA接收数据帧
+ g* `9 B3 o8 O当单片机从串口上接收数据时,一般我们都是接收一个字节数据,进入一次中断,在中断中处理数据或者做标记,这种方法虽然简单,但是对于大量数据的情况,频繁地进入中断处理数据就占有了CPU的宝贵资源。
  O6 x' P  i0 @+ K  n这个时候我们可以使用DMA来接收数据,DMA接收数据的好处就是省CPU资源,DMA仅仅在初始化的时候占用一下CPU资源,其他操作都是在DMA的控制器来完成的。
2 V$ k( y/ m% [9 d( [- L5 _+ m# U
6 V2 T$ [+ ~- J+ J% ^使用背景:单片机从USART2中接收传感器传回的数据帧(12字节),传感器每秒上报一次数据,要求单片机可以收到完整数据并且可以对数据进行处理。+ |6 B) j% R1 a; u
$ _8 w4 T3 c7 E
程序框架:以往串口接收数据都是判断数据寄存器非空的,一字节一字节地接收,现在使用DMA,使每一次数据寄存器的值都自动传给内存指定地址(也就是指定的数据缓冲区),当串口接收完一帧数据后,会触发空闲中断,这意味着一帧数据的接收完成,我们只需要在数据缓冲区中对数据进行处理即可。9 \4 J2 F. |6 t" ?$ j

' \* T8 M1 l# w3 Q8 U' e/ I4 G6 d5 [代码:
% p# `! \$ D, {7 e0 G8 ?

  1. . i' o1 G4 s7 r! ~1 U6 @
  2. uint8_t USART2_Rx_Buf[12];6 i0 z" g) c# R3 t) n/ p
  3. ) J, J. I- y4 @0 j- f& Y
  4. void USART2_Config( void )
    / M0 m% u% r6 M+ U' g, a3 q
  5. {
    7 r, O6 V/ w9 C* V- C# _6 ?! w
  6.         /* 声明各结构体 */; y# _* I% p9 o1 ~
  7.         GPIO_InitTypeDef GPIO_InitStruct;9 M& q) P3 B) E# t1 f7 }/ Q5 {
  8.         USART_InitTypeDef USART_InitStruct;
    , I9 U5 \. V9 q4 Q
  9.         NVIC_InitTypeDef NVIC_InitStruct;4 Q& W  i  a+ w( K3 L" h4 m
  10.         DMA_InitTypeDef DMA_InitStruct;/ }: n# w( K( E, N4 R" X1 L" e& F
  11.        
    2 x; [9 U4 y1 }+ E0 q- S: w
  12.         /* 打开时钟 */3 r5 X# I5 b6 H' t
  13.         RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOA, ENABLE );
    ; q+ |+ W: Q# F7 K/ I
  14.         RCC_APB1PeriphClockCmd( RCC_APB1Periph_USART2, ENABLE );
    ! V/ f7 G& X' ~$ B, |% f
  15.         RCC_AHBPeriphClockCmd( RCC_AHBPeriph_DMA1, ENABLE );0 o- D% Q7 u1 h, t
  16.        
    ! f: B5 v! n7 ]
  17.         /* GPIO配置 */
    4 R$ q" o3 p- c! \* t" T0 A
  18.         GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_PP;; i% ?' [& ~) o
  19.         GPIO_InitStruct.GPIO_Pin = GPIO_Pin_2;                 //USART2_Tx:PA2
    & s3 W6 `. f! x  O
  20.         GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;+ M: F4 ]4 c( r3 Y& x% b
  21.         GPIO_Init( GPIOA, &GPIO_InitStruct );
    # `% k" I! S7 S5 X8 Y7 h, w
  22.         GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IN_FLOATING;0 @5 A, T5 a, v1 y% t# {2 c
  23.         GPIO_InitStruct.GPIO_Pin = GPIO_Pin_3;                //USART2_Rx:PA3; b. w! {, C) _) U
  24.         GPIO_Init( GPIOA, &GPIO_InitStruct );
    * _% Z/ p7 m( }4 P& H
  25.         * c5 q1 V+ V0 E( P2 z
  26.         /* 串口配置 */
    3 t' z% P9 e9 l
  27.         USART_DeInit( USART2 );
      V+ U; v. \& M6 H( K1 q
  28.         USART_InitStruct.USART_BaudRate = 9600;                //波特率:9600$ s# G+ f' }0 }" Z
  29.         USART_InitStruct.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
    - n/ {# {. m1 k4 T6 L
  30.         USART_InitStruct.USART_Mode = USART_Mode_Tx|USART_Mode_Rx;# z. w. q& [1 B  U% a# X0 O% J+ [
  31.         USART_InitStruct.USART_Parity = USART_Parity_No;8 E3 n/ y2 w, ~7 m1 x
  32.         USART_InitStruct.USART_StopBits = USART_StopBits_1;2 p7 s/ P% E; X7 v
  33.         USART_InitStruct.USART_WordLength = USART_WordLength_8b;
    : Z2 ]% r5 l: G+ N
  34.         USART_Init( USART2, &USART_InitStruct);' ?- j  o0 l5 J( `$ x
  35.        
    ' g3 V  B1 u' E
  36.         /* 中断配置 */% Z$ L8 B9 X* R8 Q6 _
  37.         NVIC_PriorityGroupConfig( NVIC_PriorityGroup_2 );
    " L1 r4 K* M+ \; r5 T
  38.         NVIC_InitStruct.NVIC_IRQChannel = USART2_IRQn;
    0 `! k0 p+ f3 n" z9 O0 P$ o7 \8 G; b
  39.         NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;
      t  g& @! P% G! i4 l: I
  40.         NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 1;- g  M4 p- A: q+ L7 b8 P5 ~
  41.         NVIC_InitStruct.NVIC_IRQChannelSubPriority = 0;
    $ N% C+ m0 K6 W* {
  42.         NVIC_Init( &NVIC_InitStruct );
    4 |" p2 X0 w/ m
  43.         USART_ITConfig( USART2, USART_IT_IDLE, ENABLE );      //空闲中断# v) Z& [+ Y; M
  44.         USART_DMACmd( USART2, USART_DMAReq_Rx, ENABLE );   //开启DMA接收* s4 \/ V& n. h  o+ p
  45.         ' [' t. x9 U5 h
  46.         8 R7 }7 O0 T8 }; P: i# d5 ]
  47.         /* 配置DMA传输数据 USART2对应DMA1的通道6 方向:外设到存储器*/        * K3 w3 m) ~0 x9 P2 h
  48.     DMA_InitStruct.DMA_PeripheralBaseAddr = USART2_BASE+0x04;              // 设置DMA源地址:串口数据寄存器地址*/        * q* i5 o' D7 V  g9 O1 a
  49.         DMA_InitStruct.DMA_MemoryBaseAddr = (uint32_t)USART2_Rx_Buf;           // 内存地址(要传输的变量的指针)       
    / N0 _) r9 `# Z- K# T7 L
  50.         DMA_InitStruct.DMA_DIR = DMA_DIR_PeripheralSRC;                        // 方向:从外设到内存                ( p+ m4 R$ P+ }
  51.         DMA_InitStruct.DMA_BufferSize = 12;                                    // 传输大小        一帧数据12字节                            
    , l/ f; S$ U. T$ o' g
  52.         DMA_InitStruct.DMA_PeripheralInc = DMA_PeripheralInc_Disable;          // 外设地址不增                / T8 D; n8 Q' X$ Y! b
  53.         DMA_InitStruct.DMA_MemoryInc = DMA_MemoryInc_Enable;                                    // 内存地址自增                8 q0 q; A5 N5 f# O8 q
  54.         DMA_InitStruct.DMA_PeripheralDataSize =         DMA_PeripheralDataSize_Byte; // 外设数据单位                / Y! L4 d  L# k. U' P
  55.         DMA_InitStruct.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;           // 内存数据单位         
    : [9 e, z- x% {" V( M
  56.         DMA_InitStruct.DMA_Mode = DMA_Mode_Circular;                               //正常 DMA模式,一次或者循环模式
    $ [$ Z2 |9 I7 K3 ?
  57.         DMA_InitStruct.DMA_Priority = DMA_Priority_Medium;                     // 优先级:中+ G, J( w2 a+ Q4 t$ d4 C5 u, b6 O$ P
  58.         DMA_InitStruct.DMA_M2M = DMA_M2M_Disable;                              // 禁止内存到内存的传输! Y+ m- J/ ~8 C% [
  59.         DMA_Init( DMA1_Channel6, &DMA_InitStruct );- u$ w. M( X3 a3 L1 s
  60.         DMA_ClearFlag(DMA1_FLAG_TC5);
    % C* Q' ]" H- F$ F
  61.         DMA_Cmd( DMA1_Channel6, ENABLE );//使能DMA
    " b0 I- B9 A) D
  62.        
    4 S6 ~7 r- P/ K# b  h" ~4 D$ B+ T
  63.        
    ! w% y- q. M, s
  64.         /* 使能串口 容易忽略 */
    5 Y2 v% W/ d9 [; ~0 D9 u- Q" x! ^
  65.         USART_Cmd( USART2, ENABLE);6 `8 Q" _, z: D' P
  66.        
    7 H: P0 g0 Q0 T5 J. n+ j
  67. }& }" T3 s% J% K) F% y

  68. 8 `: v9 S) u* `

  69. & J4 _% @2 Q4 N3 j4 N
  70. + M. x; t" p' b$ t! r; j+ Z: A
  71. /* 串口空闲中断服务函数 */4 ^# O/ y# R( s0 c4 e$ j
  72. void USART2_IRQHandler( void )
    0 `$ z) |- }- [! P, N' z; z) {9 I
  73. {2 [1 o$ F! O5 T* L' Z8 \
  74.         if( USART_GetITStatus( USART2, USART_IT_IDLE ) != RESET ) . z/ U( ~8 C- [; b0 k2 t
  75.         {
    ) q: T- v. j1 H: D
  76.                 USART_ReceiveData( USART2 );//象征性接收数据
    1 g/ X  Q- L! Y; T0 J
  77.                 USART_ClearITPendingBit( USART2, USART_IT_IDLE );//清除标志位- y0 l5 z* D* Z, s
  78.                   R4 I' X1 j: Q" y# m
  79.         }
    5 Y! K0 a( ~0 [% l! x8 J6 F( b
  80. }" ~1 T: O( I8 l  }! `/ v
  81. //也可以在中断服务函数中扩展更多的功能
    ; ~, S( }( x% S8 E0 X
复制代码

, X$ V4 }" u" E/ t4 e0 x$ u/ I/ O要点:
$ C+ a6 g% s' Y, V/ a! `要开启串口的空闲中断
7 k& K2 R, M8 l9 A串口的空闲中断中要象征性读取数据和清除标志位
4 Y6 v9 ?! i& V3 O6 y0 ^要开启串口的DMA接收请求
* u/ x3 a2 I% j( e4 R% T
————————————————
# X1 u5 g/ d& Z' Q# C. g$ ?版权声明:Aspirant-GQ
. q1 o1 W4 ]! P! U  k如有侵权请联系删除
8 H, q- V; W0 ~: `. P6 x  @& b! G- X
  f7 z8 w0 C( N2 i* b: x5 A' G; k, d& F5 }
4 b6 m" {& `% P, Q1 [2 n+ O
赞 收藏 评论0 发布时间:2023-3-18 12:11

举报

0个回答

所属标签

相似技术帖

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