Files
DLD154Pro/BLE/DLD154Pro_Peripheral_28K/APP/rs485_modbus.c
T
wangfq d1fde88dd1 feat(DLD154Pro): RS485 省 Flash 减配 + 固件 V3.05
为节省 Flash 资源,注释掉 RS485 侧一批低频/非核心协议分支与调试打印:
- Modbus: FC 0x03(读保持寄存器)/0x10(批量写)/0x11(Report Slave ID)分发禁用;
  IR 0x0006/0x0007(Variation)/0x000A/0x000B/0x000C 读禁用(Variation 走全数据块
  0x010A/0x010B);HR 0x0000–0x0004/0x0009/0x000A 写禁用(FC 0x06 仅剩
  0x0010/0x0011/0x0012 有效)
- 700BS: 0x3D DIP 查询禁用 + 4 处调试 PRINT 移除
- MB_MAX_READ_REGS 125→40(40×2=80B 与 MB_MAX_DATA_BYTES 匹配,原 60 会越界 40B)
- 呼吸灯光耦加速步长 32→64;OTA 跳转前 mDelaymS(5) 移除
- cmcng.h 版本 V3.04 → V3.05(字符串 + SUB 宏同步)

协议文档已同步标注未实现(rs485-modbus V1.8 / rs485-700bs),见 docs commit。
2026-08-27 08:55:20 +08:00

855 lines
32 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/*
* rs485_modbus.c
* DLD154Pro RS485 Modbus RTU 从机协议 — V1.6 私有扩展
*
* 实现功能:
* FC 0x02 — 读离散输入 (车辆状态/故障/光耦/继电器)
* FC 0x03 — 读保持寄存器 (配置参数 + 主动上报控制)
* FC 0x04 — 读输入寄存器 (传感器数据 + 全数据块 0x01000x0112)
* FC 0x06 — 写单寄存器
* FC 0x10 — 批量写寄存器
* FC 0x11 — Report Slave ID
* 主动上报 — 3 级速率调度, 帧格式复用 FC 0x04 响应
*
* 2026-07-30 初始实现
*/
#include "CONFIG.h"
#include "CH59x_common.h"
#include "cmcng.h"
#include "rs485_modbus.h"
#include "rs485_700bs.h" /* rs485_send(), UART ISR 共享缓冲 */
#include "storage.h"
#include "dbn_ble_srv.h"
#include <string.h>
/*===========================================================================
* CRC16 查表 — poly=0xA001, init=0xFFFF
*===========================================================================*/
static const uint16_t _crc16_table[256] = {
0x0000,0xC0C1,0xC181,0x0140,0xC301,0x03C0,0x0280,0xC241,
0xC601,0x06C0,0x0780,0xC741,0x0500,0xC5C1,0xC481,0x0440,
0xCC01,0x0CC0,0x0D80,0xCD41,0x0F00,0xCFC1,0xCE81,0x0E40,
0x0A00,0xCAC1,0xCB81,0x0B40,0xC901,0x09C0,0x0880,0xC841,
0xD801,0x18C0,0x1980,0xD941,0x1B00,0xDBC1,0xDA81,0x1A40,
0x1E00,0xDEC1,0xDF81,0x1F40,0xDD01,0x1DC0,0x1C80,0xDC41,
0x1400,0xD4C1,0xD581,0x1540,0xD701,0x17C0,0x1680,0xD641,
0xD201,0x12C0,0x1380,0xD341,0x1100,0xD1C1,0xD081,0x1040,
0xF001,0x30C0,0x3180,0xF141,0x3300,0xF3C1,0xF281,0x3240,
0x3600,0xF6C1,0xF781,0x3740,0xF501,0x35C0,0x3480,0xF441,
0x3C00,0xFCC1,0xFD81,0x3D40,0xFF01,0x3FC0,0x3E80,0xFE41,
0xFA01,0x3AC0,0x3B80,0xFB41,0x3900,0xF9C1,0xF881,0x3840,
0x2800,0xE8C1,0xE981,0x2940,0xEB01,0x2BC0,0x2A80,0xEA41,
0xEE01,0x2EC0,0x2F80,0xEF41,0x2D00,0xEDC1,0xEC81,0x2C40,
0xE401,0x24C0,0x2580,0xE541,0x2700,0xE7C1,0xE681,0x2640,
0x2200,0xE2C1,0xE381,0x2340,0xE101,0x21C0,0x2080,0xE041,
0xA001,0x60C0,0x6180,0xA141,0x6300,0xA3C1,0xA281,0x6240,
0x6600,0xA6C1,0xA781,0x6740,0xA501,0x65C0,0x6480,0xA441,
0x6C00,0xACC1,0xAD81,0x6D40,0xAF01,0x6FC0,0x6E80,0xAE41,
0xAA01,0x6AC0,0x6B80,0xAB41,0x6900,0xA9C1,0xA881,0x6840,
0x7800,0xB8C1,0xB981,0x7940,0xBB01,0x7BC0,0x7A80,0xBA41,
0xBE01,0x7EC0,0x7F80,0xBF41,0x7D00,0xBDC1,0xBC81,0x7C40,
0xB401,0x74C0,0x7580,0xB541,0x7700,0xB7C1,0xB681,0x7640,
0x7200,0xB2C1,0xB381,0x7340,0xB101,0x71C0,0x7080,0xB041,
0x5000,0x90C1,0x9181,0x5140,0x9301,0x53C0,0x5280,0x9241,
0x9601,0x56C0,0x5780,0x9741,0x5500,0x95C1,0x9481,0x5440,
0x9C01,0x5CC0,0x5D80,0x9D41,0x5F00,0x9FC1,0x9E81,0x5E40,
0x5A00,0x9AC1,0x9B81,0x5B40,0x9901,0x59C0,0x5880,0x9841,
0x8801,0x48C0,0x4980,0x8941,0x4B00,0x8BC1,0x8A81,0x4A40,
0x4E00,0x8EC1,0x8F81,0x4F40,0x8D01,0x4DC0,0x4C80,0x8C41,
0x4400,0x84C1,0x8581,0x4540,0x8701,0x47C0,0x4680,0x8641,
0x8201,0x42C0,0x4380,0x8341,0x4100,0x81C1,0x8081,0x4040,
};
static uint16_t crc16(const uint8_t *data, uint8_t len)
{
uint16_t crc = 0xFFFF;
for (uint8_t i = 0; i < len; i++) {
crc = (crc >> 8) ^ _crc16_table[(crc ^ data[i]) & 0xFF];
}
return crc;
}
/*===========================================================================
* 访问共享的 700BS ISR 接收缓冲
*
* rs485_700bs.c 的 UART0_IRQHandler 负责填充这 3 个变量,
* Modbus 轮询时从同一缓冲读取。
*===========================================================================*/
extern uint8_t _rx_buf[16];
extern uint8_t _rx_len;
extern uint32_t _rx_last_tick;
extern uint8_t loop1_LOOP_OK; /* 线圈连接状态 (1=正常, 0=断开) */
/*===========================================================================
* Modbus 协议状态
*===========================================================================*/
static uint8_t _auto_rpt_enable = 0; /* HR 0x0010 影子 */
static uint16_t _active_threshold = 10; /* HR 0x0011 影子, 默认 10 */
/* 主动上报调度 */
static uint32_t _rpt_last_ms = 0; /* 上次上报时刻 (mstick) */
static uint8_t _rpt_active = 0; /* 当前上报速率: 0=空闲, 1=活动 */
static uint8_t _rpt_hyst_cnt = 0; /* 降速迟滞计数器 */
/* 车间距 / 通过时间 (10ms 单位) */
static uint32_t _last_relay_off_10ms = 0; /* 上次继电器释放时刻 (mstick/2) */
static uint32_t _last_relay_on_10ms = 0; /* 上次继电器吸合时刻 (mstick/2) */
static uint8_t _last_relay_state = 0; /* 继电器状态快照 (边沿检测) */
static uint16_t _misc_time_type = 0xFFFF; /* 0=间隙, 1=通过, 0xFFFF=无效 */
static uint32_t _misc_time_value = 0; /* 时间值 (10ms 单位) */
/* 计数器 (上电清零) */
static uint32_t _relay_engage_cnt = 0; /* 继电器吸合累计次数 */
/* 线圈状态追踪 (断开/重连 事件触发) */
static uint8_t _last_loop_ok = 0; /* 线圈连接状态快照 (边沿检测) */
/*===========================================================================
* 主动上报 — 全数据块组装 & 发送
*===========================================================================*/
/* 计算线圈频率 (Hz) */
static uint32_t calc_freq(void)
{
extern uint32_t loop1_CAPVD;
extern uint16_t loop1_LPCNT;
if (loop1_CAPVD == 0 || loop1_LPCNT == 0) return 0;
return (uint32_t)((uint64_t)FREQ_SYS * loop1_LPCNT / loop1_CAPVD) + 360;
}
/* 计算 Variation = CAPVD Origin */
static int32_t calc_variation(void)
{
extern uint32_t loop1_CAPVD;
extern uint32_t loop1_Origin;
return (int32_t)(loop1_CAPVD - loop1_Origin);
}
/* 构建全数据块 (19 寄存器 = 38 字节, big-endian) */
static void build_full_data(uint8_t *buf)
{
extern uint32_t loop1_CAPVD;
extern uint32_t loop1_Origin;
extern uint8_t loop1_LOOP_OK;
extern uint32_t g_loop_cut_amount;
int32_t var;
uint32_t freq, capvd, origin, val32;
/* 线圈侧: 只读快照 */
var = calc_variation();
freq = calc_freq();
capvd = loop1_CAPVD;
origin = loop1_Origin;
/* 0x0100 Sensitivity */
buf[0] = 0x00;
buf[1] = g_loop_cng_unit.sensitvity & 0x0F;
/* 0x0101 Freq Level */
buf[2] = 0x00;
buf[3] = g_freq_level & 0x01;
/* 0x0102 Fusion Mode */
buf[4] = 0x00;
buf[5] = g_output_radar_mode & 0x0F;
/* 0x0103 Car State */
buf[6] = 0x00;
buf[7] = g_loop_acs_info.car_state ? 0x01 : 0x00;
/* 0x0104 Loop Status: bit0=断开, bit1=稳定 */
{
uint8_t st = 0;
if (!loop1_LOOP_OK) st |= 0x01;
if (g_loop_stable) st |= 0x02;
buf[8] = 0x00;
buf[9] = st;
}
/* 0x0105 Opto Status (光耦输入低电平有效) */
{
uint8_t opto = (GPIOB_ReadPortPin(GPIO_Pin_10) == 0) ? 1 : 0;
buf[10] = 0x00;
buf[11] = opto;
}
/* 0x01060x0107 Loop Freq (Hz, 32-bit big-endian) */
buf[12] = (uint8_t)(freq >> 24);
buf[13] = (uint8_t)(freq >> 16);
buf[14] = (uint8_t)(freq >> 8);
buf[15] = (uint8_t)(freq);
/* 0x01080x0109 CAPVD (32-bit big-endian) */
buf[16] = (uint8_t)(capvd >> 24);
buf[17] = (uint8_t)(capvd >> 16);
buf[18] = (uint8_t)(capvd >> 8);
buf[19] = (uint8_t)(capvd);
/* 0x010A0x010B Variation (32-bit signed, big-endian) */
val32 = (uint32_t)var;
buf[20] = (uint8_t)(val32 >> 24);
buf[21] = (uint8_t)(val32 >> 16);
buf[22] = (uint8_t)(val32 >> 8);
buf[23] = (uint8_t)(val32);
/* 0x010C0x010D Cut Cnt (线圈断开累计, 32-bit big-endian) */
val32 = g_loop_cut_amount;
buf[24] = (uint8_t)(val32 >> 24);
buf[25] = (uint8_t)(val32 >> 16);
buf[26] = (uint8_t)(val32 >> 8);
buf[27] = (uint8_t)(val32);
/* 0x010E0x010F Relay Cnt (继电器吸合累计, 32-bit big-endian) */
buf[28] = (uint8_t)(_relay_engage_cnt >> 24);
buf[29] = (uint8_t)(_relay_engage_cnt >> 16);
buf[30] = (uint8_t)(_relay_engage_cnt >> 8);
buf[31] = (uint8_t)(_relay_engage_cnt);
/* 0x0110 Time Type */
buf[32] = (uint8_t)(_misc_time_type >> 8);
buf[33] = (uint8_t)(_misc_time_type);
/* 0x01110x0112 Misc Time (10ms 单位, 32-bit big-endian) */
buf[34] = (uint8_t)(_misc_time_value >> 24);
buf[35] = (uint8_t)(_misc_time_value >> 16);
buf[36] = (uint8_t)(_misc_time_value >> 8);
buf[37] = (uint8_t)(_misc_time_value);
}
/* 发送全数据块 (FC 0x04 响应格式) */
static void send_full_data(void)
{
uint8_t buf[MB_FULL_DATA_FRAME_LEN];
uint16_t crc;
buf[0] = g_rs485_id & 0xFF; /* 从机地址 */
buf[1] = 0x04; /* FC 0x04 */
buf[2] = 38; /* 字节计数 = 19 × 2 */
build_full_data(&buf[3]);
crc = crc16(buf, 41);
buf[41] = (uint8_t)(crc);
buf[42] = (uint8_t)(crc >> 8);
rs485_send(buf, MB_FULL_DATA_FRAME_LEN);
}
/* 更新车间距 / 通过时间 (每次 relay state 翻转时调用) */
static void update_misc_time(void)
{
uint32_t now_10ms = mstick() / 2; /* 5ms/tick → 10ms 单位 */
uint8_t relay = g_loop_acs_info.relay1_state;
if (relay == _last_relay_state) return;
if (relay) {
/* 0→1 吸合沿: 车间距 = now − 上次释放时刻 */
_misc_time_value = now_10ms - _last_relay_off_10ms;
_misc_time_type = 0; /* 间隙时间 */
_last_relay_on_10ms = now_10ms;
_relay_engage_cnt++;
} else {
/* 1→0 释放沿: 通过时间 = now − 上次吸合时刻 */
_misc_time_value = now_10ms - _last_relay_on_10ms;
_misc_time_type = 1; /* 通过时间 */
_last_relay_off_10ms = now_10ms;
}
_last_relay_state = relay;
}
/*===========================================================================
* 主动上报速率调度
*===========================================================================*/
/* 判断是否处于活动状态 (|Variation| ≥ 阈值) */
static uint8_t is_active(void)
{
int32_t var = calc_variation();
if (var < 0) var = -var;
return ((uint32_t)var >= _active_threshold) ? 1 : 0;
}
/* 主动上报调度 — 每 tick 调用一次 */
static void modbus_report_schedule(void)
{
if (!_auto_rpt_enable) return;
/* 继电器翻转 → 立即上报 */
{
uint8_t relay = g_loop_acs_info.relay1_state;
if (relay != _last_relay_state) {
update_misc_time(); /* 先更新时间值 */
send_full_data(); /* 立即发送 */
_rpt_last_ms = mstick();
_rpt_hyst_cnt = 0;
_rpt_active = is_active();
return;
}
}
/* 线圈断开/重连 → 立即上报 (对齐 700BS 行为) */
{
uint8_t loop_ok = loop1_LOOP_OK;
if (loop_ok != _last_loop_ok) {
_last_loop_ok = loop_ok;
update_misc_time();
send_full_data();
_rpt_last_ms = mstick();
_rpt_hyst_cnt = 0;
_rpt_active = is_active();
return;
}
}
uint32_t now = mstick();
uint32_t elapsed = now - _rpt_last_ms;
if (is_active()) {
/* 活动态: 150ms 间隔 */
if (elapsed >= MB_RPT_ACTIVE_MS) {
update_misc_time();
send_full_data();
_rpt_last_ms = now;
_rpt_active = 1;
_rpt_hyst_cnt = 0;
}
} else {
/* 空闲态: 需要先确认降速 (连续 2 次低于阈值) */
if (_rpt_active) {
_rpt_hyst_cnt++;
if (_rpt_hyst_cnt < MB_RPT_ACTIVE_HYST) {
/* 仍在迟滞期, 保持活动间隔 */
if (elapsed >= MB_RPT_ACTIVE_MS) {
update_misc_time();
send_full_data();
_rpt_last_ms = now;
}
return;
}
/* 迟滞期满 → 降速 */
_rpt_active = 0;
_rpt_hyst_cnt = 0;
_rpt_last_ms = now; /* 重置计时, 不立即发 */
}
/* 空闲态: 800ms 间隔 */
if (elapsed >= MB_RPT_IDLE_MS) {
update_misc_time();
send_full_data();
_rpt_last_ms = now;
}
}
}
/*===========================================================================
* 异常响应
*===========================================================================*/
static void send_exception(uint8_t fc, uint8_t excode)
{
uint8_t buf[5];
uint16_t crc;
buf[0] = g_rs485_id & 0xFF;
buf[1] = fc | 0x80; /* 功能码最高位置 1 */
buf[2] = excode;
crc = crc16(buf, 3);
buf[3] = (uint8_t)(crc);
buf[4] = (uint8_t)(crc >> 8);
rs485_send(buf, 5);
}
/*===========================================================================
* FC 0x02 — 读离散输入
*===========================================================================*/
static void handle_read_discrete_inputs(const uint8_t *rx, uint8_t rx_len)
{
(void)rx_len; /* 固定 8 字节请求 */
extern uint8_t loop1_LOOP_OK;
uint16_t start_addr = ((uint16_t)rx[2] << 8) | rx[3];
uint16_t quantity = ((uint16_t)rx[4] << 8) | rx[5];
if (quantity == 0 || quantity > 16) {
send_exception(0x02, 0x03); /* Illegal Data Value */
return;
}
/* 构建 9-bit 位图 (9 个离散输入: 0x00000x0008) */
uint16_t bits = 0;
if (g_loop_acs_info.car_state) bits |= (1 << 0); /* 0x0000 */
if (!loop1_LOOP_OK) bits |= (1 << 1); /* 0x0001 */
if (g_loop_stable) bits |= (1 << 2); /* 0x0002 */
if (GPIOB_ReadPortPin(GPIO_Pin_10) == 0) bits |= (1 << 3); /* 0x0003 光耦有效=低电平 */
if (!g_loop_power_up_state) bits |= (1 << 4); /* 0x0004 上电初始化 */
if (g_loop_acs_info.relay1_state) bits |= (1 << 5); /* 0x0005 */
/* 0x0006 Freq Low Fault — DLD154Pro 未实现, 恒为 0 */
/* 0x0007 Freq High Fault — DLD154Pro 未实现, 恒为 0 */
/* 0x0008 Init Flag — DLD154Pro 未实现, 恒为 0 */
if (start_addr >= 9) {
send_exception(0x02, 0x02); /* Illegal Data Address */
return;
}
/* 读取指定位段 */
uint16_t mask = ((1 << quantity) - 1) << start_addr;
uint8_t val = (uint8_t)((bits & mask) >> start_addr);
uint8_t buf[6];
uint16_t crc;
buf[0] = g_rs485_id & 0xFF;
buf[1] = 0x02;
buf[2] = 1; /* 1 字节数据 */
buf[3] = val;
crc = crc16(buf, 4);
buf[4] = (uint8_t)(crc);
buf[5] = (uint8_t)(crc >> 8);
rs485_send(buf, 6);
}
/*===========================================================================
* FC 0x04 — 读输入寄存器 (含全数据块 0x01000x0112)
*===========================================================================*/
static uint8_t read_ir_register(uint16_t addr, uint8_t *hi, uint8_t *lo)
{
extern uint32_t loop1_CAPVD;
extern uint32_t loop1_Origin;
extern uint8_t loop1_LOOP_OK;
extern uint32_t g_loop_cut_amount;
int32_t var;
uint32_t freq, capvd, val32;
switch (addr) {
/* 基础 IR 0x00000x000C (保持向后兼容) */
case 0x0000: /* Freq Hi */
freq = calc_freq();
*hi = (uint8_t)(freq >> 24); *lo = (uint8_t)(freq >> 16); return 1;
case 0x0001: /* Freq Lo */
freq = calc_freq();
*hi = (uint8_t)(freq >> 8); *lo = (uint8_t)(freq); return 1;
case 0x0002: /* CAPVD Hi */
*hi = (uint8_t)(loop1_CAPVD >> 24); *lo = (uint8_t)(loop1_CAPVD >> 16); return 1;
case 0x0003: /* CAPVD Lo */
*hi = (uint8_t)(loop1_CAPVD >> 8); *lo = (uint8_t)(loop1_CAPVD); return 1;
case 0x0004: /* Origin Hi */
*hi = (uint8_t)(loop1_Origin >> 24); *lo = (uint8_t)(loop1_Origin >> 16); return 1;
case 0x0005: /* Origin Lo */
*hi = (uint8_t)(loop1_Origin >> 8); *lo = (uint8_t)(loop1_Origin); return 1;
// case 0x0006: /* Variation Hi */
// var = (int32_t)(loop1_CAPVD - loop1_Origin);
// val32 = (uint32_t)var;
// *hi = (uint8_t)(val32 >> 24); *lo = (uint8_t)(val32 >> 16); return 1;
// case 0x0007: /* Variation Lo */
// var = (int32_t)(loop1_CAPVD - loop1_Origin);
// val32 = (uint32_t)var;
// *hi = (uint8_t)(val32 >> 8); *lo = (uint8_t)(val32); return 1;
case 0x0008: /* FW Version (BCD: v3.01 → 0x0301) */
*hi = FIRMWARE_VER_MAIN; *lo = FIRMWARE_VER_SUB; return 1;
case 0x0009: /* HW Version */
*hi = HARDWARE_VER_MAIN; *lo = HARDWARE_VER_SUB; return 1;
// case 0x000A: /* Freq Sens */
// *hi = 0; *lo = g_loop_cng_unit.sensitvity & 0x0F; return 1;
// case 0x000B: /* Condition */
// *hi = 0; *lo = g_loop_acs_info.condition; return 1;
// case 0x000C: /* DIP Virtual: bit[7:4]=function_mode, bit[3:0]=fusion_mode */
// *hi = 0; *lo = (g_function_mode << 4) | (g_output_radar_mode & 0x0F); return 1;
/* 全数据块 0x01000x0112 — 热路径, 直接计算 */
default:
if (addr >= 0x0100 && addr <= 0x0112) {
/* build_full_data 的偏移量映射 */
uint8_t off = (uint8_t)((addr - 0x0100) * 2);
uint8_t tmp[38];
build_full_data(tmp);
if (off < 38) {
*hi = tmp[off];
*lo = tmp[off + 1];
return 1;
}
}
return 0; /* 非法地址 */
}
}
static void handle_read_input_regs(const uint8_t *rx, uint8_t rx_len)
{
(void)rx_len;
uint16_t start_addr = ((uint16_t)rx[2] << 8) | rx[3];
uint16_t quantity = ((uint16_t)rx[4] << 8) | rx[5];
if (quantity == 0 || quantity > MB_MAX_READ_REGS) {
send_exception(0x04, 0x03);
return;
}
uint8_t data_buf[MB_MAX_DATA_BYTES];
uint16_t reg_addr;
for (uint16_t i = 0; i < quantity; i++) {
reg_addr = start_addr + i;
uint8_t hi, lo;
if (!read_ir_register(reg_addr, &hi, &lo)) {
send_exception(0x04, 0x02);
return;
}
data_buf[i * 2] = hi;
data_buf[i * 2 + 1] = lo;
}
uint8_t byte_cnt = (uint8_t)(quantity * 2);
uint8_t buf[MB_RESP_BUF_SIZE];
uint16_t crc;
buf[0] = g_rs485_id & 0xFF;
buf[1] = 0x04;
buf[2] = byte_cnt;
memcpy(&buf[3], data_buf, byte_cnt);
crc = crc16(buf, 3 + byte_cnt);
buf[3 + byte_cnt] = (uint8_t)(crc);
buf[3 + byte_cnt + 1] = (uint8_t)(crc >> 8);
rs485_send(buf, 3 + byte_cnt + 2);
}
/*===========================================================================
* FC 0x03 — 读保持寄存器
*===========================================================================*/
static uint8_t read_hr_register(uint16_t addr, uint8_t *hi, uint8_t *lo)
{
switch (addr) {
case 0x0000: /* Sensitivity */
*hi = 0; *lo = g_loop_cng_unit.sensitvity & 0x0F; return 1;
case 0x0001: /* Fusion Mode */
*hi = 0; *lo = g_output_radar_mode & 0x0F; return 1;
case 0x0002: /* Hold Time */
*hi = 0; *lo = g_loop_cng_unit.exist_mode; return 1;
case 0x0003: /* Output Delay */
*hi = 0; *lo = g_loop_cng_unit.delay_time; return 1;
case 0x0004: /* Freq Level */
*hi = 0; *lo = g_freq_level & 0x01; return 1;
case 0x0005: /* RS485 Baud Hi (只读) */
*hi = (uint8_t)(g_storage_uart_baud >> 24); *lo = (uint8_t)(g_storage_uart_baud >> 16); return 1;
case 0x0006: /* RS485 Baud Lo (只读) */
*hi = (uint8_t)(g_storage_uart_baud >> 8); *lo = (uint8_t)(g_storage_uart_baud); return 1;
case 0x0007: /* RS485 Addr (只读) */
*hi = 0; *lo = g_rs485_id; return 1;
case 0x0008: /* Protocol (只读) */
*hi = 0; *lo = g_fm_700bs_disable; return 1;
case 0x0009: /* BT Timeout */
*hi = 0; *lo = g_max_counter_bt_min; return 1;
case 0x000A: /* Safe Timeout */
*hi = 0; *lo = g_loop_cng_unit.loopSafe_Timeout; return 1;
case 0x000B: /* Opto Func (只读) */
*hi = 0; *lo = (g_function_mode >> 1) & 0x01; return 1;
case 0x0010: /* Auto Rpt Enable */
*hi = 0; *lo = _auto_rpt_enable; return 1;
case 0x0011: /* Active Threshold */
*hi = (uint8_t)(_active_threshold >> 8); *lo = (uint8_t)(_active_threshold); return 1;
case 0x0012: /* System Cmd (只读, 恒为 0) */
*hi = 0; *lo = 0; return 1;
default:
return 0;
}
}
static void handle_read_holding_regs(const uint8_t *rx, uint8_t rx_len)
{
(void)rx_len;
uint16_t start_addr = ((uint16_t)rx[2] << 8) | rx[3];
uint16_t quantity = ((uint16_t)rx[4] << 8) | rx[5];
if (quantity == 0 || quantity > MB_MAX_READ_REGS) {
send_exception(0x03, 0x03);
return;
}
uint8_t data_buf[MB_MAX_DATA_BYTES];
for (uint16_t i = 0; i < quantity; i++) {
uint16_t reg_addr = start_addr + i;
uint8_t hi, lo;
if (!read_hr_register(reg_addr, &hi, &lo)) {
send_exception(0x03, 0x02);
return;
}
data_buf[i * 2] = hi;
data_buf[i * 2 + 1] = lo;
}
uint8_t byte_cnt = (uint8_t)(quantity * 2);
uint8_t buf[MB_RESP_BUF_SIZE];
uint16_t crc;
buf[0] = g_rs485_id & 0xFF;
buf[1] = 0x03;
buf[2] = byte_cnt;
memcpy(&buf[3], data_buf, byte_cnt);
crc = crc16(buf, 3 + byte_cnt);
buf[3 + byte_cnt] = (uint8_t)(crc);
buf[3 + byte_cnt + 1] = (uint8_t)(crc >> 8);
rs485_send(buf, 3 + byte_cnt + 2);
}
/*===========================================================================
* FC 0x06 — 写单寄存器
*===========================================================================*/
static void handle_write_single_reg(const uint8_t *rx, uint8_t rx_len)
{
(void)rx_len;
uint16_t reg_addr = ((uint16_t)rx[2] << 8) | rx[3];
uint16_t reg_val = ((uint16_t)rx[4] << 8) | rx[5];
/* 只读寄存器拦截 */
switch (reg_addr) {
case 0x0005: case 0x0006: case 0x0007: case 0x0008: case 0x000B:
send_exception(0x06, 0x02); /* Illegal Data Address (只读) */
return;
}
/* 值校验 & 写入 */
switch (reg_addr) {
// case 0x0000: /* Sensitivity: 0-3 */
// if (reg_val > 3) { send_exception(0x06, 0x03); return; }
// g_loop_cng_unit.sensitvity = (uint8_t)reg_val;
// break;
// case 0x0001: /* Fusion Mode: 0-6 */
// if (reg_val > 6) { send_exception(0x06, 0x03); return; }
// g_output_radar_mode = (uint8_t)reg_val;
// g_loop_cng_unit.direction_mode = (g_function_mode << 4) | (g_output_radar_mode & 0x0F);
// break;
// case 0x0002: /* Hold Time: 0-255 */
// g_loop_cng_unit.exist_mode = (uint8_t)reg_val;
// g_hold_time = (uint16_t)reg_val * 20 * 5;
// break;
// case 0x0003: /* Output Delay: 0-255 */
// g_loop_cng_unit.delay_time = (uint8_t)reg_val;
// break;
// case 0x0004: /* Freq Level: 0-1 */
// if (reg_val > 1) { send_exception(0x06, 0x03); return; }
// g_freq_level = (uint8_t)reg_val;
// g_loop_cng_unit.loopFreq_Level = g_freq_level + 1;
// break;
// case 0x0009: /* BT Timeout: 0-255 */
// g_max_counter_bt_min = (uint8_t)reg_val;
// g_max_counter_bt_timeout = (uint32_t)reg_val * 60 * 1000;
// break;
// case 0x000A: /* Safe Timeout: 0-255 */
// g_loop_cng_unit.loopSafe_Timeout = (uint8_t)reg_val;
// break;
case 0x0010: /* Auto Rpt Enable: 0-1 */
if (reg_val > 1) { send_exception(0x06, 0x03); return; }
_auto_rpt_enable = (uint8_t)reg_val;
if (_auto_rpt_enable) {
_rpt_last_ms = mstick(); /* 对齐计时起点 */
_rpt_active = 0;
_rpt_hyst_cnt = 0;
}
break;
case 0x0011: /* Active Threshold: 0-65535 */
_active_threshold = reg_val;
break;
case 0x0012: /* System Cmd: 写 0x2C2C 复位 MCU */
if (reg_val == MB_CMD_RESET_MCU) {
SYS_ResetExecute();
}
/* 非法值: 忽略, 不响应异常 (对齐 700BS 0x2C 行为) */
return;
default:
send_exception(0x06, 0x02); /* Illegal Data Address */
return;
}
/* 回显 (Modbus 标准: 写成功的响应 = 请求原样返回) */
{
uint8_t buf[8];
uint16_t crc;
buf[0] = g_rs485_id & 0xFF;
buf[1] = 0x06;
buf[2] = rx[2]; buf[3] = rx[3]; /* 地址 */
buf[4] = rx[4]; buf[5] = rx[5]; /* 值 */
crc = crc16(buf, 6);
buf[6] = (uint8_t)(crc);
buf[7] = (uint8_t)(crc >> 8);
rs485_send(buf, 8);
}
}
/*===========================================================================
* FC 0x10 — 批量写寄存器
*
* 简化实现: 逐寄存器调用 FC 0x06 的校验+写入逻辑
*===========================================================================*/
static void handle_write_multiple_regs(const uint8_t *rx, uint8_t rx_len)
{
(void)rx_len;
uint16_t start_addr = ((uint16_t)rx[2] << 8) | rx[3];
uint16_t quantity = ((uint16_t)rx[4] << 8) | rx[5];
uint8_t byte_cnt = rx[6];
if (quantity == 0 || quantity > 123 || byte_cnt != quantity * 2) {
send_exception(0x10, 0x03);
return;
}
for (uint16_t i = 0; i < quantity; i++) {
uint16_t reg_addr = start_addr + i;
uint16_t reg_val = ((uint16_t)rx[7 + i * 2] << 8) | rx[8 + i * 2];
/* 只读拦截 */
if (reg_addr == 0x0005 || reg_addr == 0x0006 || reg_addr == 0x0007 ||
reg_addr == 0x0008 || reg_addr == 0x000B) {
send_exception(0x10, 0x02);
return;
}
/* 值校验 — 同样的逻辑 */
switch (reg_addr) {
case 0x0000: if (reg_val > 3) { send_exception(0x10, 0x03); return; }
g_loop_cng_unit.sensitvity = (uint8_t)reg_val; break;
case 0x0001: if (reg_val > 6) { send_exception(0x10, 0x03); return; }
g_output_radar_mode = (uint8_t)reg_val;
g_loop_cng_unit.direction_mode = (g_function_mode << 4) | (g_output_radar_mode & 0x0F); break;
case 0x0002: g_loop_cng_unit.exist_mode = (uint8_t)reg_val;
g_hold_time = (uint16_t)reg_val * 20 * 5; break;
case 0x0003: g_loop_cng_unit.delay_time = (uint8_t)reg_val; break;
case 0x0004: if (reg_val > 1) { send_exception(0x10, 0x03); return; }
g_freq_level = (uint8_t)reg_val;
g_loop_cng_unit.loopFreq_Level = g_freq_level + 1; break;
case 0x0009: g_max_counter_bt_min = (uint8_t)reg_val;
g_max_counter_bt_timeout = (uint32_t)reg_val * 60 * 1000; break;
case 0x000A: g_loop_cng_unit.loopSafe_Timeout = (uint8_t)reg_val; break;
case 0x0010: if (reg_val > 1) { send_exception(0x10, 0x03); return; }
_auto_rpt_enable = (uint8_t)reg_val;
if (_auto_rpt_enable) { _rpt_last_ms = mstick(); _rpt_active = 0; _rpt_hyst_cnt = 0; }
break;
case 0x0011: _active_threshold = reg_val; break;
case 0x0012: /* System Cmd: 写 0x2C2C 复位 MCU */
if (reg_val == MB_CMD_RESET_MCU) {
SYS_ResetExecute();
return; /* 不复位则忽略非法值 */
}
/* 非法值: 忽略, 继续后续寄存器 */
break;
default: send_exception(0x10, 0x02); return;
}
}
/* 响应: 回显 addr + fc + start + qty */
{
uint8_t buf[8];
uint16_t crc;
buf[0] = g_rs485_id & 0xFF;
buf[1] = 0x10;
buf[2] = rx[2]; buf[3] = rx[3];
buf[4] = rx[4]; buf[5] = rx[5];
crc = crc16(buf, 6);
buf[6] = (uint8_t)(crc);
buf[7] = (uint8_t)(crc >> 8);
rs485_send(buf, 8);
}
}
/*===========================================================================
* FC 0x11 — Report Slave ID
*===========================================================================*/
static void handle_report_slave_id(const uint8_t *rx, uint8_t rx_len)
{
(void)rx; (void)rx_len;
extern uint8_t loop1_LOOP_OK;
/* 返回: addr + fc + byte_cnt + model(11) + status + crc */
const char *model = PRODUCT_MODEL; /* "DLD154Pro" = 10 chars + '\0' = 11 */
uint8_t model_len = (uint8_t)strlen(model) + 1; /* 含 '\0' */
uint8_t buf[5 + 12 + 2]; /* addr+fc+bc + model(max12) + status + crc */
uint16_t crc;
uint8_t idx = 0;
buf[idx++] = g_rs485_id & 0xFF;
buf[idx++] = 0x11;
buf[idx++] = model_len + 1; /* byte_cnt = model + status */
memcpy(&buf[idx], model, model_len);
idx += model_len;
/* Status: 0x00=RUN, 0xFF=STOP */
buf[idx++] = loop1_LOOP_OK ? 0x00 : 0xFF;
crc = crc16(buf, idx);
buf[idx++] = (uint8_t)(crc);
buf[idx++] = (uint8_t)(crc >> 8);
rs485_send(buf, idx);
}
/*===========================================================================
* 初始化 & 轮询
*===========================================================================*/
void modbus_init(void)
{
/* 协议状态复位 */
_auto_rpt_enable = 0;
_active_threshold = 10;
_rpt_last_ms = mstick();
_rpt_active = 0;
_rpt_hyst_cnt = 0;
_last_relay_state = g_loop_acs_info.relay1_state;
_last_loop_ok = loop1_LOOP_OK;
_last_relay_off_10ms = 0;
_last_relay_on_10ms = 0;
_misc_time_type = 0xFFFF;
_misc_time_value = 0;
_relay_engage_cnt = 0;
}
void modbus_poll(void)
{
/*=== 帧接收 ===*/
if (_rx_len > 0) {
uint32_t now = mstick();
if (now - _rx_last_tick >= MB_FRAME_TIMEOUT_TICK) {
/* 帧超时 → 处理完整帧 */
uint8_t *rx = _rx_buf;
uint8_t len = _rx_len;
/* 最小帧: addr(1) + fc(1) + crc(2) = 4 */
if (len >= 4) {
uint8_t addr = rx[0];
uint8_t fc = rx[1];
uint16_t crc_rx = ((uint16_t)rx[len - 1] << 8) | rx[len - 2]; /* 低字节在前 */
uint16_t crc_calc = crc16(rx, len - 2);
/* 地址匹配 (0=广播, 本从机不响应) */
if (addr != 0 && addr == (g_rs485_id & 0xFF)) {
if (crc_rx == crc_calc) {
switch (fc) {
case 0x02: handle_read_discrete_inputs(rx, len); break;
// case 0x03: handle_read_holding_regs(rx, len); break;
case 0x04: handle_read_input_regs(rx, len); break;
case 0x06: handle_write_single_reg(rx, len); break;
// case 0x10: handle_write_multiple_regs(rx, len); break;
// case 0x11: handle_report_slave_id(rx, len); break;
default: send_exception(fc, 0x01); break;
}
}
/* CRC 错误 → 静默 (Modbus 标准行为) */
}
/* 地址不匹配 → 静默 */
}
_rx_len = 0; /* 清空缓冲 */
}
}
/*=== 主动上报调度 ===*/
modbus_report_schedule();
}