【电机控制】RS485通讯MODBUS协议实验

【电机控制】RS485通讯MODBUS协议实验 @TOC 前言 1.STLINK仿真器 2.USB-TTL 3.USB转RS485 4.RS485转TTL 5.STM32控制器 6.上位机SSCOM(带MODBUS协议) 提示:以下是本篇文章正文内容,下面案例可供参考 一、串口数据格式 串口通信帧数据如此,每帧由空闲位、起始位、数据位、校验位、停止位组成 传输的数据是低位在前高位在后 从低到高,1000 1101,每位104us 9600bps就是每秒传输9600bit(位)的意思,也就相当于:1/9600=1.041666666666667e-4秒为每个bit的传输时间,也即104us 二、示波器解析串口数据 1.上位机发送数据 发送字符1,对应ASCII理论值十进制为49 上位机发送数据波形 2、3、4、5、6、7、8、9组成8位数据 反馈为 0011 0001 2.MCU接收数据波形 1.加上光耦隔离 2.不加光耦隔离 可以看出,加了光耦,数据接收异常; 不加光耦,数据接收正常 三、MODBUS协议格式 四、代码 1.实验一:发送010600010055 2.实验二:发送010600010056 void TestOpr(void) { /* 这里只是演示,不具实际意义 */ if (regGroup[1] == 0x55) { printf("驱动电机A !\r\n"); regGroup[1] = 0; //这里是为了防止重复输出 } else if (regGroup[1] == 0x56) { printf("驱动电机B !\r\n"); regGroup[1] = 0; //这里是为了防止重复输出 } } 3.实验三:发送010600010017 void UartDriver(void) { static unsigned char len; static unsigned char buf[64]; unsigned char i; unsigned char cnt; unsigned int crc; unsigned char crch,crcl; if (From_Flag) //帧接收完成标志,即接收到一帧新数据 { From_Flag = 0; //帧接收完成标志清零 len = UartRead(buf,sizeof(buf)); //将接收到的命令读到缓冲区中 crc = GetCRC16(buf,len-2); //计算CRC校验值,除去CRC校验值 crch=crc >> 8; //crc高位 crcl=crc & 0xFF; //crc低位 printf("485接收数据\r\n"); for(i = 0;i

Mar 28, 2025 - 08:10
 0
【电机控制】RS485通讯MODBUS协议实验

【电机控制】RS485通讯MODBUS协议实验

@TOC

前言

1.STLINK仿真器
2.USB-TTL
3.USB转RS485
4.RS485转TTL
5.STM32控制器
6.上位机SSCOM(带MODBUS协议)

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

提示:以下是本篇文章正文内容,下面案例可供参考

一、串口数据格式

在这里插入图片描述
串口通信帧数据如此,每帧由空闲位、起始位、数据位、校验位、停止位组成
传输的数据是低位在前高位在后
从低到高,1000 1101,每位104us
9600bps就是每秒传输9600bit(位)的意思,也就相当于:1/9600=1.041666666666667e-4秒为每个bit的传输时间,也即104us
在这里插入图片描述

二、示波器解析串口数据

1.上位机发送数据

发送字符1,对应ASCII理论值十进制为49
在这里插入图片描述

在这里插入图片描述
上位机发送数据波形

在这里插入图片描述

在这里插入图片描述
2、3、4、5、6、7、8、9组成8位数据
反馈为 0011 0001

2.MCU接收数据波形

1.加上光耦隔离

在这里插入图片描述
在这里插入图片描述

2.不加光耦隔离

在这里插入图片描述
在这里插入图片描述
可以看出,加了光耦,数据接收异常;
不加光耦,数据接收正常

三、MODBUS协议格式

在这里插入图片描述

四、代码

1.实验一:发送010600010055

2.实验二:发送010600010056

void TestOpr(void)
{   
    /* 这里只是演示,不具实际意义 */
    if (regGroup[1] == 0x55)
    {
        printf("驱动电机A !\r\n");
        regGroup[1] = 0;                //这里是为了防止重复输出
    }
    else if (regGroup[1] == 0x56)
    {
        printf("驱动电机B !\r\n");
        regGroup[1] = 0;                //这里是为了防止重复输出
    }
}

3.实验三:发送010600010017

void UartDriver(void)
{
    static unsigned char len;
    static unsigned char buf[64];
  unsigned char i;
    unsigned char cnt;
    unsigned int crc;
    unsigned char crch,crcl;
    if (From_Flag)                                              //帧接收完成标志,即接收到一帧新数据
    {
        From_Flag = 0;                                          //帧接收完成标志清零
        len       = UartRead(buf,sizeof(buf));                  //将接收到的命令读到缓冲区中
        crc       = GetCRC16(buf,len-2);                        //计算CRC校验值,除去CRC校验值
        crch=crc  >> 8;                                         //crc高位
        crcl=crc  &  0xFF;                                      //crc低位
        printf("485接收数据\r\n");
        for(i = 0;i < len;i ++)
            printf("485接收的值 = buf[%d] = %x \r\n",i,buf[i]);
        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;                         //响应帧第4个字节开始为数据
                            while(cnt --)
                            {
                                buf[len ++] = 0x00;             //寄存器高字节补0
                                buf[len ++] = regGroup[i ++];   //低字节
                            }
                        }
                        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];               //保存寄存器数据
                        }
                        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低字节
            Send_Data(buf,len);                                 //发送响应帧
        }
        else    /* 如果校验值错误,执行错误处理函数 这里用串口1打印一条提示信息 也可自定义共它处理函数 */
            printf("接收错误\r\n");
    }
}

五、实验

0.实物接线

USB-TTL TXD- MCU-RXD PA10
USB-TTL RXD- MCU-RXD PA9
USB转485 A- RS485转TTL A
USB转485 B- RS485转TTL B
USB转485 RXD- MCU-RXD PA3
USB转485 GND- MCU-GND
在这里插入图片描述

在这里插入图片描述

1.实验一

在这里插入图片描述

2.实验二

在这里插入图片描述

3.实验三

在这里插入图片描述

六、参考资料

MODBUS通讯协议中文版.pdf
隔离串口通信电路设计注意事
Ascii(256个) 编码表 完整码表 ASCII编码 ASCII表 ASCII码 二进制 十进制 八进制 十六进制
隔离串口通信电路设计注意事
STM32开发(六)STM32F103 通信 —— RS485 Modbus通信编程详解
STM32F1之485通信
STM32F103C8T6的MODBUS-RTU通讯(485通讯)
基于STM32以及modbus——RTU的从机程序(STM32作为从机)
并行,串行,同步,异步与UART,串口,TTL,RS232,RS485相关概念认识与解读

总结

本文仅仅简单介绍了【电机控制】RS485通讯MODBUS协议实验,评论区欢迎讨论。