技术文档

多单片机通信 多个单片机的通信方式

小编 2024-11-28 技术文档 23 0

多个单片机的通信方式

一技在手天下我有!如您觉得本文对您有用烦请收藏转发加关注哟~笔芯

下面简述常用单片机之间的通信方式

1. 采用硬件UART进行异步串行通信。 这是一种占用口线少,有效、可靠的通信方式;但遗憾的是许多小型单片机没有硬件UART,有些也只有1个UART,如果系统还要与上位机通信的话,硬件资源是不够的。这种方法一般用于单片机有硬件UART且不需与外界进行串行通信或采用双UART单片机的场合。2. 采用片内SPI接口或I2C总线模块串行通信形式。 SPI/I2C接口具有硬件简单、软件编程容易等特点,但目前大多数单片机不具备硬件SPI/I2C模块。

3. 利用软件模拟SPI/I2C模式通信 ,这种方式很难模拟从机模式,通信双方对每一位要做出响应,通信速率与软件资源的开销会形成一个很大的矛盾,处理不好会导致系统整体性能急剧下降。这种方法只能用于通信量极少的场合。

4. 口对口并行通信,利用单片机的口线直接相连,加上1~2条握手信号线。 这种方式的特点是通信速度快,1次可以传输4位或8位,甚至更多,但需要占用大量的口线,而且数据传递是准同步的。在一个单片机向另一个单片机传送1个字节以后,必须等到另一个单片机的接收响应信号后才能传送下一个数据。一般用于一些硬件口线比较富裕的场合。

5. 利用双口RAM作为缓冲器通信。 这种方式的最大特点就是通信速度快,两边都可以直接用读写存储器的指令直接操作;但这种方式需要大量的口线,而且双口RAM的价格很高,一般只用于一些对速度有特殊要求的场合。

从上面几种方案来看,各种方法对硬件都有很大的要求与限制,特别是难以在功能简单的单片机上实现,因此寻求一种简单、有效的,能在各种单片机之间通信的方法具有重要的意义。③、④方案中,双方单片机要传递的每一位或每一个字节做出响应,通信数据量较大时会耗费大量的软件资源,这在一些实时性要求高的地方是不允许的。

针对这一问题,假设在单片机之间增加1个数据缓冲器,大批数据先写入缓冲区,然后再让对方去取,各个单片机对数据缓冲器都是主控模式,这样必然会大大提高通信效率。谈到数据缓冲,我们马上会想到并行RAM,但是并行RAM需要占用大量的口线(数据线+地址线+读写线+片选线+握手线),一般在16条以上。这是一个让人望而生畏的数字,而且会大大增加PCB面积并给布线带来一定的困难,极少有人采用这种方式。串行接口的RAM在市场上很少见,不但难以买到而且价格很高。移位寄存器也可以做数据缓冲器,但目前容量最大的也只128位,因为是“先进先出”结构,所以不管传递数据多少,接收方必须移完整个寄存器,灵活性差而且大容量的移位寄存器也是少见难买的。一种被称为“铁电存储器”芯片的出现,给我们带来了解决方法。

利用铁电存储器作为数据缓冲器的通信方式 铁电存储器是美国Ramtran公司推出的一种非易失性存储器件,简称FRAM。与普通EEPROM、Flash-ROM相比,它具有不需写入时间、读写次数无限,没有分布结构可以连续写放的优点,因此具有RAM与EEPROM的双得特性,而且价格相对较低。

现在大多数的单片机系统配备串行EEPROM(如24CXX、93CXX等)用来存储参数。如果用1片FRAM代替原有EEPROM,使它既能存储参数,又能作串行数据通信的缓冲器。2个(或多个)单片机与1片FRAM接成多主-从的I2C总线方式,增加几条握手线,即可得到简单高效的通信硬件电路。在软件方面,只要解决好I2C多主-从的控制冲突与通信协议问题,即可实现简单、高效、可靠的通信了。

实例(双单片机结构,多功能低功耗系统)(1)硬件 W78LE52与EMC78P458组成一个电池供电、可远程通信的工业流量计。78P458采用32.768kHz晶振,工作电流低,不间断工作,实时采集传感器的脉冲及温度、压力等一些模拟量;W78LE52采11.0592MHz晶振,由于它的工作电流较大,采用间断工作,负责流量的非线性校正、参数输入、液晶显示、与上位机通信等功能,它的UART用于远程通信。2个单片机共用1片I2C接口的FRAM(FM24CL16)组成二主一从的I2C总线控制方式,W78LE52的P3.5、P3.2分别与78P458的P51、P50连接作握手信号线A与B。我们把握手线A(简称A线)定义为总线控制、指示线,主要用于获取总线控制权与判别总线是否“忙”;握手线B(简称B线)定义为通知线,主要用于通知对方取走数据。

(2)I2C总线仲裁 由于我们采用的是二主一从的I2C总线方式,因此防止2个主机同时去操作从机(防冲突)是一个非常重要的问题。带有硬件I2C模块的器件一般是这样的,器件内部有1个总线仲裁器与总线超时定时器:当总线超时定时器超时后指示总线空闲,这时单片机可以发出获取总线命令,总线仲裁器通过一系列操作后确认获取总线成功或失败;超时定时器清零,以后的每一个SCL状态变化对总线所有主机的超时定时器进行清零,以防止它溢出,指示总线正处于“忙”状态,直到一个主机对总线控制结束不再产生SCL脉冲;超时定时器溢出,总线重新回到“空闲”状态。但是目前大多数单片机没有配备硬件I2C模块,而且当2个主机的工作频率相差较大时,超时定时器定时值只能设为较大的值,这样也会影响总线的使用效率。

下面介绍一种用软件模拟I2C总线仲裁的方式(I2C读写操作程序的软件模拟十分多见,这里不再多述):用1条握手线A,当A线高电平时,指示总线空闲;当其中一个主机要获取总线控制权时,先查询总线是否空闲,“忙”则退出,空闲则向A线发送一个测试序列(如:1000101011001011),在每次发送位“1”后读取的A线状态。如果读取状态为“0”,马上退出,说明有其它器件已经抢先获取总线;如果一个序列读取的A线状态都正确,则说明已成功获得总线控制权,这时要拉低A线以指示总线“忙”,直到读写高A线,使总线回到“空闲”状态。不同的主机采用不同的测试序列,或产生随机测试序列,测试序列长度可以选得长一些,这样可以增加仲裁的可靠性。

(3)通信协议 一个可靠通信体系,除了好的硬件电路外,通信协议也至关重要。在单片机系统RAM资源与执行速度都非常有限的情况下,一个简捷有效的协议是非常重要的。下面具体介绍一种比较适用于单片机通信的协议,数据以包的形式传送。数据包结构:

①包头——指示数据包的开始,有利于包完整性检测,有时可省略;

②地址——数据包要传送的目标地址,若只有双机通信或硬件区分地址可以省略;

③包长度——指示整个数据包的长度;

④命令——指示本数据包的作用;

⑤参数——需要传送的数据与参数;

⑥校验——验证数据包的正确性,可以是和校验、异或校验、CRC校验等或者是它们的组合;

⑦包尾——指示数据包的结尾,有利于包完整性检测,有时可省略。

(4)通信流程 首先,要在FRAM里划分好各个区域,各个单片机的参数区、数据接收区等。然后,单片机可以向另一个单片机发送数据包,发送完毕之后通过向握手线B发送1个脉冲通知对方取走数据;接收方读取数据并进行处理后,向FRAM内发送方的数据接收区写入回传数据或通信失败标志,再向握手线B发送1个脉冲回应发送方。

如果需要单片机2发送的话,只需交换一下操作过程即可。

总结 通过实践可知,以上方法是可行的。与其它方法相比具有发下优点:

①简单。占用单片机口线少(SCL、SDA、握手线A、握手线B)。

②通用。软件模拟I2C主机方式,可以在任何种类的单片机之间通信。

③高效。由于采用数据缓冲,可以在不同时钟频率、不同速度的单片机之间通信;读写数据时,可以I2C总线的最高速度进行,可以实现1次传送大量数据;在一个单片机向FRAM传送数据时,另一个单片机无须一一作出响应或等待,可以进行其它程序操作,提高软件工作效率。

④灵活。通信硬件接口对于各个单片机是对等的,通过软件配置,每个单片机既可以根据需要主动发送通信,也可以只响应其它单片机的呼叫。

⑤容易扩展。通过增加地址识别线,修改通信协议,即可做到多机通信。

以下是需要注意的地方:

①为了提高通信效率,握手线B最好使用中断端口,负脉冲宽度一定要满足速度较低单片机中断信号要求。如果没有中断的话应增加1条口线,用改变端口状态的方法通知对方,等待对方查询,而不是负脉冲。

②向对方发送负脉冲时,应屏蔽自己的中断。

③由于参数与通信缓冲区同时设在同一片FRAM内,要避免对参数部分的误操作。一个较好的解决办法是把参数存放在地址的后半部分(A2=1),在进行通信操作时,把FRAM的WP引脚拉高(地址在后半部分的单元写保护),这样可以有效地防止测验时对参数区误操作。

④由于I2C总线在一个时间段内只有1个主机和1个从机,所以当1个单片机正在写通信数据时,另一个单片机是不能对FRAM进行操作的。如果需要实时、频繁地读取FRAM中参数的话,请预先将参数读入RAM单元使用或另外增加专门存放参数的芯片。

51单片机多机通信

一、多机通信原理

在多机通信中,主机必须要能对各个从机进行识别,在51系列单片机中可以通过SCON寄存器的SM2位来实现。当串口以方式2或方式3发送数据时,每一帧信息都是11位,第9位是数据可编程位,通过给TB8置1或置0来区别地址帧和数据帧,当该位为1时,发送地址帧;该位为0时,发送数据帧。

在多机通信过程中,主机先发送某一从机的地址,等待从机的应答,所有的从机接收到地址帧后与本机地址进行比较,若相同,则将SM2置0准备接收数据;若不同,则丢弃当前数据,SM2位不变。

二、多机通信电路图

此处,U1作为主机,U2为从机1,U3为从机2。

三、C语言程序

(1)主机程序

#include<reg51.h>

#include<string.h>

#define _SUCC_ 0x0f//数据传送成功

#define _ERR_ 0xf0//数据传送失败

unsigned charTable[9]={0x31,0x32,0x33,0x34,0x35,0x36,0x37,0x38,0x39};

unsigned char Buff[20]; //数据缓冲区

unsigned char temp=0xff;

sbit KEY1=P1^6;

sbit KEY2=P1^7;

//unsigned char addr;

//延时1ms函数

void delay_1ms(unsigned int t)

{

unsigned int x,y;

for(x=t;x>0;x--)

for(y=110;y>0;y--);

}

//缓冲区初始化

void Buff_init()

{

unsigned chari; //将Table里的数据放到缓冲区里

for(i=0;i<9;i++)

{

Buff[i]= Table[i];

delay_1ms(100);

}

}

//串口初始化函数

void serial_init()

{

TMOD=0x20; //定时器1工作于方式2

TH1=0xfd;

TL1=0xfd; //波特率为9600

PCON=0;

SCON=0xd0; //串口工作于方式3

TR1=1; //开启定时器

TI=0;

RI=0;

}

//发送数据函数

void SEND_data(unsigned char *Buff)

{

unsigned char i;

unsigned char lenth;

unsigned char check;

lenth=strlen(Buff); //计算数据长度

check=lenth;

TI=0; //发送数据长度

TB8=0; //发送数据帧

SBUF=lenth;

while(!TI);

TI=0;

for(i=0;i<lenth;i++) //发送数据

{

check=check^Buff[i];

TB8=0;

SBUF=Buff[i];

while(!TI);

TI=0;

}

TB8=0; //发送校验字节

SBUF=check;

while(!TI);

TI=0;

}

//向指定从机地址发送数据

void ADDR_data(unsigned addr)

{

while(temp!=addr) //主机等待从机返回其地址作为应答信号

{

TI=0; //发送从机地址

TB8=1; //发送地址帧

SBUF=addr;

while(!TI);

TI=0;

RI=0;

while(!RI);

temp=SBUF;

RI=0;

}

temp=_ERR_; //主机等待从机数据接收成功信号

while(temp!=_SUCC_)

{

SEND_data(Buff);

RI=0;

while(!RI);

temp=SBUF;

RI=0;

}

}

void main()

{

Buff_init();

serial_init();

while(1)

{

if(KEY1==0)

{

delay_1ms(5);

if(KEY1==0)

{

while(!KEY1);

ADDR_data(0x01);

}

}

if(KEY2==0)

{

delay_1ms(5);

if(KEY2==0)

{

while(!KEY2);

ADDR_data(0x02);

}

}

}

}

(2)从机1程序

#include<reg51.h>

#include<string.h>

#defineaddr 0x01//从机1的地址

#define _SUCC_ 0x0f//数据传送成功

#define _ERR_ 0xf0//数据传送失败

unsigned char aa=0xff;//主机与从机之间通信标志

unsigned char Buff[20];//数据缓冲区

//串口初始化函数

void serial_init()

{

TMOD=0x20; //定时器1工作于方式2

TH1=0xfd;

TL1=0xfd; //波特率为9600

PCON=0;

SCON=0xd0; //串口工作于方式3

TR1=1; //开启定时器

TI=0;

RI=0;

}

//接收数据函数

unsigned char RECE_data(unsigned char *Buff)

{

unsigned char i,temp;

unsigned char lenth;

unsigned char check;

RI=0; //接收数据长度

while(!RI);

if(RB8==1) //若接收到地址帧,则返回0xfe

return 0xfe;

lenth=SBUF;

RI=0;

check=lenth;

for(i=0;i<lenth;i++) //接收数据

{

while(!RI);

if(RB8==1) //若接收到地址帧,则返回0xfe

return0xfe;

Buff[i]=SBUF;

check=check^(Buff[i]);

RI=0;

}

while(!RI); //接收校验字节

if(RB8==1) //若接收到地址帧,则返回0xfe

return 0xfe;

temp=SBUF;

RI=0;

check=temp^check; //将从主机接收到的校验码与自己计算的校验码比对

if(check!=0) //校验码不一致,表明数据接收错误,向主机发送错误信号,函数返回0xff

{

TI=0;

TB8=0;

SBUF=_ERR_;

while(!TI);

TI=0;

return 0xff;

}

TI=0; //校验码一致,表明数据接收正确,向主机发送成功信号,函数返回0x00

TB8=0;

SBUF=_SUCC_;

while(!TI);

TI=0;

return 0;

}

void main()

{

serial_init();

while(1)

{

SM2=1; //接收地址帧

while(aa!=addr) //从机等待主机请求自己的地址

{

RI=0;

while(!RI);

aa=SBUF;

RI=0;

}

TI=0; //一旦被请求,从机返回自己的地址作为应答,等待接收数据

TB8=0;

SBUF=addr;

while(!TI);

TI=0;

SM2=0; //接收数据帧

aa=0xff; //从机接收数据,并将数据保存到数据缓冲区

while(aa==0xff)

{

aa=RECE_data(Buff);

}

if(aa==0xfe)

continue;

P1=Buff[1]; //查看接收到的数据

}

}

(3)从机2程序

#include<reg51.h>

#include<string.h>

#defineaddr 0x02//从机2的地址

#define _SUCC_ 0x0f//数据传送成功

#define _ERR_ 0xf0//数据传送失败

unsigned char aa=0xff;//主机与从机之间通信标志

unsigned char Buff[20];//数据缓冲区

//串口初始化函数

void serial_init()

{

TMOD=0x20; //定时器1工作于方式2

TH1=0xfd;

TL1=0xfd; //波特率为9600

PCON=0;

SCON=0xd0; //串口工作于方式3

TR1=1; //开启定时器

TI=0;

RI=0;

}

//接收数据函数

unsigned char RECE_data(unsigned char *Buff)

{

unsigned char i,temp;

unsigned char lenth;

unsigned char check;

RI=0; //接收数据长度

while(!RI);

if(RB8==1) //若接收到地址帧,则返回0xfe

return 0xfe;

lenth=SBUF;

RI=0;

check=lenth;

for(i=0;i<lenth;i++) //接收数据

{

while(!RI);

if(RB8==1) //若接收到地址帧,则返回0xfe

return0xfe;

Buff[i]=SBUF;

check=check^(Buff[i]);

RI=0;

}

while(!RI); //接收校验字节

if(RB8==1) //若接收到地址帧,则返回0xfe

return 0xfe;

temp=SBUF;

RI=0;

check=temp^check; //将从主机接收到的校验码与自己计算的校验码比对

if(check!=0) //校验码不一致,表明数据接收错误,向主机发送错误信号,函数返回0xff

{

TI=0;

TB8=0;

SBUF=_ERR_;

while(!TI);

TI=0;

return 0xff;

}

TI=0; //校验码一致,表明数据接收正确,向主机发送成功信号,函数返回0x00

TB8=0;

SBUF=_SUCC_;

while(!TI);

TI=0;

return 0;

}

void main()

{

serial_init();

while(1)

{

SM2=1; //接收地址帧

while(aa!=addr) //从机等待主机请求自己的地址

{

RI=0;

while(!RI);

aa=SBUF;

RI=0;

}

TI=0; //一旦被请求,从机返回自己地址作为应答,等待接收数据

TB8=0;

SBUF=addr;

while(!TI);

TI=0;

SM2=0; //接收数据帧

aa=0xff; //从机接收数据,并将数据保存到数据缓冲区

while(aa==0xff)

{

aa=RECE_data(Buff);

}

if(aa==0xfe)

continue;

P1=Buff[2]; //查看接收到的数据

}

}

相关问答

两个 单片机 做一个系统, 单片机 之间用什么方式 通信 最好啊!!-ZO...

3、如果单片机之间通讯距离远的话,可以采用RS485通讯方式;如果单片机之间通讯距离近的话,两个单片机串口可以直接通讯。4、如果对通讯速度要求不高的话,也可以...

单片机 串口多机 通信 电路如何接?

首先需要明白两个概念,就是dte和dce。dte是指数据终端设备,典型的dte就是计算机和单片机。dce是指数据通信设备,典型的dce就是modem。rs232串口标准中的rxd和t...

PLC和 单片机 通信 怎么实现?

可以采用两种方式:一,采用串口与单片机通迅,采用无协议方式.但必须注意PLC通信方式,做必要的转换.如RS422,RS485等,可以采用与之对应的芯片与MCU连接.二,采...

如何用C语言实现用无线模块在两个 单片机 通信 ?

这个涉及的就比较多了。通常单片机通过串口和WIFI模块通信,难点是网络通信部分。首先要确定服务端和客户端。如果电脑做服务端就要写个程序监听一个端口(也有...

单片机 与多个从机 通信 ,轮询方式?

RS485、I2C和SPI不一样。RS485、I2C通讯协议中包含从机的地址,从机都不主动发送数据,主机发送数据,数据包中包含有从机地址,主机发送的数据,所有从机都接收...

不同 单片机 直接可以互相 通信 吗?例如AT与STC之间 通信 - 赐我...

可以通行分为好几类,你说的是单片机与单片机之间的通行,AT与STC之间可以进行通信,只要你按照通信协议的说明操作就可以了,记住:一定要理解通信协议...

51 单片机 能实现can通讯吗?

51单片机可以实现CAN总线通讯,但目前集成了can控制器的51单片机极为少见。51单片机要想实现can通讯,必须外接can控制器和can收发器。目前还没有国产can控制器...

不同 单片机 直接可以互相 通信 吗?例如AT与STC之间 通信 - aQCs4...

通行分为好几类,你说的是单片机与单片机之间的通行,AT与STC之间可以进行通信,只要你按照通信协议的说明操作就可以了,记住:一定要理解通信协议的具...

怎么实现两个 单片机 之间的串口 通信 ?

两个单片机之间串口通信,如果是用proteus仿真,最简单了,两个单片机的RXD,TXD交叉连接就行了。要是实物最好用RS232连接通信,距离可以达到几十米。再远点距离...

51 单片机 几根线 通信 ?

1.51单片机通信需要4根线。2.这是因为在51单片机通信中,需要使用一个引脚作为时钟信号线(SCL),一个引脚作为数据信号线(SDA),一个引脚作为主机发送信号...1...

猜你喜欢