EDA365欢迎您登录!
您需要 登录 才可以下载或查看,没有帐号?注册
x
在多机通信过程中,所有设备的 RS232接口是并在通信线上的,其中只能有一个设备为主机,其他为从机,通信由主机发起。数据帧一般采用1位起始位、9位数据位,其中第9位(RXB8)被用作为表征该帧是地址帧还是数据帧。当帧类型表征位为“1”时,表示该帧数据为一个地址帧;当帧类型表征位为“0”时,表示这个帧为一个数据帧。 在AVR中,通过设置从机的UCSRA寄存器中标志位MPCM,可以使能USART接收器对接收的数据帧进行过滤的功能。如果使能了过滤功能,从机接收器对接收到的那些不是地址信息帧的数据帧将进行过滤,不将其放入接收缓冲器中,这在多机通信中有效的方便了从机mcu处理数据帧程序的编写(同标准51 结构相比)。而发送器则不受MPCM位设置的影响。
K: Q* k- v- a2 r 多机通信模式允许多个从机并在通信线路上,接收一个主机发出的数据。通过对接收到的地址帧中的地址进行解码,确定哪个从机被主机寻址。如果某个从机被主机寻址,它将接收接下来主机发出的数据帧,而其它的从机将忽略数据帧,直到再次接收到一个地址帧。(从机地址是由各个从机自己的软件决定的)。
6 A t4 t+ j/ K- a+ i" u 对于在多机通信系统中的主机MCU,可以设置使用9位数据帧结构(UCSZ=7)。当发送地址帧时,置第9位为“1”;发送数据帧时,置第9位为 “0”。在这种情况下,从机也必须设置成接收9位数据帧结构。
$ s0 H3 N# Z: f1 _ 多机通信方式的数据交换过程如下:/ |% f/ J# t3 f: @* V% v" `
1)设置所有从机工作在多机通信模式(MPCM=1)。
- O* Y/ t8 e# T: M$ q2) 通信开始是由主机先发送一个地址帧,如8位数据为0X01(1号从机地址),第9位=“1”,呼叫1号从机。
; l, m' U; w& G( B% i3)所有从机都接收和读取该主机发出的地址帧。在所有从机的MCU中,RXC标志位被置位,表示接收到地址帧。
1 `; ~3 `8 X! j) \, |4)每一个从机MCU读UDR寄存器,并判断自己是否被主机寻址。如果被寻址,清UCSAR寄存器中的MPCM位,等待接收数据;否则保持MPCM为 “1”,等待下一个地址帧的接收(该步应由用户软件处理实现):
3 k1 p7 o3 x/ C: f! kA)作为1号从机的MCU处理过程为:收到地址帧后,判定读取UDR数据0X01为自己的地址,将MPCM位置“0”,接收之后所有主机下发的数据帧,直到下一个地址帧为止。; _; h, }" B* q0 j# E* e1 T# B
B)其它从机MCU的处理过程:收到地址帧后,判定读取UDR数据0X01不是自己的地址,将MPCM位置“1”,这样他们将忽略主机随后发送的数据帧,直到主机再次发送地址帧。
7 y! K& ]. m6 T; R5)当被寻址的从机MCU接收完最后一个数据帧后,将MPCM位置位,等待下一个地址帧的出现(该步也应由用户软件处理实现),然后从步骤2开始重复。 [转]例子; 通讯规则:
2 d6 u3 J& h2 l1:时钟7.3728 MHz/波特率9600/9个数据位/奇 校验/1个停止位/硬件多机通讯功能/
1 B8 l7 `3 w/ L; X$ Z% h2:通讯连接采用硬件MAX485,双向单工 V( g$ p3 `7 B7 }
3:每个上行/下行的数据包的字节个数都是一样的(通讯数据量)
2 J |, L+ `' \* j' ]- ~5 M4:每个上行/下行的数据包都采用CRC8校验# e1 a/ e0 Z3 M) m4 |: I8 o
5:数据接收采用中断+查询的方式( c/ v( E3 R7 m3 f \% F
6:总是由主机向从机发送一个数据包,从机收到数据包后向主机回复一个数据包
d$ v& ?! X5 L- W5 L7:不管是主机还是从机,如果收到的数据包有任何错误,都将丢弃该数据包,等 效于没有接收- J( p# x' V+ N
8:从机之间不能相互通讯,必须通过主机才能交换数据: _! C/ @9 D; Z6 B) F0 s
9:无效地址是0,主机地址是1,从机地址是2.3.4......广 播地址是2557 ]. j+ O' i7 t: x
*/
0 R0 P% [% G. h6 O* g* `, |7 L8 y#include & s$ ^, j8 b5 M! k
#include 9 k+ J" r6 L) e/ _) H8 s2 l
#include ; V8 x4 E! C# A ~7 V' \
#include //CRC校验函数就在这个文件里面
% d4 l( E) @% R0 o) Z. D
! r$ Q* f) s4 X2 [+ @#define amount 10 //设定通讯数据量(包括1个 地址帧,n个数据帧,1个校验帧)# t4 }: r# X3 ]1 @
* O4 B% A, r5 p$ H, S+ H
unsigned char send[amount]; //发件箱
$ B' M+ b9 j2 c$ |4 X+ gunsigned char inbox[amount]; //收件箱. `3 N+ A6 ^/ o! Z' p. I: f* J; R
unsigned char n=0; //记忆中断次数1 D7 W+ C: l* h* Y9 s' ^1 e
) n0 O8 z; j& Y, y7 ]7 X//--------------------------------------------------------------------
2 ^) U- W9 v# L, f' N3 V" Finterrupt[12] Rxd_isr(void) //接收中断/ T; H4 S/ S; ^+ k& x
{
$ d. u# ]; U2 }, R% Aunsigned char ERROR=0;9 }* \' V r! ~& w! w
if( UCSRA&4 || UCSRA&16 ) ERROR=1; //奇偶效验错误或者帧错误就记录下来
& B) J. D5 [# I1 S5 @' Iinbox[n]=UDR; //保存到收件箱- T" x' q4 W: k# W! `0 z+ O
n++; //记忆中断次数
; Z1 l3 H* T6 Q W: H, Y* M xif(ERROR) inbox[0]=0; //如果通讯有错,收 件箱的地址帧就标记成无效地址0. x; S. ^" L2 e }" H$ S
}9 t0 T" e- Z# ?& p/ g6 s
/ M& M7 O% v" W. l4 j9 @+ w! ^) k//---------------------------------------------------------------------
9 |0 Y/ u0 Z! U* H f }void main(void)3 \9 D1 c# w% m, `
{+ [7 R6 n2 ~6 M
usart_init(); //串口初始化
' I5 J. p7 y6 d3 p$ u3 GUCSRA=0; //主机关闭地址筛选功能(多 机通讯功能)7 [$ t- l6 n$ _) J
#asm("sei") //打开全局中断
+ v0 Z+ `6 H: o! C2 ywhile(1)
$ O4 s- d0 M) g4 ?/ `: |{
2 z! N; v( Y) v4 c0 a# d//-------------与从机2对话,与其他从机对话与下面的 程序类似-------------------
( i6 K) j4 |# N P+ R6 K3 N$ Kn=0; //中断次数清0
6 s9 P X, D) minbox[0]=0; //收件箱地址清0
5 X$ s- X6 `: F4 @6 p//请更新准备发送的数据- q- \& r4 r: h( x
//send[1]=?
3 x$ [2 p8 @0 w8 j; y" c$ [9 L3 f//......( [7 ^1 }- [" ] S2 ~- ^9 s2 x7 y y
//send[n]=?
8 w/ A1 e8 r6 f' K# ?! jsend[0]=2; //改变这个地址就可以实现与某个从机对话
+ q* T: `1 K5 `" X: Fsend[amount-1]=crc8(send,amount-1); //计算发件箱的crc8校 验码
2 p& @: t# M$ Z! h' t4 O% y7 p2 Rusart_out(send,amount); //将发件箱的数据send[]发 送出去;+ g8 I" ?7 E: {$ m
@" Y$ X" w7 L5 f
//等待,从机接收到数据后会回复数据的,如果是10个 字节数据量,不能少于13ms!!!: k/ d5 E6 E/ ]$ w7 S
//这个时间由人工计算,要考虑从机由于各种中断延长回复时间的可能% T# Y$ J/ |/ G, b% W; v; V
! M2 P/ k& u- [& T/ H, f
delay_ms(15);
5 H! u$ U! C, _
# r ?) ], G \; e//if(n<3)如果接收到的数据还不到3个,那么就是通讯线路故障
" A! ^4 P+ e: k7 r) K" H9 N: @; V$ D$ A2 j6 y' }) j. [4 N1 m
//如果收件箱已经收到amount个数据,并且crc8校 验成功就...
0 p- C7 a9 s. Z# `, n% \0 zif(n==amount && inbox[amount-1]==crc8(inbox,amount-1))$ M/ P3 f$ g& Q4 q
{
3 |% ]; z+ y2 e) x3 aif(inbox[0]==1) //如果收件箱地址帧属于本机就运行下面的测试 代码
8 j4 f& u0 O, S) P{2 s2 t, }+ `0 h* X1 \
DDRD.3=1;
: t6 p0 q0 z' G( P& m3 ZPORTD.3=1; delay_ms(10);0 p! G- d$ C: B5 Z* O) A% y( I6 P
PORTD.3=0; delay_ms(990);
$ {& y2 }; x- ]" h: F9 h+ r}
. S; p4 J1 h4 o" L( X1 i! u
8 y6 Y: L" n( B0 w: G* v: ~+ bif(inbox[0]==255)
1 t! v: T: a% x7 o5 ?{
& c/ `6 Z) E \1 o' X+ z- O# H//请在这里添加收到广播数据的处理程序
5 C9 b0 L* d1 A/ z* H1 f}
; F( W U% q, q4 R}
1 W" M# p, K' s" f9 J H}
( ?7 o* e6 n7 H1 R& ~: @' r% n1 s. R b} //end: t) B6 T, N& \
4 Q. G v$ x/ x9 |
---------------------------------------------------------------------------------8 `. y, M9 `/ o% [
从机: w3 u9 a2 T( ~& t
---------------------------------------------------------------------------------
1 s# M3 w' F/ ?9 A" @) u#include k* O8 V. Y8 ^4 K9 u
#include * v* N; S0 }8 u# D( R6 M T
#include % y! G9 V+ A9 s1 U
) R3 ?. H+ q" c9 D& P/ v8 f( |#define amount 10 //设定通讯数据量(包括1个 地址帧,n个数据帧,1个校验帧)
o1 {' {) o8 z b: ]" ^2 E& ~#define address 2 //请在这里设定本机地址
& n! R/ O) Z7 b6 y% b! n% {- W$ n! f) e
unsigned char send[amount]; //发件箱" Q5 E A) N( m/ t6 h4 a6 z
unsigned char inbox[amount]; //收件箱+ u/ h6 s% L% L/ z; j
unsigned char n=0; //记忆中断次数! D$ f7 J5 ^1 }' |
" T% r% j) e) H! h6 P& m3 E
interrupt[12] Rxd_isr(void) //接收中断" N! e( B: Q* c
{
4 o, N2 G' V! \; eunsigned char ERROR=0;5 X" c, q) ^/ X6 ~
if( UCSRA&4 || UCSRA&16 ) ERROR=1; //记录 奇偶效验错误或者帧错误
3 I: _# [- y4 A8 t. ~inbox[n]=UDR; //把接收到的数据保存到 收件箱
' @& }# w4 y7 Q2 s7 \9 rn++; //记忆接收的次数
% s. i+ {7 D1 k9 d! x$ | y5 @2 Iif(ERROR) //如果通讯有错....1 c% G# b( R- ?2 @7 s8 Q& \
{" [7 P/ h' p: |
n=0; //接收计数清0$ k' O7 g( m* H" t' U+ U S
inbox[0]=0; //把地址改为无效地址0
$ d7 h S4 A* T5 Y1 l: Z TUCSRA|=0x01; //重新打开接收器的地址 帧筛选功能
9 U k, Q! P5 ^9 G3 p( C5 J}: e/ G/ u( p, S. y, T
% \6 ~% o) j2 ^$ \# Z0 w9 [6 d. ]
//如果地址匹配本机或者是广播地址就关闭地址筛选(多机通讯)功能" ?5 p1 i# A# o* m0 U& R6 `$ F- x n
if(inbox[0]==address ||inbox[0]==255) UCSRA&=254;
1 D/ c( W' z( V. G7 z, S' ?0 t$ C5 G% q2 i& U3 E+ ^" Q
if(n==amount) //接收到amount个 数据以后...
p' @0 D. \. k- p! G- z, p8 H# Z( _{
. H! j3 ^9 L- d8 b2 @! e5 J# Gn=0; //接收计数清09 z# L7 R3 @# S6 a; x" E
UCSRA|=0x01; //重新打开接收器的地址 帧筛选功能
* X K1 x4 p3 B& y7 rif(inbox[amount-1]==crc8(inbox,amount-1)) //如果crc8校 验正确就...! Q0 Q p# M# f& a2 y# Z' X d
{
9 ~3 o! k$ }- s' Y1 }if(inbox[0]==address) //如果地址匹配本机就回 复数据
+ I, u7 z4 i5 E, n8 m2 F{5 ^. u# i, L2 r; y" ^# _8 [) u
send[0]=1; //发件箱地址指向主机& U# \8 I) x- ]2 L5 T) B
send[amount-1]=crc8(send,amount-1);//产生发件箱的crc8校 验码' o# s1 z( q; E* ]
usart_out(send,amount); //发送发件箱的数据包send[], K9 \6 \9 F1 K' v
//请在这里备份你的收件箱信息2 N: P7 M3 x3 `6 z* A1 L
}
& U7 Z4 x* j0 k+ n C+ P- C. W& zif(inbox[0]==255) //如果是广播地址就..." w+ P- B5 v8 A( `2 F0 j. \
{
1 |! s& q. K( g/ R- ~# G//请在这里添加你的代码
. Y; e8 b! b7 N M//收到广播数据请不要回复
2 G/ @( i9 [: U}
' \4 W+ Z* F) k3 F}
2 v m* S! B& [9 b8 i" h}1 v3 t( E' j8 q- A2 K! |
}
3 e' H+ c0 D3 J
$ C8 t9 @$ d4 i# }6 K! I! @5 `# m' Jvoid main(void)7 u% _1 r2 v% r0 h: k! T
{
" |( p2 Q& ]( ~usart_init();0 O$ ]' k% {; j; i0 S/ E& N
#asm("sei")" b5 E4 B: Z* B7 v; j) w
# q( d4 ?& g0 s" Q6 f2 g" {) ewhile (1) c# D4 Z. T0 `( U9 [: V7 @* ?( m
{$ h4 m) E3 o: L+ Y
//send[1]=?
) c/ Z: U- t# _+ c% z//......8 v0 f$ J2 R" N7 V2 K9 @
//send[n]=?
. [6 `. }$ f, \# A2 n0 t! L3 B+ N};+ x: r) v, [ s) t
}$ p* A/ W, q3 V: H
+ r7 Y* @; }% i0 z
/ P( M$ d6 ^- p/ K3 d/ F C
---------------------------------------------------------------------------------/ Z9 g' C# D- H+ x4 X; p
usart.h文件6 x/ p( P: R# _: b* g
---------------------------------------------------------------------------------
# Z- Z$ T- b& t, `. \+ K, @, b//波特率9600/9个数据位/1个停止位/奇校验/收 发开启/接收中断/ y, N" V. \) _! L( i
void usart_init(void), w |8 s* j x1 I
{! T- k, r i% v* ^- e% n6 `# ]4 }0 \
UCSRA=0x01;
: K& m7 {2 K) B# wUCSRB=0x9C;+ U. F2 ^( ~8 M9 a% K5 |$ Q) _
UCSRC=0xB6;
R- I9 N% W- _8 ^3 [# xUBRRH=0x00; k+ @& @2 m9 ?* V2 U, o9 m4 b M$ v) s
UBRRL=47;3 |0 k! \4 W* d
+ J* x3 g( a5 UPORTD.4=0; //MAX485平时工作在接收状态: b. O9 J0 q+ t. F3 S2 z- U
DDRD.4=1;
1 ~- ]( U4 T$ Q6 F}
- b0 m {' s1 k2 M5 s% m \ c/ O; L5 K+ p# N' h
//-----------------------------------------------------------
. w, T {2 c! e/ `" O+ s! [! Z//从数组datas[]的首地址开始发送amount个数据,其 中第0个数据是地址帧,其他是数据帧
0 `( z) X6 F* B9 K( I. [ u x8 @void usart_out(unsigned char *datas,unsigned char n)$ R- [9 E# q) F$ R. E j+ q# E% e# b
{
/ r5 f# s4 h! @* C) E! bunsigned char i=0;% k* w1 e u' a9 w, i" M
PORTD.4=1; //使MAX485处 于发送状态
* t/ }. ]6 b& o" }- z: N$ P) C' nwhile(i {4 A8 T9 L, W0 Z4 N2 m; H e" f
if(i==0) UCSRB|=1; else UCSRB&=254;
0 g( O6 a" d+ h! o3 r- e4 p- D HUDR=*(datas+i); //装载数据开始发送
1 d# K l% ~6 J$ y' twhile((UCSRA&64)==0); //等待发送结束
% W6 {3 i9 b: y8 R% D7 V: n* d7 ?UCSRA|=64; //清除发送结束标志1 f7 M& w6 F8 T3 g* ~7 k
i++; //发送次数统计: ~% n1 o. ?* A3 Y" s6 i4 o3 E% N
}
+ q! x4 O: x. X# p" TPORTD.4=0; //使MAX485处 于接收状态/ g) p6 R7 b: L ^
}
: ^1 x. U# Y" \2 k3 Z! G' Z- w% T; m. v C
---------------------------------------------------------------------------------. ~2 _. |# P" t0 @( Z5 N! Q1 w
crc8校验程序
* H% f! l7 w( s7 E---------------------------------------------------------------------------------4 Y# d' A: ]: n! q9 C
unsigned char crc8(unsigned char *ptr, unsigned char len)
6 B8 Z8 r$ t# z{
: N o+ F1 ^% D! f8 j- D# kunsigned char i;
4 _& L @& ?1 J+ a5 |unsigned char crc=0;
$ U3 A3 |' F' r; Q9 ]while(len--!=0) Z6 h4 n; v* w* \, |7 d( X1 ?
{6 @. K, S& n i5 r ]
for(i=1; i!=0; i*=2)% m6 k- J { t' v/ ?/ l; ^
{6 j! i1 ]! a) U# Q: B2 ~
if((crc&1)!=0) {crc/=2; crc^=0x8C;}( l2 s3 Z" }7 y, }: v- a
else crc/=2;
( [$ |7 A8 Y' y0 G& p3 O5 H4 b7 aif((*ptr&i)!=0) crc^=0x8C;- G' O; _8 h0 ~7 x
}% P) W+ v5 Y6 I* y* S
ptr++;
2 f4 k u" j6 @9 `4 ]' t& E5 ^}
7 u' @4 ^2 L1 M7 L! l( b3 f% V0 oreturn(crc);) Y/ F* p6 H: `" W; N& d
}
! @1 F h( Q# N8 q- D |