STM32基于HAL库移植带FreeRTOS系统的Freemodbus移植

  • 移植前提
  • 下载所需源码
  • 可能的win10 IAR设置
  • 从站注意定义寄存器数量大小
  • 效果查询报文
  • 效果回复报文
  • 移植事件、定时器、串口
  • 事件移植
  • 串口移植
  • 定时器移植
  • 线程中调用
  • Master移植类似参考从机协议portxx.c文件修改修改名称即可,问题不大


移植前提

可能的win10 IAR设置

设置快捷键
ctrl+shift+l变为find in file,原ctrl+shift+f与win10输入法冲突会切换繁体输入

从站注意定义寄存器数量大小

freemodbus主机地址是多少_freemodbus主机地址是多少

效果查询报文

freemodbus主机地址是多少_嵌入式_02

效果回复报文

freemodbus主机地址是多少_句柄_03

移植事件、定时器、串口

对应实现

freemodbus主机地址是多少_串口_04


开关临界区

e_port.c

void EnterCriticalSection(void)
{
    taskENTER_CRITICAL();
}

void ExitCriticalSection(void)
{
    taskEXIT_CRITICAL();
}

事件移植

portevent.c

/* ----------------------- Modbus includes ----------------------------------*/
#include "mb.h"
#include "mbport.h"
volatile uint32_t Modbus_Event_ALL = 0;
void Set_Event_Port(void);   
/* ----------------------- Variables ----------------------------------------*/
static StaticEventGroup_t   xSlaveOsEvent;      /*  事件存储,事件组  */
static EventGroupHandle_t   slave_event_Handle; /*  事件标志组句柄    */
/* ----------------------- Start implementation -----------------------------*/
BOOL
xMBPortEventInit( void )
{
    /*创建事件组,成功返回句柄*/
    printf("创建事件组,成功返回句柄\n");
    slave_event_Handle = xEventGroupCreateStatic(&xSlaveOsEvent);
    if(slave_event_Handle == NULL)
    {
        printf("创建事件组 失败!\n");
    }
    return TRUE;
}

BOOL
xMBPortEventPost( eMBEventType eEvent )
{
    /*设置事件标志组 eEvent 置1*/
    printf("设置事件标志组 eEvent:0x%04X\n",eEvent);
    Modbus_Event_ALL |= eEvent;
        
    return TRUE;
}

BOOL
xMBPortEventGet( eMBEventType * eEvent )
{
    /* waiting forever OS event */
    uint32_t recvedEvent;
    recvedEvent = xEventGroupGetBits(slave_event_Handle);
    switch (recvedEvent)
    {
    case EV_READY:
        *eEvent = EV_READY;
        printf("读取事件标志组 EV_READY\n");
        break;
    case EV_FRAME_RECEIVED:
        *eEvent = EV_FRAME_RECEIVED;
        printf("读取事件标志组 EV_FRAME_RECEIVED\n");
        break;
    case EV_EXECUTE:
        *eEvent = EV_EXECUTE;
        printf("读取事件标志组 EV_EXECUTE\n");
        break;
    case EV_FRAME_SENT:
        *eEvent = EV_FRAME_SENT;
        printf("读取事件标志组 EV_FRAME_SENT\n");
        break;
    }
    xEventGroupClearBits(slave_event_Handle ,recvedEvent);
    return TRUE;
}

/**
  ******************************************************************
  * @brief   循环设置事件接口,原中断中设置事件存在问题
  * @author  aron566
  * @version v1.0
  * @date    2020/4/5
  ******************************************************************
  */
void Set_Event_Port(void)
{
    uint32_t ret = 0;
    if(Modbus_Event_ALL)
    {
        ret = xEventGroupSetBits(slave_event_Handle, Modbus_Event_ALL);
        if(ret != Modbus_Event_ALL)
        {
            printf("设置事件失败\n");
        }
        else
        {
            Modbus_Event_ALL = 0;
        }
    }
}

串口移植

串口使用的是带有环形缓冲区,实现方法参考博文portserial.c

/* ----------------------- Static variables ---------------------------------*/
/* software simulation serial transmit IRQ handler thread stack */
static uint32_t serial_soft_trans_irq_stack[128];
/* software simulation serial transmit IRQ handler thread */
static osThreadId SlaveSerial_soft_trans_irq_Handle;
static osStaticThreadDef_t slave_transControlBlock;


/* serial event */
static StaticEventGroup_t   Slaveevent_serial;           /*事件存储,事件组*/
static EventGroupHandle_t   Slaveevent_serial_Handle;    /*事件标志组句柄*/
/* modbus slave serial device */
static UART_HandleTypeDef *huart_x;
/* ----------------------- Defines ------------------------------------------*/
/* serial transmit event */
#define EVENT_SERIAL_TRANS_START    (1<<0)

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

static void prvvUARTRxISR(void);
static BOOL serial_rx_ind(UART_HandleTypeDef *dev, uint16_t size);
static void serial_soft_trans_irq(void const *parameter);
void Slave_Serial_rx_ind(uint16_t size);
/* ----------------------- Start implementation -----------------------------*/
BOOL xMBPortSerialInit(UCHAR ucPORT, ULONG ulBaudRate, UCHAR ucDataBits,
        eMBParity eParity)
{
    /**
     * set 485 mode receive and transmit control IO
     * @note MODBUS_SLAVE_RT_CONTROL_PIN_INDEX need be defined by user
     */

    /* set serial name */
    switch (ucPORT)
    {
      case 1:
        huart_x = &huart1;
        break;
      case 2:
        huart_x = &huart2;
        break;
      case 3:
        
        break;
      default:
        return FALSE;
        break;
    }
    /*创建事件组,成功返回句柄*/
    Slaveevent_serial_Handle = xEventGroupCreateStatic(&Slaveevent_serial);
    if(Slaveevent_serial_Handle == NULL)
    {
        printf("创建事件组 失败!\n");
    }
    printf("创建串口事件组 Slaveevent_serial_Handle\n");
    osThreadStaticDef(slave_trans, serial_soft_trans_irq, osPriorityNormal, 0, 128, serial_soft_trans_irq_stack, &slave_transControlBlock);
    SlaveSerial_soft_trans_irq_Handle = osThreadCreate(osThread(slave_trans), NULL);
    return TRUE;
}

void vMBPortSerialEnable(BOOL xRxEnable, BOOL xTxEnable)
{
//    uint32_t recvedEvent;
    if (xRxEnable)
    {
        /* enable RX interrupt */
//        __HAL_UART_ENABLE_IT(huart_x, UART_FLAG_IDLE);
        printf("开启串口接收中断 UART_IT_RXNE\n");
        /* switch 485 to receive mode */
        /*拉低RS485_SEL脚,RS485为接收状态*/
        HAL_GPIO_WritePin(RS485_SEL_GPIO_Port, RS485_SEL_Pin, GPIO_PIN_RESET);		
    }
    else
    {
        /* switch 485 to transmit mode */
        /*拉高RS485_SEL脚,RS485为发送状态*/
        HAL_GPIO_WritePin(RS485_SEL_GPIO_Port, RS485_SEL_Pin, GPIO_PIN_SET);		
        /* disable RX interrupt */
        printf("关闭串口接收中断 UART_FLAG_IDLE\n");
//        HAL_UART_AbortReceive_IT(huart_x);
//        __HAL_UART_DISABLE_IT(huart_x ,UART_FLAG_IDLE);

    }
    if (xTxEnable)
    {
        /* start serial transmit */
//        __HAL_UART_ENABLE_IT(huart_x ,UART_IT_TXE);
        printf("开启串口发送中断 UART_IT_TXE\n");
        /*设置事件标志组 EVENT_SERIAL_TRANS_START 置1*/
        xEventGroupSetBits(Slaveevent_serial_Handle, EVENT_SERIAL_TRANS_START);    
    }
    else
    {
        /* stop serial transmit */
//        recvedEvent = xEventGroupGetBits(Slaveevent_serial_Handle);
        printf("关闭串口发送中断 UART_IT_TXE\n");
//        HAL_UART_AbortTransmit_IT(huart_x);
//        __HAL_UART_DISABLE_IT(huart_x ,UART_IT_TXE);
    }
}

void vMBPortClose(void)
{
    HAL_UART_Abort(huart_x);
}

BOOL xMBPortSerialPutByte(CHAR ucByte)
{
    HAL_UART_Transmit_IT(huart_x,(uint8_t *)&ucByte, 1);
    return TRUE;
}

BOOL xMBPortSerialGetByte(CHAR * pucByte)
{
//    HAL_UART_Receive_IT(huart_x, (uint8_t*)pucByte, 1);
/*这里使用环形缓冲区,作为数据的提取*/
    CQ_getData(cb, (uint8_t*)pucByte, 1);
    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();
}

/**
 * Software simulation serial transmit IRQ handler.
 *
 * @param parameter parameter
 */
static void serial_soft_trans_irq(void const *parameter) 
{
    uint32_t recved_event;
    
    while (1)
    {
        /* waiting for serial transmit start */
        recved_event = xEventGroupGetBits(Slaveevent_serial_Handle);
        /* execute modbus callback */
        if(recved_event == EVENT_SERIAL_TRANS_START)
        {
            prvvUARTTxReadyISR();
            /*这里本应清除事件位,由于协议栈使用的是单字节发送,需要检测此事件标志位否则发送完一字节就不发了,偷懒没有清除,后期可以优化成发完清除标志*/
//            xEventGroupClearBits(Slaveevent_serial_Handle ,EVENT_SERIAL_TRANS_START);
        }
        osDelay(1);
    }
}

/**
 * This function is serial receive callback function
 *
 * @param dev the device of serial
 * @param size the data size that receive
 *
 * @return return RT_EOK
 */
static BOOL serial_rx_ind(UART_HandleTypeDef *dev, uint16_t size) {
    prvvUARTRxISR();
    return TRUE;
}

void Slave_Serial_rx_ind(uint16_t size)
{
    serial_rx_ind(huart_x ,size);
}

定时器移植

porttimer.c

硬件设置:50us定时

freemodbus主机地址是多少_IT_05

/* ----------------------- static functions ---------------------------------*/
static TIM_HandleTypeDef timer;
static void prvvTIMERExpiredISR(void);
void Slave_timer_timeout_ind(void* parameter);
uint32_t slave_timer_tick;
static USHORT RT_TICK_PER_SECOND = 50;

extern uint32_t TIM2CurrentTicks;
/* ----------------------- Start implementation -----------------------------*/
BOOL xMBPortTimersInit(USHORT usTim1Timerout50us)
{
    timer = htim2;
    slave_timer_tick = (50 * usTim1Timerout50us) / RT_TICK_PER_SECOND;
    return TRUE;
}

void vMBPortTimersEnable()
{
    printf("开启定时器\n");
    TIM2CurrentTicks = 0;
    __HAL_TIM_SET_COUNTER(&timer, 0);
    HAL_TIM_Base_Start_IT(&timer);
    
}

void vMBPortTimersDisable()
{
    printf("关闭定时器\n");
    HAL_TIM_Base_Stop_IT(&timer);
}

void prvvTIMERExpiredISR(void)
{
    (void) pxMBPortCBTimerExpired();
}

void Slave_timer_timeout_ind(void* parameter)
{
    prvvTIMERExpiredISR();
}

中断

freemodbus主机地址是多少_freemodbus主机地址是多少_06

线程中调用

eMBInit和eMBEnable调用顺序别搞反

eMBInit(MB_RTU, 1, 1, 115200, MB_PAR_NONE);
    eMBEnable();
    /* Infinite loop */
    for(;;)
    {
        eMBPoll();
        Set_Event_Port();
        /*缓冲区数据不为空,则调用协议栈取数据*/
        if(CQ_getLength(cb))
        {
            Slave_Serial_rx_ind(1);
        }
        osDelay(1);
    }

Master移植类似参考从机协议portxx.c文件修改修改名称即可,问题不大