14.1 USART定义4 {+ g) D. T. `+ X; [3 w& _7 d
( n8 |. t8 k0 l" bUSART(Universal Synchronous/Asynchronous Receiver/Transmitter,即通用同步/异步收发器)串行通信是单片机最常用的一种通信技术,通常用于单片机和电脑之间以及单片机和单片机之间的通信。* @9 q7 E$ O$ r G1 r/ x. }
8 }! k! a/ s; \! D
" N( i3 S7 c' _: A$ T# e
14.2 USART串行通信协议& V: L# h: f' o# s( R6 r
& C, B+ ? R- s" d+ {
14.2.1 波特率和数据格式7 }6 J3 x" u4 ]: b
2 B& K; E; b$ Y( T
USART通信中的同步通信功能很少用到,大多情况下只采用异步通信,只能实现异步通信功能的接口就称之为 UART。UART 通信通常以字节为单位组成数据帧,由通信收发双方根据预先约定的波特率(传输速率)进行通信。$ I& T I* r+ o' U2 ?) U% H3 S
; V- ]! U) @6 b
波特率表示每秒发送二进制数据位的速率,单位是 bps,即 位/秒,波特率越高,传输速度越快,常用的 UART 通信波特率有2400,4800,9600,115200 等等。在进行串行通信之前,通信双方需要设置波特率保持一致,否则不能正常通信。
9 Z2 e3 u: g5 n& G( ]
; s. O' F' u7 R4 P! q* {' g7 R单片机标准串口进行通信时,没有数据传输时通信线路保持高电平状态。当要发送数据时,先发送一位0,用以表示开始发送,叫做起始位。然后再按照低位在前,高位在后的顺序发送8位数据。当8位数据发送完毕时,再发送一位1表示停止位。- F8 \ v1 {! y
5 V- I% o$ G; g; c& I: Y! z
对于接收端而言,开始时传输线路一直保持高电平,一旦检测到低电平,便准备开始接收数据。当接收完8位数据时,便检测停止位,检测完毕后,表示一帧数据发送完毕,开始准备接受下一帧数据。为了确保数据准确性,通常会在数据位之后设置校验位。
' f6 Y |. c1 x' m( u' v* x7 U
& s' B; Y! \4 |* }* m; V" x串行通信的数据帧的格式由起始位、数据位、奇偶校验位(可选)和停止位等部分组成,如图:
. V. J$ G8 ^! n# `* `
! E Z0 d) U, C1 T: I" n: s0 a
( m1 F. q& j9 v$ I! _+ _. ?! w5 A* ^: N
% {: [3 g- J; F' R; v14.2.2 TTL通信接口和RS232通信接口6 o8 s- K1 ?3 C+ V1 ^9 n7 L1 p
4 \- M U# C& ]7 R! z; u电脑和单片机之间进行串口通信,通常使用USB转UART芯片,将USB通信协议转成UART协议和单片机通信。( F. w( {. z% \3 g
0 ~% ?3 ?" B$ J' K9 N9 w
( ]2 u3 L- L' V" ]2 Q9 Z% n4 [
. z1 `3 n* d* t3 x% y+ h: A6 u/ s& `14.3 USART配置步骤) @ v* ~. }2 P, v9 n
$ K+ K4 v& [, Z8 M# g1.时钟使能' U/ b3 o) S' z% A3 G8 t# [$ ^
2.设置中断分组
3 [3 O9 l; S8 V9 j9 t9 g* x N) R3.串口复位) J2 Z. A$ y8 \1 U; s
4.GPIO初始化(TX,RX引脚)8 v$ ?8 _: i( m; _" d
5.设置中断分组
, N; |1 b6 P7 m& u0 n' ]8 p6 s6.串口初始化' ?' F7 {; [3 |6 C0 C
7.开启中断) A* I0 W7 g0 [5 e6 [. {
8.使能串口
( y8 h: f. e# d$ f1 m: I2 |8 v0 Z7 G( O
3 z( ~! M0 }8 v2 f
1. 新建两个文件,usart.c 和 usart.h
; x1 g8 J5 L+ C% b
+ _9 [0 [0 [4 s$ ~! {* \
& D9 G3 y# [5 s% X+ S* Q( h- o8 A
$ Y* a( i. {2 T& f }8 x2 t2 h3 E3 j( J$ t! E+ s3 f
2. 在头文件 usart.h 添加下面代码:- #ifndef _USART_H
2 O- i5 X3 Z" D1 [! V4 R( e/ E - #define _USART_H
$ |- H* `' A- i; @; h - #include "stdio.h" |1 r) z7 a! [2 ?3 {. t( H2 }
- #include "stm32f10x.h"
- b, W& Z# ^5 u0 I
" _7 c' x1 Q" U) g( ~- G- #define USART_REC_LEN 200 //定义最大接收字节数 200
* H d3 g& K+ q4 @1 Q* ~ - #define EN_USART1_RX 1 //使能(1)/禁止(0)串口1接收
+ n" b0 z8 p- ^5 B# E2 j( K9 h3 [: G/ N
, @* H. {7 f1 ~8 P! p# q' U- b4 K- extern u8 USART_RX_BUF[USART_REC_LEN]; //接收缓冲,最大USART_REC_LEN个字节.末字节为换行符
/ R$ I S6 ?2 B0 L - extern u16 USART_RX_STA; //接收状态标记8 G5 {* b" U# O5 n2 E* a |
- R, K6 e- B: v' f
- void usart_init(u32 bound);* L) w+ I, d8 y, c/ o
( v' [, I+ L+ L% v! Z- #endif
复制代码 8 e! r1 z3 P( X3 ]$ X7 z( n( ~
7 X" O, |' y) K; u* w. \! W* K$ x1 J6 Z+ W) b$ U( a
+ L6 A+ Z9 Q$ A) @. M% r3. 把 usart.c 添加到工程中
1 D4 Q8 d! x4 s! Q. n: G6 ]/ m0 |6 G# @8 ^
" J0 U3 V& W8 R f' r1 n
! N! Z+ f: [+ f5 y2 h
4. 在 usart.c 中添加以下代码:
" v+ m! V* S$ G# c) v. J5 g2 y! f: o. i. S Z! h
- #include "usart.h"& s9 S) l+ J8 t: \& v- g) F9 @
- & S) J& u9 L8 e6 f$ _
- #pragma import(__use_no_semihosting)# ~, G* w x$ ]- M
- //标准库需要的支持函数
& C; [( X: z( j% |2 {+ F0 N - struct __FILE
! ?/ {. O5 S/ L - {
/ |; s, t( K$ C0 |0 D - int handle;9 @3 D7 U! g( }0 R3 a( A$ |" e' X
- };; k4 x7 @7 [$ G8 t: B
- ; U2 x+ |. \9 N7 m$ w. s
- FILE __stdout;
# w/ ]/ R5 }; m - ( Z" b7 ]6 X! Y& A. f' M0 ~
- //定义_sys_exit()以避免使用半主机模式/ P! I0 ?) F4 s
- void _sys_exit(int x)
5 H( ~8 k" O/ ?+ g) Y% N - {1 }4 B" u- @' g2 ~( t
- x = x;
" j- Y& n+ q9 N+ U6 i - }4 n# d+ c. v! s! y: L8 _
- 1 t6 v3 {1 N9 C6 E% `7 E' e
- //重定义fputc函数+ [" Q/ P; c8 l/ s$ ?0 w$ t: i
- int fputc(int ch, FILE *f)
7 a# F- d8 g( R0 [& D, s2 W - {0 F7 Q) q) ~" d5 u+ \
- while((USART1->SR & 0X40) == 0); //循环发送,直到发送完毕) o0 @3 y6 {0 ]4 ?2 B, M+ f$ j
- USART1->DR = (u8)ch;
$ B2 e! E+ s" Y: ^# A9 { - return ch;
7 b1 e! S; `- T |/ i5 `2 c: U$ S' ` - }
1 s4 }7 v( x$ O3 [" j. S - a0 ?! B: H8 k8 @+ D% w
- #if EN_USART1_RX //如果使能了接收+ c6 g/ F9 C# S, b9 u
- //串口1中断服务程序0 f* M8 }. g% V( O" K: Q3 x1 z
- //注意,读取USARTx->SR能避免莫名其妙的错误
+ i5 N) ?7 I( ^: W1 l3 N% y - u8 USART_RX_BUF[USART_REC_LEN]; //接收缓冲,最大USART_REC_LEN个字节./ o+ `0 I3 Q9 ^# F( A2 F0 G
- //接收状态
% |: H! H* @% C) b - //bit15, 接收完成标志
* F! e* D9 J! j1 j5 R# [$ g - //bit14, 接收到0x0d
# W; f# m5 i$ d* d$ j6 v1 R - //bit13~0, 接收到的有效字节数目- a; @7 X6 S4 h
- u16 USART_RX_STA = 0; //接收状态标记; c2 k! g2 a+ |3 h
- - N( R9 ^7 v/ g5 _
- void usart_init(u32 bound){
6 K0 u7 G: I) [8 `. R$ b
1 x: v) w+ \& J2 M7 |- //GPIO端口设置
/ v7 y- ?+ Z) l( _# Y - GPIO_InitTypeDef GPIO_InitStructure;$ V1 {& m, D7 s. k/ R( L3 X) G
- USART_InitTypeDef USART_InitStructure;, g, ?8 h6 W6 I; w: L* A, @
- NVIC_InitTypeDef NVIC_InitStructure;
) n/ F% w. a, a* ^# i% l - ; T# n( }9 c) |; P: H6 I
- //使能USART1,GPIOA时钟
" e. a) d1 l! f* v% } - RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1|RCC_APB2Periph_GPIOA,ENABLE);+ T5 ^6 e5 f6 j# {# G* { s
- NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //配置中断优先级分组
* `3 w/ I8 d3 g( p8 U - 3 B! _2 R3 X, V3 d5 d, r3 z# s3 j4 J* m
- //USART1_TX GPIOA.9( J+ O/ D2 N: T
- GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9; //PA.9
. V* O, @# l% D - GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;) K4 i1 E# n4 w# C& ^7 t- j6 u) R* o8 Q
- GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //复用推挽输出
I! T6 x# S: W9 M) R: Z - GPIO_Init(GPIOA,&GPIO_InitStructure); //初始化GPIOA.92 q# G# W3 F+ ~! J2 J. s
I; \2 E7 B$ U7 B1 U. ], c6 H5 _. K' x- //USART1_RX GPIOA.10初始化
p7 Y8 T9 {* ~, L( P - GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10; //PA.10
9 Q; C% A- Z8 j& L9 ?9 D2 Y - GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; //浮空输入3 l* b4 t' ?" t: n$ T- |, a
- GPIO_Init(GPIOA,&GPIO_InitStructure); //初始化GPIOA.10
4 B. Z/ r- n9 u, @
7 z4 g5 J s0 v G( t- //Usart1 NVIC 配置
! f6 h" L) p" \- Q8 I W6 m. n - NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
$ q% z+ k* b5 K1 }6 z4 M# r; Y4 _ - NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 3 ; //抢占优先级3
" f d- n- v1 H! ^# M q - NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3; //子优先级3
; M3 e8 x" z2 k" k) o# F - NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道使能3 ~7 H/ a" ~ U' ]4 j7 y
- NVIC_Init(&NVIC_InitStructure); //根据指定的参数初始化NVIC寄存器
; Y# k' A# n- B v/ N z
, B1 R' e3 E1 s& o8 T# l/ b- J( I- //USART 初始化设置2 L) p8 z$ Q e! v& h5 |
- USART_InitStructure.USART_BaudRate = bound; //串口波特率
Z$ M) Z2 J. x) e, a* u3 M0 o6 ~ - USART_InitStructure.USART_WordLength = USART_WordLength_8b; //字长为8位数据格式
+ M* W3 E6 D: Q9 c$ y5 ? - USART_InitStructure.USART_StopBits = USART_StopBits_1; //一个停止位: R9 C) U% M e& W7 B
- USART_InitStructure.USART_Parity = USART_Parity_No; //无奇偶校验位4 I5 f" T! P4 d
- USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;//无硬件数据流控制7 l& b4 }9 x, ^* t! @% |* S
- USART_InitStructure.USART_Mode = USART_Mode_Rx|USART_Mode_Tx; //收发模式' a# Q. t- P9 V( b S2 C
- USART_Init(USART1,&USART_InitStructure); //初始化串口1
- W5 {% w$ f% j& X5 [$ b - , a' r. m" R) s- A( n4 x
- USART_ITConfig(USART1,USART_IT_RXNE,ENABLE); //开启串口接受中断: a- h7 F8 u: M1 E3 p5 m
- USART_Cmd(USART1,ENABLE); //使能串口1
) m, C6 L, h" S" E1 a - }
2 q7 Y! M1 a; V6 a
& C& f$ I0 ^( D' B. f+ f( c5 r9 ]" K# z- void USART1_IRQHandler(void) //串口1中断服务程序% D0 _0 [+ w% D F7 ~
- {
& R) |$ @2 o; |: ~: b1 I1 c" o - u8 Res;
8 {$ |! v, X+ |# \3 T
- ~: ]+ g6 J+ V) e: R) J- //接收中断(接收到的数据必须是0x0d 0x0a结尾)+ _9 L( b% D; S' I
- if(USART_GetITStatus(USART1,USART_IT_RXNE) != RESET)+ A+ L- P1 n8 a) A( J) U) g+ ?
- {' g9 }8 q6 _& R" W2 E2 V8 I
- Res = USART_ReceiveData(USART1); //读取接收到的数据0 E. O! c9 v. u6 A+ g% f
- 1 {3 z9 Z( H' o* d; l' Z
- if((USART_RX_STA & 0x8000) == 0) //接收未完成
5 j2 J" H1 B0 U, c- [% }9 X" I1 g - {' c' K; x7 r7 m4 v) X+ h
- if(USART_RX_STA & 0x4000) //接收到了0x0d
& s6 x$ Z4 R" ^' v; ]! A - {
]9 d' {% J- Z1 |6 ` - if(Res != 0x0a) USART_RX_STA = 0; //接收错误,重新开始
$ S/ j+ g; D. ~2 V. _ - else USART_RX_STA |= 0x8000; //接收完成了
" B9 L, l$ E; ]7 C' g6 r' ~% P$ C - }
' k1 G: G, s( C/ u5 B+ e - else //还没收到0X0D9 I1 l4 N# Q1 l) f& g4 x" Y
- {
6 N- m8 z0 m/ S f& S - if(Res == 0x0d) USART_RX_STA |= 0x4000;1 D! e/ M( Y0 |2 A" c7 A& J
- else) ^# q; a h" t: f6 p- o/ [0 B( M6 g
- {! u6 i% ?: P3 b. a
- USART_RX_BUF[USART_RX_STA & 0X3FFF] = Res;( ~' s1 u: m1 \+ R+ o+ q$ }1 H
- USART_RX_STA++;2 c0 `+ a- ?' G. ^3 W
- if(USART_RX_STA > (USART_REC_LEN - 1))USART_RX_STA = 0; //接收数据错误,重新开始接收+ C3 w- D5 d; W B& S3 y
- }& {5 p/ G% D: X& C9 Z- x
- }
3 S& t0 p2 i' G! x9 R6 z: U - }4 N3 l- ^) _5 c9 k5 _+ P4 U
- /*---- User Code Begin ----*/+ O/ k& D* L8 R
O; O' P9 r' J" Q. V- /*---- User Code End ----*/' b( R% d2 S' f# r
- }
9 {/ s& i4 ]. |, { - }5 P' ~/ S8 J5 [
- #endif
复制代码 $ |9 o2 o& ?* [' W+ o( A
5 a& h, V2 b! G# w3 n0 Z0 D
! ]8 Z( ~1 w% Q# W
, w; n4 [3 G0 x- J/ F: B( K
2 i9 E; V/ U* l; a' l
" ?/ B' e) J1 T3 ?7 m9 e
5 s. |7 c# Z4 Z( U8 P& G
3 K/ L/ `" F% ^9 c( c
0 e% C# X$ v; M
( O- K4 ?9 x: R2 x
/ f/ ~: d$ z% F
- Q/ L* C8 y1 f5. 实现USART串口通信(以printf函数为例)
) [9 p. l: Z* ]" x# p' x: L: ^# G
6 j1 ?2 j5 @9 o9 A- #include "stm32f10x.h"5 g) X: J; C( z
- #include "delay.h"
/ _' f; C" y& D) C8 \% `: Z - #include "led.h"6 c: a8 P/ |$ r5 v4 f2 ]
- #include "tim.h"' G: }; ]! a, N
- #include "key.h"$ L5 ~; b& I% `, C
- #include "pwm.h"7 {6 N7 |! h; x4 ?" m3 F
- #include "usart.h"
2 @& q3 K8 r9 v' v/ q
0 S: C, ^4 \( `$ U# n* j- int main(void)5 K, q$ R/ K* W9 {
- {
' I5 e# ^# R+ o, H/ y4 A5 c5 [% ^ - delay_init();
$ ]5 J# S5 ?2 K3 W; b3 ^1 a - usart_init(115200);% M' H2 v4 v* V* H( x% Y9 D9 R- p
- ; s2 \5 z# }) _- L4 ~, D& X
- printf("USART Init Complete.\r\n");
) v* r# {* u! T - while(1)' Z" x8 C- ?. a% k* V
- {$ v) Q9 H$ m7 x0 Z) G
- printf("Hello World !\r\n");
, b/ p1 {. k' I& k - delay_ms(500);4 V2 K% t# q) `* W; n c1 }
- }: Y; \% i& O4 d. R) e
- }
复制代码 ! E; K" e- c4 X( u0 X+ A; y( z
) A9 e; S0 W, h; K3 T
( n5 H2 b. p% |3 g: L1 w+ d- q0 w5 ~! s3 u J3 b1 i# S
测试结果:
/ F/ P6 V( `$ v- R4 l, P- G+ D C+ o t" b% a
# y( s8 ^( ]4 w
4 c6 c [8 q) h4 {$ A
0 q1 p2 R# {" ~! t% D/ C! c2 D |