wells 发表于 2023-5-6 15:30:25

分享一个关于Modbus的工程【内部还有其他外设部分】

最近接触到了STC32G的单片机,一开始使用寄存器觉得很不习惯,因为以前一直是在使用STM32系列的所以使用库函数习惯,但是当学习了STC32才感慨到这个单片机也是“确实挺不错的”。
下面分享一下我写的关于使用RS485实现modbus的功能,使用的功能码是【03/06】。

先和大家分享一下我对于03和06功能码的理解吧:

/==================================03功能码,一共是8位=============================================/
主机发送:   0             1         2 3            4 5                6 7                03功能码其实就是读多个寄存器里面的数据
                从机地址功能码    起始地址    数据长度      CRC校验码
从机回复:    0             1            2               34               56         .....      (n-1)n
                从机地址功能码   读取字节数寄存器数据1       寄存器数据2   .....       CRC校验码
/=============================================================================================/
/==================================06功能码,一共是8位 ======================================== =====/
主机发送:    0         1             2 3             4 5               6 7                06功能码其实就是写一个寄存器里面的数据
             从机地址功能码   寄存器地址    写入的数据       CRC校验码
从机回复:和主机发送的一样
/==============================================================================================/

在这个例程中,我们将使用DMA_I2C或者I2C去读写我们的EEPROM,这里我们使用的型号是AT24C64。

首先是这个项目应该是主要建立在USART和I2C之上的,因此我们应该着重配置这两个部分,我在这里使用的是库函数版本,因为真的很好用!
个人觉得假如学过51系列的单片机的话,使用STC32G的库函数版本,然后哪里不懂得或者忘了再去找其定义的寄存器,这样的过程是很有趣的一件事。
/*                                             USART4的配置函数                                          *//*                                             DMA-I2C配置函数                                          */
以上是关于I2C的配置,因为是使用的库函数版本,我也就不过多赘述了,毕竟手册里和例程里面讲的比我还详细!
下面最主要的是来说明一下,modbus——rtu函数是怎么编写的。
我使用了一种比较暴力的编写方式,就是按部就班的编写,modbus规定了什么我们就编写什么。
因为06是写单个寄存器,03是读多个寄存器,因此我们在里面进行相关操作。
void modbus_func(){
<div style="text-align: left;"><p style="line-height: 30px; text-indent: 2em;"></p>
<div><p style="line-height: 30px; text-indent: 2em;"></p>
<div>uint16 addr;                //寄存器地址【03/06】
uint8 addh,addl;        //高地址,低地址【03/06】
uint16 crcData;        //CRC校验码【03/06】
uint16 readCount;        //需要读出的数据的寄存器长度【03】
uint8 byteCount;        //需要读出的数据的字节长度【03】
uint8 sendCount;        //需要发送的数据的字节长度【03】
uint8 status,i;                         //验证位,循环量
addr = 0;
addr=(RX4_Buffer<<8)+RX4_Buffer;//读取地址【03/06】
addh=addr>>8;                //高地址【03/06】
addl=addr& 0xff;        //低地址【03/06】
TX4_Buffer = SL_ADDREESS;//返回本机地址【03/06】
if(RX4_Buffer==0x06){
<b><i><u><font color="#800080">//str=RX4_Buffer;</font></u></i></b>
<b><i><u><font color="#800080">str=RX4_Buffer;</font></u></i></b>
<b><i><u><font color="#800080">I2C_Write_16add(addh,addl,str,1);</font></u></i></b>
TX4_Buffer = 0x06;                                                                //06功能码
TX4_Buffer = RX4_Buffer;                                                                //返回高地址
TX4_Buffer = RX4_Buffer;                                                                //返回低地址
TX4_Buffer = RX4_Buffer;                                        //原样返回数据
TX4_Buffer = RX4_Buffer;
crcData = Modbus_Rtu_CRC(TX4_Buffer,6);                //CRC校验部分
TX4_Buffer = crcData & 0xff;
TX4_Buffer = crcData >> 8;
USART4_SendArr(TX4_Buffer,8);
delay_ms(2);
}
else if(RX4_Buffer==0x03){
readCount = (RX4_Buffer<<8) | RX4_Buffer;        //要读的寄存器个数
byteCount = readCount * 2;                                                                //要读字节个数
TX4_Buffer = 0x03;                                                                        //03功能码
TX4_Buffer = byteCount&0xff;                                                //读取的字节数
<b><i><u><font color="#ff8c00">I2C_Read_16add(addh,addl,str,byteCount);</font></u></i></b>
<b><i><u><font color="#ff8c00">for(i=0;i<byteCount;i+=2)        //先是34后是56所以i+2</font></u></i></b>
<b><i><u><font color="#ff8c00">{</font></u></i></b>
<b><i><u><font color="#ff8c00">TX4_Buffer = str;</font></u></i></b>
<b><i><u><font color="#ff8c00">TX4_Buffer = str;</font></u></i></b>
<b><i><u><font color="#ff8c00">}</font></u></i></b>
byteCount=byteCount+3;
crcData = Modbus_Rtu_CRC(TX4_Buffer,byteCount);
TX4_Buffer = crcData & 0xff;
byteCount++;
TX4_Buffer = crcData >> 8;
sendCount = byteCount + 1;
USART4_SendArr(TX4_Buffer,sendCount);
delay_ms(2);
}</div><p></p></div><p></p></div><p style="line-height: 30px; text-indent: 2em;"></p><div style="text-align: left;">}</div>然后是CRC校验部分,对于这个函数,建议不研究算法的同学可以直接拿来使用,网上也有很多写的方法,例如查表法,那样更快,但我还是喜欢这个计算的。
u16 Modbus_Rtu_CRC(uint8 *Data,uint16 len){
                                                                                        //我们返回的CRC校验码是两个字节
        uint16 crc_16 = 0xffff;                        //预设CRC寄存器为all_1
        uint16value = 0xA001;                        //CRC寄存器后面与0XA001进行异或
        char i,j;
        for(i = 0;i< len;i++){
                crc_16 ^= Data;                                 //把第1个8bit B数据(既通讯信息帧的第一个字节)与16位的crc_16寄存器的低8位相异或,把结果放于CRC寄存器;
                for(j = 0;j < 8;j++){                //因为是8位数据因此需要移动8次
                        if(crc_16&1){                                        //检测移出的数据是否等于1
                                crc_16>>=1;                                       
                                crc_16^=value;                                //等于1就去异或0xA001
                        }else{
                                crc_16>>=1;                                        //不等于1就直接移走
                        }
                }
        }       
        return crc_16;                                                        //返回校验码
}
最后奉上我滴工程。请大家批评指正












wells 发表于 2023-5-6 15:31:57

貌似发的时候有点问题,实在是抱歉啊,大家从末尾的文件下载吧

AI-32位8051 发表于 2023-5-6 15:47:18

本帖最后由 STCAI-32位8051 于 2023-5-6 15:49 编辑

收到,学习,感谢,家里就下面这些好东西,
还是 【免费+包邮】送您全家桶一套
1, STC32G12K128实验箱
2, STC8H8K64U实验箱
3, STC一箭双雕之USB转双串口
4, 开天斧-STC8H8K64U核心功能实验板
5, 屠龙刀-STC8H8K64U核心功能实验板
6, STC-USB Link1D
===家里就这些好东西了,各送一套,聊表谢意.
麻烦楼主加我QQ我来给您安排,QQ号:2593903262
                                           微信也可:18106296598
页: [1]
查看完整版本: 分享一个关于Modbus的工程【内部还有其他外设部分】