日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

FreeModbus ASCII传输

發布時間:2025/3/15 编程问答 36 豆豆
生活随笔 收集整理的這篇文章主要介紹了 FreeModbus ASCII传输 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

首先,在使能modbus協議棧的時候,會調用pvMBFrameStartCur函數

/* 使能modbus */ eMBErrorCode eMBEnable(void) {eMBErrorCode eStatus = MB_ENOERR;/* modbus還未使能 */if(eMBState == STATE_DISABLED){/* 啟動modbus */pvMBFrameStartCur();/* 設置modbus狀態為使能 */eMBState = STATE_ENABLED;}else{/* 狀態不合法 */eStatus = MB_EILLSTATE;}return eStatus; }

?在rtu模式下pvMBFrameStartCur指針指向eMBASCIIStart函數

/* modbus ascii啟動函數 */ void eMBASCIIStart(void) {ENTER_CRITICAL_SECTION();/* 串口打開接收、關閉發送 */vMBPortSerialEnable(TRUE, FALSE);/* 接收狀態設置為接收空閑 */eRcvState = STATE_RX_IDLE;EXIT_CRITICAL_SECTION();/* 發送就緒事件 */(void)xMBPortEventPost(EV_READY); }

啟動RTU時,接收狀態eRcvState 設置為接收空閑態STATE_RX_IDLE,打開接收中斷,向主程序發送就緒事件。

?

主程序接收到就緒事件后什么也沒做?

/* modbus輪詢 */ eMBErrorCode eMBPoll(void) {....../* 獲取事件 */if(xMBPortEventGet(&eEvent) == TRUE){/* 判斷事件類型 */switch(eEvent){/* 就緒事件 */case EV_READY:break;......}}return MB_ENOERR; }

?

ASCII傳輸模式下,modbus報文包含明確的起始字符和結束字符

因此在接收空閑態下,如果接收到數據,先判斷是否為起始字符。如果是起始字符,使能超時定時器,將接收狀態eRcvState切換為接收態STATE_RX_RCV。由于ASCII模式下每個字節需要兩個字符編碼,所以需要來回切換高半字和低半字,一開始將字節位置初始化為高半字。

/* modbus ascii接收一個字節函數 */ BOOL xMBASCIIReceiveFSM(void) {BOOL xNeedPoll = FALSE;UCHAR ucByte;UCHAR ucResult;assert_param(eSndState == STATE_TX_IDLE);/* 串口接收一個字節 */(void)xMBPortSerialGetByte((CHAR *) & ucByte);/* 判斷接收狀態 */switch(eRcvState){....../* 接收空閑狀態 */case STATE_RX_IDLE:/* 起始字符 */if(ucByte == ':'){/* 超時定時器使能 */vMBPortTimersEnable();/* 接收緩沖區偏移量初始化為0 */usRcvBufferPos = 0;/* 字節位置初始化高半字節 */eBytePos = BYTE_HIGH_NIBBLE;/* 將接收狀態切換為接收態 */eRcvState = STATE_RX_RCV;}break;}return xNeedPoll; }

在接收態下,將一個一個字節接收數據。直到接收到結束字符CR,將接收狀態eRcvState切換為等待結束態STATE_RX_WAIT_EOF。

/* modbus ascii接收一個字節函數 */ BOOL xMBASCIIReceiveFSM(void) {BOOL xNeedPoll = FALSE;UCHAR ucByte;UCHAR ucResult;assert_param(eSndState == STATE_TX_IDLE);/* 串口接收一個字節 */(void)xMBPortSerialGetByte((CHAR *) & ucByte);/* 判斷接收狀態 */switch(eRcvState){/* 接收態 */case STATE_RX_RCV:/* 超時定時器使能 */vMBPortTimersEnable();/* 起始字符 */if(ucByte == ':'){/* 字節位置重新初始化高半字節 */eBytePos = BYTE_HIGH_NIBBLE;/* 接收緩沖區偏移量重新初始化為0 */usRcvBufferPos = 0;}/* 結束字符,CR */else if(ucByte == MB_ASCII_DEFAULT_CR){/* 將接收狀態設置為等待LF狀態 */eRcvState = STATE_RX_WAIT_EOF;}/* 普通數據 */else{/* 將ascii碼轉換為數字 */ucResult = prvucMBCHAR2BIN(ucByte);/* 判斷高半字節還是低半字節 */switch(eBytePos){/* 高半字節 */case BYTE_HIGH_NIBBLE:/* 數據幀最大256字節 */if(usRcvBufferPos < MB_SER_PDU_SIZE_MAX){/* 將數據填入高半字節 */ucASCIIBuf[usRcvBufferPos] = (UCHAR)(ucResult << 4);/* 字節位置設置為低半字節 */eBytePos = BYTE_LOW_NIBBLE;break;}/* 接收字節數超過256字節 */else{/* 接收狀態設置為空閑態 */eRcvState = STATE_RX_IDLE;/* 超時定時器關閉 */vMBPortTimersDisable();}break;/* 低半字節 */case BYTE_LOW_NIBBLE:/* 將數據填入低半字節 */ucASCIIBuf[usRcvBufferPos] |= ucResult;/* 字節數加一 */usRcvBufferPos++;/* 字節位置設置為高半字節 */eBytePos = BYTE_HIGH_NIBBLE;break;}}break;......}return xNeedPoll; }

在等待結束態下,如果接收到LF字符,則表示一幀數據結束。關閉超時定時器,將接收狀態eRcvState切換為接收空閑態STATE_RX_IDLE,向主程序發送接收完成事件。

/* modbus ascii接收一個字節函數 */ BOOL xMBASCIIReceiveFSM(void) {BOOL xNeedPoll = FALSE;UCHAR ucByte;UCHAR ucResult;assert_param(eSndState == STATE_TX_IDLE);/* 串口接收一個字節 */(void)xMBPortSerialGetByte((CHAR *) & ucByte);/* 判斷接收狀態 */switch(eRcvState){......./* 等待LF狀態 */case STATE_RX_WAIT_EOF:/* 檢查該字節是否為LF */if(ucByte == ucMBLFCharacter){/* 超時定時器關閉 */vMBPortTimersDisable();/* 將接收狀態設置為接收空閑狀態 */eRcvState = STATE_RX_IDLE;/* 發送接收完成事件 */xNeedPoll = xMBPortEventPost(EV_FRAME_RECEIVED);}/* 該字節為起始字符 */else if(ucByte == ':'){/* 字節位置重新初始化高半字節 */eBytePos = BYTE_HIGH_NIBBLE;/* 接收緩沖區偏移量重新初始化為0 */usRcvBufferPos = 0;/* 將接收狀態重新切換為接收態 */eRcvState = STATE_RX_RCV;/* 超時定時器使能 */vMBPortTimersEnable();}/* 該字節不是LF字符也不是LF字符 */else{/* 將接收狀態設置為接收空閑狀態 */eRcvState = STATE_RX_IDLE;}break;......}return xNeedPoll; }

?

主程序接收到接收完成事件之后,對數據幀進行校驗和拆解,最后會得到PDU數據的指針和長度。并向主程序發送執行事件。

/* modbus輪詢 */ eMBErrorCode eMBPoll(void) {....../* 獲取事件 */if(xMBPortEventGet(&eEvent) == TRUE){/* 判斷事件類型 */switch(eEvent){....../* 接收完成事件 */case EV_FRAME_RECEIVED:/* modbus接收函數,獲取地址、PDU指針、PDU長度 */eStatus = peMBFrameReceiveCur(&ucRcvAddress, &ucMBFrame, &usLength);if(eStatus == MB_ENOERR){/* 判斷地址是否吻合 */if((ucRcvAddress == ucMBAddress) || (ucRcvAddress == MB_ADDRESS_BROADCAST)){/* 發送執行事件 */(void)xMBPortEventPost(EV_EXECUTE);}}break;......}}return MB_ENOERR; }

下面看一下peMBFrameReceiveCur調用的eMBASCIIReceive函數。主要工作是,對數據幀進行LRC校驗,然后對數據幀進行拆分。

/* modbus ascii接收函數 */ eMBErrorCode eMBASCIIReceive(UCHAR *pucRcvAddress, UCHAR **pucFrame, USHORT *pusLength) {eMBErrorCode eStatus = MB_ENOERR;ENTER_CRITICAL_SECTION();assert_param(usRcvBufferPos < MB_SER_PDU_SIZE_MAX);/* ASCII數據幀最小3字節,進行LRC校驗 */if((usRcvBufferPos >= MB_SER_PDU_SIZE_MIN) && (prvucMBLRC((UCHAR *)ucASCIIBuf, usRcvBufferPos) == 0)){/* 從機地址 */*pucRcvAddress = ucASCIIBuf[MB_SER_PDU_ADDR_OFF];/* PDU長度=ADU長度-1字節(地址)-1字節(LRC) */*pusLength = (USHORT)(usRcvBufferPos - MB_SER_PDU_PDU_OFF - MB_SER_PDU_SIZE_LRC);/* PDU數據指針 */*pucFrame = (UCHAR *)&ucASCIIBuf[MB_SER_PDU_PDU_OFF];}/* 檢驗失敗 */else{/* IO錯誤 */eStatus = MB_EIO;}EXIT_CRITICAL_SECTION();return eStatus; }

?

主程序接收到執行事件之后,判斷功能碼,調用相應功能函數。然后對主機進行響應。

/* modbus輪詢 */ eMBErrorCode eMBPoll(void) {....../* 獲取事件 */if(xMBPortEventGet(&eEvent) == TRUE){/* 判斷事件類型 */switch(eEvent){....../* 執行事件 */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){/* 調用相關功能 */eException = xFuncHandlers[i].pxHandler(ucMBFrame, &usLength);break;}}/* 不是廣播 */if(ucRcvAddress != MB_ADDRESS_BROADCAST){/* 出現異常 */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);} /* 發送響應幀 */eStatus = peMBFrameSendCur(ucMBAddress, ucMBFrame, usLength);}break;......}}return MB_ENOERR; }


peMBFrameSendCur指針調用eMBASCIISend對主機進行響應。主要工作包括:將PDU封裝為ADU數據,將發送狀態eSndState切換為發送開始態STATE_TX_START,并啟動發送,發送一個字節。

/* modbus ascii發送函數 */ eMBErrorCode eMBASCIISend(UCHAR ucSlaveAddress, const UCHAR *pucFrame, USHORT usLength) {eMBErrorCode eStatus = MB_ENOERR;UCHAR usLRC;ENTER_CRITICAL_SECTION();/* 接收空閑狀態 */if(eRcvState == STATE_RX_IDLE){/* 將指針偏移到ADU */pucSndBufferCur = (UCHAR *)pucFrame - 1;/* 1字節(從機地址) */usSndBufferCount = 1;/* 從機地址 */pucSndBufferCur[MB_SER_PDU_ADDR_OFF] = ucSlaveAddress;/* PDU長度 */usSndBufferCount += usLength;/* LRC */usLRC = prvucMBLRC((UCHAR *)pucSndBufferCur, usSndBufferCount);ucASCIIBuf[usSndBufferCount++] = usLRC;/* 發送開始狀態 */eSndState = STATE_TX_START;/* 串口啟動,使能發送 */vMBPortSerialEnable(FALSE, TRUE);}/* 接收不在空閑狀態不能發送 */else{/* IO錯誤 */eStatus = MB_EIO;}EXIT_CRITICAL_SECTION();return eStatus; }

?

在發送開始態下,發送起始字符。然后將發送狀態eSndState切換為發送數據態STATE_TX_DATA,將字節位置初始化為高半字節。

/* modbus ascii發送一個字節函數 */ BOOL xMBASCIITransmitFSM(void) {BOOL xNeedPoll = FALSE;UCHAR ucByte;assert_param(eRcvState == STATE_RX_IDLE);/* 判斷發送狀態 */switch(eSndState){/* 發送開始狀態 */case STATE_TX_START:/* 發送起始字符 */ucByte = ':';xMBPortSerialPutByte((CHAR)ucByte);/* 發送狀態設置為發送態 */eSndState = STATE_TX_DATA;/* 字節位置初始化高半字節 */eBytePos = BYTE_HIGH_NIBBLE;break;......}return xNeedPoll; }

在發送數據態下,將數據一個字節一個字節發送出去。直到數據都發送完畢,發送結束字符CR,將發送狀態eSndState設置為發送結束態STATE_TX_END。

/* modbus ascii發送一個字節函數 */ BOOL xMBASCIITransmitFSM(void) {BOOL xNeedPoll = FALSE;UCHAR ucByte;assert_param(eRcvState == STATE_RX_IDLE);/* 判斷發送狀態 */switch(eSndState){....../* 發送態 */case STATE_TX_DATA:/* 還有數據未發送 */if(usSndBufferCount > 0){/* 判斷字節位置 */switch(eBytePos){/* 高半字節 */case BYTE_HIGH_NIBBLE:/* 將數字轉換為ascii碼 */ucByte = prvucMBBIN2CHAR((UCHAR)(*pucSndBufferCur >> 4));/* 將數據發送出去 */xMBPortSerialPutByte((CHAR)ucByte);/* 字節位置設置為低半字節 */eBytePos = BYTE_LOW_NIBBLE;break;/* 低半字節 */case BYTE_LOW_NIBBLE:/* 將數字轉換為ascii碼 */ucByte = prvucMBBIN2CHAR((UCHAR)(*pucSndBufferCur & 0x0F));/* 將數據發送出去 */xMBPortSerialPutByte((CHAR)ucByte);/* 將指針向后偏移一個 */pucSndBufferCur++;/* 字節位置設置為高半字節 */eBytePos = BYTE_HIGH_NIBBLE;/* 剩余字節數減一 */usSndBufferCount--;break;}}/* 數據發完 */else{/* 發送結束字符,CR */xMBPortSerialPutByte(MB_ASCII_DEFAULT_CR);/* 將發送狀態設置為發送結束態 */eSndState = STATE_TX_END;}break;......}return xNeedPoll; }

在發送結束態下,發送結束字符LR,將發送狀態切換為通知態。

/* modbus ascii發送一個字節函數 */ BOOL xMBASCIITransmitFSM(void) {BOOL xNeedPoll = FALSE;UCHAR ucByte;assert_param(eRcvState == STATE_RX_IDLE);/* 判斷發送狀態 */switch(eSndState){....../* 發送結束態 */case STATE_TX_END:/* 發送結束字符,LF */xMBPortSerialPutByte((CHAR)ucMBLFCharacter);/* 發送狀態設置為通知態 */eSndState = STATE_TX_NOTIFY;break;......}return xNeedPoll; }

在通知態下,將發送狀態eSndState切換為發送空閑態STATE_TX_IDLE,打開接收中斷,通知主程序發送完畢事件

/* modbus ascii發送一個字節函數 */ BOOL xMBASCIITransmitFSM(void) {BOOL xNeedPoll = FALSE;UCHAR ucByte;assert_param(eRcvState == STATE_RX_IDLE);/* 判斷發送狀態 */switch(eSndState){....../* 通知態 */case STATE_TX_NOTIFY:/* 將發送狀態設置為空閑態 */eSndState = STATE_TX_IDLE;/* 發送發送完成事件 */xNeedPoll = xMBPortEventPost(EV_FRAME_SENT);/* 串口接收啟動、發送關閉 */vMBPortSerialEnable(TRUE, FALSE);/* 將發送狀態設置為空閑態 */eSndState = STATE_TX_IDLE;break;......}return xNeedPoll; }

?

主程序接收到發送完畢事件后,什么也不做

/* modbus輪詢 */ eMBErrorCode eMBPoll(void) {....../* 獲取事件 */if(xMBPortEventGet(&eEvent) == TRUE){/* 判斷事件類型 */switch(eEvent){....../* 發送完成 */case EV_FRAME_SENT:break;}}return MB_ENOERR; }

?

總結

以上是生活随笔為你收集整理的FreeModbus ASCII传输的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。

主站蜘蛛池模板: 第一宅男av导航入口 | 精品久久久久久无码中文野结衣 | 羞羞答答一区 | 国产精品熟女久久久久久 | 边添小泬边狠狠躁视频 | 免费毛片小视频 | 18色av | 秋霞欧洲 | 国产经典毛片 | 免费成人看视频 | 香蕉伊人网 | 精品国产一区二区三区四区阿崩 | 亚洲丝袜一区 | 有码av在线 | 精品一二三 | aaa影院| 制服丝袜第二页 | 精品在线一区二区 | 黄色av导航 | 亚洲精品国产精品乱码在线观看 | 免费一级欧美片在线播放 | www 在线观看视频 | 美女写真福利视频 | 乱h伦h女h在线视频 99999视频 | 欧美啪视频 | 亚洲AV无码久久精品国产一区 | 国产精品久久777777毛茸茸 | 国产精品久久伊人 | 久久.com | 国产ts系列 | 日本国产视频 | 国产艳俗歌舞表演hd | 69成人免费视频 | 亚洲精品乱码久久久久久久久久久久 | 夜夜骑夜夜操 | 欧美一区二区福利 | av性天堂网 | 国产黄色激情视频 | 国产真人做爰毛片视频直播 | 黑人巨大精品欧美一区二区免费 | 国产区在线观看 | 国产精品视频一区二区三区 | 51热门大瓜今日大瓜 | 亚洲人人插 | 欧美精品免费看 | 亚洲伦理视频 | 999免费 | 国产精品综合网 | juliaann第一次和老师 | 亚洲操 | 日本不卡一区二区三区在线观看 | 欧美手机在线 | 国内偷拍一区二区 | 日韩中文字幕在线一区 | 精品动漫一区二区三区的观看方式 | 美女网站在线免费观看 | 亚洲乱妇老熟女爽到高潮的片 | 少妇人妻偷人精品视频蜜桃 | 激情五月激情 | 国产裸体永久免费无遮挡 | 天天添 | 中文字幕精品一区二区三区精品 | 超碰人人在线观看 | 日本成人在线免费视频 | 天堂av在线中文 | 神马午夜嘿嘿 | 亚洲成av人片在www色猫咪 | 天天插日日插 | 色久综合 | 午夜激情网| 美女诱惑一区 | 蜜桃视频久久一区免费观看入口 | 免费人成自慰网站 | 精品日韩在线视频 | 免费在线观看视频a | 女人久久久 | 欧美激情一区二区视频 | 国产一区二区三区久久久 | 国产无套在线观看 | 激情久久中文字幕 | 久操视频网站 | 国产av无码国产av毛片 | 日本妈妈3| 久久99精品国产.久久久久久 | 国产少妇在线 | 国产精品一区二区三区四区 | 欧美 唯美 清纯 偷拍 | 久久黄页 | 黄色国产| 久久精品国产亚洲a | 国产女主播自拍 | a级黄片毛片 | www超碰在线 | 国产精品无遮挡 | 香蕉视频网站在线观看 | 国产精品久久久网站 | 青青青在线视频观看 | 国产精品高清无码在线观看 | 日韩中文字幕免费在线观看 |