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

歡迎訪問 生活随笔!

生活随笔

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

编程问答

记录01-FreeModbus移植入stm32单片机,以及遇到的问题

發布時間:2024/1/1 编程问答 42 豆豆
生活随笔 收集整理的這篇文章主要介紹了 记录01-FreeModbus移植入stm32单片机,以及遇到的问题 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

第一步獲取FreeModbus1.6

????????直接從官網下載就能使用,里面有相應的Demo可以進行參考,鏈接如下:

FreeMODBUS Downloads - Embedded Expertshttps://www.embedded-experts.at/en/freemodbus-downloads/

????????獲取之后解壓得到以下文件:

? ?????????這些文件的中主要就是用到demo和modbus這兩個文件。

第二步:建立一個簡單的工程模板,然后開始移植

????????這里選用的是野火stm32f407的工程模板,選用不同的內核配置大體一樣,但一些細節部分不同。

????????將freemodbus-v1.6里面demo->BARE->port,將port整個文件夾直接復制進工程文件

? ? ? ? 將freemodbus-v1.6里面的demo->modbus,將modbus這個歌文件夾直接復制進工程文件

? ? ? ? 然后將這兩個文件夾的c文件放入工程中

????????

?????????包含所用到的頭文件:

?????????tip:modbus中的ascii是可選項,在這里并不使用,所以不用添加相關的頭文件以及c文件

第三步,編譯,開始修改

????????剛移植的文件,編譯后,通常會有很多報錯。?

?Error:”port.h“,No such file or directory

出錯原因:第二步沒有包含port.h所在的文件夾

解決方法:直接包含進去即可,在編譯

再次編譯后3個錯誤,5個警告

跳過警告先,直接查看錯誤:

?雙擊,可查看錯誤位置

????????這涉及到內聯函數的一些東西,具體原因自行百度,只可意會不可言傳,修改方式,簡單粗暴,把內聯函數的inline去掉,去掉后如下圖? ? ? ? ? ? ? ? ? ? ?

?????????再次編譯后,仍然發現有一個錯誤

????????檢查后發現原來是mbascii.h的頭文件沒有包含,解決方法和第一個錯誤一樣?。

?????????一整體編譯后發現錯誤直接飆升:解決了一個錯誤出現一堆錯誤,非常的經典

?????????具體原因應該是有些函數沒有定義或者某些c文件沒有引用導致的

????????看見一堆eMBASCII什么初始化,接收,發送,停止,首先就是想到是不是關于ascii.c文件沒有放進去所以在把MODBUS在把mbascii.c文件包含進去

?再次編譯后發現仍然有這幾個錯誤

?????????查找了一番在demo->BARE->demo.c包含有這些函數名,demo.c里面這個文件的代碼復制到工程中的mian.c文件中,注意:保留工程原有的頭文件

再次編譯后仍然有一個錯誤

????????這個錯誤就是..\..\Output\Template.axf: Error: L6218E: Undefined symbol __aeabi_assert (referred from mbutils.o).

????????_aeabi_assert這個是內核中斷的一個東西,具體錯誤自行百度,這里直接上解決方法

?????????把這個勾去掉即可,再次編譯后,無錯誤,但仍有5個警告,均是有這個函數,但是沒有使用

?????????這些暫時先不管,因為這些后面在配置串口,定時器中斷的時候會使用到,具體的內容自行百度。

第四步,補充、配置串口和定時器的相關代碼

? ? ? ? 其實在添加port文件夾里面的文件時,有三個文件這里面分別是事件portevent.c(不用配置),portserial.c(配置相關串口),porttimer.c(配置定時器)

?先配置portserial.c,這里給出相關注釋代碼內容,這里會給出相關函數應配置的東西

/*串口使能*/ void vMBPortSerialEnable( BOOL xRxEnable, BOOL xTxEnable ) {/* If xRXEnable enable serial receive interrupts. If xTxENable enable* transmitter empty interrupts.*/ } /*串口初始化*/ BOOL xMBPortSerialInit( UCHAR ucPORT, ULONG ulBaudRate, UCHAR ucDataBits, eMBParity eParity ) {return FALSE; } /*發送字節*/ BOOL xMBPortSerialPutByte( CHAR ucByte ) {/* Put a byte in the UARTs transmit buffer. This function is called* by the protocol stack if pxMBFrameCBTransmitterEmpty( ) has been* called. */return TRUE; } /*接收字節*/ BOOL xMBPortSerialGetByte( CHAR * pucByte ) {/* Return the byte in the UARTs receive buffer. This function is called* by the protocol stack after pxMBFrameCBByteReceived( ) has been called.*/return TRUE; } /* Create an interrupt handler for the transmit buffer empty interrupt* (or an equivalent) for your target processor. This function should then* call pxMBFrameCBTransmitterEmpty( ) which tells the protocol stack that* a new character can be sent. The protocol stack will then call * xMBPortSerialPutByte( ) to send the character.*/ /*發送中斷要進入的函數*/ static void prvvUARTTxReadyISR( void ) {pxMBFrameCBTransmitterEmpty( ); }/* Create an interrupt handler for the receive interrupt for your target* processor. This function should then call pxMBFrameCBByteReceived( ). The* protocol stack will then call xMBPortSerialGetByte( ) to retrieve the* character.*/ /*接收中斷要進入的函數*/ static void prvvUARTRxISR( void ) {pxMBFrameCBByteReceived( ); }

????????我這邊選用的是串口1,我在頭文件中將串口1宏定義為?DEBUG_USART,對于如何初始化串口、發送字節、接收字節不再贅述,不會自行百度,或查看野火、正點原子。這里只給出串口使能相關代碼以及串口中斷代碼,我用的是野火的stm32407霸天虎,所以借鑒野火哥的,是借鑒。

/*串口使能*/ void vMBPortSerialEnable( BOOL xRxEnable, BOOL xTxEnable ) {/* If xRXEnable enable serial receive interrupts. If xTxENable enable* transmitter empty interrupts.*/if(xRxEnable == TRUE){USART_ITConfig(DEBUG_USART, USART_IT_RXNE, ENABLE);}else{USART_ITConfig(DEBUG_USART, USART_IT_RXNE, DISABLE);}if(xTxEnable == TRUE){USART_ITConfig(DEBUG_USART, USART_IT_TC, ENABLE);}else{USART_ITConfig(DEBUG_USART, USART_IT_TC, DISABLE);} } /*串口中斷*/ void USART1_IRQHandler(void) {if(USART_GetITStatus(DEBUG_USART, USART_IT_RXNE) == SET){prvvUARTRxISR(); //清除中斷標志位 USART_ClearITPendingBit(DEBUG_USART, USART_IT_RXNE); }if(USART_GetITStatus(DEBUG_USART, USART_IT_ORE) == SET){ USART_ClearITPendingBit(DEBUG_USART, USART_IT_ORE);prvvUARTRxISR(); }//發生完成中斷if(USART_GetITStatus(DEBUG_USART, USART_IT_TC) == SET){prvvUARTTxReadyISR();//清除中斷標志USART_ClearITPendingBit(DEBUG_USART, USART_IT_TC);} }

? ? ? ? ?以上關于串口的就配置完成了。

????????配置porttimer.c,這里給出相關注釋代碼內容,這里會給出相關函數應配置的東西

/*定時器初始化*/ BOOL xMBPortTimersInit( USHORT usTim1Timerout50us ) {return FALSE; } /*定時器使能*/ void vMBPortTimersEnable( ) {/* Enable the timer with the timeout passed to xMBPortTimersInit( ) */ } /*定時器失能*/ void vMBPortTimersDisable( ) {/* Disable any pending timers. */ }/* Create an ISR which is called whenever the timer has expired. This function* must then call pxMBPortCBTimerExpired( ) to notify the protocol stack that* the timer has expired.*/ /*中斷要進入的函數*/ static void prvvTIMERExpiredISR( void ) {( void )pxMBPortCBTimerExpired( ); }

? ? ? ? 關于定時器初始化的內容就不給出來了,但配置的中斷的時間為,這是參考野火的modbus例程配置的時間,不同時鐘源所配置的頻率不一樣,要注意!!!!

/* 累計 TIM_Period個后產生一個更新或者中斷*/ //當定時器從0計數到4999,即為5000次,為一個定時周期TIM_TimeBaseStructure.TIM_Period = 35; // 通用控制定時器時鐘源TIMxCLK = HCLK/2=84MHz // 設定定時器頻率為=TIMxCLK/(TIM_Prescaler+1)=20000HzTIM_TimeBaseStructure.TIM_Prescaler = 4200-1;

?????????這是porttimer.c文件的除初始化外的代碼

BOOL xMBPortTimersInit( USHORT usTim1Timerout50us ) { // USHORT usTim1Timerout; // usTim1Timerout = usTim1Timerout50us + 0;//timer2_init(usTim1Timerout50us);//timer2_nvic();TIMx_Configuration();return FALSE; }void vMBPortTimersEnable( ) {/* Enable the timer with the timeout passed to xMBPortTimersInit( ) */TIM_ClearITPendingBit(TIM2, TIM_IT_Update);TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE);TIM_SetCounter(TIM2,0x0000); TIM_Cmd(TIM2, ENABLE); } //__STATIC_INLINE void vMBPortTimersDisable( ) {/* Disable any pending timers. */TIM_ClearITPendingBit(TIM2, TIM_IT_Update);TIM_ITConfig(TIM2, TIM_IT_Update, DISABLE);TIM_SetCounter(TIM2,0x0000); TIM_Cmd(TIM2, DISABLE); }/* Create an ISR which is called whenever the timer has expired. This function* must then call pxMBPortCBTimerExpired( ) to notify the protocol stack that* the timer has expired.*/ static void prvvTIMERExpiredISR( void ) {( void )pxMBPortCBTimerExpired( ); }void TIM2_IRQHandler(void) {if(TIM_GetITStatus(TIM2, TIM_IT_Update) != RESET){prvvTIMERExpiredISR();TIM_ClearITPendingBit(TIM2, TIM_IT_Update);} }

?????????以上,做完這些freemodbus基本移植完成,整體編譯后,仍發現兩個警告,可自行修改,將進入調試階段

第五步,調試,查找問題

????????移植完成后,使用Modbus Poll進行測試發現并不能通訊,并一直出現Timer out,說明可能串口沒打開,因此查看代碼內容,懷疑是不是漏了修改什么,懷疑并沒有初始化串口和定時器

????????查看主函數代碼,eMBInit(初始化波特率停止位這些東西),所以這應該是初始化關于串口和定時器這些玩意

int main( void ) {eMBErrorCode eStatus;eStatus = eMBInit( MB_RTU, 0x01, 1, 38400, MB_PAR_EVEN );/* Enable the Modbus Protocol Stack. */eStatus = eMBEnable( );for( ;; ){( void )eMBPoll( );/* Here we simply count the number of poll cycles. */usRegInputBuf[0]++;} }

? 查看該函數后發現其函數內容如下:

eMBErrorCode eMBInit( eMBMode eMode, UCHAR ucSlaveAddress, UCHAR ucPort, ULONG ulBaudRate, eMBParity eParity ) {eMBErrorCode eStatus = MB_ENOERR;/* check preconditions */if( ( ucSlaveAddress == MB_ADDRESS_BROADCAST ) ||( ucSlaveAddress < MB_ADDRESS_MIN ) || ( ucSlaveAddress > MB_ADDRESS_MAX ) ){eStatus = MB_EINVAL;}else{ucMBAddress = ucSlaveAddress;switch ( eMode ){ #if MB_RTU_ENABLED > 0case MB_RTU: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 );break; #endif #if MB_ASCII_ENABLED > 0case MB_ASCII:pvMBFrameStartCur = eMBASCIIStart;pvMBFrameStopCur = eMBASCIIStop;peMBFrameSendCur = eMBASCIISend;peMBFrameReceiveCur = eMBASCIIReceive;pvMBFrameCloseCur = MB_PORT_HAS_CLOSE ? vMBPortClose : NULL;pxMBFrameCBByteReceived = xMBASCIIReceiveFSM;pxMBFrameCBTransmitterEmpty = xMBASCIITransmitFSM;pxMBPortCBTimerExpired = xMBASCIITimerT1SExpired;eStatus = eMBASCIIInit( ucMBAddress, ucPort, ulBaudRate, eParity );break; #endifdefault:eStatus = MB_EINVAL;}if( eStatus == MB_ENOERR ){if( !xMBPortEventInit( ) ){/* port dependent event module initalization failed. */eStatus = MB_EPORTERR;}else{eMBCurrentMode = eMode;eMBState = STATE_DISABLED;}}}return eStatus; }

翻閱說明,可以發現這應該是配置是用RTU還是ASCII的,所以點MB_RTU_ENABLED進去發現三個宏定義

/*! \brief If Modbus ASCII support is enabled. */ #define MB_ASCII_ENABLED ( 0 )//將1改成0即為關閉使能/*! \brief If Modbus RTU support is enabled. */ #define MB_RTU_ENABLED ( 1 )/*! \brief If Modbus TCP support is enabled. */ #define MB_TCP_ENABLED ( 0 )

做完返回上層函數仍未發現初始化的內容,再次點擊

#if MB_RTU_ENABLED > 0case MB_RTU: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 );//進去break;

????????看到eMBRTUInit函數發現進入后,發現這個地方應該是初始化相關串口和定時器的以及計算每幀的時間,然后繼續點擊相應函數名,果然發現初始化串口的函數

????????xMBPortSerialInit( ucPort, ulBaudRate, 8, eParity ) != TRUE,

????????但他的返回值為FALSE,返回之后不會執行初始化定時器的函數,所以應該將初始化串口的函數返回值修改成TURE;

編譯后再次運行發現仍然串口沒有數據,初始化已經結束了,會不會是沒有使能的原因,所以進入主函數中

????????eStatus = eMBEnable( ?);

? ? ? ? 函數如下:

eMBErrorCode eMBClose( void ) {eMBErrorCode eStatus = MB_ENOERR;if( eMBState == STATE_DISABLED ){if( pvMBFrameCloseCur != NULL ){pvMBFrameCloseCur( );}}else{eStatus = MB_EILLSTATE;}return eStatus;

會不會是并沒有進去if那里,即if(?? ?eMBState ==?? ?STATE_ENABLED )中的eMBstate是否相等

static enum {STATE_ENABLED,STATE_DISABLED,STATE_NOT_INITIALIZED } eMBState = STATE_NOT_INITIALIZED;

可以看到他的值是等于2,STATE_ENABLED的值為1所以一直沒有使能,更改一下即可。

編譯運行后發現,他與主機通訊只能一次lllegal Data Address,然后又繼續timerout,,說明輸出的數據不太對,而且只能輸出一次

只能輸出一次解決方法如下,在mbrtu.c文件中找到相應的eMBRTUsend()函數名,在里面增加點東西

eMBErrorCode eMBRTUSend( UCHAR ucSlaveAddress, const UCHAR * pucFrame, USHORT usLength ) {eMBErrorCode eStatus = MB_ENOERR;USHORT usCRC16;ENTER_CRITICAL_SECTION( );/* Check if the receiver is still in idle state. If not we where to* slow with processing the received frame and the master sent another* frame on the network. We have to abort sending the frame.*/if( eRcvState == STATE_RX_IDLE ){/* First byte before the Modbus-PDU is the slave address. */pucSndBufferCur = ( UCHAR * ) pucFrame - 1;usSndBufferCount = 1;/* Now copy the Modbus-PDU into the Modbus-Serial-Line-PDU. */pucSndBufferCur[MB_SER_PDU_ADDR_OFF] = ucSlaveAddress;usSndBufferCount += usLength;/* Calculate CRC16 checksum for Modbus-Serial-Line-PDU. */usCRC16 = usMBCRC16( ( UCHAR * ) pucSndBufferCur, usSndBufferCount );ucRTUBuf[usSndBufferCount++] = ( UCHAR )( usCRC16 & 0xFF );ucRTUBuf[usSndBufferCount++] = ( UCHAR )( usCRC16 >> 8 );/* Activate the transmitter. */eSndState = STATE_TX_XMIT;/* 新增的代碼*/xMBPortSerialPutByte( ( CHAR )*pucSndBufferCur );pucSndBufferCur++; usSndBufferCount--;/*結束*/vMBPortSerialEnable( FALSE, TRUE );}else{eStatus = MB_EIO;}EXIT_CRITICAL_SECTION( );return eStatus; }

?????????數據錯誤就需重新弄慢慢設置主機,查看從機給出的地址進行修改

????????然后就能正常通訊了

整體移植以及移植遇到的問題大概就是這些了,

總結

以上是生活随笔為你收集整理的记录01-FreeModbus移植入stm32单片机,以及遇到的问题的全部內容,希望文章能夠幫你解決所遇到的問題。

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