打印
[应用相关]

STM32移植使用libmodbus

[复制链接]
749|7
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
tpgf|  楼主 | 2024-8-10 08:31 | 只看该作者 |只看大图 回帖奖励 |倒序浏览 |阅读模式
学习参考韦东山老师移植libmodbus方法,将libmodbus库移植到STM32H723上,记录备忘。

1、板载串口和USB虚拟串口封装
UART和USB配置之前文章已记录,面向对象方式封装板载串口和USB虚拟串口,方便后续libmodbus后端配置使用。

driver_uart.c
/**
  ******************************************************************************
  * @file    driver_uart.c
  * @author  GLC
  * @version V1.0
  * @date    2024-07-26
  * @brief   串口驱动程序。
  ******************************************************************************
  * @Attention
  *
  * 修改历史     版本号           作者        修改内容
  *-----------------------------------------------------
  * 2024.07.26    v1.0             GLC        创建文件
  *-----------------------------------------------------
  */

#include "driver_uart.h"
#include <stdio.h>
#include "driver_timer.h"
#include "FreeRTOS.h"
#include "task.h"
#include "semphr.h"
#include "queue.h"
#include "uart_device.h"

static SemaphoreHandle_t g_uart2TxSemaphore;
static uint8_t g_uart2RxBuf[BUF_LEN_UART2RX];
static QueueHandle_t g_uart2RxQueue;

static SemaphoreHandle_t g_uart4TxSemaphore;
static uint8_t g_uart4RxBuf[BUF_LEN_UART4RX];
static QueueHandle_t g_uart4RxQueue;



static int uart2Init(uint32_t baud, char parity, uint32_t dataBit, uint32_t stopBit);
static int uart2SendData(uint8_t *pData, uint32_t len, uint32_t timeout);
static int uart2GetData(uint8_t *pData, uint32_t timeout);
static int uart2Flush(void);

static int uart4Init(uint32_t baud, char parity, uint32_t dataBit, uint32_t stopBit);
static int uart4SendData(uint8_t *pData, uint32_t len, uint32_t timeout);
static int uart4GetData(uint8_t *pData, uint32_t timeout);
static int uart4Flush(void);


struct UART_Device g_uart2Dev = {"uart2", uart2Init, uart2SendData, uart2GetData, uart2Flush};
struct UART_Device g_uart4Dev = {"uart4", uart4Init, uart4SendData, uart4GetData, uart4Flush};


// 串口发送完成中断回调
void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart)
{
        if (huart == &huart2)
        {
                // 通知发送完成
                xSemaphoreGiveFromISR(g_uart2TxSemaphore, NULL);
        }
       
        if (huart == &huart4)
        {
                // 通知发送完成
                xSemaphoreGiveFromISR(g_uart4TxSemaphore, NULL);
        }
}

// 串口接收完成中断
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
        if (huart == &huart2)
        {
                // 写队列
                for (int i = 0; i < BUF_LEN_UART2RX; i++)
                {
                        xQueueSendFromISR(g_uart2RxQueue, (const void *)&g_uart2RxBuf, NULL);
                }
                // 重新启动接收
                HAL_UARTEx_ReceiveToIdle_DMA(&huart2, g_uart2RxBuf, BUF_LEN_UART2RX);
        }
       
        if (huart == &huart4)
        {
                // 写队列
                for (int i = 0; i < BUF_LEN_UART4RX; i++)
                {
                        xQueueSendFromISR(g_uart4RxQueue, (const void *)&g_uart4RxBuf, NULL);
                }
                // 重新启动接收
                HAL_UARTEx_ReceiveToIdle_DMA(&huart4, g_uart4RxBuf, BUF_LEN_UART4RX);
        }
}

// 串口接收事件中断(空闲中断)
void HAL_UARTEx_RxEventCallback(UART_HandleTypeDef *huart, uint16_t Size)
{
        if (huart == &huart2)
        {
                // 写队列
                for (int i = 0; i < Size; i++)
                {
                        xQueueSendFromISR(g_uart2RxQueue, (const void *)&g_uart2RxBuf, NULL);
                }
                // 重新启动接收
                HAL_UARTEx_ReceiveToIdle_DMA(&huart2, g_uart2RxBuf, BUF_LEN_UART2RX);
        }
       
        if (huart == &huart4)
        {
                // 写队列
                for (int i = 0; i < Size; i++)
                {
                        xQueueSendFromISR(g_uart4RxQueue, (const void *)&g_uart4RxBuf, NULL);
                }
                // 重新启动接收
                HAL_UARTEx_ReceiveToIdle_DMA(&huart4, g_uart4RxBuf, BUF_LEN_UART4RX);
        }
}

// 串口错误中断
void HAL_UART_ErrorCallback(UART_HandleTypeDef *huart)
{
        if (huart == &huart2)
        {
                // 重新启动接收
                HAL_UARTEx_ReceiveToIdle_DMA(&huart2, g_uart2RxBuf, BUF_LEN_UART2RX);
        }
       
        if (huart == &huart4)
        {
                // 重新启动接收
                HAL_UARTEx_ReceiveToIdle_DMA(&huart4, g_uart4RxBuf, BUF_LEN_UART4RX);
        }
}


/**
  * @brief uart2Init
  * @param baud parity dataBit stopBit
  * @retval 0
  * @attention
  *
  */
static int uart2Init(uint32_t baud, char parity, uint32_t dataBit, uint32_t stopBit)
{
        // 串口初始化
       
        if (!g_uart2RxQueue)
        {
                // 队列及信号量
                g_uart2RxQueue = xQueueCreate(QUEUE_LEN_UART2RX, 1);
                g_uart2TxSemaphore = xSemaphoreCreateBinary();
               
                // 开启DMA空闲中断接收
                HAL_UARTEx_ReceiveToIdle_DMA(&huart2, g_uart2RxBuf, BUF_LEN_UART2RX);
        }
       
        return 0;
}

/**
  * @brief uart2SendData
  * @param pData len timeOut
  * @retval 0-成功  非0-失败
  * @attention
  *
  */
static int uart2SendData(uint8_t *pData, uint32_t len, uint32_t timeout)
{
        HAL_UART_Transmit_DMA(&huart2, pData, len);
       
        // 等待发送完成
        if (pdTRUE == xSemaphoreTake(g_uart2TxSemaphore, timeout))
        {
                return 0;
        }
        else
        {
                return -1;
        }
}

/**
  * @brief uart2GetData
  * @param pData timeOut
  * @retval 0-成功  非0-失败
  * @attention
  *
  */
static int uart2GetData(uint8_t *pData, uint32_t timeout)
{
        // 从接收队列中取数据
        if (pdTRUE == xQueueReceive(g_uart2RxQueue, pData, timeout))
        {
                return 0;
        }
        else
        {
                return -1;
        }
}

/**
  * @brief uart2清空接收队列
  * @param none
  * @retval 清空字节数
  * @attention
  *
  */
static int uart2Flush(void)
{
        int cnt = 0;
        uint8_t data;
        while (1)
        {
                if (pdPASS != xQueueReceive(g_uart2RxQueue, &data, 0))
                        break;
                cnt++;
        }
        return cnt;
}

/**
  * @brief uart4Init
  * @param baud parity dataBit stopBit
  * @retval 0
  * @attention
  *
  */
static int uart4Init(uint32_t baud, char parity, uint32_t dataBit, uint32_t stopBit)
{
        // 串口初始化
       
        if (!g_uart4RxQueue)
        {
                // 队列及信号量
                g_uart4RxQueue = xQueueCreate(QUEUE_LEN_UART4RX, 1);
                g_uart4TxSemaphore = xSemaphoreCreateBinary();
               
                // 开启DMA空闲中断接收
                HAL_UARTEx_ReceiveToIdle_DMA(&huart4, g_uart4RxBuf, BUF_LEN_UART4RX);
        }
       
        return 0;
}

/**
  * @brief uart4SendData
  * @param pData len timeOut
  * @retval 0-成功  非0-失败
  * @attention
  *
  */
static int uart4SendData(uint8_t *pData, uint32_t len, uint32_t timeout)
{
        HAL_UART_Transmit_DMA(&huart4, pData, len);
       
        // 等待发送完成
        if (pdTRUE == xSemaphoreTake(g_uart4TxSemaphore, timeout))
        {
                return 0;
        }
        else
        {
                return -1;
        }
}

/**
  * @brief uart4GetData
  * @param pData timeOut
  * @retval 0-成功  非0-失败
  * @attention
  *
  */
static int uart4GetData(uint8_t *pData, uint32_t timeout)
{
        // 从接收队列中取数据
        if (pdTRUE == xQueueReceive(g_uart4RxQueue, pData, timeout))
        {
                return 0;
        }
        else
        {
                return -1;
        }
}

/**
  * @brief uart4清空接收队列
  * @param none
  * @retval 清空字节数
  * @attention
  *
  */
static int uart4Flush(void)
{
        int cnt = 0;
        uint8_t data;
        while (1)
        {
                if (pdPASS != xQueueReceive(g_uart4RxQueue, &data, 0))
                        break;
                cnt++;
        }
        return cnt;
}



/*********************************************END OF FILE**********************/
usbd_cdc_if.c
/* USER CODE BEGIN Header */
/**
  ******************************************************************************
  * @file           : usbd_cdc_if.c
  * @version        : v1.0_Cube
  * @brief          : Usb device for Virtual Com Port.
  ******************************************************************************
  * @attention
  *
  * Copyright (c) 2024 STMicroelectronics.
  * All rights reserved.
  *
  * This software is licensed under terms that can be found in the LICENSE file
  * in the root directory of this software component.
  * If no LICENSE file comes with this software, it is provided AS-IS.
  *
  ******************************************************************************
  */
/* USER CODE END Header */

/* Includes ------------------------------------------------------------------*/
#include "usbd_cdc_if.h"

/* USER CODE BEGIN INCLUDE */
#include "FreeRTOS.h"
#include "task.h"
#include "semphr.h"
#include "queue.h"
#include "uart_device.h"
/* USER CODE END INCLUDE */

/* Private typedef -----------------------------------------------------------*/
/* Private define ------------------------------------------------------------*/
/* Private macro -------------------------------------------------------------*/

/* USER CODE BEGIN PV */
/* Private variables ---------------------------------------------------------*/

/* USER CODE END PV */

/** @addtogroup STM32_USB_OTG_DEVICE_LIBRARY
  * @brief Usb device library.
  * @{
  */

/** @addtogroup USBD_CDC_IF
  * @{
  */

/** @defgroup USBD_CDC_IF_Private_TypesDefinitions USBD_CDC_IF_Private_TypesDefinitions
  * @brief Private types.
  * @{
  */

/* USER CODE BEGIN PRIVATE_TYPES */

/* USER CODE END PRIVATE_TYPES */

/**
  * @}
  */

/** @defgroup USBD_CDC_IF_Private_Defines USBD_CDC_IF_Private_Defines
  * @brief Private defines.
  * @{
  */

/* USER CODE BEGIN PRIVATE_DEFINES */
/* USER CODE END PRIVATE_DEFINES */

/**
  * @}
  */

/** @defgroup USBD_CDC_IF_Private_Macros USBD_CDC_IF_Private_Macros
  * @brief Private macros.
  * @{
  */

/* USER CODE BEGIN PRIVATE_MACRO */

/* USER CODE END PRIVATE_MACRO */

/**
  * @}
  */

/** @defgroup USBD_CDC_IF_Private_Variables USBD_CDC_IF_Private_Variables
  * @brief Private variables.
  * @{
  */

/* Create buffer for reception and transmission           */
/* It's up to user to redefine and/or remove those define */
/** Received data over USB are stored in this buffer      */
uint8_t UserRxBufferHS[APP_RX_DATA_SIZE];

/** Data to send over USB CDC are stored in this buffer   */
uint8_t UserTxBufferHS[APP_TX_DATA_SIZE];

/* USER CODE BEGIN PRIVATE_VARIABLES */
extern SemaphoreHandle_t g_usbTxSemaphore;
extern QueueHandle_t g_usbRxQueue;
/* USER CODE END PRIVATE_VARIABLES */

/**
  * @}
  */

/** @defgroup USBD_CDC_IF_Exported_Variables USBD_CDC_IF_Exported_Variables
  * @brief Public variables.
  * @{
  */

extern USBD_HandleTypeDef hUsbDeviceHS;

/* USER CODE BEGIN EXPORTED_VARIABLES */

/* USER CODE END EXPORTED_VARIABLES */

/**
  * @}
  */

/** @defgroup USBD_CDC_IF_Private_FunctionPrototypes USBD_CDC_IF_Private_FunctionPrototypes
  * @brief Private functions declaration.
  * @{
  */

static int8_t CDC_Init_HS(void);
static int8_t CDC_DeInit_HS(void);
static int8_t CDC_Control_HS(uint8_t cmd, uint8_t* pbuf, uint16_t length);
static int8_t CDC_Receive_HS(uint8_t* pbuf, uint32_t *Len);
static int8_t CDC_TransmitCplt_HS(uint8_t *pbuf, uint32_t *Len, uint8_t epnum);

/* USER CODE BEGIN PRIVATE_FUNCTIONS_DECLARATION */

/* USER CODE END PRIVATE_FUNCTIONS_DECLARATION */

/**
  * @}
  */

USBD_CDC_ItfTypeDef USBD_Interface_fops_HS =
{
  CDC_Init_HS,
  CDC_DeInit_HS,
  CDC_Control_HS,
  CDC_Receive_HS,
  CDC_TransmitCplt_HS
};

/* Private functions ---------------------------------------------------------*/

/**
  * @brief  Initializes the CDC media low layer over the USB HS IP
  * @retval USBD_OK if all operations are OK else USBD_FAIL
  */
static int8_t CDC_Init_HS(void)
{
  /* USER CODE BEGIN 8 */

  /* Set Application Buffers */
  USBD_CDC_SetTxBuffer(&hUsbDeviceHS, UserTxBufferHS, 0);
  USBD_CDC_SetRxBuffer(&hUsbDeviceHS, UserRxBufferHS);
  return (USBD_OK);
  /* USER CODE END 8 */
}

/**
  * @brief  DeInitializes the CDC media low layer
  * @param  None
  * @retval USBD_OK if all operations are OK else USBD_FAIL
  */
static int8_t CDC_DeInit_HS(void)
{
  /* USER CODE BEGIN 9 */
  return (USBD_OK);
  /* USER CODE END 9 */
}

/**
  * @brief  Manage the CDC class requests
  * @param  cmd: Command code
  * @param  pbuf: Buffer containing command data (request parameters)
  * @param  length: Number of data to be sent (in bytes)
  * @retval Result of the operation: USBD_OK if all operations are OK else USBD_FAIL
  */
static int8_t CDC_Control_HS(uint8_t cmd, uint8_t* pbuf, uint16_t length)
{
  /* USER CODE BEGIN 10 */
  switch(cmd)
  {
  case CDC_SEND_ENCAPSULATED_COMMAND:

    break;

  case CDC_GET_ENCAPSULATED_RESPONSE:

    break;

  case CDC_SET_COMM_FEATURE:

    break;

  case CDC_GET_COMM_FEATURE:

    break;

  case CDC_CLEAR_COMM_FEATURE:

    break;

  /*******************************************************************************/
  /* Line Coding Structure                                                       */
  /*-----------------------------------------------------------------------------*/
  /* Offset | Field       | Size | Value  | Description                          */
  /* 0      | dwDTERate   |   4  | Number |Data terminal rate, in bits per second*/
  /* 4      | bCharFormat |   1  | Number | Stop bits                            */
  /*                                        0 - 1 Stop bit                       */
  /*                                        1 - 1.5 Stop bits                    */
  /*                                        2 - 2 Stop bits                      */
  /* 5      | bParityType |  1   | Number | Parity                               */
  /*                                        0 - None                             */
  /*                                        1 - Odd                              */
  /*                                        2 - Even                             */
  /*                                        3 - Mark                             */
  /*                                        4 - Space                            */
  /* 6      | bDataBits  |   1   | Number Data bits (5, 6, 7, 8 or 16).          */
  /*******************************************************************************/
  case CDC_SET_LINE_CODING:

    break;

  case CDC_GET_LINE_CODING:

    break;

  case CDC_SET_CONTROL_LINE_STATE:

    break;

  case CDC_SEND_BREAK:

    break;

  default:
    break;
  }

  return (USBD_OK);
  /* USER CODE END 10 */
}

/**
  * @brief Data received over USB OUT endpoint are sent over CDC interface
  *         through this function.
  *
  *         @note
  *         This function will issue a NAK packet on any OUT packet received on
  *         USB endpoint until exiting this function. If you exit this function
  *         before transfer is complete on CDC interface (ie. using DMA controller)
  *         it will result in receiving more data while previous ones are still
  *         not sent.
  *
  * @param  Buf: Buffer of data to be received
  * @param  Len: Number of data received (in bytes)
  * @retval Result of the operation: USBD_OK if all operations are OK else USBD_FAILL
  */
static int8_t CDC_Receive_HS(uint8_t* Buf, uint32_t *Len)
{
  /* USER CODE BEGIN 11 */
        // 数据发送到接收队列
        for (int i = 0; i < *Len; i++)
        {
                xQueueSendFromISR(g_usbRxQueue, (const void *)&Buf, NULL);
        }

  USBD_CDC_SetRxBuffer(&hUsbDeviceHS, &Buf[0]);
  USBD_CDC_ReceivePacket(&hUsbDeviceHS);
  return (USBD_OK);
  /* USER CODE END 11 */
}

/**
  * @brief  Data to send over USB IN endpoint are sent over CDC interface
  *         through this function.
  * @param  Buf: Buffer of data to be sent
  * @param  Len: Number of data to be sent (in bytes)
  * @retval Result of the operation: USBD_OK if all operations are OK else USBD_FAIL or USBD_BUSY
  */
uint8_t CDC_Transmit_HS(uint8_t* Buf, uint16_t Len)
{
  uint8_t result = USBD_OK;
  /* USER CODE BEGIN 12 */
  USBD_CDC_HandleTypeDef *hcdc = (USBD_CDC_HandleTypeDef*)hUsbDeviceHS.pClassData;
  if (hcdc->TxState != 0){
    return USBD_BUSY;
  }
  USBD_CDC_SetTxBuffer(&hUsbDeviceHS, Buf, Len);
  result = USBD_CDC_TransmitPacket(&hUsbDeviceHS);
  /* USER CODE END 12 */
  return result;
}

/**
  * @brief  CDC_TransmitCplt_HS
  *         Data transmitted callback
  *
  *         @note
  *         This function is IN transfer complete callback used to inform user that
  *         the submitted Data is successfully sent over USB.
  *
  * @param  Buf: Buffer of data to be received
  * @param  Len: Number of data received (in bytes)
  * @retval Result of the operation: USBD_OK if all operations are OK else USBD_FAIL
  */
static int8_t CDC_TransmitCplt_HS(uint8_t *Buf, uint32_t *Len, uint8_t epnum)
{
  uint8_t result = USBD_OK;
  /* USER CODE BEGIN 14 */
        // 释放发送完成信号量
        xSemaphoreGiveFromISR(g_usbTxSemaphore, NULL);
       
  UNUSED(Buf);
  UNUSED(Len);
  UNUSED(epnum);
  /* USER CODE END 14 */
  return result;
}

/* USER CODE BEGIN PRIVATE_FUNCTIONS_IMPLEMENTATION */

// USB串口发送
/**
  * @brief  USB串口发送
  * @param  buf - 发送buffer,len - 发送长度,timeout - 超时时间ms
  * @retval 0 - 成功  非0 - 失败
  * @attention
  *
  */
int usbSerialSend(uint8_t *buf, uint16_t len, uint32_t timeout)
{
    if (USBD_OK == CDC_Transmit_HS(buf, len))
        {
                if (pdTRUE == xSemaphoreTake(g_usbTxSemaphore, timeout))
                {
                        return 0;
                }
                else
                {
                        return -1;
                }
        }
    else
    {
        return -1;
    }
}

// USB串口获取接收数据
/**
  * @brief  USB串口获取接收数据
  * @param  pData - 存放获取到的字节数据,timeout - 超时时间ms
  * @retval 0 - 成功  非0 - 失败
  * @attention
  *
  */
int usbSerialGetChar(uint8_t *pData, uint32_t timeout)
{
        if (g_usbRxQueue)
        {
                if (pdPASS == xQueueReceive(g_usbRxQueue, pData, timeout))
                {
                        return 0;
                }
                else
                {
                        return -1;
                }
        }
        else
        {
                return -1;
        }
}

// 清空USB接收队列
/**
  * @brief  清空USB接收队列
  * @param  none
  * @retval 清空的数据量
  * @attention
  *
  */
int usbSerialFlush(void)
{
        int cnt = 0;
        uint8_t data;
        while (1)
        {
                if (pdPASS != xQueueReceive(g_usbRxQueue, &data, 0))
                        break;
                cnt++;
        }
        return cnt;
}


static int usbSerial_Init(uint32_t baud, char parity, uint32_t data_bit, uint32_t stop_bit)
{
        return 0;
}

static int usbSerial_Send(uint8_t *datas, uint32_t len, uint32_t timeout)
{
        return usbSerialSend(datas, len, timeout);
}

static int usbSerial_GetData(uint8_t *pData, uint32_t timeout)
{
        return usbSerialGetChar(pData, timeout);
}

static int usbSerial_Flush(void)
{
        return usbSerialFlush();
}

struct UART_Device g_usbSerialDev = {"usb", usbSerial_Init, usbSerial_Send, usbSerial_GetData, usbSerial_Flush};

/* USER CODE END PRIVATE_FUNCTIONS_IMPLEMENTATION */

/**
  * @}
  */

/**
  * @}
  */
uart_device.h
#ifndef __UART_DEVICE_H
#define __UART_DEVICE_H

#include <stdint.h>


// 串口设备封装结构体
struct UART_Device {
    char *name;
        int (*init)(uint32_t baud, char parity, uint32_t dataBit, uint32_t stopBit);
        int (*send)(uint8_t *pData, uint32_t len, uint32_t timeout);
        int (*recvByte)(uint8_t *pData, uint32_t timeOut);
        int (*flush)(void);
};




struct UART_Device *getUartDevice(char *name);


#endif /* __UART_DEVICE_H */

uart_device.c
/**
  ******************************************************************************
  * @file    uart_device.c
  * @author  GLC
  * @version V1.0
  * @date    2024-07-27
  * @brief   串口封装管理。
  ******************************************************************************
  * @attention
  *
  * 修改历史     版本号           作者        修改内容
  *-----------------------------------------------------
  * 2024.07.27    v1.0             GLC        创建文件
  *-----------------------------------------------------
  */
#include <stdio.h>
#include <string.h>
#include "uart_device.h"


extern struct UART_Device g_uart2Dev;
extern struct UART_Device g_uart4Dev;
extern struct UART_Device g_usbSerialDev;


static struct UART_Device *g_uartDevices[] = {&g_uart2Dev, &g_uart4Dev, &g_usbSerialDev};



/**
  * @brief GetUARTDevice 根据串口设备名称获取串口设备
  * @param name-串口设备名称
  * @retval 成功-返回串口设备结构体指针  失败-返回空指针
  * @attention
  *
  */
struct UART_Device *getUartDevice(char *name)
{
        for (int i = 0; i < sizeof(g_uartDevices)/sizeof(g_uartDevices[0]); i++)
        {
                if (!strcmp(name, g_uartDevices->name))
                {
                        return g_uartDevices;
                }
        }
       
        return NULL;
}

2、添加libmodbus
将韦东山老师使用的改造过的libmodbus代码添加到自己的工程。



3、修改代码
修改modbus_rtu_private.h
修改modbus_rtu_private.h内的_modbus_rtu结构体,增加串口设备struct UART_Device *dev;

typedef struct _modbus_rtu {
    /* Device: "/dev/ttyS0", "/dev/ttyUSB0" or "/dev/tty.USA19*" on Mac OS X. */
    char *device;
    /* Bauds: 9600, 19200, 57600, 115200, etc */
    int baud;
    /* Data bit */
    uint8_t data_bit;
    /* Stop bit */
    uint8_t stop_bit;
    /* Parity: 'N', 'O', 'E' */
    char parity;
#if defined(_WIN32)
    struct win32_ser w_ser;
    DCB old_dcb;
#else
    /* Save old termios settings */
    //struct termios old_tios;
#endif
#if HAVE_DECL_TIOCSRS485
    int serial_mode;
#endif
#if HAVE_DECL_TIOCM_RTS
    int rts;
    int rts_delay;
    int onebyte_time;
    void (*set_rts)(modbus_t *ctx, int on);
#endif
    /* To handle many slaves on the same link */
    int confirmation_to_ignore;
        struct UART_Device *dev;
} modbus_rtu_t;
修改modbus-st-rtu.c
在modbus-st-rtu.c内新建_modbus_rtu_backend_uart结构体;

const modbus_backend_t _modbus_rtu_backend_uart = {
    _MODBUS_BACKEND_TYPE_RTU,
    _MODBUS_RTU_HEADER_LENGTH,
    _MODBUS_RTU_CHECKSUM_LENGTH,
    MODBUS_RTU_MAX_ADU_LENGTH,
    _modbus_set_slave,
    _modbus_rtu_build_request_basis,
    _modbus_rtu_build_response_basis,
    _modbus_rtu_prepare_response_tid,
    _modbus_rtu_send_msg_pre,
    _modbus_rtu_send,
    _modbus_rtu_receive,
    _modbus_rtu_recv,
    _modbus_rtu_check_integrity,
    _modbus_rtu_pre_check_confirmation,
    _modbus_rtu_connect,
    _modbus_rtu_is_connected,
    _modbus_rtu_close,
    _modbus_rtu_flush,
    _modbus_rtu_select,
    _modbus_rtu_free
};
修改modbus_new_st_rtu,修改后端结构体,增加串口设备赋值等;

modbus_t *
modbus_new_st_rtu(const char *device, int baud, char parity, int data_bit, int stop_bit)
{
    modbus_t *ctx;
    modbus_rtu_t *ctx_rtu;
        struct UART_Device *pdev;

    /* Check device argument */
    if (device == NULL || *device == 0) {
        debug_fprintf(stderr, "The device string is empty\n");
        errno = EINVAL;
        return NULL;
    }

    ctx = (modbus_t *) pvPortMalloc(sizeof(modbus_t));
    if (ctx == NULL) {
        return NULL;
    }

    _modbus_init_common(ctx);

        ctx->backend = &_modbus_rtu_backend_uart;
        pdev = GetUARTDevice((char *)device);
        if (!pdev)
        {
                modbus_free(ctx);
                errno = ENOENT;
                return NULL;
        }

    ctx->backend_data = (modbus_rtu_t *) pvPortMalloc(sizeof(modbus_rtu_t));
    if (ctx->backend_data == NULL) {
        modbus_free(ctx);
        errno = ENOMEM;
        return NULL;
    }
    ctx_rtu = (modbus_rtu_t *) ctx->backend_data;
        ctx_rtu->dev = pdev;

    /* Device name and \0 */
    ctx_rtu->device = (char *) pvPortMalloc((strlen(device) + 1) * sizeof(char));
    if (ctx_rtu->device == NULL) {
        modbus_free(ctx);
        errno = ENOMEM;
        return NULL;
    }

    strcpy(ctx_rtu->device, device);

    ctx_rtu->baud = baud;
    if (parity == 'N' || parity == 'E' || parity == 'O') {
        ctx_rtu->parity = parity;
    } else {
        modbus_free(ctx);
        errno = EINVAL;
        return NULL;
    }
    ctx_rtu->data_bit = data_bit;
    ctx_rtu->stop_bit = stop_bit;


    ctx_rtu->confirmation_to_ignore = FALSE;

    return ctx;
}
修改_modbus_rtu_send;

static ssize_t _modbus_rtu_send(modbus_t *ctx, const uint8_t *req, int req_length)
{
    // GLC UART_Device来发送数据
    modbus_rtu_t *ctx_rtu = ctx->backend_data;
        struct UART_Device *pdev = ctx_rtu->dev;

        if (0 == pdev->send((uint8_t *)req, req_length, TIMEROUT_SEND_MSG))
                return req_length;
    else
    {
                errno = EIO;
                return -1;
    }
}
修改_modbus_rtu_recv;

static ssize_t _modbus_rtu_recv(modbus_t *ctx, uint8_t *rsp, int rsp_length, int timeout)
{
    // GLC 使用UART_Device来接收数据
    modbus_rtu_t *ctx_rtu = ctx->backend_data;
        struct UART_Device *pdev = ctx_rtu->dev;

        if (0 == pdev->recvByte(rsp, timeout))
                return 1;
    else
    {
                errno = EIO;
                return -1;
    }
}
修改_modbus_rtu_connect;

static int _modbus_rtu_connect(modbus_t *ctx)
{
    // GLC 使用UART_Device来初始化设备
    modbus_rtu_t *ctx_rtu = ctx->backend_data;
        struct UART_Device *pdev = ctx_rtu->dev;

        pdev->init(ctx_rtu->baud, ctx_rtu->parity, ctx_rtu->data_bit, ctx_rtu->stop_bit);
       
    ctx->s = 1; //open(ctx_rtu->device, flags);
    return 0;
}
修改_modbus_rtu_flush;

static int _modbus_rtu_flush(modbus_t *ctx)
{
        // 使用UART_Device来flush数据
    modbus_rtu_t *ctx_rtu = ctx->backend_data;
        struct UART_Device *pdev = ctx_rtu->dev;

        return pdev->flush();
}
4、测试代码
test_modbus.c
/**
  ******************************************************************************
  * @file    test_modbus.c
  * @author  GLC
  * @version V1.0
  * @date    2024-08-05
  * @brief   libmodbus测试程序。
  ******************************************************************************
  * @attention
  *
  * 修改历史     版本号           作者        修改内容
  *-----------------------------------------------------
  * 2024.08.05    v1.0             GLC        创建文件
  *-----------------------------------------------------
  */

#include "test_modbus.h"
#include <stdio.h>
#include "FreeRTOS.h"
#include "task.h"
#include "semphr.h"
#include "queue.h"
#include "modbus.h"
#include "errno.h"
#include <string.h>
#include "driver_led.h"
#include "driver_debug_usart.h"


// USB从机,线圈0控制LED
void UsbSerialServerTask(void *pvParameters)       
{
        uint8_t *query;
        modbus_t *ctx;
        int rc;
        modbus_mapping_t *mb_mapping;
       
        // 创建一个新的RTU上下文(context)
        ctx = modbus_new_st_rtu("usb", 115200, 'N', 8, 1);
        // 将从机地址设为1
        modbus_set_slave(ctx, 1);
        // 动态分配一块内存来存储接收到的 Modbus 请求(或称为应用数据单元,ADU)
        query = pvPortMalloc(MODBUS_RTU_MAX_ADU_LENGTH);
        // 创建新的modbus映射,定义从机内存布局
        mb_mapping = modbus_mapping_new_start_address(0,        // 线圈起始地址
                                                                                                  10,        // 线圈数量
                                                                                                  0,        // 离散输入其实地址
                                                                                                  10,        // 离散输入数量
                                                                                                  0,        // 输入寄存器起始地址
                                                                                                  10,        // 输入寄存器数量
                                                                                                  0,        // 保持寄存器起始地址
                                                                                                  10);        // 保持寄存器数量
        // 将所有线圈和离散输入值置为0
        memset(mb_mapping->tab_bits, 0, mb_mapping->nb_bits);
        // 将所有输入寄存器和保持寄存器值置为0x8888 (16位)
        memset(mb_mapping->tab_registers, 0x88, mb_mapping->nb_registers*2);
        // 建立连接
        rc = modbus_connect(ctx);
        if (rc == -1)
        {
                modbus_free(ctx);
                vTaskDelete(NULL);
        }

        while (1)
        {
                do
                {
                        // 接收数据
                        rc = modbus_receive(ctx, query);
                        /* Filtered queries return 0 */
                } while (rc == 0);

                // 错误处理
                /* The connection is not closed on errors which require on reply such as
                   bad CRC in RTU. */
                if (rc == -1 && errno != EMBBADCRC) {
                        /* Quit */
                        continue;
                }
                // 回复
                rc = modbus_reply(ctx, query, rc, mb_mapping);
                if (rc == -1)
                {
                        //break;
                }
                // 测试:根据线圈0动作
                if (mb_mapping->tab_bits[0])
                {
                        LED_RED_ON();
                }
                else
                {
                        LED_RED_OFF();
                }
        }

        modbus_mapping_free(mb_mapping);
        vPortFree(query);
        /* For RTU */
        modbus_close(ctx);
        modbus_free(ctx);

        vTaskDelete(NULL);
}

// USB主机,读保持寄存器1,值自加1后写入保持寄存器2
void UsbSerialClientTask(void *pvParameters)       
{
        modbus_t *ctx;
        int rc;
        uint16_t val;
        int nb = 1;
       
        // 创建一个新的RTU上下文(context)
        ctx = modbus_new_st_rtu("usb", 115200, 'N', 8, 1);
        // 将连接的从机地址设为1
        modbus_set_slave(ctx, 1);
        // 建立连接
        rc = modbus_connect(ctx);
        if (rc == -1)
        {
                modbus_free(ctx);
                vTaskDelete(NULL);;
        }

        while (1)
        {
                // 读取寄存器地址为1的保持寄存器
                rc = modbus_read_registers(ctx, 1, nb, &val);
                if (rc != nb)
                        continue;

                val++;

                // 加1后写到寄存器地址为2的保持寄存器
                rc = modbus_write_registers(ctx, 2, nb, &val);
        }

        /* For RTU */
        modbus_close(ctx);
        modbus_free(ctx);

        vTaskDelete(NULL);
}

// 主机UART2,读取保持寄存器1并串口打印,重复置位复位线圈0
void CH1_UART2_ClientTask(void *pvParameters)       
{
        modbus_t *ctx;
        int rc;
        uint16_t val;
        int nb = 1;
        int level = 1;
       
        ctx = modbus_new_st_rtu("uart2", 115200, 'N', 8, 1);
        modbus_set_slave(ctx, 1);
       
        rc = modbus_connect(ctx);
        if (rc == -1)
        {
                modbus_free(ctx);
                vTaskDelete(NULL);;
        }

        while (1)
        {
                rc = modbus_read_registers(ctx, 1, nb, &val);
                if (rc != nb)
                {
                        continue;
                }

                lockPrintf();
                printf("read_registers: 0x%04x\n", val);
                unlockPrintf();

                /* delay 2s */
                vTaskDelay(2000);
                modbus_write_bit(ctx, 0, level);
                level = !level;
        }

        /* For RTU */
        modbus_close(ctx);
        modbus_free(ctx);

        vTaskDelete(NULL);
}

// 从机UART4,线圈0控制LED,保持寄存器1自加
void CH2_UART4_ServerTask(void *pvParameters)       
{
        uint8_t *query;
        modbus_t *ctx;
        int rc;
        modbus_mapping_t *mb_mapping;
       
        ctx = modbus_new_st_rtu("uart4", 115200, 'N', 8, 1);
        modbus_set_slave(ctx, 1);
        query = pvPortMalloc(MODBUS_RTU_MAX_ADU_LENGTH);

        mb_mapping = modbus_mapping_new_start_address(0,
                                                                                                  10,
                                                                                                  0,
                                                                                                  10,
                                                                                                  0,
                                                                                                  10,
                                                                                                  0,
                                                                                                  10);
       
        memset(mb_mapping->tab_bits, 0, mb_mapping->nb_bits);
        memset(mb_mapping->tab_registers, 0x88, mb_mapping->nb_registers*2);

        rc = modbus_connect(ctx);
        if (rc == -1)
        {
                modbus_free(ctx);
                vTaskDelete(NULL);;
        }

        while (1)
        {
                do {
                        rc = modbus_receive(ctx, query);
                } while (rc == 0);

                if (rc == -1 && errno != EMBBADCRC)
                {
                        continue;
                }

                rc = modbus_reply(ctx, query, rc, mb_mapping);
                if (rc == -1)
                {
                        //break;
                }

                if (mb_mapping->tab_bits[0])
                {
                        LED_RED_ON();
                }
                else
                {
                        LED_RED_OFF();
                }

                vTaskDelay(1000);
                mb_mapping->tab_registers[1]++;
        }

        modbus_mapping_free(mb_mapping);
        vPortFree(query);
        /* For RTU */
        modbus_close(ctx);
        modbus_free(ctx);

        vTaskDelete(NULL);
}


freertos.c
/* USER CODE BEGIN Header */
/**
  ******************************************************************************
  * File Name          : freertos.c
  * Description        : Code for freertos applications
  ******************************************************************************
  * @attention
  *
  * Copyright (c) 2024 STMicroelectronics.
  * All rights reserved.
  *
  * This software is licensed under terms that can be found in the LICENSE file
  * in the root directory of this software component.
  * If no LICENSE file comes with this software, it is provided AS-IS.
  *
  ******************************************************************************
  */
/* USER CODE END Header */

/* Includes ------------------------------------------------------------------*/
#include "FreeRTOS.h"
#include "task.h"
#include "main.h"
#include "cmsis_os.h"

/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include "system.h"
#include "test_uart.h"
#include "test_usb.h"
#include "test_modbus.h"
/* USER CODE END Includes */

/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */

/* USER CODE END PTD */

/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */

/* USER CODE END PD */

/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */

/* USER CODE END PM */

/* Private variables ---------------------------------------------------------*/
/* USER CODE BEGIN Variables */

static TaskHandle_t xLedTaskHandle;                                                //LED任务句柄
static TaskHandle_t xCH1TxTaskHandle;                                        //CH1发送任务句柄
static TaskHandle_t xCH2RxTaskHandle;                                        //CH2接收任务句柄
static TaskHandle_t xUSBTxTaskHandle;                                        //USB发送任务句柄
static TaskHandle_t xUSBRxTaskHandle;                                        //USB接收任务句柄
static TaskHandle_t xLibmodbusServerTaskHandle;                        //modbus从机任务句柄
static TaskHandle_t xLibmodbusClientTaskHandle;                        //modbus主机任务句柄
static TaskHandle_t xCH1_UART2_ClientTaskHandle;                //CH1_UART2主机任务句柄
static TaskHandle_t xCH2_UART4_ServerTaskHandle;                //CH2_UART4从机任务句柄

//static TaskHandle_t xJetsonStatusAskTaskHandle;                        //JetsonStatusAskTask任务句柄
//static TaskHandle_t xJetsonReplyProcessTaskHandle;                //JetsonReplyDataProcessTask
//static TaskHandle_t xEncoderTaskHandle;                                        //EncoderTask

/* USER CODE END Variables */
/* Definitions for defaultTask */
osThreadId_t defaultTaskHandle;
const osThreadAttr_t defaultTask_attributes = {
  .name = "defaultTask",
  .stack_size = 128 * 4,
  .priority = (osPriority_t) osPriorityNormal,
};

/* Private function prototypes -----------------------------------------------*/
/* USER CODE BEGIN FunctionPrototypes */

/* USER CODE END FunctionPrototypes */

void StartDefaultTask(void *argument);

extern void MX_USB_DEVICE_Init(void);
void MX_FREERTOS_Init(void); /* (MISRA C 2004 rule 8.1) */

/**
  * @brief  FreeRTOS initialization
  * @param  None
  * @retval None
  */
void MX_FREERTOS_Init(void) {
  /* USER CODE BEGIN Init */
       
        systemInit();
       
  /* USER CODE END Init */

  /* USER CODE BEGIN RTOS_MUTEX */
  /* add mutexes, ... */
  /* USER CODE END RTOS_MUTEX */

  /* USER CODE BEGIN RTOS_SEMAPHORES */
  /* add semaphores, ... */
  /* USER CODE END RTOS_SEMAPHORES */

  /* USER CODE BEGIN RTOS_TIMERS */
  /* start timers, add new ones, ... */
  /* USER CODE END RTOS_TIMERS */

  /* USER CODE BEGIN RTOS_QUEUES */
  /* add queues, ... */
  /* USER CODE END RTOS_QUEUES */

  /* Create the thread(s) */
  /* creation of defaultTask */
  defaultTaskHandle = osThreadNew(StartDefaultTask, NULL, &defaultTask_attributes);

  /* USER CODE BEGIN RTOS_THREADS */
  /* add threads, ... */
  /* USER CODE END RTOS_THREADS */

  /* USER CODE BEGIN RTOS_EVENTS */
  /* add events, ... */
  /* USER CODE END RTOS_EVENTS */

}

/* USER CODE BEGIN Header_StartDefaultTask */
/**
  * @brief  Function implementing the defaultTask thread.
  * @param  argument: Not used
  * @retval None
  */
/* USER CODE END Header_StartDefaultTask */
void StartDefaultTask(void *argument)
{
  /* init code for USB_DEVICE */
  MX_USB_DEVICE_Init();
  /* USER CODE BEGIN StartDefaultTask */
        /* test */
        //LedTest();
        //DebugUartTest();
       
       
        taskENTER_CRITICAL();
       
        xTaskCreate(LedTask, "LedTask", 128, NULL, osPriorityNormal-1, &xLedTaskHandle);
//        xTaskCreate(CH1_UART2_TxTask, "CH1_UART2_TxTask", 300, NULL, osPriorityNormal+10, &xCH1TxTaskHandle);
//        xTaskCreate(CH2_UART4_RxTask, "CH2_UART4_RxTask", 300, NULL, osPriorityNormal+10, &xCH2RxTaskHandle);
//        xTaskCreate(USBTest_TxTask, "USBTest_TxTask", 300, NULL, osPriorityNormal+10, &xUSBTxTaskHandle);
//        xTaskCreate(USBTest_RxTask, "USBTest_RxTask", 300, NULL, osPriorityNormal+10, &xUSBRxTaskHandle);
#if 0
#if 1
        xTaskCreate(UsbSerialServerTask, "LibmodbusServerTask", 300, NULL, osPriorityNormal+10, &xLibmodbusServerTaskHandle);
#else
        xTaskCreate(UsbSerialClientTask, "LibmodbusClientTask", 300, NULL, osPriorityNormal+10, &xLibmodbusClientTaskHandle);
#endif
#else
        xTaskCreate(CH1_UART2_ClientTask, "CH1_UART2_ClientTask", 300, NULL, osPriorityNormal+10, &xCH1_UART2_ClientTaskHandle);
        xTaskCreate(CH2_UART4_ServerTask, "CH2_UART4_ServerTask", 300, NULL, osPriorityNormal+10, &xCH2_UART4_ServerTaskHandle);
#endif
        vTaskDelete(defaultTaskHandle);       
       
        taskEXIT_CRITICAL();
       
       
  /* Infinite loop */
  for(;;)
  {
    osDelay(1);
  }
  /* USER CODE END StartDefaultTask */
}

/* Private application code --------------------------------------------------*/
/* USER CODE BEGIN Application */

/* USER CODE END Application */

5、测试验证
USB主机代码测试:

结果OK,图片略

USB从机代码测试:

结果OK,图片略

串口一主一从代码测试:

结果OK,图片略
————————————————

                            版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。

原文链接:https://blog.csdn.net/m0_53284507/article/details/140945187

使用特权

评论回复
沙发
而服务器人| | 2024-8-14 15:08 | 只看该作者
在多任务环境下,串口接收和发送的数据需要严格管理,避免由于竞争导致的数据丢失或混乱。

使用特权

评论回复
板凳
micoccd| | 2024-8-14 15:49 | 只看该作者
这个和普通的MODBUS有啥区别

使用特权

评论回复
地板
狄克爱老虎油| | 2024-8-17 23:30 | 只看该作者
好长的代码

使用特权

评论回复
5
花间一壶酒sd| | 2024-8-31 22:07 | 只看该作者
这是一个较为复杂的任务

使用特权

评论回复
6
花间一壶酒sd| | 2024-8-31 22:07 | 只看该作者
在实际硬件上进行充分的测试,以确保通信的可靠性和稳定性。

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

1931

主题

15611

帖子

11

粉丝