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

【经验分享】STM32单片机串口空闲中断+DMA接收不定长数据

[复制链接]
STMCU小助手 发布时间:2022-3-21 13:41
利用串口空闲中断接收不定长数据,这种方式有一个问题就是串口每接收到一个字节就会进入一次中断,如果发送的数据比较频繁,那么串口中断就会不停打断主程序运行,影响系统运行。那么能不能在串口接收数据过程中不要每接收一个数据中断一次,只有在一帧数据接收结束完成后只中断一次?
7 l) U& M& h. M5 o
; c, Q/ t, X" }$ Q) `0 f1 ~9 a        用串口的空闲中断加上DMA功能,就可以实现每帧数据接收完成后只中断一次,而在数据接收过程中,由DMA存储串口接收到的每个字节。. F; r: ^% g" \( h, K- o' I

9 O7 t9 {3 G: v+ y7 o" O    关于串口的空闲检测和DMA在STM32参考手册中有详细介绍。
9 c2 }# s1 h8 d3 A- c) X# o2 T) ]6 Z0 p7 ~
Y@(F)F8}P%E%Z}K}INYTTRC.png 7 R0 y9 j: g$ U7 _# r3 t$ Y
, T& p2 V' B, x+ |9 H* H4 _
`C@``AFRCDQ6U16O9DJC305.png
0 ^" g+ [# k" T% i$ K, T' J1 y
* h3 r5 o, |+ S4 X% f; z- ] EY7[T[7%8(BBT54Z(EPA`28.png - N" L7 P- N; c1 h3 o7 }8 ~, x% ?% q
+ l" O9 j% ]* M$ V0 p+ @
下面看如何初始化串口空闲中断和 DMA。6 N; \! m; I  x3 M$ ?# V$ h4 ~. o

) D/ k1 H; h4 E4 c5 G& h/ |" x
  1. void  uart2_init( u16 baud )% _$ x9 N8 B  r  K
  2. {' F) x1 y0 D( `1 _6 \
  3.     GPIO_InitTypeDef GPIO_InitStructure;& h' y, d; h1 V; e, J- W
  4.     USART_InitTypeDef USART_InitStructure;! x( x6 p  e* _
  5.     NVIC_InitTypeDef  NVIC_InitStructure;
    9 ?2 v1 p' l$ g* {* N4 r6 D
  6. % \" J0 q/ c9 a3 L: N, E
  7.     RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOA, ENABLE );  R- u6 @$ K: a; x# D' k7 p, ?8 }
  8.     RCC_APB1PeriphClockCmd( RCC_APB1Periph_USART2, ENABLE );
    1 c& O6 `. i( n

  9. ) A1 {9 C- d0 A' M
  10.     GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2;( V' B' {+ j( b5 F/ o' [
  11.     GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;                                        //推挽复用模式
    5 V# y6 S* _3 J9 ^1 I& q9 [9 p
  12.     GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    / J7 H+ ]; w! X, r
  13.     GPIO_Init( GPIOA, &GPIO_InitStructure );
    8 B+ R  V& U. y
  14. 6 q0 v5 l+ f- ^& f: S8 ~: A1 Z
  15.     GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3;
    9 }3 e: e/ J9 l4 a( o
  16.     GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;                //浮空输入模式
    / a; f7 }" @  S+ s/ `
  17.     GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    3 ~7 `3 j# F: ?. X3 j' C- ^/ N( w
  18.     GPIO_Init( GPIOA, &GPIO_InitStructure );
    " H: v/ c4 _* }/ S8 V) c* u

  19. 6 m6 A; {; ]9 Y) j- u# \
  20.     NVIC_InitStructure.NVIC_IRQChannel = USART2_IRQn;/ i- i6 ^: W0 f  _8 b1 r1 a
  21.     NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x02;
    6 G! e/ s9 J, J! ]1 }
  22.     NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x02;( j1 P9 D' o! o, e7 n0 N6 h
  23.     NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;' @, z9 E$ J7 b& C  M  q+ C

  24. 5 d* P+ @( m, V2 s7 ?
  25.     NVIC_Init( &NVIC_InitStructure );
    ' `% D# O4 D$ ^# R

  26. ) N  q# U" |% k# |, f
  27.     USART_InitStructure.USART_BaudRate = baud;6 B& U5 X5 p  S& i; S8 ]
  28.     USART_InitStructure.USART_WordLength = USART_WordLength_8b;/ _  _4 }* h7 x
  29.     USART_InitStructure.USART_StopBits = USART_StopBits_1;
    1 c; m7 p! O' M$ a) k  D
  30.     USART_InitStructure.USART_Parity = USART_Parity_No;
    9 s! j) e0 o1 D/ H
  31.     USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
    & R5 C+ b" G; ^! Q' x) F
  32.     USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
    2 |7 d  A3 z$ y
  33. 0 F8 d$ j' \+ `& e
  34.     USART_Init( USART2, &USART_InitStructure );
    0 {3 \0 Y7 O6 w% L" c5 m
  35. #if (UART2_DMA == 1)2 H+ a: s* X+ @7 G& t2 i
  36.     USART_ITConfig( USART2, USART_IT_IDLE, ENABLE );                                                                                         //使能串口空闲中断' R. O0 y% m3 g( c
  37.     USART_DMACmd( USART2, USART_DMAReq_Rx, ENABLE );                                                                                         //使能串口2 DMA接收# E& ]6 |1 o  ]" C
  38.     uartDMA_Init();                                                                                                                                                                                                                                 //初始化 DMA0 T& b0 k8 o3 C+ z( |! r' Q
  39. #else
    ' A, S6 U4 @. G; R1 w4 M
  40.     USART_ITConfig( USART2, USART_IT_RXNE, ENABLE );                                                                                   //使能串口RXNE接收中断
    * u. x' T! Y3 c8 S0 @8 G
  41. #endif
    : Q8 Z' _1 {. z7 t
  42.     USART_Cmd( USART2, ENABLE );                                                                                                                                                                         //使能串口29 {' _/ T0 x. u. c
  43.                 6 ~) L* _- j: {9 V
  44. //RXNE中断和IDLE中断的区别?
    & }* W/ W1 u  r# r) X
  45. //当接收到1个字节,就会产生RXNE中断,当接收到一帧数据,就会产生IDLE中断。比如给单片机一次性发送了8个字节,就会产生8次RXNE中断,1次IDLE中断。
    0 Q9 `: W# O. K% P6 c5 B, l
  46. }
复制代码
# c( C' M9 s5 O( I$ b' y4 N0 e
为了方便对比空闲中断和DMA与常规串口设置的差别,这里用了一个宏定义UART2_DMA来设置是否需要开启空闲中断和DMA,如果宏定义UART2_DMA值为1,那么就初始化空闲中断和DMA。这里要将UART2_DMA设置为1,需要开启串口2的IDLE中断,使能串口2的DMA功能,然后初始化 DMA。$ ?( ~8 H- |& b
# W: y( e: r- v( f% G& {+ A" x
  1. void uartDMA_Init( void )
    2 {8 N, J( ?3 A
  2. {6 o# N# {2 P( b/ K* }
  3.     DMA_InitTypeDef  DMA_IniStructure;( u. s1 y' c+ @! r2 Y' }
  4. ; f, Q9 U% J6 N, V
  5.     RCC_AHBPeriphClockCmd( RCC_AHBPeriph_DMA1, ENABLE );                                                                          //使能DMA时钟
    . S: ~: x  ]4 p0 d

  6. " W* j( U" l: [+ g/ a9 A% Q
  7.     DMA_DeInit( DMA1_Channel6 );                                                                                                                                                             //DMA1通道6对应 USART2_RX
    " [( |) o) @# F; s0 g4 n
  8.     DMA_IniStructure.DMA_PeripheralBaseAddr = ( u32 )&USART2->DR;                                         //DMA外设usart基地址- u2 j, M; j0 c* j6 \# j1 q
  9.     DMA_IniStructure.DMA_MemoryBaseAddr = ( u32 )dma_rec_buff;                                                  //DMA内存基地址. C! i$ H% D/ K9 K/ n, O0 b
  10.     DMA_IniStructure.DMA_DIR = DMA_DIR_PeripheralSRC;                                                                                         //数据传输方向,从外设读取发送到内存
    * o9 i  x* _4 F0 f
  11.     DMA_IniStructure.DMA_BufferSize = DMA_REC_LEN;                                                                                                 //DMA通道的DMA缓存的大小  也就是 DMA一次传输的字节数( p9 @% U: E5 i" k1 z& J
  12.     DMA_IniStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;                                 //外设地址寄存器不变' D! @( j& r8 `
  13.     DMA_IniStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;                                                                 //数据缓冲区地址递增* K+ z0 H  C  {7 E3 b& q
  14.     DMA_IniStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte; //外设以字节为单位搬运2 v! h& C0 ~6 l
  15.     DMA_IniStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;                                  //数据缓冲区以字节为单位搬入
    . v/ ]  e6 F# G" t$ P* P
  16.     DMA_IniStructure.DMA_Mode = DMA_Mode_Normal;                                                                                                         //工作在正常缓存模式
    , j' w, D3 y9 ]
  17.     DMA_IniStructure.DMA_Priority = DMA_Priority_Medium;                                                                         //DMA通道 x拥有中优先级2 ]4 }$ _5 g5 m! P  K6 x+ i
  18.     DMA_IniStructure.DMA_M2M = DMA_M2M_Disable;                                                                                                                 //DMA通道x没有设置为内存到内存传输2 D8 F1 O' m0 J7 Q+ h
  19. 9 q  {: O$ v; a! M& [8 d
  20.     DMA_Init( DMA1_Channel6, &DMA_IniStructure );
    ( l4 S+ M7 e5 _* q
  21.     DMA_Cmd( DMA1_Channel6, ENABLE );
    5 k1 g  b1 {' L4 l0 x
  22. }
复制代码
' {$ I' B$ m. {# e  z7 k$ e$ n- R7 r
        将 DMA的外设地址设置为串口2的数据寄存器 USART2->DR,DMA的内存地址设置为 DMA数据缓存数数组dma_rec_buff。这样当串口2的数据寄存器中有数据后,DMA就会自动将这个数据存储到dma_rec_buff数组中。当串口2的空闲中断出现后,直接去dma_rec_buff数组中读取接收到的数据就行。7 [; H5 b. P% f: t0 c! X0 k# Y' Q
! `1 |. U# y0 c5 N( X( _
下面看串口2的中断执行过程
  e% ^) @- D* [  v% d' \9 @$ j& U; M" }. c( X$ T
  1. void USART2_IRQHandler( void )$ D1 e0 }6 q( l  n' K
  2. {
    . Z4 S$ `' J" O0 A1 k: P8 l
  3.     u8 tem = 0;- s+ ?1 w+ |* i4 c2 E
  4. #if (UART2_DMA == 1)                //如果使能了 UART2_DMA 则使用串口空闲中断和 DMA功能接收数据6 I- X$ X" V9 t3 l/ ~  [( N
  5.         if( USART_GetITStatus( USART2, USART_IT_IDLE ) != RESET )//空闲中断  一帧数据发送完成
    ; `; D# ?. ]8 \# P6 @
  6.     {/ |4 [+ L4 u/ a& t3 t+ H$ l
  7.         USART_ReceiveData( USART2 );        //读取数据注意:这句必须要,否则不能够清除空闲中断标志位。6 \5 ~  w  m, R1 V- \. A
  8.         DMA_Cmd( DMA1_Channel6, DISABLE );        //关闭 DMA 防止后续数据干扰9 a8 r8 |! l. \  T( k
  9.         uart2_rec_cnt = DMA_REC_LEN - DMA_GetCurrDataCounter( DMA1_Channel6 );  //DMA接收缓冲区数据长度减去当前 DMA传输通道中剩余单元数量就是已经接收到的数据数量      
    * h9 K' a3 _) Y% G+ R
  10.             copy_data( dma_rec_buff, uart2_rec_cnt );                //备份数据3 J# f0 }# _4 `" ?& p0 X$ Y5 {* P
  11.         receiveOK_flag = 1;                                                                //置位数据接收完成标志位/ O0 j2 {) F8 I* G+ u0 x
  12.         USART_ClearITPendingBit( USART2, USART_IT_IDLE );        //清除空闲中断标志位
    ! W9 q8 p5 D0 ?1 p7 J! m0 U
  13.         myDMA_Enable( DMA1_Channel6 );        //重新恢复 DMA等待下一次接收) ]: L4 J1 Q2 n/ @8 l
  14.     }3 ?3 }. q* ~) b9 Q4 U4 |
  15. #else                        //如果未使能 UART2_DMA 则通过常规方式接收数据  每接收到一个字节就会进入一次中断) g3 e/ B" ^$ B% P) s" z
  16.     if( USART_GetITStatus( USART2, USART_IT_RXNE ) != RESET )                //接收中断; d' O6 Z5 b9 h( g8 Z# E+ T
  17.     {
      o! J- M6 t! @3 Z+ u
  18.         tem = USART_ReceiveData( USART2 );  F7 M; ^6 N* S. ]
  19.         USART_SendData( USART2, tem );
    7 ~1 A: [, |# a% y3 O
  20.     }
    ( @/ s$ n! W" b" O: A
  21. #endif- M1 e0 d5 k; a6 J- {$ I4 A6 N
  22. }
复制代码
7 d% B3 @2 H6 S( T9 ^: ]
       在串口中断中如果检测到了空闲中断标志,说明串口接收一帧数据结束,这时候就关闭 DMA,然后计算DMA缓冲区中接收到的字节个数,将接收到的数据拷贝到备份数组中,然后置位数据接收成功标志最后清除中断标志位,重新启动 DMA,开始进行下一次数据接收。
3 k  b% T! ^7 D" V& c' J# d4 {2 v8 N, U) R/ y. e& Y7 c
       如果串口数据的接收频率不是很高,在这里也可以不用备份DMA缓存区接收到的数据,备份数据主要是防止,数据接收频率很高,当上一笔数据还没处理完成后,又接收到了新的数据,那么数组中的数据就会被覆盖,可能导致程序异常。所以将处理数据的数组和接收数据的数组分开,可以防止在处理数据过程中数据被改变的情况发生。4 d  n" K, b% Q: V1 v2 \/ }- w

$ {& M9 R$ F/ M6 f  m0 v% ?: q下面看一下完整代码; w; c! N, N2 K5 G0 t+ J2 _/ f

: w7 A5 A- s6 f1 h# g% n1 [" Q, H7 M
  1. #ifndef __UART2_H
    * h/ d  m& \) I- ?
  2. #define __UART2_H
    ' D* c9 f% {! r* E0 J7 F( q' R
  3. #include "sys.h"
    ) D* _" d4 z$ ~/ u
  4. & D8 ~3 t- c0 B' R" m$ E
  5. #define DMA_REC_LEN 50                                                                                                                                                        //DMA数据接收缓冲区
    ' p9 _1 Q( D$ J6 A2 |; \- l, p

  6. + p0 c; A* p  M3 t/ C& A
  7. void  uart2_init(u16 baud);! u" X! ]& x5 l3 t1 r2 d6 e: P" u# u
  8. void uartDMA_Init( void );
    - p, W- `3 V7 p0 f, S
  9. void myDMA_Enable( DMA_Channel_TypeDef*DMA_CHx );
    0 t/ o& E2 b) L2 U
  10. void uart2_Send( u8 *buf, u16 len );
    7 d1 ]$ H1 O& @
  11. void copy_data( u8 *buf, u16 len );
    ( [, M9 c$ X4 p/ _( P
  12. #endif
复制代码
  1. //串口2空闲中断 + DMA数据传输% y8 H& ^8 U( p- v
  2. #include "uart2.h"8 c7 w$ J1 P3 O3 z( u" o9 c
  3. #define UART2_DMA  1                                                                                                                                                                //使用串口2  DMA传输
    # P( s' ~; e" O% B/ ~0 ?/ y4 q

  4. # e# s$ b! w' {9 @3 Q
  5. u8 dma_rec_buff[DMA_REC_LEN] = {0};- R& V# U+ I; n3 h! @) O: H
  6. u16 uart2_rec_cnt = 0;                                                                                                                                                        //串口接收数据长度
    ( c2 _% f, j/ O% U! i. w

  7. # s+ r: o4 z; y6 K4 Q% x
  8. u8 data_backup[DMA_REC_LEN] = {0};                                                                                                         //数据备份( s; A( }; D3 U
  9. u16 dataLen_backup = 0;                                                                                                                                                        //长度备份; g" J1 b2 y' y9 l7 a
  10. _Bool receiveOK_flag = 0;                                                                                                                                                //接收完成标志位2 z( C! Z( B$ D4 b5 j/ \
  11. ' g- [: U3 Q: U8 R1 T
  12. /*
    # r: o4 d( O: t1 b* O
  13. 空闲中断是什么意思呢?" D' J! q% r) |  _: a4 @& h
  14. 指的是当总线接收数据时,一旦数据流断了,此时总线没有接收传输,处于空闲状态,IDLE就会置1,产生空闲中断;又有数据发送时,IDLE位就会置0;
    0 @* t: m) @8 u7 E, E
  15. 注意:置1之后它不会自动清0,也不会因为状态位是1而一直产生中断,它只有0跳变到1时才会产生,也可以理解为上升沿触发。9 T8 e4 P5 w/ K: t; }" a5 N$ u
  16. 所以,为确保下次空闲中断正常进行,需要在中断服务函数发送任意数据来清除标志位。! t( F' ^! X, j$ Z7 W2 k
  17. */) p* W% p) y; i6 P, k

  18. ' o5 [$ E5 q0 X/ c6 f( C
  19. void  uart2_init( u16 baud )
      U, @3 R9 V$ P8 I
  20. {
    ! J+ i# N7 R0 h+ s. h3 ~
  21.     GPIO_InitTypeDef GPIO_InitStructure;) ~- s" c: P1 |9 E% k4 d
  22.     USART_InitTypeDef USART_InitStructure;
    ; Q6 b/ W. R" o
  23.     NVIC_InitTypeDef  NVIC_InitStructure;
    . I* [& J. n# x6 [" o
  24. 2 R9 i1 b3 y: }  a3 `& F; V
  25.     RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOA, ENABLE );: A$ \( n1 V6 n% K, H, B
  26.     RCC_APB1PeriphClockCmd( RCC_APB1Periph_USART2, ENABLE );3 o3 W7 S) S3 ?0 V

  27. 0 K2 V( n9 u6 m
  28.     GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2;
    % ?# Z/ N/ k8 W" T
  29.     GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;                                        //推挽复用模式/ b2 k# ~. X& C2 G. J; C# F
  30.     GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
      w4 ^7 k3 O8 @8 y
  31.     GPIO_Init( GPIOA, &GPIO_InitStructure );
    5 i5 o& O2 B, H5 _
  32. , H3 L- u- v4 g" J
  33.     GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3;
    " \" k+ `4 H9 N7 g" m
  34.     GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;                //浮空输入模式5 p8 W7 B8 P7 q6 N: x. z; z- Z" u
  35.     GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    2 m: i' ~/ R: a/ \3 A
  36.     GPIO_Init( GPIOA, &GPIO_InitStructure );
    & n$ o! g* j2 E3 ?) Z& r6 T

  37. % P7 [' b0 Q! ^. l0 @5 E) g1 X
  38.     NVIC_InitStructure.NVIC_IRQChannel = USART2_IRQn;
    , K5 g( v2 k3 R
  39.     NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x02;8 l! m# J1 L- G$ D& ^9 S
  40.     NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x02;' ?7 Z8 E( h# M: j
  41.     NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;# |5 |' x0 f% T2 M  B

  42. - D- ~2 \: u& w+ R
  43.     NVIC_Init( &NVIC_InitStructure );
    7 t: d2 n9 l  N$ R0 F5 j7 i  a( m

  44. $ }7 V% v- K9 Y1 l' |9 G" K
  45.     USART_InitStructure.USART_BaudRate = baud;
    ; H3 \5 z! n6 Y
  46.     USART_InitStructure.USART_WordLength = USART_WordLength_8b;- Y; L& `" z8 V* g6 g& P  I" A
  47.     USART_InitStructure.USART_StopBits = USART_StopBits_1;5 y' x" c* i# a" w8 |$ A
  48.     USART_InitStructure.USART_Parity = USART_Parity_No;
    . u; Q0 R* A' B" C. A
  49.     USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;  i0 Y( @3 A4 a2 M3 I  F
  50.     USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;6 a; @6 ?( o9 A2 r' N

  51. * H; z+ B/ h; P4 z4 {
  52.     USART_Init( USART2, &USART_InitStructure );
    7 q. J) @# P/ o1 g1 ~
  53. #if (UART2_DMA == 1)- T: x! g- \6 M: H* U
  54.     USART_ITConfig( USART2, USART_IT_IDLE, ENABLE );                                                                                         //使能串口空闲中断0 f6 O& `. L; n. J( V
  55.     USART_DMACmd( USART2, USART_DMAReq_Rx, ENABLE );                                                                                         //使能串口2 DMA接收
    ( J, l# W6 o2 w" `9 X5 V. `
  56.     uartDMA_Init();                                                                                                                                                                                                                                 //初始化 DMA9 z& H! s2 L7 L9 O4 i
  57. #else
    ; U, b4 k) `- F2 F
  58.     USART_ITConfig( USART2, USART_IT_RXNE, ENABLE );                                                                                   //使能串口RXNE接收中断- V! b- }' Z' e  X
  59. #endif7 M, S1 t3 `( J& y7 W2 w" @" O% l7 V
  60.     USART_Cmd( USART2, ENABLE );                                                                                                                                                                         //使能串口2
    % c7 O! l2 e# K% }
  61.                
    * s( L% O* s0 f& w# C1 l
  62. //RXNE中断和IDLE中断的区别?8 \' J* y/ q" g
  63. //当接收到1个字节,就会产生RXNE中断,当接收到一帧数据,就会产生IDLE中断。比如给单片机一次性发送了8个字节,就会产生8次RXNE中断,1次IDLE中断。# T* Z: ^/ k3 T
  64. }2 t, _% W, z! }; Q/ ?9 B9 l3 w8 v
  65. ' ^; r/ K, e' m' h' s4 O$ g
  66. void uartDMA_Init( void )
      A9 Z' m+ U9 P' H/ E3 {. p
  67. {! O8 e& @8 p" n4 j2 a
  68.     DMA_InitTypeDef  DMA_IniStructure;
    9 o" i) x0 W/ o  r- m$ t

  69. % P" R4 X+ O6 D( C
  70.     RCC_AHBPeriphClockCmd( RCC_AHBPeriph_DMA1, ENABLE );                                                                          //使能DMA时钟/ i3 V$ v+ V( p7 i" c9 y# U% p: y
  71. 2 P- B; P3 ?/ x# [+ A9 p+ ?
  72.     DMA_DeInit( DMA1_Channel6 );                                                                                                                                                             //DMA1通道6对应 USART2_RX
    8 M9 @- e( E+ _4 Z0 w. H4 v
  73.     DMA_IniStructure.DMA_PeripheralBaseAddr = ( u32 )&USART2->DR;                                         //DMA外设usart基地址
    9 {6 {# |3 h% I6 w1 b4 d) G
  74.     DMA_IniStructure.DMA_MemoryBaseAddr = ( u32 )dma_rec_buff;                                                  //DMA内存基地址8 B4 ?: t5 L% m6 y& q8 v* T) B
  75.     DMA_IniStructure.DMA_DIR = DMA_DIR_PeripheralSRC;                                                                                         //数据传输方向,从外设读取发送到内存0 J9 q: q: m8 V9 @6 h5 t, }" j8 ]- {% [
  76.     DMA_IniStructure.DMA_BufferSize = DMA_REC_LEN;                                                                                                 //DMA通道的DMA缓存的大小  也就是 DMA一次传输的字节数/ a0 ^4 G( Z2 @3 Y4 P
  77.     DMA_IniStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;                                 //外设地址寄存器不变: U% `* i! q' k6 A
  78.     DMA_IniStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;                                                                 //数据缓冲区地址递增7 j0 z7 K! M# b4 O7 f/ r1 G
  79.     DMA_IniStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte; //外设以字节为单位搬运
    5 R9 X+ Q" F& C# X4 t) X! T
  80.     DMA_IniStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;                                  //数据缓冲区以字节为单位搬入3 |7 i3 V/ Q5 Z) b( S7 [# Q5 Q
  81.     DMA_IniStructure.DMA_Mode = DMA_Mode_Normal;                                                                                                         //工作在正常缓存模式
    : o) e/ g: P/ e" ]1 |2 r' c5 Z7 L
  82.     DMA_IniStructure.DMA_Priority = DMA_Priority_Medium;                                                                         //DMA通道 x拥有中优先级/ a0 v$ ?/ {& w0 W- W5 F
  83.     DMA_IniStructure.DMA_M2M = DMA_M2M_Disable;                                                                                                                 //DMA通道x没有设置为内存到内存传输, B! d5 K3 ^3 N- L6 x
  84.   a2 G6 n0 `3 ]. P/ C8 F4 ]8 b
  85.     DMA_Init( DMA1_Channel6, &DMA_IniStructure );% h; d5 X- V4 A0 Y  v9 X1 v- m
  86.     DMA_Cmd( DMA1_Channel6, ENABLE );' i5 o, z0 k, {* {
  87. }
    6 n) e4 i, p. k4 u  C0 W

  88. & E6 Z9 ~0 H) O
  89. //重新恢复DMA指针
    8 I( v  ?5 B! M2 m' H0 @
  90. void myDMA_Enable( DMA_Channel_TypeDef*DMA_CHx ); i' B: ^0 c( O9 g+ H% K
  91. {" @( ?" ~0 ^" f
  92.     DMA_Cmd( DMA_CHx, DISABLE );                                                                                                                                                                   //关闭DMA1所指示的通道
    % M3 I3 P- }4 m' v, N7 |/ Q1 {0 e
  93.     DMA_SetCurrDataCounter( DMA_CHx, DMA_REC_LEN );                                                                                                 //DMA通道的DMA缓存的大小! N/ G% ~- d, N$ x8 b
  94.     DMA_Cmd( DMA_CHx, ENABLE );                                                                                                                                                                           //DMA1所指示的通道5 `& N9 i, `/ u8 I7 ]
  95. }
    * t3 @6 f8 w0 [; m1 f6 w
  96. //发送len个字节+ _& l3 n1 h2 h4 t) A
  97. //buf:发送区首地址& G( P" Q1 \# J8 a
  98. //len:发送的字节数+ v% f) y1 t& ]: b* O
  99. void uart2_Send( u8 *buf, u16 len )' s8 {5 V9 i' n% ~
  100. {+ t( o; X. C8 z# e& I1 M4 S; z7 h
  101.     u16 t;- K7 f  i) m4 S. Q, p0 x4 Y( @
  102.     for( t = 0; t < len; t++ )                                                                                                                                                                                //循环发送数据4 l: \/ R& J  E! q8 Z: b
  103.     {
    7 v2 U$ l  W1 W. T) n
  104.         while( USART_GetFlagStatus( USART2, USART_FLAG_TC ) == RESET );  n- c' `+ |9 X: i7 d
  105.         USART_SendData( USART2, buf[t] );" O9 A2 X: J2 H+ s6 p" ]5 w1 G0 `* x
  106.     }+ U4 v( O$ Y" b: @, U; u
  107.     while( USART_GetFlagStatus( USART2, USART_FLAG_TC ) == RESET );
    * n5 y  E4 e" f0 V! R; j
  108. }
    5 c1 y, r; d. }$ ?

  109. 3 }% |- Z' p' N6 _6 ^
  110. //备份接收到的数据* W8 ]- t* b' x/ w" y' [
  111. void copy_data( u8 *buf, u16 len )2 B, q' z  D8 `
  112. {
    1 s1 V( W- s. G. d8 r8 n
  113.     u16 t;6 Z, ~7 O! h4 r
  114.     dataLen_backup = len;                                                                                                                                                                                                         //保存数据长度. C: q8 `  b; ?# k. p+ X; H
  115.     for( t = 0; t < len; t++ )
    " l- H8 I0 }+ I2 C# L, r# c8 t
  116.     {% @3 ~8 G& S, x* n0 h
  117.         data_backup[t] = buf[t];                                                                                                                                                                         //备份接收到的数据,防止在处理数据过程中接收到新数据,将旧数据覆盖掉。' O# h2 u" A" S2 U3 h
  118.     }
    - |- q: n- A; l( W0 ?
  119. }
    ; G8 Q* p/ ]* J( z* w) ~

  120. : E) f8 N( d- L. a
  121. //利用空闲中断接收串口不定长数据
    5 D- n6 a  C' k2 R
  122. //串口接收一组数据结束后才会进入中断,在数据发送过程中,已经接收到的数据将会被存入DMA的缓冲区中0 L. {) C* b4 ?" Q
  123. void USART2_IRQHandler( void )
    1 r. ~9 z. C& N# |6 `) V1 u
  124. {
    6 j* ^, R' V; |# z* C, ^
  125.     u8 tem = 0;
    - s7 D# j5 J6 P/ Z
  126. #if (UART2_DMA == 1)                        //如果使能了 UART2_DMA 则使用串口空闲中断和 DMA功能接收数据3 V( p/ J' x1 n, {1 T* ~
  127.         if( USART_GetITStatus( USART2, USART_IT_IDLE ) != RESET )                                                                                   //空闲中断  一帧数据发送完成
    # O. Q) d) s+ c0 L) |
  128.     {
      G& a4 K; A7 N! T! s
  129.         USART_ReceiveData( USART2 );                                                                                                                                                                                 //读取数据注意:这句必须要,否则不能够清除空闲中断标志位。+ v( j# N8 d3 b6 G: {
  130.         DMA_Cmd( DMA1_Channel6, DISABLE );                                                                                                                                                         //关闭 DMA 防止后续数据干扰  i6 f  @& g' ]& C* C
  131.         uart2_rec_cnt = DMA_REC_LEN - DMA_GetCurrDataCounter( DMA1_Channel6 );  //DMA接收缓冲区数据长度减去当前 DMA传输通道中剩余单元数量就是已经接收到的数据数量
    6 m" g2 _7 G+ a: a: o
  132.         //uart2_Send( dma_rec_buff, uart2_rec_cnt );                                                                                                                //发送接收到的数据
    * F" O; t/ }9 i" J
  133.                           copy_data( dma_rec_buff, uart2_rec_cnt );                                                                                                                    //备份数据% O. l: }' z2 B9 R/ a
  134.         receiveOK_flag = 1;                                                                                                                                                                                                                        //置位数据接收完成标志位0 S; g) g2 e( ~& @" G/ \; B5 I
  135.         USART_ClearITPendingBit( USART2, USART_IT_IDLE );                                                                                                 //清除空闲中断标志位
    - b3 F" H7 ?9 h$ i+ \/ S- [
  136.         myDMA_Enable( DMA1_Channel6 );                                                                                                                                                                         //重新恢复 DMA等待下一次接收
    % }& H& s) C  i* _$ [- L
  137.     }. a3 |" ]( ^. Q% v1 ~) A
  138. #else                                                                                //如果未使能 UART2_DMA 则通过常规方式接收数据  每接收到一个字节就会进入一次中断  |( E1 g7 N/ M7 m' d1 D3 k8 E
  139.     if( USART_GetITStatus( USART2, USART_IT_RXNE ) != RESET )                                                 //接收中断) ?: R4 h1 b: u
  140.     {
    2 {" `) `& L2 }2 q  K: I
  141.         tem = USART_ReceiveData( USART2 );
    ; {9 f  z& P" g9 D9 M& u8 t! P
  142.         USART_SendData( USART2, tem );( _% [4 r% k( {/ U& E* w; u! {
  143.     }
    & I% J# Q7 H# b
  144. #endif8 F$ M) u2 L) x: A/ @
  145. }
复制代码
  1. #include "sys.h": Y6 o: P7 A* r" d
  2. #include "delay.h"
    : I: g  p' y9 m
  3. #include "usart.h"
      e; O3 w4 L% P2 D' l$ L2 o
  4. #include "led.h"
    ; w( N* D0 L" u: ]1 w2 c: _) d% J
  5. #include "key.h"
    1 {' w: T: y( j2 P% Q
  6. #include "uart2.h"
    : R& w" }( n6 Y/ P; H/ |
  7. #include "string.h"( r. L& h& g) c/ z; v0 Y4 G; a( y# ]
  8. ( b6 U, b7 ^% {
  9. extern u8 data_backup[DMA_REC_LEN];                                                                                                         //数据备份& z$ [2 M9 g3 I) O' W
  10. extern u16 dataLen_backup;                                                                                                                                                        //长度备份
    * j+ A1 T+ t: z
  11. extern _Bool receiveOK_flag;                                                                                                                                                //接收完成标志位
    " ~' o: M3 b; z& [, J# ~& }0 ^' ?( n  E
  12. 0 d4 _' y; n0 Q) n9 \1 w1 o0 f
  13. int main(void)
    % t& G5 H  K) h& f  m* l! I( y/ ^  ~
  14. {
    % e( d! {, Z- D+ g- |- ~
  15.     u8  j = 0;' z# T/ v9 ~  @3 F7 \0 r* m+ p
  16.           O7 N+ M# Y/ q6 ]8 J7 k; H% }& `. _
  17.         NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
    ) U$ ~& T. Q4 v' a3 |- c) _' l% o$ b
  18.         7 ?/ K; ?( z9 g
  19.     delay_init();       //延时函数初始化) u$ i: w3 m+ |* H
  20.     LED_Init();         //初始化与LED连接的硬件接口
    5 @' @6 \1 T  \0 p" m- i( T& Y
  21.     uart2_init(9600);. n* l: ^1 D# Q) T8 U* S. F
  22.     while(1)" K' i2 E' g! _, l4 U6 q3 I1 |  |& v
  23.     {
    - F: i: t: D0 g; r

  24. : w% {6 e+ p# L* J# E" t# R
  25.         if( receiveOK_flag )                                                                                                                                                //一帧数据接收完成后开始处理数据
    8 ~- v, H, }6 h
  26.         {
      T+ ~6 V& |) ]
  27.             receiveOK_flag = 0;
    1 b. l8 w# z" I: p
  28.             uart2_Send( data_backup, dataLen_backup );                                        //发送数据5 r8 [  D# q4 C2 n7 p$ D. J9 h
  29.             memset( data_backup, 0, sizeof( data_backup ) );                //清空备份数组
    2 R" _9 g* C% h/ X. U
  30.         }, _. i9 I$ I$ P& L1 I2 q- U
  31.                         ; S4 P" Z- l$ Q4 @
  32.         j++;7 B7 w% H" ?+ k+ R. w. y0 f8 g
  33.         if(j > 50)
    , _- A) X/ X( f/ x# I& s
  34.         {% t) `4 v1 F2 n
  35.             j = 0;/ }7 D' r  D3 `. f4 r5 a) ]
  36.             LED = !LED;+ h2 ~! L! Y6 p3 Y3 v  {8 `8 P
  37.         }
    5 t: m/ b, I0 y. ?" r. d; H$ ?, m  n
  38.         delay_ms(10);/ a6 r' C; \+ N& `* R1 r% t0 f
  39.     }
    : q, d% {) l. X8 O
  40. }
复制代码

4 M/ F. s0 }' J3 c% S0 d下面看一下测试效果
2 e4 R0 \5 `3 E; h9 R  r6 w
. l# C1 k9 ^/ G& }* p+ J KD9WO())O5D5HTW1]8F~1B0.png
7 q7 i; z5 U  A: h3 H
  |7 g6 m: l5 x W)L]ZAVJ`YV~[X4(JX09S3H.png
5 _0 V6 ?1 j6 F+ ~1 C+ K2 w+ U; Q" ^2 ?& a
6 q. U& o9 _2 l
* _; Q3 y6 P2 A. c$ i

9 K6 f7 g2 \& |3 e8 x
赞 收藏 评论0 发布时间:2022-3-21 13:41

举报

0个回答

所属标签

相似技术帖

官网相关资源

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