标题:
单片机RS485程序
[打印本页]
作者:
饮酒作乐
时间:
2020-4-23 10:25
标题:
单片机RS485程序
#ifndef __485_C__
#define __485_C__
#include <reg51.h>
#include <string.h>
#define unsigned char uchar
#define unsigned int uint
/*
通信命令
*/
#define __ACTIVE_ 0x01 //
主机询问从机是否存在
#define __GETDATA_ 0x02 //
主机发送读设备请求
#define __OK_ 0x03 //
从机应答
#define __STATUS_ 0x04 //
从机发送设备状态信息
#define __MAXSIZE 0x08 //
缓冲区长度
#define __ERRLEN 12 //
任何通信帧长度超过
12
则表示出错
uchar dbuf[__MAXSIZE]; //
该缓冲区用于保存设备状态信息
uchar dev; //
该字节用于保存本机设备号
sbit M_DE = P1^0; //
驱动器使能,
1
有效
sbit M_RE = P1^1; //
接收器使能,
0
有效
void get_status(); //
调用该函数获得设备状态信息,函数代码未给出
void send_data(uchar type, uchar len, uchar *buf); //
发送数据帧
bit recv_cmd(uchar *type); //
接收主机命令,主机请求仅包含命令信息
void send_byte(uchar da); //
该函数发送一帧数据中的一个字节,
由
send_data()
函
数调用
void main()
{
uchar type;
uchar len;
/*
系统初始化
*/
P1 = 0xff; //
读取本机设备号
dev = (P1>>2);
TMOD = 0x20; //
定时器
T1
使用工作方式
2
TH1 = 250; //
设置初值
TL1 = 250;
TR1 = 1; //
开始计时
PCON = 0x80; // SMOD = 1
SCON = 0x50; //
工作方式
1
,波特率
9600bps
,允许接收
ES = 0; //
关闭串口中断
IT0 = 0; //
外部中断
0
使用电平触发模式
EX0 = 1; //
开启外部中断
0
EA = 1; //
开启中断
/*
主程序流程
*/
while(1) //
主循环
{
if(recv_cmd(&type) == 0) //
发生帧错误或帧地址与本机地址不符,
丢弃当前帧后
返回
continue;
switch(type)
{
case __ACTIVE_: //
主机询问从机是否存在
send_data(__OK_, 0, dbuf); //
发送应答信息,这里
buf
的内容并未用到
break;
case __GETDATA_:
len = strlen(dbuf);
send_data(__STATUS_, len, dbuf); //
发送设备状态信息
break;
default:
break; //
命令类型错误,丢弃当前帧后返回
}
}
}
void READSTATUS() interrupt 0 using 1 //
产生外部中断
0
时表示设备状态发生改
变,该函数使用寄存器组
1
{
get_status(); //
获得设备状态信息,并将其存入
dbuf
指向的存储区,数据最后一字
节置
0
表示数据结束
}
/*
该函数接收一帧数据并进行检测,无论该帧是否错误,函数均会返回
*
函数参数
type
保存接收到的命令字
*
当接收到数据帧错误或其地址位不为
0
时(非主机发送帧),函数返回
0
,反之返回
1
*/
bit recv_cmd(uchar *type)
{
bit db = 0; //
当接收到的上一个字节为
0xdb
时,该位置位
bit c0 = 0; //
当接收到的上一个字节为
0xc0
时,该位置位
uchar data_buf[__ERRLEN]; //
保存接收到的帧
uchar tmp;
uchar ecc = 0;
uchar i;
M_DE = 0; //
置发送禁止,接收允许
M_RE = 0;
/*
接收一帧数据
*/
i = 0;
while(!c0) //
循环直至帧接收完毕
{
RI = 0;
while(!RI);
tmp = SBUF;
RI = 0;
if(db == 1) //
接收到的上一个字节为
0xdb
{
switch(tmp)
{
case 0xdd:
data_buf[i] = 0xdb; // 0xdbdd
表示
0xdb
ecc = ecc^0xdb;
db = 0;
break;
case 0xdc
data_buf[i] = 0xc0; // 0xdbdc
表示
0xc0
ecc = ecc^0xc0;
db = 0;
break;
default
return 0; //
帧错误,返回
}
i++;
}
switch(tmp) //
正常情况
{
case 0xc0: //
帧结束
c0 = 1;
break;
case 0xdb: //
检测到转义字符
db = 1;
break;
default: //
普通数据
data_buf[i] = tmp; //
保存数据
ecc = ecc^tmp; //
计算校验字节
i++;
}
if(i == __ERRLEN) //
帧超长,错误,返回
return 0;
}
/*
判断帧是否错误
*/
if(i<4) //
帧过短,错误,返回
return 0;
if(ecc != 0) //
校验错误,返回
return 0;
if(data_buf[0] != dev) //
非访问本机命令,错误,返回
return 0;
*type = data_buf[1]; //
获得命令字
return 1; //
函数成功返回
}
/*
该函数发送一帧数据帧,参数
type
为命令字、
len
为数据长度、
buf
为要发送的数据内
容
*/
void send_data(uchar type, uchar len, uchar *buf)
{
uchar i;
uchar ecc = 0; //
该字节用于保存校验字节
M_DE = 1; //
置发送允许,接收禁止
M_RE = 1;
send_byte(dev); //
发送本机地址
ecc = dev;
send_byte(type); //
发送命令字
ecc = ecc^type;
send_byte(len); //
发送长度
ecc = ecc^len;
for(i=0; i<len; i++) //
发送数据
{
send_byte(*buf);
ecc = ecc^(*buf);
buf++;
}
send_byte(ecc); //
发送校验字节
TI = 0; //
发送帧结束标志
SBUF = 0xc0;
while(!TI);
TI = 0;
}
/*
该函数发送一个数据字节,若该字节为
0xdb
,则发送
0xdbdd
,若该字节为
0xc0
则,发
送
0xdbdc */
void send_byte(uchar da)
{
switch(da)
{
case 0xdb: //
字节为
0xdb
,发送
0xdbdd
TI = 0;
SBUF = 0xdb;
while(!TI);
TI = 0;
SBUF = 0xdd;
while(!TI)
TI = 0;
break;
case 0xc0: //
字节为
0xc0
,发送
0xdbdc
TI = 0;
SBUF = 0xdb;
while(!TI);
TI = 0;
SBUF = 0xdc;
while(!TI)
TI = 0;
break;
default: //
普通数据则直接发送
TI = 0;
SBUF = da;
while(!TI);
TI = 0;
}
}
#endif
复制代码
欢迎光临 (http://www.51hei.com/bbs/)
Powered by Discuz! X3.1