学习参考韦东山老师移植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
|
|