FreeModbus开源协议栈的移植和详解(四)- FreeModbus在STM32上的移植

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
      • 4.1`xMBPortEventInit()`函数
      • 4.2`xMBPortEventPost()`函数
      • 4.1`xMBPortEventGet()`函数
    • 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 
 *
 * 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代码请参考我其他文章。

你可能感兴趣的:(FreeModbus)