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 调用
This commit is contained in:
@@ -38,7 +38,10 @@
|
|||||||
/* 初始化 RS485 通信 (UART0 9600 8N1 + 中断接收) */
|
/* 初始化 RS485 通信 (UART0 9600 8N1 + 中断接收) */
|
||||||
void rs485_init(void);
|
void rs485_init(void);
|
||||||
|
|
||||||
/* 主循环轮询 — 处理接收的 3 字节命令帧, 驱动心跳上报 */
|
/* 发送一帧 — 切 RE=TX, 发完等 1ms, 切回 RE=RX (供 700BS / Modbus 模块复用) */
|
||||||
|
void rs485_send(const uint8_t *data, uint8_t len);
|
||||||
|
|
||||||
|
/* 主循环轮询 — 处理接收的 3 字节命令帧, 驱动心跳上报 (仅 700BS 模式) */
|
||||||
void rs485_poll(void);
|
void rs485_poll(void);
|
||||||
|
|
||||||
/* 有车/无车事件触发立即上报 (由检测算法调用) */
|
/* 有车/无车事件触发立即上报 (由检测算法调用) */
|
||||||
@@ -50,4 +53,13 @@ void rs485_loop_notify(void);
|
|||||||
/* 重置心跳计时 (复位后调用, 触发首次 A1 上报) */
|
/* 重置心跳计时 (复位后调用, 触发首次 A1 上报) */
|
||||||
void rs485_reset_heartbeat(void);
|
void rs485_reset_heartbeat(void);
|
||||||
|
|
||||||
|
/*===========================================================================
|
||||||
|
* 共享 UART 接收缓冲 (Modbus 模块共用)
|
||||||
|
*===========================================================================*/
|
||||||
|
|
||||||
|
/* UART0 ISR (rs485_700bs.c) 填充的接收缓冲 */
|
||||||
|
extern uint8_t _rx_buf[16];
|
||||||
|
extern uint8_t _rx_len;
|
||||||
|
extern uint32_t _rx_last_tick;
|
||||||
|
|
||||||
#endif /* __RS485_700BS_H__ */
|
#endif /* __RS485_700BS_H__ */
|
||||||
|
|||||||
@@ -0,0 +1,49 @@
|
|||||||
|
/*
|
||||||
|
* rs485_modbus.h
|
||||||
|
* DLD154Pro RS485 Modbus RTU 从机协议 — V1.6 私有扩展
|
||||||
|
*
|
||||||
|
* 物理层: RS485 半双工 (共享 UART0, 与 700BS 互斥)
|
||||||
|
* 帧格式: ADDR(1) + FC(1) + DATA(N) + CRC16(2)
|
||||||
|
* 主动上报: 3 级速率 (事件立即 / 活动 150ms / 空闲 800ms), FC 0x04 响应格式
|
||||||
|
*
|
||||||
|
* 2026-07-30 初始实现
|
||||||
|
*/
|
||||||
|
#ifndef __RS485_MODBUS_H__
|
||||||
|
#define __RS485_MODBUS_H__
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
/*===========================================================================
|
||||||
|
* 协议常量
|
||||||
|
*===========================================================================*/
|
||||||
|
|
||||||
|
/* 寄存器地址范围 */
|
||||||
|
#define MB_IR_FULL_BLOCK_START 0x0100 /* 全数据块起始地址 */
|
||||||
|
#define MB_IR_FULL_BLOCK_COUNT 19 /* 全数据块寄存器数 (0x0100–0x0112) */
|
||||||
|
|
||||||
|
#define MB_HR_AUTO_RPT_ENABLE 0x0010 /* 主动上报使能 (0=关, 1=开) */
|
||||||
|
#define MB_HR_ACTIVE_THRESHOLD 0x0011 /* 活动状态阈值 (|Variation|≥N) */
|
||||||
|
|
||||||
|
/* 主动上报间隔 (mstick() 5ms/tick) */
|
||||||
|
#define MB_RPT_IDLE_MS 160 /* 空闲: 800ms / 5ms = 160 tick */
|
||||||
|
#define MB_RPT_ACTIVE_MS 30 /* 活动: 150ms / 5ms = 30 tick */
|
||||||
|
#define MB_RPT_ACTIVE_HYST 2 /* 降速迟滞: 连续 N 次低于阈值 */
|
||||||
|
|
||||||
|
/* 帧超时: t3.5 = 3.5 × 11 / 9600 ≈ 4.0ms, 取 2 tick (10ms) */
|
||||||
|
#define MB_FRAME_TIMEOUT_TICK 2
|
||||||
|
|
||||||
|
/* 全数据块 CRC 后的帧总长:
|
||||||
|
* addr(1) + fc(1) + byte_cnt(1) + data(38) + crc(2) = 43 */
|
||||||
|
#define MB_FULL_DATA_FRAME_LEN 43
|
||||||
|
|
||||||
|
/*===========================================================================
|
||||||
|
* API
|
||||||
|
*===========================================================================*/
|
||||||
|
|
||||||
|
/* 初始化 Modbus 协议 (UART0 已在 rs485_init 中配置, 此处仅做协议状态初始化) */
|
||||||
|
void modbus_init(void);
|
||||||
|
|
||||||
|
/* 主循环轮询 — 帧接收 & 主动上报调度 (替代 rs485_poll) */
|
||||||
|
void modbus_poll(void);
|
||||||
|
|
||||||
|
#endif /* __RS485_MODBUS_H__ */
|
||||||
@@ -30,6 +30,7 @@
|
|||||||
#include "dbn_ble_srv.h"
|
#include "dbn_ble_srv.h"
|
||||||
#include "CH59x_pwm.h"
|
#include "CH59x_pwm.h"
|
||||||
#include "rs485_700bs.h"
|
#include "rs485_700bs.h"
|
||||||
|
#include "rs485_modbus.h"
|
||||||
|
|
||||||
|
|
||||||
uint8_t g_flag_bt_state = 0; // 0 蓝牙没有连接,1 有蓝牙连接
|
uint8_t g_flag_bt_state = 0; // 0 蓝牙没有连接,1 有蓝牙连接
|
||||||
@@ -394,6 +395,7 @@ void gpio_uart_init(void)
|
|||||||
{
|
{
|
||||||
/*--- RS485 通信 (PA14=TX, PA15=RX, PA13=RE) ---*/
|
/*--- RS485 通信 (PA14=TX, PA15=RX, PA13=RE) ---*/
|
||||||
rs485_init(); /* 初始化 UART0 + GPIO + 中断 */
|
rs485_init(); /* 初始化 UART0 + GPIO + 中断 */
|
||||||
|
modbus_init(); /* 初始化 Modbus 协议状态 */
|
||||||
|
|
||||||
/*--- 光耦输入 (PB10=IN_1) ---*/
|
/*--- 光耦输入 (PB10=IN_1) ---*/
|
||||||
R32_PIN_CONFIG2 &= ~(1UL << 26); // 使能 PB10 数字输入 (默认关闭省电)
|
R32_PIN_CONFIG2 &= ~(1UL << 26); // 使能 PB10 数字输入 (默认关闭省电)
|
||||||
@@ -1038,7 +1040,11 @@ void Main_Circulation()
|
|||||||
|
|
||||||
opt_input_poll(); // 光耦输入处理 (复位 / 雷达)
|
opt_input_poll(); // 光耦输入处理 (复位 / 雷达)
|
||||||
|
|
||||||
|
if (g_fm_700bs_disable) {
|
||||||
|
modbus_poll(); // RS485 Modbus RTU 协议
|
||||||
|
} else {
|
||||||
rs485_poll(); // RS485 700BS 协议: 命令处理 + 心跳上报
|
rs485_poll(); // RS485 700BS 协议: 命令处理 + 心跳上报
|
||||||
|
}
|
||||||
|
|
||||||
if(g_flag_bt_state == 0){
|
if(g_flag_bt_state == 0){
|
||||||
// 如果蓝牙断开,主动上报需要禁用
|
// 如果蓝牙断开,主动上报需要禁用
|
||||||
|
|||||||
@@ -20,9 +20,9 @@
|
|||||||
* UART0 中断接收缓冲
|
* UART0 中断接收缓冲
|
||||||
*===========================================================================*/
|
*===========================================================================*/
|
||||||
#define RS485_RX_BUF_SIZE 16
|
#define RS485_RX_BUF_SIZE 16
|
||||||
static uint8_t _rx_buf[RS485_RX_BUF_SIZE];
|
uint8_t _rx_buf[RS485_RX_BUF_SIZE];
|
||||||
static uint8_t _rx_len = 0;
|
uint8_t _rx_len = 0;
|
||||||
static uint32_t _rx_last_tick = 0; // 帧间隔计时 (3.5 字符 ≈ 4ms @9600)
|
uint32_t _rx_last_tick = 0; // 帧间隔计时 (3.5 字符 ≈ 4ms @9600)
|
||||||
|
|
||||||
/*===========================================================================
|
/*===========================================================================
|
||||||
* 心跳 & 状态追踪
|
* 心跳 & 状态追踪
|
||||||
@@ -68,7 +68,8 @@ void rs485_init(void)
|
|||||||
/*===========================================================================
|
/*===========================================================================
|
||||||
* 发送一帧 — 切 RE=TX, 发完等 1ms, 切回 RE=RX
|
* 发送一帧 — 切 RE=TX, 发完等 1ms, 切回 RE=RX
|
||||||
*===========================================================================*/
|
*===========================================================================*/
|
||||||
static void rs485_send(const uint8_t *data, uint8_t len)
|
/* 发送一帧 (供 Modbus 模块复用) */
|
||||||
|
void rs485_send(const uint8_t *data, uint8_t len)
|
||||||
{
|
{
|
||||||
RS485_RE_TX();
|
RS485_RE_TX();
|
||||||
mDelayuS(100); // 总线稳定
|
mDelayuS(100); // 总线稳定
|
||||||
|
|||||||
@@ -0,0 +1,818 @@
|
|||||||
|
/*
|
||||||
|
* 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 > 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();
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user