第一范文网 - 专业文章范例文档资料分享平台

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

来源:用户分享 时间:2025/5/24 15:34:24 本文由loading 分享 下载这篇文档手机版
说明:文章内容仅供预览,部分内容可能不全,需要完整文档或者需要复制内容,请下载word后使用。下载word有问题请添加微信号:xxxxxxx或QQ:xxxxxx 处理(尽可能给您提供完整文档),感谢您的支持与谅解。

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) //

软件延时函数,延时时间 {

do {

_nop_();

_nop_();

_nop_();

_nop_();

_nop_();

_nop_();

_nop_();

_nop_();

} while (--t);

}

(t*10)us

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; //

读取数据的字节数,为寄存器数

16 位

len = 3;

while (cnt--)

{

buf[len++] = 0x00;

// 寄存器高字节补

0buf[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;

}

*2 ,因 Modbus

0x0005

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

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

0x0000 ~

{

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协议.doc 将本文的Word文档下载到电脑,方便复制、编辑、收藏和打印
本文链接:https://www.diyifanwen.net/c8k1165yeja0sr9z0p01l1xu1x81dzc00o8f_4.html(转载请注明文章来源)
热门推荐
Copyright © 2012-2023 第一范文网 版权所有 免责声明 | 联系我们
声明 :本网站尊重并保护知识产权,根据《信息网络传播权保护条例》,如果我们转载的作品侵犯了您的权利,请在一个月内通知我们,我们会及时删除。
客服QQ:xxxxxx 邮箱:xxxxxx@qq.com
渝ICP备2023013149号
Top