增加modbus绑定
This commit is contained in:
629
src/features/menu_modbus.c
Normal file
629
src/features/menu_modbus.c
Normal file
@ -0,0 +1,629 @@
|
||||
/**
|
||||
* @file menu_modbus.c
|
||||
* @brief 菜单参数-Modbus寄存器映射:注册、查询、数据双向转换(工业级:类型安全、字节序适配)
|
||||
*/
|
||||
#include "menu.h"
|
||||
#include "menu_core.h"
|
||||
#include "menu_data.h"
|
||||
#include "menu_def.h"
|
||||
#include "menu_modbus.h"
|
||||
#include <stddef.h>
|
||||
#include <string.h>
|
||||
|
||||
#if MENU_CONFIG_ENABLE_MODBUS_MAP
|
||||
|
||||
/************************** 内部辅助函数 **************************/
|
||||
/**
|
||||
* @brief 转换Modbus协议地址到实际偏移地址(如40001→0,00001→0)
|
||||
* @param reg_type 寄存器类型
|
||||
* @param reg_addr 协议地址(如40001)
|
||||
* @param offset 输出参数,实际偏移地址
|
||||
* @return 错误码
|
||||
*/
|
||||
static MenuErrCode menu_modbus_convert_addr(ModbusRegType reg_type, uint16_t reg_addr, uint16_t* offset)
|
||||
{
|
||||
if (offset == NULL)
|
||||
{
|
||||
return MENU_ERR_INVALID_PARAM;
|
||||
}
|
||||
|
||||
// 校验协议地址范围(符合Modbus标准)
|
||||
switch (reg_type)
|
||||
{
|
||||
case MODBUS_REG_TYPE_COIL:
|
||||
if (reg_addr < 1 || reg_addr > 9999)
|
||||
{
|
||||
MENU_DEBUG("Modbus coil addr out of range: %d", reg_addr);
|
||||
return MENU_ERR_INVALID_PARAM;
|
||||
}
|
||||
*offset = reg_addr - 1;
|
||||
break;
|
||||
case MODBUS_REG_TYPE_DISCRETE_INPUT:
|
||||
if (reg_addr < 10001 || reg_addr > 19999)
|
||||
{
|
||||
MENU_DEBUG("Modbus discrete input addr out of range: %d", reg_addr);
|
||||
return MENU_ERR_INVALID_PARAM;
|
||||
}
|
||||
*offset = reg_addr - 10001;
|
||||
break;
|
||||
case MODBUS_REG_TYPE_HOLDING_REG:
|
||||
if (reg_addr < 40001 || reg_addr > 49999)
|
||||
{
|
||||
MENU_DEBUG("Modbus holding reg addr out of range: %d", reg_addr);
|
||||
return MENU_ERR_INVALID_PARAM;
|
||||
}
|
||||
*offset = reg_addr - 40001;
|
||||
break;
|
||||
case MODBUS_REG_TYPE_INPUT_REG:
|
||||
if (reg_addr < 30001 || reg_addr > 39999)
|
||||
{
|
||||
MENU_DEBUG("Modbus input reg addr out of range: %d", reg_addr);
|
||||
return MENU_ERR_INVALID_PARAM;
|
||||
}
|
||||
*offset = reg_addr - 30001;
|
||||
break;
|
||||
default:
|
||||
return MENU_ERR_INVALID_PARAM;
|
||||
}
|
||||
|
||||
// 校验实际偏移地址不超过配置的最大值
|
||||
// 注意:MENU_CONFIG_MODBUS_MAX_ADDR 不应超过 uint16_t 的最大值 0xFFFF
|
||||
if (*offset > (uint16_t)MENU_CONFIG_MODBUS_MAX_ADDR)
|
||||
{
|
||||
MENU_DEBUG("Modbus reg offset out of range: %d, max allowed: %d", *offset, MENU_CONFIG_MODBUS_MAX_ADDR);
|
||||
return MENU_ERR_INVALID_PARAM;
|
||||
}
|
||||
|
||||
return MENU_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 校验寄存器类型与参数类型的匹配性(工业级:类型安全)
|
||||
* @param param_type 参数类型
|
||||
* @param reg_type 寄存器类型
|
||||
* @return 错误码
|
||||
*/
|
||||
static MenuErrCode menu_modbus_check_type_match(MenuParamType param_type, ModbusRegType reg_type)
|
||||
{
|
||||
// 1位寄存器(线圈、离散输入)只能匹配布尔型/8位整型参数
|
||||
if (reg_type == MODBUS_REG_TYPE_COIL || reg_type == MODBUS_REG_TYPE_DISCRETE_INPUT)
|
||||
{
|
||||
if (param_type != MENU_PARAM_TYPE_INT8 && param_type != MENU_PARAM_TYPE_UINT8)
|
||||
{
|
||||
MENU_DEBUG("Modbus 1-bit reg not match param type: %d", param_type);
|
||||
return MENU_ERR_INVALID_PARAM;
|
||||
}
|
||||
return MENU_OK;
|
||||
}
|
||||
|
||||
// 16位寄存器(保持、输入)可匹配所有类型(16/32/浮点需多寄存器)
|
||||
return MENU_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 字节序转换(适配不同的Modbus字节序,工业级:兼容不同从站)
|
||||
* @param data 数据缓冲区(16位为单位)
|
||||
* @param len 数据长度(16位的数量)
|
||||
* @param byte_order 字节序(0-小端,1-大端,2-Modbus标准(字小端,字节大端))
|
||||
* @param is_reverse 是否反向转换(用于读取/写入)
|
||||
*/
|
||||
static void menu_modbus_byte_order_convert(uint16_t* data, uint8_t len, uint8_t byte_order, bool is_reverse)
|
||||
{
|
||||
if (data == NULL || len == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// 默认使用配置的字节序
|
||||
if (byte_order > 2)
|
||||
{
|
||||
byte_order = MENU_CONFIG_MODBUS_BYTE_ORDER;
|
||||
}
|
||||
|
||||
switch (byte_order)
|
||||
{
|
||||
case 0: // 小端(低字节在前,高字节在后)
|
||||
if (is_reverse)
|
||||
{
|
||||
for (uint8_t i = 0; i < len; i++)
|
||||
{
|
||||
data[i] = (data[i] << 8) | (data[i] >> 8);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 1: // 大端(高字节在前,低字节在后)
|
||||
// 无需转换,Modbus默认是大端
|
||||
break;
|
||||
case 2: // Modbus标准(字小端,字节大端:如32位值0x12345678→0x5678 0x1234)
|
||||
if (is_reverse)
|
||||
{
|
||||
// 读取时:将Modbus顺序转换为主机顺序
|
||||
for (uint8_t i = 0; i < len; i += 2)
|
||||
{
|
||||
if (i + 1 < len)
|
||||
{
|
||||
uint16_t temp = data[i];
|
||||
data[i] = data[i + 1];
|
||||
data[i + 1] = temp;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// 写入时:将主机顺序转换为Modbus顺序
|
||||
for (uint8_t i = 0; i < len; i += 2)
|
||||
{
|
||||
if (i + 1 < len)
|
||||
{
|
||||
uint16_t temp = data[i];
|
||||
data[i] = data[i + 1];
|
||||
data[i + 1] = temp;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 查找Modbus映射(通过参数ID)
|
||||
* @param param_id 参数ID
|
||||
* @return 映射内部结构体指针(NULL表示未找到)
|
||||
*/
|
||||
static ModbusMapInternal* menu_modbus_find_by_param(uint16_t param_id)
|
||||
{
|
||||
for (uint8_t i = 0; i < MENU_CONFIG_MAX_MODBUS_MAPS; i++)
|
||||
{
|
||||
if (s_menu_modbus_maps[i].is_registered && s_menu_modbus_maps[i].pub.param_id == param_id)
|
||||
{
|
||||
return &s_menu_modbus_maps[i];
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 查找Modbus映射(通过寄存器类型和地址)
|
||||
* @param reg_type 寄存器类型
|
||||
* @param reg_addr 寄存器地址(协议地址)
|
||||
* @return 映射内部结构体指针(NULL表示未找到)
|
||||
*/
|
||||
static ModbusMapInternal* menu_modbus_find_by_reg(ModbusRegType reg_type, uint16_t reg_addr)
|
||||
{
|
||||
for (uint8_t i = 0; i < MENU_CONFIG_MAX_MODBUS_MAPS; i++)
|
||||
{
|
||||
if (s_menu_modbus_maps[i].is_registered &&
|
||||
s_menu_modbus_maps[i].pub.reg_type == reg_type &&
|
||||
s_menu_modbus_maps[i].pub.reg_addr == reg_addr)
|
||||
{
|
||||
return &s_menu_modbus_maps[i];
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/************************** 对外接口实现 **************************/
|
||||
MenuErrCode menu_modbus_map_register(uint16_t param_id, ModbusRegType reg_type, uint16_t reg_addr, uint8_t reg_count, ModbusPerm perm, uint8_t byte_order)
|
||||
{
|
||||
// 1. 校验参数是否已注册(通过获取参数值间接检查)
|
||||
float dummy_val;
|
||||
MenuErrCode err = menu_param_get_value(param_id, &dummy_val);
|
||||
if (err != MENU_OK)
|
||||
{
|
||||
MENU_DEBUG("Modbus map param %d not found", param_id);
|
||||
return MENU_ERR_NODE_NOT_FOUND;
|
||||
}
|
||||
|
||||
// 2. 校验寄存器地址和转换为偏移
|
||||
uint16_t reg_offset;
|
||||
err = menu_modbus_convert_addr(reg_type, reg_addr, ®_offset);
|
||||
if (err != MENU_OK)
|
||||
{
|
||||
return err;
|
||||
}
|
||||
|
||||
// 3. 校验寄存器数量(至少1个)
|
||||
if (reg_count == 0)
|
||||
{
|
||||
MENU_DEBUG("Modbus map reg count is zero");
|
||||
return MENU_ERR_INVALID_PARAM;
|
||||
}
|
||||
|
||||
// 4. 由于无法直接获取参数类型,这里跳过类型匹配校验
|
||||
// 实际项目中,可以考虑在参数模块中添加获取参数类型的接口
|
||||
// err = menu_modbus_check_type_match(param->type, reg_type);
|
||||
// if (err != MENU_OK)
|
||||
// {
|
||||
// return err;
|
||||
// }
|
||||
|
||||
// 5. 校验读写权限(与寄存器类型联动,如离散输入只能只读)
|
||||
#if MENU_CONFIG_MODBUS_PERMISSION
|
||||
if ((reg_type == MODBUS_REG_TYPE_DISCRETE_INPUT || reg_type == MODBUS_REG_TYPE_INPUT_REG) && perm != MODBUS_PERM_READ_ONLY)
|
||||
{
|
||||
MENU_DEBUG("Modbus input reg only support read");
|
||||
return MENU_ERR_INVALID_PARAM;
|
||||
}
|
||||
if (reg_type == MODBUS_REG_TYPE_COIL && perm == MODBUS_PERM_READ_ONLY)
|
||||
{
|
||||
MENU_DEBUG("Modbus coil not support read only");
|
||||
return MENU_ERR_INVALID_PARAM;
|
||||
}
|
||||
#endif
|
||||
|
||||
// 6. 检查映射是否已存在
|
||||
if (menu_modbus_find_by_param(param_id) != NULL || menu_modbus_find_by_reg(reg_type, reg_addr) != NULL)
|
||||
{
|
||||
MENU_DEBUG("Modbus map already exists: param %d, reg %d:%d", param_id, reg_type, reg_addr);
|
||||
return MENU_ERR_INVALID_PARAM;
|
||||
}
|
||||
|
||||
// 7. 查找空闲的映射位置(静态数组)
|
||||
ModbusMapInternal* map = NULL;
|
||||
for (uint8_t i = 0; i < MENU_CONFIG_MAX_MODBUS_MAPS; i++)
|
||||
{
|
||||
if (!s_menu_modbus_maps[i].is_registered)
|
||||
{
|
||||
map = &s_menu_modbus_maps[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (map == NULL)
|
||||
{
|
||||
MENU_DEBUG("Modbus map out of memory: max %d", MENU_CONFIG_MAX_MODBUS_MAPS);
|
||||
return MENU_ERR_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
// 8. 获取参数类型
|
||||
MenuParamType param_type;
|
||||
err = menu_param_get_type(param_id, ¶m_type);
|
||||
if (err != MENU_OK)
|
||||
{
|
||||
return err;
|
||||
}
|
||||
|
||||
// 9. 校验寄存器类型与参数类型匹配
|
||||
err = menu_modbus_check_type_match(param_type, reg_type);
|
||||
if (err != MENU_OK)
|
||||
{
|
||||
return err;
|
||||
}
|
||||
|
||||
// 10. 初始化映射关系
|
||||
MENU_MEM_SET_ZERO(map, sizeof(ModbusMapInternal));
|
||||
map->pub.param_id = param_id;
|
||||
map->pub.reg_type = reg_type;
|
||||
map->pub.reg_addr = reg_addr;
|
||||
map->pub.reg_count = reg_count;
|
||||
map->pub.perm = perm;
|
||||
map->pub.byte_order = byte_order;
|
||||
map->param_type = param_type;
|
||||
map->reg_addr_offset = reg_offset;
|
||||
map->is_registered = true;
|
||||
|
||||
MENU_DEBUG("Modbus map registered: param %d → reg %d:%d (count %d, perm %d)",
|
||||
param_id, reg_type, reg_addr, reg_count, perm);
|
||||
return MENU_OK;
|
||||
}
|
||||
|
||||
MenuErrCode menu_modbus_map_query_by_param(uint16_t param_id, ModbusMap* map)
|
||||
{
|
||||
if (map == NULL)
|
||||
{
|
||||
return MENU_ERR_INVALID_PARAM;
|
||||
}
|
||||
|
||||
ModbusMapInternal* internal_map = menu_modbus_find_by_param(param_id);
|
||||
if (internal_map == NULL)
|
||||
{
|
||||
MENU_DEBUG("Modbus map param %d not found", param_id);
|
||||
return MENU_ERR_NODE_NOT_FOUND;
|
||||
}
|
||||
|
||||
// 复制对外暴露的信息
|
||||
*map = internal_map->pub;
|
||||
return MENU_OK;
|
||||
}
|
||||
|
||||
MenuErrCode menu_modbus_map_query_by_reg(ModbusRegType reg_type, uint16_t reg_addr, ModbusMap* map)
|
||||
{
|
||||
if (map == NULL)
|
||||
{
|
||||
return MENU_ERR_INVALID_PARAM;
|
||||
}
|
||||
|
||||
ModbusMapInternal* internal_map = menu_modbus_find_by_reg(reg_type, reg_addr);
|
||||
if (internal_map == NULL)
|
||||
{
|
||||
MENU_DEBUG("Modbus map reg %d:%d not found", reg_type, reg_addr);
|
||||
return MENU_ERR_NODE_NOT_FOUND;
|
||||
}
|
||||
|
||||
// 复制对外暴露的信息
|
||||
*map = internal_map->pub;
|
||||
return MENU_OK;
|
||||
}
|
||||
|
||||
MenuErrCode menu_modbus_map_param_to_reg(uint16_t param_id, uint8_t* reg_buf, uint8_t* buf_len)
|
||||
{
|
||||
if (reg_buf == NULL || buf_len == NULL || *buf_len == 0)
|
||||
{
|
||||
return MENU_ERR_INVALID_PARAM;
|
||||
}
|
||||
|
||||
// 1. 查找映射关系
|
||||
ModbusMapInternal* map = menu_modbus_find_by_param(param_id);
|
||||
if (map == NULL)
|
||||
{
|
||||
return MENU_ERR_NODE_NOT_FOUND;
|
||||
}
|
||||
|
||||
// 2. 校验读写权限
|
||||
#if MENU_CONFIG_MODBUS_PERMISSION
|
||||
if (map->pub.perm == MODBUS_PERM_READ_ONLY)
|
||||
{
|
||||
MENU_DEBUG("Modbus map param %d is read only", param_id);
|
||||
return MENU_ERR_HW_PORT_ERROR; // 复用原有错误码
|
||||
}
|
||||
#endif
|
||||
|
||||
// 3. 获取参数值
|
||||
float param_val;
|
||||
MenuErrCode err = menu_param_get_value(param_id, ¶m_val);
|
||||
if (err != MENU_OK)
|
||||
{
|
||||
MENU_DEBUG("Failed to get param value: %d", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
// 4. 转换参数值到寄存器数据(按类型和寄存器数量)
|
||||
uint16_t* reg_data = (uint16_t*)reg_buf;
|
||||
uint8_t req_len = map->pub.reg_count;
|
||||
|
||||
// 检查缓冲区长度是否足够
|
||||
if (*buf_len < req_len * 2) // 寄存器数据按16位计算,每个16位占2字节
|
||||
{
|
||||
MENU_DEBUG("Modbus reg buf too small: need %d bytes, got %d bytes", req_len * 2, *buf_len);
|
||||
return MENU_ERR_INVALID_PARAM;
|
||||
}
|
||||
|
||||
// 清空缓冲区
|
||||
memset(reg_data, 0, req_len * 2);
|
||||
|
||||
// 根据参数类型转换
|
||||
// 使用之前保存的参数类型,不需要再次调用menu_param_find
|
||||
switch (map->pub.reg_type)
|
||||
{
|
||||
case MODBUS_REG_TYPE_COIL:
|
||||
case MODBUS_REG_TYPE_DISCRETE_INPUT:
|
||||
// 1位寄存器:取参数值的最低位
|
||||
if (req_len != 1)
|
||||
{
|
||||
MENU_DEBUG("Invalid reg count for 1-bit reg: %d, expected 1", req_len);
|
||||
return MENU_ERR_INVALID_PARAM;
|
||||
}
|
||||
reg_data[0] = (uint16_t)(param_val > 0 ? 1 : 0);
|
||||
break;
|
||||
case MODBUS_REG_TYPE_HOLDING_REG:
|
||||
case MODBUS_REG_TYPE_INPUT_REG:
|
||||
// 16位寄存器:根据参数类型和数量转换
|
||||
if (map->param_type == MENU_PARAM_TYPE_INT16 || map->param_type == MENU_PARAM_TYPE_UINT16)
|
||||
{
|
||||
if (req_len != 1)
|
||||
{
|
||||
MENU_DEBUG("Invalid reg count for 16-bit param: %d, expected 1", req_len);
|
||||
return MENU_ERR_INVALID_PARAM;
|
||||
}
|
||||
reg_data[0] = (uint16_t)param_val;
|
||||
}
|
||||
else if (map->param_type == MENU_PARAM_TYPE_INT32 || map->param_type == MENU_PARAM_TYPE_UINT32)
|
||||
{
|
||||
if (req_len != 2)
|
||||
{
|
||||
MENU_DEBUG("Invalid reg count for 32-bit param: %d, expected 2", req_len);
|
||||
return MENU_ERR_INVALID_PARAM;
|
||||
}
|
||||
uint32_t val = (uint32_t)param_val;
|
||||
reg_data[0] = (uint16_t)(val & 0xFFFF);
|
||||
reg_data[1] = (uint16_t)((val >> 16) & 0xFFFF);
|
||||
}
|
||||
else if (map->param_type == MENU_PARAM_TYPE_FLOAT)
|
||||
{
|
||||
if (req_len != 2)
|
||||
{
|
||||
MENU_DEBUG("Invalid reg count for float param: %d, expected 2", req_len);
|
||||
return MENU_ERR_INVALID_PARAM;
|
||||
}
|
||||
// 浮点型转换为32位二进制,占2个16位寄存器
|
||||
union {
|
||||
float f;
|
||||
uint32_t u32;
|
||||
} float_val;
|
||||
float_val.f = param_val;
|
||||
reg_data[0] = (uint16_t)(float_val.u32 & 0xFFFF);
|
||||
reg_data[1] = (uint16_t)((float_val.u32 >> 16) & 0xFFFF);
|
||||
}
|
||||
else if (map->param_type == MENU_PARAM_TYPE_INT8 || map->param_type == MENU_PARAM_TYPE_UINT8)
|
||||
{
|
||||
if (req_len != 1)
|
||||
{
|
||||
MENU_DEBUG("Invalid reg count for 8-bit param: %d, expected 1", req_len);
|
||||
return MENU_ERR_INVALID_PARAM;
|
||||
}
|
||||
reg_data[0] = (uint16_t)param_val;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
MENU_DEBUG("Invalid reg type: %d", map->pub.reg_type);
|
||||
return MENU_ERR_INVALID_PARAM;
|
||||
}
|
||||
|
||||
// 5. 字节序转换(写入寄存器前)
|
||||
menu_modbus_byte_order_convert(reg_data, req_len, map->pub.byte_order, false);
|
||||
|
||||
// 6. 设置实际写入长度
|
||||
*buf_len = req_len * 2;
|
||||
|
||||
MENU_DEBUG("Modbus param %d to reg: val %f → reg data len %d", param_id, param_val, *buf_len);
|
||||
return MENU_OK;
|
||||
}
|
||||
|
||||
MenuErrCode menu_modbus_map_reg_to_param(uint16_t param_id, const uint8_t* reg_buf, uint8_t buf_len)
|
||||
{
|
||||
if (reg_buf == NULL || buf_len == 0)
|
||||
{
|
||||
return MENU_ERR_INVALID_PARAM;
|
||||
}
|
||||
|
||||
// 1. 查找映射关系
|
||||
ModbusMapInternal* map = menu_modbus_find_by_param(param_id);
|
||||
if (map == NULL)
|
||||
{
|
||||
return MENU_ERR_NODE_NOT_FOUND;
|
||||
}
|
||||
|
||||
// 2. 校验读写权限
|
||||
#if MENU_CONFIG_MODBUS_PERMISSION
|
||||
if (map->pub.perm == MODBUS_PERM_WRITE_ONLY)
|
||||
{
|
||||
MENU_DEBUG("Modbus map param %d is write only", param_id);
|
||||
return MENU_ERR_HW_PORT_ERROR; // 复用原有错误码
|
||||
}
|
||||
#endif
|
||||
|
||||
// 3. 校验缓冲区长度
|
||||
uint8_t req_len = map->pub.reg_count * 2;
|
||||
if (buf_len < req_len)
|
||||
{
|
||||
MENU_DEBUG("Modbus reg buf too small: need %d, got %d", req_len, buf_len);
|
||||
return MENU_ERR_INVALID_PARAM;
|
||||
}
|
||||
|
||||
// 4. 复制寄存器数据并进行字节序转换(读取寄存器后)
|
||||
uint16_t reg_data[MENU_CONFIG_MAX_MODBUS_MAPS * 2] = {0}; // 足够大的临时缓冲区
|
||||
|
||||
// 检查缓冲区长度是否足够
|
||||
if (buf_len < req_len)
|
||||
{
|
||||
MENU_DEBUG("Modbus reg buf too small: need %d bytes, got %d bytes", req_len, buf_len);
|
||||
return MENU_ERR_INVALID_PARAM;
|
||||
}
|
||||
|
||||
// 复制数据到临时缓冲区
|
||||
memcpy(reg_data, reg_buf, req_len);
|
||||
|
||||
// 字节序转换
|
||||
menu_modbus_byte_order_convert(reg_data, map->pub.reg_count, map->pub.byte_order, true);
|
||||
|
||||
// 5. 转换寄存器数据到参数值
|
||||
float param_val = 0.0f;
|
||||
// 使用之前保存的参数类型,不需要再次调用menu_param_find
|
||||
switch (map->pub.reg_type)
|
||||
{
|
||||
case MODBUS_REG_TYPE_COIL:
|
||||
case MODBUS_REG_TYPE_DISCRETE_INPUT:
|
||||
// 1位寄存器:0→0,非0→1
|
||||
if (map->pub.reg_count != 1)
|
||||
{
|
||||
MENU_DEBUG("Invalid reg count for 1-bit reg: %d, expected 1", map->pub.reg_count);
|
||||
return MENU_ERR_INVALID_PARAM;
|
||||
}
|
||||
param_val = (reg_data[0] & 0x01) ? 1.0f : 0.0f;
|
||||
break;
|
||||
case MODBUS_REG_TYPE_HOLDING_REG:
|
||||
case MODBUS_REG_TYPE_INPUT_REG:
|
||||
// 16位寄存器:根据参数类型转换
|
||||
if (map->param_type == MENU_PARAM_TYPE_INT8)
|
||||
{
|
||||
if (map->pub.reg_count != 1)
|
||||
{
|
||||
MENU_DEBUG("Invalid reg count for 8-bit param: %d, expected 1", map->pub.reg_count);
|
||||
return MENU_ERR_INVALID_PARAM;
|
||||
}
|
||||
param_val = (int8_t)reg_data[0];
|
||||
}
|
||||
else if (map->param_type == MENU_PARAM_TYPE_UINT8)
|
||||
{
|
||||
if (map->pub.reg_count != 1)
|
||||
{
|
||||
MENU_DEBUG("Invalid reg count for 8-bit param: %d, expected 1", map->pub.reg_count);
|
||||
return MENU_ERR_INVALID_PARAM;
|
||||
}
|
||||
param_val = (uint8_t)reg_data[0];
|
||||
}
|
||||
else if (map->param_type == MENU_PARAM_TYPE_INT16)
|
||||
{
|
||||
if (map->pub.reg_count != 1)
|
||||
{
|
||||
MENU_DEBUG("Invalid reg count for 16-bit param: %d, expected 1", map->pub.reg_count);
|
||||
return MENU_ERR_INVALID_PARAM;
|
||||
}
|
||||
param_val = (int16_t)reg_data[0];
|
||||
}
|
||||
else if (map->param_type == MENU_PARAM_TYPE_UINT16)
|
||||
{
|
||||
if (map->pub.reg_count != 1)
|
||||
{
|
||||
MENU_DEBUG("Invalid reg count for 16-bit param: %d, expected 1", map->pub.reg_count);
|
||||
return MENU_ERR_INVALID_PARAM;
|
||||
}
|
||||
param_val = (uint16_t)reg_data[0];
|
||||
}
|
||||
else if (map->param_type == MENU_PARAM_TYPE_INT32)
|
||||
{
|
||||
if (map->pub.reg_count != 2)
|
||||
{
|
||||
MENU_DEBUG("Invalid reg count for 32-bit param: %d, expected 2", map->pub.reg_count);
|
||||
return MENU_ERR_INVALID_PARAM;
|
||||
}
|
||||
uint32_t val = ((uint32_t)reg_data[1] << 16) | reg_data[0];
|
||||
param_val = (int32_t)val;
|
||||
}
|
||||
else if (map->param_type == MENU_PARAM_TYPE_UINT32)
|
||||
{
|
||||
if (map->pub.reg_count != 2)
|
||||
{
|
||||
MENU_DEBUG("Invalid reg count for 32-bit param: %d, expected 2", map->pub.reg_count);
|
||||
return MENU_ERR_INVALID_PARAM;
|
||||
}
|
||||
uint32_t val = ((uint32_t)reg_data[1] << 16) | reg_data[0];
|
||||
param_val = val;
|
||||
}
|
||||
else if (map->param_type == MENU_PARAM_TYPE_FLOAT)
|
||||
{
|
||||
if (map->pub.reg_count != 2)
|
||||
{
|
||||
MENU_DEBUG("Invalid reg count for float param: %d, expected 2", map->pub.reg_count);
|
||||
return MENU_ERR_INVALID_PARAM;
|
||||
}
|
||||
union {
|
||||
float f;
|
||||
uint32_t u32;
|
||||
} float_val;
|
||||
float_val.u32 = ((uint32_t)reg_data[1] << 16) | reg_data[0];
|
||||
param_val = float_val.f;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
MENU_DEBUG("Invalid reg type: %d", map->pub.reg_type);
|
||||
return MENU_ERR_INVALID_PARAM;
|
||||
}
|
||||
|
||||
// 6. 设置参数值(自动进行范围检查)
|
||||
MenuErrCode err = menu_param_set_value(param_id, param_val);
|
||||
if (err != MENU_OK)
|
||||
{
|
||||
return err;
|
||||
}
|
||||
|
||||
MENU_DEBUG("Modbus reg to param %d: reg data len %d → val %f", param_id, buf_len, param_val);
|
||||
return MENU_OK;
|
||||
}
|
||||
|
||||
// 定义Modbus映射静态数组
|
||||
ModbusMapInternal s_menu_modbus_maps[MENU_CONFIG_MAX_MODBUS_MAPS];
|
||||
|
||||
#endif // MENU_CONFIG_ENABLE_MODBUS_MAP
|
||||
@ -11,7 +11,7 @@
|
||||
MenuParam s_menu_params[MENU_CONFIG_MAX_PARAMS];
|
||||
|
||||
/**
|
||||
* @brief 查找参数(通过参数ID)
|
||||
* @brief 查找参数(通过参数ID,内部使用)
|
||||
* @param param_id 参数ID
|
||||
* @return 参数指针(NULL表示未找到)
|
||||
*/
|
||||
@ -299,4 +299,29 @@ MenuErrCode menu_param_decrease(uint16_t param_id, float step)
|
||||
return menu_param_set_value(param_id, current_val - step);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 获取参数类型
|
||||
* @param param_id 参数ID
|
||||
* @param type 输出参数,参数类型
|
||||
* @return 错误码
|
||||
*/
|
||||
MenuErrCode menu_param_get_type(uint16_t param_id, MenuParamType* type)
|
||||
{
|
||||
if (type == NULL)
|
||||
{
|
||||
return MENU_ERR_INVALID_PARAM;
|
||||
}
|
||||
|
||||
// 查找参数
|
||||
MenuParam* param = menu_param_find(param_id);
|
||||
if (param == NULL)
|
||||
{
|
||||
return MENU_ERR_NODE_NOT_FOUND;
|
||||
}
|
||||
|
||||
// 返回参数类型
|
||||
*type = param->type;
|
||||
return MENU_OK;
|
||||
}
|
||||
|
||||
#endif // MENU_CONFIG_ENABLE_PARAM
|
||||
Reference in New Issue
Block a user