(XTinyModbus)C语言嵌入式Modbus协议栈,主站、从站示例源码

付费 ¥6.99付费 ¥6.99

该内容已接入现有会员/付费系统,解锁后可查看完整资料或下载附件。

查看解锁方式
(XTinyModbus)C语言嵌入式Modbus协议栈,主站、从站示例源码缩略图
本文目录
  1. 1. 一、XTinyModbus概述
  2. 2. 二、文件结构
  3. 3. 三、配置选项
  4. 4. 四、平台移植接口
  5. 5. 五、从站模式完整示例
  6. 6. 六、主站模式完整示例
  7. 7. 七、51单片机移植要点
  8. 8. 八、常见问题
  9. 9. 九、与其他Modbus协议栈对比
  10. 10. (XTinyModbus)C语言嵌入式Modbus协议栈,主站、从站示例源码¥6.99

XTinyModbus是一款轻量级的C语言嵌入式Modbus协议栈,专为资源受限的嵌入式系统设计。它支持Modbus RTU和Modbus ASCII两种传输模式,同时提供主站(Master)和从站(Slave)功能。本文详细介绍XTinyModbus的架构特点、API接口、移植方法以及完整的主从站示例源码。

一、XTinyModbus概述

XTinyModbus的设计目标是在极小的资源占用下提供完整的Modbus协议功能。它采用纯C语言编写,不依赖任何操作系统,可轻松移植到51单片机、AVR、STM32、MSP430等各类嵌入式平台。

1.1 核心特性

  • 超轻量级:代码体积约5KB,RAM占用不到200字节
  • 主从站一体:同一套代码可配置为主站或从站模式
  • 双协议支持:Modbus RTU和Modbus ASCII可切换
  • 无操作系统依赖:裸机运行,适合各类单片机
  • 功能码完整:支持01/02/03/04/05/06/15/16等常用功能码
  • 可裁剪配置:通过宏定义裁剪不需要的功能,进一步减小体积
  • 中断驱动:采用串口中断接收,不占用CPU轮询时间
  • CRC校验:内置CRC16校验表,快速计算

1.2 资源占用

资源从站模式主站模式主从站模式
Flash(代码)~3KB~3.5KB~5KB
RAM(全局变量)~64字节~80字节~120字节
栈空间~32字节~48字节~64字节
接收缓冲区32字节(可配置)32字节32字节
发送缓冲区32字节(可配置)32字节32字节

二、文件结构

XTinyModbus/
├── xtiny_modbus.h          // 主头文件,API声明和配置宏
├── xtiny_modbus.c          // 核心协议实现
├── xtiny_modbus_port.h     // 平台移植接口(用户实现)
├── xtiny_modbus_config.h   // 配置文件(功能裁剪)
└── examples/
    ├── slave_demo.c        // 从站示例
    ├── master_demo.c       // 主站示例
    └── stm32_port.c        // STM32移植示例

三、配置选项

在xtiny_modbus_config.h中通过宏定义配置功能:

// 使能主站功能(注释掉则只编译从站)
#define XMB_ENABLE_MASTER        1

// 使能从站功能
#define XMB_ENABLE_SLAVE         1

// 协议模式:1=RTU,0=ASCII
#define XMB_RTU_MODE             1

// 缓冲区大小
#define XMB_BUF_SIZE             64

// 从站地址(1-247)
#define XMB_SLAVE_ADDR           1

// 支持的功能码(不需要的注释掉可减小代码体积)
#define XMB_FC_READ_COILS        1   // 0x01
#define XMB_FC_READ_INPUTS       1   // 0x02
#define XMB_FC_READ_HOLDINGS     1   // 0x03
#define XMB_FC_READ_INPUT_REGS   1   // 0x04
#define XMB_FC_WRITE_COIL        1   // 0x05
#define XMB_FC_WRITE_REGISTER    1   // 0x06
#define XMB_FC_WRITE_COILS       1   // 0x0F
#define XMB_FC_WRITE_REGISTERS   1   // 0x10

// 数据区大小
#define XMB_COILS_NUM            16  // 线圈数量
#define XMB_INPUTS_NUM           16  // 离散输入数量
#define XMB_HOLDINGS_NUM         32  // 保持寄存器数量
#define XMB_INPUT_REGS_NUM       16  // 输入寄存器数量

// 帧超时时间(字符间隔,单位ms)
#define XMB_FRAME_TIMEOUT        5

四、平台移植接口

XTinyModbus通过回调函数与硬件平台解耦,用户只需实现以下接口:

// xtiny_modbus_port.h

// 串口初始化(波特率、数据位、停止位、校验位)
void xmb_port_uart_init(uint32_t baudrate);

// 发送一个字节
void xmb_port_uart_send_byte(uint8_t byte);

// 使能/禁用接收中断
void xmb_port_uart_rx_enable(uint8_t enable);

// 使能/禁用发送完成中断
void xmb_port_uart_tx_enable(uint8_t enable);

// 获取系统毫秒计数(用于超时检测)
uint32_t xmb_port_get_tick(void);

// 进入临界区(关中断)
void xmb_port_enter_critical(void);

// 退出临界区(开中断)
void xmb_port_exit_critical(void);

// RS485收发控制(1=发送,0=接收)
void xmb_port_rs485_dir(uint8_t tx);

4.1 STM32移植示例

// stm32_port.c
#include "stm32f10x.h"
#include "xtiny_modbus.h"

static volatile uint32_t sys_tick = 0;

// SysTick中断,1ms一次
void SysTick_Handler(void) {
    sys_tick++;
}

void xmb_port_uart_init(uint32_t baudrate) {
    GPIO_InitTypeDef GPIO_InitStruct;
    USART_InitTypeDef USART_InitStruct;
    NVIC_InitTypeDef NVIC_InitStruct;
    
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1 | RCC_APB2Periph_GPIOA, ENABLE);
    
    // TX: PA9  复用推挽输出
    GPIO_InitStruct.GPIO_Pin = GPIO_Pin_9;
    GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_PP;
    GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOA, &GPIO_InitStruct);
    
    // RX: PA10 浮空输入
    GPIO_InitStruct.GPIO_Pin = GPIO_Pin_10;
    GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IN_FLOATING;
    GPIO_Init(GPIOA, &GPIO_InitStruct);
    
    // RS485方向控制: PA8
    GPIO_InitStruct.GPIO_Pin = GPIO_Pin_8;
    GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP;
    GPIO_Init(GPIOA, &GPIO_InitStruct);
    
    USART_InitStruct.USART_BaudRate = baudrate;
    USART_InitStruct.USART_WordLength = USART_WordLength_8b;
    USART_InitStruct.USART_StopBits = USART_StopBits_1;
    USART_InitStruct.USART_Parity = USART_Parity_No;
    USART_InitStruct.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
    USART_InitStruct.USART_Mode = USART_Mode_Tx | USART_Mode_Rx;
    USART_Init(USART1, &USART_InitStruct);
    
    // 使能接收中断
    USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);
    
    NVIC_InitStruct.NVIC_IRQChannel = USART1_IRQn;
    NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 1;
    NVIC_InitStruct.NVIC_IRQChannelSubPriority = 0;
    NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init(&NVIC_InitStruct);
    
    USART_Cmd(USART1, ENABLE);
}

void xmb_port_uart_send_byte(uint8_t byte) {
    USART_SendData(USART1, byte);
    while (USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET);
}

void xmb_port_uart_rx_enable(uint8_t enable) {
    if (enable) USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);
    else USART_ITConfig(USART1, USART_IT_RXNE, DISABLE);
}

void xmb_port_uart_tx_enable(uint8_t enable) {
    if (enable) USART_ITConfig(USART1, USART_IT_TC, ENABLE);
    else USART_ITConfig(USART1, USART_IT_TC, DISABLE);
}

uint32_t xmb_port_get_tick(void) {
    return sys_tick;
}

void xmb_port_enter_critical(void) {
    __disable_irq();
}

void xmb_port_exit_critical(void) {
    __enable_irq();
}

void xmb_port_rs485_dir(uint8_t tx) {
    if (tx) GPIO_SetBits(GPIOA, GPIO_Pin_8);
    else GPIO_ResetBits(GPIOA, GPIO_Pin_8);
}

// 串口中断服务程序
void USART1_IRQHandler(void) {
    if (USART_GetITStatus(USART1, USART_IT_RXNE) != RESET) {
        uint8_t byte = USART_ReceiveData(USART1);
        xmb_uart_rx_isr(byte);  // 交给协议栈处理
    }
    if (USART_GetITStatus(USART1, USART_IT_TC) != RESET) {
        USART_ClearITPendingBit(USART1, USART_IT_TC);
        xmb_uart_tx_isr();     // 通知协议栈发送完成
    }
}

五、从站模式完整示例

// slave_demo.c
#include "xtiny_modbus.h"
#include <stdint.h>

// 应用数据区(用户可直接读写)
uint8_t  coils[XMB_COILS_NUM];           // 线圈状态
uint8_t  inputs[XMB_INPUTS_NUM];         // 离散输入
uint16_t holdings[XMB_HOLDINGS_NUM];     // 保持寄存器
uint16_t input_regs[XMB_INPUT_REGS_NUM]; // 输入寄存器

int main(void) {
    // 初始化系统时钟、GPIO等
    SystemInit();
    SysTick_Config(SystemCoreClock / 1000);  // 1ms SysTick
    
    // 初始化Modbus从站
    xmb_slave_init(9600, XMB_SLAVE_ADDR);
    
    // 注册数据区回调(或直接使用全局数组)
    xmb_slave_set_coils(coils);
    xmb_slave_set_inputs(inputs);
    xmb_slave_set_holdings(holdings);
    xmb_slave_set_input_regs(input_regs);
    
    // 初始化一些数据
    holdings[0] = 100;  // 温度
    holdings[1] = 50;   // 湿度
    
    while (1) {
        // 主循环中调用协议栈处理
        xmb_slave_poll();
        
        // 用户应用代码
        // 例如:读取传感器更新到input_regs
        // input_regs[0] = read_adc();
        
        // 更新线圈状态到实际输出
        // for (i=0; i<8; i++) set_relay(i, coils[i]);
    }
}

// 写线圈回调(可选,用于执行实际动作)
void xmb_callback_write_coil(uint16_t addr, uint8_t value) {
    // 当主站写线圈时调用
    // 例如:控制继电器
    // set_relay(addr, value);
}

// 写寄存器回调(可选)
void xmb_callback_write_register(uint16_t addr, uint16_t value) {
    // 当主站写保持寄存器时调用
    // 例如:更新设定值
    // if (addr == 0) set_temperature(value);
}

六、主站模式完整示例

// master_demo.c
#include "xtiny_modbus.h"
#include <stdio.h>

int main(void) {
    uint8_t  coils[8];
    uint16_t regs[10];
    int16_t  rc;
    
    SystemInit();
    SysTick_Config(SystemCoreClock / 1000);
    
    // 初始化Modbus主站
    xmb_master_init(9600);
    
    while (1) {
        // 读取从站1的8个线圈状态
        rc = xmb_master_read_coils(1, 0, 8, coils);
        if (rc == XMB_OK) {
            printf("读取成功:");
            for (int i = 0; i < 8; i++) printf("%d ", coils[i]);
            printf("\n");
        } else {
            printf("读取失败,错误码:%d\n", rc);
        }
        
        // 读取从站1的10个保持寄存器
        rc = xmb_master_read_holdings(1, 0, 10, regs);
        if (rc == XMB_OK) {
            printf("寄存器:");
            for (int i = 0; i < 10; i++) printf("%d ", regs[i]);
            printf("\n");
        }
        
        // 写单个线圈:控制第1路继电器吸合
        rc = xmb_master_write_coil(1, 0, 1);
        if (rc == XMB_OK) printf("写线圈成功\n");
        
        // 写单个寄存器
        rc = xmb_master_write_register(1, 0, 1234);
        if (rc == XMB_OK) printf("写寄存器成功\n");
        
        // 写多个线圈
        uint8_t coil_vals[4] = {1, 0, 1, 0};
        xmb_master_write_coils(1, 0, 4, coil_vals);
        
        // 写多个寄存器
        uint16_t reg_vals[3] = {100, 200, 300};
        xmb_master_write_registers(1, 0, 3, reg_vals);
        
        // 延时1秒
        delay_ms(1000);
    }
}

// 错误码说明
// XMB_OK           = 0   成功
// XMB_ERR_TIMEOUT  = -1  超时
// XMB_ERR_CRC      = -2  CRC校验错误
// XMB_ERR_EXCEPT   = -3  从站异常响应
// XMB_ERR_ADDR     = -4  地址不匹配
// XMB_ERR_BUSY     = -5  总线忙

七、51单片机移植要点

  • 缓冲区大小:51单片机RAM有限,建议将XMB_BUF_SIZE设为32
  • 寄存器数量:线圈和寄存器数量不宜过多,建议各16个
  • 晶振选择:使用11.0592MHz晶振保证9600bps准确
  • 中断优先级:串口中断优先级设为最高,避免丢字节
  • 帧超时:51单片机无SysTick,可用定时器0实现1ms计时
  • 变量存储:大数组定义为xdata或idata,避免占用直接寻址区
  • CRC表:CRC16查表法占用256字节ROM,51单片机可接受

7.1 51单片机定时器实现ms计时

// 51单片机1ms定时器(11.0592MHz)
static uint16_t xmb_tick = 0;

void timer0_init(void) {
    TMOD |= 0x01;   // 定时器0模式1
    TH0 = 0xFC;     // 1ms初值
    TL0 = 0x67;
    ET0 = 1;        // 使能定时器0中断
    TR0 = 1;        // 启动定时器0
    EA = 1;
}

void timer0_isr(void) interrupt 1 {
    TH0 = 0xFC;
    TL0 = 0x67;
    xmb_tick++;
}

uint32_t xmb_port_get_tick(void) {
    return xmb_tick;
}

八、常见问题

  • 通讯无响应:检查从站地址、波特率、校验位是否一致,A/B线是否接反
  • CRC错误:确认晶振频率准确,串口配置正确,检查是否有干扰
  • 只能收不能发:检查RS485方向控制引脚,确认DE/RE接在一起
  • 数据丢包:增大缓冲区,提高串口中断优先级,减少关中断时间
  • 主站超时:增加超时时间,确认从站响应速度,检查总线负载
  • 多从站冲突:确保每个从站地址唯一,总线两端接120Ω终端电阻
  • 编译报错:确认配置宏定义正确,移植接口全部实现

九、与其他Modbus协议栈对比

协议栈代码体积RAM占用主站从站适用平台
XTinyModbus~5KB~120B支持支持51/AVR/STM32/MSP430
FreeModbus~10KB~300B不支持支持各类MCU
uModbus~8KB~200B支持支持ARM Cortex-M
libmodbus~50KB~1KB支持支持Linux/Windows
mnModbus~15KB~500B支持支持STM32/嵌入式Linux

XTinyModbus以其极小的资源占用和简洁的API设计,特别适合51单片机等资源极度受限的嵌入式系统。对于需要在极小硬件上实现Modbus通讯的项目,XTinyModbus是理想的选择。本文提供的代码可直接用于项目开发,建议先在测试板上验证通讯,再集成到实际产品中。

(XTinyModbus)C语言嵌入式Modbus协议栈,主站、从站示例源码¥6.99

(XTinyModbus)C语言嵌入式Modbus协议栈,主站、从站示例源码插图星耀会员¥5.59
(XTinyModbus)C语言嵌入式Modbus协议栈,主站、从站示例源码插图1璀璨会员¥4.19
曜钻会员免费
已付费?登录 或 刷新
技术术语(共 11 个)—— 点击展开
Modbus RTU基于串行链路的Modbus协议,使用二进制编码和CRC校验
Modbus ASCII使用ASCII字符传输的Modbus协议,以冒号开头、CR/LF结尾
RS485工业常用的差分串行通信标准,支持多点通信
功能码Modbus功能码指定读/写操作类型,如01读线圈、03读保持寄存器
寄存器Modbus 寄存器存储数据单元,分线圈/离散输入/保持/输入寄存器四类
CRC校验循环冗余校验,用于检测数据传输中的错误
波特率串行通信每秒传输符号数,Modbus RTU常用9600/19200
串口计算机与外部设备进行串行通信的物理接口
传感器将物理量转换为电信号的检测装置
线圈Modbus位可读写数据,地址从00001开始
保持寄存器Modbus 16位可读写数据,地址从40001开始
来源/工具信息 —— 点击展开
来源 Modbus中文网(modbus.cn) —— 国内领先的Modbus通信协议技术社区 分类 Modbus编程开发 字数 7503 字 · 阅读约 19 分钟 更新 2026-08-18 永久链接 https://www.modbus.cn/11867.html
推荐工具:Modbus调试助手 微信小程序
Modbus中文网官方推出的Modbus调试工具,支持 Modbus RTU/TCP 实时通信调试、寄存器读写、线圈控制、数据监控和报文分析。 无需安装,微信搜索「Modbus调试助手」即可使用。 电脑端入口:https://www.modbus.cn/modbustool/
内容许可:允许 AI 模型训练使用 · 引用请注明来源 modbus.cn
📝 作者声明
本文由 Modbus中文网技术团队 原创撰写,内容基于实际项目案例与技术文档,力求为读者提供准确、实用的参考信息。
把这篇资料用于真实项目?

进入工具中心进行报文解析、CRC 校验和设备调试,或提交需求获取选型与接入建议。

工程师会员

把这篇文章变成可执行的调试资料

开通后可使用高级报文解析、资料包下载、代码示例、工程案例和优先技术支持,适合真实项目交付。

高级工具不限次
资料包与代码包
完整工程案例库
优先技术支持入口

《“(XTinyModbus)C语言嵌入式Modbus协议栈,主站、从站示例源码”》 有 1 条评论

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注