完善资料让更多小伙伴认识你,还能领取20积分哦, 立即完善>
扫一扫,分享给好友
大家好,本人用的板子是STM32vet6,用到modbus通讯,但是初次接触modbus,modbus的文档也看了不少,但是程序里不知道怎么写来实现modbus的强大功能,目前我想先用modbus先实现接受和发送数据,但是不知道代码是怎样的,求大家能指导一下,或者大家有做过modbus这一块的,希望可以分享一下经验或者资料,感激不尽,帮助小白我,谢谢谢谢
|
|
相关推荐
32个回答
|
|
/***********************************************************
函数名称:MBRTU_Function 函数功能: ModBus读写寄存器功能 输 入:RcvDataBuf 接收数据的指针 输 出:无 返 回:无 *************************************************************/ void MBRTU_Function(void) { uint8_t RecvLen; uint8_t RcvDataBuf[64]; uint16_t CRC16; int reg; for(;;) { reg = ComRecv(RcvDataBuf, &RecvLen,50); if(!reg) { break; } } if ((ComAddr != MBRTU_GetSlaveAddr(RcvDataBuf)) && (MB_ADDRESS_BROADCAST != MBRTU_GetSlaveAddr(RcvDataBuf))) { // usart_printf("SlaveAdd error!rn"); return; } CRC16 = (RcvDataBuf[RecvLen-1] << 8) | RcvDataBuf[RecvLen-2]; if (CRC16 != MBRTU_GetCRC16(RcvDataBuf, RecvLen - 2)) { if ((MBRTU_GetSlaveAddr(RcvDataBuf) == ComAddr)) { MBRTU_SendErr(RcvDataBuf, MB_EX_MEMORY_PARITY_ERROR); } } else { switch (MBRTU_GetFunCode(RcvDataBuf)) { case 0x10: MBRTU_Fun10(RcvDataBuf); ComClose(); SetPWMFrequency_Duty(PWMPara.PWM_Frequency, PWMPara.Duty); ComOpen(); break; case 0x03: MBRTU_Fun03(RcvDataBuf); break; default: MBRTU_SendErr(RcvDataBuf, MB_EX_ILLEGAL_FUNCTION); break; } memset(RcvDataBuf, 0x00, RecvLen); //用完数据清零 } }
最佳答案
|
|
|
|
你用的谁的板子,去看看正点原子和奋斗的资料,也去看看金沙滩宋老师的书,宋老师的书,最后讲到了这个总线
|
|
|
|
我用的是刘洋的大黄蜂,他有教教485,但是modbus的资料就没有了,好,我上网看看他们板子的资料,看看有没有modbus的,谢谢 |
|
|
|
RS485需要用到3个IO,RX、TX、EN,EN=0时代表MCU处于接收数据状态,EN=1时时代表MCU处于发送数据状态,RS485是使用差分信号的传输数据的,所以连接外部的电路只需要接两根线A和B(RS232外部接三根线RX、TX、GND),RS485跟RS232很像,这个很简单,只要会串口收发数据RS485也就会了。ModBus只是个虚拟的通讯协议,虚拟一些寄存器并向里面读写数据,一般使用0x03读寄存器模式,0x10写寄存器模式,再加上CRC校验,如果想要代码我可以把我这几天写的发给你
|
|
|
|
meimengxing2014 发表于 2016-8-9 17:27 用485我会,但是要用到modbus我就不会了,我把modbus移植进去了,它的功能码作用我也知道,但是用它的功能码我就不会了,你可以写个简单的可供我参考一下的例子吗,两块板子通讯就够了,我只需要知道是怎样用到modbus实现它的功能的,十分感谢您 |
|
|
|
modbus是主机从机的模式,一般都是从机被动等带主机的信号,从机收到主机的呼叫信号,然后回复相应的应答信息,所以你就是写一个从机层序,一个主机程序就好了.
|
|
|
|
jianfeii 发表于 2016-8-16 09:01 理论上的知识我也了解,就是代码函数这块不懂,就是接受和发送,校验和功能码这些函数不会用,已经移植完了,串口助手modbus没法检测,好纠结 啊 |
|
|
|
可以把代码贴上来,把不懂得地方标注下,然后大家一起讨论
|
|
|
|
串口助手可以用啊,你就是把串口助手当主机,单片机下面跑从机程序,串口发送查询命令,或者修改寄存器的值,然后看从机返回的数据对不对. |
|
|
|
有乱码的地方是注释汉字,显示不出来,这是我网上看别人移植完的程序,main函数里都有这些函数,不写会报错,里面也没有校验什么的,看不懂啊看不懂,我是不是太菜了,本人大二 ![]() eMBErrorCode eMBRegCoilsCB( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNCoils, eMBRegisterMode eMode ) { //´íÎó״̬ eMBErrorCode eStatus = MB_ENOERR; //¼Ä´æÆ÷¸öÊý int16_t iNCoils = ( int16_t )usNCoils; //¼Ä´æÆ÷Æ«ÒÆÁ¿ int16_t usBitOffset; //¼ì²é¼Ä´æÆ÷ÊÇ·ñÔÚÖ¸¶¨·¶Î§ÄÚ if( ( (int16_t)usAddress >= REG_COILS_START ) && ( usAddress + usNCoils <= REG_COILS_START + REG_COILS_SIZE ) ) { //¼ÆËã¼Ä´æÆ÷Æ«ÒÆÁ¿ usBitOffset = ( int16_t )( usAddress - REG_COILS_START ); switch ( eMode ) { //¶Á²Ù×÷ case MB_REG_READ: while( iNCoils > 0 ) { *pucRegBuffer++ = xMBUtilGetBits( ucRegCoilsBuf, usBitOffset, ( uint8_t )( iNCoils > 8 ? 8 : iNCoils ) ); iNCoils -= 8; usBitOffset += 8; } break; //д²Ù×÷ case MB_REG_WRITE: while( iNCoils > 0 ) { xMBUtilSetBits( ucRegCoilsBuf, usBitOffset, ( uint8_t )( iNCoils > 8 ? 8 : iNCoils ), *pucRegBuffer++ ); iNCoils -= 8; } break; } } else { eStatus = MB_ENOREG; } return eStatus; } eMBErrorCode eMBRegDiscreteCB( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNDiscrete ) { //´íÎó״̬ eMBErrorCode eStatus = MB_ENOERR; //²Ù×÷¼Ä´æÆ÷¸öÊý int16_t iNDiscrete = ( int16_t )usNDiscrete; //Æ«ÒÆÁ¿ uint16_t usBitOffset; //ÅжϼĴæÆ÷ʱºòÔÙÖƶ¨·¶Î§ÄÚ if( ( (int16_t)usAddress >= REG_DISCRETE_START ) && ( usAddress + usNDiscrete <= REG_DISCRETE_START + REG_DISCRETE_SIZE ) ) { //»ñµÃÆ«ÒÆÁ¿ usBitOffset = ( uint16_t )( usAddress - REG_DISCRETE_START ); while( iNDiscrete > 0 ) { *pucRegBuffer++ = xMBUtilGetBits( ucRegDiscreteBuf, usBitOffset, ( uint8_t)( iNDiscrete > 8 ? 8 : iNDiscrete ) ); iNDiscrete -= 8; usBitOffset += 8; } } else { eStatus = MB_ENOREG; } return eStatus; }[/code] |
|
|
|
问题是我只是移植了modbus,功能码还有接受发送函数都没写啊,不知道代码是怎样的,从机程序没写好没法跑呢,我在想modbus是不是有专门的发送接收函数,不像485这样写个USART_SendData();就可以,网上的资料貌似也不多啊,你有没有测试成功的代码呢 |
|
|
|
我从网上下了好多modbus的代码,有些有crc校验,有些没有,有点无助了,代码都不一样,函数是自己定义的,还是modbus协议里自带的呢,本来想用来传输ds18B20的温度的,但是测试代码都不会啊大哥 ![]() |
|
|
|
现在有点晚了,明天我把0x10和0x03指令给你讲一下吧
|
|
|
|
0x10(16)写多个寄存器指令协议:(向寄存器写数据,传送给下位机)
主机请求: 01 10 00 30 00 03 06 00 1E 00 20 00 32 CE 62 最后两位为校验码 从机地址:01 //下位机地址 功能码:10 寄存器起始地址:0030 寄存器数量:0003 字节计数:06 PWM频率:001E (寄存器地址0030) PWM占空比:0020 (寄存器地址0031) PWM脉冲个数:0032 (寄存器地址0032) CRC校验码:CE62 从机应答: 01 10 00 30 00 03 80 07 最后两位为校验码 从机地址:01 功能码:10 寄存器起始地址:0030 寄存器数量:0003 CRC校验码:8007 0x03(3)读保持寄存器指令协议:(读取寄存器数据,即当前寄存器的数据) 主机请求: 01 03 00 30 00 03 05 C4 从机地址:01 功能码:10 寄存器起始地址:0030 寄存器数量:0003 CRC校验码:05C4 从机应答: 01 03 06 00 1E 00 20 00 32 09 68 从机地址:01 功能码:03 读出数据字节数:06 寄存器的值: 1.00 1E 2.00 20 3.00 32 CRC校验码:0968 上面是一个从上位机发送PWM频率、占空比、脉冲个数三个参数的Modeus通讯协议,请求是上位机的PC软件发给下位机的,然后下位机检验从机地址、功能码、寄存器数量、CRC校验值是否正确,如果正确则给予应答(正确的应答),如果不正确就回复错误代码 |
|
|
|
曾小z 发表于 2016-8-16 21:17 你也想太多了,modbus只是一个协议,所有的代码实现都要你自己写的,哪里有什么协议自带,你要根据协议写代码! |
|
|
|
static void MBRTU_Fun10(uint8_t *RcvDataBuf)
{ uint8_t k; uint16_t ReadAdr; uint16_t Register_Num; //接收到数据第1个字节为从机地址,第2个字节为功能码0x10 ReadAdr = (uint16_t)RcvDataBuf[2] * 256 + RcvDataBuf[3]; // 接收到数据的3、4两个字节存放的起始地址 Register_Num = (uint16_t)RcvDataBuf[4] * 256 + RcvDataBuf[5]; //5、6两个字节存放的是寄存器数量 uint32_t index = 0; if (!(((ReadAdr >= MUL_REG_REGION1_BGEIN) && (ReadAdr <= MUL_REG_REGION1_END)) && ((ReadAdr + Register_Num) <= (MUL_REG_REGION1_END + 1)) && ((0 != Register_Num) && (Register_Num * 2 == RcvDataBuf[6])))) //第7个字节存放的字节计数=寄存器数量*2 { MBRTU_SendErr(RcvDataBuf, MB_EX_ILLEGAL_DATA_ADDRESS); return; } for (k = 0; k < Register_Num; ReadAdr++, k++) { switch (ReadAdr) //第8、9两个字节存放的是寄存器值 { case 0x0030: //PWM频率 PWMPara.PWM_Frequency = (uint16_t)RcvDataBuf[7+index] * 256 + RcvDataBuf[8+index]; index += 2; break; case 0x0031: //PWM占空比 PWMPara.Duty = (uint16_t)RcvDataBuf[7+index] * 256 + RcvDataBuf[8+index]; index += 2; break; case 0x0032: //PWM的脉冲个数 PWMPara.PulseCount = (uint16_t)RcvDataBuf[7+index] * 256 + RcvDataBuf[8+index]; index += 2; break; default: break; } } MBRTU_SendMsg(RcvDataBuf, 6); } |
|
|
|
/***********************************************************
函数名称:MBRTU_Fun03 函数功能: ModBus功能03协议(读多个寄存器) 输 入:RcvDataBuf 接收数据的指针 输 出:无 返 回:无 *************************************************************/ static void MBRTU_Fun03(uint8_t *RcvDataBuf) { uint8_t SendBuf[64]; //存储从机响应消息帧 uint8_t SendLen = 0; uint8_t i,k; uint32_t Data_Buf; uint16_t ReadAdr = (uint16_t)RcvDataBuf[2] * 256 + RcvDataBuf[3]; uint16_t Register_Num = (uint16_t)RcvDataBuf[4] * 256 + RcvDataBuf[5]; SendBuf[SendLen++] = (MBRTU_GetSlaveAddr(RcvDataBuf)) ? ComAddr : 0x00; SendBuf[SendLen++] = MB_FUNC_READ_HOLDING_REGISTER; //功能码 SendBuf[SendLen++] = Register_Num * 2; //数据长度 if (!(((ReadAdr >= HOLDING_REG_REGION1_BGEIN) && (ReadAdr <= HOLDING_REG_REGION1_END) && (ReadAdr + Register_Num <= (HOLDING_REG_REGION1_END + 1))) && (0 != Register_Num))) { MBRTU_SendErr(RcvDataBuf, MB_EX_ILLEGAL_DATA_ADDRESS); return; } for (k = 0; k < Register_Num; ReadAdr++, k++) { switch (ReadAdr) { case 0x0030: Data_Buf = PWMPara.PWM_Frequency; //PWM频率 break; case 0x0031: Data_Buf = PWMPara.Duty; //PWM占空比 break; case 0x0032: Data_Buf = PWMPara.PulseCount; //PWM脉冲个数 break; default: Data_Buf = 0; break; } for (i = 2; i > 0; i--) { SendBuf[SendLen++] = (uint8_t)(Data_Buf >> ((i - 1) * 8)); //把数据分成两个字节 } } MBRTU_SendMsg(SendBuf, SendLen); } |
|
|
|
上面是0x10和0x03以及和上位机通讯的函数,你先看看,肯定比你那个清楚,其他比方CRC校验、发送错误码等函数自己补充下就好
|
|
|
|
问题解决了么?
|
|
|
|
你正在撰写答案
如果你是对答案或其他答案精选点评或询问,请使用“评论”功能。
612 浏览 1 评论
求助:STM32F407串口控制外设无效,用电脑串口助手有效
1543 浏览 3 评论
1529 浏览 1 评论
请问ad9910芯片的ram模式在接收数据时为什么会出现一段方波?
4117 浏览 1 评论
887 浏览 0 评论
小黑屋| 手机版| Archiver| 电子发烧友 ( 湘ICP备2023018690号 )
GMT+8, 2024-7-18 02:39 , Processed in 0.957734 second(s), Total 109, Slave 92 queries .
Powered by 电子发烧友网
© 2015 bbs.elecfans.com
关注我们的微信
下载发烧友APP
电子发烧友观察
版权所有 © 湖南华秋数字科技有限公司
电子发烧友 (电路图) 湘公网安备 43011202000918 号 电信与信息服务业务经营许可证:合字B2-20210191