426 lines
16 KiB
C
426 lines
16 KiB
C
/*
|
||
* @Author: mypx
|
||
* @Date: 2025-08-01 16:41:44
|
||
* @LastEditTime: 2025-09-26 15:00:17
|
||
* @LastEditors: mypx mypx_coder@163.com
|
||
* @Description:
|
||
*/
|
||
#include "mb_command.h"
|
||
#include "et_log.h"
|
||
#include "etk_byte_conv.h"
|
||
#include "tec_control.h"
|
||
#include <stdio.h>
|
||
#include <string.h>
|
||
|
||
static mb_cmd_handler_t *cmd_handler = NULL;
|
||
static mb_command_t *mb_cmd = NULL;
|
||
|
||
// external declaration for temperature control
|
||
extern tec_control_t *tec_control;
|
||
// Note: g_mb_regs was removed; use mb_command_t->input_regs as the authoritative cache
|
||
|
||
static mb_cmd_handler_t *get_command_handler(void);
|
||
mb_command_t *get_command_instance(void);
|
||
|
||
|
||
// 输入寄存器同步接口
|
||
void mb_sync_running_status_reg(PmRunningState state)
|
||
{
|
||
mb_command_t *mb = get_command_instance();
|
||
if (!mb)
|
||
return;
|
||
mb->input_regs[INPUT_REG_DEV_RUNNING_STATE] = state;
|
||
}
|
||
|
||
void mb_sync_exception_reg(uint32_t exception, bool set)
|
||
{
|
||
mb_command_t *mb = get_command_instance();
|
||
if (!mb)
|
||
return;
|
||
if (set)
|
||
{
|
||
/* ALARM_STATUS occupies two registers (U32). We store as little-endian word pair
|
||
* INPUT_REG_ALARM_STATUS = high-word? choose big-endian ordering consistent with docs.
|
||
* Here we pack into two 16-bit words: high 16 bits and low 16 bits.
|
||
*/
|
||
uint32_t cur = ((uint32_t)mb->input_regs[INPUT_REG_ALARM_STATUS] << 16)
|
||
| (uint32_t)mb->input_regs[INPUT_REG_ALARM_STATUS + 1];
|
||
cur |= exception;
|
||
mb->input_regs[INPUT_REG_ALARM_STATUS] = (uint16_t)((cur >> 16) & 0xFFFF);
|
||
mb->input_regs[INPUT_REG_ALARM_STATUS + 1] = (uint16_t)(cur & 0xFFFF);
|
||
}
|
||
else
|
||
{
|
||
uint32_t cur = ((uint32_t)mb->input_regs[INPUT_REG_ALARM_STATUS] << 16)
|
||
| (uint32_t)mb->input_regs[INPUT_REG_ALARM_STATUS + 1];
|
||
cur &= ~exception;
|
||
mb->input_regs[INPUT_REG_ALARM_STATUS] = (uint16_t)((cur >> 16) & 0xFFFF);
|
||
mb->input_regs[INPUT_REG_ALARM_STATUS + 1] = (uint16_t)(cur & 0xFFFF);
|
||
}
|
||
}
|
||
|
||
// 新增:同步实时温度到底层输入寄存器(ABCD顺序,2寄存器)
|
||
void mb_sync_realtime_temp(float temperature)
|
||
{
|
||
mb_command_t *mb = get_command_instance();
|
||
if (!mb)
|
||
return;
|
||
uint16_t reg_h, reg_l;
|
||
et_float_to_mb_abcd(temperature, ®_h, ®_l);
|
||
if (mb->input_lock != RT_NULL)
|
||
rt_mutex_take(mb->input_lock, RT_WAITING_FOREVER);
|
||
mb->input_regs[INPUT_REG_REAL_TEMP_H] = reg_h;
|
||
mb->input_regs[INPUT_REG_REAL_TEMP_L] = reg_l;
|
||
if (mb->input_lock != RT_NULL)
|
||
rt_mutex_release(mb->input_lock);
|
||
}
|
||
|
||
// 新增:同步实时旋光度到底层输入寄存器(ABCD顺序,2寄存器)
|
||
void mb_sync_realtime_rotation(float rotation)
|
||
{
|
||
mb_command_t *mb = get_command_instance();
|
||
if (!mb)
|
||
return;
|
||
uint16_t reg_h, reg_l;
|
||
et_float_to_mb_abcd(rotation, ®_h, ®_l);
|
||
if (mb->input_lock != RT_NULL)
|
||
rt_mutex_take(mb->input_lock, RT_WAITING_FOREVER);
|
||
mb->input_regs[INPUT_REG_REAL_ROT_H] = reg_h;
|
||
mb->input_regs[INPUT_REG_REAL_ROT_L] = reg_l;
|
||
if (mb->input_lock != RT_NULL)
|
||
rt_mutex_release(mb->input_lock);
|
||
}
|
||
|
||
// 新增:同步测量结果(模式、数值、温度)到底层输入寄存器
|
||
void mb_sync_result(uint8_t mode, float value, float temp)
|
||
{
|
||
uint16_t reg_h, reg_l;
|
||
mb_command_t *mb = get_command_instance();
|
||
if (!mb)
|
||
return;
|
||
if (mb->input_lock != RT_NULL)
|
||
rt_mutex_take(mb->input_lock, RT_WAITING_FOREVER);
|
||
mb->input_regs[INPUT_REG_RESULT_MODE] = mode;
|
||
et_float_to_mb_abcd(value, ®_h, ®_l);
|
||
mb->input_regs[INPUT_REG_RESULT_VAL_H] = reg_h;
|
||
mb->input_regs[INPUT_REG_RESULT_VAL_L] = reg_l;
|
||
et_float_to_mb_abcd(temp, ®_h, ®_l);
|
||
mb->input_regs[INPUT_REG_RESULT_TEMP_H] = reg_h;
|
||
mb->input_regs[INPUT_REG_RESULT_TEMP_L] = reg_l;
|
||
if (mb->input_lock != RT_NULL)
|
||
rt_mutex_release(mb->input_lock);
|
||
}
|
||
|
||
void mb_sync_meas_done_flag(bool done)
|
||
{
|
||
mb_command_t *mb = get_command_instance();
|
||
if (!mb)
|
||
return;
|
||
if (mb->input_lock != RT_NULL)
|
||
rt_mutex_take(mb->input_lock, RT_WAITING_FOREVER);
|
||
mb->input_regs[INPUT_REG_MEAS_DONE_FLAG] = done ? 1 : 0;
|
||
if (mb->input_lock != RT_NULL)
|
||
rt_mutex_release(mb->input_lock);
|
||
}
|
||
// 处理保持寄存器写入
|
||
nmbs_error mb_handle_write_holding_registers(uint16_t address, uint16_t quantity, const uint16_t *registers)
|
||
{
|
||
mb_command_t *mb_cmd = get_command_instance();
|
||
mb_cmd_handler_t *handler = get_command_handler();
|
||
if (mb_cmd == NULL || handler == NULL)
|
||
{
|
||
ET_ERR("mb command instance failed");
|
||
return NMBS_EXCEPTION_SERVER_DEVICE_FAILURE;
|
||
}
|
||
|
||
switch (address)
|
||
{
|
||
case HOLD_REG_SYS_CTRL_CMD: // 系统控制命令(30001)
|
||
if (quantity != 1)
|
||
{
|
||
ET_ERR("HOLD_REG_SYS_CTRL_CMD quantity error, expect 1, got %d", quantity);
|
||
return NMBS_ERROR_INVALID_REQUEST;
|
||
}
|
||
|
||
if (handler->system_ctrl_handler)
|
||
{
|
||
handler->system_ctrl_handler(registers[0]);
|
||
}
|
||
mb_cmd->regs[address] = registers[0] & 0xFF;
|
||
return NMBS_ERROR_NONE;
|
||
|
||
case HOLD_REG_MEAS_MODE: // 测量模式设置(30002)
|
||
if (quantity != 1)
|
||
{
|
||
ET_ERR("HOLD_REG_MEAS_MODE quantity error, expect 1, got %d", quantity);
|
||
return NMBS_ERROR_INVALID_REQUEST;
|
||
}
|
||
mb_cmd->regs[address] = registers[0] & 0xFF; // 取低8位,写入底层寄存器
|
||
ET_INFO("Measurement mode set to: %d", mb_cmd->regs[address]);
|
||
|
||
if (handler->meas_mode_handler)
|
||
{
|
||
handler->meas_mode_handler(mb_cmd->regs[address]);
|
||
}
|
||
return NMBS_ERROR_NONE;
|
||
|
||
case HOLD_REG_TARGET_TEMP_H: // 目标温度设置(30003-30004)
|
||
if (quantity != 2)
|
||
{
|
||
ET_ERR("HOLD_REG_TARGET_TEMP_H quantity error, expect 2, got %d", quantity);
|
||
return NMBS_ERROR_INVALID_REQUEST;
|
||
}
|
||
float target_temp = et_mb_to_float_abcd(registers[0], registers[1]);
|
||
|
||
// 温度范围检查(-10℃ ~ 100℃)
|
||
if (target_temp > PM_TARGET_TEMP_MAX || target_temp < PM_TARGET_TEMP_MIN)
|
||
{
|
||
ET_ERR("HOLD_REG_TARGET_TEMP_H value error, expect:[%.2f~%.2f], got %.2f", PM_TARGET_TEMP_MIN,
|
||
PM_TARGET_TEMP_MAX, target_temp);
|
||
return NMBS_EXCEPTION_ILLEGAL_DATA_VALUE;
|
||
}
|
||
if (handler->target_temp_handler)
|
||
{
|
||
handler->target_temp_handler(target_temp);
|
||
}
|
||
|
||
// 写入底层寄存器(高16位和低16位)
|
||
mb_cmd->regs[address] = registers[0];
|
||
mb_cmd->regs[address + 1] = registers[1];
|
||
ET_INFO("Target temperature set to: %.1f deg", target_temp);
|
||
|
||
// send temperature update event
|
||
if (tec_control && tec_control->control_event)
|
||
{
|
||
rt_event_send(tec_control->control_event, TEC_TEMP_UPDATE_EVENT);
|
||
}
|
||
ET_WARN("[MB] RX Target Temp: %.2f C", target_temp);
|
||
return NMBS_ERROR_NONE;
|
||
case HOLD_REG_TARGET_TEMP_L: // // 低16位单独写入时拒绝(需通过高地址写入2个寄存器)
|
||
ET_ERR("[MB] RX Target Temp Low, Rejected");
|
||
return NMBS_ERROR_INVALID_REQUEST;
|
||
|
||
case HOLD_REG_TUBE_LEN: // 旋光管长度设置(30005-30006)
|
||
if (quantity != 1)
|
||
{
|
||
ET_ERR("[MB] RX HOLD_REG_TUBE_LEN quantity error, expect 2, got %d", quantity);
|
||
return NMBS_ERROR_INVALID_REQUEST;
|
||
}
|
||
uint16_t tube_length = registers[0];
|
||
|
||
// 检查旋光管长度是否有效(>0)
|
||
if (tube_length < PM_TUBE_LENGTH_MIN || tube_length > PM_TUBE_LENGTH_MAX)
|
||
{
|
||
ET_ERR("[MB] RX tube length value error, expect:[%d~%d], got %d", PM_TUBE_LENGTH_MIN, PM_TUBE_LENGTH_MAX,
|
||
tube_length);
|
||
return NMBS_EXCEPTION_ILLEGAL_DATA_VALUE; // 对应Modbus异常0x03
|
||
}
|
||
if (handler->tube_length_handler)
|
||
{
|
||
handler->tube_length_handler(tube_length);
|
||
}
|
||
// 写入底层寄存器(高16位和低16位)
|
||
mb_cmd->regs[address] = registers[0];
|
||
ET_INFO("Tube length set to: %d mm", tube_length);
|
||
return NMBS_ERROR_NONE;
|
||
|
||
case HOLD_REG_MEAS_PRECISION: // 测量精度设置(30007)
|
||
if (quantity != 1)
|
||
{
|
||
ET_ERR("[MB] RX HOLD_REG_MEAS_PRECISION value error, must write one register");
|
||
return NMBS_ERROR_INVALID_REQUEST;
|
||
}
|
||
uint8_t measure_precision = registers[0] & 0xFF;
|
||
if (handler->meas_precision_handler)
|
||
{
|
||
handler->meas_precision_handler(measure_precision);
|
||
}
|
||
// 写入底层寄存器
|
||
mb_cmd->regs[address] = measure_precision;
|
||
ET_INFO("Measure precision set to: %d", measure_precision);
|
||
|
||
return NMBS_ERROR_NONE;
|
||
|
||
case HOLD_REG_AUTO_MEAS_CNT: // 自动测量次数(30008)
|
||
if (quantity != 1)
|
||
{
|
||
ET_ERR("Auto measure count write error: %d", registers[0]);
|
||
return NMBS_ERROR_INVALID_REQUEST;
|
||
}
|
||
float auto_measure_count = registers[0] & 0xFF;
|
||
if (handler->auto_measure_count_handler)
|
||
{
|
||
handler->auto_measure_count_handler(auto_measure_count);
|
||
}
|
||
if (auto_measure_count == 0)
|
||
{
|
||
ET_ERR("Auto measure count write error: %d", auto_measure_count);
|
||
return NMBS_ERROR_INVALID_REQUEST; // 次数不能为0(0x01错误)
|
||
}
|
||
// 写入底层寄存器
|
||
mb_cmd->regs[address] = auto_measure_count;
|
||
ET_INFO("Auto measure count set to: %d", auto_measure_count);
|
||
return NMBS_ERROR_NONE;
|
||
|
||
case HOLD_REG_HIST_QUERY_START: // 历史数据查询范围(30009-30010)
|
||
if (quantity != 2)
|
||
{
|
||
ET_ERR("History query start error, quantity error, expect 2, got %d", quantity);
|
||
return NMBS_ERROR_INVALID_REQUEST;
|
||
}
|
||
uint8_t history_start = registers[0];
|
||
uint8_t history_count = registers[1];
|
||
|
||
// 检查历史数据范围有效性
|
||
if (history_count == 0 || history_count > 5)
|
||
{
|
||
ET_ERR("History query count error, expect 1-5, got %d", history_count);
|
||
return NMBS_EXCEPTION_ILLEGAL_DATA_VALUE; // 对应Modbus异常0x03
|
||
}
|
||
|
||
ET_INFO("History query range set: start=%d, count=%d", history_start, history_count);
|
||
return NMBS_ERROR_NONE;
|
||
|
||
default:
|
||
return NMBS_ERROR_INVALID_REQUEST;
|
||
}
|
||
return NMBS_EXCEPTION_SERVER_DEVICE_FAILURE;
|
||
}
|
||
|
||
// 处理输入寄存器读取
|
||
nmbs_error mb_handle_read_input_registers(uint16_t address, uint16_t quantity, uint16_t *registers_out)
|
||
{
|
||
mb_command_t *mb = get_command_instance();
|
||
if (!mb)
|
||
return NMBS_EXCEPTION_SERVER_DEVICE_FAILURE;
|
||
|
||
if (address + quantity > 0x0029)
|
||
return NMBS_ERROR_INVALID_REQUEST;
|
||
|
||
for (uint16_t i = 0; i < quantity;)
|
||
{
|
||
uint16_t curr_addr = address + i;
|
||
switch (curr_addr)
|
||
{
|
||
case INPUT_REG_REAL_TEMP_H:
|
||
{
|
||
if (mb->input_lock != RT_NULL)
|
||
rt_mutex_take(mb->input_lock, RT_WAITING_FOREVER);
|
||
registers_out[i++] = mb->input_regs[INPUT_REG_REAL_TEMP_H];
|
||
if (i < quantity)
|
||
registers_out[i++] = mb->input_regs[INPUT_REG_REAL_TEMP_L];
|
||
if (mb->input_lock != RT_NULL)
|
||
rt_mutex_release(mb->input_lock);
|
||
ET_DBG("Send real temp high: 0x%x low: 0x%x", mb->input_regs[INPUT_REG_REAL_TEMP_H],
|
||
mb->input_regs[INPUT_REG_REAL_TEMP_L]);
|
||
break;
|
||
}
|
||
case INPUT_REG_REAL_ROT_H:
|
||
{
|
||
if (mb->input_lock != RT_NULL)
|
||
rt_mutex_take(mb->input_lock, RT_WAITING_FOREVER);
|
||
registers_out[i++] = mb->input_regs[INPUT_REG_REAL_ROT_H];
|
||
if (i < quantity)
|
||
registers_out[i++] = mb->input_regs[INPUT_REG_REAL_ROT_L];
|
||
if (mb->input_lock != RT_NULL)
|
||
rt_mutex_release(mb->input_lock);
|
||
ET_DBG("Send real rot high: 0x%x low: 0x%x", mb->input_regs[INPUT_REG_REAL_ROT_H],
|
||
mb->input_regs[INPUT_REG_REAL_ROT_L]);
|
||
break;
|
||
}
|
||
case INPUT_REG_RESULT_MODE:
|
||
registers_out[i++] = mb->input_regs[INPUT_REG_RESULT_MODE];
|
||
ET_DBG("Send result val high: 0x%x low: 0x%x", mb->input_regs[INPUT_REG_RESULT_VAL_H],
|
||
mb->input_regs[INPUT_REG_RESULT_VAL_L]);
|
||
break;
|
||
case INPUT_REG_RESULT_VAL_H:
|
||
{
|
||
if (mb->input_lock != RT_NULL)
|
||
rt_mutex_take(mb->input_lock, RT_WAITING_FOREVER);
|
||
registers_out[i++] = mb->input_regs[INPUT_REG_RESULT_VAL_H];
|
||
if (i < quantity)
|
||
registers_out[i++] = mb->input_regs[INPUT_REG_RESULT_VAL_L];
|
||
if (mb->input_lock != RT_NULL)
|
||
rt_mutex_release(mb->input_lock);
|
||
ET_DBG("Send result val high: 0x%x low: 0x%x", mb->input_regs[INPUT_REG_RESULT_VAL_H],
|
||
mb->input_regs[INPUT_REG_RESULT_VAL_L]);
|
||
break;
|
||
}
|
||
case INPUT_REG_RESULT_TEMP_H:
|
||
{
|
||
if (mb->input_lock != RT_NULL)
|
||
rt_mutex_take(mb->input_lock, RT_WAITING_FOREVER);
|
||
registers_out[i++] = mb->input_regs[INPUT_REG_RESULT_TEMP_H];
|
||
if (i < quantity)
|
||
registers_out[i++] = mb->input_regs[INPUT_REG_RESULT_TEMP_L];
|
||
if (mb->input_lock != RT_NULL)
|
||
rt_mutex_release(mb->input_lock);
|
||
ET_DBG("Send result temp high: 0x%x low: 0x%x", mb->input_regs[INPUT_REG_RESULT_TEMP_H],
|
||
mb->input_regs[INPUT_REG_RESULT_TEMP_L]);
|
||
break;
|
||
}
|
||
case INPUT_REG_DEV_RUNNING_STATE:
|
||
registers_out[i++] = mb->input_regs[INPUT_REG_DEV_RUNNING_STATE];
|
||
ET_DBG("Send device running state: %d", mb->input_regs[INPUT_REG_DEV_RUNNING_STATE]);
|
||
break;
|
||
case INPUT_REG_ALARM_STATUS:
|
||
{
|
||
/* Output two registers (high then low) representing 32-bit alarm bitmap */
|
||
registers_out[i++] = mb->input_regs[INPUT_REG_ALARM_STATUS];
|
||
if (i < quantity)
|
||
registers_out[i++] = mb->input_regs[INPUT_REG_ALARM_STATUS + 1];
|
||
ET_DBG("Send alarm status high: 0x%x low: 0x%x", mb->input_regs[INPUT_REG_ALARM_STATUS],
|
||
mb->input_regs[INPUT_REG_ALARM_STATUS + 1]);
|
||
break;
|
||
}
|
||
case INPUT_REG_MEAS_DONE_FLAG:
|
||
registers_out[i++] = mb->input_regs[INPUT_REG_MEAS_DONE_FLAG];
|
||
ET_DBG("Send meas done flag: %d", mb->input_regs[INPUT_REG_MEAS_DONE_FLAG]);
|
||
break;
|
||
case INPUT_REG_DEVICE_MODEL_H:
|
||
registers_out[i++] = mb->input_regs[INPUT_REG_DEVICE_MODEL_H];
|
||
ET_DBG("Send device model high: 0x%x", mb->input_regs[INPUT_REG_DEVICE_MODEL_H]);
|
||
break;
|
||
case INPUT_REG_DEVICE_MODEL_L:
|
||
registers_out[i++] = mb->input_regs[INPUT_REG_DEVICE_MODEL_L];
|
||
ET_DBG("Send device model low: 0x%x", mb->input_regs[INPUT_REG_DEVICE_MODEL_L]);
|
||
break;
|
||
case INPUT_REG_FW_VERSION_H:
|
||
registers_out[i++] = mb->input_regs[INPUT_REG_FW_VERSION_H];
|
||
ET_DBG("Send firmware version high: 0x%x", mb->input_regs[INPUT_REG_FW_VERSION_H]);
|
||
break;
|
||
case INPUT_REG_FW_VERSION_L:
|
||
registers_out[i++] = mb->input_regs[INPUT_REG_FW_VERSION_L];
|
||
ET_DBG("Send firmware version low: 0x%x", mb->input_regs[INPUT_REG_FW_VERSION_L]);
|
||
break;
|
||
default:
|
||
return NMBS_ERROR_INVALID_REQUEST;
|
||
}
|
||
}
|
||
return NMBS_ERROR_NONE;
|
||
}
|
||
|
||
|
||
mb_cmd_handler_t *get_command_handler(void)
|
||
{
|
||
return cmd_handler;
|
||
}
|
||
|
||
mb_command_t *get_command_instance(void)
|
||
{
|
||
return mb_cmd;
|
||
}
|
||
|
||
|
||
// 初始化命令模块
|
||
void mb_command_init(mb_command_t *cmd)
|
||
{
|
||
cmd_handler = cmd->handler;
|
||
mb_cmd = cmd;
|
||
/* create mutex for protecting input_regs (ignore if already created) */
|
||
if (mb_cmd->input_lock == RT_NULL)
|
||
{
|
||
mb_cmd->input_lock = rt_mutex_create("mb_in_lk", RT_IPC_FLAG_PRIO);
|
||
}
|
||
}
|