RS485缺点:7 ]3 K4 o3 _7 e0 ?4 z% I+ u
w2 o% @* I$ k6 ~1 q- j
RS485总线是一种常规的通信总线,它不能够做总线的自动仲裁,也就是不能够同时发送数据以避免总线竞争,所以整个系统的通信效率必然较低,数据冗余量较大,对于速度要求高的应用场所不适应用RS485总线。同时由于RS485总线上通常只有一台主机,所以这种总线方式是典型的集中—分散型控制系统。一旦主机出现故障,会使整个系统的通信限于瘫痪状态,因此做好主机的在线备份是一个重要措施。
2 `* N/ j' @6 v0 p2 g4 q0 A4 m; J( g
**传统光电隔离的典型电路:6 |, H* u- x2 i! d' X! A
. e: w: v: D" a& z
9 [6 ~/ F/ W! _8 V. g8 D+ l
" t4 u/ t, t+ Q
VDD与+5V1(VCC485)是两组不共地的电源,一般用隔离型的DC-DC来实现。通过光耦隔离来实现信号的隔离传输,ISL3152EIBZ与MCU系统不共地,完全隔离则有效的抑制了高共模电压的产生,大大降低485的损坏率,提高了系统稳定性。但也存在电路体积过大、电路繁琐、分立器件过多,传输速率受光电器件限制等缺点,对整个系统的稳定性也有一定影响。' C5 A; ~+ \5 m3 r7 L& u
" b6 ?% S1 Q% D" X3 F2 j6 r***RXD1 :串口接收端
; p) {8 _* E7 l3 |( i$ P, i* K" ^8 P B# W. t; Y; _
***TXD1 :串口传输端; {: I# q, x9 a# ^) ?
4 i3 I S" g7 u2 `
***TRE1 :为控制位:控制发送还是接收数据;- u7 s8 X _6 J. f: M& n
, Q+ f; e7 ~, ^8 U( C0 a, v
当TRE1=1(高电平时),光耦电路121截止,/RE=1(无效),DE=1(有效),即发送数据;& x: O! ^1 J/ n5 G$ [! m3 M
+ ]$ m0 U( u( L3 p- W1 V- ]0 w* F当TRE=0 (低电平时),光耦电路导通,/RE=0(有效),即接收数据,DE=0(无效);
' q- h7 v* v, j. _! z6 m1 A4 O# ]4 e
/RE: 485接收端
5 }! ~% E1 n+ x1 b
8 S* q+ ~$ F3 J+ A( }DE:485发送端
8 c. T I2 K" r% @( X* j6 v2 L2 o3 `& G- c( e& x
" v1 |2 e; r; L+ Z: M
( \9 h; s! h3 E1 L. V
, {7 R) o2 X; u$ T" b) b
3 k( @6 b/ t2 n0 @
1 c; F. G# ~# P* [第一步,配置好串口发送、接收端引脚和485控制引脚;
8 a; R- }3 s0 F1 d. o; K6 y* k& w1 ~" w# N: Y- q
因为RXD1引脚相对于STM32芯片来说是接收外来数据,所以设置为输入; W/ x; Y, U$ W
% U- U. t' n8 ~: q/ z3 l9 C$ \TXD1引脚相对于STM32芯片来说是对外发送数据,所以设置为输出;' o( x$ a) |: V F& ^( B
5 D% r0 P$ d/ ]! [6 sTRE1 引脚是对外发送“1”或“0”高低电平命令,所以设置为输出;9 W/ Z+ E& b6 A) ?, R- k6 Q
, W2 r, D5 D/ A( q- /*****************************************************************
( H- v! Q9 s0 A) Y5 y - *函数名称: UART2Init6 X4 S# T. s5 t
- *功能描述: 对串口2参数进行设置、485控制端口初始化
- [0 M& _/ Y/ Z6 R$ i( ^) Z - *
. o3 c0 q' ?3 A g; W - *输入参数:无
4 E7 |# s# u) g. j. }3 L - *返 回 值:无
! H8 ^2 Y& _0 ? - *其他说明:无
' g- p0 \' U' d7 S, g3 g - *当前版本:v1.05 g' O5 O# o- Y6 K/ a
- *作 者: 尹宣
2 ~3 G- R/ @& y& U - *完成日期:2012年8月3日
9 K! d: T* [% T) L; S/ t8 U9 G: K - *修改日期 版本号 修改人 修改内容. Q0 T% p9 K! v0 l) d0 O7 w# z
- *-----------------------------------------------------------------
1 `6 T ^5 I5 V* {( P9 q8 G8 ] - *! _ i% }$ ^& G+ O0 J/ b
- ******************************************************************/+ T- m' F/ t9 ]3 u
- void UART2Init(void)
0 w4 [. V7 @' @8 z2 E, | - {+ x/ X9 h8 S$ z% `5 h
- GPIO_InitTypeDef GPIO_InitStructure;/ x$ }; t6 Z- x3 n) i( E
- USART_InitTypeDef USART_InitStructure;9 {" A, k. t& k& z$ c( C, r2 B
' i- n6 [9 y2 X+ m- RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2, ENABLE);
' E: V7 \( J& O6 B2 u- C - RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
) t; U# I2 ^1 c1 T _; | - RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
$ f# b+ ^- I, X' @1 L - RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC, ENABLE);//使能外设时钟
* F+ U4 c' ]- S3 s9 n. ]1 `
7 m q+ z; `% [0 }3 `" H/ ^- //GPIO结构的成员设置如下:* L% [) ~4 d- W9 Q" V# e2 {# _
- /*--------------485控制端初始化------PA1----------*/( |# P6 i: c; _! m K
- GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1 ;; m2 N1 ?. w4 c# w
- GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //50M时钟速度
5 s1 g; D6 N, J$ C5 ^5 o, M5 R* ` - GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //推挽输出$ P( B6 ?* \! T+ @+ X V- F
- GPIO_Init(GPIOA, &GPIO_InitStructure);9 n( C4 g3 {5 n- o; u" X- J
& k$ [1 m6 [' M9 Y8 M& G9 k
: H6 }- Q& n3 r. ?- X( }: g s( z- GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2; //485_TX
& m' Q1 m" y( c5 D) ~ - GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; 2 L8 e) ?* Q/ s, v, [
- GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //复用推挽输出
& P1 o3 ^. Q( t' m: g - GPIO_Init(GPIOA, &GPIO_InitStructure);7 ~) N- @9 D9 o4 G8 {
& @4 |# d: j! Q; S( G" s( q- GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3; //485_RX
0 c* a2 ~7 V& b7 G/ n) @- I2 L - GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; //浮空输入
0 W" X. V+ ~ H/ J7 f) S' d9 @ - GPIO_Init(GPIOA, &GPIO_InitStructure);+ b1 _, T0 N2 e. C! {, ?. B( A
9 |" V. t7 E9 @, c% D- //串口的结构成员设置如下:
' D5 [, D, q" p* T1 ^0 U7 G. C - USART_InitStructure.USART_BaudRate = 9600;
/ \. \0 d7 _7 L! n. n/ a - USART_InitStructure.USART_WordLength = USART_WordLength_8b;+ Y1 j' _( Q/ J' u. q! M" E
- USART_InitStructure.USART_StopBits = USART_StopBits_1;
6 U# w1 G/ v8 d5 Y. f/ x h) S - USART_InitStructure.USART_Parity = USART_Parity_No;
0 d. z" M. s4 ^$ a - USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
) A( @* a# v) ~$ [& h9 s& D - USART_InitStructure.USART_Mode = USART_Mode_Tx | USART_Mode_Rx;
+ R3 U" o5 K8 Y7 D; y+ M5 g | - USART_Init(USART2, &USART_InitStructure);+ Z; m' o; u# {
- 2 N1 l* T0 U1 B5 w
- USART_Cmd(USART2, ENABLE);
& _( v( I% m4 x& m - 5 J4 x% e6 X1 L
- /*方法一: 清发送完成标志*/$ n- |2 V8 o2 D
- // USART_ClearFlag(USART3, USART_FLAG_TC); : H1 P6 G" `/ b! D) W0 V7 `! r6 y
- 9 [* b% P0 ~9 X; k
- /*方法二:获取串口1状态标志位*/5 w5 O8 O0 W) D5 k3 W
- USART_GetITStatus(USART1, USART_FLAG_TC);
+ X, U1 T' r8 ~- j5 R# K/ S8 e - 5 U1 S; X. y8 _
- }
复制代码
& k2 c' Q3 b8 C( D1 A' t9 I2 C, p+ [7 |! l( D4 C7 U, t+ w
第二步:发送数据. S7 D$ o* z5 J4 Y" a1 K$ A! |
# }8 ` E: F( p2 h/ x0 B
这里需要注意的是:
( y% j- G" k1 p# D6 o
6 W! ?3 Y4 ~8 [; e0 P S/* CPU的小缺陷:串口配置好,如果直接Send,则第1个字节发送不出去
Q0 p: X! B0 O C' ?
3 {) W2 e& D5 }( S如下两个方法语句解决第1个字节无法正确发送出去的问题 */7 U9 m; X5 }' K* u3 J; k
9 C+ f. z, E# O2 A% D3 N# ?方法一:USART_ClearFlag(USART3, USART_FLAG_TC); /*清发送完成标志,Transmission Complete flag */- I7 t J3 ?# C" @' ?& v
! w% w O+ ?- o# m方法二:/*获取串口1状态标志位*/
6 r/ R- a$ H$ H3 e* O- t7 i# Z& i( |1 X& ~
USART_GetITStatus(USART1, USART_FLAG_TC);" y$ S7 g1 q- I) _" m p
2 T" O, t( k8 F( r
刚上电时出现乱码的原因:
' G6 d$ f9 ^% [0 `
1 T7 t3 }; [% `while(USART_GetFlagStatus(USART2, USART_FLAG_TXE) == RESET); // USART_FLAG_TXE---检测发送数据寄存器空标志位8 f/ Y) u+ k# A% n6 c1 _. Z' h
0 \7 \# j7 s& ?( _
如果USART_FLAG_TC---发送完成标志位" F# L. z. a; W0 R
8 r+ y8 f- m8 i" L4 n7 \0 f: s& `; ?(1) 当设为USART_FLAG_TXE---检测发送数据寄存器空标志位—为空,但是发送移位寄存器不为空,数据还没有完全的发送出去,又有数据就被写进来了,所以就会容易出现乱码;" K' ]6 Q- H# \. h: E
- Z& j& E1 a1 t6 v H3 Q
(2) 当设为USART_FLAG_TC—检测发送完成标志位—为空,即发送移位寄存器为空,数据才真正的发送出去,因此此时又有数据被写进来也不会发生乱码3 _. x1 B. t: ]. P- e# a
" I, P* m+ o& `4 B: u5 `
3 f$ B/ h: T* Q8 r9 d/ J9 L
6 r, [* Q( L p8 f( [# \) K6 Y/ uSTM32的数据发送有两个中断标志,一个是发送数据寄存器空标志,一个是发送完毕标志。两个标志都可以引起中断.
. V* @$ N9 s; C5 R9 I$ E) s
2 _ J6 I) t. A: s" H$ \要以中断的方式发送一个数据包,流程是这样的:7 J1 s* ]: F `, j6 R
4 c: _5 W* N% O* B' w; J
1.设置RS485的方向为发送,使能发送寄存器空中断,使能完毕进入串口中断。1 p, z/ ?! i9 k& i. M
* `$ D( o4 k- j2.串口中断里读取串口状态,并填充一个数据到发送数据寄存器,硬件自动清除发送数据寄存器空标志,串口数据发送开始。" _7 @7 k% Q- D4 [
5 J' [7 t# ~- |" B5 Y' x+ H1 U+ s3.串口发送完一个数据,发送数据寄存器变空,再进入中断,继续填充下一个数据,直到最后一个数据填充完,使能串口
0 @& l3 N3 T: L' c, n$ d+ D& p" Y/ i% @
发送完毕中断。* y4 I& q7 B3 K4 N# n& l- R
) _7 Q8 b& Z) z2 ^! A4.最后一个数据发送完毕,再次进入中断,清除发送数据寄存器空标志,清除发送完毕中断标志,清除这两个中断标志
& C1 E0 a* d: S) o" D2 L U5 E9 ^8 h( z; n
的使能位,设置RS485的方向为接收.2 v. l/ r% E& p, i- |
% B( P4 c7 A! H/ { p5 L& ]- /*****************************************************************% l+ J* P2 H8 {2 o$ k
- - k7 U' z8 n* c
- * 宏定义
6 `5 w* E7 G/ t) [ - - k) d2 v0 E: z# }$ W! ]+ y7 v
- ******************************************************************/
0 x) |8 b9 J+ ^8 X3 q( k - * y# D! q6 d, G' S, B: q
- #define RX_485 GPIO_SetBits(GPIOA,GPIO_Pin_1);
+ W" w2 V; [7 m0 o' F5 C
5 y; e8 B$ h# l- #define TX_485 GPIO_ResetBits(GPIOA,GPIO_Pin_1);# \% m0 H0 h) f3 e1 m- E- [
$ P4 H% T7 _' m# h6 v" P0 _- /*****************************************************************
N$ P d! n4 X. h. m! T$ M' U3 \2 L
, z/ Q D+ p9 y7 p8 P3 h- *函数名称: UART2_TX485_Puts$ k: z! d- J; u- T7 g9 F
+ N8 D4 a/ n' p5 D) T* v1 R- *功能描述: 发送字符串数据
8 u% s- S- S/ ], w- G: L
& q/ [! k% F' q) m8 ]- * ! g" u, `6 P( b3 @' L F' S& W
- ( c% O6 \$ {3 \( g! x
- *输入参数:str:要发送的字符串, T" Y8 t& f. z% L
6 h, n3 R; D2 L. f" Q- *返回值:无
" l* G% s! X1 p7 p8 {
8 g4 t' w. H8 z& J+ U7 Y K- *其他说明:无" A: L% T% I4 b& x- ^4 k4 ~1 K
. K- Z% I' I( B) [; N4 C3 a- Z1 P b- *当前版本:v1.0
/ j, N% z \3 _( N3 o) r5 {
- H, c3 S4 M* h3 l8 T- A0 H) _- *作 者: 梁尹宣9 J6 J( e. g0 D$ q' E5 l
2 ?0 {2 }& p) i3 l: f- *完成日期:2012年8月3日
5 L9 M$ ]2 Q0 \9 s - # I1 ~* n7 X; d# K( j
- *修改日期 版本号 修改人 修改内容
% ~1 V, O }, c, P3 `
( ~; v: m8 w" G8 w" O' ~- *-----------------------------------------------------------------8 b1 q# y, ]. O
- 2 W1 y5 o: j+ l1 Z n! K
- *
0 b6 |% U2 z; e' g0 u - 5 Z9 F8 W2 h6 a; e
- ******************************************************************/
' r6 p) e" S7 _, |1 C* v
T( T" j6 c# o8 v) v: S
+ j5 h/ ] r: n4 q' [4 Q1 P7 c
( P- S. Y. R" j1 D- void UART2_TX485_Puts(char * str)3 T% h- [9 ^" N' K1 {$ f" T
! j7 H* a% G5 Y- t- { 6 E$ q5 P7 f% V( z o! k
- & ]1 A0 V- h8 p6 n& ^
) Z: A( X+ Y- r3 d" U7 {- 2 A+ g$ d, I' \5 U# ], F) }3 H
- while(*str)* O3 P( |( s. o# U1 \
! k. R U3 T; v9 w2 r- { 0 |) a- ^" m+ J S4 {, Z
- I1 l' m5 [2 U7 Y Z3 k2 J3 S& ?- TX_485; //打开485发送DE端口,关闭接收/RE端口% V5 J) h' A- V7 G# ?* e: \" t
8 l/ i9 S& v; Y8 o- g: c- DelayNmS(1);
' U; } k. N. |1 e/ U. O - |# s/ ]- }: E5 ^6 O. w3 m
- USART_SendData(USART2, *str++);. q) u/ x. {. h, D, |+ K+ @& l& X
: U2 W- D: ?' N) o$ n/ s0 K- /* Loop until the end of transmission */) g. ?7 C2 W' n' v% l
- . g. y' v7 y' k" l$ Y5 E" n
- while(USART_GetFlagStatus(USART2, USART_FLAG_TXE) == RESET); //检测发送数据寄存器空标志位# a4 F2 X+ J* I- B0 s9 K4 x
- + R1 `4 M% k/ U" X- u$ k
- DelayNmS(1);
/ b- z4 _ x% g) X# N% ]' ^9 K
( n5 ?# N# K- A: Y+ G3 j7 O- RX_485; //关闭发送DE端口,打开接收/RE485端口,5 Y2 ]9 {" a6 [
- : B% M& ]% o% T6 J
- }
4 q" _. }4 i9 v& n
% n$ ~$ c! t" h) L; u; ~- }
: Q5 Y2 W0 l; T8 c0 v& u: r' g g
复制代码
# S* P7 g3 A1 D2 k8 h% r在485芯片的通信中,尤其要注意对485控制端DE的软件编程。为了可靠工作,在485总线状态切换时需要做适当延时,再进行数据收发。具体的做法是在数据发送状态下, 先将控制端置“1”,延时1ms左右的时间,在发送有效的数据,一包数据发送结束后再延时1ms后,将控制端置“0”,这样处理会使总线在状态切换时,有一个稳定的工作过程。% {5 x& A( F1 {& }" \( P
2 B: r: [% h% Y6 H3 W& v, [# g7 N
5 ^0 `& [1 K) E" K& h9 K6 N
* F% K" I( S' b6 s- _8 g |