#freemodbus移植 >基於freemodbus1.6 >使用HAL庫 >軟體:stm32cubemx stm32cubeide >>後續會更新標準庫的移植。以及rtos下的移植(儘量) ##下載freemodbus1.6 這個獲取方法網上到處都是,不細說了。 ##cubemx新建工程 新建工 ...
freemodbus移植
基於freemodbus1.6
使用HAL庫
軟體:stm32cubemx stm32cubeide後續會更新標準庫的移植。以及rtos下的移植(儘量)
下載freemodbus1.6
這個獲取方法網上到處都是,不細說了。
cubemx新建工程
新建工程只列出了與移植freemodbus相關的設置
這裡我使用的是485通信,所以額外使能了一個引腳
使能一個定時器,這裡我用的是tim2。並且開始定時器2中斷
其他設置如下圖,參數其實設什麼無所謂,因為後面要改的,我們並不用系統的初始化函數。
然後使能一個串口,我這裡用的串口1,參數其實設什麼無所謂,因為後面要改的,
這裡可以把串口1和定時器2的最前面的取消勾選,就不會生成他們的初始化函數,不勾也沒有太大關係,因為我們的函數在他之後,會覆蓋掉系統的設置。
另外在中斷優先順序設置中,將串口優先順序設置高於定時器2,數字越小越高。
相關的中斷處理函數也要生成。
然後就可以generate code!生成代碼。
代碼修改
首先我們在我們項目的根目錄中新建一個freemodbus文件夾,文件夾中再建一個modbus文件夾,一個port文件夾。
把你最開始下載下來的freemodbus中modbus文件夾中的內容複製到你剛纔的modbus文件夾中,
把你最開始下載下來的freemodbus中demo/bare路徑下的內容全部複製到你剛纔的port文件夾中
然後我們進入cubeide,右鍵項目->屬性,配置頭文件和源文件路徑。
把如圖六個頭文件路徑添加。
把如圖最下麵兩個源文件路徑添加。
先在port.h文件中補充這兩個巨集定義,這是HAL庫的全局中斷開啟、關閉函數。
ok,然後我們修改portserail.c
這兩個函數前面的static標誌去掉。
vMBPortSerialEnable函數修改如下
void
vMBPortSerialEnable( BOOL xRxEnable, BOOL xTxEnable )
{
/* If xRXEnable enable serial receive interrupts. If xTxENable enable
* transmitter empty interrupts.
*/
if (xRxEnable) //將串口收發中斷和modbus聯繫起來,下麵的串口改為自己使能的串口
{
__HAL_UART_ENABLE_IT(&huart2,UART_IT_RXNE); //我用的是串口2,故為&huart2
HAL_GPIO_WritePin(EN485_GPIO_Port, EN485_Pin, GPIO_PIN_RESET);//
}
else
{
__HAL_UART_DISABLE_IT(&huart2,UART_IT_RXNE);
HAL_GPIO_WritePin(EN485_GPIO_Port, EN485_Pin, GPIO_PIN_SET);//
}
if (xTxEnable)
{
HAL_GPIO_WritePin(EN485_GPIO_Port, EN485_Pin, GPIO_PIN_SET);//
__HAL_UART_ENABLE_IT(&huart2,UART_IT_TXE);
}//
else
{
HAL_GPIO_WritePin(EN485_GPIO_Port, EN485_Pin, GPIO_PIN_RESET);//
__HAL_UART_DISABLE_IT(&huart2,UART_IT_TXE);
}
}
串口初始化函數如下
BOOL
xMBPortSerialInit( UCHAR ucPORT, ULONG ulBaudRate, UCHAR ucDataBits, eMBParity eParity )
{
huart2.Instance = USART2;
huart2.Init.BaudRate = ulBaudRate;
huart2.Init.StopBits = UART_STOPBITS_1;
huart2.Init.Mode = UART_MODE_TX_RX;
huart2.Init.HwFlowCtl = UART_HWCONTROL_NONE;
huart2.Init.OverSampling = UART_OVERSAMPLING_16;
switch(eParity)
{
// 奇校驗
case MB_PAR_ODD:
huart2.Init.Parity = UART_PARITY_ODD;
huart2.Init.WordLength = UART_WORDLENGTH_9B; // 帶奇偶校驗數據位為9bits
break;
// 偶校驗
case MB_PAR_EVEN:
huart2.Init.Parity = UART_PARITY_EVEN;
huart2.Init.WordLength = UART_WORDLENGTH_9B; // 帶奇偶校驗數據位為9bits
break;
// 無校驗
default:
huart2.Init.Parity = UART_PARITY_NONE;
huart2.Init.WordLength = UART_WORDLENGTH_8B; // 無奇偶校驗數據位為8bits
break;
}
return HAL_UART_Init(&huart2) == HAL_OK ? TRUE : 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. */
HAL_GPIO_WritePin(EN485_GPIO_Port, EN485_Pin, GPIO_PIN_SET);//
if(HAL_UART_Transmit (&huart2 ,(uint8_t *)&ucByte,1,10) != HAL_OK )
return FALSE ;//HAL_UART_Transmit最後一位形參為最大發送時間,
//超出改時間退出發送,可能導致485發送失敗,可稍微長一點。
else
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.
*/
HAL_GPIO_WritePin(EN485_GPIO_Port, EN485_Pin, GPIO_PIN_RESET);
if(HAL_UART_Receive (&huart2,(uint8_t *)pucByte,1,10) != HAL_OK )
return FALSE ;
else
return TRUE;
}
然後我們修改porttimer.c
首先依舊去掉這個函數前的static標誌,方便之後調用,函數聲明和函數實體前的static都要去掉
然後修改這幾個函數:
定時器初始化函數
BOOL
xMBPortTimersInit( USHORT usTim1Timerout50us )
{
TIM_ClockConfigTypeDef sClockSourceConfig = {0};
TIM_MasterConfigTypeDef sMasterConfig = {0};
htim2.Instance = TIM2;
htim2.Init.Prescaler = 3599; // 50us記一次數
htim2.Init.CounterMode = TIM_COUNTERMODE_UP;
htim2.Init.Period = usTim1Timerout50us-1; // usTim1Timerout50us * 50即為定時器溢出時間
htim2.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
htim2.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
if (HAL_TIM_Base_Init(&htim2) != HAL_OK)
{
return FALSE;
}
sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL;
if (HAL_TIM_ConfigClockSource(&htim2, &sClockSourceConfig) != HAL_OK)
{
return FALSE;
}
sMasterConfig.MasterOutputTrigger = TIM_TRGO_UPDATE;
sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
if (HAL_TIMEx_MasterConfigSynchronization(&htim2, &sMasterConfig) != HAL_OK)
{
return FALSE;
}
return TRUE;
}
然後是定時器開啟、關閉、中斷服務函數
inline void
vMBPortTimersEnable( )
{
/* Enable the timer with the timeout passed to xMBPortTimersInit( ) */
__HAL_TIM_CLEAR_IT(&htim2,TIM_IT_UPDATE);//避免程式一上電就進入定時器中斷
__HAL_TIM_ENABLE_IT(&htim2,TIM_IT_UPDATE);
__HAL_TIM_SET_COUNTER(&htim2, 0); // 清空計數器
__HAL_TIM_ENABLE(&htim2); // 使能定時器
}
inline void
vMBPortTimersDisable( )
{
/* Disable any pending timers. */
__HAL_TIM_DISABLE(&htim2); // 禁能定時器
__HAL_TIM_SET_COUNTER(&htim2,0);
__HAL_TIM_DISABLE_IT(&htim2,TIM_IT_UPDATE);
__HAL_TIM_CLEAR_IT(&htim2,TIM_IT_UPDATE);
}
/* 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.
*/
void prvvTIMERExpiredISR( void )
{
( void )pxMBPortCBTimerExpired( );
}
最後還有兩處修改,有的教程中並沒有提到這兩處修改,應該是與vMBPortSerialEnable中使用USART_IT_TC還是USART_IT_TXE中斷標誌有關,如果使用USART_IT_TC中斷的話需要添加這兩處修改,就我目前使用USART_IT_TXE中斷標誌的情況下,加上這兩處修改也並無問題,待後續研究明白了再更新
//啟動第一次發送,進入發送完成中斷
xMBPortSerialPutByte( ( CHAR )*pucSndBufferCur );
pucSndBufferCur++; /* next byte in sendbuffer. */
usSndBufferCount--;
//添加代碼end
//插入代碼begin
if(eStatus==MB_ENOERR)
{
xMBRTUTransmitFSM(); //發送一幀數據中第一個位元組出發發送完成中斷
}
//插入代碼end
在系統的中斷處理.c中添加以下的函數聲明,有些教程是自己寫的中斷處理,這裡我們還是用系統自己的。
定時器中斷處理函數:
void TIM2_IRQHandler(void)
{
/* USER CODE BEGIN TIM2_IRQn 0 */
HAL_NVIC_ClearPendingIRQ(TIM2_IRQn);
/* USER CODE END TIM2_IRQn 0 */
HAL_TIM_IRQHandler(&htim2);
/* USER CODE BEGIN TIM2_IRQn 1 */
/* USER CODE END TIM2_IRQn 1 */
}
串口中斷函數:
void USART2_IRQHandler(void)
{
/* USER CODE BEGIN USART2_IRQn 0 */
if(__HAL_UART_GET_IT_SOURCE(&huart2, UART_IT_RXNE)!= RESET)
{
prvvUARTRxISR();//接收中斷
}
if(__HAL_UART_GET_IT_SOURCE(&huart2, UART_IT_TXE)!= RESET)
{
prvvUARTTxReadyISR();//發送中斷
}
HAL_NVIC_ClearPendingIRQ(USART2_IRQn);
/* USER CODE END USART2_IRQn 0 */
HAL_UART_IRQHandler(&huart2);
/* USER CODE BEGIN USART2_IRQn 1 */
/* USER CODE END USART2_IRQn 1 */
}
在文檔末尾user code 代碼段添加定時器中斷回調函數:
/* USER CODE BEGIN 1 */
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
/* NOTE : This function Should not be modified, when the callback is needed,
the __HAL_TIM_PeriodElapsedCallback could be implemented in the user file
*/
if(htim->Instance == TIM2)
{
prvvTIMERExpiredISR( );
}
}
/* USER CODE END 1 */
回到main函數
首先包含幾個頭文件
在Private define段添加如下:
/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
//輸入寄存器起始地址
#define REG_INPUT_START 0x0001
//輸入寄存器數量
#define REG_INPUT_NREGS 8
//保持寄存器起始地址
#define REG_HOLDING_START 0x0001
//保持寄存器數量
#define REG_HOLDING_NREGS 8
//線圈起始地址
#define REG_COILS_START 0x0001
//線圈數量
#define REG_COILS_SIZE 16
//離散寄存器起始地址
#define REG_DISCRETE_START 0x0001
//離散寄存器數量
#define REG_DISCRETE_SIZE 16
/* USER CODE END PD */
Private variables段添加如下:
/* Private variables ---------------------------------------------------------*/
/* USER CODE BEGIN PV */
//輸入寄存器內容
uint16_t usRegInputBuf[REG_INPUT_NREGS] = {0x1000,0x1001,0x1002,0x1003,0x1004,0x1005,0x1006,0x1007};
//輸入寄存器起始地址
uint16_t usRegInputStart = REG_INPUT_START;
//保持寄存器內容
uint32_t usRegHoldingBuf[REG_HOLDING_NREGS] = {0x147b,0x3f8e,0x147b,0x400e,0x1eb8,0x4055,0x147b,0x408e};
//保持寄存器起始地址
uint16_t usRegHoldingStart = REG_HOLDING_START;
//線圈狀態
uint8_t ucRegCoilsBuf[REG_COILS_SIZE / 8] = {0x01,0x02};
//離散輸入狀態
uint8_t usRegDiscreteBuf[REG_DISCRETE_SIZE / 8] = {0x01,0x02};
uint8_t testfalg=0;
extern unsigned char NUM [];
/* USER CODE END PV */
Private function prototypes段添加如下:
/* USER CODE BEGIN PFP */
/**
* @Brief : 讀輸入寄存器處理函數,功能碼04
* @param pucRegBuffer 保存輸入寄存器值的緩存
* @param usAddress 寄存器地址
* @param usNRegs 讀取個數
* @return?eMBErrorCode?
*/
eMBErrorCode
eMBRegInputCB( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNRegs )
{
eMBErrorCode eStatus = MB_ENOERR;
int iRegIndex;
if( ( usAddress >= REG_INPUT_START )\
&& ( usAddress + usNRegs <= REG_INPUT_START + REG_INPUT_NREGS ) )
{
iRegIndex = ( int )( usAddress - usRegInputStart );
while( usNRegs > 0 )
{
*pucRegBuffer++ = ( UCHAR )( usRegInputBuf[iRegIndex] >> 8 );
*pucRegBuffer++ = ( UCHAR )( usRegInputBuf[iRegIndex] & 0xFF );
iRegIndex++;
usNRegs--;
}
}
else
{
eStatus = MB_ENOREG;
}
return eStatus;
}
/**
* @Brief : 讀保持寄存器處理函數,功能碼03
* @param pucRegBuffer
* @param usAddress
* @param usNRegs
* @param eMode
* @return?eMBErrorCode?
*/
eMBErrorCode
eMBRegHoldingCB( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNRegs, eMBRegisterMode eMode )
{
eMBErrorCode eStatus = MB_ENOERR;
int iRegIndex;
if((usAddress >= REG_HOLDING_START)&&\
((usAddress+usNRegs) <= (REG_HOLDING_START + REG_HOLDING_NREGS)))
{
iRegIndex = (int)(usAddress - usRegHoldingStart);
switch(eMode)
{
case MB_REG_READ://�??? MB_REG_READ = 0
while(usNRegs > 0)
{
*pucRegBuffer++ = (uint8_t)(usRegHoldingBuf[iRegIndex] >> 8);
*pucRegBuffer++ = (uint8_t)(usRegHoldingBuf[iRegIndex] & 0xFF);
iRegIndex++;
usNRegs--;
}
break;
case MB_REG_WRITE://�??? MB_REG_WRITE = 1
while(usNRegs > 0)
{
usRegHoldingBuf[iRegIndex] = *pucRegBuffer++ << 8;
usRegHoldingBuf[iRegIndex] |= *pucRegBuffer++;
iRegIndex++;
usNRegs--;
}
}
}
else//錯誤
{
eStatus = MB_ENOREG;
}
return eStatus;
}
/**
*****************************************************************************
* @Name : 操作線圈
*
* @Brief : 對應功能�???0x01 -> eMBFuncReadCoils
* 0x05 -> eMBFuncWriteCoil
* 0x15 -> 寫多個線�??? eMBFuncWriteMultipleCoils
*
* @Input : *pucRegBuffer:數據緩衝區,響應主機用
* usAddress: 寄存器地�???
* usNRegs: 操作寄存器個�???
* eMode: 功能�???
*
* @Output : none
*
* @Return : Modbus狀�?�信�???
*****************************************************************************
**/
eMBErrorCode eMBRegCoilsCB( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNCoils, eMBRegisterMode eMode )
{
eMBErrorCode eStatus = MB_ENOERR;
int iNCoils = ( int )usNCoils;
unsigned short usBitOffset;
/* Check if we have registers mapped at this block. */
if( ( usAddress >= REG_COILS_START ) && ( usAddress + usNCoils <= REG_COILS_START + REG_COILS_SIZE ) )
{
usBitOffset = ( unsigned short )( usAddress - REG_COILS_START );
switch ( eMode )
{
/* Read current values and pass to protocol stack. */
case MB_REG_READ:
while( iNCoils > 0 )
{
*pucRegBuffer++ = xMBUtilGetBits( ucRegCoilsBuf, usBitOffset, ( unsigned char )( iNCoils > 8 ? 8 : iNCoils ) );
iNCoils -= 8;
usBitOffset += 8;
}
break;
/* Update current register values. */
case MB_REG_WRITE:
while( iNCoils > 0 )
{
xMBUtilSetBits( ucRegCoilsBuf, usBitOffset, ( unsigned char )( iNCoils > 8 ? 8 : iNCoils ), *pucRegBuffer++ );
iNCoils -= 8;
usBitOffset += 8;
}
break;
}
}
else
{
eStatus = MB_ENOREG;
}
return eStatus;
}
/**
*****************************************************************************
* @Name : 操作離散寄存�???
*
* @Brief : 對應功能�???0x02 -> eMBFuncReadDiscreteInputs
*
* @Input : *pucRegBuffer:數據緩衝區,響應主機用
* usAddress: 寄存器地�???
* usNRegs: 操作寄存器個�???
*
* @Output : none
*
* @Return : Modbus狀�?�信�???
*****************************************************************************
**/
//eMBErrorCode eMBRegDiscreteCB( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNDiscrete )
//{
// pucRegBuffer = pucRegBuffer;
// return MB_ENOREG;
//}
eMBErrorCode
eMBRegDiscreteCB( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNDiscrete )
{
eMBErrorCode eStatus = MB_ENOERR;
short iNDiscrete = ( short )usNDiscrete;
USHORT usBitOffset;
/* Check if we have registers mapped at this block. */
if( ( usAddress >= REG_DISCRETE_START ) && ( usAddress + usNDiscrete <= REG_DISCRETE_START + REG_DISCRETE_SIZE ) )
{
usBitOffset = ( USHORT )( usAddress - REG_DISCRETE_START );
while( iNDiscrete > 0 )
{
*pucRegBuffer++ =
xMBUtilGetBits( usRegDiscreteBuf, usBitOffset,( UCHAR )( iNDiscrete > 8 ? 8 : iNDiscrete ) );
iNDiscrete -= 8;
usBitOffset += 8;
}
}
else
{
eStatus = MB_ENOREG;
}
return eStatus;
}
/* USER CODE END PFP */
新建變數estatus 前後說的這些添加內容必須夾在user code欄位中,否則cubemx修改工程重新生成代碼後,你修改的內容會消失
初始化、使能
因為前面在串口初始化中,串口選擇是被我寫死的,不能通過這裡的第三個形參去選擇使用串口幾,但是通過簡單修改可以實現自由配置
最後在main函數中啟動輪詢就可以了
最後你把想傳出去的數據放在各個寄存器中,外部modbus主機就可以查詢到你放入寄存器的數據了
/* Private variables ---------------------------------------------------------*/
/* USER CODE BEGIN PV */
//輸入寄存器內容
uint16_t usRegInputBuf[REG_INPUT_NREGS] = {0x1000,0x1001,0x1002,0x1003,0x1004,0x1005,0x1006,0x1007};
//輸入寄存器起始地址
uint16_t usRegInputStart = REG_INPUT_START;
//保持寄存器內容
uint32_t usRegHoldingBuf[REG_HOLDING_NREGS] = {0x147b,0x3f8e,0x147b,0x400e,0x1eb8,0x4055,0x147b,0x408e};
//保持寄存器起始地址
uint16_t usRegHoldingStart = REG_HOLDING_START;
//線圈狀態
uint8_t ucRegCoilsBuf[REG_COILS_SIZE / 8] = {0x01,0x02};
//離散輸入狀態
uint8_t usRegDiscreteBuf[REG_DISCRETE_SIZE / 8] = {0x01,0x02};
uint8_t testfalg=0;
extern unsigned char NUM [];
/* USER CODE END PV */