在日常中,串口经常作为和上位机通信的接口,进行打印信息方便调试程序,有时也会作为模块的驱动接口,所以总结一下串口的几种使用方法对以后的开发还是很有帮助的。/ 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 @! ]- void USART1_Init( uint32_t btl )0 p( z- s W7 Z8 M
- {6 m8 S" k: m# {6 L
- GPIO_InitTypeDef GPIO_InitStruct;0 W+ Y! Z6 D! l# q6 ]
- USART_InitTypeDef USART_InitStruct;# M7 V4 c1 i: c4 Q
- 2 I8 S0 u# l3 d7 ^5 v! @
- RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOA|RCC_APB2Periph_USART1, ENABLE );
) z/ N; H+ V% y2 n, X( ` - # L# @! R0 q# e+ |3 ~! }% h
- GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_PP;& q" c( t' u9 L% i
- GPIO_InitStruct.GPIO_Pin = GPIO_Pin_9;//Tx( @' v( f8 H; J
- GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
9 t1 H1 \8 ?) I; h - GPIO_Init( GPIOA, &GPIO_InitStruct );4 r4 ]) r# ]3 [7 g$ @
- GPIO_InitStruct.GPIO_Pin = GPIO_Pin_10;//Rx
, y$ ]! B8 B1 s5 B0 z - GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IN_FLOATING;; W. z6 f5 S0 @3 u. P- W; t
- GPIO_Init( GPIOA, &GPIO_InitStruct );
0 ~4 G2 d- T; _, b0 G# F -
. Q% G) @& W7 O0 j6 u. b - USART_InitStruct.USART_BaudRate = btl;4 B0 V& A: t u, p* ^
- USART_InitStruct.USART_HardwareFlowControl = USART_HardwareFlowControl_None;* O7 i2 U4 _6 D# g
- USART_InitStruct.USART_Mode = USART_Mode_Tx;
2 [' T4 B8 ?9 W2 f2 m, L' c - USART_InitStruct.USART_Parity = USART_Parity_No;
q4 \, d- E) ^/ c& I0 x - USART_InitStruct.USART_StopBits = USART_StopBits_1;& m" w& b9 d9 v+ U+ M& T! ?
- USART_InitStruct.USART_WordLength = USART_WordLength_8b;( h _, g8 f/ L5 T6 G( z
- USART_Init( USART1, &USART_InitStruct );& s9 z* S3 `$ i
-
3 j9 ~0 Q2 U U8 z - USART_Cmd( USART1, ENABLE );
$ O2 Y2 f& w3 y - . J; {) \6 U" g0 z
- }
+ J* D- A& P& c# @$ q5 z - 4 |, X1 v! }0 h! b$ ~# ^
- //串口重定向,直接利用printf函数输出调试信息
$ C! O: y) W, |. b - int fputc( int ch, FILE *f )0 N# o5 {( k. D) [. x1 k
- {
) u( X( o& |; Z' F6 q4 i* q - USART_SendData( USART1, ( uint8_t )ch );
0 _, Q. ^8 `* P/ A - while( USART_GetFlagStatus( USART1, USART_FLAG_TXE )!=SET ); P% s: d) m$ k6 I& k5 x
- return ch;
, {3 w- c+ {: Z- z - }: [0 R6 t1 [3 `" Y3 a: q
- * J& w7 L9 B9 m% x9 Z' z4 f
* 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
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
9 r# k" N3 a0 W! Z- void USART1_Init( uint32_t btl )1 k7 z, _" ]: ?- F" U) u6 m6 a
- {
6 e2 A& M6 ` K4 h1 W* C. h - /* 结构体声明 */
( ]& z6 R w0 [, F! L$ s - GPIO_InitTypeDef GPIO_InitStruct;
. Y4 _" \# \3 ?6 i* Z' y" v7 o$ Z - USART_InitTypeDef USART_InitStruct;# Y3 P' m$ _& ^
- NVIC_InitTypeDef NVIC_InitStruct;
! S# {0 t3 M- U" _1 t* d. {% M -
' C, ^! T9 ?8 u0 [- N3 P. v9 r5 h - /* 打开时钟 */, b5 {( b( R2 X" p
- RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOA, ENABLE );, O. K8 L; z- B
- RCC_APB2PeriphClockCmd( RCC_APB2Periph_USART1, ENABLE );% s+ b5 Y' W. O" @
-
" S: _0 t; l" P! | - /* GPIO配置 */- H- D6 m' {! i. D
- GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_PP;8 s; A0 ]7 ~3 f- I# Y
- GPIO_InitStruct.GPIO_Pin = GPIO_Pin_9;//Tx
0 X$ P) H% j& B+ w0 A: C5 Z! t - GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;) b9 @& m+ C* j/ u
- GPIO_Init( GPIOA, &GPIO_InitStruct );
3 `' r6 t6 W$ M; @+ r - GPIO_InitStruct.GPIO_Pin = GPIO_Pin_10;//Rx; p- z! @" b4 W( H% k5 t9 J* F
- GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IN_FLOATING;
' `4 ~7 c$ D4 ?$ T; n - GPIO_Init( GPIOA, &GPIO_InitStruct );
3 p: D6 @7 q/ D% I* e - : K% P- _* f p- ^! I7 ?, ~
- /* 串口配置 */ {$ Q" H0 j; X0 B
- USART_InitStruct.USART_BaudRate = btl;
, T3 x# m& f; A/ j8 g - USART_InitStruct.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
2 [/ a7 |9 b% I# h - USART_InitStruct.USART_Mode = USART_Mode_Tx|USART_Mode_Rx;//收发模式2 U- |7 ^0 Z3 h+ u9 H+ ~
- USART_InitStruct.USART_Parity = USART_Parity_No;( G6 X/ z8 \9 @( h0 k$ T
- USART_InitStruct.USART_StopBits = USART_StopBits_1;/ N' f% g& C9 V& y
- USART_InitStruct.USART_WordLength = USART_WordLength_8b;& N' F) p# N V% {7 Y, Y
- USART_Init( USART1, &USART_InitStruct );9 N2 G0 e) n* U' C8 B) D
-
4 L3 _# e7 a6 x - /* 中断配置 */' N( c3 g) Y5 D- L. o/ N2 e9 d
- NVIC_InitStruct.NVIC_IRQChannel = USART1_IRQn;1 ?3 s' i/ T; q. K
- NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority=3 ;+ { S- _6 a$ y; z
- NVIC_InitStruct.NVIC_IRQChannelSubPriority = 3;
% n& `! ^' e2 b: W& I, R, e - NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;
2 q1 f7 E3 X3 a, G4 y - NVIC_Init(&NVIC_InitStruct);
% X% O+ f e0 J/ D1 c - USART_ITConfig( USART1, USART_IT_RXNE, ENABLE );//接收寄存器非空触发中断 a' }) ?* G0 u1 A7 v6 h! g/ z
-
, f# M& w3 i" u E' n5 @0 m+ x - /* 使能串口 */$ D/ {/ i \8 ]- x% g1 c
- USART_Cmd( USART1, ENABLE );4 |2 \. i- M" {5 w+ P5 O; B3 J
- 2 G& u) @' ?5 c3 Z
- }) z8 U- Z0 Q; u& N0 v
- ; S% [1 `; Y9 F7 [: g, |( J
- ( D* V- M9 `) Y, l4 z
- volatile uint8_t n=0;! V8 S6 j4 m' a, M4 }# u' ]
- //接收缓冲区: n7 R6 u% M2 N2 q% Q7 J
- uint8_t USART1_Rx_Buf[100];4 d/ T/ ]8 U1 G% G- |1 O: v. r- U
- void USART1_IRQHandler( void )( _: k6 V y5 Q1 ^
- {" _- f3 x' Q4 h: V- ]3 z
- if( USART_GetITStatus( USART1, USART_IT_RXNE )==SET )- O$ {" x4 i, W1 D1 ]
- {) X4 Q4 V+ G# w3 S0 r7 u1 G
- USART1_Rx_Buf[n]=USART_ReceiveData( USART1 );* e( a5 Z7 r# c: q+ U3 D
- n++;
" u) R; l/ `! v! |# y - } & h$ y2 n% L6 ^+ ?0 z9 E/ q; [, E9 {
- USART_ClearFlag( USART1, USART_IT_RXNE );
5 q& p: t% @7 n9 ? - }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中断服务程序; I' K& C! H. E2 q0 T
- //注意,读取USARTx->SR能避免莫名其妙的错误
) p. O4 s7 f+ z2 f+ S( J' A - u8 USART_RX_BUF[USART_REC_LEN]; //接收缓冲,最大USART_REC_LEN个字节.
2 y: U* l% e8 U% b4 _ - //接收状态: ~+ a1 I( H' y- b' O
- //bit15, 接收完成标志
9 A4 y3 k T% h7 Q5 ] - //bit14, 接收到0x0d2 O* v. ~2 s+ W* D" F
- //bit13~0, 接收到的有效字节数目' J; f8 [- z. r1 Q6 U. _: G
- u16 USART_RX_STA=0; //接收状态标记
# X9 @- ~4 `* n8 S7 k8 K" U: l' @5 @ - h, F7 I' S* x4 W2 e, n3 Q* O0 `
- void uart_init(u32 bound){
4 d7 `4 s$ {6 J, w. l7 ^ - //GPIO端口设置8 e, r. y. [8 S* y8 r
- GPIO_InitTypeDef GPIO_InitStructure;+ D4 x' n# z" K6 S6 j1 m& ] l* e& M
- USART_InitTypeDef USART_InitStructure;
6 k( K6 F( B2 ]# f. s2 O& }: |# x( Z! } - NVIC_InitTypeDef NVIC_InitStructure;1 F6 f2 g0 p1 r; B+ e
- 1 d; ?, `, R' {! U8 Z. }
- RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1|RCC_APB2Periph_GPIOA, ENABLE); //使能USART1,GPIOA时钟; g4 o) B: f; e/ X7 S
- 9 I& I' P2 r' c! \+ P
- //USART1_TX GPIOA.9
) l% h# }, X9 _' ]+ @& t - GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9; //PA.9
9 o, Z7 G6 A. X - GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;, i. u4 m4 l: j/ m( F
- GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //复用推挽输出% C8 T) y9 {: X- s
- GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化GPIOA.9
6 ]1 b G" g! Y3 H/ z% s - ( A' O6 j) P `
- //USART1_RX GPIOA.10初始化( o [6 {) {, M, G8 G
- GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;//PA10; @" }+ \7 A% F1 a* b$ _! C9 L* P! V
- GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;//浮空输入
6 Q5 `2 c2 A, ?! v/ G2 R: H - GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化GPIOA.10 * C" `7 h9 o& A2 k
- ' j9 U3 ~- ~& L5 }' [7 e/ |5 [
- //Usart1 NVIC 配置
: q( z* ]5 x' c1 X( W( r4 Y - NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
# p( ~/ v( k. n8 | - NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=3 ;//抢占优先级3
2 c$ J) O) l @" A; @ - NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3; //子优先级3
2 y/ x- ?9 U/ C: x6 |& b% K - NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道使能
A: C" |5 _# q |- ? - NVIC_Init(&NVIC_InitStructure); //根据指定的参数初始化VIC寄存器7 e/ J5 {1 \* j& V
-
0 Z% G7 [2 _0 P( I$ P/ a) I+ `4 } - //USART 初始化设置
5 ~$ i& P0 V% O+ X - ' ]; H7 [9 j7 ?7 h8 d
- USART_InitStructure.USART_BaudRate = bound;//串口波特率
+ o0 ^. `9 X, Z- c; c; t - USART_InitStructure.USART_WordLength = USART_WordLength_8b;//字长为8位数据格式
( A3 `0 `0 r7 l- `: c% H9 f - USART_InitStructure.USART_StopBits = USART_StopBits_1;//一个停止位' }2 |8 K# s) T
- USART_InitStructure.USART_Parity = USART_Parity_No;//无奇偶校验位, D- ^- Z( ]4 J' Y4 [% `$ _
- USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;//无硬件数据流控制
+ `8 F9 Y% y. S4 Z ? - USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx; //收发模式
$ ]4 j, m+ `: V' X
- N4 \5 r7 M; H! u. b- USART_Init(USART1, &USART_InitStructure); //初始化串口15 r, g+ |' [; U6 U: g- T
- USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);//开启串口接受中断
+ I0 v/ e. {" u; {6 x0 _ - USART_Cmd(USART1, ENABLE); //使能串口1
: _6 f6 K1 g* `, u( k8 V - 5 o( h4 T! [! T. Q! Q
- }2 V! L3 U6 ]$ S9 ~
- $ m; F7 M9 D" W1 y0 L8 ^
- void USART1_IRQHandler(void) //串口1中断服务程序2 X( W7 I$ }' g5 W- n6 ?- e
- {
6 F; }# p7 E7 ^" k4 u - u8 Res;/ Z5 Y8 z8 U- j; \$ F
6 V+ ~3 K1 T% Z4 P( e# v- if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET) //接收中断(接收到的数据必须是0x0d 0x0a结尾), P# _6 [, p% K3 H- z
- {
, q& |$ V% e5 b* }5 @ - Res =USART_ReceiveData(USART1); //读取接收到的数据
4 o( Q5 p; k" ~3 S! m# \ -
; u/ u$ P) m& i" W - if((USART_RX_STA&0x8000)==0)//接收未完成
X: }7 [/ P; K7 Z - { N$ G1 H+ |; {. G
- if(USART_RX_STA&0x4000)//接收到了0x0d
/ U) x$ r' `: k6 b( p - {+ h- i" ~2 e0 u2 y
- if(Res!=0x0a)USART_RX_STA=0;//接收错误,重新开始2 Y: u8 ~/ @ |8 f, m) B4 H4 h, N
- else USART_RX_STA|=0x8000; //接收完成了
8 P" z; d) u8 q. ?! ` - }8 e. s9 Y( T7 o4 ?5 t- C t8 ?
- else //还没收到0X0D3 D; b3 U" j/ S+ C6 ?
- { ! G& M. C# H( r5 b9 K: t
- if(Res==0x0d)USART_RX_STA|=0x4000;0 a' B( a- W6 N# J4 P7 Z
- else ?5 v) G m4 @7 a) y: ?
- {; W6 V+ M0 y& m, G
- USART_RX_BUF[USART_RX_STA&0X3FFF]=Res ;8 n# p8 ~( p9 r
- USART_RX_STA++;, s6 n( V% m) e: \* P) n
- if(USART_RX_STA>(USART_REC_LEN-1))USART_RX_STA=0;//接收数据错误,重新开始接收
' R1 e3 G1 m$ E c - } 5 U% p, I6 x7 g6 E- P; a' b
- }7 v: q% y* ~) @' p. E
- } S( f* D4 i" O- |' I8 d5 d ?! R5 w
- } , D& y6 b' K# j: v1 h2 \: X- ?
- } $ T2 \2 f7 |' m6 p; v
- 6 j4 M# l8 N# m C$ m/ p
复制代码
6 ?7 x/ {& @9 D. T7 l% o; U Btime7:( D; h# R* S' Q( \
- 2 F% K/ c" M. g; ]' W
- extern vu16 USART3_RX_STA;
; y e# ^" p# Y5 w Q+ `' \1 e
$ G+ Y6 ]# i( z5 ~( j7 I( o9 G: m- //定时器7中断服务程序
2 s5 j% {. i% ^, A+ Z" T - void TIM7_IRQHandler(void)
* T1 I7 J, b7 D/ V; r/ R; y - {
6 h4 r3 G/ Q: F9 E2 f4 ? - if (TIM_GetITStatus(TIM7, TIM_IT_Update) != RESET)//是更新中断
4 C M1 w9 g: K' I! ` - { 1 ?2 ]" |+ {% b- {
- USART3_RX_STA|=1<<15; //标记接收完成& O9 f& M- b% H2 E) F J
- TIM_ClearITPendingBit(TIM7, TIM_IT_Update ); //清除TIM7更新中断标志 & R7 R5 J- ?0 x2 v. J3 [- p
- TIM_Cmd(TIM7, DISABLE); //关闭TIM7 + E J3 ?# `" h7 e6 _! n; R
- }
* }# I% `' V. a0 B3 Y4 l - }3 l Q2 F0 G- }2 F+ C3 x8 P R( |
-
" O7 n+ u P4 o0 `1 B: ` - //通用定时器7中断初始化, y* J! [9 N, s+ h9 m
- //这里时钟选择为APB1的2倍,而APB1为42M
8 y& k- l/ X" b* a( e2 ] - //arr:自动重装值。5 I: a) ~# r6 Q/ x
- //psc:时钟预分频数6 }5 r0 S; s& X( v' D4 M( u
- //定时器溢出时间计算方法:Tout=((arr+1)*(psc+1))/Ft us.. z+ p& D/ H, N
- //Ft=定时器工作频率,单位:Mhz
* m: C3 X8 I1 C& D+ B b( ~ - //通用定时器中断初始化
; J5 C9 d% A: z - //这里始终选择为APB1的2倍,而APB1为36M9 C0 e t9 \6 ~- _+ P/ L9 X- j
- //arr:自动重装值。$ Q0 c2 r3 D" Z) j/ h1 m
- //psc:时钟预分频数 % X% z2 B M: K7 n
- void TIM7_Int_Init(u16 arr,u16 psc)
L- ]$ `7 L% H - {
6 C4 P6 P+ v5 E - NVIC_InitTypeDef NVIC_InitStructure;
$ q- W8 e% D' U/ j" a - TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
" C8 r' Q; _9 \ - . w4 E1 f6 k: Q4 ^' A' Z
- RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM7, ENABLE);//TIM7时钟使能 3 J* e! W# z! a- ~4 r
-
+ |: r+ ]2 |. G - //定时器TIM7初始化
0 @' P# }) A. i2 X8 N - TIM_TimeBaseStructure.TIM_Period = arr; //设置在下一个更新事件装入活动的自动重装载寄存器周期的值 ( z# I! t5 A( q8 j$ Z G
- TIM_TimeBaseStructure.TIM_Prescaler =psc; //设置用来作为TIMx时钟频率除数的预分频值
/ \0 ~4 V) j$ X* W7 g - TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; //设置时钟分割:TDTS = Tck_tim
" n5 I( z. V* y* T2 o1 {4 K - TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; //TIM向上计数模式
+ }8 M5 F6 y9 U+ I, {1 }/ y - TIM_TimeBaseInit(TIM7, &TIM_TimeBaseStructure); //根据指定的参数初始化TIMx的时间基数单位
# J0 J; ?* r! _ H# z) U8 M2 W' T! T - 4 d4 J+ ^% |* ~% h
- TIM_ITConfig(TIM7,TIM_IT_Update,ENABLE ); //使能指定的TIM7中断,允许更新中断3 ^" f( t& W8 e) w* N. F
- ' t6 S& h. C9 M2 s
- TIM_Cmd(TIM7,ENABLE);//开启定时器7
7 ~/ }, C) x$ ^1 A7 h -
# v1 T' }6 w5 R" J - NVIC_InitStructure.NVIC_IRQChannel = TIM7_IRQn;- }! S* ~7 `7 Q& `! I& R# q* `
- NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=0 ;//抢占优先级0( W! c5 D+ l$ Z
- NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2; //子优先级2
`. X# s8 q h4 Q1 }. Q/ v2 k - NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道使能
2 Z. w- m6 B- n2 f - NVIC_Init(&NVIC_InitStructure); //根据指定的参数初始化VIC寄存器0 Z1 y4 C( ^% E; U7 C/ Y
-
6 ~! c L) w5 V) D - }
0 q* T4 X1 k% e& I4 c# t# G -
' _, 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 ?
. i' o1 G4 s7 r! ~1 U6 @- uint8_t USART2_Rx_Buf[12];6 i0 z" g) c# R3 t) n/ p
- ) J, J. I- y4 @0 j- f& Y
- void USART2_Config( void )
/ M0 m% u% r6 M+ U' g, a3 q - {
7 r, O6 V/ w9 C* V- C# _6 ?! w - /* 声明各结构体 */; y# _* I% p9 o1 ~
- GPIO_InitTypeDef GPIO_InitStruct;9 M& q) P3 B) E# t1 f7 }/ Q5 {
- USART_InitTypeDef USART_InitStruct;
, I9 U5 \. V9 q4 Q - NVIC_InitTypeDef NVIC_InitStruct;4 Q& W i a+ w( K3 L" h4 m
- DMA_InitTypeDef DMA_InitStruct;/ }: n# w( K( E, N4 R" X1 L" e& F
-
2 x; [9 U4 y1 }+ E0 q- S: w - /* 打开时钟 */3 r5 X# I5 b6 H' t
- RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOA, ENABLE );
; q+ |+ W: Q# F7 K/ I - RCC_APB1PeriphClockCmd( RCC_APB1Periph_USART2, ENABLE );
! V/ f7 G& X' ~$ B, |% f - RCC_AHBPeriphClockCmd( RCC_AHBPeriph_DMA1, ENABLE );0 o- D% Q7 u1 h, t
-
! f: B5 v! n7 ] - /* GPIO配置 */
4 R$ q" o3 p- c! \* t" T0 A - GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_PP;; i% ?' [& ~) o
- GPIO_InitStruct.GPIO_Pin = GPIO_Pin_2; //USART2_Tx:PA2
& s3 W6 `. f! x O - GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;+ M: F4 ]4 c( r3 Y& x% b
- GPIO_Init( GPIOA, &GPIO_InitStruct );
# `% k" I! S7 S5 X8 Y7 h, w - GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IN_FLOATING;0 @5 A, T5 a, v1 y% t# {2 c
- GPIO_InitStruct.GPIO_Pin = GPIO_Pin_3; //USART2_Rx:PA3; b. w! {, C) _) U
- GPIO_Init( GPIOA, &GPIO_InitStruct );
* _% Z/ p7 m( }4 P& H - * c5 q1 V+ V0 E( P2 z
- /* 串口配置 */
3 t' z% P9 e9 l - USART_DeInit( USART2 );
V+ U; v. \& M6 H( K1 q - USART_InitStruct.USART_BaudRate = 9600; //波特率:9600$ s# G+ f' }0 }" Z
- USART_InitStruct.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
- n/ {# {. m1 k4 T6 L - USART_InitStruct.USART_Mode = USART_Mode_Tx|USART_Mode_Rx;# z. w. q& [1 B U% a# X0 O% J+ [
- USART_InitStruct.USART_Parity = USART_Parity_No;8 E3 n/ y2 w, ~7 m1 x
- USART_InitStruct.USART_StopBits = USART_StopBits_1;2 p7 s/ P% E; X7 v
- USART_InitStruct.USART_WordLength = USART_WordLength_8b;
: Z2 ]% r5 l: G+ N - USART_Init( USART2, &USART_InitStruct);' ?- j o0 l5 J( `$ x
-
' g3 V B1 u' E - /* 中断配置 */% Z$ L8 B9 X* R8 Q6 _
- NVIC_PriorityGroupConfig( NVIC_PriorityGroup_2 );
" L1 r4 K* M+ \; r5 T - NVIC_InitStruct.NVIC_IRQChannel = USART2_IRQn;
0 `! k0 p+ f3 n" z9 O0 P$ o7 \8 G; b - NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;
t g& @! P% G! i4 l: I - NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 1;- g M4 p- A: q+ L7 b8 P5 ~
- NVIC_InitStruct.NVIC_IRQChannelSubPriority = 0;
$ N% C+ m0 K6 W* { - NVIC_Init( &NVIC_InitStruct );
4 |" p2 X0 w/ m - USART_ITConfig( USART2, USART_IT_IDLE, ENABLE ); //空闲中断# v) Z& [+ Y; M
- USART_DMACmd( USART2, USART_DMAReq_Rx, ENABLE ); //开启DMA接收* s4 \/ V& n. h o+ p
- ' [' t. x9 U5 h
- 8 R7 }7 O0 T8 }; P: i# d5 ]
- /* 配置DMA传输数据 USART2对应DMA1的通道6 方向:外设到存储器*/ * K3 w3 m) ~0 x9 P2 h
- DMA_InitStruct.DMA_PeripheralBaseAddr = USART2_BASE+0x04; // 设置DMA源地址:串口数据寄存器地址*/ * q* i5 o' D7 V g9 O1 a
- DMA_InitStruct.DMA_MemoryBaseAddr = (uint32_t)USART2_Rx_Buf; // 内存地址(要传输的变量的指针)
/ N0 _) r9 `# Z- K# T7 L - DMA_InitStruct.DMA_DIR = DMA_DIR_PeripheralSRC; // 方向:从外设到内存 ( p+ m4 R$ P+ }
- DMA_InitStruct.DMA_BufferSize = 12; // 传输大小 一帧数据12字节
, l/ f; S$ U. T$ o' g - DMA_InitStruct.DMA_PeripheralInc = DMA_PeripheralInc_Disable; // 外设地址不增 / T8 D; n8 Q' X$ Y! b
- DMA_InitStruct.DMA_MemoryInc = DMA_MemoryInc_Enable; // 内存地址自增 8 q0 q; A5 N5 f# O8 q
- DMA_InitStruct.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte; // 外设数据单位 / Y! L4 d L# k. U' P
- DMA_InitStruct.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte; // 内存数据单位
: [9 e, z- x% {" V( M - DMA_InitStruct.DMA_Mode = DMA_Mode_Circular; //正常 DMA模式,一次或者循环模式
$ [$ Z2 |9 I7 K3 ? - DMA_InitStruct.DMA_Priority = DMA_Priority_Medium; // 优先级:中+ G, J( w2 a+ Q4 t$ d4 C5 u, b6 O$ P
- DMA_InitStruct.DMA_M2M = DMA_M2M_Disable; // 禁止内存到内存的传输! Y+ m- J/ ~8 C% [
- DMA_Init( DMA1_Channel6, &DMA_InitStruct );- u$ w. M( X3 a3 L1 s
- DMA_ClearFlag(DMA1_FLAG_TC5);
% C* Q' ]" H- F$ F - DMA_Cmd( DMA1_Channel6, ENABLE );//使能DMA
" b0 I- B9 A) D -
4 S6 ~7 r- P/ K# b h" ~4 D$ B+ T -
! w% y- q. M, s - /* 使能串口 容易忽略 */
5 Y2 v% W/ d9 [; ~0 D9 u- Q" x! ^ - USART_Cmd( USART2, ENABLE);6 `8 Q" _, z: D' P
-
7 H: P0 g0 Q0 T5 J. n+ j - }& }" T3 s% J% K) F% y
8 `: v9 S) u* `
& J4 _% @2 Q4 N3 j4 N- + M. x; t" p' b$ t! r; j+ Z: A
- /* 串口空闲中断服务函数 */4 ^# O/ y# R( s0 c4 e$ j
- void USART2_IRQHandler( void )
0 `$ z) |- }- [! P, N' z; z) {9 I - {2 [1 o$ F! O5 T* L' Z8 \
- if( USART_GetITStatus( USART2, USART_IT_IDLE ) != RESET ) . z/ U( ~8 C- [; b0 k2 t
- {
) q: T- v. j1 H: D - USART_ReceiveData( USART2 );//象征性接收数据
1 g/ X Q- L! Y; T0 J - USART_ClearITPendingBit( USART2, USART_IT_IDLE );//清除标志位- y0 l5 z* D* Z, s
- R4 I' X1 j: Q" y# m
- }
5 Y! K0 a( ~0 [% l! x8 J6 F( b - }" ~1 T: O( I8 l }! `/ v
- //也可以在中断服务函数中扩展更多的功能
; ~, 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
|