手把手教你学51单片机之十八 RS485通信与Modbus协议(4)

2020-05-06 11:18

ES = 1; //使能串口中断 TR1 = 1; //启动T1 }

unsigned char UartRead(unsigned char *buf, unsigned char len) //串口数据读取函数,数据接收指针buf,读取数据长度len,返回值为实际读取到的数据长度 {

unsigned char i;

if (len > cntRxd) //读取长度大于接收到的数据长度时, {

len = cntRxd; //读取长度设置为实际接收到的数据长度 }

for (i=0; i

*buf = bufRxd[ i]; buf++; }

cntRxd = 0; //清零接收计数器

return len; //返回实际读取长度 }

void DelayX10us(unsigned char t) //软件延时函数,延时时间(t*10)us { do { _nop_(); _nop_(); _nop_(); _nop_(); _nop_(); _nop_(); _nop_(); _nop_(); } while (--t); }

void UartWrite(unsigned char *buf, unsigned char len) //串口数据写入函数,即串口发送函数,待发送数据指针buf,数据长度len {

RS485_DIR = 1; //RS485设置为发送 while (len--) //发送数据 {

flagOnceTxd = 0; SBUF = *buf; buf++;

while (!flagOnceTxd); }

DelayX10us(5); //等待最后的停止位完成,延时时间由波特率决定 RS485_DIR = 0; //RS485设置为接收 }

void UartDriver() //串口驱动函数,检测接收到的命令并执行相应动作 {

unsigned char i; unsigned char cnt; unsigned char len; unsigned char buf[30]; unsigned char str[4]; unsigned int crc; unsigned char crch, crcl;

if (cmdArrived) //有命令到达时,读取处理该命令 {

cmdArrived = 0;

len = UartRead(buf, sizeof(buf)); //将接收到的命令读取到缓冲区中 if (buf[0] == 0x01) //核对地址以决定是否响应命令,本例本机地址为0x01 {

crc = GetCRC16(buf, len-2); //计算CRC校验值 crch = crc >> 8; crcl = crc & 0xFF;

if ((buf[len-2] == crch) && (buf[len-1] == crcl)) //判断CRC校验是否正确 {

switch (buf[1]) //按功能码执行操作

{

case 0x03: //读取一个或连续的寄存器

if ((buf[2] == 0x00) && (buf[3] <= 0x05)) //寄存器地址支持0x0000~0x0005

{

if (buf[3] <= 0x04) {

i = buf[3]; //提取寄存器地址

cnt = buf[5]; //提取待读取的寄存器数量

buf[2] = cnt*2; //读取数据的字节数,为寄存器数*2,因Modbus定义的寄存器为16位

len = 3; while (cnt--) {

buf[len++] = 0x00; //寄存器高字节补0 buf[len++] = regGroup[ i++]; //低字节 } }

else //地址0x05为蜂鸣器状态 {

buf[2] = 2; //读取数据的字节数 buf[3] = 0x00; buf[4] = flagBuzzOn; len = 5; } break; }

else //寄存器地址不被支持时,返回错误码 {

buf[1] = 0x83; //功能码最高位置1

buf[2] = 0x02; //设置异常码为02-无效地址 len = 3; break; }

case 0x06: //写入单个寄存器

if ((buf[2] == 0x00) && (buf[3] <= 0x05)) //寄存器地址支持0x0000~0x0005

{

if (buf[3] <= 0x04) {

i = buf[3]; //提取寄存器地址 regGroup[ i] = buf[5]; //保存寄存器数据 cnt = regGroup[ i] >> 4; //显示到液晶上 if (cnt >= 0xA)

str[0] = cnt - 0xA + 'A'; else

str[0] = cnt + '0'; cnt = regGroup[ i] & 0x0F; if (cnt >= 0xA)

str[1] = cnt - 0xA + 'A'; else

str[1] = cnt + '0'; str[2] = '\\0';

LcdShowStr(i*3, 0, str); }

else //地址0x05为蜂鸣器状态 {

flagBuzzOn = (bit)buf[5]; //寄存器值转换为蜂鸣器的开关 }

len -= 2; //长度-2以重新计算CRC并返回原帧 break; }

else //寄存器地址不被支持时,返回错误码 {

buf[1] = 0x86; //功能码最高位置1

buf[2] = 0x02; //设置异常码为02-无效地址 len = 3; break; }

default: //其它不支持的功能码 buf[1] |= 0x80; //功能码最高位置1

buf[2] = 0x01; //设置异常码为01-无效功能 len = 3; break; }

crc = GetCRC16(buf, len); //计算CRC校验值 buf[len++] = crc >> 8; //CRC高字节 buf[len++] = crc & 0xFF; //CRC低字节 UartWrite(buf, len); //发送响应帧 } } } }

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 {

if (idletmr < 5) //接收计数器未改变,即总线空闲时,累积空闲时间 {

idletmr += ms;

if (idletmr >= 5) //空闲时间超过4个字节传输时间即认为一帧命令接收完毕 {

cmdArrived = 1; //设置命令到达标志 }


手把手教你学51单片机之十八 RS485通信与Modbus协议(4).doc 将本文的Word文档下载到电脑 下载失败或者文档不完整,请联系客服人员解决!

下一篇:演讲稿-拿什么奉献给你我的祖国 -

相关阅读
本类排行
× 注册会员免费下载(下载后可以自由复制和排版)

马上注册会员

注:下载文档有可能“只有目录或者内容不全”等情况,请下载之前注意辨别,如果您已付费且无法下载或内容有问题,请联系我们协助你处理。
微信: QQ: