@@ -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 ( ) ;
}