|
1 什么是串口通讯? 串行通讯是指仅用一根接收线和一根发送线就能将数据以位进行传输的一种通讯方式。尽管串行通讯的比按字节传输的并行通信慢,但是串口可以在仅仅使用两根线的情况下就能实现数据的传输。 典型的串口通信使用3根线完成,分别是地线、发送、接收。由于串口通信是异步的,所以端口能够在一根线上发送数据同时在另一根线上接收数据。串口通信最重要的参数是波特率、数据位、停止位和奇偶的校验。对于两个需要进行串口通信的端口,这些参数必须匹配,这也是能够实现串口通讯的前提。如下是串行通讯示数据传输意图。 / P8 o# D4 `( t3 K) L C% |) J
O+ [# O9 a" E) `3 y- i
2 串口通讯的通讯协议7 |' W* \1 X/ ^
' v0 w' h, [+ R
最初数据是模拟信号输出简单过程量,后来仪表接口出现了RS232接口,这种接口可以实现点对点的通信方式,但这种方式不能实现联网功能,这就促生了RS485。 我们知道串口通信的数据传输都是0和1,在单总线、I2C、UART中都是通过一根线的高低电平来判断逻辑1或者逻辑0,但这种信号线的GND再与其他设备形成共地模式的通信,这种共地模式传输容易产生干扰,并且抗干扰性能也比较弱。所以差分通信、支持多机通信、抗干扰强的RS485就被广泛的使用了。 RS485通信最大特点就是传输速度可以达到10Mb/s以上,传输距离可以达到3000米左右。大家需要注意的是虽然485最大速度和最大传输距离都很大,但是传输的速度是会随距离的增加而变慢的,所以两者是不可以兼得的。
& ? a8 u2 g0 g7 N3 j' G 3 串口通讯的物理层 串口通讯的物理层有很多标准,例如上面提到的,我们主要讲解RS-232标准,RS-232标准主要规定了信号的用途、通讯接口以及信号的电平标准。
1 z* W( d( h! z O+ j: o" X2 @5 p: r
; h% o* S! o$ `% N' B0 n
在上面的通讯方式中,两个通讯设备的"DB9接口"之间通过串口信号线建立起连接,串口信号线中使用"RS-232标准"传输数据信号。由于RS-232电平标准的信号不能直接被控制器直接识别,所以这些信号会经过一个"电平转换芯片"转换成控制器能识别的"TTL校准"的电平信号,才能实现通讯。 下图为DB9标准串口通讯接口: % ]: p9 j, m/ X( |2 t/ X
9 x' c% J0 t2 i- d/ @* G& J/ F; U9 B8 S& s/ o, J
DB9引脚说明: 7 v: x* R! M5 ?3 H2 d
. v' [! `0 ]* U' f6 w5 X2 a) A
- r; L u( o0 B 上表中的是计算机端的DB9公头标准接法,由于两个通讯设备之间的收发信号(RXD与TXD)应交叉相连,所以调制调解器端的DB9母头的收发信号接法一般与公头的相反,两个设备之间连接时,只要使用"直通型"的串口线连接起来即可。
$ c8 r3 m5 a. R! Y/ G; t! `: \7 d; _7 @7 Y6 a
串口线中的RTS、CTS、DSR、DTR及DCD信号,使用逻辑 1表示信号有效,逻辑0表示信号无效。例如,当计算机端控制DTR信号线表示为逻辑1时,它是为了告知远端的调制调解器,本机已准备好接收数据,0则表示还没准备就绪。 : D2 J! h8 q2 J* L' w' |# ?
4 波特率 波特率是指数据信号对载波的调制速率,它用单位时间内载波调制状态改变的次数来表示。
) P9 o5 }! W, B- w, \! n
4 ~7 Y/ d8 Q: P! W. K0 k" y1 i% g1 i x6 W! _$ l3 O
比如波特率为9600bps;代表的就是每秒中传输9600bit,也就是相当于每一秒中划分成了9600等份。 因此,那么每1bit的时间就是1/9600秒=104.1666...us。约0.1ms。既然是9600等份,即每1bit紧接着下一个比特,不存在额外的间隔。两台设备要想实现串口通讯,这收发端设置的波特率必须相同,否则是没办法实现通讯的 收发波特率一致可以实现通讯:
3 j. ~+ D ` e3 ]
& q/ b! Z5 `$ p( j& c5 u* T) T. L( H 收发波特率不一致,导致RX端不能正常接收: 7 ^: k/ Z% n2 [) Q( D2 f
! V; F- n' S0 `
5 串口通讯的数据结构6 {( x+ f7 |3 s
. \/ G4 a4 E1 S* Z" o. G, u 起始位: 起始位必须是持续一个比特时间的逻辑0电平,标志传输一个字符的开始,接收方可用起始位使自己的接收时钟与发送方的数据同步。 数据位: 数据位紧跟在起始位之后,是通信中的真正有效信息。数据位的位数可以由通信双方共同约定。传输数据时先传送字符的低位,后传送字符的高位。 奇偶校验位: 奇偶校验位仅占一位,用于进行奇校验或偶校验,奇偶检验位不是必须有的。如果是奇校验,需要保证传输的数据总共有奇数个逻辑高位;如果是偶校验,需要保证传输的数据总共有偶数个逻辑高位。 停止位: 停止位可以是是1位、1.5位或2位,可以由软件设定。它一定是逻辑1电平,标志着传输一个字符的结束。 空闲位: 空闲位是指从一个字符的停止位结束到下一个字符的起始位开始,表示线路处于空闲状态,必须由高电平来填充。 0 D5 V, `3 v" y/ k
6 单双工通讯 单工: 数据传输只支持数据在一个方向上传输; 半双工: 允许数据在两个方向上传输,但某一时刻只允许数据在一个方向上传输,实际上是一种切换方向的单工通信,不需要独立的接收端和发送端,两者可合并为一个端口; 全双工: 允许数据同时在两个方向上传输,因此全双工通信是两个单工方式的结合,需要独立的接收端和发送端。
3 I% b ^: E3 B9 K- c' q# D
2 F4 |" Z- d5 F* n" L% K# H
7 STM32中的串口通讯 STM32串口通信接口有两种,分别是:UART(通用异步收发器)、USART(通用同步异步收发器),对于大容量STM32F10x系列芯片,分别由3个USART和两个UART。
4 Y6 a: b* b7 X ! E2 y4 v) O0 n9 r6 x* M. K3 n
3 z+ R3 f4 S: A7 t5 Y J TXD:数据发送引脚; RXD:数据输入引脚 对于两芯片的间的连接,两个芯片GND共地,同时TXD和RXD交叉连接,这样两个芯片间可进行TTL电平通信。 但如果对于芯片和PC机相连,除了共地条件外,不能使用如上的直接交叉连接,虽然两者都有TXD和RXD引脚,但通常PC机使用的是RS232接口(9针),通常是TXC和RXD经过电平转换得到,故如果要使芯片与PC机的RS232接口直接通信,需要将芯片的输入输出端口也电平转换为RS232类型,再交叉连接。 二者的电平标准不同:
$ H# K, A9 q% h( g( `. g, `- T7 d8 q3 \9 q
( c" P1 Y4 S. v8 \ 因此单片机与PC机进行串口通信应该遵循:在单片机串口与上位机给出的RS232口之间,通过电平转换电路实现TTL电平与RS232电平间的转换。 如果使用USB转串口也可以实现串口通讯,USB转串口电路图如下所示。 % ~ k$ x1 C) S. W
STM32中串口通讯已经给大家建好了相应的库函数,大家在使用和配置串口的时候直接进行调用库函数和配置就行了。
2 V9 o4 K, z. p! }" u$ C* C" H请大家参照一下代码:
" }2 X) q! y0 j- [ 1、初始化结构体代码 - typedef struct {
) G. M) _1 v3 J1 ?6 _9 J - uint32_t USART_BaudRate; // 波特率
2 C: h. t" a3 p# p - uint16_t USART_WordLength; // 字长: d% l3 y [& q/ ?' P3 b4 h
- uint16_t USART_StopBits; // 停止位/ O5 i4 r Z+ v: L
- uint16_t USART_Parity; // 校验位
9 x' q$ e7 u" k2 o* V - uint16_t USART_Mode; // USART 模式 L* ]$ @4 {: U5 C X& }$ E
- uint16_t USART_HardwareFlowControl; // 硬件流控制# d4 v# O- |. A3 U1 X
- } USART_InitTypeDef;
复制代码2、NVIC配置中断优先级 - NVIC_Configuration(void)
3 D" f* j/ T9 R4 [ - {& p$ b* C- {5 E3 O* W
- NVIC_InitTypeDef NVIC_InitStructure;
3 |0 E* ?7 z# c( Y- B
$ }. J( z2 J/ F+ f3 m1 q6 F- /* 嵌套向量中断控制器组选择 */
7 W8 _5 v, h! J p - NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);5 A, Y, U2 p5 p9 E+ N8 c
- % K7 b+ Q' ~! D0 N( ^
- /* 配置USART为中断源 */
, @/ I, M: C1 w- C - NVIC_InitStructure.NVIC_IRQChannel = DEBUG_USART_IRQ;
6 T; x% ~1 B/ J2 z% X - /* 抢断优先级*/0 j# c* U# K# }9 E
- NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;+ ^5 B2 d6 p+ J2 W L# k0 E; k
- /* 子优先级 */% t4 r* A9 W9 G5 P( }" F6 |/ I. [
- NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
: @ I: y. r( D& s+ ?2 \5 g, z# X+ W - /* 使能中断 */$ q' n/ J* Z9 y, ]7 ?0 ]2 b& Q
- NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
4 k7 l; {2 ?: e. v+ k, K - /* 初始化配置NVIC */
' }! f1 u; ^6 W6 N - NVIC_Init(&NVIC_InitStructure);1 R, p K9 g: {9 Q. T. G6 L @
- }
复制代码3、USART配置函数 - void DEBUG_USART_Config(void)7 W9 C) f$ u9 R2 e
- {) C% [/ M6 V1 a/ \7 H. C' w2 U& G
- GPIO_InitTypeDef GPIO_InitStructure;! g' t- w6 M, `5 E: `$ n: n& R
- USART_InitTypeDef USART_InitStructure;$ M5 P3 V: g9 X
- G7 e, [9 |0 r- /* 第一步:初始化GPIO */
: r( C! s/ i6 A5 c! u - // 打开串口GPIO的时钟
4 ?( q* s3 S- l, {/ ^ g - DEBUG_USART_GPIO_APBxClkCmd(DEBUG_USART_GPIO_CLK, ENABLE);. E. j: s! Z7 B+ q3 {
- // 将USART Tx的GPIO配置为推挽复用模式# z# L* M" |. F+ }, R
- GPIO_InitStructure.GPIO_Pin = DEBUG_USART_TX_GPIO_PIN;( n8 m h m8 J& ~9 ^
- GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;& ^8 t' F( d! O; `1 ], l/ G
- GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
. K( o* X& `8 {' U C - GPIO_Init(DEBUG_USART_TX_GPIO_PORT, &GPIO_InitStructure);. X% f6 V6 G, y" Y1 U& ?
( S% B6 @% I- E6 i+ D0 i- // 将USART Rx的GPIO配置为浮空输入模式: r# O9 C% f5 \( r
- GPIO_InitStructure.GPIO_Pin = DEBUG_USART_RX_GPIO_PIN;" z# x" Z0 |( `$ s% x" H: g
- GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;6 j+ H. ]2 M4 T) F: W
- GPIO_Init(DEBUG_USART_RX_GPIO_PORT, &GPIO_InitStructure); 0 }' o5 ~7 Y. h! f( f3 o4 }* p
- . H. q" y% o: s7 i) l8 l
- /* 第二步:配置串口的初始化结构体 */: E) [% |1 A. a, }
- // 打开串口外设的时钟
5 _- y6 q3 c/ H# B4 n - DEBUG_USART_APBxClkCmd(DEBUG_USART_CLK, ENABLE);
' i, g" Y7 n& L! c7 n - // 配置串口的工作参数
- G8 H; ?) v, z1 _+ i - // 配置波特率) y0 z) A9 a2 f2 T1 }3 c
- USART_InitStructure.USART_BaudRate = DEBUG_USART_BAUDRATE;
# o! {# z# ]* T) d- b5 J' l - // 配置 针数据字长+ Z W# c2 T8 l& u
- USART_InitStructure.USART_WordLength = USART_WordLength_8b;9 g& S0 B5 l! ]/ C6 W
- // 配置停止位
" @. A$ k$ s4 S$ Y - USART_InitStructure.USART_StopBits = USART_StopBits_1;
5 @) Q% v6 N, D1 S4 W+ ?3 Q - // 配置校验位" D2 X, U N$ s3 M
- USART_InitStructure.USART_Parity = USART_Parity_No ;, c. Q. g: J; i7 R
- // 配置硬件流控制
4 N9 l2 W, ]' |: b6 X6 ^3 A - USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;0 l+ h# g/ }, W( I) l! b
- // 配置工作模式,收发一起
" n- W& R/ U) S/ U - USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
/ F6 V3 Q$ H1 O4 X+ Z; f4 c - // 完成串口的初始化配置
3 w- w C- t8 S1 e, G4 g - USART_Init(DEBUG_USARTx, &USART_InitStructure); s: d0 b& A* x4 E! ~8 t
- : A" p% {% ~+ z+ B+ x2 h
- /*--------------------------------------------------------*/
# n2 ?' F+ p( ]. S$ U3 c - // 串口中断优先级配置
0 A0 Q& n. L5 o3 ~2 n6 {& j5 @4 x - NVIC_Configuration();
. d- c; G) p# c2 U6 T
# F; b$ [0 p# |8 V1 d% F- // 使能串口接收中断
" x# [( g9 |, I* U3 G r) r - USART_ITConfig(DEBUG_USARTx, USART_IT_RXNE, ENABLE);; w$ @9 q- m+ }* y" D$ b: \
- /*--------------------------------------------------------*/
7 U2 z+ t$ Y% o8 j- f - 5 X- U) f" o# E* c8 f
- /* 第三步:使能串口 */
' p1 p- p4 W; l! M* M' k0 ]" Y - // 使能串口
0 `) h) m3 [6 S4 a$ D - USART_Cmd(DEBUG_USARTx, ENABLE);
# r% r& {7 s2 c - }
复制代码 $ K m+ z2 W' g h; q; F: h9 O
* u' _ ?0 S; n8 z8 Q8 }. B! J; r% i
|