FreeModbus ASCII传输
首先,在使能modbus協(xié)議棧的時(shí)候,會(huì)調(diào)用pvMBFrameStartCur函數(shù)
/* 使能modbus */ eMBErrorCode eMBEnable(void) {eMBErrorCode eStatus = MB_ENOERR;/* modbus還未使能 */if(eMBState == STATE_DISABLED){/* 啟動(dòng)modbus */pvMBFrameStartCur();/* 設(shè)置modbus狀態(tài)為使能 */eMBState = STATE_ENABLED;}else{/* 狀態(tài)不合法 */eStatus = MB_EILLSTATE;}return eStatus; }?在rtu模式下pvMBFrameStartCur指針指向eMBASCIIStart函數(shù)
/* modbus ascii啟動(dòng)函數(shù) */ void eMBASCIIStart(void) {ENTER_CRITICAL_SECTION();/* 串口打開接收、關(guān)閉發(fā)送 */vMBPortSerialEnable(TRUE, FALSE);/* 接收狀態(tài)設(shè)置為接收空閑 */eRcvState = STATE_RX_IDLE;EXIT_CRITICAL_SECTION();/* 發(fā)送就緒事件 */(void)xMBPortEventPost(EV_READY); }啟動(dòng)RTU時(shí),接收狀態(tài)eRcvState 設(shè)置為接收空閑態(tài)STATE_RX_IDLE,打開接收中斷,向主程序發(fā)送就緒事件。
?
主程序接收到就緒事件后什么也沒做?
/* modbus輪詢 */ eMBErrorCode eMBPoll(void) {....../* 獲取事件 */if(xMBPortEventGet(&eEvent) == TRUE){/* 判斷事件類型 */switch(eEvent){/* 就緒事件 */case EV_READY:break;......}}return MB_ENOERR; }?
ASCII傳輸模式下,modbus報(bào)文包含明確的起始字符和結(jié)束字符
因此在接收空閑態(tài)下,如果接收到數(shù)據(jù),先判斷是否為起始字符。如果是起始字符,使能超時(shí)定時(shí)器,將接收狀態(tài)eRcvState切換為接收態(tài)STATE_RX_RCV。由于ASCII模式下每個(gè)字節(jié)需要兩個(gè)字符編碼,所以需要來回切換高半字和低半字,一開始將字節(jié)位置初始化為高半字。
/* modbus ascii接收一個(gè)字節(jié)函數(shù) */ BOOL xMBASCIIReceiveFSM(void) {BOOL xNeedPoll = FALSE;UCHAR ucByte;UCHAR ucResult;assert_param(eSndState == STATE_TX_IDLE);/* 串口接收一個(gè)字節(jié) */(void)xMBPortSerialGetByte((CHAR *) & ucByte);/* 判斷接收狀態(tài) */switch(eRcvState){....../* 接收空閑狀態(tài) */case STATE_RX_IDLE:/* 起始字符 */if(ucByte == ':'){/* 超時(shí)定時(shí)器使能 */vMBPortTimersEnable();/* 接收緩沖區(qū)偏移量初始化為0 */usRcvBufferPos = 0;/* 字節(jié)位置初始化高半字節(jié) */eBytePos = BYTE_HIGH_NIBBLE;/* 將接收狀態(tài)切換為接收態(tài) */eRcvState = STATE_RX_RCV;}break;}return xNeedPoll; }在接收態(tài)下,將一個(gè)一個(gè)字節(jié)接收數(shù)據(jù)。直到接收到結(jié)束字符CR,將接收狀態(tài)eRcvState切換為等待結(jié)束態(tài)STATE_RX_WAIT_EOF。
/* modbus ascii接收一個(gè)字節(jié)函數(shù) */ BOOL xMBASCIIReceiveFSM(void) {BOOL xNeedPoll = FALSE;UCHAR ucByte;UCHAR ucResult;assert_param(eSndState == STATE_TX_IDLE);/* 串口接收一個(gè)字節(jié) */(void)xMBPortSerialGetByte((CHAR *) & ucByte);/* 判斷接收狀態(tài) */switch(eRcvState){/* 接收態(tài) */case STATE_RX_RCV:/* 超時(shí)定時(shí)器使能 */vMBPortTimersEnable();/* 起始字符 */if(ucByte == ':'){/* 字節(jié)位置重新初始化高半字節(jié) */eBytePos = BYTE_HIGH_NIBBLE;/* 接收緩沖區(qū)偏移量重新初始化為0 */usRcvBufferPos = 0;}/* 結(jié)束字符,CR */else if(ucByte == MB_ASCII_DEFAULT_CR){/* 將接收狀態(tài)設(shè)置為等待LF狀態(tài) */eRcvState = STATE_RX_WAIT_EOF;}/* 普通數(shù)據(jù) */else{/* 將ascii碼轉(zhuǎn)換為數(shù)字 */ucResult = prvucMBCHAR2BIN(ucByte);/* 判斷高半字節(jié)還是低半字節(jié) */switch(eBytePos){/* 高半字節(jié) */case BYTE_HIGH_NIBBLE:/* 數(shù)據(jù)幀最大256字節(jié) */if(usRcvBufferPos < MB_SER_PDU_SIZE_MAX){/* 將數(shù)據(jù)填入高半字節(jié) */ucASCIIBuf[usRcvBufferPos] = (UCHAR)(ucResult << 4);/* 字節(jié)位置設(shè)置為低半字節(jié) */eBytePos = BYTE_LOW_NIBBLE;break;}/* 接收字節(jié)數(shù)超過256字節(jié) */else{/* 接收狀態(tài)設(shè)置為空閑態(tài) */eRcvState = STATE_RX_IDLE;/* 超時(shí)定時(shí)器關(guān)閉 */vMBPortTimersDisable();}break;/* 低半字節(jié) */case BYTE_LOW_NIBBLE:/* 將數(shù)據(jù)填入低半字節(jié) */ucASCIIBuf[usRcvBufferPos] |= ucResult;/* 字節(jié)數(shù)加一 */usRcvBufferPos++;/* 字節(jié)位置設(shè)置為高半字節(jié) */eBytePos = BYTE_HIGH_NIBBLE;break;}}break;......}return xNeedPoll; }在等待結(jié)束態(tài)下,如果接收到LF字符,則表示一幀數(shù)據(jù)結(jié)束。關(guān)閉超時(shí)定時(shí)器,將接收狀態(tài)eRcvState切換為接收空閑態(tài)STATE_RX_IDLE,向主程序發(fā)送接收完成事件。
/* modbus ascii接收一個(gè)字節(jié)函數(shù) */ BOOL xMBASCIIReceiveFSM(void) {BOOL xNeedPoll = FALSE;UCHAR ucByte;UCHAR ucResult;assert_param(eSndState == STATE_TX_IDLE);/* 串口接收一個(gè)字節(jié) */(void)xMBPortSerialGetByte((CHAR *) & ucByte);/* 判斷接收狀態(tài) */switch(eRcvState){......./* 等待LF狀態(tài) */case STATE_RX_WAIT_EOF:/* 檢查該字節(jié)是否為LF */if(ucByte == ucMBLFCharacter){/* 超時(shí)定時(shí)器關(guān)閉 */vMBPortTimersDisable();/* 將接收狀態(tài)設(shè)置為接收空閑狀態(tài) */eRcvState = STATE_RX_IDLE;/* 發(fā)送接收完成事件 */xNeedPoll = xMBPortEventPost(EV_FRAME_RECEIVED);}/* 該字節(jié)為起始字符 */else if(ucByte == ':'){/* 字節(jié)位置重新初始化高半字節(jié) */eBytePos = BYTE_HIGH_NIBBLE;/* 接收緩沖區(qū)偏移量重新初始化為0 */usRcvBufferPos = 0;/* 將接收狀態(tài)重新切換為接收態(tài) */eRcvState = STATE_RX_RCV;/* 超時(shí)定時(shí)器使能 */vMBPortTimersEnable();}/* 該字節(jié)不是LF字符也不是LF字符 */else{/* 將接收狀態(tài)設(shè)置為接收空閑狀態(tài) */eRcvState = STATE_RX_IDLE;}break;......}return xNeedPoll; }?
主程序接收到接收完成事件之后,對(duì)數(shù)據(jù)幀進(jìn)行校驗(yàn)和拆解,最后會(huì)得到PDU數(shù)據(jù)的指針和長度。并向主程序發(fā)送執(zhí)行事件。
/* modbus輪詢 */ eMBErrorCode eMBPoll(void) {....../* 獲取事件 */if(xMBPortEventGet(&eEvent) == TRUE){/* 判斷事件類型 */switch(eEvent){....../* 接收完成事件 */case EV_FRAME_RECEIVED:/* modbus接收函數(shù),獲取地址、PDU指針、PDU長度 */eStatus = peMBFrameReceiveCur(&ucRcvAddress, &ucMBFrame, &usLength);if(eStatus == MB_ENOERR){/* 判斷地址是否吻合 */if((ucRcvAddress == ucMBAddress) || (ucRcvAddress == MB_ADDRESS_BROADCAST)){/* 發(fā)送執(zhí)行事件 */(void)xMBPortEventPost(EV_EXECUTE);}}break;......}}return MB_ENOERR; }下面看一下peMBFrameReceiveCur調(diào)用的eMBASCIIReceive函數(shù)。主要工作是,對(duì)數(shù)據(jù)幀進(jìn)行LRC校驗(yàn),然后對(duì)數(shù)據(jù)幀進(jìn)行拆分。
/* modbus ascii接收函數(shù) */ eMBErrorCode eMBASCIIReceive(UCHAR *pucRcvAddress, UCHAR **pucFrame, USHORT *pusLength) {eMBErrorCode eStatus = MB_ENOERR;ENTER_CRITICAL_SECTION();assert_param(usRcvBufferPos < MB_SER_PDU_SIZE_MAX);/* ASCII數(shù)據(jù)幀最小3字節(jié),進(jìn)行LRC校驗(yàn) */if((usRcvBufferPos >= MB_SER_PDU_SIZE_MIN) && (prvucMBLRC((UCHAR *)ucASCIIBuf, usRcvBufferPos) == 0)){/* 從機(jī)地址 */*pucRcvAddress = ucASCIIBuf[MB_SER_PDU_ADDR_OFF];/* PDU長度=ADU長度-1字節(jié)(地址)-1字節(jié)(LRC) */*pusLength = (USHORT)(usRcvBufferPos - MB_SER_PDU_PDU_OFF - MB_SER_PDU_SIZE_LRC);/* PDU數(shù)據(jù)指針 */*pucFrame = (UCHAR *)&ucASCIIBuf[MB_SER_PDU_PDU_OFF];}/* 檢驗(yàn)失敗 */else{/* IO錯(cuò)誤 */eStatus = MB_EIO;}EXIT_CRITICAL_SECTION();return eStatus; }?
主程序接收到執(zhí)行事件之后,判斷功能碼,調(diào)用相應(yīng)功能函數(shù)。然后對(duì)主機(jī)進(jìn)行響應(yīng)。
/* modbus輪詢 */ eMBErrorCode eMBPoll(void) {....../* 獲取事件 */if(xMBPortEventGet(&eEvent) == TRUE){/* 判斷事件類型 */switch(eEvent){....../* 執(zhí)行事件 */case EV_EXECUTE:/* 功能碼 */ucFunctionCode = ucMBFrame[MB_PDU_FUNC_OFF];eException = MB_EX_ILLEGAL_FUNCTION;/* 遍歷所有支持的功能碼 */for(i = 0; i < MB_FUNC_HANDLERS_MAX; i++){/* 遍歷完了 */if(xFuncHandlers[i].ucFunctionCode == 0){break;}/* 匹配到合適的功能碼 */else if(xFuncHandlers[i].ucFunctionCode == ucFunctionCode){/* 調(diào)用相關(guān)功能 */eException = xFuncHandlers[i].pxHandler(ucMBFrame, &usLength);break;}}/* 不是廣播 */if(ucRcvAddress != MB_ADDRESS_BROADCAST){/* 出現(xiàn)異常 */if(eException != MB_EX_NONE){/* PDU長度初始化為0 */usLength = 0;/* 功能碼+0x80則表示異常 */ucMBFrame[usLength++] = (UCHAR)(ucFunctionCode | MB_FUNC_ERROR);/* 異常碼 */ucMBFrame[usLength++] = eException;}if((eMBCurrentMode == MB_ASCII) && MB_ASCII_TIMEOUT_WAIT_BEFORE_SEND_MS){vMBPortTimersDelay(MB_ASCII_TIMEOUT_WAIT_BEFORE_SEND_MS);} /* 發(fā)送響應(yīng)幀 */eStatus = peMBFrameSendCur(ucMBAddress, ucMBFrame, usLength);}break;......}}return MB_ENOERR; }
peMBFrameSendCur指針調(diào)用eMBASCIISend對(duì)主機(jī)進(jìn)行響應(yīng)。主要工作包括:將PDU封裝為ADU數(shù)據(jù),將發(fā)送狀態(tài)eSndState切換為發(fā)送開始態(tài)STATE_TX_START,并啟動(dòng)發(fā)送,發(fā)送一個(gè)字節(jié)。
?
在發(fā)送開始態(tài)下,發(fā)送起始字符。然后將發(fā)送狀態(tài)eSndState切換為發(fā)送數(shù)據(jù)態(tài)STATE_TX_DATA,將字節(jié)位置初始化為高半字節(jié)。
/* modbus ascii發(fā)送一個(gè)字節(jié)函數(shù) */ BOOL xMBASCIITransmitFSM(void) {BOOL xNeedPoll = FALSE;UCHAR ucByte;assert_param(eRcvState == STATE_RX_IDLE);/* 判斷發(fā)送狀態(tài) */switch(eSndState){/* 發(fā)送開始狀態(tài) */case STATE_TX_START:/* 發(fā)送起始字符 */ucByte = ':';xMBPortSerialPutByte((CHAR)ucByte);/* 發(fā)送狀態(tài)設(shè)置為發(fā)送態(tài) */eSndState = STATE_TX_DATA;/* 字節(jié)位置初始化高半字節(jié) */eBytePos = BYTE_HIGH_NIBBLE;break;......}return xNeedPoll; }在發(fā)送數(shù)據(jù)態(tài)下,將數(shù)據(jù)一個(gè)字節(jié)一個(gè)字節(jié)發(fā)送出去。直到數(shù)據(jù)都發(fā)送完畢,發(fā)送結(jié)束字符CR,將發(fā)送狀態(tài)eSndState設(shè)置為發(fā)送結(jié)束態(tài)STATE_TX_END。
/* modbus ascii發(fā)送一個(gè)字節(jié)函數(shù) */ BOOL xMBASCIITransmitFSM(void) {BOOL xNeedPoll = FALSE;UCHAR ucByte;assert_param(eRcvState == STATE_RX_IDLE);/* 判斷發(fā)送狀態(tài) */switch(eSndState){....../* 發(fā)送態(tài) */case STATE_TX_DATA:/* 還有數(shù)據(jù)未發(fā)送 */if(usSndBufferCount > 0){/* 判斷字節(jié)位置 */switch(eBytePos){/* 高半字節(jié) */case BYTE_HIGH_NIBBLE:/* 將數(shù)字轉(zhuǎn)換為ascii碼 */ucByte = prvucMBBIN2CHAR((UCHAR)(*pucSndBufferCur >> 4));/* 將數(shù)據(jù)發(fā)送出去 */xMBPortSerialPutByte((CHAR)ucByte);/* 字節(jié)位置設(shè)置為低半字節(jié) */eBytePos = BYTE_LOW_NIBBLE;break;/* 低半字節(jié) */case BYTE_LOW_NIBBLE:/* 將數(shù)字轉(zhuǎn)換為ascii碼 */ucByte = prvucMBBIN2CHAR((UCHAR)(*pucSndBufferCur & 0x0F));/* 將數(shù)據(jù)發(fā)送出去 */xMBPortSerialPutByte((CHAR)ucByte);/* 將指針向后偏移一個(gè) */pucSndBufferCur++;/* 字節(jié)位置設(shè)置為高半字節(jié) */eBytePos = BYTE_HIGH_NIBBLE;/* 剩余字節(jié)數(shù)減一 */usSndBufferCount--;break;}}/* 數(shù)據(jù)發(fā)完 */else{/* 發(fā)送結(jié)束字符,CR */xMBPortSerialPutByte(MB_ASCII_DEFAULT_CR);/* 將發(fā)送狀態(tài)設(shè)置為發(fā)送結(jié)束態(tài) */eSndState = STATE_TX_END;}break;......}return xNeedPoll; }在發(fā)送結(jié)束態(tài)下,發(fā)送結(jié)束字符LR,將發(fā)送狀態(tài)切換為通知態(tài)。
/* modbus ascii發(fā)送一個(gè)字節(jié)函數(shù) */ BOOL xMBASCIITransmitFSM(void) {BOOL xNeedPoll = FALSE;UCHAR ucByte;assert_param(eRcvState == STATE_RX_IDLE);/* 判斷發(fā)送狀態(tài) */switch(eSndState){....../* 發(fā)送結(jié)束態(tài) */case STATE_TX_END:/* 發(fā)送結(jié)束字符,LF */xMBPortSerialPutByte((CHAR)ucMBLFCharacter);/* 發(fā)送狀態(tài)設(shè)置為通知態(tài) */eSndState = STATE_TX_NOTIFY;break;......}return xNeedPoll; }在通知態(tài)下,將發(fā)送狀態(tài)eSndState切換為發(fā)送空閑態(tài)STATE_TX_IDLE,打開接收中斷,通知主程序發(fā)送完畢事件
/* modbus ascii發(fā)送一個(gè)字節(jié)函數(shù) */ BOOL xMBASCIITransmitFSM(void) {BOOL xNeedPoll = FALSE;UCHAR ucByte;assert_param(eRcvState == STATE_RX_IDLE);/* 判斷發(fā)送狀態(tài) */switch(eSndState){....../* 通知態(tài) */case STATE_TX_NOTIFY:/* 將發(fā)送狀態(tài)設(shè)置為空閑態(tài) */eSndState = STATE_TX_IDLE;/* 發(fā)送發(fā)送完成事件 */xNeedPoll = xMBPortEventPost(EV_FRAME_SENT);/* 串口接收啟動(dòng)、發(fā)送關(guān)閉 */vMBPortSerialEnable(TRUE, FALSE);/* 將發(fā)送狀態(tài)設(shè)置為空閑態(tài) */eSndState = STATE_TX_IDLE;break;......}return xNeedPoll; }?
主程序接收到發(fā)送完畢事件后,什么也不做
/* modbus輪詢 */ eMBErrorCode eMBPoll(void) {....../* 獲取事件 */if(xMBPortEventGet(&eEvent) == TRUE){/* 判斷事件類型 */switch(eEvent){....../* 發(fā)送完成 */case EV_FRAME_SENT:break;}}return MB_ENOERR; }?
總結(jié)
以上是生活随笔為你收集整理的FreeModbus ASCII传输的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: framebuffer驱动
- 下一篇: 2018生活消费趋势:越来越多95后开始