在日常中,串口经常作为和上位机通信的接口,进行打印信息方便调试程序,有时也会作为模块的驱动接口,所以总结一下串口的几种使用方法对以后的开发还是很有帮助的。
! 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- void USART1_Init( uint32_t btl )
8 ~. m1 v+ |! |+ N - {& T0 d' e* G( p) |4 G7 q+ m
- GPIO_InitTypeDef GPIO_InitStruct;
7 O* C D, H7 f. j3 {; l - USART_InitTypeDef USART_InitStruct;
# j5 e7 E' l0 H @' G- m: ? - 6 R* ~3 ]- H5 b. _
- RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOA|RCC_APB2Periph_USART1, ENABLE );5 t+ @7 m3 X. n- M, r
-
d7 V5 k5 a% l N _6 M - GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_PP;
0 _1 b2 F6 T- D F - GPIO_InitStruct.GPIO_Pin = GPIO_Pin_9;//Tx
! T" T+ w7 Y, s1 F - GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
, Y$ _7 f$ B( ^( G$ |4 c- m4 ` - GPIO_Init( GPIOA, &GPIO_InitStruct );3 |+ {7 [& s* H) r4 l
- GPIO_InitStruct.GPIO_Pin = GPIO_Pin_10;//Rx
0 t# ~$ A4 c' F6 T - GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IN_FLOATING;
0 K! l+ _4 ^: i) ~1 D - GPIO_Init( GPIOA, &GPIO_InitStruct );
' W O0 T: z2 o: [) E" x* ` -
; _& l# X1 b c8 K" `1 f O - USART_InitStruct.USART_BaudRate = btl;
# k, @- _* h8 {3 l - USART_InitStruct.USART_HardwareFlowControl = USART_HardwareFlowControl_None;8 C& G! t% }$ L N" a9 F
- USART_InitStruct.USART_Mode = USART_Mode_Tx;/ R2 y( V5 M/ d4 l5 R
- USART_InitStruct.USART_Parity = USART_Parity_No;+ F5 N+ L5 e i: Y) ~
- USART_InitStruct.USART_StopBits = USART_StopBits_1;
& s$ H. Y" l3 D - USART_InitStruct.USART_WordLength = USART_WordLength_8b;
; J, I3 P- x: A! O8 U - USART_Init( USART1, &USART_InitStruct );
7 R% c2 i$ j( d$ |( N0 K, i4 P: o, j -
# E9 ^6 x+ s" [ - USART_Cmd( USART1, ENABLE );
( v/ V$ N2 Y, c# y -
/ k5 {' u' u) J+ w* F% q. | - }
) f V( P: x- E8 N6 r: T8 f
7 L- c& y) ^. y) t1 Q' I% ^( E- //串口重定向,直接利用printf函数输出调试信息
; e# }$ M8 C3 g. w$ X - int fputc( int ch, FILE *f )
1 }6 W5 m' ^1 j' Q$ w7 r - {: j8 P" z( n& R3 }' a3 p- c
- USART_SendData( USART1, ( uint8_t )ch );
i7 X+ _& t3 v. | - while( USART_GetFlagStatus( USART1, USART_FLAG_TXE )!=SET );
" P4 X* \) L5 t7 B% e S3 A | - return ch;, X% G6 o( h/ {
- }1 C3 z1 \$ m2 u. w) B% {) _
- + B3 e8 E+ z A! q6 @0 ]
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
: ~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
- " f- q2 o; N8 G1 A9 m1 a \
- void USART1_Init( uint32_t btl )* o" l# W; }+ q o: B* V
- {& r. Y" ~* Q% |& o( p' q
- /* 结构体声明 */
" K$ G1 K* D8 m; X, J - GPIO_InitTypeDef GPIO_InitStruct;- t0 w& W5 T: n |* n; O! F
- USART_InitTypeDef USART_InitStruct;' Q- j# h. f/ q
- NVIC_InitTypeDef NVIC_InitStruct;* `. T6 i5 ]) {3 {/ r. b4 k
-
/ {7 W4 j, r q - /* 打开时钟 */+ ?$ B" i* `' b1 h* J ?
- RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOA, ENABLE );
& j5 S# x! i+ f9 D - RCC_APB2PeriphClockCmd( RCC_APB2Periph_USART1, ENABLE );
, D' P4 w- C% ~4 c \ -
, T# `5 Y7 ]& Q! o - /* GPIO配置 */
3 |$ e( b8 o" O- s6 I - GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_PP;6 m& G! j R; i3 Y
- GPIO_InitStruct.GPIO_Pin = GPIO_Pin_9;//Tx
& E: [( ?9 m' @9 h2 O- V: U - GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
- i* R1 t0 l* G* Q4 R, u - GPIO_Init( GPIOA, &GPIO_InitStruct );
! h1 t" V% h% y- b/ G0 C - GPIO_InitStruct.GPIO_Pin = GPIO_Pin_10;//Rx+ i% i( u) D, c) p
- GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IN_FLOATING;! N$ O- ^: [3 N$ A) S; ]/ p
- GPIO_Init( GPIOA, &GPIO_InitStruct );3 [% A2 W5 v9 c" V9 p/ |
- 6 ?4 i$ m9 n" R2 w3 C: B
- /* 串口配置 */0 _) U2 t& n# \" ^4 a0 P- l$ k
- USART_InitStruct.USART_BaudRate = btl;5 F* K+ b, z8 Q+ n, ^- m$ _# z
- USART_InitStruct.USART_HardwareFlowControl = USART_HardwareFlowControl_None;. p2 M6 I% P/ u' }
- USART_InitStruct.USART_Mode = USART_Mode_Tx|USART_Mode_Rx;//收发模式* T. H/ x; t! ]* F
- USART_InitStruct.USART_Parity = USART_Parity_No;- N3 @, q2 b' B) E# W
- USART_InitStruct.USART_StopBits = USART_StopBits_1; K; w9 B/ |0 `
- USART_InitStruct.USART_WordLength = USART_WordLength_8b;
# |2 L- n" N) W - USART_Init( USART1, &USART_InitStruct );2 Y# G& y K; B" P
- $ H. B5 ^- Z9 O( a" b) q% h
- /* 中断配置 */9 [* u' k- X9 n @' f0 @
- NVIC_InitStruct.NVIC_IRQChannel = USART1_IRQn;) B4 q, }, N: w9 a6 T/ e8 a: ~. c
- NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority=3 ;: A9 M2 X4 K( K- f: p
- NVIC_InitStruct.NVIC_IRQChannelSubPriority = 3;
9 W) q8 B& ] g# e, [ - NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;
1 q6 {- k+ E( r0 g2 ~1 `3 u - NVIC_Init(&NVIC_InitStruct);
/ Q4 q) F. B5 V2 Z5 x; ~3 u - USART_ITConfig( USART1, USART_IT_RXNE, ENABLE );//接收寄存器非空触发中断; I, ^$ g, `$ y
- 4 O, |0 H# i1 p0 S/ E( |9 x9 b
- /* 使能串口 */4 m, i/ n7 D$ Z. A! {4 w8 ?: a
- USART_Cmd( USART1, ENABLE );
4 i& d; }" x/ y" u, ^3 Y! J -
( M+ a" l+ f' v - }
2 n- k s5 P& H) m - ; d# y9 w, H; t; Y( c! v0 k, s7 p
- 5 E5 @4 p' u W* r' V2 d) z
- volatile uint8_t n=0;
+ l- i, X V0 z/ u: H - //接收缓冲区5 b# v8 m! V y4 l
- uint8_t USART1_Rx_Buf[100];: s" \2 k3 Q( |: @
- void USART1_IRQHandler( void )1 w% M/ ]" A# @1 j5 v- R
- {3 V5 A9 ~, _% P3 P3 ]4 D
- if( USART_GetITStatus( USART1, USART_IT_RXNE )==SET )
0 t, M( U2 ^( [, q/ D" T - {0 X( f; f# f' Y, x5 b
- USART1_Rx_Buf[n]=USART_ReceiveData( USART1 );7 q3 S# m0 X/ _5 R
- n++;
% C; h) h9 V' ^. }2 r6 G4 m! _7 H - }
6 @- L; b4 K5 K( a5 x# q! G - USART_ClearFlag( USART1, USART_IT_RXNE );9 l8 s$ F; A, n1 z" |! S' |9 L- G
- }: `# 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中断服务程序8 g( ~/ E' [, O9 s: y: N
- //注意,读取USARTx->SR能避免莫名其妙的错误
% S$ W7 q2 U2 z8 O& K4 Q - u8 USART_RX_BUF[USART_REC_LEN]; //接收缓冲,最大USART_REC_LEN个字节.
& ?; I2 u, R" U! @$ E - //接收状态/ k% _# x& B! {: K
- //bit15, 接收完成标志
: ^1 A8 X: v* `% t7 W, J& e4 ? - //bit14, 接收到0x0d
2 \4 ^3 S* W4 |6 n% A7 h* q - //bit13~0, 接收到的有效字节数目0 |3 n O" [) |
- u16 USART_RX_STA=0; //接收状态标记
- u9 W- Y4 _! o6 n: O) A -
^0 \9 n) I" J- V - void uart_init(u32 bound){
5 n+ D# e3 ?, K5 M# Y - //GPIO端口设置
/ `% X# K9 D x( t. c8 E4 m! e* p. W - GPIO_InitTypeDef GPIO_InitStructure;# U9 m) L2 |0 K% v% R5 V
- USART_InitTypeDef USART_InitStructure;
* j; Z8 u/ f& N2 J0 |9 q6 Y - NVIC_InitTypeDef NVIC_InitStructure;
: ~% ~+ C* H r* G -
) S" M' v6 F- Y! J1 R& B - RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1|RCC_APB2Periph_GPIOA, ENABLE); //使能USART1,GPIOA时钟
$ ~4 n2 m) S9 z1 f6 I- p -
: w6 Y! `4 `( R: p+ z - //USART1_TX GPIOA.9
. I% C, v# L7 k - GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9; //PA.96 f) h" T( p K, b7 K" e
- GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
% k% b' T; m# J9 m ]1 m - GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //复用推挽输出7 D8 d N. _: \2 y2 G, k X( O
- GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化GPIOA.9; e/ J/ o; C- L% ~6 P4 a p" w* `1 Q
- 3 X% k8 t+ J R6 Y" n
- //USART1_RX GPIOA.10初始化
" p! K6 ? w$ G* Q$ k- a9 n - GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;//PA107 t+ w5 G& {7 p B* p+ L% U# C
- GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;//浮空输入
4 N+ z [1 J8 `3 n1 U& x- Z+ P4 A, U - GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化GPIOA.10 0 ~7 {+ J7 S7 \0 {9 _; V
! \$ p- f$ \, n2 w& h- //Usart1 NVIC 配置6 `/ J1 y3 _0 T' I
- NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
: d9 t2 |4 W- z& H6 |2 w w - NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=3 ;//抢占优先级3! j: O- S, V& m# o' b
- NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3; //子优先级3
7 D! w+ p2 M0 d. w! \ - NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道使能0 G8 n0 i, L* B$ ^
- NVIC_Init(&NVIC_InitStructure); //根据指定的参数初始化VIC寄存器
- L: b2 O' }9 U/ p6 n {$ b1 S - 4 r7 U+ a& J+ N( H5 P7 Y
- //USART 初始化设置
/ U3 o" O" o* ]* X% j0 c7 M
) y! W/ }4 w* z/ [$ Y2 ^) l; p- USART_InitStructure.USART_BaudRate = bound;//串口波特率
: _' x& M) q- j - USART_InitStructure.USART_WordLength = USART_WordLength_8b;//字长为8位数据格式
2 j! g( z( z7 x. b$ |7 M - USART_InitStructure.USART_StopBits = USART_StopBits_1;//一个停止位
- B. _! U) B& L5 x o - USART_InitStructure.USART_Parity = USART_Parity_No;//无奇偶校验位. T C3 m% i4 V. o% o2 h" V9 P7 {
- USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;//无硬件数据流控制; _! o2 O- ]+ q
- USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx; //收发模式
l; \& ?) x! Q - 6 W' N# @# h! o& U
- USART_Init(USART1, &USART_InitStructure); //初始化串口1
; J+ n5 \/ [* I8 z( w - USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);//开启串口接受中断
8 A* b a" `+ ?9 c - USART_Cmd(USART1, ENABLE); //使能串口1
l4 W0 k4 c/ w! _" V& \
/ ]5 j/ m; {5 o- }
1 ?2 G0 ^8 h2 e* o( d - 1 i) J" I' s" Z) n) D" D+ T
- void USART1_IRQHandler(void) //串口1中断服务程序
, R/ F# J3 N) h% s7 { - { d& @. q1 E1 J& `* g- D
- u8 Res;
9 c3 J7 I T$ e- O* u
* ]& d& n" H, i9 F$ P- if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET) //接收中断(接收到的数据必须是0x0d 0x0a结尾)
2 i2 T# S7 s; [% `) F& N/ e - {
" s0 b6 B; M$ e! e7 @" f$ P - Res =USART_ReceiveData(USART1); //读取接收到的数据
+ w9 _1 J7 L( r. X -
( H$ h' n. {7 c - if((USART_RX_STA&0x8000)==0)//接收未完成. N$ b1 |: q8 {- P A
- {! W: e, G5 }# H2 o/ ^( o! A
- if(USART_RX_STA&0x4000)//接收到了0x0d
/ Z( V. q- |0 b- O \4 { - {+ b$ Z# P% A# }
- if(Res!=0x0a)USART_RX_STA=0;//接收错误,重新开始% R6 w/ w4 N, {* H+ y/ {5 O8 f: i
- else USART_RX_STA|=0x8000; //接收完成了 4 Y8 S; o$ j7 S! C% i" R7 k9 g
- }1 I/ U/ q% U% F
- else //还没收到0X0D
1 _+ U `0 ]( v8 h8 N3 e1 _ - {
$ |3 `$ }/ h. P - if(Res==0x0d)USART_RX_STA|=0x4000;
7 p* ?! k; ~+ O, b - else
9 K, ]/ B! F2 o* T - {
: p( W: p& J8 ?9 p3 z; [5 O2 U - USART_RX_BUF[USART_RX_STA&0X3FFF]=Res ;8 \% ~# f& V! R$ u+ h$ X- R
- USART_RX_STA++;
2 k5 K: {$ _4 ?9 s. t- r; u- c8 K - if(USART_RX_STA>(USART_REC_LEN-1))USART_RX_STA=0;//接收数据错误,重新开始接收
' N3 m$ A; N& g. v, o0 k: N - } / U9 c8 E0 t1 N+ ^
- }
* P+ ?4 Q* S. Y# p# B- r* g - }
2 V4 H# n! a4 T - } ' u2 L7 e* _9 b! n" }
- }
6 x: \5 e9 s( p) \% ]: {- }4 s - / 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
" v. f2 c% G3 Z f- extern vu16 USART3_RX_STA;
6 R2 L1 {8 H& J4 H9 T+ n
, m- o3 q7 P+ B. v5 [5 J: F- //定时器7中断服务程序 4 x0 S# D% Y+ _
- void TIM7_IRQHandler(void)
9 V- @& b/ c9 ]4 ]" Q# O - {
+ f! q9 L8 u I$ G; G8 j: X - if (TIM_GetITStatus(TIM7, TIM_IT_Update) != RESET)//是更新中断, d1 ]0 m2 k% ]) C, D1 m6 \! ~
- { 6 M: d$ \+ F* o$ k& P# f2 w; h
- USART3_RX_STA|=1<<15; //标记接收完成9 n. n0 H8 U1 C g. P! ^
- TIM_ClearITPendingBit(TIM7, TIM_IT_Update ); //清除TIM7更新中断标志 , @2 {1 H: ~+ O( `- T8 F
- TIM_Cmd(TIM7, DISABLE); //关闭TIM7 ( u* t; D, f# Z" ~% c" L
- }
/ [' y/ r& a6 c - }
1 ]: ?6 p a- x, q+ c6 Q3 P" {" l" k - " L6 n$ ?% w" Z6 c( q2 B. Z1 `6 z, u
- //通用定时器7中断初始化2 i* l; Q" ^0 i0 ]+ u% T/ ]) j
- //这里时钟选择为APB1的2倍,而APB1为42M
5 y- k* q o: Q5 c' T8 M* y - //arr:自动重装值。
/ j+ x! `+ b1 u$ [* o* k* I - //psc:时钟预分频数9 }1 g ^/ r9 C5 _
- //定时器溢出时间计算方法:Tout=((arr+1)*(psc+1))/Ft us.
7 Z& k6 S8 {& M o6 `2 w8 q! K - //Ft=定时器工作频率,单位:Mhz
f% Y8 a* J% d+ A' n- \" | - //通用定时器中断初始化
5 ]7 M3 i( p' U; S - //这里始终选择为APB1的2倍,而APB1为36M
6 `' r! I8 G4 V+ e- G - //arr:自动重装值。$ Q( X7 _6 @ q( } _3 P9 P
- //psc:时钟预分频数 * X# ]4 C6 R9 v1 a3 P% w
- void TIM7_Int_Init(u16 arr,u16 psc)
( u6 N/ \7 V' g* i2 ? - {
* A) R% a5 s, X9 y - NVIC_InitTypeDef NVIC_InitStructure;0 ^8 X; C4 n2 U7 J! T
- TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
, j9 D* g- {3 q6 |0 @0 S - % G1 H8 a7 s4 S7 A( e
- RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM7, ENABLE);//TIM7时钟使能 , N2 w/ S9 |9 ~5 M2 D/ l
-
5 z, Y$ S0 C$ J- l: m% [* g; Z4 @ - //定时器TIM7初始化
3 r; |9 l1 o9 Z" ~& K5 K5 q - TIM_TimeBaseStructure.TIM_Period = arr; //设置在下一个更新事件装入活动的自动重装载寄存器周期的值
0 U. e" K8 F: k% u - TIM_TimeBaseStructure.TIM_Prescaler =psc; //设置用来作为TIMx时钟频率除数的预分频值2 ]' r' F3 c4 n' c& E" f
- TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; //设置时钟分割:TDTS = Tck_tim t8 J4 G2 @, d4 B$ R2 p
- TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; //TIM向上计数模式
& Q7 C2 v- a g# O8 V! q: x$ f! j1 k - TIM_TimeBaseInit(TIM7, &TIM_TimeBaseStructure); //根据指定的参数初始化TIMx的时间基数单位
$ F' s- a5 T b; m2 t1 e' [: z/ X: D -
' O7 `/ O# [1 c2 R( [' K - TIM_ITConfig(TIM7,TIM_IT_Update,ENABLE ); //使能指定的TIM7中断,允许更新中断
0 C+ R& ? x: @; }& ]$ u - * o4 p& a: l& X, i5 m5 u
- TIM_Cmd(TIM7,ENABLE);//开启定时器7
& g" D" q9 N5 H- p; q -
* L8 i6 ]6 w0 x# z2 b6 N - NVIC_InitStructure.NVIC_IRQChannel = TIM7_IRQn;
1 s9 p0 h# t" h S - NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=0 ;//抢占优先级0
+ j* ^7 ~7 x+ s1 E; |: A5 z: D - NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2; //子优先级2
7 t* a/ B# b" H8 K - NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道使能
; t, z1 U; h0 ^2 B/ J - NVIC_Init(&NVIC_InitStructure); //根据指定的参数初始化VIC寄存器
1 o) S4 ^- J. T& U) g -
0 }9 l/ h- x6 b9 t& Y4 J - }
4 I) U) {$ c( W" M -
- 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- * z5 D s: h/ q6 x3 F! I- K; J
- uint8_t USART2_Rx_Buf[12];
! S, J5 s% F8 u3 { - + {( X& U4 @ D4 P
- void USART2_Config( void )
# w- G) M7 |$ h+ o - {
! ]# ?5 h6 w, \ - /* 声明各结构体 */+ c8 `" f+ T& I* M
- GPIO_InitTypeDef GPIO_InitStruct;+ Y' f1 N3 o3 @( W; R9 i
- USART_InitTypeDef USART_InitStruct;
2 C. q4 j& j. d' n1 i2 a5 m) z - NVIC_InitTypeDef NVIC_InitStruct;
+ Z% E" k% `; N$ j5 q - DMA_InitTypeDef DMA_InitStruct;
- J! Y) }) C% e# {% C3 J -
0 |1 s6 `/ _, y4 d - /* 打开时钟 */
! n4 M2 B p& }$ n M( o$ h8 l7 A - RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOA, ENABLE );
9 W& [% P8 f' N L - RCC_APB1PeriphClockCmd( RCC_APB1Periph_USART2, ENABLE );3 s+ G0 p& K1 C3 h( ?
- RCC_AHBPeriphClockCmd( RCC_AHBPeriph_DMA1, ENABLE );% i7 Z( \$ m9 u4 S- W
-
! z3 F: V6 G- v2 ]2 a6 q - /* GPIO配置 */% J/ ~' q6 C9 Y% J- n# t
- GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_PP;3 E6 U9 V# J7 D: i2 |+ p6 W* J. _* j
- GPIO_InitStruct.GPIO_Pin = GPIO_Pin_2; //USART2_Tx:PA2
8 i% Z2 s9 X3 q4 s - GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;, e$ K& Z T* K7 r- l0 S1 a& ^+ Y% j
- GPIO_Init( GPIOA, &GPIO_InitStruct );! u# w" `% d0 N- p$ A2 _$ {
- GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IN_FLOATING;- t+ K: u& Q1 Z7 s
- GPIO_InitStruct.GPIO_Pin = GPIO_Pin_3; //USART2_Rx:PA3$ K$ M' Z- E+ e5 O5 h
- GPIO_Init( GPIOA, &GPIO_InitStruct );
: R4 f3 s& u' L - / F9 a+ o' i9 x% _/ N
- /* 串口配置 */
0 z0 o2 b! {8 Z+ b/ ? - USART_DeInit( USART2 );
1 f# P9 A0 s6 F# Y - USART_InitStruct.USART_BaudRate = 9600; //波特率:9600
5 G9 J: B+ Q( Q - USART_InitStruct.USART_HardwareFlowControl = USART_HardwareFlowControl_None;& {5 V) L3 V( h8 \
- USART_InitStruct.USART_Mode = USART_Mode_Tx|USART_Mode_Rx;
0 _3 A h% x& {7 x6 W+ ^; Q - USART_InitStruct.USART_Parity = USART_Parity_No;
* @+ e" o7 w8 B, ~" F- w - USART_InitStruct.USART_StopBits = USART_StopBits_1;
* g: c6 d7 `' N2 ]& U) s - USART_InitStruct.USART_WordLength = USART_WordLength_8b;) D3 q, S$ K$ ^
- USART_Init( USART2, &USART_InitStruct);; K$ T6 \- ]0 z
-
4 |) B& Z; ]! l% u3 s - /* 中断配置 */( p% n+ i$ i* p9 R4 _" z/ v
- NVIC_PriorityGroupConfig( NVIC_PriorityGroup_2 );
S+ w; R; @% _4 [ - NVIC_InitStruct.NVIC_IRQChannel = USART2_IRQn;
3 M5 _1 [- t* G0 z - NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;
/ y) `' e5 B; c5 p - NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 1;/ O: I/ G% s' m9 k/ e: I
- NVIC_InitStruct.NVIC_IRQChannelSubPriority = 0;
3 K! i9 Y5 j3 H, L0 M- b! X: u$ m - NVIC_Init( &NVIC_InitStruct );5 [. f5 `# m+ G% u2 g- Z' Z9 v
- USART_ITConfig( USART2, USART_IT_IDLE, ENABLE ); //空闲中断8 {, }4 v3 f% |) T/ J8 `/ K
- USART_DMACmd( USART2, USART_DMAReq_Rx, ENABLE ); //开启DMA接收 Y- @: c, f4 w' {/ e9 I5 r1 U: c5 c
- 9 g9 K8 q# G( p/ G2 M
-
8 ` r3 z1 |7 `% O% ~* D" i" r. T - /* 配置DMA传输数据 USART2对应DMA1的通道6 方向:外设到存储器*/
4 E1 J6 W5 z2 p- p - DMA_InitStruct.DMA_PeripheralBaseAddr = USART2_BASE+0x04; // 设置DMA源地址:串口数据寄存器地址*/ 8 b( w% h+ J \6 l
- DMA_InitStruct.DMA_MemoryBaseAddr = (uint32_t)USART2_Rx_Buf; // 内存地址(要传输的变量的指针)
: A+ i% s! V& K; ?7 S - DMA_InitStruct.DMA_DIR = DMA_DIR_PeripheralSRC; // 方向:从外设到内存
3 k' g' ^6 J3 v$ g - DMA_InitStruct.DMA_BufferSize = 12; // 传输大小 一帧数据12字节 $ V& t6 J [& Y j% {0 U
- DMA_InitStruct.DMA_PeripheralInc = DMA_PeripheralInc_Disable; // 外设地址不增 # j7 v' N& ]- H. m* b6 }. T9 V+ w
- DMA_InitStruct.DMA_MemoryInc = DMA_MemoryInc_Enable; // 内存地址自增 ( }* }# N0 k6 N8 Q ~4 ?3 |
- DMA_InitStruct.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte; // 外设数据单位 6 ~. b. O; u$ C
- DMA_InitStruct.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte; // 内存数据单位 : E" W; m0 w+ C5 k0 _
- DMA_InitStruct.DMA_Mode = DMA_Mode_Circular; //正常 DMA模式,一次或者循环模式
0 P# R" D" ~- M5 |* T - DMA_InitStruct.DMA_Priority = DMA_Priority_Medium; // 优先级:中8 O& p6 N& d8 u0 r. u" _2 m1 f
- DMA_InitStruct.DMA_M2M = DMA_M2M_Disable; // 禁止内存到内存的传输
/ }8 z/ u) p- \# p9 b9 b - DMA_Init( DMA1_Channel6, &DMA_InitStruct );0 b& C, v/ ^' t
- DMA_ClearFlag(DMA1_FLAG_TC5);5 M3 @& K, j& }7 N- X
- DMA_Cmd( DMA1_Channel6, ENABLE );//使能DMA
- j8 P0 D5 r; c9 V( @ - / @& M# Z" A! P1 l
- ! d) ?) m n) d" \+ |
- /* 使能串口 容易忽略 */8 Q$ |/ o/ p; x0 t7 ^( j
- USART_Cmd( USART2, ENABLE);
* V2 G1 q1 W/ v -
/ M/ H' v+ k% T, A" h - }
( B" u. \1 n' A1 |3 m7 } - : _: g" y% ^+ I1 O& }" K
- " Y6 E2 \0 e1 Y, m9 M5 x
5 G1 M/ c4 C) O" ~; Z, z" }; c5 i- /* 串口空闲中断服务函数 */: q0 K" Z0 \' K
- void USART2_IRQHandler( void )
# s b" J n" s$ R/ e) ~1 z1 B1 l - {
$ V0 H5 X/ ]3 n0 o8 ?2 [ - if( USART_GetITStatus( USART2, USART_IT_IDLE ) != RESET ) : e N6 t1 I' o+ r
- {
* T% l- Y$ c6 b: U5 M - USART_ReceiveData( USART2 );//象征性接收数据# ?. x. ?8 f- h! w# c, h
- USART_ClearITPendingBit( USART2, USART_IT_IDLE );//清除标志位
( H7 |7 G: u/ S/ R, T -
2 ~! i0 n3 A1 X: ?) P4 a! v - }
7 k( n( f1 j7 s! b - }
: g {! _: n% G3 d' S - //也可以在中断服务函数中扩展更多的功能
' }% 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
|