- rs485_modbus.h: +MB_MAX_READ_REGS(125) / MB_MAX_DATA_BYTES(250) / MB_RESP_BUF_SIZE(255)
- rs485_modbus.c: data_buf[250]→data_buf[MB_MAX_DATA_BYTES]
buf[5+250] →buf[MB_RESP_BUF_SIZE]
quantity>125→quantity>MB_MAX_READ_REGS
819 lines
29 KiB
C
819 lines
29 KiB
C
/*
|
||
* rs485_modbus.c
|
||
* DLD154Pro RS485 Modbus RTU 从机协议 — V1.6 私有扩展
|
||
*
|
||
* 实现功能:
|
||
* FC 0x02 — 读离散输入 (车辆状态/故障/光耦/继电器)
|
||
* FC 0x03 — 读保持寄存器 (配置参数 + 主动上报控制)
|
||
* FC 0x04 — 读输入寄存器 (传感器数据 + 全数据块 0x0100–0x0112)
|
||
* 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;
|
||
}
|
||
|
||
/* 0x0106–0x0107 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);
|
||
|
||
/* 0x0108–0x0109 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);
|
||
|
||
/* 0x010A–0x010B 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);
|
||
|
||
/* 0x010C–0x010D 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);
|
||
|
||
/* 0x010E–0x010F 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);
|
||
|
||
/* 0x0111–0x0112 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 个离散输入: 0x0000–0x0008) */
|
||
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 — 读输入寄存器 (含全数据块 0x0100–0x0112)
|
||
*===========================================================================*/
|
||
|
||
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 0x0000–0x000C (保持向后兼容) */
|
||
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;
|
||
|
||
/* 全数据块 0x0100–0x0112 — 热路径, 直接计算 */
|
||
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;
|
||
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;
|
||
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();
|
||
}
|