⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 rs485.c

📁 基于80C196KC微处理器的高速串行通讯、单片机将FIFO中的数据读取出来后
💻 C
字号:
#ifndef __485_C__ 
#define __485_C__ 

#include <AT89X51.H> 
#include <string.h> 

#define uchar unsigned char 
#define uint unsigned int 
/* 通信命令 */ 
#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(&amt;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

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -