14.1 USART定义* \) z9 y/ I; N! d8 S- w5 N
: P4 I* p% {2 e; g1 Z$ x0 j& LUSART(Universal Synchronous/Asynchronous Receiver/Transmitter,即通用同步/异步收发器)串行通信是单片机最常用的一种通信技术,通常用于单片机和电脑之间以及单片机和单片机之间的通信。( Y: z3 R4 U7 ?5 k. i2 u
# T! w9 _ c' U3 D1 [
( x% _6 n& W0 g' ^
14.2 USART串行通信协议
! X: x1 s5 w' m/ Y8 p6 \: m" q9 y% d5 [2 v3 Q
14.2.1 波特率和数据格式: _, T b6 v7 E* a( E" z
- z' {4 [7 U; ~4 D: t
USART通信中的同步通信功能很少用到,大多情况下只采用异步通信,只能实现异步通信功能的接口就称之为 UART。UART 通信通常以字节为单位组成数据帧,由通信收发双方根据预先约定的波特率(传输速率)进行通信。
3 {" y5 [5 X- P( y9 h, @" A6 T4 L/ D8 O0 [/ O+ N, l" c( a2 P
波特率表示每秒发送二进制数据位的速率,单位是 bps,即 位/秒,波特率越高,传输速度越快,常用的 UART 通信波特率有2400,4800,9600,115200 等等。在进行串行通信之前,通信双方需要设置波特率保持一致,否则不能正常通信。
/ }% b, h) Q1 [# ^3 ~
2 F4 e# I" W( F" o( |单片机标准串口进行通信时,没有数据传输时通信线路保持高电平状态。当要发送数据时,先发送一位0,用以表示开始发送,叫做起始位。然后再按照低位在前,高位在后的顺序发送8位数据。当8位数据发送完毕时,再发送一位1表示停止位。' F% h2 A, u) w$ C2 e
( K+ d' U/ ?, W% u# p对于接收端而言,开始时传输线路一直保持高电平,一旦检测到低电平,便准备开始接收数据。当接收完8位数据时,便检测停止位,检测完毕后,表示一帧数据发送完毕,开始准备接受下一帧数据。为了确保数据准确性,通常会在数据位之后设置校验位。
, W! g1 U& j3 w- h0 w7 W* t& p; a+ j: K7 e# T1 S
串行通信的数据帧的格式由起始位、数据位、奇偶校验位(可选)和停止位等部分组成,如图:
# q" S8 k- b' \1 \ K% z5 M5 z4 N0 K v2 _' W. |
: ^5 y& d* I5 o/ ~& A
) ]5 |. P& c/ K1 ~ [3 _; B* c9 `
8 c" v, C% H5 D( L& S14.2.2 TTL通信接口和RS232通信接口
! x. {6 V6 g( q+ T' n! t/ n, p! o% x; V7 X
电脑和单片机之间进行串口通信,通常使用USB转UART芯片,将USB通信协议转成UART协议和单片机通信。
/ ^7 r+ [+ ` [/ k4 u" p* D$ d- j' Z3 L, t4 D
" W8 O" o9 Q& J
& h* j- C' F# E( A$ i0 h7 c
14.3 USART配置步骤
; ~# O" l: J; n! ]
/ ~1 X% ~/ t& v4 ^; [1.时钟使能! D9 W. C' O7 e
2.设置中断分组* Y9 d6 L/ s. ]
3.串口复位
7 v3 a# ^$ W0 z% V6 u) u. V$ v4.GPIO初始化(TX,RX引脚)
& u& {+ w- {" ^' a9 Y5.设置中断分组" ]5 x8 U+ X* x5 X) S/ q% i* M, Q
6.串口初始化. ]/ K! X7 ~# P6 T' I7 \
7.开启中断 m+ j8 h1 m- R: L" M& \9 q
8.使能串口
% V* E( w h) i$ O
, h1 C* g }8 n* e
) H1 o( ~. O m6 [# W5 A+ C2 y' \ w1. 新建两个文件,usart.c 和 usart.h" G( K9 ^/ @& B/ N9 i5 D
3 Z6 n' R9 |/ s
( L1 L. O- _1 H6 N
- M! K- o9 R0 l0 q! D
* \" V7 @. f9 {. _) E* Q m2. 在头文件 usart.h 添加下面代码:- #ifndef _USART_H
1 a4 [- x) v1 ~ - #define _USART_H1 A- a! L$ h- x( u1 ?
- #include "stdio.h"; S# L6 g+ J4 ~* [) R- Q
- #include "stm32f10x.h" 4 f9 s. E9 t) w$ B" Q$ O" [4 R
- , D U0 U* ~$ q& ~
- #define USART_REC_LEN 200 //定义最大接收字节数 200" P; g8 Z% @1 {; `' p# ?( Y3 m
- #define EN_USART1_RX 1 //使能(1)/禁止(0)串口1接收1 a! d# ?1 [% o2 o! B, ~
- 5 E% E W( \5 ^- F6 O1 I b! E! M
- extern u8 USART_RX_BUF[USART_REC_LEN]; //接收缓冲,最大USART_REC_LEN个字节.末字节为换行符 ' u- S& w' R0 e$ P; \! @6 `% @
- extern u16 USART_RX_STA; //接收状态标记) W9 e* d4 d) W& z
- 7 C7 q: ^) I, N) I, L- J ^. k, A# @- V% ?
- void usart_init(u32 bound);4 b6 E2 h4 \7 q0 A& d: u
7 Q% T/ ^$ c; _. ^- #endif
复制代码
; N; f, b$ t5 I; o% V( o& _3 X3 n& k
& t3 w* N7 E( t) X7 L
d$ s; C8 B5 G- {- v( Q; g
8 w9 a$ o- [' W3. 把 usart.c 添加到工程中1 y+ @$ v1 `6 }) d3 K- B9 W
. w1 ^- `4 l, c) r# x6 M& U% z$ C
! U1 j& u/ n( q& F# [
% x: f1 ~% r9 N/ ~5 ~/ e/ |8 \; B2 u- H
4. 在 usart.c 中添加以下代码:
, {& W* a2 ` K. K! W. x* H3 d( O7 M' i: |5 Z
- #include "usart.h"
$ T, t& z G: e - : E$ G. C2 I# H0 X
- #pragma import(__use_no_semihosting)
& G4 |, Q6 t, B, ^ - //标准库需要的支持函数0 h3 a, O$ e1 } B. k" }
- struct __FILE9 J! J0 H7 H" L* |. i g
- {3 B# o; \& H* a0 U9 L K
- int handle;) p2 `5 h4 O( I5 H* B* K/ _
- };# p* ]. ^0 q& j u# l- I
, O7 P( z' O4 I' H- FILE __stdout;$ N4 |. q5 O* b( q7 R& ?
6 [/ M& z- n6 M) w- //定义_sys_exit()以避免使用半主机模式
. [. n9 p) |! D' s7 k4 i& _ - void _sys_exit(int x)
; b7 V1 L& w' q - {
$ H8 ?2 b! H. @; v9 q0 b D6 n4 T" ` - x = x;
$ s1 `. D8 U. s2 u, A - }
0 Z9 V% U2 u0 D# r - 2 y5 E$ K7 q# Z7 B. m+ o
- //重定义fputc函数( ~9 [+ b3 o; l7 q ]/ e
- int fputc(int ch, FILE *f)
/ E( F. y5 k' v5 s5 M - {- S5 v @* }$ e/ ~
- while((USART1->SR & 0X40) == 0); //循环发送,直到发送完毕
) C+ p9 m$ }! E - USART1->DR = (u8)ch;# C# s8 p5 @3 ~
- return ch;0 _8 F& K) X5 H, y
- }
* }: R$ j5 @$ @; p - ) Z+ r; g1 X& F2 a, y
- #if EN_USART1_RX //如果使能了接收' M) i5 ?; d7 N5 p
- //串口1中断服务程序
9 ~1 h$ z9 d6 L. _6 d - //注意,读取USARTx->SR能避免莫名其妙的错误
2 X5 L' U& L% \* y4 }2 r - u8 USART_RX_BUF[USART_REC_LEN]; //接收缓冲,最大USART_REC_LEN个字节.
/ j( E+ i. x! [ - //接收状态- p) k6 z4 T: h; C, O3 [+ I
- //bit15, 接收完成标志, r1 y) d) { |4 y/ Q$ b
- //bit14, 接收到0x0d
/ k# ?5 \7 K1 x2 r# M& D - //bit13~0, 接收到的有效字节数目
2 w+ ^( D- k* A) J0 e - u16 USART_RX_STA = 0; //接收状态标记
6 l9 h: t( N- K
- @* }4 v7 N% W. Z# k1 L- void usart_init(u32 bound){+ u) l" \: x) {0 \/ Y
I/ d. |/ `( R6 {# g4 \( c- //GPIO端口设置/ y7 c% `0 w0 Q3 _
- GPIO_InitTypeDef GPIO_InitStructure;
- Z' x3 E: A* f2 \& ? - USART_InitTypeDef USART_InitStructure;' o8 E* E# A* I$ E( [
- NVIC_InitTypeDef NVIC_InitStructure;4 j" a9 S7 M+ {2 J& d6 M4 M
+ a l! j* |" W. e8 C. _: d- //使能USART1,GPIOA时钟
I( n, F4 i8 P6 {* h - RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1|RCC_APB2Periph_GPIOA,ENABLE);/ N9 F, |3 i [' d v. k0 @+ m" U2 i
- NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //配置中断优先级分组' @4 d$ M! {6 J; V3 J% `! c
2 y& I8 I: I8 P0 D0 h- //USART1_TX GPIOA.9* T: _* b8 m" n! f- M$ L8 }
- GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9; //PA.9
* `. O; D" y& E) q% v# X# i - GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
+ A* m9 E. L) o. u6 r - GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //复用推挽输出& p, y5 Z$ F9 G m* Z6 t! o: [* |
- GPIO_Init(GPIOA,&GPIO_InitStructure); //初始化GPIOA.9" r' B7 i, f3 r5 h0 V$ O
- 1 R- z& D6 N7 [3 j
- //USART1_RX GPIOA.10初始化
' W J- h6 Q$ a - GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10; //PA.10& M8 G- E0 }$ r) O; H
- GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; //浮空输入
+ I0 C: R2 O' v; Z! w( V - GPIO_Init(GPIOA,&GPIO_InitStructure); //初始化GPIOA.102 _7 H# _) q' M
7 h6 `/ Z I) L, y* \- //Usart1 NVIC 配置
8 m8 h% q3 }0 q - NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
- [2 a: u( p; e* y) o* v9 ^! K* v% C - NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 3 ; //抢占优先级3
. Q7 k2 i- y1 M8 [, f - NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3; //子优先级3
% ]- p- s- y* q6 e1 @+ k2 r C, u - NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道使能
* ?4 l( I( I2 }6 D* c6 K7 | p - NVIC_Init(&NVIC_InitStructure); //根据指定的参数初始化NVIC寄存器' j' m) |5 h K4 o, [1 C
- 4 [! E3 h {3 N$ O$ v
- //USART 初始化设置) b) l, s+ H, ]$ W
- USART_InitStructure.USART_BaudRate = bound; //串口波特率. M; M4 }0 t% K" J
- USART_InitStructure.USART_WordLength = USART_WordLength_8b; //字长为8位数据格式
' `& D. J& p+ t$ j' B% z - USART_InitStructure.USART_StopBits = USART_StopBits_1; //一个停止位4 I" ~9 O: }- ]5 h: ?$ b7 R# p% u
- USART_InitStructure.USART_Parity = USART_Parity_No; //无奇偶校验位
|7 |+ C- j% W8 ^" c3 u! m - USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;//无硬件数据流控制
2 {1 M% i8 V. o - USART_InitStructure.USART_Mode = USART_Mode_Rx|USART_Mode_Tx; //收发模式
8 ?1 g! W1 F6 s - USART_Init(USART1,&USART_InitStructure); //初始化串口1
6 K8 e& p" ^8 R+ q* b
& e" P0 \+ `. ^7 p6 {+ y- USART_ITConfig(USART1,USART_IT_RXNE,ENABLE); //开启串口接受中断/ d6 B# U' b5 Y0 }2 s0 ~. D
- USART_Cmd(USART1,ENABLE); //使能串口11 T8 ^' W, z; E! s) l) N2 n2 q
- }
/ \ h% F5 z9 `% s" n. G) M% M - ( ?% S1 n; m. [; @; w0 ~
- void USART1_IRQHandler(void) //串口1中断服务程序- U9 Z& R" d0 d" R; R( X: b. b/ `
- {' D, r) R7 j& Q( O
- u8 Res;0 ~5 _+ L) n6 y6 A3 \- _! d
& [7 z" M& _5 M t/ l0 P3 |- //接收中断(接收到的数据必须是0x0d 0x0a结尾)3 f+ `% t: p* t, A' @ \! m
- if(USART_GetITStatus(USART1,USART_IT_RXNE) != RESET)7 X& Z+ P. ~) D! S
- {2 G" Q8 ~% j" D( V& r: L
- Res = USART_ReceiveData(USART1); //读取接收到的数据
$ ~8 C; F: A4 r0 H6 _/ B# N - * c$ g7 K- r0 Y" f' M T1 ?. H
- if((USART_RX_STA & 0x8000) == 0) //接收未完成
& k; N/ ^. }7 N3 k7 H0 K - {8 d/ c# [7 N4 e% Q6 x4 t1 p! m
- if(USART_RX_STA & 0x4000) //接收到了0x0d' S5 H# n8 n! p. Z# T
- {
+ M. C7 ^8 Y, O1 F0 m6 t - if(Res != 0x0a) USART_RX_STA = 0; //接收错误,重新开始
( y+ q9 j; O2 v: W4 Y; o5 l - else USART_RX_STA |= 0x8000; //接收完成了
' B8 ?. H, n: f9 D- i6 G$ U - }) h5 e6 U0 ?+ G/ r6 F, y5 L5 t
- else //还没收到0X0D( s5 A& w8 F7 |$ b$ F* S
- {2 a7 d0 z+ s$ m _( U
- if(Res == 0x0d) USART_RX_STA |= 0x4000;' \; |7 l. c5 \1 z2 S9 g# w8 F
- else2 `0 U9 Z% O0 M
- {
: n, z& v! h- ]# q8 c$ S- F - USART_RX_BUF[USART_RX_STA & 0X3FFF] = Res;
0 H, H' Z* R* b& \. \) T - USART_RX_STA++; v! Q3 g/ F# @ E9 T
- if(USART_RX_STA > (USART_REC_LEN - 1))USART_RX_STA = 0; //接收数据错误,重新开始接收
" ]! v5 Y |; H - }
, i: }, F5 v- H P - }! ]) a2 w& p: P0 e
- }8 M/ m' Z- ~/ U. V" ^
- /*---- User Code Begin ----*/
0 z2 g8 E% d* ?9 P" `& C( ^. g3 } - 7 @2 k- r5 G# ^, c8 v) x
- /*---- User Code End ----*/) T+ n+ y. N' c+ ~
- }7 D+ G3 `8 A. q+ M4 B
- }
6 k4 |! r1 t, Y3 _5 L - #endif
复制代码 : d1 Q. G( \- a* A
. Y1 b! X/ y* B# j/ Q
1 d! ~ L- O v+ X
, D- c8 f" V9 n: T l; q
I( i4 U t1 z. C+ Y5 T5 l/ O
, i/ b) G5 D, k [; x( W+ p
& B+ X: z8 w& ?9 Q+ Q
# q ~1 s3 |# s5 l0 Y( a
0 z3 o8 Q" _! f0 b
+ N3 v9 H% a/ H$ |
5 M0 g/ B7 [ ]3 A* a, z2 |& W
7 r5 C$ g# @! ?5. 实现USART串口通信(以printf函数为例)
4 J. B4 q9 B( n1 J, i6 I' B+ h) K, f& u+ u; r7 k4 j
- #include "stm32f10x.h"
) f/ a @: z" U2 | - #include "delay.h"( m9 M1 w$ i+ C/ c. ^/ |
- #include "led.h"; w2 F/ i* m9 k7 H; |
- #include "tim.h"( _4 h) |/ b1 J9 u, b5 w
- #include "key.h"" X) }# Y: H8 V. g. o' J9 C8 I
- #include "pwm.h"% Z- {, Z! k( P! X/ B3 O6 z
- #include "usart.h"' \7 A* E* |+ e. y& p5 y
- 5 G$ ]. U0 [# | J2 i, ?% D; i: g
- int main(void)% R5 X( ~7 j( _* k( `) l
- {, O. F3 @/ `6 z
- delay_init();
) T2 m, Y, t8 A( G2 _! o0 F7 O0 O - usart_init(115200);
w* U/ \# E% I2 H1 F1 ^4 N
' f. a2 m- `1 m- printf("USART Init Complete.\r\n");' u# O5 x! J7 i
- while(1)1 P8 _4 H( \- @4 s0 v: S
- {
9 }" n R8 W# }2 Y+ Q - printf("Hello World !\r\n");
% C$ Y4 z( e) K% a j% T; w5 @0 [, M/ [ - delay_ms(500);* |8 ^2 s4 J( _( Z! N8 C1 j# X- T
- }$ j* u$ Q; l( }# k0 _9 y
- }
复制代码 5 R1 f/ N$ E* E: C7 i
2 }( K2 l8 \3 f) y) e
9 N( U t( H( k! F' |- ~& B+ O& ?$ E8 p) T6 M5 v) D5 {) f
测试结果:
; v7 m+ d7 J) [- h6 b! J9 _; y @3 k8 E6 @4 f: t# C/ Z
) \- w& l0 q u7 C: C+ k8 L2 q+ D z i
J- t: |1 `) d, k+ i* W |