最近接触到了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的库函数版本,然后哪里不懂得或者忘了再去找其定义的寄存器,这样的过程是很有趣的一件事。
复制代码
复制代码 以上是关于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[2]<<8)+RX4_Buffer[3];//读取地址【03/06】
- addh=addr>>8; //高地址【03/06】
- addl=addr& 0xff; //低地址【03/06】
- TX4_Buffer[0] = SL_ADDREESS;//返回本机地址【03/06】
- if(RX4_Buffer[1]==0x06){
- <b><i><u><font color="#800080">//str[0]=RX4_Buffer[4];</font></u></i></b>
- <b><i><u><font color="#800080">str[1]=RX4_Buffer[5];</font></u></i></b>
- <b><i><u><font color="#800080">I2C_Write_16add(addh,addl,str,1);</font></u></i></b>
- TX4_Buffer[1] = 0x06; //06功能码
- TX4_Buffer[2] = RX4_Buffer[2]; //返回高地址
- TX4_Buffer[3] = RX4_Buffer[3]; //返回低地址
- TX4_Buffer[4] = RX4_Buffer[4]; //原样返回数据
- TX4_Buffer[5] = RX4_Buffer[5];
- crcData = Modbus_Rtu_CRC(TX4_Buffer,6); //CRC校验部分
- TX4_Buffer[6] = crcData & 0xff;
- TX4_Buffer[7] = crcData >> 8;
- USART4_SendArr(TX4_Buffer,8);
- delay_ms(2);
- }
- else if(RX4_Buffer[1]==0x03){
- readCount = (RX4_Buffer[4]<<8) | RX4_Buffer[5]; //要读的寄存器个数
- byteCount = readCount * 2; //要读字节个数
- TX4_Buffer[1] = 0x03; //03功能码
- TX4_Buffer[2] = 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[i+3] = str[i];</font></u></i></b>
- <b><i><u><font color="#ff8c00">TX4_Buffer[i+4] = str[i+1];</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[byteCount] = crcData & 0xff;
- byteCount++;
- TX4_Buffer[byteCount] = 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
- uint16 value = 0xA001; //CRC寄存器后面与0XA001进行异或
- char i,j;
- for(i = 0;i< len;i++){
- crc_16 ^= Data[i]; //把第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; //返回校验码
- }
复制代码
最后奉上我滴工程。请大家批评指正
|