记录01-FreeModbus移植入stm32单片机,以及遇到的问题
第一步獲取FreeModbus1.6
????????直接從官網(wǎng)下載就能使用,里面有相應(yīng)的Demo可以進(jìn)行參考,鏈接如下:
FreeMODBUS Downloads - Embedded Expertshttps://www.embedded-experts.at/en/freemodbus-downloads/
????????獲取之后解壓得到以下文件:
? ?????????這些文件的中主要就是用到demo和modbus這兩個(gè)文件。
第二步:建立一個(gè)簡單的工程模板,然后開始移植
????????這里選用的是野火stm32f407的工程模板,選用不同的內(nèi)核配置大體一樣,但一些細(xì)節(jié)部分不同。
????????將freemodbus-v1.6里面demo->BARE->port,將port整個(gè)文件夾直接復(fù)制進(jìn)工程文件
? ? ? ? 將freemodbus-v1.6里面的demo->modbus,將modbus這個(gè)歌文件夾直接復(fù)制進(jìn)工程文件
? ? ? ? 然后將這兩個(gè)文件夾的c文件放入工程中
????????
?????????包含所用到的頭文件:
?????????tip:modbus中的ascii是可選項(xiàng),在這里并不使用,所以不用添加相關(guān)的頭文件以及c文件
第三步,編譯,開始修改
????????剛移植的文件,編譯后,通常會(huì)有很多報(bào)錯(cuò)。?
?Error:”port.h“,No such file or directory
出錯(cuò)原因:第二步?jīng)]有包含port.h所在的文件夾
解決方法:直接包含進(jìn)去即可,在編譯
再次編譯后3個(gè)錯(cuò)誤,5個(gè)警告
跳過警告先,直接查看錯(cuò)誤:
?雙擊,可查看錯(cuò)誤位置
????????這涉及到內(nèi)聯(lián)函數(shù)的一些東西,具體原因自行百度,只可意會(huì)不可言傳,修改方式,簡單粗暴,把內(nèi)聯(lián)函數(shù)的inline去掉,去掉后如下圖? ? ? ? ? ? ? ? ? ? ?
?????????再次編譯后,仍然發(fā)現(xiàn)有一個(gè)錯(cuò)誤
????????檢查后發(fā)現(xiàn)原來是mbascii.h的頭文件沒有包含,解決方法和第一個(gè)錯(cuò)誤一樣?。
?????????一整體編譯后發(fā)現(xiàn)錯(cuò)誤直接飆升:解決了一個(gè)錯(cuò)誤出現(xiàn)一堆錯(cuò)誤,非常的經(jīng)典
?????????具體原因應(yīng)該是有些函數(shù)沒有定義或者某些c文件沒有引用導(dǎo)致的
????????看見一堆eMBASCII什么初始化,接收,發(fā)送,停止,首先就是想到是不是關(guān)于ascii.c文件沒有放進(jìn)去所以在把MODBUS在把mbascii.c文件包含進(jìn)去
?再次編譯后發(fā)現(xiàn)仍然有這幾個(gè)錯(cuò)誤
?????????查找了一番在demo->BARE->demo.c包含有這些函數(shù)名,demo.c里面這個(gè)文件的代碼復(fù)制到工程中的mian.c文件中,注意:保留工程原有的頭文件
再次編譯后仍然有一個(gè)錯(cuò)誤
????????這個(gè)錯(cuò)誤就是..\..\Output\Template.axf: Error: L6218E: Undefined symbol __aeabi_assert (referred from mbutils.o).
????????_aeabi_assert這個(gè)是內(nèi)核中斷的一個(gè)東西,具體錯(cuò)誤自行百度,這里直接上解決方法
?????????把這個(gè)勾去掉即可,再次編譯后,無錯(cuò)誤,但仍有5個(gè)警告,均是有這個(gè)函數(shù),但是沒有使用
?????????這些暫時(shí)先不管,因?yàn)檫@些后面在配置串口,定時(shí)器中斷的時(shí)候會(huì)使用到,具體的內(nèi)容自行百度。
第四步,補(bǔ)充、配置串口和定時(shí)器的相關(guān)代碼
? ? ? ? 其實(shí)在添加port文件夾里面的文件時(shí),有三個(gè)文件這里面分別是事件portevent.c(不用配置),portserial.c(配置相關(guān)串口),porttimer.c(配置定時(shí)器)
?先配置portserial.c,這里給出相關(guān)注釋代碼內(nèi)容,這里會(huì)給出相關(guān)函數(shù)應(yīng)配置的東西
/*串口使能*/ 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; } /*發(fā)送字節(jié)*/ 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; } /*接收字節(jié)*/ 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.*/ /*發(fā)送中斷要進(jìn)入的函數(shù)*/ 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.*/ /*接收中斷要進(jìn)入的函數(shù)*/ static void prvvUARTRxISR( void ) {pxMBFrameCBByteReceived( ); }????????我這邊選用的是串口1,我在頭文件中將串口1宏定義為?DEBUG_USART,對(duì)于如何初始化串口、發(fā)送字節(jié)、接收字節(jié)不再贅述,不會(huì)自行百度,或查看野火、正點(diǎn)原子。這里只給出串口使能相關(guān)代碼以及串口中斷代碼,我用的是野火的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(); //清除中斷標(biāo)志位 USART_ClearITPendingBit(DEBUG_USART, USART_IT_RXNE); }if(USART_GetITStatus(DEBUG_USART, USART_IT_ORE) == SET){ USART_ClearITPendingBit(DEBUG_USART, USART_IT_ORE);prvvUARTRxISR(); }//發(fā)生完成中斷if(USART_GetITStatus(DEBUG_USART, USART_IT_TC) == SET){prvvUARTTxReadyISR();//清除中斷標(biāo)志USART_ClearITPendingBit(DEBUG_USART, USART_IT_TC);} }? ? ? ? ?以上關(guān)于串口的就配置完成了。
????????配置porttimer.c,這里給出相關(guān)注釋代碼內(nèi)容,這里會(huì)給出相關(guān)函數(shù)應(yīng)配置的東西
/*定時(shí)器初始化*/ BOOL xMBPortTimersInit( USHORT usTim1Timerout50us ) {return FALSE; } /*定時(shí)器使能*/ void vMBPortTimersEnable( ) {/* Enable the timer with the timeout passed to xMBPortTimersInit( ) */ } /*定時(shí)器失能*/ 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.*/ /*中斷要進(jìn)入的函數(shù)*/ static void prvvTIMERExpiredISR( void ) {( void )pxMBPortCBTimerExpired( ); }? ? ? ? 關(guān)于定時(shí)器初始化的內(nèi)容就不給出來了,但配置的中斷的時(shí)間為,這是參考野火的modbus例程配置的時(shí)間,不同時(shí)鐘源所配置的頻率不一樣,要注意!!!!
/* 累計(jì) TIM_Period個(gè)后產(chǎn)生一個(gè)更新或者中斷*/ //當(dāng)定時(shí)器從0計(jì)數(shù)到4999,即為5000次,為一個(gè)定時(shí)周期TIM_TimeBaseStructure.TIM_Period = 35; // 通用控制定時(shí)器時(shí)鐘源TIMxCLK = HCLK/2=84MHz // 設(shè)定定時(shí)器頻率為=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基本移植完成,整體編譯后,仍發(fā)現(xiàn)兩個(gè)警告,可自行修改,將進(jìn)入調(diào)試階段
第五步,調(diào)試,查找問題
????????移植完成后,使用Modbus Poll進(jìn)行測試發(fā)現(xiàn)并不能通訊,并一直出現(xiàn)Timer out,說明可能串口沒打開,因此查看代碼內(nèi)容,懷疑是不是漏了修改什么,懷疑并沒有初始化串口和定時(shí)器
????????查看主函數(shù)代碼,eMBInit(初始化波特率停止位這些東西),所以這應(yīng)該是初始化關(guān)于串口和定時(shí)器這些玩意
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]++;} }? 查看該函數(shù)后發(fā)現(xiàn)其函數(shù)內(nèi)容如下:
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; }翻閱說明,可以發(fā)現(xiàn)這應(yīng)該是配置是用RTU還是ASCII的,所以點(diǎn)MB_RTU_ENABLED進(jìn)去發(fā)現(xiàn)三個(gè)宏定義
/*! \brief If Modbus ASCII support is enabled. */ #define MB_ASCII_ENABLED ( 0 )//將1改成0即為關(guān)閉使能/*! \brief If Modbus RTU support is enabled. */ #define MB_RTU_ENABLED ( 1 )/*! \brief If Modbus TCP support is enabled. */ #define MB_TCP_ENABLED ( 0 )做完返回上層函數(shù)仍未發(fā)現(xiàn)初始化的內(nèi)容,再次點(diǎn)擊
#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 );//進(jìn)去break;????????看到eMBRTUInit函數(shù)發(fā)現(xiàn)進(jìn)入后,發(fā)現(xiàn)這個(gè)地方應(yīng)該是初始化相關(guān)串口和定時(shí)器的以及計(jì)算每幀的時(shí)間,然后繼續(xù)點(diǎn)擊相應(yīng)函數(shù)名,果然發(fā)現(xiàn)初始化串口的函數(shù)
????????xMBPortSerialInit( ucPort, ulBaudRate, 8, eParity ) != TRUE,
????????但他的返回值為FALSE,返回之后不會(huì)執(zhí)行初始化定時(shí)器的函數(shù),所以應(yīng)該將初始化串口的函數(shù)返回值修改成TURE;
編譯后再次運(yùn)行發(fā)現(xiàn)仍然串口沒有數(shù)據(jù),初始化已經(jīng)結(jié)束了,會(huì)不會(huì)是沒有使能的原因,所以進(jìn)入主函數(shù)中
????????eStatus = eMBEnable( ?);
? ? ? ? 函數(shù)如下:
eMBErrorCode eMBClose( void ) {eMBErrorCode eStatus = MB_ENOERR;if( eMBState == STATE_DISABLED ){if( pvMBFrameCloseCur != NULL ){pvMBFrameCloseCur( );}}else{eStatus = MB_EILLSTATE;}return eStatus;會(huì)不會(huì)是并沒有進(jìn)去if那里,即if(?? ?eMBState ==?? ?STATE_ENABLED )中的eMBstate是否相等
static enum {STATE_ENABLED,STATE_DISABLED,STATE_NOT_INITIALIZED } eMBState = STATE_NOT_INITIALIZED;可以看到他的值是等于2,STATE_ENABLED的值為1所以一直沒有使能,更改一下即可。
編譯運(yùn)行后發(fā)現(xiàn),他與主機(jī)通訊只能一次lllegal Data Address,然后又繼續(xù)timerout,,說明輸出的數(shù)據(jù)不太對(duì),而且只能輸出一次
只能輸出一次解決方法如下,在mbrtu.c文件中找到相應(yīng)的eMBRTUsend()函數(shù)名,在里面增加點(diǎn)東西
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--;/*結(jié)束*/vMBPortSerialEnable( FALSE, TRUE );}else{eStatus = MB_EIO;}EXIT_CRITICAL_SECTION( );return eStatus; }?????????數(shù)據(jù)錯(cuò)誤就需重新弄慢慢設(shè)置主機(jī),查看從機(jī)給出的地址進(jìn)行修改
????????然后就能正常通訊了
整體移植以及移植遇到的問題大概就是這些了,
總結(jié)
以上是生活随笔為你收集整理的记录01-FreeModbus移植入stm32单片机,以及遇到的问题的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 随笔--读书笔记《人人都是产品经理2.0
- 下一篇: 《STM32从零开始学习历程》——I2C