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

基于STM32串口的经验总结

[复制链接]
攻城狮Melo 发布时间:2023-3-18 12:11
在日常中,串口经常作为和上位机通信的接口,进行打印信息方便调试程序,有时也会作为模块的驱动接口,所以总结一下串口的几种使用方法对以后的开发还是很有帮助的。
! E: ]8 D/ w* ?3 F) s$ {
$ ~% a; M8 Y; n7 R( f一.仅向上位机打印调试信息
0 U4 U7 y1 p' t, r# A- j; P单纯利用串口向上位机打印调试信息,程序如下:
  d6 e/ E  l3 Q( K  {: ^" C
  1. void USART1_Init( uint32_t btl )
    8 ~. m1 v+ |! |+ N
  2. {& T0 d' e* G( p) |4 G7 q+ m
  3.         GPIO_InitTypeDef  GPIO_InitStruct;
    7 O* C  D, H7 f. j3 {; l
  4.         USART_InitTypeDef USART_InitStruct;
    # j5 e7 E' l0 H  @' G- m: ?
  5.         6 R* ~3 ]- H5 b. _
  6.         RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOA|RCC_APB2Periph_USART1, ENABLE );5 t+ @7 m3 X. n- M, r
  7.        
      d7 V5 k5 a% l  N  _6 M
  8.         GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_PP;
    0 _1 b2 F6 T- D  F
  9.         GPIO_InitStruct.GPIO_Pin = GPIO_Pin_9;//Tx
    ! T" T+ w7 Y, s1 F
  10.         GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
    , Y$ _7 f$ B( ^( G$ |4 c- m4 `
  11.         GPIO_Init( GPIOA, &GPIO_InitStruct );3 |+ {7 [& s* H) r4 l
  12.         GPIO_InitStruct.GPIO_Pin = GPIO_Pin_10;//Rx
    0 t# ~$ A4 c' F6 T
  13.         GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IN_FLOATING;
    0 K! l+ _4 ^: i) ~1 D
  14.         GPIO_Init( GPIOA, &GPIO_InitStruct );
    ' W  O0 T: z2 o: [) E" x* `
  15.        
    ; _& l# X1 b  c8 K" `1 f  O
  16.         USART_InitStruct.USART_BaudRate = btl;
    # k, @- _* h8 {3 l
  17.         USART_InitStruct.USART_HardwareFlowControl = USART_HardwareFlowControl_None;8 C& G! t% }$ L  N" a9 F
  18.         USART_InitStruct.USART_Mode = USART_Mode_Tx;/ R2 y( V5 M/ d4 l5 R
  19.         USART_InitStruct.USART_Parity = USART_Parity_No;+ F5 N+ L5 e  i: Y) ~
  20.         USART_InitStruct.USART_StopBits = USART_StopBits_1;
    & s$ H. Y" l3 D
  21.         USART_InitStruct.USART_WordLength = USART_WordLength_8b;
    ; J, I3 P- x: A! O8 U
  22.         USART_Init( USART1, &USART_InitStruct );
    7 R% c2 i$ j( d$ |( N0 K, i4 P: o, j
  23.        
    # E9 ^6 x+ s" [
  24.         USART_Cmd( USART1, ENABLE );
    ( v/ V$ N2 Y, c# y
  25.        
    / k5 {' u' u) J+ w* F% q. |
  26. }
    ) f  V( P: x- E8 N6 r: T8 f

  27. 7 L- c& y) ^. y) t1 Q' I% ^( E
  28. //串口重定向,直接利用printf函数输出调试信息
    ; e# }$ M8 C3 g. w$ X
  29. int fputc( int ch, FILE *f )
    1 }6 W5 m' ^1 j' Q$ w7 r
  30. {: j8 P" z( n& R3 }' a3 p- c
  31.         USART_SendData( USART1, ( uint8_t )ch );
      i7 X+ _& t3 v. |
  32.         while( USART_GetFlagStatus( USART1, USART_FLAG_TXE )!=SET );
    " P4 X* \) L5 t7 B% e  S3 A  |
  33.         return ch;, X% G6 o( h/ {
  34. }1 C3 z1 \$ m2 u. w) B% {) _
  35. + B3 e8 E+ z  A! q6 @0 ]

  36. 2 B6 U: [; v6 z% k+ u/ _" S
复制代码

, X- F4 }+ i$ q! }, \2 S( p/ ]) n记得包含头文件,勾选Use MicroLIB,以使用printf等函数
% G! z; J+ \2 `6 H7 j: z# Z5 E" o* q1 n( M# o
2020042220264754.png : ~7 d# ]7 b8 u( @
2 I3 W# T/ g- Q- [3 Y, w# r+ I
  @: t, }: m9 [2 Z
二.与上位机交互信息- i& P2 l' K' b' R8 d  q  v
相比于上面的单向通信,有时候需要从上位机接收信息,然后进行反馈,这个时候就使用到串口的中断了。
5 n% f0 i2 {2 U9 E. ]8 n上位机向单片机发送字符串,接收后再发送给上位机:3 h0 Q. V, D  C5 L4 z
  1. " f- q2 o; N8 G1 A9 m1 a  \
  2. void USART1_Init( uint32_t btl )* o" l# W; }+ q  o: B* V
  3. {& r. Y" ~* Q% |& o( p' q
  4.         /* 结构体声明 */
    " K$ G1 K* D8 m; X, J
  5.         GPIO_InitTypeDef  GPIO_InitStruct;- t0 w& W5 T: n  |* n; O! F
  6.         USART_InitTypeDef USART_InitStruct;' Q- j# h. f/ q
  7.         NVIC_InitTypeDef NVIC_InitStruct;* `. T6 i5 ]) {3 {/ r. b4 k
  8.        
    / {7 W4 j, r  q
  9.         /* 打开时钟 */+ ?$ B" i* `' b1 h* J  ?
  10.         RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOA, ENABLE );
    & j5 S# x! i+ f9 D
  11.         RCC_APB2PeriphClockCmd( RCC_APB2Periph_USART1, ENABLE );
    , D' P4 w- C% ~4 c  \
  12.        
    , T# `5 Y7 ]& Q! o
  13.         /* GPIO配置 */
    3 |$ e( b8 o" O- s6 I
  14.         GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_PP;6 m& G! j  R; i3 Y
  15.         GPIO_InitStruct.GPIO_Pin = GPIO_Pin_9;//Tx
    & E: [( ?9 m' @9 h2 O- V: U
  16.         GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
    - i* R1 t0 l* G* Q4 R, u
  17.         GPIO_Init( GPIOA, &GPIO_InitStruct );
    ! h1 t" V% h% y- b/ G0 C
  18.         GPIO_InitStruct.GPIO_Pin = GPIO_Pin_10;//Rx+ i% i( u) D, c) p
  19.         GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IN_FLOATING;! N$ O- ^: [3 N$ A) S; ]/ p
  20.         GPIO_Init( GPIOA, &GPIO_InitStruct );3 [% A2 W5 v9 c" V9 p/ |
  21.         6 ?4 i$ m9 n" R2 w3 C: B
  22.         /* 串口配置 */0 _) U2 t& n# \" ^4 a0 P- l$ k
  23.         USART_InitStruct.USART_BaudRate = btl;5 F* K+ b, z8 Q+ n, ^- m$ _# z
  24.         USART_InitStruct.USART_HardwareFlowControl = USART_HardwareFlowControl_None;. p2 M6 I% P/ u' }
  25.         USART_InitStruct.USART_Mode = USART_Mode_Tx|USART_Mode_Rx;//收发模式* T. H/ x; t! ]* F
  26.         USART_InitStruct.USART_Parity = USART_Parity_No;- N3 @, q2 b' B) E# W
  27.         USART_InitStruct.USART_StopBits = USART_StopBits_1;  K; w9 B/ |0 `
  28.         USART_InitStruct.USART_WordLength = USART_WordLength_8b;
    # |2 L- n" N) W
  29.         USART_Init( USART1, &USART_InitStruct );2 Y# G& y  K; B" P
  30.         $ H. B5 ^- Z9 O( a" b) q% h
  31.         /* 中断配置 */9 [* u' k- X9 n  @' f0 @
  32.         NVIC_InitStruct.NVIC_IRQChannel = USART1_IRQn;) B4 q, }, N: w9 a6 T/ e8 a: ~. c
  33.         NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority=3 ;: A9 M2 X4 K( K- f: p
  34.         NVIC_InitStruct.NVIC_IRQChannelSubPriority = 3;               
    9 W) q8 B& ]  g# e, [
  35.         NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;                       
    1 q6 {- k+ E( r0 g2 ~1 `3 u
  36.         NVIC_Init(&NVIC_InitStruct);       
    / Q4 q) F. B5 V2 Z5 x; ~3 u
  37.         USART_ITConfig( USART1, USART_IT_RXNE, ENABLE );//接收寄存器非空触发中断; I, ^$ g, `$ y
  38.         4 O, |0 H# i1 p0 S/ E( |9 x9 b
  39.         /* 使能串口 */4 m, i/ n7 D$ Z. A! {4 w8 ?: a
  40.         USART_Cmd( USART1, ENABLE );
    4 i& d; }" x/ y" u, ^3 Y! J
  41.        
    ( M+ a" l+ f' v
  42. }
    2 n- k  s5 P& H) m
  43. ; d# y9 w, H; t; Y( c! v0 k, s7 p
  44. 5 E5 @4 p' u  W* r' V2 d) z
  45. volatile uint8_t n=0;
    + l- i, X  V0 z/ u: H
  46. //接收缓冲区5 b# v8 m! V  y4 l
  47. uint8_t USART1_Rx_Buf[100];: s" \2 k3 Q( |: @
  48. void USART1_IRQHandler( void )1 w% M/ ]" A# @1 j5 v- R
  49. {3 V5 A9 ~, _% P3 P3 ]4 D
  50.         if( USART_GetITStatus( USART1, USART_IT_RXNE )==SET )
    0 t, M( U2 ^( [, q/ D" T
  51.         {0 X( f; f# f' Y, x5 b
  52.                         USART1_Rx_Buf[n]=USART_ReceiveData( USART1 );7 q3 S# m0 X/ _5 R
  53.                         n++;
    % C; h) h9 V' ^. }2 r6 G4 m! _7 H
  54.         }       
    6 @- L; b4 K5 K( a5 x# q! G
  55.         USART_ClearFlag( USART1, USART_IT_RXNE );9 l8 s$ F; A, n1 z" |! S' |9 L- G
  56. }: `# G) \" {6 @. {
复制代码
8 r7 x5 ~0 T7 ~3 ]# i8 S
每从上位机中接收一字节的数据,都将数据存储在串口的接收缓冲区USART1_Rx_Buf[100]中。- x7 z5 g/ |* e' Y# _

" E( @4 e  j5 e1 b) J* h

$ z9 i' u! J% t4 D$ [! y三.作为驱动接口
- }- K& t9 q! E( R1 n+ d一些模块的驱动接口就是串口,这个时候就需要单片机从模块中读取指定格式的数据,比如GPS模块,将定位信息从串口发出,单片机解析串口数据,显示在上位机中。
5 U, o4 b2 M: T6 y! ?5 Y6 jusart3用来与GPS模块通信,从GPS模块接收数据,认为10ms内的数据属于一次数据,所以就需要定时器来控制时间。" V4 O" w# |) M4 x
usart3:
0 Q; @' i- L2 `+ T, ?# X
  1. //串口1中断服务程序8 g( ~/ E' [, O9 s: y: N
  2. //注意,读取USARTx->SR能避免莫名其妙的错误          
    % S$ W7 q2 U2 z8 O& K4 Q
  3. u8 USART_RX_BUF[USART_REC_LEN];     //接收缓冲,最大USART_REC_LEN个字节.
    & ?; I2 u, R" U! @$ E
  4. //接收状态/ k% _# x& B! {: K
  5. //bit15,        接收完成标志
    : ^1 A8 X: v* `% t7 W, J& e4 ?
  6. //bit14,        接收到0x0d
    2 \4 ^3 S* W4 |6 n% A7 h* q
  7. //bit13~0,        接收到的有效字节数目0 |3 n  O" [) |
  8. u16 USART_RX_STA=0;       //接收状态标记          
    - u9 W- Y4 _! o6 n: O) A
  9.   
      ^0 \9 n) I" J- V
  10. void uart_init(u32 bound){
    5 n+ D# e3 ?, K5 M# Y
  11.   //GPIO端口设置
    / `% X# K9 D  x( t. c8 E4 m! e* p. W
  12.   GPIO_InitTypeDef GPIO_InitStructure;# U9 m) L2 |0 K% v% R5 V
  13.         USART_InitTypeDef USART_InitStructure;
    * j; Z8 u/ f& N2 J0 |9 q6 Y
  14.         NVIC_InitTypeDef NVIC_InitStructure;
    : ~% ~+ C* H  r* G
  15.        
    ) S" M' v6 F- Y! J1 R& B
  16.         RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1|RCC_APB2Periph_GPIOA, ENABLE);        //使能USART1,GPIOA时钟
    $ ~4 n2 m) S9 z1 f6 I- p
  17.   
    : w6 Y! `4 `( R: p+ z
  18.         //USART1_TX   GPIOA.9
    . I% C, v# L7 k
  19.   GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9; //PA.96 f) h" T( p  K, b7 K" e
  20.   GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    % k% b' T; m# J9 m  ]1 m
  21.   GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;        //复用推挽输出7 D8 d  N. _: \2 y2 G, k  X( O
  22.   GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化GPIOA.9; e/ J/ o; C- L% ~6 P4 a  p" w* `1 Q
  23.    3 X% k8 t+ J  R6 Y" n
  24.   //USART1_RX          GPIOA.10初始化
    " p! K6 ?  w$ G* Q$ k- a9 n
  25.   GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;//PA107 t+ w5 G& {7 p  B* p+ L% U# C
  26.   GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;//浮空输入
    4 N+ z  [1 J8 `3 n1 U& x- Z+ P4 A, U
  27.   GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化GPIOA.10  0 ~7 {+ J7 S7 \0 {9 _; V

  28. ! \$ p- f$ \, n2 w& h
  29.   //Usart1 NVIC 配置6 `/ J1 y3 _0 T' I
  30.   NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
    : d9 t2 |4 W- z& H6 |2 w  w
  31.         NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=3 ;//抢占优先级3! j: O- S, V& m# o' b
  32.         NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3;                //子优先级3
    7 D! w+ p2 M0 d. w! \
  33.         NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;                        //IRQ通道使能0 G8 n0 i, L* B$ ^
  34.         NVIC_Init(&NVIC_InitStructure);        //根据指定的参数初始化VIC寄存器
    - L: b2 O' }9 U/ p6 n  {$ b1 S
  35.   4 r7 U+ a& J+ N( H5 P7 Y
  36.    //USART 初始化设置
    / U3 o" O" o* ]* X% j0 c7 M

  37. ) y! W/ }4 w* z/ [$ Y2 ^) l; p
  38.         USART_InitStructure.USART_BaudRate = bound;//串口波特率
    : _' x& M) q- j
  39.         USART_InitStructure.USART_WordLength = USART_WordLength_8b;//字长为8位数据格式
    2 j! g( z( z7 x. b$ |7 M
  40.         USART_InitStructure.USART_StopBits = USART_StopBits_1;//一个停止位
    - B. _! U) B& L5 x  o
  41.         USART_InitStructure.USART_Parity = USART_Parity_No;//无奇偶校验位. T  C3 m% i4 V. o% o2 h" V9 P7 {
  42.         USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;//无硬件数据流控制; _! o2 O- ]+ q
  43.         USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;        //收发模式
      l; \& ?) x! Q
  44. 6 W' N# @# h! o& U
  45.   USART_Init(USART1, &USART_InitStructure); //初始化串口1
    ; J+ n5 \/ [* I8 z( w
  46.   USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);//开启串口接受中断
    8 A* b  a" `+ ?9 c
  47.   USART_Cmd(USART1, ENABLE);                    //使能串口1
      l4 W0 k4 c/ w! _" V& \

  48. / ]5 j/ m; {5 o
  49. }
    1 ?2 G0 ^8 h2 e* o( d
  50. 1 i) J" I' s" Z) n) D" D+ T
  51. void USART1_IRQHandler(void)                        //串口1中断服务程序
    , R/ F# J3 N) h% s7 {
  52.         {  d& @. q1 E1 J& `* g- D
  53.         u8 Res;
    9 c3 J7 I  T$ e- O* u

  54. * ]& d& n" H, i9 F$ P
  55.         if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET)  //接收中断(接收到的数据必须是0x0d 0x0a结尾)
    2 i2 T# S7 s; [% `) F& N/ e
  56.                 {
    " s0 b6 B; M$ e! e7 @" f$ P
  57.                 Res =USART_ReceiveData(USART1);        //读取接收到的数据
    + w9 _1 J7 L( r. X
  58.                
    ( H$ h' n. {7 c
  59.                 if((USART_RX_STA&0x8000)==0)//接收未完成. N$ b1 |: q8 {- P  A
  60.                         {! W: e, G5 }# H2 o/ ^( o! A
  61.                         if(USART_RX_STA&0x4000)//接收到了0x0d
    / Z( V. q- |0 b- O  \4 {
  62.                                 {+ b$ Z# P% A# }
  63.                                 if(Res!=0x0a)USART_RX_STA=0;//接收错误,重新开始% R6 w/ w4 N, {* H+ y/ {5 O8 f: i
  64.                                 else USART_RX_STA|=0x8000;        //接收完成了 4 Y8 S; o$ j7 S! C% i" R7 k9 g
  65.                                 }1 I/ U/ q% U% F
  66.                         else //还没收到0X0D
    1 _+ U  `0 ]( v8 h8 N3 e1 _
  67.                                 {       
    $ |3 `$ }/ h. P
  68.                                 if(Res==0x0d)USART_RX_STA|=0x4000;
    7 p* ?! k; ~+ O, b
  69.                                 else
    9 K, ]/ B! F2 o* T
  70.                                         {
    : p( W: p& J8 ?9 p3 z; [5 O2 U
  71.                                         USART_RX_BUF[USART_RX_STA&0X3FFF]=Res ;8 \% ~# f& V! R$ u+ h$ X- R
  72.                                         USART_RX_STA++;
    2 k5 K: {$ _4 ?9 s. t- r; u- c8 K
  73.                                         if(USART_RX_STA>(USART_REC_LEN-1))USART_RX_STA=0;//接收数据错误,重新开始接收          
    ' N3 m$ A; N& g. v, o0 k: N
  74.                                         }                 / U9 c8 E0 t1 N+ ^
  75.                                 }
    * P+ ?4 Q* S. Y# p# B- r* g
  76.                         }                    
    2 V4 H# n! a4 T
  77.      } ' u2 L7 e* _9 b! n" }
  78. }
    6 x: \5 e9 s( p) \% ]: {- }4 s
  79. / X8 ~) Y3 w  ?4 K7 u6 [' ^9 k
复制代码
6 S. K" K" L2 f) b
time7:
6 w' Z3 K# G/ H- G, K' G

  1. " v. f2 c% G3 Z  f
  2. extern vu16 USART3_RX_STA;
    6 R2 L1 {8 H& J4 H9 T+ n

  3. , m- o3 q7 P+ B. v5 [5 J: F
  4. //定时器7中断服务程序                    4 x0 S# D% Y+ _
  5. void TIM7_IRQHandler(void)
    9 V- @& b/ c9 ]4 ]" Q# O
  6. {        
    + f! q9 L8 u  I$ G; G8 j: X
  7.         if (TIM_GetITStatus(TIM7, TIM_IT_Update) != RESET)//是更新中断, d1 ]0 m2 k% ]) C, D1 m6 \! ~
  8.         {                                    6 M: d$ \+ F* o$ k& P# f2 w; h
  9.                 USART3_RX_STA|=1<<15;        //标记接收完成9 n. n0 H8 U1 C  g. P! ^
  10.                 TIM_ClearITPendingBit(TIM7, TIM_IT_Update  );  //清除TIM7更新中断标志    , @2 {1 H: ~+ O( `- T8 F
  11.                 TIM_Cmd(TIM7, DISABLE);  //关闭TIM7 ( u* t; D, f# Z" ~% c" L
  12.         }            
    / [' y/ r& a6 c
  13. }
    1 ]: ?6 p  a- x, q+ c6 Q3 P" {" l" k
  14. " L6 n$ ?% w" Z6 c( q2 B. Z1 `6 z, u
  15. //通用定时器7中断初始化2 i* l; Q" ^0 i0 ]+ u% T/ ]) j
  16. //这里时钟选择为APB1的2倍,而APB1为42M
    5 y- k* q  o: Q5 c' T8 M* y
  17. //arr:自动重装值。
    / j+ x! `+ b1 u$ [* o* k* I
  18. //psc:时钟预分频数9 }1 g  ^/ r9 C5 _
  19. //定时器溢出时间计算方法:Tout=((arr+1)*(psc+1))/Ft us.
    7 Z& k6 S8 {& M  o6 `2 w8 q! K
  20. //Ft=定时器工作频率,单位:Mhz
      f% Y8 a* J% d+ A' n- \" |
  21. //通用定时器中断初始化
    5 ]7 M3 i( p' U; S
  22. //这里始终选择为APB1的2倍,而APB1为36M
    6 `' r! I8 G4 V+ e- G
  23. //arr:自动重装值。$ Q( X7 _6 @  q( }  _3 P9 P
  24. //psc:时钟预分频数                 * X# ]4 C6 R9 v1 a3 P% w
  25. void TIM7_Int_Init(u16 arr,u16 psc)
    ( u6 N/ \7 V' g* i2 ?
  26. {       
    * A) R% a5 s, X9 y
  27.         NVIC_InitTypeDef NVIC_InitStructure;0 ^8 X; C4 n2 U7 J! T
  28.         TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;
    , j9 D* g- {3 q6 |0 @0 S
  29. % G1 H8 a7 s4 S7 A( e
  30.         RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM7, ENABLE);//TIM7时钟使能    , N2 w/ S9 |9 ~5 M2 D/ l
  31.        
    5 z, Y$ S0 C$ J- l: m% [* g; Z4 @
  32.         //定时器TIM7初始化
    3 r; |9 l1 o9 Z" ~& K5 K5 q
  33.         TIM_TimeBaseStructure.TIM_Period = arr; //设置在下一个更新事件装入活动的自动重装载寄存器周期的值       
    0 U. e" K8 F: k% u
  34.         TIM_TimeBaseStructure.TIM_Prescaler =psc; //设置用来作为TIMx时钟频率除数的预分频值2 ]' r' F3 c4 n' c& E" f
  35.         TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; //设置时钟分割:TDTS = Tck_tim  t8 J4 G2 @, d4 B$ R2 p
  36.         TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;  //TIM向上计数模式
    & Q7 C2 v- a  g# O8 V! q: x$ f! j1 k
  37.         TIM_TimeBaseInit(TIM7, &TIM_TimeBaseStructure); //根据指定的参数初始化TIMx的时间基数单位
    $ F' s- a5 T  b; m2 t1 e' [: z/ X: D

  38. ' O7 `/ O# [1 c2 R( [' K
  39.         TIM_ITConfig(TIM7,TIM_IT_Update,ENABLE ); //使能指定的TIM7中断,允许更新中断
    0 C+ R& ?  x: @; }& ]$ u
  40.         * o4 p& a: l& X, i5 m5 u
  41.         TIM_Cmd(TIM7,ENABLE);//开启定时器7
    & g" D" q9 N5 H- p; q
  42.        
    * L8 i6 ]6 w0 x# z2 b6 N
  43.         NVIC_InitStructure.NVIC_IRQChannel = TIM7_IRQn;
    1 s9 p0 h# t" h  S
  44.         NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=0 ;//抢占优先级0
    + j* ^7 ~7 x+ s1 E; |: A5 z: D
  45.         NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2;                //子优先级2
    7 t* a/ B# b" H8 K
  46.         NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;                        //IRQ通道使能
    ; t, z1 U; h0 ^2 B/ J
  47.         NVIC_Init(&NVIC_InitStructure);        //根据指定的参数初始化VIC寄存器
    1 o) S4 ^- J. T& U) g
  48.        
    0 }9 l/ h- x6 b9 t& Y4 J
  49. }
    4 I) U) {$ c( W" M
  50.          
    - u/ T$ n  R* ^' y: T5 O
复制代码
/ y. j: a5 z! _" O" }1 U* o
四.结合DMA接收数据帧3 y/ X& {" \1 Q6 o( k+ [
当单片机从串口上接收数据时,一般我们都是接收一个字节数据,进入一次中断,在中断中处理数据或者做标记,这种方法虽然简单,但是对于大量数据的情况,频繁地进入中断处理数据就占有了CPU的宝贵资源。1 A( g4 r1 D: l
这个时候我们可以使用DMA来接收数据,DMA接收数据的好处就是省CPU资源,DMA仅仅在初始化的时候占用一下CPU资源,其他操作都是在DMA的控制器来完成的。
6 T, M  p% ^) I" [8 z& w" [2 L% T+ B; V) y, z* L1 W6 G# w) Y+ `
使用背景:单片机从USART2中接收传感器传回的数据帧(12字节),传感器每秒上报一次数据,要求单片机可以收到完整数据并且可以对数据进行处理。
$ u9 P2 w& s8 u
5 N/ s' \* s* H  R7 V' i程序框架:以往串口接收数据都是判断数据寄存器非空的,一字节一字节地接收,现在使用DMA,使每一次数据寄存器的值都自动传给内存指定地址(也就是指定的数据缓冲区),当串口接收完一帧数据后,会触发空闲中断,这意味着一帧数据的接收完成,我们只需要在数据缓冲区中对数据进行处理即可。
* d, }4 I0 k$ L0 l5 g8 ]" W) ?
/ a; X) j  J% Z0 {$ [6 ~! p代码:
& m6 V( |; _/ q
  1. * z5 D  s: h/ q6 x3 F! I- K; J
  2. uint8_t USART2_Rx_Buf[12];
    ! S, J5 s% F8 u3 {
  3. + {( X& U4 @  D4 P
  4. void USART2_Config( void )
    # w- G) M7 |$ h+ o
  5. {
    ! ]# ?5 h6 w, \
  6.         /* 声明各结构体 */+ c8 `" f+ T& I* M
  7.         GPIO_InitTypeDef GPIO_InitStruct;+ Y' f1 N3 o3 @( W; R9 i
  8.         USART_InitTypeDef USART_InitStruct;
    2 C. q4 j& j. d' n1 i2 a5 m) z
  9.         NVIC_InitTypeDef NVIC_InitStruct;
    + Z% E" k% `; N$ j5 q
  10.         DMA_InitTypeDef DMA_InitStruct;
    - J! Y) }) C% e# {% C3 J
  11.        
    0 |1 s6 `/ _, y4 d
  12.         /* 打开时钟 */
    ! n4 M2 B  p& }$ n  M( o$ h8 l7 A
  13.         RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOA, ENABLE );
    9 W& [% P8 f' N  L
  14.         RCC_APB1PeriphClockCmd( RCC_APB1Periph_USART2, ENABLE );3 s+ G0 p& K1 C3 h( ?
  15.         RCC_AHBPeriphClockCmd( RCC_AHBPeriph_DMA1, ENABLE );% i7 Z( \$ m9 u4 S- W
  16.        
    ! z3 F: V6 G- v2 ]2 a6 q
  17.         /* GPIO配置 */% J/ ~' q6 C9 Y% J- n# t
  18.         GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_PP;3 E6 U9 V# J7 D: i2 |+ p6 W* J. _* j
  19.         GPIO_InitStruct.GPIO_Pin = GPIO_Pin_2;                 //USART2_Tx:PA2
    8 i% Z2 s9 X3 q4 s
  20.         GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;, e$ K& Z  T* K7 r- l0 S1 a& ^+ Y% j
  21.         GPIO_Init( GPIOA, &GPIO_InitStruct );! u# w" `% d0 N- p$ A2 _$ {
  22.         GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IN_FLOATING;- t+ K: u& Q1 Z7 s
  23.         GPIO_InitStruct.GPIO_Pin = GPIO_Pin_3;                //USART2_Rx:PA3$ K$ M' Z- E+ e5 O5 h
  24.         GPIO_Init( GPIOA, &GPIO_InitStruct );
    : R4 f3 s& u' L
  25.         / F9 a+ o' i9 x% _/ N
  26.         /* 串口配置 */
    0 z0 o2 b! {8 Z+ b/ ?
  27.         USART_DeInit( USART2 );
    1 f# P9 A0 s6 F# Y
  28.         USART_InitStruct.USART_BaudRate = 9600;                //波特率:9600
    5 G9 J: B+ Q( Q
  29.         USART_InitStruct.USART_HardwareFlowControl = USART_HardwareFlowControl_None;& {5 V) L3 V( h8 \
  30.         USART_InitStruct.USART_Mode = USART_Mode_Tx|USART_Mode_Rx;
    0 _3 A  h% x& {7 x6 W+ ^; Q
  31.         USART_InitStruct.USART_Parity = USART_Parity_No;
    * @+ e" o7 w8 B, ~" F- w
  32.         USART_InitStruct.USART_StopBits = USART_StopBits_1;
    * g: c6 d7 `' N2 ]& U) s
  33.         USART_InitStruct.USART_WordLength = USART_WordLength_8b;) D3 q, S$ K$ ^
  34.         USART_Init( USART2, &USART_InitStruct);; K$ T6 \- ]0 z
  35.        
    4 |) B& Z; ]! l% u3 s
  36.         /* 中断配置 */( p% n+ i$ i* p9 R4 _" z/ v
  37.         NVIC_PriorityGroupConfig( NVIC_PriorityGroup_2 );
      S+ w; R; @% _4 [
  38.         NVIC_InitStruct.NVIC_IRQChannel = USART2_IRQn;
    3 M5 _1 [- t* G0 z
  39.         NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;
    / y) `' e5 B; c5 p
  40.         NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 1;/ O: I/ G% s' m9 k/ e: I
  41.         NVIC_InitStruct.NVIC_IRQChannelSubPriority = 0;
    3 K! i9 Y5 j3 H, L0 M- b! X: u$ m
  42.         NVIC_Init( &NVIC_InitStruct );5 [. f5 `# m+ G% u2 g- Z' Z9 v
  43.         USART_ITConfig( USART2, USART_IT_IDLE, ENABLE );      //空闲中断8 {, }4 v3 f% |) T/ J8 `/ K
  44.         USART_DMACmd( USART2, USART_DMAReq_Rx, ENABLE );   //开启DMA接收  Y- @: c, f4 w' {/ e9 I5 r1 U: c5 c
  45.         9 g9 K8 q# G( p/ G2 M
  46.        
    8 `  r3 z1 |7 `% O% ~* D" i" r. T
  47.         /* 配置DMA传输数据 USART2对应DMA1的通道6 方向:外设到存储器*/       
    4 E1 J6 W5 z2 p- p
  48.     DMA_InitStruct.DMA_PeripheralBaseAddr = USART2_BASE+0x04;              // 设置DMA源地址:串口数据寄存器地址*/        8 b( w% h+ J  \6 l
  49.         DMA_InitStruct.DMA_MemoryBaseAddr = (uint32_t)USART2_Rx_Buf;           // 内存地址(要传输的变量的指针)       
    : A+ i% s! V& K; ?7 S
  50.         DMA_InitStruct.DMA_DIR = DMA_DIR_PeripheralSRC;                        // 方向:从外设到内存               
    3 k' g' ^6 J3 v$ g
  51.         DMA_InitStruct.DMA_BufferSize = 12;                                    // 传输大小        一帧数据12字节                            $ V& t6 J  [& Y  j% {0 U
  52.         DMA_InitStruct.DMA_PeripheralInc = DMA_PeripheralInc_Disable;          // 外设地址不增                # j7 v' N& ]- H. m* b6 }. T9 V+ w
  53.         DMA_InitStruct.DMA_MemoryInc = DMA_MemoryInc_Enable;                                    // 内存地址自增                ( }* }# N0 k6 N8 Q  ~4 ?3 |
  54.         DMA_InitStruct.DMA_PeripheralDataSize =         DMA_PeripheralDataSize_Byte; // 外设数据单位                6 ~. b. O; u$ C
  55.         DMA_InitStruct.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;           // 内存数据单位         : E" W; m0 w+ C5 k0 _
  56.         DMA_InitStruct.DMA_Mode = DMA_Mode_Circular;                               //正常 DMA模式,一次或者循环模式
    0 P# R" D" ~- M5 |* T
  57.         DMA_InitStruct.DMA_Priority = DMA_Priority_Medium;                     // 优先级:中8 O& p6 N& d8 u0 r. u" _2 m1 f
  58.         DMA_InitStruct.DMA_M2M = DMA_M2M_Disable;                              // 禁止内存到内存的传输
    / }8 z/ u) p- \# p9 b9 b
  59.         DMA_Init( DMA1_Channel6, &DMA_InitStruct );0 b& C, v/ ^' t
  60.         DMA_ClearFlag(DMA1_FLAG_TC5);5 M3 @& K, j& }7 N- X
  61.         DMA_Cmd( DMA1_Channel6, ENABLE );//使能DMA
    - j8 P0 D5 r; c9 V( @
  62.         / @& M# Z" A! P1 l
  63.         ! d) ?) m  n) d" \+ |
  64.         /* 使能串口 容易忽略 */8 Q$ |/ o/ p; x0 t7 ^( j
  65.         USART_Cmd( USART2, ENABLE);
    * V2 G1 q1 W/ v
  66.        
    / M/ H' v+ k% T, A" h
  67. }
    ( B" u. \1 n' A1 |3 m7 }
  68. : _: g" y% ^+ I1 O& }" K
  69. " Y6 E2 \0 e1 Y, m9 M5 x

  70. 5 G1 M/ c4 C) O" ~; Z, z" }; c5 i
  71. /* 串口空闲中断服务函数 */: q0 K" Z0 \' K
  72. void USART2_IRQHandler( void )
    # s  b" J  n" s$ R/ e) ~1 z1 B1 l
  73. {
    $ V0 H5 X/ ]3 n0 o8 ?2 [
  74.         if( USART_GetITStatus( USART2, USART_IT_IDLE ) != RESET ) : e  N6 t1 I' o+ r
  75.         {
    * T% l- Y$ c6 b: U5 M
  76.                 USART_ReceiveData( USART2 );//象征性接收数据# ?. x. ?8 f- h! w# c, h
  77.                 USART_ClearITPendingBit( USART2, USART_IT_IDLE );//清除标志位
    ( H7 |7 G: u/ S/ R, T
  78.                
    2 ~! i0 n3 A1 X: ?) P4 a! v
  79.         }
    7 k( n( f1 j7 s! b
  80. }
    : g  {! _: n% G3 d' S
  81. //也可以在中断服务函数中扩展更多的功能
    ' }% e& m% n- b, ?+ O
复制代码

# m6 \! V7 A3 U6 C' l. x' H要点:1 N9 \2 g: `' e2 i! T8 L; _
要开启串口的空闲中断+ _6 S; u9 w0 [! _
串口的空闲中断中要象征性读取数据和清除标志位  O1 J" Z' W3 Z0 w
要开启串口的DMA接收请求& c% i; M8 K3 I, A" ?1 H, W) h) ~
————————————————" _% D6 Y; P1 K4 u; r- S
版权声明:Aspirant-GQ, F5 u" y( [$ M+ u9 V5 o( s
如有侵权请联系删除
; Z  Y" v0 S: \- V. V5 e& Z3 m" B' M; F2 {. \, W' h

6 Q' i& Y) N& |/ M- `4 Q4 j" G+ c2 ?4 K. _0 h
收藏 评论0 发布时间:2023-3-18 12:11

举报

0个回答
关于意法半导体
我们是谁
投资者关系
意法半导体可持续发展举措
创新和工艺
招聘信息
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
关注我们
st-img 微信公众号
st-img 手机版