EDA365欢迎您登录!
您需要 登录 才可以下载或查看,没有帐号?注册
x
在多机通信过程中,所有设备的 RS232接口是并在通信线上的,其中只能有一个设备为主机,其他为从机,通信由主机发起。数据帧一般采用1位起始位、9位数据位,其中第9位(RXB8)被用作为表征该帧是地址帧还是数据帧。当帧类型表征位为“1”时,表示该帧数据为一个地址帧;当帧类型表征位为“0”时,表示这个帧为一个数据帧。 在AVR中,通过设置从机的UCSRA寄存器中标志位MPCM,可以使能USART接收器对接收的数据帧进行过滤的功能。如果使能了过滤功能,从机接收器对接收到的那些不是地址信息帧的数据帧将进行过滤,不将其放入接收缓冲器中,这在多机通信中有效的方便了从机mcu处理数据帧程序的编写(同标准51 结构相比)。而发送器则不受MPCM位设置的影响。7 u- O; F, L1 r6 k8 r. I& p$ R5 g, K
多机通信模式允许多个从机并在通信线路上,接收一个主机发出的数据。通过对接收到的地址帧中的地址进行解码,确定哪个从机被主机寻址。如果某个从机被主机寻址,它将接收接下来主机发出的数据帧,而其它的从机将忽略数据帧,直到再次接收到一个地址帧。(从机地址是由各个从机自己的软件决定的)。4 A5 Q+ W+ ~) K5 x, d
对于在多机通信系统中的主机MCU,可以设置使用9位数据帧结构(UCSZ=7)。当发送地址帧时,置第9位为“1”;发送数据帧时,置第9位为 “0”。在这种情况下,从机也必须设置成接收9位数据帧结构。% ^. _& T/ N6 D, N, U/ Z
多机通信方式的数据交换过程如下:
3 `" Y; b% u4 i- w1)设置所有从机工作在多机通信模式(MPCM=1)。
. O7 j7 \7 e- x- b( G9 p8 f2) 通信开始是由主机先发送一个地址帧,如8位数据为0X01(1号从机地址),第9位=“1”,呼叫1号从机。2 s( l% x- ?- Q7 o( ^
3)所有从机都接收和读取该主机发出的地址帧。在所有从机的MCU中,RXC标志位被置位,表示接收到地址帧。
$ W2 u4 w2 r, g/ _! t4)每一个从机MCU读UDR寄存器,并判断自己是否被主机寻址。如果被寻址,清UCSAR寄存器中的MPCM位,等待接收数据;否则保持MPCM为 “1”,等待下一个地址帧的接收(该步应由用户软件处理实现):
0 ]4 d* V7 [! z" G; z: q/ l" nA)作为1号从机的MCU处理过程为:收到地址帧后,判定读取UDR数据0X01为自己的地址,将MPCM位置“0”,接收之后所有主机下发的数据帧,直到下一个地址帧为止。
! y: ~ c% n }# cB)其它从机MCU的处理过程:收到地址帧后,判定读取UDR数据0X01不是自己的地址,将MPCM位置“1”,这样他们将忽略主机随后发送的数据帧,直到主机再次发送地址帧。8 \9 s% h& k: o% ^$ n# w- G
5)当被寻址的从机MCU接收完最后一个数据帧后,将MPCM位置位,等待下一个地址帧的出现(该步也应由用户软件处理实现),然后从步骤2开始重复。 [转]例子; 通讯规则:! w0 [, I8 F1 M0 \6 `
1:时钟7.3728 MHz/波特率9600/9个数据位/奇 校验/1个停止位/硬件多机通讯功能/) n# o4 ?! A/ o$ f9 N% g v" {
2:通讯连接采用硬件MAX485,双向单工
. F' z: x5 X/ t6 K: V ~3:每个上行/下行的数据包的字节个数都是一样的(通讯数据量)
# ?6 b o9 Q* m) F, W5 D4:每个上行/下行的数据包都采用CRC8校验
- J8 F6 D; I0 v! T7 t, o5:数据接收采用中断+查询的方式
2 p* B1 q2 T$ }0 D/ L" r5 }# q8 ]9 e6:总是由主机向从机发送一个数据包,从机收到数据包后向主机回复一个数据包
7 q, U- U1 W1 ~7:不管是主机还是从机,如果收到的数据包有任何错误,都将丢弃该数据包,等 效于没有接收
: A3 p: }" g, i) q$ p8 f8:从机之间不能相互通讯,必须通过主机才能交换数据0 \) p# Q+ u+ I# ^, \- T
9:无效地址是0,主机地址是1,从机地址是2.3.4......广 播地址是255
/ @, e2 X6 B: I; h$ X. ^*/
3 M6 _. b9 s5 R#include
* k1 i$ K0 |: S# t. z' L* t& m6 r#include 7 {. `! H+ h8 E! s* B0 w
#include - ~2 g, N2 }. k. w) u& i% U' s9 K6 |
#include //CRC校验函数就在这个文件里面
) T. @) m) Z0 s' c& p
" j U' x+ q+ k#define amount 10 //设定通讯数据量(包括1个 地址帧,n个数据帧,1个校验帧)# a) ` C1 _+ v+ f8 s
0 Q( F# i( ~7 C2 X1 cunsigned char send[amount]; //发件箱
1 x2 b; D/ u: o% l- d, kunsigned char inbox[amount]; //收件箱 \7 i# |- t& w1 @4 W" I
unsigned char n=0; //记忆中断次数: Q3 }+ g( o" K0 T# \: i
' a: ~# z* o o. r/ i% {/ D o l6 q5 w//--------------------------------------------------------------------5 N5 o1 x2 O& f- f! ~5 |
interrupt[12] Rxd_isr(void) //接收中断3 u5 u. T/ L5 ? k1 w$ z1 J
{
2 K& |: R. `, u! ^; O3 gunsigned char ERROR=0;3 w& c$ }0 y4 h/ [5 l* R+ B
if( UCSRA&4 || UCSRA&16 ) ERROR=1; //奇偶效验错误或者帧错误就记录下来2 M) U2 x% H2 h3 Y! H4 J, A8 v
inbox[n]=UDR; //保存到收件箱+ h. I* _) L5 k/ V% U
n++; //记忆中断次数! e1 Q+ z0 u( `4 s
if(ERROR) inbox[0]=0; //如果通讯有错,收 件箱的地址帧就标记成无效地址0$ Z; A/ X' W" l7 } e; ]( p
}
6 p, Q M4 ]6 b+ ]# A' Z# ~: h# Q
//---------------------------------------------------------------------; [: f' d% @; L. N
void main(void)8 i1 N+ \2 y" D2 }) T& t! K9 O7 ^ b
{4 o$ W* S V- p. o, A1 B' D# u- E
usart_init(); //串口初始化3 F4 ]0 i- I8 U0 S& b4 D. b" x9 f
UCSRA=0; //主机关闭地址筛选功能(多 机通讯功能)5 C- l: ]. {7 D- G
#asm("sei") //打开全局中断+ r5 o7 u, \" m. Y, f" g! w# n
while(1)
1 M* d2 Y( F/ v5 ^{) h% g& V5 @! V' h8 o
//-------------与从机2对话,与其他从机对话与下面的 程序类似-------------------
F$ E" ]2 J0 Rn=0; //中断次数清0
4 N3 }6 b3 E9 ]inbox[0]=0; //收件箱地址清0
0 o6 |2 o8 e) C6 y3 L//请更新准备发送的数据; J6 B; s+ l6 d5 _
//send[1]=?
2 e! x* W* X7 u, c2 O//......5 P: f7 Q C' @- }' ]4 K- p
//send[n]=?
$ E* N% M K- [" u1 Ksend[0]=2; //改变这个地址就可以实现与某个从机对话
) V$ j/ F7 u& a2 I$ O9 dsend[amount-1]=crc8(send,amount-1); //计算发件箱的crc8校 验码2 L0 m& `) f% F1 H$ w5 m; f( k
usart_out(send,amount); //将发件箱的数据send[]发 送出去;
) d$ q; V c- M0 ]
0 Y5 T7 j0 ?6 x+ D1 N/ U$ k$ r' [//等待,从机接收到数据后会回复数据的,如果是10个 字节数据量,不能少于13ms!!! }$ \) E2 _) ~& D
//这个时间由人工计算,要考虑从机由于各种中断延长回复时间的可能
$ G$ Q0 I, |4 K [( y6 G7 M- `4 V5 @
delay_ms(15);
8 M4 s# W( T2 l8 d) L6 a7 T) O/ m5 A
//if(n<3)如果接收到的数据还不到3个,那么就是通讯线路故障3 v9 l; @: ?. I! g, @7 m
: ~; K) b7 V! n+ c! \- d//如果收件箱已经收到amount个数据,并且crc8校 验成功就...
' x7 o+ s' m' I7 ]- jif(n==amount && inbox[amount-1]==crc8(inbox,amount-1))1 X& i3 K& J# h
{
& _2 X3 y( E# ~7 {- Aif(inbox[0]==1) //如果收件箱地址帧属于本机就运行下面的测试 代码
7 c2 o' H1 |9 o. W! d' z1 A{( J. K8 Y5 j8 x& _4 @# S7 f
DDRD.3=1;2 k& ~+ |2 S# e5 R" D1 ?
PORTD.3=1; delay_ms(10);, Q" j( t8 F) C2 r& O
PORTD.3=0; delay_ms(990);
9 s7 d. M) c" x1 {4 V}' i* H `, U6 b
! ?# L" K( g4 i- i- _
if(inbox[0]==255)8 M- ^) ~- v g: \9 [( @
{3 B) }$ @/ ~) X& ^" C
//请在这里添加收到广播数据的处理程序
5 G6 m( f1 `. A) J. d9 h}
& h6 M+ C& v' D1 F}
/ e( e& w8 F8 C}- W8 i! W6 V% ? H$ o8 L
} //end
4 m9 ^' F' ^5 O- C
2 |% v: O! S0 ^7 v---------------------------------------------------------------------------------) t4 N7 E* {1 z5 D6 H
从机
# q/ @$ t, ]& h {, j---------------------------------------------------------------------------------
$ t+ Z, t" ~) {" _& P/ H4 X#include
' ]. m _6 f, H3 `& Z#include
* }2 F% }3 w/ z0 F1 \#include
4 ]6 V' `5 ?; G0 ~9 A O
5 ^3 Y1 w ~/ ]#define amount 10 //设定通讯数据量(包括1个 地址帧,n个数据帧,1个校验帧)# Z) \- z0 G3 N5 j
#define address 2 //请在这里设定本机地址
1 I# B: Y/ L9 k
0 M$ a/ V( n! s2 ^, ^unsigned char send[amount]; //发件箱3 g/ Z" o y/ j: ~" P& [
unsigned char inbox[amount]; //收件箱
( W, g* W6 g/ g% N! h: t. n' A& Dunsigned char n=0; //记忆中断次数
. N$ }5 a8 F9 ?' ]
1 m0 l9 g3 ?6 g8 ~interrupt[12] Rxd_isr(void) //接收中断
% f* {/ u& |, |{
" y( V! s8 v; ^unsigned char ERROR=0;
$ k! ? E! r* W. o2 q* J2 {4 pif( UCSRA&4 || UCSRA&16 ) ERROR=1; //记录 奇偶效验错误或者帧错误3 H4 n0 \( ~8 v7 M% s
inbox[n]=UDR; //把接收到的数据保存到 收件箱0 G+ j- X; H" T3 N1 n# j* {
n++; //记忆接收的次数
" f4 w) M+ V2 N2 T, P% |if(ERROR) //如果通讯有错....% N; w2 [! b% B3 t8 ^) U6 G
{
C6 b" C. a3 x kn=0; //接收计数清0
4 W/ s5 M0 e: \5 D- Z2 O% X# ?inbox[0]=0; //把地址改为无效地址0
/ ?! C* r" c/ I5 v; LUCSRA|=0x01; //重新打开接收器的地址 帧筛选功能
3 c& O" ~' h8 l; z9 J}
8 d. W' e7 v7 U1 N. W6 C1 V% X+ S2 ]8 |) S& Q! h5 `
//如果地址匹配本机或者是广播地址就关闭地址筛选(多机通讯)功能- b1 M; m' D# ~) H B
if(inbox[0]==address ||inbox[0]==255) UCSRA&=254;, v; ]2 s% n- D6 p% Q' a$ K# v
/ j }% U" I+ k8 Z: X
if(n==amount) //接收到amount个 数据以后...0 ~/ ^2 _6 k. s# X9 p7 d
{
/ o, o2 h2 `4 @! ~; ]n=0; //接收计数清0
1 u# \" d# c' t5 r. r+ Z: K# iUCSRA|=0x01; //重新打开接收器的地址 帧筛选功能5 \& u* @& K% ]: E0 I
if(inbox[amount-1]==crc8(inbox,amount-1)) //如果crc8校 验正确就...6 t c2 W0 a; Q; s/ P) Y' {4 X, ^
{
( M7 N+ ^8 F& v: Jif(inbox[0]==address) //如果地址匹配本机就回 复数据0 V% d0 L6 n4 e4 Q# h' ]
{
; M' s" {3 L9 ]8 [+ J* usend[0]=1; //发件箱地址指向主机$ N% q' }7 M, E) p1 a, i( ^
send[amount-1]=crc8(send,amount-1);//产生发件箱的crc8校 验码
& n+ a* ~( D5 F: V5 r, b$ xusart_out(send,amount); //发送发件箱的数据包send[]
: T% l' d4 V0 |1 r//请在这里备份你的收件箱信息" m. t$ ?2 T/ o" }
}
1 w& z! [( y. }( C0 O7 i* `, J% [if(inbox[0]==255) //如果是广播地址就...
+ K! W; _/ V4 S4 d# B. f( h{
% Y/ p9 m5 e6 J1 n//请在这里添加你的代码7 z) p' J% [4 z( \
//收到广播数据请不要回复
* N/ N" S$ } M. a" ]}9 k ~6 K) X4 y7 L
}
- A: x+ R/ F" ~! w3 G* K}2 b3 n9 O, a1 ?( V5 o \
}
/ ~ g0 G- C' N4 N6 O9 P( Z1 q" P0 Z5 w% D
void main(void)! p* M8 e/ `- Z; a- S0 g
{; Z" ? M3 B! O+ i/ y$ \4 g
usart_init();. F" X& k" a6 t5 l. Y9 o
#asm("sei")
4 S- ~ ^9 k! P4 U4 P% W+ R% x; u/ ~9 T
while (1)
% l7 b6 B, y; s$ t {8 R2 b{
5 b/ d4 S6 T+ A//send[1]=?6 V& `5 {" M; j1 I8 q% u" G' }
//......
6 b' q$ ^+ V/ ]' ], J0 U; V5 b: j//send[n]=?
# {. \1 I2 v3 [};
% s( @. F. v! n8 H' y8 a}
$ d( d W! H6 j
& Q( @' ?) l7 d8 _0 c( w r! Z) l. N+ L, U. h
---------------------------------------------------------------------------------9 G+ }. S; z+ P0 d- X: a3 k' |
usart.h文件' o1 l) V3 [- ]) l- L# N9 i( S
---------------------------------------------------------------------------------
. M$ T* t. o: G9 W//波特率9600/9个数据位/1个停止位/奇校验/收 发开启/接收中断
2 w7 @9 a7 Y( y0 L8 ~void usart_init(void)
. I2 e5 i4 U% }* j{6 u4 f) e; c3 x0 c1 h
UCSRA=0x01;
* k% H. O ]" P2 l+ v; uUCSRB=0x9C;9 k0 a( O! F* Z) |9 G q% I* D
UCSRC=0xB6;
7 l5 U' t' U% I* _( R# C! R- RUBRRH=0x00;; r8 e8 ?& \: @/ _/ e9 E7 X) R
UBRRL=47;( S# O) d3 a4 T8 F' j
' c& g1 U3 m9 j! xPORTD.4=0; //MAX485平时工作在接收状态
1 q( C" @2 k2 z* A* k' z6 {DDRD.4=1;, S$ M/ b z! B& {; w4 M# d X
}
& B* h% A3 W9 {2 s, G4 m/ A* e/ z7 x( X4 D0 C" ]6 D! u
//-----------------------------------------------------------. f( w% V% c# z. x* _
//从数组datas[]的首地址开始发送amount个数据,其 中第0个数据是地址帧,其他是数据帧% }' N- N- @! a" e, {" C8 T
void usart_out(unsigned char *datas,unsigned char n)
, _( o: i, B2 _- C{
+ d: R( S, Z$ ^1 i3 dunsigned char i=0;
# ` z8 s4 V* E, A" J* @0 ]3 SPORTD.4=1; //使MAX485处 于发送状态6 L; r9 a4 H( F6 V2 K8 ^
while(i {- K4 f# I6 w8 q3 M, e* l
if(i==0) UCSRB|=1; else UCSRB&=254;
8 j: ?2 t" o- C; {( nUDR=*(datas+i); //装载数据开始发送7 f) z9 U8 y* r5 L. } |- O# D
while((UCSRA&64)==0); //等待发送结束
8 W$ z# j. f! |# xUCSRA|=64; //清除发送结束标志
: F9 H( S5 a8 V/ ^2 m7 a+ Ti++; //发送次数统计
7 ]/ `7 ~; j. o: T) y3 ?$ J- U}
: l( }1 B |9 R1 ]PORTD.4=0; //使MAX485处 于接收状态9 u& P' W0 M4 a! R- v7 S
}
: f" l) I2 z. o0 B; I; y' K
2 D1 O1 n/ ~- A7 V---------------------------------------------------------------------------------
8 v5 H+ U# e5 q$ b @8 N& N7 Rcrc8校验程序# ^0 D1 y6 l E' B# C% ^8 I* j
---------------------------------------------------------------------------------
) U" s* L; u* N Z2 punsigned char crc8(unsigned char *ptr, unsigned char len)% n2 Y& @0 K! a' d
{ \( ?& K, v$ q! l* R0 j
unsigned char i;' ^8 h4 Q* A1 w* i0 h9 A
unsigned char crc=0;
9 c; W& t ~# Rwhile(len--!=0)
. d+ i5 W ~8 ~) f( W' p2 s: }{! i) C- C# l$ @. d
for(i=1; i!=0; i*=2)
0 P! S |$ N4 a& R$ d/ R5 Z{" w5 \$ t8 X! y' G9 F. i* b
if((crc&1)!=0) {crc/=2; crc^=0x8C;}& L9 R9 j; x8 \ ~8 C: G( V
else crc/=2;, u( z9 R' G; C+ L( d% \5 |
if((*ptr&i)!=0) crc^=0x8C;
5 M5 I) l! a' ?9 A. }: e& Y9 f} s2 t+ e$ ^5 p; _% Z. b( [; e" h
ptr++;* Z( i# r4 V5 Q, } _
}
( b7 l; e L/ f/ _return(crc);( N4 z5 y1 \9 S4 l
}
h; r- j0 O6 G |