在日常中,串口经常作为和上位机通信的接口,进行打印信息方便调试程序,有时也会作为模块的驱动接口,所以总结一下串口的几种使用方法对以后的开发还是很有帮助的。6 U" }1 s% K k3 y% C
3 t1 M5 F. T- Z2 b
一.仅向上位机打印调试信息8 W2 P* N( i% q+ N: R4 j; E7 t
单纯利用串口向上位机打印调试信息,程序如下:
/ Z G1 G8 D$ T" y `- void USART1_Init( uint32_t btl )' c0 Q4 @1 w) h! |1 [. f0 N+ k) c
- { J3 c- |! `' ^" A) u9 n
- GPIO_InitTypeDef GPIO_InitStruct;
2 |* l2 ^: e. l, O# r P3 q - USART_InitTypeDef USART_InitStruct;
$ v% S8 ~: }2 l -
7 N4 E7 f9 j, l% a - RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOA|RCC_APB2Periph_USART1, ENABLE );
% G" U- N$ ^5 H, `4 W* B+ n - 7 _4 B1 G+ A8 p, v- L, m/ N& l
- GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_PP;5 u. T1 u$ Z1 @! h* ^
- GPIO_InitStruct.GPIO_Pin = GPIO_Pin_9;//Tx: W G$ w8 g/ i& r; _( Z2 {
- GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;! O% S* @1 w. ^+ @ H4 [
- GPIO_Init( GPIOA, &GPIO_InitStruct );+ ~/ @7 c2 O( F a" j3 y9 N9 V
- GPIO_InitStruct.GPIO_Pin = GPIO_Pin_10;//Rx
. z% S) x! n$ I - GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IN_FLOATING;
, v7 p7 J9 t2 {/ c" A - GPIO_Init( GPIOA, &GPIO_InitStruct );/ x4 J5 W. a, e+ J+ E! K$ K$ V
-
6 s4 e& O9 p! W# U: d+ r - USART_InitStruct.USART_BaudRate = btl;" Q' K) H) {3 O# w1 i! l$ ]0 s
- USART_InitStruct.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
$ d6 b, X; e: I - USART_InitStruct.USART_Mode = USART_Mode_Tx;2 Q: Z2 d8 t/ e: p/ D9 o$ h4 i
- USART_InitStruct.USART_Parity = USART_Parity_No;
& \. g1 J2 ]; C7 C$ T, \ - USART_InitStruct.USART_StopBits = USART_StopBits_1;
7 A, Y5 `& [+ ~4 h/ v* w - USART_InitStruct.USART_WordLength = USART_WordLength_8b;
4 O, w5 D6 O |# s- f - USART_Init( USART1, &USART_InitStruct );% a2 }; P+ ^) W. O. s5 g
-
1 b _! g5 q. s! G - USART_Cmd( USART1, ENABLE );
9 w' A& b G$ c$ l/ C - + a" E! ^3 `, Q1 a- r
- }/ h' a' f8 C2 i, j4 S/ d, W2 T* T
+ K1 r& o' M) {1 {. K; r2 P0 s- //串口重定向,直接利用printf函数输出调试信息
4 N. P9 } p. ^' U4 } - int fputc( int ch, FILE *f )
4 P; Y# J7 e' U4 O* {9 ^ u( T - {
1 m6 |) E+ G7 f5 i3 J& a' u, i - USART_SendData( USART1, ( uint8_t )ch );
6 K ~: ?: w2 t4 v0 f - while( USART_GetFlagStatus( USART1, USART_FLAG_TXE )!=SET );% @2 t; D8 L& P" {& Q* L
- return ch;, m) B4 m5 b# s% U4 J- _
- }
`* c) A0 i0 r- l0 l
: v- X: G8 V C5 z
& ^* r. A, g* [/ ?
复制代码 ; Q1 ~0 A8 p- i8 J( `
记得包含头文件,勾选Use MicroLIB,以使用printf等函数
) l/ R2 N8 @/ \) S
' M( Q: L1 d8 x% d
/ S' x2 L. A# T
1 B0 s# l) D* O* u& \2 d' P
+ }+ P- Q) \ _; {. S' {二.与上位机交互信息8 b" I3 y' m, X% j' K" x/ e
相比于上面的单向通信,有时候需要从上位机接收信息,然后进行反馈,这个时候就使用到串口的中断了。
9 W/ K& o7 q. C: y2 Y }上位机向单片机发送字符串,接收后再发送给上位机:# }/ O4 @( ?$ h) y# A
- 9 A( [9 W; q% N$ t% D, w# g8 K! @& C
- void USART1_Init( uint32_t btl )7 N( [5 c; v' p8 F) O& N2 }5 F% K
- {3 G$ |4 O: Y! O7 Y# G' V9 |
- /* 结构体声明 */. B' J9 n$ s& Y& L
- GPIO_InitTypeDef GPIO_InitStruct;9 q. Y+ W) r) o5 T( D- ^: S+ ^
- USART_InitTypeDef USART_InitStruct;+ s0 a# t- Y6 ~
- NVIC_InitTypeDef NVIC_InitStruct;, B+ R7 K/ y8 D( ?5 m
- , ~' F' F8 X* O
- /* 打开时钟 */. V( x; C d' _. N" a F9 `. G
- RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOA, ENABLE );' C- b7 @' H- e
- RCC_APB2PeriphClockCmd( RCC_APB2Periph_USART1, ENABLE );
. F' p7 R1 k, O9 k: K6 w% } - 7 X, d F" s; d! W5 Y
- /* GPIO配置 */; S; U8 |1 b6 k1 R( H" ]4 H
- GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_PP;" Q0 z" {+ K1 e P3 E1 H
- GPIO_InitStruct.GPIO_Pin = GPIO_Pin_9;//Tx1 L) \) Z/ Q" N0 j
- GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;3 ~: Y7 o, {% f9 J6 L
- GPIO_Init( GPIOA, &GPIO_InitStruct );0 ~+ g* J# z/ n ^& H
- GPIO_InitStruct.GPIO_Pin = GPIO_Pin_10;//Rx; D# e) }0 g# ~/ s5 Q" O
- GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IN_FLOATING;
a/ ^( S0 q N7 j1 M; f - GPIO_Init( GPIOA, &GPIO_InitStruct );0 Y* o1 h: k! L2 H
-
: R! K& t# c! }6 X7 G6 o - /* 串口配置 */
, c) @) h2 L. W0 V% m - USART_InitStruct.USART_BaudRate = btl;; ?9 _. F: I* t# U4 |
- USART_InitStruct.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
" q7 p+ ~) h/ ~4 I1 s) P - USART_InitStruct.USART_Mode = USART_Mode_Tx|USART_Mode_Rx;//收发模式
4 p4 i* q9 ?# w M# ^' h% d7 ` - USART_InitStruct.USART_Parity = USART_Parity_No;" d9 |6 x d% P: g% I& w
- USART_InitStruct.USART_StopBits = USART_StopBits_1;
% |' Q6 M* s n+ E, D- D# b6 m) X - USART_InitStruct.USART_WordLength = USART_WordLength_8b;
2 r% _) N3 z, S! }. M$ I0 H7 K - USART_Init( USART1, &USART_InitStruct );
6 ~0 [1 Z" q4 B -
& g x2 c3 B; i* m0 Z9 Q - /* 中断配置 */ ?/ L% v. v P& z+ G
- NVIC_InitStruct.NVIC_IRQChannel = USART1_IRQn;5 |' f1 s. k$ \% t+ D1 d1 V) G( w
- NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority=3 ;. J! O3 q; l3 r) C0 Y$ k I& _; B
- NVIC_InitStruct.NVIC_IRQChannelSubPriority = 3; 5 \# p. n- ?/ p) y
- NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE; 4 n9 w7 x' Y5 N5 H$ Y( |1 B
- NVIC_Init(&NVIC_InitStruct); / V) U9 g0 N# J+ d
- USART_ITConfig( USART1, USART_IT_RXNE, ENABLE );//接收寄存器非空触发中断
, N! C c$ j) i8 J" H' U; z# y -
, P, x Y5 }, `# u. U/ ^( ?. e; Q - /* 使能串口 */# F1 M# R$ H# l
- USART_Cmd( USART1, ENABLE );: ?3 X3 }. u. f- m' k, @9 c. v0 E
- 0 Y9 Z( }% N' g) `8 t( }! [! W3 _
- }
& |3 K" ], Z4 R
: x4 [- ^ g- a+ ~9 L* N" U
! D7 T. y. Y# n& S# A9 a) p- volatile uint8_t n=0;) W( X9 B+ i: Z4 s+ c
- //接收缓冲区- u: K. I( ]0 P8 o. ]
- uint8_t USART1_Rx_Buf[100];% ~2 z$ \ B. K/ Q& X8 {. ?, @0 J6 J
- void USART1_IRQHandler( void )
4 S& ]! Q- u# ~7 u7 ? - {
# e2 A$ o+ y6 ^ - if( USART_GetITStatus( USART1, USART_IT_RXNE )==SET )
0 W: C7 T) P$ N- v) j - {/ D9 v1 v% g4 D' E
- USART1_Rx_Buf[n]=USART_ReceiveData( USART1 );
* Q e9 O: c1 F k; {! P/ a - n++;
& T' t3 U5 ~9 ^: ^) k - } 7 T& }- ]: \2 Z5 J! |
- USART_ClearFlag( USART1, USART_IT_RXNE );+ j) K+ S7 Y8 u n& R- q' V
- }; ] ~: M5 f8 b7 q$ p: _& d
复制代码 $ [$ U) n. A$ {, Q
每从上位机中接收一字节的数据,都将数据存储在串口的接收缓冲区USART1_Rx_Buf[100]中。4 Y n8 `6 N6 |( G Z- s* A' b
( V* k) _) w2 G, a0 Y6 J _# G
5 U$ t) ?) }0 F三.作为驱动接口+ M0 C3 l3 ]* x" K) W
一些模块的驱动接口就是串口,这个时候就需要单片机从模块中读取指定格式的数据,比如GPS模块,将定位信息从串口发出,单片机解析串口数据,显示在上位机中。
& g/ S3 C G, ^' M3 c8 D- A husart3用来与GPS模块通信,从GPS模块接收数据,认为10ms内的数据属于一次数据,所以就需要定时器来控制时间。
; B# j, d. _ E+ ?6 L$ Musart3:* X4 K) H9 Q3 g2 [1 O
- //串口1中断服务程序/ l1 T7 {7 K; ]1 _7 X, @! A: d
- //注意,读取USARTx->SR能避免莫名其妙的错误 : I, z9 \/ [. e/ a, [# C
- u8 USART_RX_BUF[USART_REC_LEN]; //接收缓冲,最大USART_REC_LEN个字节.. n8 w9 K, m/ y" m: P8 C3 E
- //接收状态: A& S; G3 t6 M3 C. T! \
- //bit15, 接收完成标志
W3 ]* S7 X! |) j - //bit14, 接收到0x0d
0 t1 {2 _7 Y! r* n+ _. i0 S - //bit13~0, 接收到的有效字节数目- c; _& _1 r8 c
- u16 USART_RX_STA=0; //接收状态标记
. S" s: D, [6 F2 s/ h$ f -
- x6 H* M, d* \+ E# k - void uart_init(u32 bound){9 c" W) o f8 X+ C
- //GPIO端口设置5 c4 e! q! e+ l+ k6 ]2 ^; r) H' ~
- GPIO_InitTypeDef GPIO_InitStructure;2 N' N1 _0 b* k$ @; _& D
- USART_InitTypeDef USART_InitStructure;
/ p1 x6 {, \, x0 n9 W6 H - NVIC_InitTypeDef NVIC_InitStructure;7 s. t8 r/ q! Q8 f$ B* L. n+ |6 L
-
0 X, w- B; l# g _. C+ Y: U - RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1|RCC_APB2Periph_GPIOA, ENABLE); //使能USART1,GPIOA时钟! L! ]; [! L% s1 L q
- 4 C: ^6 S! @" f3 Y5 P/ W* S
- //USART1_TX GPIOA.9# a& m8 I$ N ~' n' \2 E; e
- GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9; //PA.9' M8 S/ m: _! _! w
- GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;9 [& o% l6 _; N
- GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //复用推挽输出
; m( `( B- h, T) |3 L+ r0 T1 E8 c7 _$ q - GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化GPIOA.9; L G! n) ^4 S# C
- . _. K, h) C# i1 Y% ~( k4 ?
- //USART1_RX GPIOA.10初始化3 ~: \) T$ z8 k0 T4 u; \( f/ H
- GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;//PA10/ v) E2 G1 b% {
- GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;//浮空输入! Z; y; z0 @" Y" T( N' c8 j: u
- GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化GPIOA.10 " a/ [5 J. v# Z) J
& A2 |$ W% Z$ x7 D, L' t4 w- //Usart1 NVIC 配置
% H2 v6 y- t3 t* I - NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
! y5 x' y9 }& N7 y - NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=3 ;//抢占优先级3( h& _6 h) e' X/ P7 I
- NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3; //子优先级3
/ G+ U: g" n" E - NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道使能, e) w. ~8 a6 q. o5 k" f' C" k5 H
- NVIC_Init(&NVIC_InitStructure); //根据指定的参数初始化VIC寄存器
! I" s% L/ N7 m$ R9 R1 ?( K -
4 O; d M8 ]% ]9 }6 T, B - //USART 初始化设置
% R* b' M/ m, ?1 b/ }2 N2 U4 X - - n6 P6 H' v6 V* m7 n y9 o. I8 ~9 D, {
- USART_InitStructure.USART_BaudRate = bound;//串口波特率
' R# O6 k' |& i, e. y0 f& {9 h4 Y - USART_InitStructure.USART_WordLength = USART_WordLength_8b;//字长为8位数据格式0 m5 W Y) I7 D# b. X C
- USART_InitStructure.USART_StopBits = USART_StopBits_1;//一个停止位
- J# W, s" j" D& G8 F; e6 Z4 R - USART_InitStructure.USART_Parity = USART_Parity_No;//无奇偶校验位( t F% B( i8 L H, }
- USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;//无硬件数据流控制' |. A- F4 | X
- USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx; //收发模式
9 j) y0 d. l) O+ S" O( w4 h
* \$ w* T: i+ z2 U# s% x9 u- USART_Init(USART1, &USART_InitStructure); //初始化串口17 |) h/ Z6 u0 n" c! S. F5 _. b
- USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);//开启串口接受中断" V$ N- H: y* ^% u" ~; J: \# |
- USART_Cmd(USART1, ENABLE); //使能串口1
) Y7 {( M+ h- g! S9 Z. b6 L! E - * u. H: O4 J: W m$ ~( w ~5 O& C
- }
1 ?- u+ w) N0 ?- o1 @% l - 7 _& M5 S3 Y+ M" D/ s% X7 k: G
- void USART1_IRQHandler(void) //串口1中断服务程序! u5 m0 j' V4 K9 e! Z% v
- {
+ \. y+ o3 o) q; U3 K - u8 Res;
s+ u8 Q Z& u5 Q# P
% e- y# k) M a- if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET) //接收中断(接收到的数据必须是0x0d 0x0a结尾)7 T$ \/ k! N" x, h, v
- {
8 u# D( p* U, b+ u2 {# o - Res =USART_ReceiveData(USART1); //读取接收到的数据
* l' Y4 r5 T; T8 `& f2 m& x - & ~; O0 m7 v( k; ~0 ^# n/ w
- if((USART_RX_STA&0x8000)==0)//接收未完成/ O7 g% n) A2 ]. J1 L* M* r( V
- {
7 ]6 x7 f7 M3 l3 u1 f - if(USART_RX_STA&0x4000)//接收到了0x0d
. ~6 }0 Y+ m C - {; i& m a% H) P2 t/ ?, x
- if(Res!=0x0a)USART_RX_STA=0;//接收错误,重新开始' |" M9 T5 r S$ S5 }# N
- else USART_RX_STA|=0x8000; //接收完成了
! l# j; e [4 x( n - }
4 \8 d' \0 D" i( H - else //还没收到0X0D
, t; l5 I" L6 A) X - { : @7 l9 f% u& U1 W$ p8 H% q
- if(Res==0x0d)USART_RX_STA|=0x4000;
+ H5 a8 o1 M. T. V; r - else" X# }* S( a+ m6 R6 b
- {5 S5 T3 E* c7 b H
- USART_RX_BUF[USART_RX_STA&0X3FFF]=Res ;; z+ t: ?) a5 }6 F) V) w
- USART_RX_STA++;
$ T3 _$ J2 @0 L P9 [6 G - if(USART_RX_STA>(USART_REC_LEN-1))USART_RX_STA=0;//接收数据错误,重新开始接收
8 }8 `! j* ?. t+ X' o* `" ?% U' [ - }
" y5 C9 V4 f4 e P! H8 L - }
# | |" y: l) V6 U - }
: D0 Y1 o# Q+ g( d0 L! H - }
+ b; c6 x5 k5 K8 q/ p) d( G - } 7 g4 l' c2 q9 C5 m0 e
$ K" R" |8 a4 A, B! R9 \" y. h
复制代码 " A% H! U( h8 q7 P. G6 U
time7:1 }& o9 q6 y; h9 N8 G: U% w
- . `9 e! Q1 T4 o: q! ^
- extern vu16 USART3_RX_STA;( r) v& M' ?. F( k' |
- ) S; E( M; {/ c& r, T/ \
- //定时器7中断服务程序 2 H: `9 M- S r; k! m3 l2 L
- void TIM7_IRQHandler(void)* ^. ^9 G1 _1 \* g5 p* {- Z
- { # k. p. U9 c% ]6 c
- if (TIM_GetITStatus(TIM7, TIM_IT_Update) != RESET)//是更新中断* Y3 V& u' z7 t
- { 5 I# n1 z* Y; m" c
- USART3_RX_STA|=1<<15; //标记接收完成
3 y( p0 u& A2 ^- }$ b: Y; B - TIM_ClearITPendingBit(TIM7, TIM_IT_Update ); //清除TIM7更新中断标志 : H6 K4 U: q' A1 F3 h- L' e
- TIM_Cmd(TIM7, DISABLE); //关闭TIM7 0 W, ] _9 d8 }
- } ! d; ^+ W2 T% n. P7 A
- }
) t' |9 L3 y' e9 h: W5 p: e - 3 F% P: _8 @1 F A! Y4 W
- //通用定时器7中断初始化4 B1 I, G( C6 q' h" o
- //这里时钟选择为APB1的2倍,而APB1为42M
. X/ \9 V. r- O* m - //arr:自动重装值。
0 X$ ~- j% L; x) P - //psc:时钟预分频数- x2 y o, K9 t) C& |
- //定时器溢出时间计算方法:Tout=((arr+1)*(psc+1))/Ft us.
1 i4 N8 t7 K* E1 } - //Ft=定时器工作频率,单位:Mhz
# c l2 |# a1 ?& h3 ~; Z - //通用定时器中断初始化
6 U0 N& p" Q" k9 V - //这里始终选择为APB1的2倍,而APB1为36M' _% o, T. `% {# ]9 r/ ?$ |0 T0 f
- //arr:自动重装值。
+ ~. p' V: a0 C& _3 r5 o4 ] - //psc:时钟预分频数
4 c; C# F, _4 B, n - void TIM7_Int_Init(u16 arr,u16 psc)* f2 j2 d5 X' F2 A4 Z
- { 5 h, X4 D/ B% \/ w' N, j
- NVIC_InitTypeDef NVIC_InitStructure;
8 O, J( r% g( a6 M - TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;) u4 }9 u# c( d4 \6 k6 ]3 o
- ' z8 A9 X9 h: o' q1 C
- RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM7, ENABLE);//TIM7时钟使能 6 l; i0 e% ^, i3 _$ v5 o3 A" O& S+ H; A
-
: a& m( w5 _! x7 D# u - //定时器TIM7初始化* |0 n! U! {, ?+ i$ X# f
- TIM_TimeBaseStructure.TIM_Period = arr; //设置在下一个更新事件装入活动的自动重装载寄存器周期的值 : F5 N( ]! i' E7 h- W- [, u
- TIM_TimeBaseStructure.TIM_Prescaler =psc; //设置用来作为TIMx时钟频率除数的预分频值3 s/ z! L$ M" ~- L, M, y
- TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; //设置时钟分割:TDTS = Tck_tim0 p+ Y3 m* l/ P1 n/ K
- TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; //TIM向上计数模式5 M+ `# A8 H/ W2 H: E4 ~4 T: {
- TIM_TimeBaseInit(TIM7, &TIM_TimeBaseStructure); //根据指定的参数初始化TIMx的时间基数单位8 R2 g$ D- }7 P v2 b7 f5 F; a
-
( U+ ~: N8 U V0 s" C1 J - TIM_ITConfig(TIM7,TIM_IT_Update,ENABLE ); //使能指定的TIM7中断,允许更新中断2 |4 R! P3 a; m! y* N9 T
- 9 K5 j2 o) v. d. _' n! s
- TIM_Cmd(TIM7,ENABLE);//开启定时器7 _! J5 X- x+ G6 t0 p5 B
- 0 m, p& k- I1 r! x/ a& P2 G% P- I
- NVIC_InitStructure.NVIC_IRQChannel = TIM7_IRQn;
3 r1 g; l' i; `" g& m; L1 [ - NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=0 ;//抢占优先级0
0 \' q! `) D/ _& b - NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2; //子优先级2
" B/ K+ Y1 {$ A) M - NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道使能 D( F+ K+ d1 j
- NVIC_Init(&NVIC_InitStructure); //根据指定的参数初始化VIC寄存器, a" b- V& V; [& l& `( U
- - w9 f# L0 L- c6 G1 ~+ }
- }3 P* V& e5 N/ [# r( U5 v" Q
- / t8 f6 h$ m; c2 x9 b3 i9 u; w
复制代码 8 Q1 s# g# j" P& g/ A5 {: R( w
四.结合DMA接收数据帧" }+ a7 {" ]4 f7 L% [
当单片机从串口上接收数据时,一般我们都是接收一个字节数据,进入一次中断,在中断中处理数据或者做标记,这种方法虽然简单,但是对于大量数据的情况,频繁地进入中断处理数据就占有了CPU的宝贵资源。 g; F0 E, a1 _) u- k' D5 w
这个时候我们可以使用DMA来接收数据,DMA接收数据的好处就是省CPU资源,DMA仅仅在初始化的时候占用一下CPU资源,其他操作都是在DMA的控制器来完成的。
$ S" L& u7 {5 K X# Q( N
0 _4 z6 d* f: b' e0 I使用背景:单片机从USART2中接收传感器传回的数据帧(12字节),传感器每秒上报一次数据,要求单片机可以收到完整数据并且可以对数据进行处理。* `: i+ M( W9 d
1 U3 b7 u2 M8 t5 W T( _% t. P
程序框架:以往串口接收数据都是判断数据寄存器非空的,一字节一字节地接收,现在使用DMA,使每一次数据寄存器的值都自动传给内存指定地址(也就是指定的数据缓冲区),当串口接收完一帧数据后,会触发空闲中断,这意味着一帧数据的接收完成,我们只需要在数据缓冲区中对数据进行处理即可。7 R! M( N7 W$ G) Z; i
/ ~, s7 |# }0 @代码:
8 B( B2 [4 c2 U' V8 G1 `+ k3 i7 t
; t2 G) h. u' p- uint8_t USART2_Rx_Buf[12];
% r1 |6 r5 j$ U; Z, r - - x3 B% ]& ^ ^% P% W, j
- void USART2_Config( void )0 W. E5 s' H% Y* I! _$ |, Z- Z
- {* @$ _) z% k4 s! M1 [1 d
- /* 声明各结构体 */
2 N* B4 _3 k0 ?1 v& J - GPIO_InitTypeDef GPIO_InitStruct;
]0 K! |8 U7 l! V% Y; K - USART_InitTypeDef USART_InitStruct;
3 c/ Q* b* s1 S4 u8 o2 {! |* r - NVIC_InitTypeDef NVIC_InitStruct;
7 R% b3 ~* P0 s$ s! I - DMA_InitTypeDef DMA_InitStruct;- K( \5 c3 v5 I" ^' Z; n9 y2 B; c
- $ I* h3 s" E/ @: g! r7 _% g2 u
- /* 打开时钟 */) j: c/ j A* v( z! V! b
- RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOA, ENABLE );
3 x/ F2 S$ Q. v' b1 ]4 K: v! E: t0 y - RCC_APB1PeriphClockCmd( RCC_APB1Periph_USART2, ENABLE );
- l. e. D- Y+ |# g% ~ - RCC_AHBPeriphClockCmd( RCC_AHBPeriph_DMA1, ENABLE );0 s8 F5 U K: b% k- U
- 3 E! ]$ B0 t: J5 M
- /* GPIO配置 */
1 i0 }4 E4 n1 \) e - GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_PP;4 Y3 f7 M9 L# c" F9 ^5 D
- GPIO_InitStruct.GPIO_Pin = GPIO_Pin_2; //USART2_Tx:PA2
) O8 ^0 \% t5 b4 ^; ?0 m {8 ?# L - GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;% W2 E; ^5 L4 o( ^$ E- y
- GPIO_Init( GPIOA, &GPIO_InitStruct );
# c) E! c' T! Q, a0 J2 n3 O - GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IN_FLOATING;
0 Y5 K( l# N+ x9 Y - GPIO_InitStruct.GPIO_Pin = GPIO_Pin_3; //USART2_Rx:PA3& N# }/ p4 }, f. M' e
- GPIO_Init( GPIOA, &GPIO_InitStruct );
9 B8 G' P5 p" L0 B, I5 r3 ` -
: L; e. \; g! g - /* 串口配置 */
4 p3 F' x6 I8 U# l0 ?+ V$ Z - USART_DeInit( USART2 );
' w. O! N* [6 ]3 Z2 z9 M, {' ]% N- f - USART_InitStruct.USART_BaudRate = 9600; //波特率:9600
) P8 O' U- n, a" W" w - USART_InitStruct.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
( {" G) D4 S9 L. w1 Q1 U, L - USART_InitStruct.USART_Mode = USART_Mode_Tx|USART_Mode_Rx;
4 ^! X8 f2 w" J: j - USART_InitStruct.USART_Parity = USART_Parity_No;
7 I1 ?2 |" L# [/ J) l - USART_InitStruct.USART_StopBits = USART_StopBits_1;/ _' |1 L$ a6 }# F* p6 A! K
- USART_InitStruct.USART_WordLength = USART_WordLength_8b;
( ~% B, f. H/ R' D0 F - USART_Init( USART2, &USART_InitStruct);
' E" m% t" J2 n7 x: D! s! q- o - % k/ {2 P9 D, h `) ]% n1 j1 {: f
- /* 中断配置 */! n: x; i' ^4 b% G9 R# ]
- NVIC_PriorityGroupConfig( NVIC_PriorityGroup_2 );" ~( k% y9 \ q3 H6 H% n1 C8 p
- NVIC_InitStruct.NVIC_IRQChannel = USART2_IRQn;
( Y3 I# E* T" L" y) c( Y9 T; I - NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;- p4 Z- Q* T5 C; ?# D
- NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 1;
2 a0 k# o% p% C1 t! [$ d% X' S! } - NVIC_InitStruct.NVIC_IRQChannelSubPriority = 0;( \% {9 C2 J/ m' A% S
- NVIC_Init( &NVIC_InitStruct );
* z4 f9 _7 E3 C# |/ p$ L* ]9 n - USART_ITConfig( USART2, USART_IT_IDLE, ENABLE ); //空闲中断- ~5 p) `$ B+ X1 \, F
- USART_DMACmd( USART2, USART_DMAReq_Rx, ENABLE ); //开启DMA接收6 N. F4 ~( e5 r- J- s. i
-
+ b% h6 z0 t6 C9 p- x" n -
0 \1 H, S; W8 P/ Z/ F1 }2 E) Q - /* 配置DMA传输数据 USART2对应DMA1的通道6 方向:外设到存储器*/ Z& n# G8 d. Q1 \
- DMA_InitStruct.DMA_PeripheralBaseAddr = USART2_BASE+0x04; // 设置DMA源地址:串口数据寄存器地址*/ : j; H- s- C% c" g" t8 x: r
- DMA_InitStruct.DMA_MemoryBaseAddr = (uint32_t)USART2_Rx_Buf; // 内存地址(要传输的变量的指针)
; M l& _( e% J# E' ^2 H/ F - DMA_InitStruct.DMA_DIR = DMA_DIR_PeripheralSRC; // 方向:从外设到内存 9 a% G( s* R4 t" V W: B- |
- DMA_InitStruct.DMA_BufferSize = 12; // 传输大小 一帧数据12字节 2 s8 _3 x- ~2 ]: o4 U
- DMA_InitStruct.DMA_PeripheralInc = DMA_PeripheralInc_Disable; // 外设地址不增 + H/ ]" z4 S# ~
- DMA_InitStruct.DMA_MemoryInc = DMA_MemoryInc_Enable; // 内存地址自增
6 {4 t; ~9 c% g& y$ ~3 V - DMA_InitStruct.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte; // 外设数据单位
1 S/ a0 w' b9 ]: ?- m- G+ Q - DMA_InitStruct.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte; // 内存数据单位 . {; k8 ^0 g$ a/ Z# c
- DMA_InitStruct.DMA_Mode = DMA_Mode_Circular; //正常 DMA模式,一次或者循环模式
. P0 n% I g5 Y: @) j- [: k - DMA_InitStruct.DMA_Priority = DMA_Priority_Medium; // 优先级:中
7 o& a0 r3 |1 B0 C$ d - DMA_InitStruct.DMA_M2M = DMA_M2M_Disable; // 禁止内存到内存的传输
i, e( Q, \7 Z6 T$ M, f! Q - DMA_Init( DMA1_Channel6, &DMA_InitStruct );& ~ P4 c) f) ~0 e! U
- DMA_ClearFlag(DMA1_FLAG_TC5);
5 H9 y! Z+ H; G, ]& z6 s% f - DMA_Cmd( DMA1_Channel6, ENABLE );//使能DMA. s4 d+ P x3 D0 a5 W6 S" E
- ) k' f- D; x- ]
-
; V: b& s- {- f - /* 使能串口 容易忽略 */$ E: u2 ~4 w2 n: e" h4 K& D
- USART_Cmd( USART2, ENABLE);3 O3 K. L$ `/ ^) N' D
-
; ]$ {" L. ? ^6 }: j1 j, ] - }
" T) j0 R! l: \
4 Q$ @" \- z o- 1 q0 \+ U! q. q$ R
8 n/ X! O; U( y* O* }% C- /* 串口空闲中断服务函数 */
9 j" m) l+ d$ T; l, R, i - void USART2_IRQHandler( void )
" s7 P& B0 n6 N7 Q* g# I' g - {6 v k4 y) f6 [( e8 d$ W; @8 f) V1 K
- if( USART_GetITStatus( USART2, USART_IT_IDLE ) != RESET )
& Q2 T6 V+ d6 V# ?: k7 [ - {
: e5 q( |5 o% y6 v - USART_ReceiveData( USART2 );//象征性接收数据& d4 ?# j$ ?/ v' F; [
- USART_ClearITPendingBit( USART2, USART_IT_IDLE );//清除标志位( x- c3 \' v5 V$ ^7 B1 n
-
, h8 a" B7 d- b - }
- i% [ t$ k, t$ D# Y9 R - }' C' H, K: L2 x# R5 v
- //也可以在中断服务函数中扩展更多的功能3 Q. c: i5 [7 ?
复制代码
$ Q* f' x9 t* w9 Z- H; I9 Y5 R" q, ?要点:4 C& B* ~, b& D5 p- K
要开启串口的空闲中断
3 o! i5 E4 U1 N( C" A串口的空闲中断中要象征性读取数据和清除标志位
" a( I% q& P$ S) @* D3 z0 r要开启串口的DMA接收请求
) o6 q' m) f: f; O+ M9 ?4 h————————————————
- t8 a9 I! s( X3 b r- l5 g版权声明:Aspirant-GQ
! `. Q; ~* \% G' ]如有侵权请联系删除
0 N1 ?% C4 Z: X: L7 Q
% i: c* b g6 `3 s3 v! W2 x& }& H, n' Y9 P1 \
: X$ Z; R* ~1 H. B, o9 C$ k
|