#include "main.h"
#include "RS485.h"
#include <string.h>
#include <stdio.h>
#include "OLED.h"
#include "tjc_usart_hmi.h"
#include "read_param.h"
#define FRAME_LENGTH 7
// 显示控制结构体
typedef struct {
uint8_t line_count;
uint32_t last_update;
uint32_t last_history_update;
volatile char lines[MAX_LINES][LINE_BUF_SIZE];
volatile char latest_line[LINE_BUF_SIZE];
uint8_t update_flag;
uint8_t history_updated;
char last_alarm_text[LINE_BUF_SIZE]; // 上次报警文本
uint32_t last_alarm_time; // 上次报警时间
uint8_t history_changed; // 历史记录内容变化
} DisplayCtrl;
volatile DisplayCtrl disp_ctrl = {0};
volatile AlarmState temp_alarm = {.trigger_delay = 3000}; // 温度报警状态
volatile uint8_t g_alarm_flag = 50;
volatile float outlet_water_temperature =50;
volatile float inlet_water_upper =50;
volatile float inlet_water_lower =50;
volatile float outlet_water_upper =50;
volatile float outlet_water_lower =50;
volatile float flow_difference =50;
volatile float alarm_delay =50;
// 初始化显示系统
void Display_Init(void) {
memset((void*)&disp_ctrl, 0, sizeof(DisplayCtrl));
disp_ctrl.line_count = 0;
disp_ctrl.last_update = HAL_GetTick();
disp_ctrl.last_history_update = HAL_GetTick();
disp_ctrl.update_flag = 0;
disp_ctrl.last_alarm_text[0] = '\0'; // 清空上次报警文本
disp_ctrl.last_alarm_time = 0;
// 初始化所有行为空字符串
for (int i = 0; i < MAX_LINES; i++) {
disp_ctrl.lines[i][0] = '\0';
}
disp_ctrl.latest_line[0] = '\0';
// 清空屏幕
const char* clear_cmd = "lishishuju.slt0.txt=\"\"" CMD_END;
HAL_UART_Transmit(&TJC_UART, (uint8_t*)clear_cmd, strlen(clear_cmd), 100);
// 初始化报警状态
memset(&temp_alarm, 0, sizeof(temp_alarm));
disp_ctrl.update_flag = 1;
}
// 检查参数是否有效
uint8_t IsParamValid(float value) {
// 温度有效范围:-20℃ 到 150℃
if(value < -20.0f || value > 150.0f) {
return 0;
}
return 1;
}
// 检查多个参数是否有效
uint8_t AreParamsValid(void) {
return IsParamValid(outlet_water_temperature) &&
IsParamValid(inlet_water_upper) &&
IsParamValid(inlet_water_lower) &&
IsParamValid(outlet_water_upper) &&
IsParamValid(outlet_water_lower);
}
// 安全字符串操作函数
void safe_strcpy(volatile char *dest, const char *src) {
char tmp[LINE_BUF_SIZE];
strncpy(tmp, src, LINE_BUF_SIZE - 1);
tmp[LINE_BUF_SIZE - 1] = '\0';
for(int i = 0; i < LINE_BUF_SIZE && tmp[i] != '\0'; i++) {
dest[i] = tmp[i];
}
dest[LINE_BUF_SIZE - 1] = '\0';
}
void safe_strcat(volatile char *dest, const char *src) {
char tmp[LINE_BUF_SIZE];
int i = 0;
// 复制源到临时缓冲区
while(i < LINE_BUF_SIZE - 1 && src[i] != '\0') {
tmp[i] = src[i];
i++;
}
tmp[i] = '\0';
// 找到目标字符串结束位置
i = 0;
while(i < LINE_BUF_SIZE && dest[i] != '\0') i++;
// 追加内容
int j = 0;
while(i < LINE_BUF_SIZE - 1 && tmp[j] != '\0') {
dest[i++] = tmp[j++];
}
dest[LINE_BUF_SIZE - 1] = '\0';
}
size_t safe_strlen(volatile const char *s) {
size_t len = 0;
while(len < LINE_BUF_SIZE && s[len] != '\0') len++;
return len;
}
// 安全添加新行
void SafeAddNewLine(const char *newText,float current_value, float limit_value,AlarmState* state) {
char alarm_buf[LINE_BUF_SIZE];
uint32_t now = HAL_GetTick();
// 在系统稳定前不记录报警
if(!sys_state.system_stable) {
return;
}
snprintf(alarm_buf, sizeof(alarm_buf), "%d-%d-%d-%d:%d:%d-%s %0.1f℃ > %0.1f℃",
param_values[7], param_values[8], param_values[9], param_values[10],param_values[11],
param_values[12],newText,current_value,limit_value);
// 检查是否需要添加新行
int add_new_line = 0;
if (!state->active) {
// 新报警激活
add_new_line = 1;
state->active = 1;
state->recorded = 0; // 重置记录标志
} else if (strcmp(alarm_buf, state->display_text) != 0) {
// 报警内容变化
add_new_line = 1;
state->recorded = 1; // 标记已记录
} else if ((now - state->last_display) > 5000) {
// 5秒更新一次显示值
state->last_display = now;
} else {
// 内容相同且在5秒内,只更新显示不添加新行
strncpy(state->display_text, alarm_buf, sizeof(state->display_text));
disp_ctrl.update_flag = 1;
return;
}
// 保存当前报警文本
strncpy(state->display_text, alarm_buf, sizeof(state->display_text));
// 更新最新一行(只显示当前活动报警)
if(add_new_line)
{
// 缓冲区满时删除最旧行
if (disp_ctrl.line_count >= MAX_LINES) {
for (int i = MAX_LINES - 1; i > 0; i--) {
safe_strcpy((char*)disp_ctrl.lines[i], (const char*)disp_ctrl.lines[i-1]);
}
} else {
// 下移所有行
for (int i = disp_ctrl.line_count; i > 0; i--) {
safe_strcpy((char*)disp_ctrl.lines[i], (const char*)disp_ctrl.lines[i-1]);
}
disp_ctrl.line_count++;
}
// 添加新行到顶部
safe_strcpy((char*)disp_ctrl.lines[0], alarm_buf);
// 标记历史记录内容已变化
disp_ctrl.history_changed = 1;
}
// 更新最新一行
safe_strcpy((char*)disp_ctrl.latest_line, alarm_buf);
// strncpy((char*)disp_ctrl.latest_line, alarm_buf, LINE_BUF_SIZE);
//
disp_ctrl.latest_line[LINE_BUF_SIZE-1] = '\0'; // 强制终止
// 标记需要更新显示
disp_ctrl.update_flag = 1;
state->last_trigger = now;
state->last_display = now;
}
// 安全更新显示
void SafeUpdateDisplay(void) {
uint32_t now = HAL_GetTick();
//
// // 检查更新间隔
// if (now - disp_ctrl.last_update < DISP_UPDATE_INTERVAL) {
// return;
// }
//
// // 避开参数读取关键期
// if (com_ctrl.retry_count != 0 || (now - com_ctrl.last_send_time) < 50) {
// return;
// }
// 1. 更新滚动文本显示
if(disp_ctrl.update_flag ) {
static char g0_cmd[CMD_BUF_SIZE];
static char display_text[LINE_BUF_SIZE] ={0};
// 检查是否有激活的报警
if (temp_alarm.active) {
strncpy(display_text, temp_alarm.display_text, sizeof(display_text));
display_text[sizeof(display_text)-1] = '\0'; // 确保以null结尾
} else if (disp_ctrl.latest_line[0] != '\0') {
// 2. 非报警状态的最新信息
strncpy(display_text, (const char*)disp_ctrl.latest_line, sizeof(display_text)-1);
}
// 显示空
else {
// 无活动报警时清空
display_text[0] = '\0';
}
// 清理非法字符(保留中文字符)
for(char *p = display_text; *p; p++) {
if((uint8_t)*p < 32) {
*p = ' '; // 只替换控制字符
}
}
// 确保字符串以null结尾
display_text[sizeof(display_text)-1] = '\0';
snprintf(g0_cmd, sizeof(g0_cmd), "main.g0.txt=\"%s\"" CMD_END, display_text);
if(HAL_UART_Transmit(&TJC_UART, (uint8_t*)g0_cmd, strlen(g0_cmd), 100) == HAL_OK) {
disp_ctrl.update_flag = 0;
}
}
// 2. 更新历史记录
if(disp_ctrl.history_changed) {
// 清空历史记录控件
// const char* clear_cmd = "lishishuju.slt0.txt=\"\"" CMD_END;
// HAL_UART_Transmit(&TJC_UART, (uint8_t*)clear_cmd, strlen(clear_cmd), 100);
// HAL_Delay(50); // 等待清空完成
for(int i = 0;i<disp_ctrl.line_count ; i++) {
// 跳过空行
if(disp_ctrl.lines[i][0] == '\0') continue;
char clean_line[LINE_BUF_SIZE];
strncpy(clean_line, (const char*)disp_ctrl.lines[i], sizeof(clean_line)-1);
clean_line[sizeof(clean_line)-1] = '\0'; // 确保以null结尾
// 清理非法字符
for(char *p = clean_line; *p; p++) {
if((uint8_t)*p < 32) {
*p = ' ';
}
}
char line_cmd[256];
// 第一行使用 ="" 语法,后续行使用 +=""
if(i == disp_ctrl.line_count-1) {
snprintf(line_cmd, sizeof(line_cmd),
"lishishuju.slt0.txt=\"%s\\r\"" CMD_END,
clean_line);
} else {
snprintf(line_cmd, sizeof(line_cmd),
"lishishuju.slt0.txt+=\"%s\\r\"" CMD_END,
clean_line);
}
// 发送命令
HAL_UART_Transmit(&TJC_UART, (uint8_t*)line_cmd, strlen(line_cmd), 100);
HAL_Delay(10); // 行间延时
}
disp_ctrl.history_updated = 1;
disp_ctrl.history_changed = 0; // 清除变化标志
}
}
void alarm_tip(void)
{
if(!IsSystemStable() || !AreParamsValid()) {
return; // 系统不稳定或参数无效,跳过报警检测
}
uint16_t hex = 100;
outlet_water_temperature = param_values[4]/hex;
inlet_water_upper = param_values[0]/hex;
inlet_water_lower = param_values[1]/hex;
outlet_water_upper = param_values[2]/hex;
outlet_water_lower = param_values[3]/hex;
flow_difference = param_values[5]/hex;
alarm_delay = param_values[6]/hex;
static uint32_t last_temp_alarm_time = 0;
uint32_t now = HAL_GetTick();
// 特别处理:避免"30℃>0℃"误报警
if(outlet_water_temperature < 1.0f) {
return; // 跳过本次检测
}
// 温度报警检测
if(temp1 > outlet_water_temperature) {
// if(!temp_alarm.active) {
// 首次检测到报警条件
if(temp_alarm.condition_start == 0) {
temp_alarm.condition_start = now;
}
else if(now - temp_alarm.condition_start > alarm_delay) {
SafeAddNewLine("出水温度超过上限", temp1, outlet_water_temperature, &temp_alarm);
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_5, GPIO_PIN_RESET);
temp_alarm.active = 1;
}
// }
}
else {
// 报警条件不再满足
temp_alarm.condition_start = 0;
// if(temp_alarm.active) {
temp_alarm.active = 0;
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_5, GPIO_PIN_SET);
temp_alarm.display_text[0] = '\0'; // 清空报警文本
// 添加以下代码:清除滚动文本
disp_ctrl.latest_line[0] = '\0'; // 清空最新行
disp_ctrl.update_flag = 1; // 触发显示更新
// }
}
}
以上程序可以在报警时只显示一行,但每次报警就只显示第一行,我想把之前的数据也显示在屏幕上,即当我第一次报警时,显示在最上面,报警结束后,当遇到第二次报警后,该显示在第二行上,第一行为第二次报警的信息,我该如何修改这端程序