Freemodbus RTU在stm32上的移植分析.doc_第1页
Freemodbus RTU在stm32上的移植分析.doc_第2页
Freemodbus RTU在stm32上的移植分析.doc_第3页
Freemodbus RTU在stm32上的移植分析.doc_第4页
Freemodbus RTU在stm32上的移植分析.doc_第5页
已阅读5页,还剩8页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

Freemodbus RTU在stm32上的移植分析 最近用到free modbus,需要在stm32上进行移植,以作modbus-RTU之用。现成协议的东西用起来很方便,现成源码很快就可以为设计者所用,也是当初制定标准的初衷吧。首先下载最新的modbus源码,所谓技术更新换代的比较快,用就用最新的东西,协议嘛也要下载最新的,google一下,在/index.php?idx=5下载最新的版本freemodbus-v1.5,下载最新的协议不仅可以防止被人改动导致自己做无用功,保持原生态也可以很好的与制定者进行交流。解压freemodbus-v1.5,目录结构很清晰,主要有四个文件件,分别是demo、modbus、tools、doc。其中tools为上位机测试modbus程序,doc为一些说明文件先不讨论。有用的是demo以及modbus。打开demo,没有看到stm32的工程文件,有一个叫BARE的文件夹,是一些不包括任何处理器的部分源代码,我们就用这个建立工程文件。为了给以后移植modbus-TCP带来方便,这里直接打开之前测试好的基于ENC28J68的LwIP的stm32工程,在其中导入各个文件。导入前的原始以太网测试工程将prot以及modbus文件夹拷贝到工程文件夹下,导入工程,将demo中的main等几个函数拷贝到原先main.c中,注释掉原先的mian函数,就成了这个样子: 先理清所有依赖关系,肯定出现一大堆找不到头文件宏定义什么的错误,这个在keil中将文件夹的路径添加到include path中即可,非常方便。rebuild一下,发现有两个错误: 原来Keil4不支持inline这个关键字,直接将其删掉,编译出现了:.OutputSTM32-DEMO.axf: Error:L6218E: Undefined symbol _aeabi_assert (referred from mbascii.o).出现的这个问题,各种百度以及谷歌,找了半天也没找到解决方法。这里不得不说百度虽然本土化做得很好,可是以英文作为关键词时,往往搜出一大堆不相干的东西,基本上搜不到国外的网页;谷歌本土化做的不好,服务器响应比较慢。想起360新出了搜索引擎,赶紧去试试,还真的有惊喜,在一篇帖子中写道“MicroLib并不支援assert(),所以才会出现错误讯息”,原来原工程使用了微库,在target中钩掉USE MicroLIB编译就可以通过了。昨天看优库老友记采访周鸿祎说360做搜索引擎,作为360的忠实用户应该支持一下。任何一个公司想做的更好,必须注意用户体验。下面开始正式的移植以及分析和测试工作: 一、对于时钟的移植:由于modbus RTU模式需要定时器的支持,所以第一步先移植与定时器相关的函数。在porttimer.c中添加BOOL xMBPortTimersInit( USHORT usTim1Timerout50us )的实现,实现50us的基时时钟。添加打开和关闭时钟的函数void vMBPortTimersEnable( )以及voidvMBPortTimersDisable( ),还有超时中断函数voidTIM4_IRQHandler(void) 二、对串口通信的移植 无论是modbus ASCII还是RTU模式,都以串口通讯做为载体,需要添加串口的使能 BOOL xMBPortSerialInit,收发中断的使能voidvMBPortSerialEnable( BOOLxRxEnable, BOOL xTxEnable ),发送以及接收BOOL xMBPortSerialPutByte( CHAR ucByte ),BOOL xMBPortSerialGetByte( CHAR * pucByte ),这几个函数没什么好说的,有两个中断函数我比较好奇,就是static void prvvUARTTxReadyISR( void )以及staticvoid prvvUARTRxISR( void ),就是一个发送中断一个接收中断,为什么是这样的名字呢,stm32串口发生中断怎么去调用它们呢,如果换成其他单片机,为什么是这样的一个名字呢?原来在freemodbus中并没有提供中断函数的具体名称,还需要根据自己使用的处理器自己添加中断处理函数voidUSART1_IRQHandler(void),在其中调用上述两个发送和接收的函数。 三、第一个功能单元读写寄存的支持 FreeModbus源码包中BARE工程文件的main.c中定义了eMBErrorCode eMBRegInputCB()、eMBErrorCode eMBRegHoldingCB()、eMBErrorCode eMBRegCoilsCB()以及eMBErrorCode eMBRegDiscreteCB()四个函数与从机寄存器做了接口,并给出了eMBErrorCode eMBRegInputCB()的例子,在工程中建立portreg.c来实现这几个函数。 其中eMBErrorCode eMBRegInputCB()为读寄存器的值,eMBErrorCode eMBRegHoldingCB()为向单片机写入寄存器的值,eMBErrorCodeeMBRegCoilsCB()为读写多个开关量的函数,eMBErrorCodeeMBRegDiscreteCB()为读多个离散开关量的函数。 为了方便测试,我们只先实现第一个eMBErrorCodeeMBRegInputCB()读连续多个寄存器值的函数,比如实现读GPIOA-GPIOG的值。定义 #define REG_INPUT_START 0 #define REG_INPUT_NREGS 7 在eMBErrorCode eMBRegInputCB()开始部分加入度寄存器值语句,以备查询时使用。 比如参数UCHAR *pucRegBuffer, USHORT usAddress = 0, USHORT usNRegs = 3 就是读取端口GPIOA-GPIOC的值 usRegInputBuf0 =GPIO_ReadInputData(GPIOA); usRegInputBuf1 =GPIO_ReadInputData(GPIOB); usRegInputBuf2 =GPIO_ReadInputData(GPIOC); usRegInputBuf3 =GPIO_ReadInputData(GPIOD); usRegInputBuf4 =GPIO_ReadInputData(GPIOE); usRegInputBuf5 =GPIO_ReadInputData(GPIOF); usRegInputBuf6 =GPIO_ReadInputData(GPIOG); 这部分定义好,我们看一下freemodbus运行的流程。 四、freemodbus运行的软件仿真分析 在main函数中可以看到 int main( void ) eMBErrorCode eStatus; eStatus = eMBInit( MB_RTU, 0x0A, 0, 38400, MB_PAR_EVEN ); /* Enable the Modbus Protocol Stack. */ eStatus = eMBEnable( ); for( ; ) ( void )eMBPoll( ); /* Here we simply count the number ofpoll cycles. */ usRegInputBuf0+; (1)其中调用了 eMBErrorCode eMBInit( eMBMode eMode, UCHARucSlaveAddress, UCHAR ucPort, ULONGulBaudRate, eMBParity eParity ); 其目的就是选择要使用的模式是RTU、ASCII码还是TCP方式,如果选择的模式是RTU或ASCII模式其他都是串口的一些设置;如果选择的是TCP模式,需要调用到eMBErrorCode eMBTCPInit( USHORT usTCPPort )只需要制定端口号即可。这里我们先用RTU模式做测试。在这同时也对定时器进行了初始化。 (2)之后调用了 eMBErrorCode eMBEnable( void );来使能modbus协议栈,其中调用pvMBFrameStartCur( ),在eMBInit根据模式选择的不同,pvMBFrameStartCur( )会有不同的原型,这里选用的是RTU模式,那么将调用eMBRTUStart,其中调用了 vMBPortSerialEnable vMBPortTimersEnable来时能串口和定时器,使能了超时定时器,故经过T35时间后,发生第一次超时中断,在中断中,向协议栈发送消息EV_READY(Startupfinished),并调用voidvMBPortTimersDisable( )关闭超时定时器,同时将eRcvState设为STATE_RX_IDLE。此时,协议栈可以接收串口数据。 (3)最后调用 eMBErrorCode eMBPoll( void )用来检测协议栈状态用于处理消息。 五,编译后进行软件仿真查看协议栈具体运行流程打开keil软件仿真器,软件仿真进行单步运行,如下图所示。对工程进行软件仿真 (1)执行main()中eStatus = eMBInit( MB_RTU, 0x0A, 0, 9600,MB_PAR_NONE );,我们进去看一下,可以看到,我们初始化的slave address是0x0A,modbus支持1-247个从地址。我们制定的是RTU模式,所以要对RTU一些参数进行赋值,如所调用的初始化函数为 pvMBFrameStartCur = eMBRTUStart; pvMBFrameStopCur = eMBRTUStop; peMBFrameSendCur = eMBRTUSend; peMBFrameReceiveCur =eMBRTUReceive; pvMBFrameCloseCur =MB_PORT_HAS_CLOSE ? vMBPortClose : NULL; pxMBFrameCBByteReceived =xMBRTUReceiveFSM; pxMBFrameCBTransmitterEmpty =xMBRTUTransmitFSM; pxMBPortCBTimerExpired =xMBRTUTimerT35Expired; 其中包括了modbus的启动、停止、发送以及接收数据的函数具体形式,其中比较关心的是xMBRTUReceiveFSM、xMBRTUTransmitFSM这两个函数,规定了modbus状态转换的状态机,其中xMBRTUReceiveFSM为接收状态机,如下图所示:xMBRTUReceiveFSM实现的modbus接收状态机,可以这样描述以上状态转换状态,上电启动或复位进入STATE_RX_INIT状态,为了防止协议栈在初始化过程中就收到串口数据,要放弃这个无效的数据,要先等待一个T35时间,过了这个时间才进入STATE_RX_IDLE状态,开始接收数据。实际上只要注意系统启动顺序,这个问题还是可以避免的。进入STATE_RX_IDLE状态后,从收到第一个字符数据开始启动定时器进入STATE_RX_RCV状态,每接收一个字节都要重新复位定时器开始定时,直到出现一个T35超时。如果接收的数据符合规定的数据格式,那么整帧数据接收完毕,又回到STATE_RX_IDLE状态,如果数据过长那么就到STATE_RX_ERROR状态,等待整帧接收完毕,放弃整个帧然后进入STATE_RX_IDLE状态。当然实现所有状态的状换还需要BOOL xMBRTUTimerT35Expired(void )函数。 其中xMBRTUTransmitFSM为发送状态机,如下图所示:xMBRTUTransmitFSM实现的modbus发送状态机,说到发送状态机就简单多了,因为不用去判断自己发送的时候是否会超时,基本上就是没需求就在STATE_TX_IDLE状态,要发送就进入STATE_TX_XMIT状态。看到xMBRTUTimerT35Expired这里想到一个问题,在timer初始化的时候设定的时基是50us的时间就中断一次,那么modbus是如何实现不同波特率下3.5个字符时间判断的呢?我们看到在timer中断中调用了pxMBPortCBTimerExpired这个函数,但是实际上T35超时函数调用的是xMBRTUTimerT35Expired这个函数,它们之间是什么关系呢?发现在eMBRTUInit中对xMBPortTimersInit( ( USHORT ) usTimerT35_50us)进行了重新初始化,如果波特率大于19200,那么就采用固定的1800us时间,否则就用3.5个字符时间间隔。我觉得之所以在波特率大的时候才用固定的时间可能为了防止出现判断时间过小时误判了正常的线路延迟的原因。(2)运行到eStatus = eMBEnable( )这里。这个比较简单,就是激活协议栈以及串口和定时器而已。(3)运行到( void )eMBPoll( ),进去看一下。这里面比较关键的地方就是if( xMBPortEventGet( &eEvent ) = TRUE )控制了一个事件处理状态机,这就要问了,这个事件处理是个啥玩意?也许进去看看就可以知道了。原来在这个获取事件的玩意还有个叫事件队列的东西*eEvent = eQueuedEvent,原来这是上述两个状态机和一个超时函数中会反馈一些事件例如xMBPortEventPost( EV_FRAME_SENT ),来供调度。具体调度方法不去深究。 六、对modbus RTU模式的接收发送机制分析 明白了RTU模式的运行流程,下面对RTU模式的接收和发送模式的分析。 (1)我们用于使用的接收buf是哪个呢?什么状态时数据是有效的?对于这个问题,我们还得从xMBRTUReceiveFSM入手,可以发现当进入STATE_RX_IDLE状态时可能是一帧数据传输完毕,到mbrtu.c 中的eMBInit里看一下,显然STATE_RX_IDLE接收了第一个字节后,STATE_RX_RCV将剩下的字节放到了ucRTUBuf中,usRcvBufferPos为接收的数据长度,那么肯定应该在经过一个T35的时间后结束的对不对,去看看就知道了。在xMBRTUTimerT35Expired函数中发现了以下语句: case STATE_RX_RCV: xNeedPoll = xMBPortEventPost(EV_FRAME_RECEIVED ); break; 不就说明当eMBPoll 收到EV_FRAME_RECEIVED消息的时候,告诉eMBPoll新的一帧数据来了,要去ucRTUBuf中对usRcvBufferPos的数据进行处理吗,这就对了!我们在返回到eMBPoll这个函数中一探究竟。 现在再看事件探测器if( xMBPortEventGet( &eEvent ) = TRUE ) 就明朗多了,当触发case EV_FRAME_RECEIVED:时,调用peMBFrameReceiveCur去获取消息帧,这个函数的实现是谁呢?就是eMBRTUReceive!打开一看就一目了然。eMBRTUReceive把ucRTUBuf的数据信息取出来通过eStatus =peMBFrameReceiveCur( &ucRcvAddress, &ucMBFrame, &usLength ); 重新赋给了从机地址ucRcvAddress、帧内容开始地址ucMBFrame、数据长度usLength(不包括地址位以及CRC校验位)。 (2)消息取出来了,怎么用?还是回到eMBPoll接着看case EV_FRAME_RECEIVED:接下来干啥了,检查消息是不是给我们的,要是不是广播的消息并且没有发生异常我们就进行处理,我们的地址在eMBInit中已经设定。于是就到了case EV_EXECUTE:状态,接下来就要看发来的数据帧是干什么的了,这要通过功能码来判断。 Freemodbus支持以下功能码:Modbus支持的功能码 如果检测到该帧数据是协议栈支持的功能码,就调用相应的函数进行处理,比如说Read input register,就会调用在mb.c中定义的static xMBFunctionHandler xFuncHandlersMB_FUNC_HANDLERS_MAX这个二维数组中注册的eMBFuncReadInputRegister函数进行处理。还记得我们之前实现的eMBRegInputCB函数有什么区别呢,为什么调用的不是这个函数呢,其实仔细看下eMBFuncReadInputRegister这个函数就行了,原来eMBFuncReadInputRegister调用了eMBRegInputCB这个函数!还以为之前搞错了。 看这个函数之前先看主机发送过来的代码格式:查询输入寄存器命令格式 发送的数据帧里包括从机地址、功能码,寄存器起始地址、以及读寄存器的长度、CRC校验。eMBFuncReadInputRegister要做的事儿就是读出寄存器地址以及寄存器长度后调用eMBRegInputCB,读取成功后返回一个MB_ENOERR状态,表明没有错误发生。 (3)接着看eMBPoll中的case EV_EXECUTE:执行完 eException= xFuncHandlersi.pxHandler( ucMBFrame, &usLength );这个处理函数,寄存器值读出来了,帧格式也写好了,什么地方调用发送函数了呢?找了大半天,终于在串口中断这个地方看到了,原来当串口发送完成时调用了prvvUARTTxReadyISR,又调用了pxMBFrameCBTransmitterEmpty,也就是xMBRTUTransmitFSM发送状态机,当满足STATE_TX_XMIT状态时,调用xMBPortSerialPutByte将数据发送出去。 那么是什么东西使发送状态机从STATE_TX_IDLE变化到了STATE_TX_XMIT状态呢?我们的eMBRTUSend似乎没有用到?那么到eMBRTUSend里面去看看他做了什么。原来它做的事情就是当状态机处于STATE_RX_IDLE接收空闲的时候把发送缓冲区pucSndBufferCur的数据填写好从机地址、对其中数据进行CRC16校验,把状态机置为STATE_TX_XMIT,使能串口。 到底是谁调用了eMBRTUSend呢?真是一个问题接着一个问题呀。搜一下peMBFrameSendCur这个函数,原来还是在eMBPoll中的case EV_EXECUTE:中,如果不是广播消息的话,我们就返回一条消息,于是调用了peMBFrameSendCur。 说了大半天可能都给搞糊涂了,整理下发送和接收的整体思路: 协议栈以及定时器初始化T35第一次超时eMBPoll STATE_RX_IDLE收到数据中断prvvUARTRxISRpxMBFrameCBByteReceivedxMBRTUReceiveFSM接收数据 STATE_RX_RCVT35超时 eMBPollEV_FRAME_RECEIVED(peMBFrameReceiveCur-eMBRTUReceive)提取完整数据帧 eMBPoll case EV_EXECUTE:xFuncHandlersi.pxHandler(eMBRegInputCB)对接收的数据进行处理 peMBFrameSendCureMBRTUSend(&STATE_RX_IDLE)STATE_TX_XMIT 串口发送完成中断 prvvUARTTxReadyISR FSMpxMBFrameCBTransmitterEmptyxMBRTUTransmitFSM(& STATE_TX_XMIT)xMBPortSerialPutByte发送数据。 这样是不是明白了很多,freemodbus状态机写的还是很巧妙的。 七、RTU模式的测试 (1)测试软件 测试软件采用串口调试助手或modbus调试精灵V1.024,外加一个CRC校验码计算工具,如下图所示:串口助手测试modbus (2)测试命令,测试读多个输入寄存器的值,即eMBFuncReadInputRegister发送地址为:0A ,发送命令代码为04,寄存器开始地址为00 00 (GPIOA),寄存器个数为0001(GPIOA)发送0a 04 00 00 00 01 30 B1,无反应,无任何数据返回。 看一下出现这个问题可能的原因。一是modbus接收不到数据帧,这个通过debug已经排除,可以接收到CRC验证码正确的数据帧,那么一定是modbus不能发送数据,先看看简单串口能不能发送数据,在main里通过USART1Write()进行测试发现发送没问题。 在上一节的分析可以看出,只有满足两个条件MB协议栈才可以返回数据,一是当接收的数据处理完调用了eMBRTUSend(&STATE_RX_IDLE)STATE_TX_XMIT使接收状态机进入STATE_TX_XMIT状态;二是串口发送完成中断,从而调用发送状态机进行发送数据。那么先验证第一条,在eMBPoll:peMBFrameSendCur 位置设置断点,看看有没有调用eMBRTUSend。可以看到确实可以满足这一条件,由eMBRTUSend触发STATE_TX_XMIT状态。 排除了这一可能,会不会进不到串口发送完成中断中断呢?同样在中断函数中设置断点查看仿真结果。果然问题出现在无法进入中断语句去调用发送状态机,那么这个中断触发需要满足什么条件呢? /发生发送完成中断 if(USART_GetITStatus(USART1, USART_IT_TC) = SET) prvvUARTTxReadyISR(); /清除中断标志 USART_ClearITPendingBit(USART1, USART_IT_TC); 网上搜了一下,发现在USART的发送端有2个寄存器,一个是程序可以看到的USART_DR寄存器,另一个是程序看不到的移位寄存器,对应USART数据发送有两个标志,一个是TXE=发送数据寄存器空,另一个是TC=发送结束。当USART_DR中的数据传送到移位寄存器后,TXE被设置,此时移位寄存器开始向TX信号线按位传输数据,所有位发送结束时(送出停止位后)硬件会设置TC标志。 试试改成USART_IT_TXE这个标准来引发中断,果然就好用了! 发送0a 04 00 00 0001 30 B1,返回0A 04 02 00 00 1C F1 那么为什么可以进入USART_IT_TXE中断就不能进入USART_IT_TC中断呢? 原来在vMBPortSerialEnable中断控制使能函数中控制的是USART_ITConfig(USART1, USART_IT_TXE, ENABLE)这个中断,并不是USART_IT_TC这个中断,没有使能进了中断才怪呢。 究其USART_IT_TC,即Transmission Complete,需要先发送一个字节后才进入中断,这里称为“发送后中断”。原来是发送完数据引发中断去处理一些事情的意思,而USART_IT_TXE,即发送寄存器空中断,当使能TXEIE后,只要Tx DR空了,就会产生中断。所以,发送完字符串后必须关掉,否则会导致重复进入中断。这也是和TC不同之处。数据是返回来了,但是读取GPIOA的数据怎么是全0呢,这显然不正确,再回到eMBRegInputCB()函数中去看一下,接收的地址怎么从1开始而不是设定的从0开始?去eMBFuncReadInputRegister看一下,原来不知道什么原因解析完地址以后出现了usRegAddress+,百度看别人也有类似的问题导致通信格式不正确,注释即可。到这里关于freemodbus RTU在STM32上的移植就算结束了,移植的工作不多,但想把协议栈是如何工作的搞清楚需要花一点时间,本文如有错误和不妥的地方还希望请多交流指正0. 版权声明:本文著作权归属作者本人所有,提供广大网友学习分析用,如需在发表作品中引用,请联系作者本人。作者ID:smily,百度文库id:mcs3000,电子邮件:。本人保留署名权。如需转载请包含本版权声明。如果本文有不准确之处,欢迎与作者讨论,QQ:83414576。本人不对使用文中技术造成的后果负责。本文分析基于FreeModbus1.50.可以与作者联系获取pdf或者docx格式文档。1. FreeModbus协议分析协议必须首先调用初始化功能eMBinit()函数。后调用eMBEnable(),最后,在循环体或者单独一个任务中调用eMBPoll()函数。2. 应用层协议2.1. 系统的启动2.1.1. eMBInit()函数的源码分析以RTU方式为例,首先,检查调用的地址是否合法。如不合法,返回错误。如果合法则继续执行,首先,针对RTU方式还是ASCII方式,选择不同的编译模块。对需要调用的函数指针进行复制。如果移植需要改变其他用途,则要修改相应的指针,包括如下赋值: pvMBFrameStartCur = eMBRTUStart; pvMBFrameStopCur = eMBRTUStop; peMBFrameSendCur = eMBRTUSend; peMBFrameReceiveCur = eMBRTUReceive; pvMBFrameCloseCur = MB_PORT_HAS_CLOSE ? vMBPortClose : NULL; pxMBFrameCBByteReceived = xMBRTUReceiveFSM; pxMBFrameCBTransmitterEmpty = xMBRTUTransmitFSM; pxMBPortCBTimerExpired = xMBRTUTimerT35Expired;然后调用eStatus = eMBRTUInit( ucMBAddress, ucPort, ulBaudRate, eParity );具体初始化通讯端口。2.1.2. eMBRTUIniteMBRTUInit这个函数主要干两件事:第一, 初始化串口: if( xMBPortSerialInit( ucPort, ulBaudRate, 8, eParity ) != TRUE ) eStatus = MB_EPORTERR;这个函数在portserial.c中,需要用户在移植的时候根据自己的处理器编写。第二, 初始化计时器:首先要根据波特率计算一下是3.55.0个字节周期的时间,然后再调用xMBPortTimersInit( ( USHORT ) usTimerT35_50us ),初始化计时器。这个函数在porttimer.c中,需要用户在移植的时候根据自己的处理器编写。2.1.3. eMBEnable源码分析首先,看看Modbus功能是否是被关闭的,如果不是被关闭(可能是没有被初始化或者已经打开),就返回错误。如果是disable状态,就干下面两件事:l 调用pvMBFrameStartCur()。由于这是个函数指针,在模块eMBInit中,指向了eMBRTUStart函数n 在源代码中有这样一段注释:,意思是,首先设置成STATE_RX_INIT,然后打开计时器,等待t3.5以后,进入STATE_RX_IDLE状态。n 看源代码中,首先有设置Receiver的状态,后调用vMBPortSerialEnable,设置接收状态,然后打开定时器。n 当定时器中断后,自动调用中断服务程序,在中断服务程序中,只调用了pxMBPortCBTimerExpired,而这是一个函数指针,在RTU方式初始化时,被指向了xMBRTUTimerT35Expired()函数。n xMBRTUTimerT35Expired函数在mbrtu.c中,在这里,我们只看第一种方式,就是进入初始化状态,在t35时间以后,只调用了一个xNeedPoll = xMBPortEventPost( EV_READY );n xMBPortEventPost函数就是在事件队列里加了一个EV_RDY事件。l 然后,将eMB状态改为使能状态,l 初始化结束。2.2. 总线侦听eMBPoll()首先,判断系统是否被使能,如果没有,则返回错误值。然后,检查是否有事件发生,如果有,则根据不同类型的事件响应:l 如果是EV_RDY,表示系统刚刚进入侦听状态,则什么都不做;l 如果状态为EV_FRAME_RECEIVED,也就是接收到完整的帧,做下面两件事情:n 调用eStatus=peMBFrameReceiveCur( &ucRcvAddress, &ucMBFrame, &usLength )。这是一个函数指针,在eMBInit中,被初始化指向eMBRTUReceive。n eMBRTUReceive这个函数首先校验帧的长度和CRC,然后从协议中解析出地址、数据和长度。n 然后检查地址,如果是广播地址或者是本机地址,就调用xMBPortEventPost( EV-EXECUTE),将接收器的状态更改为EV_EXECUTE。l 如果状态为EV_EXECUTE,就在函数列表中检查,有没有与命令字段相符合的函数来解析相应则执行该函数,否则返回非法功能代码。2.3. 数据发送发送数据通过指针eMBRTUSend,调用eMBRTUSend函数。2.3.1. eMBRTUSend函数这个函数的作用就是打包,将数据打包成帧。l 首先,检查接收状态。因为MODBUS是基于RS-485半双工通讯,所以当正在接收数据时,不发送该帧。l 如果总线空,就将数据打包,将地址和CRC加入数据帧l 将总线状态改为发送。2.4. 功能注册l 对于指定的功能代码,需要一个功能回调函数来处理,格式如下。eMBException eMXXXXXX ( UCHAR * pucFrame, USHORT * usLen )l 需要通过函数eMBRegi

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论