找回密码
 立即注册

QQ登录

只需一步,快速开始

搜索
查看: 4185|回复: 6
收起左侧

单片机RS485通信与Modbus协议

  [复制链接]
ID:415338 发表于 2021-7-5 22:56 | 显示全部楼层 |阅读模式
RS485通信的特点
n1、采用差分信号。
n2、RS485通信速率快,最大传输速率可以达到10Mb/s以上。
n3、RS485内部采用平衡驱动器和差分接收器的组合,抗干扰能力大大增加。
n4、传输距离最远可以达到1200米左右。
n5、可以在总线上进行联网多机通信。
n6、RS485接口非常简单。
Modbus通信协议介绍
n1、Modbus产生的背景。
n2、Modbus协议特点。
n3、RTU协议帧数据
n4、Modbus功能码
/* 备  注:
* 1、在lesson15_3的基础上去掉按键校时,添加lesson18_2中的Modbus协议支持
* 2、利用Modbus调试精灵的写寄存器功能,可修改日期时间的每一个字节
* 3、寄存器地址0x0000~0x0006分别对应“年/月/日/时/分/秒/星期”
* 4、RS485方向控制信号由原来的P1.7改为P2.0,因本例使用了DS1302而未使用按键
*******************************************************************************
*/

#include <reg52.h>

struct sTime {  //日期时间结构体定义
    unsigned int  year;
    unsigned char mon;
    unsigned char day;
    unsigned char hour;
    unsigned char min;
    unsigned char sec;
    unsigned char week;
};

bit flag200ms = 1;  //200ms定时标志
bit reqRefresh = 0;  //时间刷新请求
struct sTime bufTime;  //日期时间缓冲区
unsigned char T0RH = 0;  //T0重载值的高字节
unsigned char T0RL = 0;  //T0重载值的低字节

void ConfigTimer0(unsigned int ms);
void RefreshTimeShow();
extern void InitDS1302();
extern void GetRealTime(struct sTime *time);
extern void SetRealTime(struct sTime *time);
extern void UartDriver();
extern void ConfigUART(unsigned int baud);
extern void UartRxMonitor(unsigned char ms);
extern void UartWrite(unsigned char *buf, unsigned char len);
extern unsigned int GetCRC16(unsigned char *ptr,  unsigned char len);
extern void InitLcd1602();
extern void LcdShowStr(unsigned char x, unsigned char y, unsigned char *str);

void main()
{
    unsigned char psec=0xAA;  //秒备份,初值AA确保首次读取时间后会刷新显示

    EA = 1;            //开总中断
    ConfigTimer0(1);   //配置T0定时1ms
    ConfigUART(9600);  //配置波特率为9600
    InitDS1302();      //初始化实时时钟
    InitLcd1602();     //初始化液晶

    //初始化屏幕上固定不变的内容
    LcdShowStr(3, 0, "20  -  -  ");
    LcdShowStr(4, 1, "  :  :  ");

    while (1)
    {
        UartDriver();  //调用串口驱动
        if (flag200ms)
        {
            flag200ms = 0;
            GetRealTime(&bufTime);    //获取当前时间
            if (reqRefresh || (psec!=bufTime.sec))
            {   //检测到时间刷新请求或时间有变化时刷新显示
                RefreshTimeShow();
                psec = bufTime.sec;   //用当前值更新上次秒数
            }
        }
    }
}
/* 将一个BCD码字节显示到屏幕上,(x,y)-屏幕起始坐标,bcd-待显示BCD码 */
void ShowBcdByte(unsigned char x, unsigned char y, unsigned char bcd)
{
    unsigned char str[4];

    str[0] = (bcd >> 4) + '0';
    str[1] = (bcd&0x0F) + '0';
    str[2] = '\0';
    LcdShowStr(x, y, str);
}
/* 刷新日期时间的显示 */
void RefreshTimeShow()
{
    ShowBcdByte(5,  0, bufTime.year);
    ShowBcdByte(8,  0, bufTime.mon);
    ShowBcdByte(11, 0, bufTime.day);
    ShowBcdByte(4,  1, bufTime.hour);
    ShowBcdByte(7,  1, bufTime.min);
    ShowBcdByte(10, 1, bufTime.sec);
}
/* 串口动作函数,根据接收到的命令帧执行响应的动作
   buf-接收到的命令帧指针,len-命令帧长度 */
void UartAction(unsigned char *buf, unsigned char len)
{
    unsigned int  crc;
    unsigned char crch, crcl;

    if (buf[0] != 0x01) //本例中的本机地址设定为0x01,
    {                   //如数据帧中的地址字节与本机地址不符,
        return;         //则直接退出,即丢弃本帧数据不做任何处理
    }
    //地址相符时,再对本帧数据进行校验
    crc = GetCRC16(buf, len-2);  //计算CRC校验值
    crch = crc >> 8;
    crcl = crc & 0xFF;
    if ((buf[len-2]!=crch) || (buf[len-1]!=crcl))
    {
        return;   //如CRC校验不符时直接退出
    }
    //地址和校验字均相符后,解析功能码,执行相关操作
    switch (buf[1])
    {
        case 0x06:  //写入单个寄存器
            if ((buf[2]==0x00) && (buf[3]<=0x06)) //地址0x0000~0x0006分别对应
            {                                     // “年/月/日/时/分/秒/星期”
                GetRealTime(&bufTime);  //获取当前时间
                switch (buf[3])  //由寄存器地址决定要修改的时间位
                {
                    case 0: bufTime.year = 0x2000 + buf[5]; break;
                    case 1: bufTime.mon  = buf[5]; break;
                    case 2: bufTime.day  = buf[5]; break;
                    case 3: bufTime.hour = buf[5]; break;
                    case 4: bufTime.min  = buf[5]; break;
                    case 5: bufTime.sec  = buf[5]; break;
                    case 6: bufTime.week = buf[5]; break;
                    default: break;
                }
                SetRealTime(&bufTime);  //写入新修改后的时间
                reqRefresh = 1;  //设置显示刷新请求
                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);      //发送返回帧
}
/* 配置并启动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中断服务函数,执行按键扫描和200ms定时 */
void InterruptTimer0() interrupt 1
{
    static unsigned char tmr200ms = 0;

    TH0 = T0RH;  //重新加载重载值
    TL0 = T0RL;
    UartRxMonitor(1);  //串口接收监控
    tmr200ms++;
    if (tmr200ms >= 200)  //定时200ms
    {
        tmr200ms = 0;
        flag200ms = 1;
    }
}

Modbus注意要项.pdf

216.87 KB, 下载次数: 91, 下载积分: 黑币 -5

MODBUS协议中文版.pdf

1.11 MB, 下载次数: 109, 下载积分: 黑币 -5

Modbus通信协议.pdf

156.79 KB, 下载次数: 83, 下载积分: 黑币 -5

MAX485.pdf

438.77 KB, 下载次数: 80, 下载积分: 黑币 -5

例程.7z

18.93 KB, 下载次数: 130, 下载积分: 黑币 -5

评分

参与人数 1黑币 +50 收起 理由
admin + 50 共享资料的黑币奖励!

查看全部评分

回复

使用道具 举报

ID:624685 发表于 2021-7-6 10:16 | 显示全部楼层
资料真好,就是有点非 钱啊
回复

使用道具 举报

ID:415338 发表于 2021-7-7 15:16 | 显示全部楼层
没积分,需要资料的可以直接私信我,我发你
回复

使用道具 举报

ID:686788 发表于 2021-7-7 16:25 | 显示全部楼层
很好的资料,感谢分享,顶!!!
回复

使用道具 举报

ID:373892 发表于 2022-3-12 09:51 | 显示全部楼层
远小刘 发表于 2021-7-7 15:16
没积分,需要资料的可以直接私信我,我发你

求发915572320@qq.com,谢谢楼主
回复

使用道具 举报

ID:99525 发表于 2022-5-4 04:05 | 显示全部楼层
这就是宋老师第十八章的内容!
回复

使用道具 举报

ID:18797 发表于 2022-6-10 00:07 | 显示全部楼层
好贴,有一定的借鉴意义
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

手机版|小黑屋|51黑电子论坛 |51黑电子论坛6群 QQ 管理员QQ:125739409;技术交流QQ群281945664

Powered by 单片机教程网

快速回复 返回顶部 返回列表