nanomodbus库移植笔记1:移植创建slave从机实例

移植nanomodbus到STM32f103c8t6 的个人笔记

目录

可参考文章:

nanomodbus下载:

库移植:

使用参考:

编写初始化文件:

定义modbus实例:

编写初始化函数:

编写回调函数:

nanomodbus_config.h

nanomodbus_config.c

使用:

NMBS_DEBUG_PRINT函数宏:

中断接收冲突问题:


可参考文章:

嵌入式MODBUS终极指南:nanoMODBUS轻量级库完整使用教程-CSDN博客

nanomodbus下载:

nanoMODBUS:A compact MODBUS RTU/TCP C library for embedded/microcontrollers - AtomGit | GitCode

库移植:

只需要俩个文件就能移植nanomodbus库了:

就是nanomodbus.c 和 nanomodbus.h

直接复制到keil的工程,别忘了添加路径就行

使用参考:

协议传输层主要参考port.c和port.h:

这里的应用很简单,其实只要

调用一个自己写的函数去初始化

调用库中的函数循环执行就行

编写初始化文件:

初始化函数是需要自己编写的,这里我直接写了一个文件#include "nanomodbus_config.h"

来存初始化函数以及它链接的一系列回调函数

回调函数也是需要自己写的,这里大部分都是抄的port.c的内容

定义modbus实例:

这俩就是定义的实例结构体,这里的初始化是作为从机的实例

nmbs_server中的

.id就是从机地址

.coils 和.regs就是不同类型的寄存器实际值的存放点

.regs就是保持寄存器,用0x03功能码就能读到

cpp 复制代码
nmbs_t nmbs_slave;            // 实例

nmbs_server_t nmbs_server = {
        .id = 0x02,
        .coils =
                {
                        0,
                },
        .regs =
                {
                        0,
                },
};

他们会在初始化函数以及poll中被调用

编写初始化函数:

最开头初始化 需要调用 自定义写好的 初始化函数

我这里的初始化是作为从机的实例

这个初始化函数主要定义并初始化了一些结构体,并链接了各个回调函数

回调函数也需要自己写,一般可以先抄写port.c的例子,有需要再自己改细节

需要注意的是:

nmbs_platform_conf_create(); 和 nmbs_callbacks_create();作用是清空结构体(准确讲应该是初始化,把所有链接成默认值,就比如它的crc函数就有自己默认的)

cpp 复制代码
//初始化 从机
nmbs_error nmodbus_rtu_slave_init(nmbs_t* nmbs, nmbs_server_t* _server)
{
	nmbs_platform_conf platform;  // 平台配置
	nmbs_callbacks callbacks;     // 从机回调
	
	//先初始化清空结构体
	nmbs_platform_conf_create(&platform);
	nmbs_callbacks_create(&callbacks);
	
	server = _server;
	
	platform.transport = NMBS_TRANSPORT_RTU;    // RTU
	platform.read    = uart_modbus_read;        // 串口读取函数
	platform.write   = uart_modbus_write;       // 串口发送函数
	platform.crc_calc = nmbs_crc_calc;          // CRC (nanomodbus 库中的 原生函数)
	//platform.crc_calc = Modbus_CRC16;         // CRC16(我的)
	
	// 绑定读写寄存器 回调函数
	callbacks.read_coils = server_read_coils;
  callbacks.read_holding_registers = server_read_holding_registers;
  callbacks.write_single_coil = server_write_single_coil;
  callbacks.write_multiple_coils = server_write_multiple_coils;
  callbacks.write_single_register = server_write_single_register;
  callbacks.write_multiple_registers = server_write_multiple_registers;
	
	
	 nmbs_error status = nmbs_server_create(nmbs, server->id, &platform, &callbacks);
  if (status != NMBS_ERROR_NONE) 
	{
     return status;
  }
   nmbs_set_byte_timeout(nmbs, 100);
   nmbs_set_read_timeout(nmbs, 1000);
	//nmbs_server_create(&nmbs_slave, 1, &platform, &callbacks);
	nmbs_set_platform_arg(&nmbs_slave, &huart1);
	
	return NMBS_ERROR_NONE;
}

编写回调函数:

回调函数基本都是抄的port.c的例子,下面会随着整个文件一块贴出了

nanomodbus_config.h

cpp 复制代码
#ifndef __nanomodbus_config_H__
#define __nanomodbus_config_H__

#include "stm32f1xx_hal.h"
#include "Uart.h"
#include "nanomodbus.h"
#include "string.h" 


#define MB_UART huart1
#define MB_RX_BUF_SIZE 256

#define COIL_BUF_SIZE 1024
#define REG_BUF_SIZE 2048

typedef struct tNmbsServer {
    uint8_t id;
    uint8_t coils[COIL_BUF_SIZE];
    uint16_t regs[REG_BUF_SIZE];
} nmbs_server_t;


extern nmbs_t nmbs_slave;      
extern nmbs_server_t nmbs_server;
//extern nmbs_platform_conf platform;
//extern uint16_t nmbs_holding_regs[];


nmbs_error nmodbus_rtu_slave_init(nmbs_t* nmbs, nmbs_server_t* _server);//初始化 从机



#endif

nanomodbus_config.c

cpp 复制代码
#include "nanomodbus_config.h"

static nmbs_server_t* server;
nmbs_t nmbs_slave;            // 实例
//nmbs_t nmbs;
nmbs_server_t nmbs_server = {
        .id = 0x02,
        .coils =
                {
                        0,
                },
        .regs =
                {
                        0,
                },
};

int32_t uart_modbus_read(uint8_t* buf, uint16_t count, int32_t byte_timeout_ms,void* arg);
int32_t uart_modbus_write(const uint8_t* buf, uint16_t count, int32_t byte_timeout_ms,void* arg);
//uint16_t Modbus_CRC16(const uint8_t* data, uint32_t length,void* arg);

static nmbs_error server_read_coils(uint16_t address, uint16_t quantity, nmbs_bitfield coils_out, uint8_t unit_id,void* arg);
static nmbs_error server_read_holding_registers(uint16_t address, uint16_t quantity, uint16_t* registers_out,uint8_t unit_id, void* arg);
static nmbs_error server_write_single_coil(uint16_t address, bool value, uint8_t unit_id, void* arg);
static nmbs_error server_write_multiple_coils(uint16_t address, uint16_t quantity, const nmbs_bitfield coils,uint8_t unit_id, void* arg);
static nmbs_error server_write_single_register(uint16_t address, uint16_t value, uint8_t unit_id, void* arg);
static nmbs_error server_write_multiple_registers(uint16_t address, uint16_t quantity, const uint16_t* registers,uint8_t unit_id, void* arg);


//初始化 从机
nmbs_error nmodbus_rtu_slave_init(nmbs_t* nmbs, nmbs_server_t* _server)
{
	nmbs_platform_conf platform;  // 平台配置
	nmbs_callbacks callbacks;     // 从机回调
	
	//先初始化清空结构体
	nmbs_platform_conf_create(&platform);
	nmbs_callbacks_create(&callbacks);
	
	server = _server;
	
	platform.transport = NMBS_TRANSPORT_RTU;    // RTU
	platform.read    = uart_modbus_read;        // 串口读取函数
	platform.write   = uart_modbus_write;       // 串口发送函数
	platform.crc_calc = nmbs_crc_calc;          // CRC (nanomodbus 库中的 原生函数)
	//platform.crc_calc = Modbus_CRC16;         // CRC16(我的)
	
	// 绑定读写寄存器 回调函数
	callbacks.read_coils = server_read_coils;
  callbacks.read_holding_registers = server_read_holding_registers;
  callbacks.write_single_coil = server_write_single_coil;
  callbacks.write_multiple_coils = server_write_multiple_coils;
  callbacks.write_single_register = server_write_single_register;
  callbacks.write_multiple_registers = server_write_multiple_registers;
	
	
	 nmbs_error status = nmbs_server_create(nmbs, server->id, &platform, &callbacks);
  if (status != NMBS_ERROR_NONE) 
	{
     return status;
  }
   nmbs_set_byte_timeout(nmbs, 100);
   nmbs_set_read_timeout(nmbs, 1000);
	//nmbs_server_create(&nmbs_slave, 1, &platform, &callbacks);
	nmbs_set_platform_arg(&nmbs_slave, &huart1);
	
	return NMBS_ERROR_NONE;
}



int32_t uart_modbus_read(uint8_t* buf, uint16_t count, int32_t byte_timeout_ms, void* arg)
{
	  HAL_StatusTypeDef status = HAL_UART_Receive(&MB_UART, buf, count, byte_timeout_ms);
    if (status == HAL_OK) {
        return count;
    }
    else {
        return 0;
    }
}


int32_t uart_modbus_write(const uint8_t* buf, uint16_t count, int32_t byte_timeout_ms,void* arg)
{
	  HAL_StatusTypeDef status = HAL_UART_Transmit(&MB_UART, buf, count, byte_timeout_ms);
    if (status == HAL_OK) {
        return count;
    }
    else {
        return 0;
    }
}

static nmbs_server_t* get_server(uint8_t id) {
    if (id == server->id) {
        return server;
    }
    else {
        return NULL;
    }
}

static nmbs_error server_read_coils(uint16_t address, uint16_t quantity, nmbs_bitfield coils_out, uint8_t unit_id,
                                    void* arg) {
    nmbs_server_t* server = get_server(unit_id);

    for (size_t i = 0; i < quantity; i++) {
        if ((address >> 3) > COIL_BUF_SIZE) {
            return NMBS_ERROR_INVALID_REQUEST;
        }
        nmbs_bitfield_write(coils_out, address, nmbs_bitfield_read(server->coils, address));
        address++;
    }
    return NMBS_ERROR_NONE;
}

static nmbs_error server_read_holding_registers(uint16_t address, uint16_t quantity, uint16_t* registers_out,
                                                uint8_t unit_id, void* arg) {
    nmbs_server_t* server = get_server(unit_id);

    for (size_t i = 0; i < quantity; i++) {
        if (address > REG_BUF_SIZE) {
            return NMBS_ERROR_INVALID_REQUEST;
        }
        registers_out[i] = server->regs[address++];
    }
    return NMBS_ERROR_NONE;
}

static nmbs_error server_write_single_coil(uint16_t address, bool value, uint8_t unit_id, void* arg) {
    uint8_t coil = 0;
    if (value) {
        coil |= 0x01;
    }
    return server_write_multiple_coils(address, 1, &coil, unit_id, arg);
}

static nmbs_error server_write_multiple_coils(uint16_t address, uint16_t quantity, const nmbs_bitfield coils,
                                              uint8_t unit_id, void* arg) {
    nmbs_server_t* server = get_server(unit_id);

    for (size_t i = 0; i < quantity; i++) {
        if ((address >> 3) > COIL_BUF_SIZE) {
            return NMBS_ERROR_INVALID_REQUEST;
        }
        nmbs_bitfield_write(server->coils, address, nmbs_bitfield_read(coils, i));
        address++;
    }
    return NMBS_ERROR_NONE;
}

static nmbs_error server_write_single_register(uint16_t address, uint16_t value, uint8_t unit_id, void* arg) {
    uint16_t reg = value;
    return server_write_multiple_registers(address, 1, &reg, unit_id, arg);
}

static nmbs_error server_write_multiple_registers(uint16_t address, uint16_t quantity, const uint16_t* registers,
                                                  uint8_t unit_id, void* arg) {
    nmbs_server_t* server = get_server(unit_id);

    for (size_t i = 0; i < quantity; i++) {
        if (address > REG_BUF_SIZE) {
            return NMBS_ERROR_INVALID_REQUEST;
        }
        server->regs[address++] = registers[i];
    }
    return NMBS_ERROR_NONE;
}

/*!< CRC calculation function pointer. Optional */
//uint16_t Modbus_CRC16(const uint8_t* data, uint32_t length,void* arg)
//{
//   unsigned short crc = 0xFFFF,i=0;
//	
//    for (i = 0; i < length; i++) {
//        crc ^= data[i];
//        for (unsigned char j = 0; j < 8; j++) 
//        {
//            if (crc & 0x0001) {
//                crc >>= 1;
//                crc ^= 0xA001;
//            } else {
//                crc >>= 1;
//            }
//        }
//    }
//    return crc;
//}


		
		

使用:

初始化

cpp 复制代码
	nmodbus_rtu_slave_init(&nmbs_slave, &nmbs_server);


循环调用( 可以放在直接 while( 1 )中 )

cpp 复制代码
	nmbs_server_poll(&nmbs_slave);

NMBS_DEBUG_PRINT函数宏:

如果你没有为其链接打印函数,那它就是个空语句,不会执行任何动作,不会卡死程序

这里我就没给他链接任何语句

中断接收冲突问题:

不能同时开 HAL_UART_Receive_IT 和阻塞 HAL_UART_Receive

两者不能同时用,STM32 串口开了 IT,再调用阻塞 Receive 会直接卡死在 HAL 内部。
我的串口是中断方式传输数据的,本来我在搭框架时就 写好了缓存处理的逻辑:

后来发现与uart_modbus_read 函数的接收冲突,就注释了前者:

相关推荐
ZhiqianXia3 小时前
PyTorch 学习笔记(13):third_party 第三方依赖全景图
pytorch·笔记·学习
智者知已应修善业3 小时前
【51单片机利用外部中断编写程序用两个按键控制数码管显示从0到9,S1控制加计数0—9,S2控制减计数9—0。】
c语言·经验分享·笔记·算法·51单片机
智者知已应修善业3 小时前
【51单片机实现0-7和8-1循环显示共阴数码管】2023-5-12
c语言·经验分享·笔记·算法·51单片机
一定要AK3 小时前
Vue 从入门到实战笔记
前端·vue.js·笔记
oi..3 小时前
Web 安全入门:XSS 漏洞原理与防护学习笔记 [ OWASP Top10 漏洞原理学习(仅用于合规测试)]
前端·网络·笔记·安全·网络安全·xss
Pixlout3 小时前
关于7元算子演算技术的个人笔记
ide·笔记·硬件工程
中屹指纹浏览器3 小时前
2026浏览器指纹追踪与反追踪技术深度解析:从风控原理到安全实践
经验分享·笔记
chase。3 小时前
【学习笔记】训练时动作条件化:一种更高效的机器人实时控制方案
笔记·学习·机器人
一轮弯弯的明月1 天前
贝尔数求集合划分方案总数
java·笔记·蓝桥杯·学习心得