# modbus_master_f103rc **Repository Path**: workspaces_mcu/modbus_master_f103rc ## Basic Information - **Project Name**: modbus_master_f103rc - **Description**: 700行的modbus主站代码,支持异步同步收发,03 04 06 10功能码 - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2025-12-08 - **Last Updated**: 2025-12-08 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # Modbus Master 使用说明 本代码实现了一个高性能、轻量级的 Modbus RTU 主机协议栈,支持同步阻塞和异步非阻塞两种工作模式。协议高度抽象,可以把 C 文件和 H 文件放在**任意工程使用**,按要求调用即可。主机程序大小 23K,从机 17K,新程序初始化完 12K,协议栈占用约 10K 和 5K(算上调用测试实现)。发送然后接收,115200 稳定在 10ms 内,9600 稳定在 30ms 左右。 - 实现发送函数 `int8_t Modbus_Send(uint8_t* data, uint16_t len);` - 串口中断调用 `Modbus_Master_UART_RxCpltCallback(rx_buffer, Size);` - 即可完成初始化,剩下就是调用发送等待接收 ## 1. 功能特性 - **支持功能码**:(其他不常用,懒得写,用的时候拓展一下就行) - `03` (Read Holding Registers): 读取保持寄存器 - `04` (Read Input Registers):读取输入寄存器 - `06` (Write Single Register): 写单个寄存器 - `10` (Write Multiple Registers): 写多个寄存器 - **双模式支持**: - **同步阻塞模式**: 发送请求后挂起等待,直到收到回复或超时。 - **异步非阻塞模式**: 发送请求后立即返回,通过轮询状态获取结果,适合多任务环境。 - **队列管理** (可选): 支持多请求并发入队(需开启 `request_to_queue` 宏),适合多从机并发通讯。 ## 2. 快速集成指南 ### 2.1. 实现发送接口 用户需要 Modbus_Master.h 头文件的 `int8_t Modbus_Send(uint8_t* data, uint16_t len);` 在外部实现一个底层发送函数即可。 ```c #include "Modbus_Master.h" // 示例:基于 HAL 库的发送函数 int8_t Modbus_Send(uint8_t *data, uint16_t len) { HAL_GPIO_WritePin(GPIOA, GPIO_PIN_1, GPIO_PIN_SET); HAL_Delay(1); HAL_StatusTypeDef status = HAL_UART_Transmit(&huart2, data, len, 1000); // HAL_Delay(1); HAL_GPIO_WritePin(GPIOA, GPIO_PIN_1, GPIO_PIN_RESET); return (status == HAL_OK) ? 0 : -1; } ``` ### 2.2. 初始化配置 在程序初始化阶段最好给串口接收中断或者 DMA。 ```c // 启动串口接收 (推荐使用 DMA 空闲中断) HAL_UARTEx_ReceiveToIdle_DMA(&huart1, rx_buffer, 256); /* USER CODE END 2 */ ``` ### 2.3. 串口接收处理 在串口接收回调中调用解析函数。 ```c void HAL_UARTEx_RxEventCallback(UART_HandleTypeDef *huart, uint16_t Size) { if (huart->Instance == USART1) { // 将数据传递给 Modbus Master 解析器 Modbus_Master_UART_RxCpltCallback(rx_buffer, Size); // 重新开启 DMA 接收 HAL_UARTEx_ReceiveToIdle_DMA(&huart1, rx_buffer, 256); } } ``` ### 2.4 主函数处理 ```C int8_t Modbus_Send(uint8_t *data, uint16_t len) { HAL_GPIO_WritePin(GPIOA, GPIO_PIN_1, GPIO_PIN_SET); HAL_Delay(1); HAL_StatusTypeDef status = HAL_UART_Transmit(&huart2, data, len, 1000); // HAL_Delay(1); HAL_GPIO_WritePin(GPIOA, GPIO_PIN_1, GPIO_PIN_RESET); return (status == HAL_OK) ? 0 : -1; } int main(void) { //初始化你的外设 HAL_UARTEx_ReceiveToIdle_DMA(&huart2, modbus_rx_buffer, 64); uint16_t a = 0; uint32_t start_time,end_time; while (1) { HAL_GPIO_TogglePin(GPIOC, GPIO_PIN_15); start_time = HAL_GetTick(); Modbus_ReadHoldingRegisters(0x01,0x00,0x01,&a,1000); end_time = HAL_GetTick(); printf("[TX End] Time =%lu ms\r\n", end_time - start_time); printf("code 03 Read %d\r\n",a); HAL_Delay(1000); } } ``` ## 3. API 说明 ### 3.1. 读保持寄存器 (03) ```c int8_t Modbus_ReadHoldingRegisters(uint8_t slave_id, uint16_t start_addr, uint16_t num_regs, void *recv_data, uint32_t timeout); ``` - **recv_data**: - 当 `num_regs == 1` 时,传入 `uint16_t*` 变量地址。 - 当 `num_regs > 1` 时,传入 `uint16_t[]` 数组首地址。 - **timeout**: 超时时间 (ms)。设为 `0` 进入异步模式。 - **返回值**: - 同步模式: `MODBUS_SUCCESS` (9) 表示成功,负数表示错误。 - 异步模式: 返回队列索引 (>=0) 或错误码 (<0)。 ### 3.2. 写单个寄存器 (06) ```c int8_t Modbus_WriteSingleRegister(uint8_t slave_id, uint16_t reg_addr, uint16_t reg_value, uint32_t timeout); ``` ### 3.3. 写多个寄存器 (10) ```c int8_t Modbus_WriteMultipleRegisters(uint8_t slave_id, uint16_t start_addr, uint16_t num_regs, uint16_t *data, uint32_t timeout); ``` ### 3.4. 检查异步请求状态 ```c int8_t modbus_CheckRequestStatus(int8_t idx_or_funcode); ``` - **参数**: 异步调用返回的队列索引(开启队列模式时)或功能码(关闭队列模式时)。 - **返回值**: `1` 完成, `0` 进行中, `<0` 错误。 ## 4. 配置宏 (Modbus_Master.h) | 宏定义 | 说明 | 默认状态 | | :------------------ | :--------------------------------- | :------- | | `request_to_queue` | 开启请求队列模式,支持并发请求管理 | 开启 | | `LOG_DEBUG` | 日志输出宏,默认映射到 `printf` | `printf` | | `MAX_REQUEST_QUEUE` | 最大并发请求数量 | 4 | | `LOG_FUNCODE_xx` | 各功能码的日志开关 | 部分开启 | ## 5. 使用示例(详细见 main 函数) ### 5.1. 同步阻塞读取 最简单的使用方式,函数会阻塞直到收到数据或超时。 ```c uint16_t data[10]; int8_t result = Modbus_ReadHoldingRegisters(1, 0x0000, 10, data, 1000); // 超时 1000ms if (result == MODBUS_SUCCESS) { printf("Read Success: Reg[0]=%d\n", data[0]); } else { printf("Read Failed: %d\n", result); } ``` ### 5.2. 异步非阻塞读取 适合在主循环中轮询,不阻塞其他任务。 ```c // 发送请求 (timeout = 0) int8_t task_id = Modbus_ReadHoldingRegisters(1, 0x0000, 10, data, 0);//设置0进入异步模式 if (task_id >= 0) { // 在主循环中检查状态 while (1) { int8_t status = modbus_CheckRequestStatus(task_id); if (status == 1) { printf("Async Read Done!\n"); break; } // 执行其他任务... HAL_Delay(1); } } ```