利用串口空闲中断接收不定长数据,这种方式有一个问题就是串口每接收到一个字节就会进入一次中断,如果发送的数据比较频繁,那么串口中断就会不停打断主程序运行,影响系统运行。那么能不能在串口接收数据过程中不要每接收一个数据中断一次,只有在一帧数据接收结束完成后只中断一次?
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 ~
7 R0 y9 j: g$ U7 _# r3 t$ Y
, T& p2 V' B, x+ |9 H* H4 _
0 ^" g+ [# k" T% i$ K, T' J1 y
* h3 r5 o, |+ S4 X% f; z- ]
- 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- void uart2_init( u16 baud )% _$ x9 N8 B r K
- {' F) x1 y0 D( `1 _6 \
- GPIO_InitTypeDef GPIO_InitStructure;& h' y, d; h1 V; e, J- W
- USART_InitTypeDef USART_InitStructure;! x( x6 p e* _
- NVIC_InitTypeDef NVIC_InitStructure;
9 ?2 v1 p' l$ g* {* N4 r6 D - % \" J0 q/ c9 a3 L: N, E
- RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOA, ENABLE ); R- u6 @$ K: a; x# D' k7 p, ?8 }
- RCC_APB1PeriphClockCmd( RCC_APB1Periph_USART2, ENABLE );
1 c& O6 `. i( n
) A1 {9 C- d0 A' M- GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2;( V' B' {+ j( b5 F/ o' [
- GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //推挽复用模式
5 V# y6 S* _3 J9 ^1 I& q9 [9 p - GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
/ J7 H+ ]; w! X, r - GPIO_Init( GPIOA, &GPIO_InitStructure );
8 B+ R V& U. y - 6 q0 v5 l+ f- ^& f: S8 ~: A1 Z
- GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3;
9 }3 e: e/ J9 l4 a( o - GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; //浮空输入模式
/ a; f7 }" @ S+ s/ ` - GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
3 ~7 `3 j# F: ?. X3 j' C- ^/ N( w - GPIO_Init( GPIOA, &GPIO_InitStructure );
" H: v/ c4 _* }/ S8 V) c* u
6 m6 A; {; ]9 Y) j- u# \- NVIC_InitStructure.NVIC_IRQChannel = USART2_IRQn;/ i- i6 ^: W0 f _8 b1 r1 a
- NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x02;
6 G! e/ s9 J, J! ]1 } - NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x02;( j1 P9 D' o! o, e7 n0 N6 h
- NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;' @, z9 E$ J7 b& C M q+ C
5 d* P+ @( m, V2 s7 ?- NVIC_Init( &NVIC_InitStructure );
' `% D# O4 D$ ^# R
) N q# U" |% k# |, f- USART_InitStructure.USART_BaudRate = baud;6 B& U5 X5 p S& i; S8 ]
- USART_InitStructure.USART_WordLength = USART_WordLength_8b;/ _ _4 }* h7 x
- USART_InitStructure.USART_StopBits = USART_StopBits_1;
1 c; m7 p! O' M$ a) k D - USART_InitStructure.USART_Parity = USART_Parity_No;
9 s! j) e0 o1 D/ H - USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
& R5 C+ b" G; ^! Q' x) F - USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
2 |7 d A3 z$ y - 0 F8 d$ j' \+ `& e
- USART_Init( USART2, &USART_InitStructure );
0 {3 \0 Y7 O6 w% L" c5 m - #if (UART2_DMA == 1)2 H+ a: s* X+ @7 G& t2 i
- USART_ITConfig( USART2, USART_IT_IDLE, ENABLE ); //使能串口空闲中断' R. O0 y% m3 g( c
- USART_DMACmd( USART2, USART_DMAReq_Rx, ENABLE ); //使能串口2 DMA接收# E& ]6 |1 o ]" C
- uartDMA_Init(); //初始化 DMA0 T& b0 k8 o3 C+ z( |! r' Q
- #else
' A, S6 U4 @. G; R1 w4 M - USART_ITConfig( USART2, USART_IT_RXNE, ENABLE ); //使能串口RXNE接收中断
* u. x' T! Y3 c8 S0 @8 G - #endif
: Q8 Z' _1 {. z7 t - USART_Cmd( USART2, ENABLE ); //使能串口29 {' _/ T0 x. u. c
- 6 ~) L* _- j: {9 V
- //RXNE中断和IDLE中断的区别?
& }* W/ W1 u r# r) X - //当接收到1个字节,就会产生RXNE中断,当接收到一帧数据,就会产生IDLE中断。比如给单片机一次性发送了8个字节,就会产生8次RXNE中断,1次IDLE中断。
0 Q9 `: W# O. K% P6 c5 B, l - }
复制代码 # 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
- void uartDMA_Init( void )
2 {8 N, J( ?3 A - {6 o# N# {2 P( b/ K* }
- DMA_InitTypeDef DMA_IniStructure;( u. s1 y' c+ @! r2 Y' }
- ; f, Q9 U% J6 N, V
- RCC_AHBPeriphClockCmd( RCC_AHBPeriph_DMA1, ENABLE ); //使能DMA时钟
. S: ~: x ]4 p0 d
" W* j( U" l: [+ g/ a9 A% Q- DMA_DeInit( DMA1_Channel6 ); //DMA1通道6对应 USART2_RX
" [( |) o) @# F; s0 g4 n - DMA_IniStructure.DMA_PeripheralBaseAddr = ( u32 )&USART2->DR; //DMA外设usart基地址- u2 j, M; j0 c* j6 \# j1 q
- DMA_IniStructure.DMA_MemoryBaseAddr = ( u32 )dma_rec_buff; //DMA内存基地址. C! i$ H% D/ K9 K/ n, O0 b
- DMA_IniStructure.DMA_DIR = DMA_DIR_PeripheralSRC; //数据传输方向,从外设读取发送到内存
* o9 i x* _4 F0 f - DMA_IniStructure.DMA_BufferSize = DMA_REC_LEN; //DMA通道的DMA缓存的大小 也就是 DMA一次传输的字节数( p9 @% U: E5 i" k1 z& J
- DMA_IniStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; //外设地址寄存器不变' D! @( j& r8 `
- DMA_IniStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; //数据缓冲区地址递增* K+ z0 H C {7 E3 b& q
- DMA_IniStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte; //外设以字节为单位搬运2 v! h& C0 ~6 l
- DMA_IniStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte; //数据缓冲区以字节为单位搬入
. v/ ] e6 F# G" t$ P* P - DMA_IniStructure.DMA_Mode = DMA_Mode_Normal; //工作在正常缓存模式
, j' w, D3 y9 ] - DMA_IniStructure.DMA_Priority = DMA_Priority_Medium; //DMA通道 x拥有中优先级2 ]4 }$ _5 g5 m! P K6 x+ i
- DMA_IniStructure.DMA_M2M = DMA_M2M_Disable; //DMA通道x没有设置为内存到内存传输2 D8 F1 O' m0 J7 Q+ h
- 9 q {: O$ v; a! M& [8 d
- DMA_Init( DMA1_Channel6, &DMA_IniStructure );
( l4 S+ M7 e5 _* q - DMA_Cmd( DMA1_Channel6, ENABLE );
5 k1 g b1 {' L4 l0 x - }
复制代码 ' {$ 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
- void USART2_IRQHandler( void )$ D1 e0 }6 q( l n' K
- {
. Z4 S$ `' J" O0 A1 k: P8 l - u8 tem = 0;- s+ ?1 w+ |* i4 c2 E
- #if (UART2_DMA == 1) //如果使能了 UART2_DMA 则使用串口空闲中断和 DMA功能接收数据6 I- X$ X" V9 t3 l/ ~ [( N
- if( USART_GetITStatus( USART2, USART_IT_IDLE ) != RESET )//空闲中断 一帧数据发送完成
; `; D# ?. ]8 \# P6 @ - {/ |4 [+ L4 u/ a& t3 t+ H$ l
- USART_ReceiveData( USART2 ); //读取数据注意:这句必须要,否则不能够清除空闲中断标志位。6 \5 ~ w m, R1 V- \. A
- DMA_Cmd( DMA1_Channel6, DISABLE ); //关闭 DMA 防止后续数据干扰9 a8 r8 |! l. \ T( k
- uart2_rec_cnt = DMA_REC_LEN - DMA_GetCurrDataCounter( DMA1_Channel6 ); //DMA接收缓冲区数据长度减去当前 DMA传输通道中剩余单元数量就是已经接收到的数据数量
* h9 K' a3 _) Y% G+ R - copy_data( dma_rec_buff, uart2_rec_cnt ); //备份数据3 J# f0 }# _4 `" ?& p0 X$ Y5 {* P
- receiveOK_flag = 1; //置位数据接收完成标志位/ O0 j2 {) F8 I* G+ u0 x
- USART_ClearITPendingBit( USART2, USART_IT_IDLE ); //清除空闲中断标志位
! W9 q8 p5 D0 ?1 p7 J! m0 U - myDMA_Enable( DMA1_Channel6 ); //重新恢复 DMA等待下一次接收) ]: L4 J1 Q2 n/ @8 l
- }3 ?3 }. q* ~) b9 Q4 U4 |
- #else //如果未使能 UART2_DMA 则通过常规方式接收数据 每接收到一个字节就会进入一次中断) g3 e/ B" ^$ B% P) s" z
- if( USART_GetITStatus( USART2, USART_IT_RXNE ) != RESET ) //接收中断; d' O6 Z5 b9 h( g8 Z# E+ T
- {
o! J- M6 t! @3 Z+ u - tem = USART_ReceiveData( USART2 ); F7 M; ^6 N* S. ]
- USART_SendData( USART2, tem );
7 ~1 A: [, |# a% y3 O - }
( @/ s$ n! W" b" O: A - #endif- M1 e0 d5 k; a6 J- {$ I4 A6 N
- }
复制代码 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- #ifndef __UART2_H
* h/ d m& \) I- ? - #define __UART2_H
' D* c9 f% {! r* E0 J7 F( q' R - #include "sys.h"
) D* _" d4 z$ ~/ u - & D8 ~3 t- c0 B' R" m$ E
- #define DMA_REC_LEN 50 //DMA数据接收缓冲区
' p9 _1 Q( D$ J6 A2 |; \- l, p
+ p0 c; A* p M3 t/ C& A- void uart2_init(u16 baud);! u" X! ]& x5 l3 t1 r2 d6 e: P" u# u
- void uartDMA_Init( void );
- p, W- `3 V7 p0 f, S - void myDMA_Enable( DMA_Channel_TypeDef*DMA_CHx );
0 t/ o& E2 b) L2 U - void uart2_Send( u8 *buf, u16 len );
7 d1 ]$ H1 O& @ - void copy_data( u8 *buf, u16 len );
( [, M9 c$ X4 p/ _( P - #endif
复制代码- //串口2空闲中断 + DMA数据传输% y8 H& ^8 U( p- v
- #include "uart2.h"8 c7 w$ J1 P3 O3 z( u" o9 c
- #define UART2_DMA 1 //使用串口2 DMA传输
# P( s' ~; e" O% B/ ~0 ?/ y4 q
# e# s$ b! w' {9 @3 Q- u8 dma_rec_buff[DMA_REC_LEN] = {0};- R& V# U+ I; n3 h! @) O: H
- u16 uart2_rec_cnt = 0; //串口接收数据长度
( c2 _% f, j/ O% U! i. w
# s+ r: o4 z; y6 K4 Q% x- u8 data_backup[DMA_REC_LEN] = {0}; //数据备份( s; A( }; D3 U
- u16 dataLen_backup = 0; //长度备份; g" J1 b2 y' y9 l7 a
- _Bool receiveOK_flag = 0; //接收完成标志位2 z( C! Z( B$ D4 b5 j/ \
- ' g- [: U3 Q: U8 R1 T
- /*
# r: o4 d( O: t1 b* O - 空闲中断是什么意思呢?" D' J! q% r) | _: a4 @& h
- 指的是当总线接收数据时,一旦数据流断了,此时总线没有接收传输,处于空闲状态,IDLE就会置1,产生空闲中断;又有数据发送时,IDLE位就会置0;
0 @* t: m) @8 u7 E, E - 注意:置1之后它不会自动清0,也不会因为状态位是1而一直产生中断,它只有0跳变到1时才会产生,也可以理解为上升沿触发。9 T8 e4 P5 w/ K: t; }" a5 N$ u
- 所以,为确保下次空闲中断正常进行,需要在中断服务函数发送任意数据来清除标志位。! t( F' ^! X, j$ Z7 W2 k
- */) p* W% p) y; i6 P, k
' o5 [$ E5 q0 X/ c6 f( C- void uart2_init( u16 baud )
U, @3 R9 V$ P8 I - {
! J+ i# N7 R0 h+ s. h3 ~ - GPIO_InitTypeDef GPIO_InitStructure;) ~- s" c: P1 |9 E% k4 d
- USART_InitTypeDef USART_InitStructure;
; Q6 b/ W. R" o - NVIC_InitTypeDef NVIC_InitStructure;
. I* [& J. n# x6 [" o - 2 R9 i1 b3 y: } a3 `& F; V
- RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOA, ENABLE );: A$ \( n1 V6 n% K, H, B
- RCC_APB1PeriphClockCmd( RCC_APB1Periph_USART2, ENABLE );3 o3 W7 S) S3 ?0 V
0 K2 V( n9 u6 m- GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2;
% ?# Z/ N/ k8 W" T - GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //推挽复用模式/ b2 k# ~. X& C2 G. J; C# F
- GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
w4 ^7 k3 O8 @8 y - GPIO_Init( GPIOA, &GPIO_InitStructure );
5 i5 o& O2 B, H5 _ - , H3 L- u- v4 g" J
- GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3;
" \" k+ `4 H9 N7 g" m - GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; //浮空输入模式5 p8 W7 B8 P7 q6 N: x. z; z- Z" u
- GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
2 m: i' ~/ R: a/ \3 A - GPIO_Init( GPIOA, &GPIO_InitStructure );
& n$ o! g* j2 E3 ?) Z& r6 T
% P7 [' b0 Q! ^. l0 @5 E) g1 X- NVIC_InitStructure.NVIC_IRQChannel = USART2_IRQn;
, K5 g( v2 k3 R - NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x02;8 l! m# J1 L- G$ D& ^9 S
- NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x02;' ?7 Z8 E( h# M: j
- NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;# |5 |' x0 f% T2 M B
- D- ~2 \: u& w+ R- NVIC_Init( &NVIC_InitStructure );
7 t: d2 n9 l N$ R0 F5 j7 i a( m
$ }7 V% v- K9 Y1 l' |9 G" K- USART_InitStructure.USART_BaudRate = baud;
; H3 \5 z! n6 Y - USART_InitStructure.USART_WordLength = USART_WordLength_8b;- Y; L& `" z8 V* g6 g& P I" A
- USART_InitStructure.USART_StopBits = USART_StopBits_1;5 y' x" c* i# a" w8 |$ A
- USART_InitStructure.USART_Parity = USART_Parity_No;
. u; Q0 R* A' B" C. A - USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx; i0 Y( @3 A4 a2 M3 I F
- USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;6 a; @6 ?( o9 A2 r' N
* H; z+ B/ h; P4 z4 {- USART_Init( USART2, &USART_InitStructure );
7 q. J) @# P/ o1 g1 ~ - #if (UART2_DMA == 1)- T: x! g- \6 M: H* U
- USART_ITConfig( USART2, USART_IT_IDLE, ENABLE ); //使能串口空闲中断0 f6 O& `. L; n. J( V
- USART_DMACmd( USART2, USART_DMAReq_Rx, ENABLE ); //使能串口2 DMA接收
( J, l# W6 o2 w" `9 X5 V. ` - uartDMA_Init(); //初始化 DMA9 z& H! s2 L7 L9 O4 i
- #else
; U, b4 k) `- F2 F - USART_ITConfig( USART2, USART_IT_RXNE, ENABLE ); //使能串口RXNE接收中断- V! b- }' Z' e X
- #endif7 M, S1 t3 `( J& y7 W2 w" @" O% l7 V
- USART_Cmd( USART2, ENABLE ); //使能串口2
% c7 O! l2 e# K% } -
* s( L% O* s0 f& w# C1 l - //RXNE中断和IDLE中断的区别?8 \' J* y/ q" g
- //当接收到1个字节,就会产生RXNE中断,当接收到一帧数据,就会产生IDLE中断。比如给单片机一次性发送了8个字节,就会产生8次RXNE中断,1次IDLE中断。# T* Z: ^/ k3 T
- }2 t, _% W, z! }; Q/ ?9 B9 l3 w8 v
- ' ^; r/ K, e' m' h' s4 O$ g
- void uartDMA_Init( void )
A9 Z' m+ U9 P' H/ E3 {. p - {! O8 e& @8 p" n4 j2 a
- DMA_InitTypeDef DMA_IniStructure;
9 o" i) x0 W/ o r- m$ t
% P" R4 X+ O6 D( C- RCC_AHBPeriphClockCmd( RCC_AHBPeriph_DMA1, ENABLE ); //使能DMA时钟/ i3 V$ v+ V( p7 i" c9 y# U% p: y
- 2 P- B; P3 ?/ x# [+ A9 p+ ?
- DMA_DeInit( DMA1_Channel6 ); //DMA1通道6对应 USART2_RX
8 M9 @- e( E+ _4 Z0 w. H4 v - DMA_IniStructure.DMA_PeripheralBaseAddr = ( u32 )&USART2->DR; //DMA外设usart基地址
9 {6 {# |3 h% I6 w1 b4 d) G - DMA_IniStructure.DMA_MemoryBaseAddr = ( u32 )dma_rec_buff; //DMA内存基地址8 B4 ?: t5 L% m6 y& q8 v* T) B
- DMA_IniStructure.DMA_DIR = DMA_DIR_PeripheralSRC; //数据传输方向,从外设读取发送到内存0 J9 q: q: m8 V9 @6 h5 t, }" j8 ]- {% [
- DMA_IniStructure.DMA_BufferSize = DMA_REC_LEN; //DMA通道的DMA缓存的大小 也就是 DMA一次传输的字节数/ a0 ^4 G( Z2 @3 Y4 P
- DMA_IniStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; //外设地址寄存器不变: U% `* i! q' k6 A
- DMA_IniStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; //数据缓冲区地址递增7 j0 z7 K! M# b4 O7 f/ r1 G
- DMA_IniStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte; //外设以字节为单位搬运
5 R9 X+ Q" F& C# X4 t) X! T - DMA_IniStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte; //数据缓冲区以字节为单位搬入3 |7 i3 V/ Q5 Z) b( S7 [# Q5 Q
- DMA_IniStructure.DMA_Mode = DMA_Mode_Normal; //工作在正常缓存模式
: o) e/ g: P/ e" ]1 |2 r' c5 Z7 L - DMA_IniStructure.DMA_Priority = DMA_Priority_Medium; //DMA通道 x拥有中优先级/ a0 v$ ?/ {& w0 W- W5 F
- DMA_IniStructure.DMA_M2M = DMA_M2M_Disable; //DMA通道x没有设置为内存到内存传输, B! d5 K3 ^3 N- L6 x
- a2 G6 n0 `3 ]. P/ C8 F4 ]8 b
- DMA_Init( DMA1_Channel6, &DMA_IniStructure );% h; d5 X- V4 A0 Y v9 X1 v- m
- DMA_Cmd( DMA1_Channel6, ENABLE );' i5 o, z0 k, {* {
- }
6 n) e4 i, p. k4 u C0 W
& E6 Z9 ~0 H) O- //重新恢复DMA指针
8 I( v ?5 B! M2 m' H0 @ - void myDMA_Enable( DMA_Channel_TypeDef*DMA_CHx ); i' B: ^0 c( O9 g+ H% K
- {" @( ?" ~0 ^" f
- DMA_Cmd( DMA_CHx, DISABLE ); //关闭DMA1所指示的通道
% M3 I3 P- }4 m' v, N7 |/ Q1 {0 e - DMA_SetCurrDataCounter( DMA_CHx, DMA_REC_LEN ); //DMA通道的DMA缓存的大小! N/ G% ~- d, N$ x8 b
- DMA_Cmd( DMA_CHx, ENABLE ); //DMA1所指示的通道5 `& N9 i, `/ u8 I7 ]
- }
* t3 @6 f8 w0 [; m1 f6 w - //发送len个字节+ _& l3 n1 h2 h4 t) A
- //buf:发送区首地址& G( P" Q1 \# J8 a
- //len:发送的字节数+ v% f) y1 t& ]: b* O
- void uart2_Send( u8 *buf, u16 len )' s8 {5 V9 i' n% ~
- {+ t( o; X. C8 z# e& I1 M4 S; z7 h
- u16 t;- K7 f i) m4 S. Q, p0 x4 Y( @
- for( t = 0; t < len; t++ ) //循环发送数据4 l: \/ R& J E! q8 Z: b
- {
7 v2 U$ l W1 W. T) n - while( USART_GetFlagStatus( USART2, USART_FLAG_TC ) == RESET ); n- c' `+ |9 X: i7 d
- USART_SendData( USART2, buf[t] );" O9 A2 X: J2 H+ s6 p" ]5 w1 G0 `* x
- }+ U4 v( O$ Y" b: @, U; u
- while( USART_GetFlagStatus( USART2, USART_FLAG_TC ) == RESET );
* n5 y E4 e" f0 V! R; j - }
5 c1 y, r; d. }$ ?
3 }% |- Z' p' N6 _6 ^- //备份接收到的数据* W8 ]- t* b' x/ w" y' [
- void copy_data( u8 *buf, u16 len )2 B, q' z D8 `
- {
1 s1 V( W- s. G. d8 r8 n - u16 t;6 Z, ~7 O! h4 r
- dataLen_backup = len; //保存数据长度. C: q8 ` b; ?# k. p+ X; H
- for( t = 0; t < len; t++ )
" l- H8 I0 }+ I2 C# L, r# c8 t - {% @3 ~8 G& S, x* n0 h
- data_backup[t] = buf[t]; //备份接收到的数据,防止在处理数据过程中接收到新数据,将旧数据覆盖掉。' O# h2 u" A" S2 U3 h
- }
- |- q: n- A; l( W0 ? - }
; G8 Q* p/ ]* J( z* w) ~
: E) f8 N( d- L. a- //利用空闲中断接收串口不定长数据
5 D- n6 a C' k2 R - //串口接收一组数据结束后才会进入中断,在数据发送过程中,已经接收到的数据将会被存入DMA的缓冲区中0 L. {) C* b4 ?" Q
- void USART2_IRQHandler( void )
1 r. ~9 z. C& N# |6 `) V1 u - {
6 j* ^, R' V; |# z* C, ^ - u8 tem = 0;
- s7 D# j5 J6 P/ Z - #if (UART2_DMA == 1) //如果使能了 UART2_DMA 则使用串口空闲中断和 DMA功能接收数据3 V( p/ J' x1 n, {1 T* ~
- if( USART_GetITStatus( USART2, USART_IT_IDLE ) != RESET ) //空闲中断 一帧数据发送完成
# O. Q) d) s+ c0 L) | - {
G& a4 K; A7 N! T! s - USART_ReceiveData( USART2 ); //读取数据注意:这句必须要,否则不能够清除空闲中断标志位。+ v( j# N8 d3 b6 G: {
- DMA_Cmd( DMA1_Channel6, DISABLE ); //关闭 DMA 防止后续数据干扰 i6 f @& g' ]& C* C
- uart2_rec_cnt = DMA_REC_LEN - DMA_GetCurrDataCounter( DMA1_Channel6 ); //DMA接收缓冲区数据长度减去当前 DMA传输通道中剩余单元数量就是已经接收到的数据数量
6 m" g2 _7 G+ a: a: o - //uart2_Send( dma_rec_buff, uart2_rec_cnt ); //发送接收到的数据
* F" O; t/ }9 i" J - copy_data( dma_rec_buff, uart2_rec_cnt ); //备份数据% O. l: }' z2 B9 R/ a
- receiveOK_flag = 1; //置位数据接收完成标志位0 S; g) g2 e( ~& @" G/ \; B5 I
- USART_ClearITPendingBit( USART2, USART_IT_IDLE ); //清除空闲中断标志位
- b3 F" H7 ?9 h$ i+ \/ S- [ - myDMA_Enable( DMA1_Channel6 ); //重新恢复 DMA等待下一次接收
% }& H& s) C i* _$ [- L - }. a3 |" ]( ^. Q% v1 ~) A
- #else //如果未使能 UART2_DMA 则通过常规方式接收数据 每接收到一个字节就会进入一次中断 |( E1 g7 N/ M7 m' d1 D3 k8 E
- if( USART_GetITStatus( USART2, USART_IT_RXNE ) != RESET ) //接收中断) ?: R4 h1 b: u
- {
2 {" `) `& L2 }2 q K: I - tem = USART_ReceiveData( USART2 );
; {9 f z& P" g9 D9 M& u8 t! P - USART_SendData( USART2, tem );( _% [4 r% k( {/ U& E* w; u! {
- }
& I% J# Q7 H# b - #endif8 F$ M) u2 L) x: A/ @
- }
复制代码- #include "sys.h": Y6 o: P7 A* r" d
- #include "delay.h"
: I: g p' y9 m - #include "usart.h"
e; O3 w4 L% P2 D' l$ L2 o - #include "led.h"
; w( N* D0 L" u: ]1 w2 c: _) d% J - #include "key.h"
1 {' w: T: y( j2 P% Q - #include "uart2.h"
: R& w" }( n6 Y/ P; H/ | - #include "string.h"( r. L& h& g) c/ z; v0 Y4 G; a( y# ]
- ( b6 U, b7 ^% {
- extern u8 data_backup[DMA_REC_LEN]; //数据备份& z$ [2 M9 g3 I) O' W
- extern u16 dataLen_backup; //长度备份
* j+ A1 T+ t: z - extern _Bool receiveOK_flag; //接收完成标志位
" ~' o: M3 b; z& [, J# ~& }0 ^' ?( n E - 0 d4 _' y; n0 Q) n9 \1 w1 o0 f
- int main(void)
% t& G5 H K) h& f m* l! I( y/ ^ ~ - {
% e( d! {, Z- D+ g- |- ~ - u8 j = 0;' z# T/ v9 ~ @3 F7 \0 r* m+ p
- O7 N+ M# Y/ q6 ]8 J7 k; H% }& `. _
- NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
) U$ ~& T. Q4 v' a3 |- c) _' l% o$ b - 7 ?/ K; ?( z9 g
- delay_init(); //延时函数初始化) u$ i: w3 m+ |* H
- LED_Init(); //初始化与LED连接的硬件接口
5 @' @6 \1 T \0 p" m- i( T& Y - uart2_init(9600);. n* l: ^1 D# Q) T8 U* S. F
- while(1)" K' i2 E' g! _, l4 U6 q3 I1 | |& v
- {
- F: i: t: D0 g; r
: w% {6 e+ p# L* J# E" t# R- if( receiveOK_flag ) //一帧数据接收完成后开始处理数据
8 ~- v, H, }6 h - {
T+ ~6 V& |) ] - receiveOK_flag = 0;
1 b. l8 w# z" I: p - uart2_Send( data_backup, dataLen_backup ); //发送数据5 r8 [ D# q4 C2 n7 p$ D. J9 h
- memset( data_backup, 0, sizeof( data_backup ) ); //清空备份数组
2 R" _9 g* C% h/ X. U - }, _. i9 I$ I$ P& L1 I2 q- U
- ; S4 P" Z- l$ Q4 @
- j++;7 B7 w% H" ?+ k+ R. w. y0 f8 g
- if(j > 50)
, _- A) X/ X( f/ x# I& s - {% t) `4 v1 F2 n
- j = 0;/ }7 D' r D3 `. f4 r5 a) ]
- LED = !LED;+ h2 ~! L! Y6 p3 Y3 v {8 `8 P
- }
5 t: m/ b, I0 y. ?" r. d; H$ ?, m n - delay_ms(10);/ a6 r' C; \+ N& `* R1 r% t0 f
- }
: q, d% {) l. X8 O - }
复制代码
4 M/ F. s0 }' J3 c% S0 d下面看一下测试效果
2 e4 R0 \5 `3 E; h9 R r6 w
. l# C1 k9 ^/ G& }* p+ J
7 q7 i; z5 U A: h3 H
|7 g6 m: l5 x
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 |