STM32移植FreeModbus

Posted 旧年不在666

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了STM32移植FreeModbus相关的知识,希望对你有一定的参考价值。

STM32移植FreeModbus

一、平台

  • 芯片:STM32F103RDT6
  • FreeModbus版本:freemodbus-v1.5.0
  • 开发工具:stm32cubemx V6.1.2 + keil MDK V5.27.1.0
  • 开发所用的库:HAL库

二、源码

三、串口、定时器配置

在这里插入图片描述

在这里插入图片描述

四、文件移植

  • 移植FreeModbus源码下的port、modbus文件到工程中并将所有的文件添加进工程中

在这里插入图片描述

在这里插入图片描述

  • 添加好后,编译整个工程,如果有报错,先不管它,继续移植;

五、移植串口相关接口

  • 串口接口文件在:port/portserial.c,需要移植的部分如下:
/*
 * FreeModbus Libary: BARE Port
 * Copyright (C) 2006 Christian Walter <wolti@sil.at>
 *
 * 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: portserial.c,v 1.1 2006/08/22 21:35:13 wolti Exp $
 */

#include "port.h"
#include "usart.h"
#include "main.h"
#include <stdint.h>
//#include "stm32f1xx_ll_usart.h"

/* ----------------------- Modbus includes ----------------------------------*/
#include "mb.h"
#include "mbport.h"

/* ----------------------- static functions ---------------------------------*/
void prvvUARTTxReadyISR( void );
void prvvUARTRxISR( void );

// uint8_t modbus_usart_rx_data;
// uint8_t modbus_usart_tx_data;

/* ----------------------- Start implementation -----------------------------*/
void
vMBPortSerialEnable( BOOL xRxEnable, BOOL xTxEnable )
{
    /* If xRXEnable enable serial receive interrupts. If xTxENable enable
     * transmitter empty interrupts.
     */

    //STM32串口接收中断使能
    if(xRxEnable == TRUE)
    {
        //使能接收中断
        //HAL_UART_Receive_IT(&huart2, &modbus_usart_rx_data, 1);
        __HAL_UART_ENABLE_IT(&huart2, UART_IT_RXNE);
    }
    else
    {
        //禁止接收中断
        //HAL_UART_AbortReceive_IT(&huart2);
        __HAL_UART_DISABLE_IT(&huart2, UART_IT_RXNE);
    }

    //STM32串口发送中断使能
    if(xTxEnable == TRUE)
    {
        //使能发送中断
        //LL_USART_EnableIT_TXE(huart2.Instance);

        //通知MODBUS库发送缓冲区为空
        //prvvUARTTxReadyISR();

        __HAL_UART_ENABLE_IT(&huart2, UART_IT_TXE);
    }
    else
    {
        //LL_USART_DisableIT_TXE(huart2.Instance);
        __HAL_UART_DISABLE_IT(&huart2, UART_IT_TXE);
    }
}

BOOL
xMBPortSerialInit( UCHAR ucPORT, ULONG ulBaudRate, UCHAR ucDataBits, eMBParity eParity )
{
    //因为串口初始化已经由stm32cubemx生成,这里直接返回true
    return TRUE;
}

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. */

    // modbus_usart_tx_data = ucByte;
    // HAL_UART_Transmit_IT(&huart2, &modbus_usart_tx_data, 1);

    // return TRUE;

    if (HAL_UART_Transmit(&huart2, (uint8_t *)&ucByte, 1, 100) != HAL_OK)
    {
        return FALSE;
    }
    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.
     */

    // *pucByte = modbus_usart_rx_data;
    // HAL_UART_Receive_IT(&huart2, &modbus_usart_rx_data, 1);

    // return TRUE;

    if (HAL_UART_Receive(&huart2, (uint8_t *)pucByte, 1, 100) != HAL_OK)
    {
        return FALSE;
    }
    else
    {
        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.
 */
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.
 */
void prvvUARTRxISR( void )
{
    pxMBFrameCBByteReceived(  );
}

六、移植定时器相关接口

  • 定时器接口文件在:port/porttimer.c,需要移植的部分如下:
/*
 * FreeModbus Libary: BARE Port
 * Copyright (C) 2006 Christian Walter <wolti@sil.at>
 *
 * 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: porttimer.c,v 1.1 2006/08/22 21:35:13 wolti Exp $
 */

/* ----------------------- Platform includes --------------------------------*/
#include "port.h"
#include "tim.h"

/* ----------------------- Modbus includes ----------------------------------*/
#include "mb.h"
#include "mbport.h"

/* ----------------------- static functions ---------------------------------*/
void prvvTIMERExpiredISR( void );

/* ----------------------- Start implementation -----------------------------*/
BOOL
xMBPortTimersInit( USHORT usTim1Timerout50us )
{
    //因为定时器初始化已经由stm32cubemx生成,这里直接返回true
    return TRUE;
}


inline void
vMBPortTimersEnable(  )
{
    /* Enable the timer with the timeout passed to xMBPortTimersInit( ) */

    __HAL_TIM_CLEAR_IT(&htim3, TIM_IT_UPDATE);
    __HAL_TIM_ENABLE_IT(&htim3, TIM_IT_UPDATE);
    __HAL_TIM_SET_COUNTER(&htim3, 0);
    __HAL_TIM_ENABLE(&htim3);
}

inline void
vMBPortTimersDisable(  )
{
    /* Disable any pending timers. */

    __HAL_TIM_DISABLE(&htim3);
    __HAL_TIM_SET_COUNTER(&htim3, 0);
    __HAL_TIM_DISABLE_IT(&htim3, TIM_IT_UPDATE);
    __HAL_TIM_CLEAR_IT(&htim3, 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(  );
}

七、port.h相关接口移植

  • port.h文件在:port/port.h,需要移植的部分如下:
#define ENTER_CRITICAL_SECTION( )   __set_PRIMASK(1)
#define EXIT_CRITICAL_SECTION( )    __set_PRIMASK(0)

八、编写定时器和串口中断回调函数

  • 串口中断回调函数
/**
  * @brief This function handles USART2 global interrupt.
  */
void USART2_IRQHandler(void)
{
  /* USER CODE BEGIN USART2_IRQn 0 */
    drv_usart_irq_handler_callbake(&huart2);
  /* USER CODE END USART2_IRQn 0 */
  HAL_UART_IRQHandler(&huart2);
  /* USER CODE BEGIN USART2_IRQn 1 */
  /* USER CODE END USART2_IRQn 1 */
}

extern void prvvUARTTxReadyISR(void);
extern void prvvUARTRxISR(void);

/******************************************************************************
 * @Function: drv_usart2_rxne_irq_callbake
 * @Description: USART2接收中断服务函数
 * @Input: void
 * @Output: None
 * @Return: void
 * @Others: None
 * @param {UART_HandleTypeDef} *huart
*******************************************************************************/
void drv_usart2_rxne_irq_callbake(UART_HandleTypeDef *huart)
{
    prvvUARTRxISR();
}

/******************************************************************************
 * @Function: drv_usart2_txe_irq_callbake
 * @Description: USART2发送为空中断服务函数
 * @Input: void
 * @Output: None
 * @Return: void
 * @Others: None
 * @param {UART_HandleTypeDef} *huart
*******************************************************************************/
void drv_usart2_txe_irq_callbake(UART_HandleTypeDef *huart)
{
    prvvUARTTxReadyISR();
}

/******************************************************************************
 * @Function: drv_usart_irq_handler_callbake
 * @Description: USART中断回调函数
 * @Input: void
 * @Output: None
 * @Return: void
 * @Others: None
 * @param {UART_HandleTypeDef} *huart
*******************************************************************************/
void drv_usart_irq_handler_callbake(UART_HandleTypeDef *huart)
{
    if (huart->Instance == USART2)
    {
        if(__HAL_UART_GET_IT_SOURCE(huart, UART_IT_RXNE)!= RESET)
        {
            drv_usart2_rxne_irq_callbake(huart);
        }

        if(__HAL_UART_GET_IT_SOURCE(huart, UART_IT_TXE)!= RESET)
        {
            drv_usart2_txe_irq_callbake(huart);
        }

        HAL_NVIC_ClearPendingIRQ(USART2_IRQn);
    }
}
  • 定时器中断回调函数
extern void prvvTIMERExpiredISR( void );

/******************************************************************************
 * @Function: drv_tim3_update_irq_callbake
 * @Description: TIM3更新中断服务函数
 * @Input: void
 * @Output: None
 * @Return: void
 * @Others: None
*******************************************************************************/
void drv_tim3_update_irq_callbake(void)
{
    prvvTIMERExpiredISR();
}

/******************************************************************************
 * @Function: HAL_TIM_PeriodElapsedCallback
 * @Description: TIM更新中断回调函数
 * @Input: void
 * @Output: None
 * @Return: void
 * @Others: None
 * @param {TIM_HandleTypeDef} *htim
*******************************************************************************/
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
	if (htim->Instance == TIM3)
    {
        drv_tim3_update_irq_callbake();
    }
}

九、编写处理函数

  • 因为我只用到输入寄存器和保持寄存器,这里只给出这两个处理函数示例:
    • pal_modbus.h
    #ifndef __PAL_MODBUS_H__
    #define __PAL_MODBUS_H__
    
    #include <stdint.h>
    
    #ifndef MODBUS_SLAVE_ADDR
    #define MODBUS_SLAVE_ADDR                       0x5A
    #endif
    
    #define MODBUS_INPUT_REGISTER_DEFINE(reg)       (reg+1)
    #define MODBUS_HOLD_REGISTER_DEFINE(reg)        (reg+1)
    
    typedef struct
    {
        uint16_t reg;
        uint8_t reg_max_len;
        void (*write)(uint8_t *register_val, uint16_t len, uint16_t index);
        void (*read)(uint8_t *register_val, uint16_t len, uint16_t index);
    }modbus_cmd_ops_stu_t;
    
    typedef struct
    {
        uint16_t reg_addr;
        uint8_t read_write_reg_cnt;
        uint8_t read_write_reg_index;
    }modbus_debug_info_stu_t;
    
    #endif
    
    • pal_modbus.c
#include "pal_modbus.h"
#include "mb.h"

#define ARRAY_SIZE(a)       (sizeof(a) / sizeof(a[0]))

modbus_cmd_ops_stu_t input_reg[] = 
{
    {MODBUS_INPUT_REGISTER_DEFINE(0x30A0), 2, NULL, read_power_off_request},
    {MODBUS_INPUT_REGISTER_DEFINE(0x30D0), 4, NULL, read_temperature_data},
    {MODBUS_INPUT_REGISTER_DEFINE(0x30FC), 10, NULL, read_uitrasonic_data},
    {MODBUS_INPUT_REGISTER_DEFINE(0x3308), 40, NULL, read_mcu_version_info},
    {MODBUS_INPUT_REGISTER_DEFINE(0x3400), 12, NULL, read_rtc_data},
    {MODBUS_INPUT_REGISTER_DEFINE(0x3500), 4, NULL, read_periphral_status},
};

modbus_cmd_ops_stu_t hold_reg[] = 
{
    {MODBUS_HOLD_REGISTER_DEFINE(0x4132), 2, write_power_on_or_off_respond, NULL},
};

/******************************************************************************
 * @Function: eMBRegInputCB
 * @Description: 读输入寄存器处理函数
 * @Input: pucRegBuffer:存放要读取得数据的地址
 *         usAddress:读取的寄存器地址
 *         usNRegs:要读取的寄存器的个数
 * @Output: None
 * @Return: void
 * @Others: 对应功能码04
 * @param {UCHAR} *pucRegBuffer
 * @param {USHORT} usAddress
 * @param {USHORT} usNRegs
*******************************************************************************/
eMBErrorCode eMBRegInputCB(UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNRegs)
{
    eMBErrorCode eStatus = MB_ENOERR;
    uint8_t i = 0;
    uint8_t index = 0;

    for (i = 0; i < ARRAY_SIZE(input_reg); i++)
    {
        if ((usAddress >= input_reg[i].reg) && ((usAddress + usNRegs) <= (input_reg[i].reg+input_reg[i].reg_max_len)))
        {
            index = usAddress-input_reg[i].reg;
            input_reg[i].read(pucRegBuffer, 2*usNRegs, 2*index);
            eStatus = MB_ENOERR;
            break;
        }
        else
        {
            eStatus = MB_ENOREG;
        }
    }

    return eStatus;
}

/******************************************************************************
 * @Function: eMBRegHoldingCB
 * @Description: 保持寄存器处理函数
 * @Input: pucRegBuffer:读写数据地址
 *         usAddress:读写寄存器地址
 *         usNRegs:读写寄存器个数
 *         eMode:读写模式
 * @Output: None
 * @Return: void
 * @Others: 对应功能码03、06、16
 * @param {UCHAR} *pucRegBuffer
 * @param {USHORT} usAddress
 * @param {USHORT} usNRegs
 * @param {eMBRegisterMode} eMode
*******************************************************************************/
eMBErrorCode eMBRegHoldingCB(UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNRegs, eMBRegisterMode eMode)
{
    eMBErrorCode eStatus = MB_ENOERR;
    uint8_t i = 0;
    uint8_t index = 0;

    for (i = 0; i < ARRAY_SIZE(hold_reg); i++)
    {
        if ((usAddress >= hold_reg[i].reg) && (usAddress <= hold_reg[i].reg+hold_reg[i].reg_max_len))
        {
            switch (eMode)
            {
                case MB_REG_READ:
                    index = usAddress-hold_reg[i].reg;
                    hold_reg[i].read(pucRegBuffer, 2*usNRegs, 2*index);
                    eStatus = MB_ENOERR;
                    break;

                case MB_REG_WRITE:
                    index = usAddress-hold_reg[i].reg;
                    hold_reg[i].write(pucRegBuffer, 2*usNRegs, 2*index);
                    eStatus = MB_ENOERR;
                    break;
            }
        }
        else
        {
            eStatus = MB_ENOREG;
        }
    }

    return eStatus;
}

/******************************************************************************
 * @Function: eMBRegCoilsCB
 * @Description: 线圈寄存器处理函数
 * @Input: pucRegBuffer:读写数据地址
 *         usAddress:读写寄存器地址
 *         usNRegs:读写寄存器个数
 *         eMode:读写模式
 * @Output: None
 * @Return: void
 * @Others: 对应功能码01、05、15
 * @param {UCHAR} *pucRegBuffer
 * @param {USHORT} usAddress
 * @param {USHORT} usNCoils
 * @param {eMBRegisterMode} eMode
*******************************************************************************/
eMBErrorCode eMBRegCoilsCB(UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNCoils, eMBRegisterMode eMode)
{
    eMBErrorCode eStatus = MB_ENOERR;

    return eStatus;
}

/******************************************************************************
 * @Function: eMBRegDiscreteCB
 * @Description: 
 * @Input: pucRegBuffer:存放要读取得数据的地址
 *         usAddress:读取的寄存器地址
 *         usNRegs:要读取的寄存器的个数
 * @Output: None
 * @Return: void
 * @Others: 对应功能码02
 * @param {UCHAR} *pucRegBuffer
 * @param {USHORT} usAddress
 * @param {USHORT} usNDiscrete
*******************************************************************************/
eMBErrorCode eMBRegDiscreteCB(UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNDiscrete)
{
    eMBErrorCode eStatus = MB_ENOERR;

    return eStatus;
}

十、开启Modbus功能

  • 先初始化Modbus,再轮询的方式调用
/******************************************************************************
 * @Function: pal_modbus_init
 * @Description: MODBUS初始化
 * @Input: void
 * @Output: None
 * @Return: void
 * @Others: None
*******************************************************************************/
void pal_modbus_init(void)
{
    eMBInit(MB_RTU, MODBUS_SLAVE_ADDR, 0x02, 115200, MB_PAR_NONE);
    eMBSetSlaveID(MODBUS_SLAVE_ADDR, TRUE, NULL, 0);
    eMBEnable();
    
    while (1)
    {
        eMBPoll();
    }
}

以上是关于STM32移植FreeModbus的主要内容,如果未能解决你的问题,请参考以下文章

FreeModbus移植实例(转)

STM32F103移植FreeModbus实现ModbusRTU和ModbusTCP同时工作

单片机的freemodbus移植应用

FreeModbus RTU 移植指南

协议 freemodbus的分层结构分析

使用 freemodbus 托管多个客户端