FreeModbus開源協議棧的移植和詳解(四)
- 概述
- 一、移植前的準備
- 二、將FreeModbus文件源碼添加到STM32工程中
- 三、PORT文件夾修改
- 1、port.h文件
- 2、portserial.c
- 2.1 `vMBPortSerialEnable()`函數
- 2.2`xMBPortSerialInit()`函數
- 2.3`xMBPortSerialPutByte()`函數
- 2.4`xMBPortSerialGetByte()`函數
- 2.5`USART1_IRQHandler()`函數
- 3、porttimer.c
- 3.1 `xMBPortTimersInit()`函數
- 3.2`vMBPortTimersEnable()`函數
- 3.3 `vMBPortTimersDisable()`函數
- 3.4 `TIM2_IRQHandler()`函數
- 4、portevent.c
- 5、main.c
- 6、其他注意地方
概述
在前面幾篇文章中,對FreeModbus文件的源碼進行了分析,還剩下與平臺相關的接口部分,在這裏通過對FreeModbus在STM32上的移植過程爲例來介紹FreeModbus的接口部分。
一、移植前的準備
移植FreeModbus之前需要準備好FreeModbus源碼,關於源碼的獲取方式,參考我之前的文章:
https://blog.csdn.net/u014100102/article/details/90453930
STM32的工程,這個在此不做說明,只要之前做過STM32的對STM32工程的建立都比較熟悉了。
總結:
移植前需要準備:
1、FreeModbus源碼
2、STM32基礎工程
二、將FreeModbus文件源碼添加到STM32工程中
1、在工程中新建FreeModbus文件夾,將Modbus文件夾中的所有文件及文件夾複製進來。
2、將FreeModbus-v1.6->demo->AVR->port文件夾複製到STM32工程中的FreeModbus文件夾中。並將文件添加到工程中。
3、文件添加好之後,來開始看port文件夾下的文件。裏面的文件需要修改,移植到STM32平臺上,就可以使用了。
三、PORT文件夾修改
1、port.h文件
port.h文件主要定義了FreeModbus使用到的數據類型定義,這裏和平臺相關的有一個,就是進出臨界區的定義,不同的MCU的定義不同,STM32修改如下:
#define ENTER_CRITICAL_SECTION() __set_PRIMASK(1) //關總中斷
#define EXIT_CRITICAL_SECTION() __set_PRIMASK(0) //開總中斷
此外,頭文件也需要修改,包含stm32的頭文件
#include "stm32f10x.h"
2、portserial.c
port serial.c文件夾中是實現底層串口的相關函數。這裏先貼出代碼。
#include "port.h"
/* ----------------------- Modbus includes ----------------------------------*/
#include "mb.h"
#include "mbport.h"
/**控制串口發送和接收中斷**/
void
vMBPortSerialEnable( BOOL xRxEnable, BOOL xTxEnable )
{
if(xRxEnable == TRUE)
{
USART_ITConfig(USART1, USART_IT_RXNE, ENABLE); //接收非空中斷
}
else
{
USART_ITConfig(USART1, USART_IT_RXNE, DISABLE);
}
if(xTxEnable == TRUE)
{
USART_ITConfig(USART1, USART_IT_TXE, ENABLE); //發送中斷空
}
else
{
USART_ITConfig(USART1, USART_IT_TXE, DISABLE);
}
}
BOOL
xMBPortSerialInit( UCHAR ucPORT, ULONG ulBaudRate, UCHAR ucDataBits, eMBParity eParity )
{
GPIO_InitTypeDef GPIO_InitStructure;
USART_InitTypeDef USART_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
/* prevent compiler warning. */
(void)ucPORT;
//USART1端口配置
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1|RCC_APB2Periph_GPIOA, ENABLE); //使能USART1,GPIOA時鐘
//USART1_TX PA.9
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9; //PA.9
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //複用推輓輸出
GPIO_Init(GPIOA, &GPIO_InitStructure); //初始化PA9
//USART1_RX PA.10
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;//浮空輸入
GPIO_Init(GPIOA, &GPIO_InitStructure); //初始化PA10
USART_InitStructure.USART_BaudRate = ulBaudRate;
if(ucDataBits == 9)
USART_InitStructure.USART_WordLength = USART_WordLength_9b; // 9位數據 ;
else
USART_InitStructure.USART_WordLength = USART_WordLength_8b; // 8位數據 ;
if(eParity == MB_PAR_ODD)
USART_InitStructure.USART_Parity = USART_Parity_Odd; // 奇校驗;
else if(eParity == MB_PAR_EVEN)
USART_InitStructure.USART_Parity = USART_Parity_Even; // 偶校驗;
else
USART_InitStructure.USART_Parity = USART_Parity_No; // 無校驗位;
USART_InitStructure.USART_StopBits = USART_StopBits_1; // 在幀結尾傳輸1個停止位 ;
USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None; // 硬件流控制失能 ;
USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx; // 接收發送使能 ;
USART_Init(USART1, &USART_InitStructure);
// USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);
USART_Cmd(USART1, ENABLE); //
NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
return TRUE;
}
BOOL
xMBPortSerialPutByte( CHAR ucByte )
{
USART_SendData(USART1, ucByte);
while (USART_GetFlagStatus(USART1, USART_FLAG_TC) == RESET);/*等待發送完成*/
return TRUE;
}
BOOL
xMBPortSerialGetByte( CHAR * pucByte )
{
*pucByte = USART_ReceiveData(USART1);
return TRUE;
}
void USART1_IRQHandler(void) //串口1中斷服務程序
{
u8 Res;
OSIntEnter();
if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET)
{
pxMBFrameCBByteReceived();
USART_ClearITPendingBit(USART1, USART_IT_RXNE);
}
if(USART_GetITStatus(USART1, USART_IT_TXE) != RESET)
{
pxMBFrameCBTransmitterEmpty();
USART_ClearITPendingBit(USART1, USART_IT_TXE);
}
OSIntExit(); //退出中斷
}
2.1 vMBPortSerialEnable()
函數
該函數實現STM32串口發送中斷和接收中斷的使能。
2.2xMBPortSerialInit()
函數
該函數對UART串口進行初始化,由eMBRTUInit函數進行調用。
2.3xMBPortSerialPutByte()
函數
串口發送函數,將STM32串口發送函數進行封裝,供協議棧使用
2.4xMBPortSerialGetByte()
函數
串口接收函數,將STM32串口接收函數進行封裝,供協議棧使用
2.5USART1_IRQHandler()
函數
串口中斷處理函數,包含發送中斷和接收中斷,都是調用之前mbrtu.c中的中斷函數進行處理,關於mbrtu.c,請參考之前的文章:
https://blog.csdn.net/u014100102/article/details/90543437
3、porttimer.c
該文件主要包含定時器處理相關函數,話不多說,直接貼代碼
/*
* FreeModbus Libary: BARE Port
* Copyright (C) 2006 Christian Walter <[email protected]>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
* File: $Id$
*/
/* ----------------------- Platform includes --------------------------------*/
#include "port.h"
/* ----------------------- Modbus includes ----------------------------------*/
#include "mb.h"
#include "mbport.h"
/* ----------------------- static functions ---------------------------------*/
/* ----------------------- Start implementation -----------------------------*/
BOOL
xMBPortTimersInit( USHORT usTim1Timerout50us )
{
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE); //時鐘使能
TIM_TimeBaseInitStructure.TIM_Period = usTim1Timerout50us;
TIM_TimeBaseInitStructure.TIM_Prescaler=16800-1; //定時器分頻 50us
TIM_TimeBaseInitStructure.TIM_CounterMode=TIM_CounterMode_Up; //向上計數模式
TIM_TimeBaseInitStructure.TIM_ClockDivision=TIM_CKD_DIV1;
TIM_TimeBaseInit(TIM2,&TIM_TimeBaseInitStructure);//初始化TIM
NVIC_InitStructure.NVIC_IRQChannel=TIM2_IRQn; //定時器3中斷
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=1; //搶佔優先級1
NVIC_InitStructure.NVIC_IRQChannelSubPriority=9; //子優先級8
NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE;
NVIC_Init(&NVIC_InitStructure);
//TIM_ITConfig(TIM2,TIM_IT_Update,ENABLE); //允許定時器更新中斷
TIM_Cmd(TIM2,ENABLE); //使能定時器
return TRUE;
}
inline void
vMBPortTimersEnable( )
{
/* Enable the timer with the timeout passed to xMBPortTimersInit( ) */
TIM_ClearITPendingBit(TIM2, TIM_IT_Update);
TIM_SetCounter(TIM2, 0);
//TIM_Cmd(TIM2, ENABLE);
TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE);
}
inline void
vMBPortTimersDisable( )
{
/* Disable any pending timers. */
TIM_SetCounter(TIM2, 0);
//TIM_Cmd(TIM2, DISABLE);
TIM_ITConfig(TIM2, TIM_IT_Update, DISABLE);
}
void TIM2_IRQHandler(void)
{
OSIntEnter(); //進入中斷
if(TIM_GetITStatus(TIM2,TIM_IT_Update)==SET) //溢出中斷
{
pxMBPortCBTimerExpired();
}
TIM_ClearITPendingBit(TIM2, TIM_IT_Update);
OSIntExit();
}
3.1 xMBPortTimersInit()
函數
定時器初始化函數,初始化定時器50us一次中斷。
3.2vMBPortTimersEnable()
函數
使能定時器中斷
3.3 vMBPortTimersDisable()
函數
禁止定時器中斷
3.4 TIM2_IRQHandler()
函數
定時器中斷處理函數,調用mbrtu.c中的定時器中斷函數,關於mbrtu.c,請參考之前的文章:
https://blog.csdn.net/u014100102/article/details/90543437
4、portevent.c
該文件主要包含事件處理相關函數,話不多說,直接貼代碼
/* ----------------------- Modbus includes ----------------------------------*/
#include "mb.h"
#include "mbport.h"
/* ----------------------- Variables ----------------------------------------*/
static eMBEventType eQueuedEvent;
static BOOL xEventInQueue;
/* ----------------------- Start implementation -----------------------------*/
BOOL
xMBPortEventInit( void )
{
xEventInQueue = FALSE;
return TRUE;
}
BOOL
xMBPortEventPost( eMBEventType eEvent )
{
xEventInQueue = TRUE;
eQueuedEvent = eEvent;
return TRUE;
}
BOOL
xMBPortEventGet( eMBEventType * eEvent )
{
BOOL xEventHappened = FALSE;
if( xEventInQueue )
{
*eEvent = eQueuedEvent;
xEventInQueue = FALSE;
xEventHappened = TRUE;
}
return xEventHappened;
}
4.1xMBPortEventInit()
函數
初始化事件隊列
4.2xMBPortEventPost()
函數
發送一個事件
4.1xMBPortEventGet()
函數
讀取一個事件
5、main.c
上面把接口文件都移植好了,現在看一下main函數怎麼使用
#define REG_HOLDING_START 0x2000
#define REG_HOLDING_NREGS 4
/* ----------------------- Static variables ---------------------------------*/
static USHORT usRegHoldingStart = REG_HOLDING_START;
static USHORT usRegHoldingBuf[REG_HOLDING_NREGS] = {23, 45, 88, 32};
int main(void)
{
delay_init(); //延時初始化
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //中斷分組配置
eMBInit(MB_RTU, 0x01, 0, 115200, MB_PAR_NONE); //初始化freemodbus 設置RTU模式和ID等
eMBEnable();
while(1)
{
eMBPoll();
delay_ms(30);
}
}
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 )
{
/* Pass current register values to the protocol stack. */
case MB_REG_READ:
while( usNRegs > 0 )
{
*pucRegBuffer++ =
( unsigned char )( usRegHoldingBuf[iRegIndex] >> 8 );
*pucRegBuffer++ =
( unsigned char )( usRegHoldingBuf[iRegIndex] &
0xFF );
iRegIndex++;
usNRegs--;
}
break;
/* Update current register values with new values from the
* protocol stack. */
case MB_REG_WRITE:
while( usNRegs > 0 )
{
usRegHoldingBuf[iRegIndex] = *pucRegBuffer++ << 8;
usRegHoldingBuf[iRegIndex] |= *pucRegBuffer++;
iRegIndex++;
usNRegs--;
}
}
}
else
{
eStatus = MB_ENOREG;
}
return eStatus;
}
/**
* @功能
* @參數
* @返回值
*/
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++ =
( unsigned char )( usRegInputBuf[iRegIndex] >> 8 );
*pucRegBuffer++ =
( unsigned char )( usRegInputBuf[iRegIndex] & 0xFF );
iRegIndex++;
usNRegs--;
}
}
else
{
eStatus = MB_ENOREG;
}
return eStatus;
}
這裏主函數主要是進行協議棧的初始化、使能和輪循,之前的博客已經介紹過,再次不做介紹。可以看之前的文章。下面兩個函數實現具體的邏輯,就是讀保持寄存器的值和寄存器輸入。這兩個函數都由freemodbus中具體的功能碼處理函數調用。
6、其他注意地方
FreeModbus代碼中有一處有問題,代碼如下:
eMBException
eMBFuncReadHoldingRegister( UCHAR * pucFrame, USHORT * usLen )
{
USHORT usRegAddress;
USHORT usRegCount;
UCHAR *pucFrameCur;
eMBException eStatus = MB_EX_NONE;
eMBErrorCode eRegStatus;
if( *usLen == ( MB_PDU_FUNC_READ_SIZE + MB_PDU_SIZE_MIN ) )
{
usRegAddress = ( USHORT )( pucFrame[MB_PDU_FUNC_READ_ADDR_OFF] << 8 );
usRegAddress |= ( USHORT )( pucFrame[MB_PDU_FUNC_READ_ADDR_OFF + 1] );
//usRegAddress++;
usRegCount = ( USHORT )( pucFrame[MB_PDU_FUNC_READ_REGCNT_OFF] << 8 );
usRegCount = ( USHORT )( pucFrame[MB_PDU_FUNC_READ_REGCNT_OFF + 1] );
/* Check if the number of registers to read is valid. If not
* return Modbus illegal data value exception.
*/
if( ( usRegCount >= 1 ) && ( usRegCount <= MB_PDU_FUNC_READ_REGCNT_MAX ) )
{
/* Set the current PDU data pointer to the beginning. */
pucFrameCur = &pucFrame[MB_PDU_FUNC_OFF];
*usLen = MB_PDU_FUNC_OFF;
/* First byte contains the function code. */
*pucFrameCur++ = MB_FUNC_READ_HOLDING_REGISTER;
*usLen += 1;
/* Second byte in the response contain the number of bytes. */
*pucFrameCur++ = ( UCHAR ) ( usRegCount * 2 );
*usLen += 1;
/* Make callback to fill the buffer. */
eRegStatus = eMBRegHoldingCB( pucFrameCur, usRegAddress, usRegCount, MB_REG_READ );
/* If an error occured convert it into a Modbus exception. */
if( eRegStatus != MB_ENOERR )
{
eStatus = prveMBError2Exception( eRegStatus );
}
else
{
*usLen += usRegCount * 2;
}
}
else
{
eStatus = MB_EX_ILLEGAL_DATA_VALUE;
}
}
else
{
/* Can't be a valid request because the length is incorrect. */
eStatus = MB_EX_ILLEGAL_DATA_VALUE;
}
return eStatus;
}
將以下這句話註釋掉
//usRegAddress++;
本人移植的代碼如下
說明:本人移植的代碼包含UCOSIII操作系統且在GNU編譯器下移植。
鏈接如下:
https://download.csdn.net/download/u014100102/11203636
其他FreeModbus代碼請參考我其他文章。