Files
Leakage-Control/calib_board/usr/bsp/tjc_usart_hmi.c
2026-01-22 19:24:33 +08:00

1321 lines
39 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#include "tjc_usart_hmi.h"
#include <stdio.h>
#include <string.h>
#include <stdarg.h>
#include <stddef.h>
#include "bsp_Flash.h" // 添加Flash操作
#define STR_LENGTH 100
// 环形缓冲区结构
typedef struct {
uint16_t Head;
uint16_t Tail;
uint16_t Length;
uint8_t Ring_data[RINGBUFF_LEN];
} RingBuff_t;
static RingBuff_t ringBuff; // 创建一个环形缓冲区
// 指令缓冲区
static uint8_t command_buffer[MAX_COMMAND_LEN];
static uint16_t cmd_index = 0;
// COM_Uart2指针用于访问串口驱动
static bsp_Uart_t *p_TJC_Uart = NULL;
// 设备列表最多32个设备
static DeviceInfo device_list[32];
static uint8_t device_count = 0;
static uint8_t alarm_history_count = 6;
/********************************************************
函数名: TJC_CleanBufferFromInvalidPatterns
功能: 从环形缓冲区中清除所有无效的04 FF FF FF模式
返回值: 清除的字节数
**********************************************************/
uint16_t TJC_CleanBufferFromInvalidPatterns(void) {
uint16_t cleaned_bytes = 0;
uint16_t buff_len = getRingBuffLength();
// 不断查找并删除04 FF FF FF模式
while (buff_len >= 4) {
// 查看缓冲区前4个字节
uint8_t pattern[4];
for (uint8_t i = 0; i < 4; i++) {
pattern[i] = read1BFromRingBuff(i);
}
// 检查是否为 04 FF FF FF 模式
if (pattern[0] == 0x04 &&
pattern[1] == 0xFF &&
pattern[2] == 0xFF &&
pattern[3] == 0xFF) {
// 删除这4个字节
deleteRingBuff(4);
cleaned_bytes += 4;
buff_len = getRingBuffLength();
} else {
// 如果不是04 FF FF FF模式停止清理
break;
}
}
return cleaned_bytes;
}
/********************************************************
函数名: TJC_Init
功能: 初始化TJC串口屏驱动
输入参数: pUart: 指向bsp_Uart_t结构体的指针用于与串口屏通信
**********************************************************/
void TJC_Init(bsp_Uart_t *pUart)
{
if (pUart == NULL) {
return;
}
p_TJC_Uart = pUart;
initRingBuffer();
// 清理可能存在的无效数据
TJC_CleanBufferFromInvalidPatterns();
// 将数据解析函数设置为TJC串口屏解析函数
p_TJC_Uart->Rx_DataAnalysis = TJC_ProcessSerialData;
// 初始化设备列表
device_count = 0;
memset(device_list, 0, sizeof(device_list));
// 发送初始化完成消息
TJCPrintf("t0.txt=\"TJC初始化完成\"");
}
/********************************************************
函数名: TJC_SendData
功能: 向串口屏发送数据
输入参数: data: 要发送的数据指针
len: 数据长度
**********************************************************/
void TJC_SendData(uint8_t *data, uint16_t len) {
if (p_TJC_Uart == NULL || data == NULL || len == 0) {
return;
}
// 使用bsp_Uart模块的发送函数
p_TJC_Uart->Send(p_TJC_Uart, data, len);
}
/********************************************************
函数名: TJCPrintf
功能: 向串口屏发送格式化字符串自动添加0xFF结束符
输入参数: 格式字符串和可变参数
**********************************************************/
void TJCPrintf(const char *str, ...) {
if (p_TJC_Uart == NULL) {
return;
}
char buffer[STR_LENGTH + 4]; // 预留3个0xFF的位置
uint8_t end_bytes[3] = {0xFF, 0xFF, 0xFF};
va_list arg_ptr;
va_start(arg_ptr, str);
int len = vsnprintf(buffer, STR_LENGTH, str, arg_ptr);
va_end(arg_ptr);
if (len > 0) {
// 发送格式化字符串
p_TJC_Uart->Send(p_TJC_Uart, (uint8_t *)buffer, len);
// 发送结束符
p_TJC_Uart->Send(p_TJC_Uart, end_bytes, 3);
}
}
/********************************************************
函数名: TJC_SendResponse
功能: 发送响应给串口屏
输入参数: response: 响应字符串
**********************************************************/
void TJC_SendResponse(const char *response) {
TJCPrintf("%s", response);
}
/********************************************************
函数名: TJC_CheckEndBytes
功能: 检查指令结束符
输入参数: data: 数据指针
len: 数据长度
end_pos: 结束位置指针
返回值: 1: 找到结束符0: 未找到
**********************************************************/
uint8_t TJC_CheckEndBytes(uint8_t *data, uint16_t len, uint16_t *end_pos) {
if (len < 3) return 0;
// 从后向前查找更高效
for (uint16_t i = 0; i <= len - 3; i++) {
if (data[i] == 0xFF && data[i+1] == 0xFF && data[i+2] == 0xFF) {
*end_pos = i;
return 1;
}
}
return 0;
}
/********************************************************
函数名: CalculateCRC16
功能: 计算MODBUS CRC16校验码
输入参数: data: 数据指针
length: 数据长度
返回值: CRC16校验码
**********************************************************/
uint16_t CalculateCRC16(uint8_t *data, uint16_t length) {
uint16_t crc = 0xFFFF;
uint16_t i, j;
for (i = 0; i < length; i++) {
crc ^= data[i];
for (j = 0; j < 8; j++) {
if (crc & 0x0001) {
crc >>= 1;
crc ^= 0xA001;
} else {
crc >>= 1;
}
}
}
return crc;
}
/********************************************************
函数名: TJC_AddDeviceToFlash
功能: 解析并存储设备信息到内存数组后续会存入Flash
输入参数: data: 指令数据
len: 数据长度
返回值: 1: 成功0: 失败
**********************************************************/
uint8_t TJC_AddDeviceToFlash(uint8_t *data, uint16_t len) {
if (len < 6) {
//TJCPrintf("t0.txt=\"指令太短\"");
return 0;
}
DeviceInfo new_device;
uint8_t temp_buffer[50];
uint8_t temp_index = 0;
// 跳过指令头 AA 55
uint16_t i = 2;
// 解析数据1: 端口号如COM1
temp_index = 0;
while (i < len && data[i] != DATA_SEPARATOR) {
if (temp_index < sizeof(temp_buffer) - 1) {
temp_buffer[temp_index++] = data[i++];
} else {
i++;
}
}
if (i >= len) {
//TJCPrintf("t0.txt=\"解析端口号失败\"");
return 0;
}
temp_buffer[temp_index] = '\0';
// 解析端口号支持COM1或直接数字
if (strncmp((char*)temp_buffer, "COM", 3) == 0) {
new_device.port = atoi((char*)temp_buffer + 3);
} else {
new_device.port = atoi((char*)temp_buffer);
}
// 验证端口号范围只有COM1-COM4
if (new_device.port < 1 || new_device.port > 4) {
TJCPrintf("t0.txt=\"端口号无效:%d(只支持COM1-COM4)\"", new_device.port);
return 0;
}
i++; // 跳过分隔符AA
// 解析数据2: 区域名
temp_index = 0;
while (i < len && data[i] != DATA_SEPARATOR) {
if (temp_index < sizeof(temp_buffer) - 1) {
temp_buffer[temp_index++] = data[i++];
} else {
i++;
}
}
if (i >= len) {
//TJCPrintf("t0.txt=\"解析区域名失败\"");
return 0;
}
temp_buffer[temp_index] = '\0';
strncpy(new_device.region, (char*)temp_buffer, sizeof(new_device.region) - 1);
new_device.region[sizeof(new_device.region) - 1] = '\0';
i++; // 跳过分隔符AA
// 解析数据3: 设备ID
temp_index = 0;
while (i < len && data[i] != DATA_SEPARATOR) {
if (temp_index < sizeof(temp_buffer) - 1) {
temp_buffer[temp_index++] = data[i++];
} else {
i++;
}
}
if (i >= len) {
//TJCPrintf("t0.txt=\"解析设备ID失败\"");
return 0;
}
temp_buffer[temp_index] = '\0';
new_device.device_id = atoi((char*)temp_buffer);
// 验证设备ID范围
if (new_device.device_id < 1 || new_device.device_id > 254) {
//TJCPrintf("t0.txt=\"设备ID无效:%d\"", new_device.device_id);
return 0;
}
i++; // 跳过分隔符AA
// 解析数据4: 设备名
temp_index = 0;
while (i < len && data[i] != DATA_SEPARATOR) {
if (temp_index < sizeof(temp_buffer) - 1) {
temp_buffer[temp_index++] = data[i++];
} else {
i++;
}
}
if (temp_index == 0) {
//TJCPrintf("t0.txt=\"解析设备名失败\"");
return 0;
}
temp_buffer[temp_index] = '\0';
strncpy(new_device.device_name, (char*)temp_buffer, sizeof(new_device.device_name) - 1);
new_device.device_name[sizeof(new_device.device_name) - 1] = '\0';
// 初始化设备状态为正常
new_device.leak_status = LEAK_NORMAL;
new_device.break_status = BREAK_NORMAL;
new_device.comm_status = COMM_STATUS_NORMAL;
// 显示解析结果
// TJCPrintf("t0.txt=\"解析成功\"");
// TJCPrintf("t1.txt=\"端口:%d 区域:%s\"", new_device.port, new_device.region);
// TJCPrintf("t2.txt=\"ID:%d 名称:%s\"", new_device.device_id, new_device.device_name);
// 检查是否已存在相同ID的设备
for (uint8_t j = 0; j < device_count; j++) {
if (device_list[j].device_id == new_device.device_id) {
// 更新现有设备
device_list[j] = new_device;
//TJCPrintf("t3.txt=\"设备%d已更新\"", new_device.device_id);
return 1;
}
}
// 添加新设备到内存数组
if (device_count < 32) {
device_list[device_count] = new_device;
device_count++;
//TJCPrintf("t3.txt=\"设备%d添加成功\"", new_device.device_id);
//TJCPrintf("t4.txt=\"总设备数:%d\"", device_count);
return 1;
} else {
//TJCPrintf("t3.txt=\"设备列表已满\"");
return 0;
}
}
/********************************************************
函数名: TJC_SendDeviceList
功能: 发送设备列表到添加设备界面
说明: 将设备信息显示在t1_1到t8_4文本框中
每个设备占用4个文本框格式如下
- tX_1: 区域名
- tX_2: 端口号
- tX_3: 设备ID
- tX_4: 设备名称
其中X从1到8分别对应第1到第8个设备
最多显示8个设备
**********************************************************/
void TJC_SendDeviceList(void) {
// 清空所有相关文本框t1_1到t8_4
for (int row = 1; row <= 8; row++) {
for (int col = 1; col <= 4; col++) {
TJCPrintf("t%d_%d.txt=\"\"", row, col);
// 每清空几个文本框稍作延迟
if ((row * col) % 4 == 0) {
HAL_Delay(5);
}
}
}
HAL_Delay(100);
if (device_count == 0) {
// 如果没有设备,显示提示信息
TJCPrintf("t1_1.txt=\"无设备数据\"");
TJCPrintf("t0.txt=\"设备列表为空\"");
HAL_Delay(300);
TJCPrintf("t0.txt=\"\"");
return;
}
// 计算要显示的设备数量最多8个
uint8_t display_count = (device_count > 8) ? 8 : device_count;
// 显示设备信息
for (uint8_t i = 0; i < display_count; i++) {
DeviceInfo *device = &device_list[i];
// 计算行号从1开始
int row = i + 1;
// tX_1: 区域名
TJCPrintf("t%d_1.txt=\"%s\"", row, device->region);
HAL_Delay(10);
// tX_2: 端口号显示为COMx格式
TJCPrintf("t%d_2.txt=\"COM%d\"", row, device->port);
HAL_Delay(10);
// tX_3: 设备ID
TJCPrintf("t%d_3.txt=\"%d\"", row, device->device_id);
HAL_Delay(10);
// tX_4: 设备名称
TJCPrintf("t%d_4.txt=\"%s\"", row, device->device_name);
HAL_Delay(10);
// 每显示完一个设备稍作等待
HAL_Delay(20);
}
}
/********************************************************
函数名: TJC_SendRegionStats
功能: 发送区域统计信息到串口屏
说明: 统计每个区域的设备数量和各种报警数量
t1_1显示区域名t1_2显示该区域设备数量
t1_3显示漏液数量t1_4显示断带数量t1_5显示通讯异常数量
最多显示4个区域t1_1-t4_5
**********************************************************/
void TJC_SendRegionStats(void) {
HAL_Delay(100);
if (device_count == 0) {
TJCPrintf("t1_1.txt=\"无设备数据\"");
return;
}
// 统计每个区域的信息
RegionStats region_stats[10];
uint8_t region_count = 0;
// 初始化区域统计数组
for (uint8_t i = 0; i < device_count; i++) {
DeviceInfo *device = &device_list[i];
uint8_t found = 0;
// 查找是否已存在该区域的统计信息
for (uint8_t j = 0; j < region_count; j++) {
if (strcmp(region_stats[j].region_name, device->region) == 0) {
found = 1;
// 更新该区域的统计信息
region_stats[j].total_devices++;
if (device->leak_status == LEAK_ABNORMAL) {
region_stats[j].leak_devices++;
}
if (device->break_status == BREAK_ABNORMAL) {
region_stats[j].break_devices++;
}
if (device->comm_status == COMM_STATUS_ABNORMAL) {
region_stats[j].comm_devices++;
}
break;
}
}
// 如果区域不存在,添加新的区域统计
if (!found && region_count < 10) {
strncpy(region_stats[region_count].region_name, device->region, 19);
region_stats[region_count].region_name[19] = '\0';
region_stats[region_count].total_devices = 1;
// 初始化报警数量
region_stats[region_count].leak_devices = (device->leak_status == LEAK_ABNORMAL) ? 1 : 0;
region_stats[region_count].break_devices = (device->break_status == BREAK_ABNORMAL) ? 1 : 0;
region_stats[region_count].comm_devices = (device->comm_status == COMM_STATUS_ABNORMAL) ? 1 : 0;
region_count++;
}
}
// 计算要显示的区域数量最多4个
uint8_t display_count = (region_count > 4) ? 4 : region_count;
// 显示区域统计信息
for (uint8_t i = 0; i < display_count; i++) {
RegionStats *stats = &region_stats[i];
// 计算行号从1开始
int row = i + 1;
// 区域名
TJCPrintf("t%d_1.txt=\"%s\"", row, stats->region_name);
HAL_Delay(10);
// 总设备数量
TJCPrintf("t%d_2.txt=\"%d\"", row, stats->total_devices);
HAL_Delay(10);
// 漏液设备数量
TJCPrintf("t%d_3.txt=\"%d\"", row, stats->leak_devices);
HAL_Delay(10);
// 断带设备数量
TJCPrintf("t%d_4.txt=\"%d\"", row, stats->break_devices);
HAL_Delay(10);
// 通信异常设备数量
TJCPrintf("t%d_5.txt=\"%d\"", row, stats->comm_devices);
HAL_Delay(10);
// 每显示完一行稍作等待
HAL_Delay(30);
}
}
/********************************************************
函数名: TJC_SendRegionDeviceDetails
功能: 发送指定区域的设备详情到串口屏
输入参数: region_index: 区域索引1-4对应不同的区域
说明: 显示格式:
每个设备显示15个文本框
- tX_1: 设备ID
- tX_2: 设备名称
- tX_3: 通信状态(正常/异常)
- tX_4-tX_6: 第一个通道状态(漏液/断带/漏液位置)
- tX_7-tX_9: 第二个通道状态(漏液/断带/漏液位置)
- tX_10-tX_12: 第三个通道状态(漏液/断带/漏液位置)
- tX_13-tX_15: 第四个通道状态(漏液/断带/漏液位置)
一个区域最多显示4个设备分别显示在
t1_1-t1_15: 第一个设备
t2_1-t2_15: 第二个设备
t3_1-t3_15: 第三个设备
t4_1-t4_15: 第四个设备
**********************************************************/
void TJC_SendRegionDeviceDetails(uint8_t region_index) {
// 清空所有相关文本框t1_1到t4_15
for (int row = 1; row <= 4; row++) {
for (int col = 1; col <= 15; col++) {
TJCPrintf("t%d_%d.txt=\"\"", row, col);
if ((row * col) % 4 == 0) {
HAL_Delay(5);
}
}
}
HAL_Delay(100);
// 检查是否有设备
if (device_count == 0) {
TJCPrintf("t1_1.txt=\"无设备数据\"");
return;
}
// 获取所有不同的区域名称
char region_names[10][20];
uint8_t region_count = 0;
for (uint8_t i = 0; i < device_count && region_count < 10; i++) {
DeviceInfo *device = &device_list[i];
uint8_t found = 0;
for (uint8_t j = 0; j < region_count; j++) {
if (strcmp(region_names[j], device->region) == 0) {
found = 1;
break;
}
}
if (!found) {
strncpy(region_names[region_count], device->region, 19);
region_names[region_count][19] = '\0';
region_count++;
}
}
// 检查区域索引是否有效
if (region_index < 1 || region_index > region_count) {
TJCPrintf("t1_1.txt=\"区域无效\"");
return;
}
// 获取指定区域的设备最多4个
uint8_t region_devices[4];
uint8_t region_device_count = 0;
for (uint8_t i = 0; i < device_count && region_device_count < 4; i++) {
DeviceInfo *device = &device_list[i];
if (strcmp(device->region, region_names[region_index-1]) == 0) {
region_devices[region_device_count] = i;
region_device_count++;
}
}
// 显示该区域的设备详情
for (uint8_t i = 0; i < region_device_count; i++) {
DeviceInfo *device = &device_list[region_devices[i]];
// 计算行号从1开始
int row = i + 1;
// 设备基本信息
TJCPrintf("t%d_1.txt=\"%d\"", row, device->device_id);
HAL_Delay(10);
TJCPrintf("t%d_2.txt=\"%s\"", row, device->device_name);
HAL_Delay(10);
// 通信状态
const char *comm_status_str;
switch (device->comm_status) {
case COMM_STATUS_NORMAL:
comm_status_str = "正常";
break;
case COMM_STATUS_ABNORMAL:
comm_status_str = "异常";
break;
default:
comm_status_str = "未知";
break;
}
TJCPrintf("t%d_3.txt=\"%s\"", row, comm_status_str);
HAL_Delay(10);
// 4个通道的状态
for (int channel = 0; channel < 4; channel++) {
int base_col = 4 + channel * 3;
// 漏液状态
const char *leak_status_str;
switch (device->channels[channel].leak_status) {
case LEAK_NORMAL:
leak_status_str = "正常";
break;
case LEAK_ABNORMAL:
leak_status_str = "漏液";
break;
default:
leak_status_str = "-";
break;
}
TJCPrintf("t%d_%d.txt=\"%s\"", row, base_col, leak_status_str);
HAL_Delay(5);
// 断带状态
const char *break_status_str;
switch (device->channels[channel].break_status) {
case BREAK_NORMAL:
break_status_str = "正常";
break;
case BREAK_ABNORMAL:
break_status_str = "断带";
break;
default:
break_status_str = "-";
break;
}
TJCPrintf("t%d_%d.txt=\"%s\"", row, base_col + 1, break_status_str);
HAL_Delay(5);
// 漏液位置
char leak_meter_str[10];
if (device->channels[channel].leak_status == LEAK_ABNORMAL) {
sprintf(leak_meter_str, "%d米", device->channels[channel].leak_meter);
} else {
sprintf(leak_meter_str, "0米");
}
TJCPrintf("t%d_%d.txt=\"%s\"", row, base_col + 2, leak_meter_str);
HAL_Delay(5);
}
// 每显示完一个设备稍作等待
HAL_Delay(30);
}
}
/********************************************************
函数名: TJC_ProcessDeleteCommand
功能: 处理删除设备指令
输入参数: cmd: 指令数据
len: 指令长度
说明: 指令格式AA 55 04 [8个选中标志字节] [CRC低字节] [CRC高字节]
每个选中标志字节00表示未选中01表示选中删除
最多可以一次删除8个设备
**********************************************************/
void TJC_ProcessDeleteCommand(uint8_t *cmd, uint16_t len) {
if (len != 13) {
// 添加调试信息
char debug_msg[50];
sprintf(debug_msg, "t0.txt=\"删除指令长度错误:%d\"", len);
TJCPrintf(debug_msg);
HAL_Delay(300);
TJCPrintf("t0.txt=\"\"");
return;
}
// 检查功能码是否正确
if (cmd[2] != CMD_DELETE_DEVICE) {
TJCPrintf("t0.txt=\"删除功能码错误\"");
HAL_Delay(300);
TJCPrintf("t0.txt=\"\"");
return;
}
// 提取8个选中标志字节从第3字节开始
uint8_t delete_flags[8];
for (int i = 0; i < 8; i++) {
delete_flags[i] = cmd[3 + i];
}
// 执行删除操作
TJC_DeleteDevices(delete_flags, 8);
}
/********************************************************
函数名: findDeviceCmdLength
功能: 查找添加设备指令的长度
返回值: 指令长度0表示长度不足
说明: 通过查找AA分隔符确定指令长度
**********************************************************/
uint16_t findDeviceCmdLength(void) {
uint16_t buff_len = getRingBuffLength();
if (buff_len < 10) { // 最小长度
return 0;
}
uint8_t aa_count = 0;
uint16_t last_aa_pos = 0;
// 从第三个字节开始查找AA分隔符
for (uint16_t i = 2; i < buff_len; i++) {
uint8_t byte = read1BFromRingBuff(i);
if (byte == DATA_SEPARATOR) {
aa_count++;
last_aa_pos = i;
// 添加设备指令需要4个AA分隔符
if (aa_count >= 4) {
// 检查是否有足够的空间存放CRC2字节
if (last_aa_pos + 3 <= buff_len) {
return last_aa_pos + 3; // 包括AA和CRC
}
}
}
}
return 0; // 没有找到完整的指令
}
/********************************************************
函数名: TJC_DeleteDevices
功能: 根据选中标志删除设备
输入参数: delete_flags: 选中标志数组
flag_count: 标志数量
说明: 删除device_list中的设备并更新device_count
支持一次删除多个设备
**********************************************************/
void TJC_DeleteDevices(uint8_t *delete_flags, uint8_t flag_count) {
if (device_count == 0) {
//TJCPrintf("t0.txt=\"无设备可删除\"");
return;
}
// 统计要删除的设备数量
uint8_t delete_count = 0;
uint8_t to_delete[32] = {0}; // 标记要删除的设备索引
// 遍历标志数组,标记要删除的设备
// 注意flag_count通常为8对应当前显示的8个设备
for (uint8_t i = 0; i < flag_count && i < device_count; i++) {
if (delete_flags[i] == 0x01) {
to_delete[i] = 1;
delete_count++;
}
}
if (delete_count == 0) {
//TJCPrintf("t0.txt=\"未选中设备\"");
return;
}
// 从后往前删除,避免索引混乱
uint8_t new_device_count = 0;
DeviceInfo temp_list[32];
// 复制未标记删除的设备到临时数组
for (uint8_t i = 0; i < device_count; i++) {
// 检查当前设备是否在删除列表中
uint8_t should_delete = 0;
// 只检查前flag_count个设备对应界面上显示的设备
if (i < flag_count && to_delete[i] == 1) {
should_delete = 1;
}
if (!should_delete) {
// 保留设备
temp_list[new_device_count] = device_list[i];
new_device_count++;
}
}
// 更新设备列表和数量
device_count = new_device_count;
for (uint8_t i = 0; i < device_count; i++) {
device_list[i] = temp_list[i];
}
// 清空剩余位置
for (uint8_t i = device_count; i < 32; i++) {
memset(&device_list[i], 0, sizeof(DeviceInfo));
}
// // 显示删除结果
// char msg[50];
// sprintf(msg, "t0.txt=\"删除了%d个设备\"", delete_count);
// TJCPrintf(msg);
// HAL_Delay(300);
}
/********************************************************
函数名: TJC_ProcessCustomCommand
功能: 处理自定义指令
输入参数: cmd: 指令数据
len: 指令长度
**********************************************************/
void TJC_ProcessCustomCommand(uint8_t *cmd, uint16_t len) {
if (len < 6) {
return; // 指令长度不足
}
// 验证指令头
if (cmd[0] != CUSTOM_CMD_HEADER_0 || cmd[1] != CUSTOM_CMD_HEADER_1) {
return; // 指令头错误
}
// 提取CRC最后两个字节
uint16_t received_crc = (cmd[len-1] << 8) | cmd[len-2];
// 计算CRC不包括CRC本身
uint16_t calculated_crc = CalculateCRC16(cmd, len-2);
// 校验CRC
if (received_crc != calculated_crc) {
// CRC错误忽略指令
return;
}
// 判断指令类型
uint8_t cmd_type = cmd[2]; // 第三个字节为功能码
// 如果是添加设备指令(第三个字节是'C' = 0x43
if (cmd_type == ADD_DEVICE_CMD_BYTE) {
// 这是设备添加指令
if (TJC_AddDeviceToFlash(cmd, len)) {
TJCPrintf("b2.txt=\"add suc\"");
}
return;
}
// 如果是删除设备指令功能码为0x04
if (cmd_type == CMD_DELETE_DEVICE) {
// 处理删除设备指令
TJC_ProcessDeleteCommand(cmd, len);
return;
}
// 如果是标准指令6字节长度
if (len == 6) {
uint8_t sub_cmd = cmd[3]; // 第四个字节为子命令
// 根据功能码和子命令处理
switch (cmd_type) {
case CMD_DISPLAY_DATA: // 0x02 显示数据
switch (sub_cmd) {
case SUB_CMD_SHOW_DEVICES: // 0x01 显示已添加的设备
TJC_SendDeviceList();
break;
case SUB_CMD_REGION_STATS: // 0x02 主界面区域显示
TJC_SendRegionStats();
break;
case SUB_CMD_REGION1_DEVICES: // 0x03 第一个区域设备
TJC_SendRegionDeviceDetails(1);
break;
case SUB_CMD_REGION2_DEVICES: // 0x04 第二个区域设备
TJC_SendRegionDeviceDetails(2);
break;
case SUB_CMD_REGION3_DEVICES: // 0x05 第三个区域设备
TJC_SendRegionDeviceDetails(3);
break;
case SUB_CMD_REGION4_DEVICES: // 0x06 第四个区域设备
TJC_SendRegionDeviceDetails(4);
break;
default:
// 未知子命令,忽略
break;
}
break;
case CMD_ALARM: // 0x03 报警
switch (sub_cmd) {
case SUB_CMD_HISTORY_ALARM: // 0x01 历史报警
TJC_SendInitCommands();
break;
case SUB_CMD_REALTIME_ALARM: // 0x02 实时报警
TJC_SendRealtimeAlarms();
break;
default:
// 未知子命令,忽略
break;
}
break;
default:
// 未知功能码,忽略指令
break;
}
} else {
// 非6字节的标准指令忽略
}
}
/********************************************************
函数名: TJC_ProcessCommand
功能: 处理串口屏指令
输入参数: cmd: 指令数据
len: 指令长度
**********************************************************/
void TJC_ProcessCommand(uint8_t *cmd, uint16_t len) {
// 如果指令为空,直接返回
if (len == 0) return;
// 首先检查是否为自定义指令以AA 55开头
if (len >= 3 && cmd[0] == CUSTOM_CMD_HEADER_0 && cmd[1] == CUSTOM_CMD_HEADER_1) {
TJC_ProcessCustomCommand(cmd, len);
return;
}
}
/********************************************************
函数名: TJC_ProcessSerialData
功能: 处理串口接收到的数据
输入参数: data: 接收到的数据
len: 数据长度
p_arg: 参数指针
**********************************************************/
void TJC_ProcessSerialData(u8 *data, u16 len, void *p_arg) {
// 将接收到的数据写入环形缓冲区
for (uint16_t i = 0; i < len; i++) {
writeRingBuff(data[i]);
}
// 循环处理缓冲区中的数据
while (1) {
uint16_t buff_len = getRingBuffLength();
// 如果数据不足3字节AA 55 功能码),退出
if (buff_len < 3) {
break;
}
// 检查前两个字节是否为 AA 55
uint8_t first_byte = read1BFromRingBuff(0);
uint8_t second_byte = read1BFromRingBuff(1);
// 情况1不是 AA 55删除第一个字节继续
if (first_byte != CUSTOM_CMD_HEADER_0 || second_byte != CUSTOM_CMD_HEADER_1) {
deleteRingBuff(1); // 删除第一个字节
continue; // 继续处理下一个字节
}
// 获取功能码(第三个字节)
uint8_t cmd_type = read1BFromRingBuff(2);
// 检查是否为已知功能码
uint8_t valid_cmd = 0;
if (cmd_type == ADD_DEVICE_CMD_BYTE || // 添加设备
cmd_type == CMD_DISPLAY_DATA || // 显示数据
cmd_type == CMD_ALARM || // 报警
cmd_type == CMD_DELETE_DEVICE) { // 删除设备
valid_cmd = 1;
}
// 情况2未知功能码删除 AA 55 两个字节
if (!valid_cmd) {
deleteRingBuff(2); // 删除 AA 55
continue; // 继续处理
}
// 确定指令长度
uint16_t cmd_length = 0;
if (cmd_type == ADD_DEVICE_CMD_BYTE) {
// 添加设备指令:可变长度,需要查找结束位置
cmd_length = findDeviceCmdLength();
if (cmd_length == 0) {
// 长度不足,等待更多数据
break;
}
} else if (cmd_type == CMD_DISPLAY_DATA || cmd_type == CMD_ALARM) {
cmd_length = 6; // 固定6字节
} else if (cmd_type == CMD_DELETE_DEVICE) {
cmd_length = 13; // 固定13字节
}
// 检查长度是否足够
if (buff_len < cmd_length) {
// 数据不足,等待更多数据
break;
}
// 读取完整指令
uint8_t command_buffer[MAX_COMMAND_LEN];
for (uint16_t i = 0; i < cmd_length; i++) {
command_buffer[i] = read1BFromRingBuff(i);
}
// 情况3CRC校验失败删除整个指令
uint16_t received_crc = (command_buffer[cmd_length-1] << 8) |
command_buffer[cmd_length-2];
uint16_t calculated_crc = CalculateCRC16(command_buffer, cmd_length-2);
if (received_crc != calculated_crc) {
deleteRingBuff(cmd_length); // CRC错误删除整个指令
continue; // 继续处理
}
// 所有检查通过,处理有效指令
TJC_ProcessCommand(command_buffer, cmd_length);
// 删除已处理的指令
deleteRingBuff(cmd_length);
}
}
/********************************************************
函数名: initRingBuffer
功能: 初始化环形缓冲区
**********************************************************/
void initRingBuffer(void) {
ringBuff.Head = 0;
ringBuff.Tail = 0;
ringBuff.Length = 0;
memset(ringBuff.Ring_data, 0, RINGBUFF_LEN);
cmd_index = 0;
}
/********************************************************
函数名: writeRingBuff
功能: 往环形缓冲区写入数据
输入参数: data: 要写入的数据
**********************************************************/
void writeRingBuff(uint8_t data) {
if (ringBuff.Length >= RINGBUFF_LEN) {
// 缓冲区已满,丢弃最旧的数据
ringBuff.Head = (ringBuff.Head + 1) % RINGBUFF_LEN;
ringBuff.Length--;
}
ringBuff.Ring_data[ringBuff.Tail] = data;
ringBuff.Tail = (ringBuff.Tail + 1) % RINGBUFF_LEN;
ringBuff.Length++;
}
/********************************************************
函数名: deleteRingBuff
功能: 删除环形缓冲区中指定长度的数据
输入参数: size: 要删除的数据长度
**********************************************************/
void deleteRingBuff(uint16_t size) {
if (size >= ringBuff.Length) {
initRingBuffer();
return;
}
for (int i = 0; i < size; i++) {
if (ringBuff.Length == 0) {
break;
}
ringBuff.Head = (ringBuff.Head + 1) % RINGBUFF_LEN;
ringBuff.Length--;
}
}
/********************************************************
函数名: read1BFromRingBuff
功能: 从环形缓冲区读取指定位置的数据
输入参数: position: 读取位置相对于Head的偏移
返回值: 读取到的数据
**********************************************************/
uint8_t read1BFromRingBuff(uint16_t position) {
if (position >= ringBuff.Length) {
return 0;
}
uint16_t realPosition = (ringBuff.Head + position) % RINGBUFF_LEN;
return ringBuff.Ring_data[realPosition];
}
/********************************************************
函数名: getRingBuffLength
功能: 获取环形缓冲区中的数据长度
返回值: 缓冲区中的数据长度
**********************************************************/
uint16_t getRingBuffLength(void) {
return ringBuff.Length;
}
/********************************************************
函数名: isRingBuffOverflow
功能: 检查环形缓冲区是否已满
返回值: 1: 缓冲区已满0: 缓冲区未满
**********************************************************/
uint8_t isRingBuffOverflow(void) {
return (ringBuff.Length >= RINGBUFF_LEN);
}
/*测试发送历史报警数据*/
void TJC_SendInitCommands(void)
{
HAL_Delay(100);
for (int row = 1; row <= 15; row++) {
char region[10];
char fault_type[10];
char start_time[20];
char end_time[20];
char device_id[10];
// 根据行号生成不同的数据
if (row <= 5) {
strcpy(region, "LH");
} else if (row <= 10) {
strcpy(region, "SH");
} else {
strcpy(region, "BJ");
}
// 故障类型交替
if (row % 2 == 0) {
strcpy(fault_type, "漏液");
} else {
strcpy(fault_type, "断带");
}
// 设备ID两位数不足补0
sprintf(device_id, "%02d", row);
// 计算日期从2026-01-19开始
int day = 19 + (row - 1) % 30; // 保持在1-31天内
int month = 1 + (row - 1) / 30; // 月份递增
// 开始时间
int start_hour = 8 + (row - 1) / 6; // 每6行小时加1
int start_minute = (row * 3) % 60;
if (start_hour >= 24) start_hour = 23; // 防止小时溢出
sprintf(start_time, "2026-%02d-%02d %02d:%02d",
month, day, start_hour, start_minute);
// 结束时间(开始时间+2小时
int end_hour = start_hour + 2;
int end_minute = start_minute + 15;
if (end_minute >= 60) {
end_minute -= 60;
end_hour++;
}
if (end_hour >= 24) {
end_hour = 23;
end_minute = 59;
}
sprintf(end_time, "2026-%02d-%02d %02d:%02d",
month, day, end_hour, end_minute);
// 发送第1列区域
TJCPrintf("t%d_1.txt=\"%s\"", row, region);
HAL_Delay(5);
// 发送第2列ID
TJCPrintf("t%d_2.txt=\"%s\"", row, device_id);
HAL_Delay(5);
// 发送第3列设备名称
TJCPrintf("t%d_3.txt=\"HK\"", row);
HAL_Delay(5);
// 发送第4列报警类型
TJCPrintf("t%d_4.txt=\"%s\"", row, fault_type);
HAL_Delay(5);
// 发送第5列开始时间
TJCPrintf("t%d_5.txt=\"%s\"", row, start_time);
HAL_Delay(5);
// 发送第6列结束时间
TJCPrintf("t%d_6.txt=\"%s\"", row, end_time);
HAL_Delay(5);
// 每发送完一行,稍微等待一下
HAL_Delay(20);
}
}
/********************************************************
函数名: TJC_SendRealtimeAlarms
功能: 发送实时报警数据到串口屏
说明: 按照以下格式显示:
t1_1: 区域名(英文)
t1_2: 设备ID (1-254)
t1_3: 设备名称(英文)
t1_4: 通信状态(正常/异常)
t1_5-t1_16: 每3个为一组共4组每组显示
第1个漏液状态正常/漏液)
第2个断带状态正常/断带)
第3个漏液米数如果漏液则显示具体米数否则显示0米
**********************************************************/
void TJC_SendRealtimeAlarms(void) {
HAL_Delay(50);
// 示例设备数据(实际使用时应该从设备列表中获取)
DeviceInfo device_info = {
.port = 1,
.region = "SH",
.device_id = 25,
.device_name = "HK001"
};
CommStatus comm_status = COMM_STATUS_NORMAL;
// 示例4个通道状态
ChannelStatus channel_status[4] = {
{LEAK_NORMAL, BREAK_ABNORMAL, 0}, // CH1: 正常, 断带, 0米
{LEAK_ABNORMAL, BREAK_NORMAL, 3}, // CH2: 漏液, 正常, 3米
{LEAK_NORMAL, BREAK_NORMAL, 0}, // CH3: 正常, 正常, 0米
{LEAK_ABNORMAL, BREAK_NORMAL, 4} // CH4: 漏液, 正常, 4米
};
// t1_1: 区域名
TJCPrintf("t1_1.txt=\"%s\"", device_info.region);
HAL_Delay(10);
// t1_2: 设备ID
TJCPrintf("t1_2.txt=\"%d\"", device_info.device_id);
HAL_Delay(10);
// t1_3: 设备名称
TJCPrintf("t1_3.txt=\"%s\"", device_info.device_name);
HAL_Delay(10);
// t1_4: 通信状态
const char *comm_status_str;
switch (comm_status) {
case COMM_STATUS_NORMAL:
comm_status_str = "正常";
break;
case COMM_STATUS_ABNORMAL:
comm_status_str = "异常";
break;
default:
comm_status_str = "未知";
break;
}
TJCPrintf("t1_4.txt=\"%s\"", comm_status_str);
HAL_Delay(10);
// 2. 发送4个通道的状态 (t1_5 到 t1_16)
// 每个通道占用3个文本框
for (int channel = 0; channel < 4; channel++) {
ChannelStatus *ch = &channel_status[channel];
// 计算文本框起始索引
int base_index = 5 + channel * 3;
// 第一个文本框:漏液状态
const char *leak_status_str;
switch (ch->leak_status) {
case LEAK_NORMAL:
leak_status_str = "正常";
break;
case LEAK_ABNORMAL:
leak_status_str = "漏液";
break;
default:
leak_status_str = "未知";
break;
}
TJCPrintf("t1_%d.txt=\"%s\"", base_index, leak_status_str);
HAL_Delay(5);
// 第二个文本框:断带状态
const char *break_status_str;
switch (ch->break_status) {
case BREAK_NORMAL:
break_status_str = "正常";
break;
case BREAK_ABNORMAL:
break_status_str = "断带";
break;
default:
break_status_str = "未知";
break;
}
TJCPrintf("t1_%d.txt=\"%s\"", base_index + 1, break_status_str);
HAL_Delay(5);
// 第三个文本框:漏液位置
char leak_meter_str[10];
if (ch->leak_status == LEAK_ABNORMAL) {
sprintf(leak_meter_str, "%d米", ch->leak_meter);
} else {
sprintf(leak_meter_str, "0米");
}
TJCPrintf("t1_%d.txt=\"%s\"", base_index + 2, leak_meter_str);
HAL_Delay(5);
}
}
// 兼容旧代码的宏定义
#define usize getRingBuffLength()
#define code_c() initRingBuffer()
#define udelete(x) deleteRingBuff(x)
#define u(x) read1BFromRingBuff(x)