Files
DLD154Pro/BLE/DLD154Pro_Peripheral_28K/APP/rs485_modbus.c
T
wangfq e32f3892fe feat: Modbus RTU V1.6 私有协议编码实现
新增:
- rs485_modbus.h/c — 完整 Modbus RTU 从机实现 (516行)
  · FC 0x02 读离散输入 (9-bit 位图: car_state/线圈/光耦/继电器)
  · FC 0x03 读保持寄存器 (含 HR 0x0010/0x0011)
  · FC 0x04 读输入寄存器 (基础 IR + 全数据块 0x0100-0x0112)
  · FC 0x06/0x10 写寄存器 (含值域校验+只读拦截)
  · FC 0x11 Report Slave ID
  · CRC16 查表 (poly=0xA001)
  · 全数据块组装 (19 reg = 38B big-endian)
  · 主动上报 3 级速率调度 (事件立即/活动150ms/空闲800ms)
  · 车间距/通过时间测量 (mstick/2=10ms单位, relay边沿触发)
  · 光耦状态实时读取 (GPIOB_ReadPortPin)
  · Holding Register 写穿透: Fusion Mode 同步 direction_mode,
    Hold Time 同步 g_hold_time, Freq Level 同步 g_loop_cng_unit

改造:
- rs485_700bs.h/c: 暴露 rs485_send() + rx缓冲(_rx_buf/_rx_len/_rx_last_tick) 供Modbus共用
- peripheral_main.c: g_fm_700bs_disable 分派 700BS/Modbus; modbus_init() 随 rs485_init 调用
2026-07-30 12:02:24 +08:00

819 lines
29 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;
/*===========================================================================
* 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; /* 继电器吸合累计次数 */
/*===========================================================================
* 主动上报 — 全数据块组装 & 发送
*===========================================================================*/
/* 计算线圈频率 (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;
}
}
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 > 125) {
send_exception(0x04, 0x03);
return;
}
uint8_t data_buf[250];
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[5 + 250]; /* addr + fc + bc + data + crc */
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;
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 > 125) {
send_exception(0x03, 0x03);
return;
}
uint8_t data_buf[250];
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[5 + 250];
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;
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;
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_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)
{
/*=== 帧接收 ===*/\n 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; /* 清空缓冲 */
}
}
/*=== 主动上报调度 ===*/\n modbus_report_schedule();
}