14.1 USART定义$ Y7 u$ K X+ M4 y H* E# z$ y
8 @( p* e" O$ Q8 \" ]3 s! D( VUSART(Universal Synchronous/Asynchronous Receiver/Transmitter,即通用同步/异步收发器)串行通信是单片机最常用的一种通信技术,通常用于单片机和电脑之间以及单片机和单片机之间的通信。6 j7 O! b9 Y w0 s
5 [0 r) t' m- H# B* [
8 N8 x' m$ K$ x2 c14.2 USART串行通信协议
2 h: Y K6 x* ]3 N
# f" H+ C) X4 C. `( Z$ Q2 G$ A, g14.2.1 波特率和数据格式
6 U+ Z2 @, _- {- Z5 A$ D' _ s0 d6 ?
USART通信中的同步通信功能很少用到,大多情况下只采用异步通信,只能实现异步通信功能的接口就称之为 UART。UART 通信通常以字节为单位组成数据帧,由通信收发双方根据预先约定的波特率(传输速率)进行通信。
; k5 q% S2 ?0 g" V, A
+ {& |6 _, R% Q4 M波特率表示每秒发送二进制数据位的速率,单位是 bps,即 位/秒,波特率越高,传输速度越快,常用的 UART 通信波特率有2400,4800,9600,115200 等等。在进行串行通信之前,通信双方需要设置波特率保持一致,否则不能正常通信。
( R1 A P% r' g9 b& U
1 }6 n" x8 J6 b. t1 [单片机标准串口进行通信时,没有数据传输时通信线路保持高电平状态。当要发送数据时,先发送一位0,用以表示开始发送,叫做起始位。然后再按照低位在前,高位在后的顺序发送8位数据。当8位数据发送完毕时,再发送一位1表示停止位。3 P4 _1 W2 n q4 _7 Q/ a
6 J& r4 I2 K; _, U- g5 W' ]4 x6 X, j: X对于接收端而言,开始时传输线路一直保持高电平,一旦检测到低电平,便准备开始接收数据。当接收完8位数据时,便检测停止位,检测完毕后,表示一帧数据发送完毕,开始准备接受下一帧数据。为了确保数据准确性,通常会在数据位之后设置校验位。
4 k/ A* S5 t2 s
: N( @% ?5 \* T$ a% c, m( m串行通信的数据帧的格式由起始位、数据位、奇偶校验位(可选)和停止位等部分组成,如图:1 K5 J7 x2 U: B8 j
) F3 p7 H- i, x0 h9 w7 m- T
8 {& Y; k) [ d3 B' A: q
' `( \* j+ h, n9 _3 e+ O
) s; W, Q$ y. n14.2.2 TTL通信接口和RS232通信接口
; ]/ f" }0 K$ B8 a% D
3 z. f6 _' T- p电脑和单片机之间进行串口通信,通常使用USB转UART芯片,将USB通信协议转成UART协议和单片机通信。
# }4 q9 H; m" [$ F* d+ h3 ?1 P
' ~0 V- D2 ^4 O$ C$ [0 n1 Q, K$ [
3 c6 m7 P0 q' B6 p- w; J
# `/ x+ N/ [0 `1 M14.3 USART配置步骤: H M2 L0 v7 \3 \1 ]
" K9 P8 @5 y% {4 p
1.时钟使能
" h$ m) W8 G: N3 Z! p2.设置中断分组
; l, V0 F7 r) n2 V. O3.串口复位2 |, a, A$ ~1 T% _
4.GPIO初始化(TX,RX引脚)
! m( v. n' _ F) `" t5.设置中断分组7 o: D. l# l9 g; e, p# n6 L
6.串口初始化8 W' Y' i7 F6 T
7.开启中断
; g8 ~$ W9 M: A' }8.使能串口% _" x& e+ w8 F. D
2 C8 {# S! w, [7 L' m
6 O. M8 m0 `6 h1 T1. 新建两个文件,usart.c 和 usart.h
2 V0 z' ?' d/ u" e3 W1 R' q0 G- d$ G
4 r' L3 \* l! J, L7 A
. F1 C6 o5 ?3 @
s1 a3 U4 Z X- ?5 A& V: o$ z) M
3 m0 O$ m) F! R0 L2. 在头文件 usart.h 添加下面代码:- #ifndef _USART_H! m O+ Z! O$ j* }! g% R6 g4 A1 a4 B
- #define _USART_H
: a+ u9 Y4 O$ @+ l! b - #include "stdio.h"2 A1 W' v" c( c5 k* D( w4 S
- #include "stm32f10x.h"
/ ?/ z( s& i; @( }+ Y7 ]5 W - " V8 J1 [' o/ Q+ {4 \
- #define USART_REC_LEN 200 //定义最大接收字节数 200* x1 ^8 u2 g" v! w/ e, Q _
- #define EN_USART1_RX 1 //使能(1)/禁止(0)串口1接收7 C; D5 s y/ `* o- m
- ! |! x7 `; B% I. u- j0 f. s) `3 ?9 M
- extern u8 USART_RX_BUF[USART_REC_LEN]; //接收缓冲,最大USART_REC_LEN个字节.末字节为换行符
2 a$ t) W i6 A( r6 O& Q* z# n - extern u16 USART_RX_STA; //接收状态标记
1 a" M2 `% ?! [: J, ^* V
/ ?' e8 @! [9 r1 G# i" \- void usart_init(u32 bound);/ ]8 n) ?" D- P/ b
% {8 Q- ~8 Z* W) D- @5 s- #endif
复制代码 / p+ v8 K8 `" D
3 U) I. e8 w, Z% x3 j. ~8 M
9 k1 w/ ~. U! O% X. ?5 |3 @% e5 g3 Z
3. 把 usart.c 添加到工程中 B. P# m) E' T: @
" Q0 ^: j9 }; e) U
# N3 m, t9 L) Y3 H
0 Q) K# i8 U) L/ X
4. 在 usart.c 中添加以下代码:
+ h# f, ^' N2 K* m/ V7 V9 J+ i9 r: Z( l1 r/ T8 `
- #include "usart.h"+ Y' j8 T3 x |+ R- L# ]
- # I$ q+ O' X, W6 v! b" f4 [- y) n
- #pragma import(__use_no_semihosting)
0 L) ~2 U: J/ g& L) d8 l - //标准库需要的支持函数. X; Q& S5 q+ u- l; X
- struct __FILE
: ?" N! l- \" \7 d/ t - {; p7 X! C! v1 e8 p' P" S
- int handle;/ }0 t, \ V' m( d9 Q& T
- };
B3 a, i+ L2 [6 f/ c) s - ' L" [, f& K3 w- [
- FILE __stdout;
! e9 B9 L' A; g" n7 V
1 d. x' A. Y" p9 u5 T$ T/ V" u3 u% l- //定义_sys_exit()以避免使用半主机模式+ S8 h: t/ P- ]' t5 o" B4 Y/ @
- void _sys_exit(int x)
' m! e) l- h) J" x1 i - {7 @7 u8 P% w5 v" G6 e( s( O c
- x = x;* y$ @9 v: v5 B) D0 V3 T$ T
- }1 }- C( s3 \2 V$ b. K
' h2 B. k; g2 U. C- //重定义fputc函数
c1 Y$ z8 Q- j& O - int fputc(int ch, FILE *f)3 }: e& z. `# ]1 H) ^: k& S2 k
- {
+ i( ~- r" [" r7 t6 A - while((USART1->SR & 0X40) == 0); //循环发送,直到发送完毕
g! h% k. ]2 f. @ - USART1->DR = (u8)ch;
2 [3 {4 ?/ q2 B' k/ ` - return ch;
* G( h- e- i* K& l( r) h* e6 } - }
" Y/ ?: R4 u! u5 V; ~ - 7 C2 }+ J3 P0 J- ^
- #if EN_USART1_RX //如果使能了接收
/ P; _, j) q( G; ^7 o - //串口1中断服务程序' @( k' G2 p4 [
- //注意,读取USARTx->SR能避免莫名其妙的错误3 S! ]7 }, c6 [0 f* T* I. ~+ O! K
- u8 USART_RX_BUF[USART_REC_LEN]; //接收缓冲,最大USART_REC_LEN个字节.* h b' T/ x1 ]6 X( C8 `. W+ U d! |
- //接收状态; @+ v# [ l9 v3 }2 l% w6 @
- //bit15, 接收完成标志1 i$ n# M- H6 J# t
- //bit14, 接收到0x0d, M2 K. l2 {" Q4 Y7 Y+ D6 Q
- //bit13~0, 接收到的有效字节数目: [9 D3 \4 B D* h
- u16 USART_RX_STA = 0; //接收状态标记
1 G: [- `# I }$ I8 t
% H! P7 z" d6 z: j1 `- void usart_init(u32 bound){
! }4 ~1 x1 a' F7 {5 q
7 _, ?: l R0 Q- //GPIO端口设置 c# d: N9 L: N3 H" b& V! }
- GPIO_InitTypeDef GPIO_InitStructure;
6 d9 F9 K0 z( K - USART_InitTypeDef USART_InitStructure;
$ H1 j% O* w9 m: ~6 | - NVIC_InitTypeDef NVIC_InitStructure;" Z+ M! y; r( V- d/ @( s F
- ! F" I% l+ S; a* j) T
- //使能USART1,GPIOA时钟
0 D) ~1 p& `( n& j) K$ Y9 n8 \ - RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1|RCC_APB2Periph_GPIOA,ENABLE);- O! x, B4 N' h7 b- u
- NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //配置中断优先级分组& }( v9 _9 `2 y8 k/ A
( n9 h* M: `8 v- //USART1_TX GPIOA.9
/ B9 q2 ^( T& N' _: t6 U% | - GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9; //PA.9
2 Z* P" f X3 C9 z) a: O. ]6 c+ B5 S0 ~ - GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;9 ~1 K# S, N4 B; J) {
- GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //复用推挽输出5 k1 J+ V) V9 R8 B6 ~5 g
- GPIO_Init(GPIOA,&GPIO_InitStructure); //初始化GPIOA.9
, L- B: |( A7 v8 Y+ b$ l - @1 r% ]% K( x5 m2 k6 q) W- Q
- //USART1_RX GPIOA.10初始化2 [' O8 }" h( P% h0 X
- GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10; //PA.10; O0 F9 w" z) Q
- GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; //浮空输入/ j9 A$ ~9 N3 P/ G3 y1 S
- GPIO_Init(GPIOA,&GPIO_InitStructure); //初始化GPIOA.10
7 S/ Y. Q5 y( }9 B0 l: X - " U1 v+ N5 W/ L2 o
- //Usart1 NVIC 配置* c- {) `& }' D8 `1 e8 M
- NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
" _$ c0 l* a1 O$ ` - NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 3 ; //抢占优先级3
& f# @$ X; L L - NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3; //子优先级36 i f* ^! U6 [* ^
- NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道使能
. n+ c% Z$ C/ Y2 W1 ~% j7 W; w - NVIC_Init(&NVIC_InitStructure); //根据指定的参数初始化NVIC寄存器' }, [+ ~' ?9 b# K' S9 e
- 8 Q4 c% c% T9 ?6 A% o
- //USART 初始化设置
0 R- B- n4 ?+ w8 x& u0 j$ v - USART_InitStructure.USART_BaudRate = bound; //串口波特率: e6 k! ]' G8 a0 c
- USART_InitStructure.USART_WordLength = USART_WordLength_8b; //字长为8位数据格式
/ U! f- N, d' F& N - USART_InitStructure.USART_StopBits = USART_StopBits_1; //一个停止位
4 K- X4 C# N( e, |7 u7 O - USART_InitStructure.USART_Parity = USART_Parity_No; //无奇偶校验位$ [) y; t7 `6 b) U
- USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;//无硬件数据流控制
* T, P! E, B. i. x9 N5 S& s - USART_InitStructure.USART_Mode = USART_Mode_Rx|USART_Mode_Tx; //收发模式) f* }8 R; Q4 X. T. T7 @8 u
- USART_Init(USART1,&USART_InitStructure); //初始化串口1
% i3 P* P0 u5 I( _* \6 B# y - , n$ R% [" T) m9 P l
- USART_ITConfig(USART1,USART_IT_RXNE,ENABLE); //开启串口接受中断 h2 v3 X! U* L; i" S' V1 L
- USART_Cmd(USART1,ENABLE); //使能串口1
' d- @' a8 c8 Z0 |2 g J - }
# J {3 N4 k7 o) X8 O
" t) }" A) R5 O: s G$ h8 u% z- void USART1_IRQHandler(void) //串口1中断服务程序
) U# Y' h: F0 \ - {
5 o# V2 P" C3 {8 Y# V - u8 Res;/ D" ]$ I4 f! V
) b. i! ]% Y4 w& e" [; {- //接收中断(接收到的数据必须是0x0d 0x0a结尾) ]/ w. K l2 m: m6 @2 l+ W3 n
- if(USART_GetITStatus(USART1,USART_IT_RXNE) != RESET)
6 \- r0 w) t% n+ g/ d - {
- P, ]8 x8 a/ k4 B1 i0 p - Res = USART_ReceiveData(USART1); //读取接收到的数据
6 C* f% k; A6 o8 d8 @ - # P( @ g) v" U$ `
- if((USART_RX_STA & 0x8000) == 0) //接收未完成3 Z+ I0 s2 w; \3 t
- {. T- K7 s+ W$ i4 o/ a' y3 x. C
- if(USART_RX_STA & 0x4000) //接收到了0x0d6 ?; E! Q# h' B. V+ l
- {$ e" S( g$ O7 a8 ~4 I2 H5 R
- if(Res != 0x0a) USART_RX_STA = 0; //接收错误,重新开始5 _. q/ g l5 g# _
- else USART_RX_STA |= 0x8000; //接收完成了
8 v+ W0 r/ W+ B& p0 m3 S - }
g2 r5 n! j# `; \# q& ~ - else //还没收到0X0D3 _/ K, W# r3 V; S. h- ^, B' j* j* z: e
- {. X/ b2 w; n! Q d1 e
- if(Res == 0x0d) USART_RX_STA |= 0x4000;: }2 I7 M& W( {; R$ h5 R9 d4 d
- else! P/ b I( t" y9 {
- {0 z9 W# S h% D
- USART_RX_BUF[USART_RX_STA & 0X3FFF] = Res;) m9 M- g/ `2 _9 y
- USART_RX_STA++;2 \( i3 H$ ~9 X0 K
- if(USART_RX_STA > (USART_REC_LEN - 1))USART_RX_STA = 0; //接收数据错误,重新开始接收7 }- z" B0 Q7 o3 r% d5 w
- }
: T7 S, h" l4 ]0 V1 ?9 n - }
7 |' q, v4 {- F6 E - }
, U I- x' b0 ]$ x; a* a' B - /*---- User Code Begin ----*/8 C" V& P# a5 w7 L0 u
& N- |0 F9 u! ^8 {. a3 x: d; @+ j- /*---- User Code End ----*/
9 \: D3 m$ A4 c0 ? - }0 t4 f% G8 ~, `9 K
- }7 ]$ d' G! Q6 _2 D$ [
- #endif
复制代码 * \* L0 ~# M& }$ X) @5 q( Q( Z
9 ]% s! l" d) a {0 F. B1 r
1 l% z, c+ F9 m1 j; Q
3 e% L% r1 ~9 V* {0 ~
, f. Z; F( H/ Z
& u5 u0 E! J& r
4 C. C! J; O+ J) ~+ T
3 s. _* B) `! L" a8 l6 T7 W
6 J& X* W4 L- E: I
/ x$ b9 }: t( ]3 v- x6 C
! C5 N! i9 f9 W0 Z
6 K4 [4 D0 i2 j' o1 F- W5. 实现USART串口通信(以printf函数为例)
2 n) \+ w8 |/ n0 {- r5 A9 V
* B/ n. W1 q5 K1 d- #include "stm32f10x.h"
# U1 _4 I9 ]6 p8 h1 X - #include "delay.h"1 ~* N8 G, y/ \& R' a: g
- #include "led.h"# c- R! I' G, H. \
- #include "tim.h"
0 v) Q1 Q+ o( W8 |! O - #include "key.h"
& I1 b. y8 l& p - #include "pwm.h"# `4 e- G, |9 V: W: b* o) G
- #include "usart.h"1 E4 Z8 L) ?; b! N
- + r: ~8 ^: ]9 @9 i! f
- int main(void)# p# \) Q% Y/ Z/ _7 \5 U
- {
" V9 T4 g$ R( r: U& x - delay_init();
4 }7 z L) z) s" V* A/ x - usart_init(115200);
5 H& G& _ G3 h u) B, b
" L- a$ v% }" r6 M) `- m- printf("USART Init Complete.\r\n");' \ q: T& S: d7 _
- while(1)
* c' C+ g# B* p8 ~0 C; J - {
2 J2 r4 N( F9 D9 d, a# x - printf("Hello World !\r\n");
5 m$ y/ T6 `4 k8 O - delay_ms(500);0 U4 k. j3 B& G4 F X/ ^5 Z8 o( Y
- }/ a6 q; V- {( c$ u
- }
复制代码 7 @4 [- C) X( c. V2 d
9 `4 h5 X; d8 Y- O- O" b% x, L: N" N- M) W
4 K) V# Y0 {; m: g/ O/ _5 l7 e测试结果:
8 J0 Q. L! x( {3 p7 P- L
/ r8 p) Y! i7 Q
. B2 L! \7 N. U$ |" U: E0 x; D! `* Z( f! y! c& w
& w: l0 ?6 q9 D+ P
|