[前语]RS232 规范是诞生于 RS485 之前的,可是 RS232 有几处缺乏的当地:接口的信号电平值较高,抵达十几 V,运用不当简略损坏接口芯片,电平规范也与TTL 电平不兼容。传输速率有限制,不能够过高,一般到一两百千比特每秒(Kb/s)就到极限了。接口运用信号线和 GND 与其它设备构成共地形式的通讯,这种共地形式传输简略发生搅扰,而且抗搅扰功用也比较弱。传输距离有限,最多只能通讯几十米。通讯的时分只能两点之间进行通讯,不能够完结多机联网通讯。针对 RS232 接口的缺乏,就不断出现了一些新的接口规范,RS485 便是其间之一。 RS232 规范是诞生于 RS485 之前的,可是 RS232 有几处缺乏的当地:
接口的信号电平值较高,抵达十几 V,运用不当简略损坏接口芯片,电平规范也与TTL 电平不兼容。
传输速率有限制,不能够过高,一般到一两百千比特每秒(Kb/s)就到极限了。
接口运用信号线和 GND 与其它设备构成共地形式的通讯,这种共地形式传输简略发生搅扰,而且抗搅扰功用也比较弱。
传输距离有限,最多只能通讯几十米。
通讯的时分只能两点之间进行通讯,不能够完结多机联网通讯。
针对 RS232 接口的缺乏,就不断出现了一些新的接口规范,RS485 便是其间之一,它具有以下的特色:
选用差分信号。咱们在讲 A/D 的时分,讲过差分信号输入的概念,一同也介绍了差分输入的优点,最大的优势是能够按捺共模搅扰。特别当工业现场环境比较杂乱,搅扰比较多时,选用差分办法能够有用的进步通讯可靠性。RS485 选用两根通讯线,通常用 A 和 B 或许 D+和 D-来表明。逻辑“1”以两线之间的电压差为+(0.2~6)V 表明,逻辑“0”以两线间的电压差为-(0.2~6)V 来表明,是一种典型的差分通讯。
RS485 通讯速率快,最大传输速度能够抵达 10Mb/s 以上。
RS485 内部的物理结构,选用的是平衡驱动器和差分接纳器的组合,抗搅扰才干也大大增加。
传输距离最远能够抵达 1200 米左右,可是它的传输速率和传输距离是成反比的,只要在 100Kb/s 以下的传输速度,才干抵达最大的通讯距离,假如需求传输更远距离能够运用中继。
能够在总线上进行联网完结多机通讯,总线上答应挂多个收发器,从现有的 RS485芯片来看,有能够挂 32、64、128、256 等不同个设备的驱动器。
RS485 的接口十分简略,与 RS232 所运用的 MAX232 是相似的,只需求一个 RS485转化器,就能够直接与单片机的 UART 串口连接起来,而且运用彻底相同的异步串行通讯协议。可是由于 RS485 是差分通讯,因而接纳数据和发送数据是不能一同进行的,也便是说它是一种半双工通讯。那咱们怎么判别什么时分发送,什么时分接纳呢?
RS485 转化芯片许多,这节课咱们以典型的 MAX485 为例解说 RS485 通讯,如图 18-1所示。
图 18-1 MAX485 硬件接口
MAX485 是美信(Maxim)推出的一款常用 RS485 转化器。其间 5 脚和 8 脚是电源引脚;6脚和 7 脚便是 RS485 通讯中的 A 和 B 两个引脚;1 脚和 4 脚别离接到单片机的 RXD 和 TXD引脚上,直接运用单片机 UART 进行数据接纳和发送;2 脚和 3 脚是方向引脚,其间 2 脚是低电平使能接纳器,3 脚是高电平使能输出驱动器,咱们把这两个引脚连到一同,往常不发送数据的时分,坚持这两个引脚是低电平,让 MAX485 处于接纳状况,当需求发送数据的时分,把这个引脚拉高,发送数据,发送结束后再拉低这个引脚就能够了。为了进步 RS485 的抗搅扰才干,需求在接近 MAX485 的 A 和 B 引脚之间并接一个电阻,这个电阻阻值从 100欧到 1K 都是能够。
在这里咱们还要介绍一下怎么运用 KST-51 单片机开发板进行外围扩展试验。咱们的开发板只能把根本的功用给同学们做出来供给试验操练,可是同学们学习的脚步不应该停留在这个试验板上。假如想进行更多的试验,就能够经过单片机开发板的扩展接口进行扩展试验。咱们能够看到蓝绿色的单片机座周围有 32 个插针,这 32 个插针便是把单片机的 32 个 IO 引脚全部都引出来了。在原理图上体现出来的便是 J4、J5、J6、J7 这 4 个器材,如图 18-2 所示。
图 18-2 单片机扩展接口
这 32 个 IO 口中并不是一切的都能够用来对外扩展,其间既作为数据输出,又能够作为数据输入的引脚是不能够用的,比方 P3.2、P3.4、P3.6 引脚,这三个引脚是不可用的。比方P3.2 这个引脚,假如咱们用来扩展,发送的信号假如和 DS18B20 的时序符合,会导致 DS18B20拉低引脚,影响通讯。除这 3 个 IO 口以外的其它 29 个,都能够运用杜邦线接上插针,扩展出来运用。当然了,假如把当时的 IO 口应用于扩展功用了,板子上的相应功用就完结不了了,也便是说需求扩展功用和板载功用之间二选一。
在进行 RS485 试验中,咱们通讯用的引脚有必要是 P3.0 和 P3.1,此外还有一个方向操控引脚,咱们运用杜邦线将其连接到 P1.7 上去。RS485 的别的一端,咱们能够运用一个 USB转 RS485 模块,用双绞线把开发板和模块上的 A 和 B 别离对应连起来,USB 那头刺进电脑,然后就能够进行通讯了。
学习了第 13 章有用的串口通讯办法和程序后,做这种串口通讯的办法就很简略了,根本是共同的。咱们运用有用串口通讯例程的思路,做了一个简略的程序,经过串口调试帮手下发恣意个字符,单片机接纳到后在结尾增加“回车+换行”符后再送回,在调试帮手上从头显示出来,先把程序贴出来。
程序中需求留意的一点是:由于往常都是将 MAX485 设置为接纳状况,只要在发送数据的时分才将 MAX485 改为发送状况,所以在 UartWrite()函数最初将 MAX485 方向引脚拉高,函数退出前再拉低。可是这里有一个细节,便是单片机的发送和接纳中止发生的时刻都是在中止位的一半上,也便是说每逢中止位传送了一半的时分,RI 或 TI 就现已置位而且立刻进入中止(假如中止使能的话)函数了,接纳的时分天然不会存在问题,但发送的时分就不相同了:当紧接着向 SBUF 写入一个字节数据时,UART 硬件会在完结上一个中止位的发送后,再开端新字节的发送,但假如此刻不是继续发送下一个字节,而是现已发送结束了,要中止发送并将 MAX485 方向引脚拉低以使 MAX485 从头处于接纳状况时就有问题了,由于这时分最终的这个中止位实践只发送了一半,还没有彻底完结,所以就有了 UartWrite()函数内DelayX10us(5)这个操作,这是人为的增加了 50us 的延时,这 50us 的时刻正好让剩余的一半中止位完结,那么这个时刻天然便是由通讯波特率决议的了,为波特率周期的一半。
/****************************RS485.c 文件程序源代码*****************************/
纯文本仿制
#include
#include
sbit RS485_DIR = P1^7; //RS485 方向挑选引脚
bit flagFrame = 0; //帧接纳完结标志,即接纳到一帧新数据
bit flagTxd = 0; //单字节发送完结标志,用来代替 TXD 中止标志位
unsigned char cntRxd = 0; //接纳字节计数器
unsigned char pdata bufRxd[64]; //接纳字节缓冲区
extern void UartAcTIon(unsigned char *buf, unsigned char len);
/* 串口装备函数,baud-通讯波特率 */
void ConfigUART(unsigned int baud){
RS485_DIR = 0; //RS485 设置为接纳方向
SCON = 0x50; //装备串口为形式 1
TMOD = 0x0F; //清零 T1 的操控位
TMOD |= 0x20; //装备 T1 为形式 2
TH1 = 256 – (11059200/12/32)/baud; //核算 T1 重载值
TL1 = TH1; //初值等于重载值
ET1 = 0; //制止 T1 中止
ES = 1; //使能串口中止
TR1 = 1; //发动 T1
}
/* 软件延时函数,延时时刻(t*10)us */
void DelayX10us(unsigned char t){
do {
_nop_();
_nop_();
_nop_();
_nop_();
_nop_();
_nop_();
_nop_();
_nop_();
} while (–t);
}
/* 串口数据写入,即串口发送函数,buf-待发送数据的指针,len-指定的发送长度 */
void UartWrite(unsigned char *buf, unsigned char len){
RS485_DIR = 1; //RS485 设置为发送
while (len–){ //循环发送一切字节
flagTxd = 0; //清零发送标志
SBUF = *buf++; //发送一个字节数据
while (!flagTxd); //等候该字节发送完结
}
DelayX10us(5); //等候最终的中止位完结,延时时刻由波特率决议
RS485_DIR = 0; //RS485 设置为接纳
}
/* 串口数据读取函数,buf-接纳指针,len-指定的读取长度,回来值-实践读到的长度 */
unsigned char UartRead(unsigned char *buf, unsigned char len){
unsigned char i;
//指定读取长度大于实践接纳到的数据长度时,
//读取长度设置为实践接纳到的数据长度
if (len 》 cntRxd){
len = cntRxd;
}
for (i=0; i
*buf++ = bufRxd[i];
}
cntRxd = 0; //接纳计数器清零
return len; //回来实践读取长度
}
/* 串口接纳监控,由闲暇时刻断定帧结束,需在守时中止中调用,ms-守时刻隔 */
void UartRxMonitor(unsigned char ms){
staTIc unsigned char cntbkp = 0;
staTIc unsigned char idletmr = 0;
if (cntRxd 》 0){ //接纳计数器大于零时,监控总线闲暇时刻
if (cntbkp != cntRxd){ //接纳计数器改动,即刚接纳到数据时,清零闲暇计时
cntbkp = cntRxd;
idletmr = 0;
}else{ //接纳计数器未改动,即总线空
}else{ //接纳计数器未改动,即总线闲暇时,累积闲暇时刻
if (idletmr 《 30){ //闲暇计时小于 30ms 时,继续累加
idletmr += ms;
if (idletmr 》= 30){ //闲暇时刻抵达 30ms 时,即断定为一帧接纳结束
flagFrame = 1; //设置帧接纳完结标志
}
}
}
}else{
cntbkp = 0;
}
}
/* 串口驱动函数,监测数据帧的接纳,调度功用函数,需在主循环中调用 */
void UartDriver(){
unsigned char len;
unsigned char pdata buf[40];
if (flagFrame){ //有指令抵达时,读取处理该指令
flagFrame = 0;
len = UartRead(buf, sizeof(buf)-2); //将接纳到的指令读取到缓冲区中
UartAction(buf, len); //传递数据帧,调用动作履行函数
}
}
/* 串口中止服务函数 */
void InterruptUART() interrupt 4{
if (RI){ //接纳到新字节
RI = 0; //清零接纳中止标志位
//接纳缓冲区没有用完时,保存接纳字节,并递加计数器
if (cntRxd 《 sizeof(bufRxd)){
bufRxd[cntRxd++] = SBUF;
}
}
if (TI){ //字节发送结束
TI = 0; //清零发送中止标志位
flagTxd = 1; //设置字节发送完结标志
}
}
/*****************************main.c 文件程序源代码******************************/
#include
unsigned char T0RH = 0; //T0 重载值的高字节
unsigned char T0RL = 0; //T0 重载值的低字节
void ConfigTimer0(unsigned int ms);
extern void UartDriver();
extern void ConfigUART(unsigned int baud);
extern void UartRxMonitor(unsigned char ms);
extern void UartWrite(unsigned char *buf, unsigned char len);
void main(){
EA = 1; //开总中止
ConfigTimer0(1); //装备 T0 守时 1ms
ConfigUART(9600); //装备波特率为 9600
while (1){
UartDriver(); //调用串口驱动
}
}
/* 串口动作函数,依据接纳到的指令帧履行呼应的动作
buf-接纳到的指令帧指针,len-指令帧长度 */
void UartAction(unsigned char *buf, unsigned char len){
//在接纳到的数据帧后增加换车换行符后发回
buf[len++] = ‘\r’;
buf[len++] = ‘\n’;
UartWrite(buf, len);
}
/* 装备并发动 T0,ms-T0 守时时刻 */
void ConfigTimer0(unsigned int ms){
unsigned long tmp; //暂时变量
tmp = 11059200 / 12; //守时器计数频率
tmp = (tmp * ms) / 1000; //核算所需的计数值
tmp = 65536 – tmp; //核算守时器重载值
tmp = tmp + 33; //补偿中止呼应延时形成的差错
T0RH = (unsigned char)(tmp》》8); //守时器重载值拆分为凹凸字节
T0RL = (unsigned char)tmp;
TMOD = 0xF0; //清零 T0 的操控位
TMOD |= 0x01; //装备 T0 为形式 1
TH0 = T0RH; //加载 T0 重载值
TL0 = T0RL;
ET0 = 1; //使能 T0 中止
TR0 = 1; //发动 T0
}
/* T0 中止服务函数,履行串口接纳监控 */
void InterruptTimer0() interrupt 1{
TH0 = T0RH; //从头加载重载值
TL0 = T0RL;
UartRxMonitor(1); //串口接纳监控
}
现在看这种串口程序,是不是感觉很简略了呢?串口通讯程序咱们反反复复的运用,加上跟着学习的模块越来越多,实践的越来越多,原先感觉很杂乱的东西,现在就会感到简略了。从设备管理器里能够检查一切的 COM 标语,咱们下载程序用的是 COM4,而 USB 转RS485 虚拟的是 COM5,通讯的时分咱们用的是 COM5 口,如图 18-3 所示。