current_v/Src/ht16k33_dot_matrix.c
2025-12-31 08:21:43 +08:00

562 lines
18 KiB
C
Raw Permalink 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.

/* USER CODE BEGIN Header */
/**
******************************************************************************
* @file ht16k33_dot_matrix.c
* @author Motor Control SDK Team, Yuwell Software XiangenWang
* @brief Voice Recognition Module Initialization Section,
including peripheral initialization and message node insertion, etc.
* @version 1.0
* @changelog version 1.0 初始版本 2025.11.13
- 新增:新建第一个版本的软件,待完善解析命令后的程序执行部分
******************************************************************************
* @attention
*
* <h2><center>&copy; Copyright (c) 2025 Yuwell Software Danyang.Jiangsu.China.
* All rights reserved.</center></h2>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted, provided that the following conditions are met:
*
* 1. Redistribution of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* 3. Neither the name of Yuwell Software nor the names of other
* contributors to this software may be used to endorse or promote products
* derived from this software without specific written permission.
* 4. This software, including modifications and/or derivative works of this
* software, must execute solely and exclusively on microcontroller or
* microprocessor devices manufactured by or for Yuwell Software.
* 5. Redistribution and use of this software other than as permitted under
* this license is void and will automatically terminate your rights under
* this license.
*
* THIS SOFTWARE IS PROVIDED BY Yuwell Software AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS, IMPLIED OR STATUTORY WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
* PARTICULAR PURPOSE AND NON-INFRINGEMENT OF THIRD PARTY INTELLECTUAL PROPERTY
* RIGHTS ARE DISCLAIMED TO THE FULLEST EXTENT PERMITTED BY LAW. IN NO EVENT
* SHALL Yuwell Software OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
* OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
******************************************************************************
*/
/* USER CODE END Header */
#include "ht16k33_dot_matrix.h"
UIElements ui;
/**
* @brief 初始化屏幕UI配置参数
* @param p_ui - UIElements类型指针指向需初始化的UI配置结构体
* @retval NONE
* @note 1. 函数内部会对空指针进行判断,避免非法访问
* 2. 初始化指示灯默认状态OK灯亮(1),报警灯灭(0),清洁滤网提示灭(0),更换过滤盒提示灭(0)
* 3. 初始化默认显示模式为累时模式(DISPLAY_MODE_TIMER)
* 4. 氧浓度初始值设为210对应显示21.0%
* 5. 该函数已弃用,建议使用任务与队列形式进行内容刷新
* @author jarvis
* @date 2025.11.25
*/
void ht16k33_ui_init(UIElements* p_ui)
{
FL_GPIO_InitTypeDef GPIO_InitStruct;
MsgQueueItem queue_item;
bool result = false;
if (p_ui == NULL)
{
return; // 避免空指针
}
// 指示灯默认状态
p_ui->ok_led_show = 1;
p_ui->alarm_led_show = 0;
p_ui->clean_filter_show = 0;
p_ui->replace_box_show = 0;
// 显示模式默认值
p_ui->disp_info.oxygen_val = 210; // 氧浓度初始21.0%(示例)
GPIO_InitStruct.pin = HT16K33_SDA_GPIO_PIN;
GPIO_InitStruct.mode = FL_GPIO_MODE_OUTPUT;
GPIO_InitStruct.pull = FL_DISABLE;
GPIO_InitStruct.remapPin = FL_DISABLE;
GPIO_InitStruct.analogSwitch = FL_DISABLE;
GPIO_InitStruct.outputType = FL_GPIO_OUTPUT_OPENDRAIN;
FL_GPIO_Init(HT16K33_SDA_GPIO_GROUP, &GPIO_InitStruct); // 初始化SDA引脚
GPIO_InitStruct.pin = HT16K33_SCL_GPIO_PIN;
GPIO_InitStruct.pull = FL_ENABLE;
GPIO_InitStruct.outputType = FL_GPIO_OUTPUT_PUSHPULL;
FL_GPIO_Init(HT16K33_SCL_GPIO_GROUP, &GPIO_InitStruct); // 初始化SCL引脚
ht16k33_chip_init();
ht16k33_send_buffer(ht16k33_disp_buff); // 发送全灭灯的显存(仅在上电后执行)
if(global_queue.length != 0)
{
result = remove_queue_node_by_type(&global_queue, MSG_TYPE_TIME_SEND, &queue_item); // 取出队列中对应类型的数据
if(result == true)
{
ui.disp_info.work_hours = queue_item.data.hour_meter.hour;
ui.disp_info.work_minutes = queue_item.data.hour_meter.minute;
}
}
}
/**
* @brief 切换显示两组数字及索引(用于校准/调试模式的交替显示)
* @param first_value - 第一组要显示的数值
* @param first_index - 第一组数值的首位索引(对应段码表的符号/标识)
* @param second_value - 第二组要显示的数值
* @param second_index - 第二组数值的首位索引(对应段码表的符号/标识)
* @retval NONE
* @note 1. 每组状态显示2秒通过show_switch_count计数控制计数周期为100ms
* 2. 使用静态变量记录显示状态和计数,保证函数调用间状态延续
* 3. 数值按个位、十位、百位拆分到对应的段码索引位
* @author wxe
* @date 2025.11.25
*/
static void switch_show_2_num(uint16_t first_value, uint16_t first_index, uint16_t second_value, uint16_t second_index)
{
static uint16_t show_switch_count = 20;
static uint16_t show_mode_record = 1;
if(show_switch_count >= 20) // 每种状态显示2s
{
show_switch_count = 0;
if(show_mode_record)
{
show_mode_record = 0;
ui.disp_info.seg_index.fourth_8_index = first_value % 10;
ui.disp_info.seg_index.third_8_index = (first_value / 10) % 10;
ui.disp_info.seg_index.second_8_index = (first_value / 100) % 10;
ui.disp_info.seg_index.first_8_index = first_index;
}else{
show_mode_record = 1;
ui.disp_info.seg_index.fourth_8_index = second_value % 10;
ui.disp_info.seg_index.third_8_index = (second_value / 10) % 10;
ui.disp_info.seg_index.second_8_index = (second_value / 100) % 10;
ui.disp_info.seg_index.first_8_index = second_index;
}
}else{
show_switch_count++;
}
}
/**
* @brief 初始化状态下的UI刷新STM_INIT状态
* @param ui - UIElements类型指针指向UI配置结构体
* @retval NONE
* @note 1. 初始状态显示累时数据,隐藏氧浓度、滤网/滤盒提示、报警灯
* 2. 点亮OK灯拆分累时小时数到各位段码索引支持万位、千位、百位、十位、个位
* 3. 万位为0时隐藏仅显示有效位数
* @author wxe
* @date 2025.11.25
*/
static void on_stm_init_ui_refresh(UIElements* ui)
{
// 初始状态下显示累时数据
ui->total_time_chn = 1;
ui->oxg_concentration_chn = 0;
ui->replace_box_show = 0;
ui->clean_filter_show = 0;
// ui->alarm_led_show = 0; // 此部分在it文件中进行了实现
// 显示ok灯
ui->ok_led_show = 1;
// 显示当前累时
uint8_t wh_ten_thou, wh_thou, wh_hund, wh_ten, wh_unit; // 万位、千位、百位、十位、个位
// 根据累时时间设定显示的数字
wh_ten_thou = (ui->disp_info.work_hours / 10000) % 10; // 万位12508/10000=1 → 1%10=1
wh_thou = (ui->disp_info.work_hours / 1000) % 10; // 千位12508/1000=12 → 12%10=2
wh_hund = (ui->disp_info.work_hours / 100) % 10; // 百位12508/100=125 → 125%10=5
wh_ten = (ui->disp_info.work_hours / 10) % 10; // 十位12508/10=1250 → 1250%10=0
wh_unit = ui->disp_info.work_hours % 10; // 个位12508%10=8
if(wh_ten_thou != 0)
{
ui->disp_info.seg_index.num1_is_show = 1;
}else{
ui->disp_info.seg_index.num1_is_show = 0;
}
ui->disp_info.seg_index.first_8_index = wh_thou;
ui->disp_info.seg_index.second_8_index = wh_hund;
ui->disp_info.seg_index.third_8_index = wh_ten;
ui->disp_info.seg_index.fourth_8_index = wh_unit;
}
/**
* @brief 正常工作状态下的UI刷新STM_NOM状态
* @param ui - UIElements类型指针指向UI配置结构体
* @retval NONE
* @note 1. 隐藏累时显示,显示氧浓度数据
* 2. 从全局队列中读取氧传感器数据浓度、流量并更新到UI结构体
* 3. 拆分氧浓度值到各位段码索引首位设为固定标识11
* @author wxe
* @date 2025.11.25
*/
static void on_stm_nom_ui_refresh(UIElements* ui)
{
ui->total_time_chn = 0;
ui->oxg_concentration_chn = 1;
MsgQueueItem oxg_item;
// 取出队列中对应类型的数据
if(peek_queue_node_by_type(&global_queue, MSG_TYPE_OXYGEN_SENSOR, &oxg_item))
{
ui->disp_info.oxygen_val = oxg_item.data.oxygen.concentration;
ui->disp_info.flow_val = oxg_item.data.oxygen.flow_rate;
}
uint8_t o2_hund, o2_ten, o2_unit; // 百位、十位、个位
// 根据浓度设置显示内容
o2_hund = (ui->disp_info.oxygen_val / 100) % 10; // 百位935/100=9 → 9%10=9
o2_ten = (ui->disp_info.oxygen_val / 10) % 10; // 十位935/10=93 → 93%10=3
o2_unit = ui->disp_info.oxygen_val % 10; // 个位935%10=5
ui->disp_info.seg_index.num1_is_show = 0;
ui->disp_info.seg_index.first_8_index = 11;
ui->disp_info.seg_index.second_8_index = o2_hund;
ui->disp_info.seg_index.third_8_index = o2_ten;
ui->disp_info.seg_index.fourth_8_index = o2_unit;
}
/**
* @brief 故障状态下的UI刷新STM_ERROR_SHOUNTDOWN/STM_ERROR_NONE_STOP状态
* @param ui - UIElements类型指针指向UI配置结构体
* @retval NONE
* @note 1. 关闭OK灯和累时/氧浓度显示根据故障优先级显示对应故障码E3>E4>E5>LL>E2>E1>E7>传感器故障)
* 2. 故障码对应关系E3(低电压)、E4(高电压)、E5(温度高)、LL(流量低)、E2(压力高)、E1(压力低)、E7(220V低)
* 3. 氧传感器故障时显示全“-”,蜂鸣器静音时点亮报警蓝灯
* @author wxe
* @date 2025.11.25
*/
static void on_stm_error_ui_refresh(UIElements* ui)
{
// 故障模式
// 根据故障显示的优先级确定显示内容 (只根据故障优先级进行显示 故障是否发生的判断 放在前面的显示优先级最高)
// 故障显示优先级如下 E3 > E4 > E5 > LL > E2 > E1 > E7
ui->ok_led_show = 0; // 关闭OK灯显示
ui->total_time_chn = 0;
ui->oxg_concentration_chn = 0;
// 显示E3
if(xOxygenEventGroupCheckBit(&global_event, EVENT_E3_ELEC_LOW))
{
ui->disp_info.seg_index.first_8_index = 11; // 显示“ ”
ui->disp_info.seg_index.second_8_index = 11; // 显示“ ”
ui->disp_info.seg_index.third_8_index = 12; // 显示“E”
ui->disp_info.seg_index.fourth_8_index = 3; // 显示“3”
}else if(xOxygenEventGroupCheckBit(&global_event, EVENT_E4_ELEC_HIGH)) // 显示E4
{
ui->disp_info.seg_index.first_8_index = 11; // 显示“ ”
ui->disp_info.seg_index.second_8_index = 11; // 显示“ ”
ui->disp_info.seg_index.third_8_index = 12; // 显示“E”
ui->disp_info.seg_index.fourth_8_index = 4; // 显示“4”
}else if(xOxygenEventGroupCheckBit(&global_event, EVENT_E5_NTC_HIGH)) // 显示E5
{
ui->disp_info.seg_index.first_8_index = 11; // 显示“ ”
ui->disp_info.seg_index.second_8_index = 11; // 显示“ ”
ui->disp_info.seg_index.third_8_index = 12; // 显示“E”
ui->disp_info.seg_index.fourth_8_index = 5; // 显示“5”
}else if(xOxygenEventGroupCheckBit(&global_event, EVENT_LL_FLOW_LOW)) // 显示LL
{
ui->disp_info.seg_index.first_8_index = 11; // 显示“ ”
ui->disp_info.seg_index.second_8_index = 11; // 显示“ ”
ui->disp_info.seg_index.third_8_index = 13; // 显示“L”
ui->disp_info.seg_index.fourth_8_index = 13; // 显示“L”
}else if(xOxygenEventGroupCheckBit(&global_event, EVENT_E2_PRESS_HIGH)) // 显示E2
{
ui->disp_info.seg_index.first_8_index = 11; // 显示“ ”
ui->disp_info.seg_index.second_8_index = 11; // 显示“ ”
ui->disp_info.seg_index.third_8_index = 12; // 显示“E”
ui->disp_info.seg_index.fourth_8_index = 2; // 显示“2”
}else if(xOxygenEventGroupCheckBit(&global_event, EVENT_E1_PRESS_LOW)) // 显示E1
{
ui->disp_info.seg_index.first_8_index = 11; // 显示“ ”
ui->disp_info.seg_index.second_8_index = 11; // 显示“ ”
ui->disp_info.seg_index.third_8_index = 12; // 显示“E”
ui->disp_info.seg_index.fourth_8_index = 1; // 显示“1”
}else if(xOxygenEventGroupCheckBit(&global_event, EVENT_E7_220V_LOW)) // 显示E7
{
ui->disp_info.seg_index.first_8_index = 11; // 显示“ ”
ui->disp_info.seg_index.second_8_index = 11; // 显示“ ”
ui->disp_info.seg_index.third_8_index = 12; // 显示“E”
ui->disp_info.seg_index.fourth_8_index = 7; // 显示“7”
}else if(xOxygenEventGroupCheckBit(&global_event, EVENT_O2_SENSOR_ERROR)) // 氧传感器故障
{
ui->disp_info.seg_index.first_8_index = 10; // 显示“-”
ui->disp_info.seg_index.second_8_index = 10; // 显示“-”
ui->disp_info.seg_index.third_8_index = 10; // 显示“-”
ui->disp_info.seg_index.fourth_8_index = 10; // 显示“-”
}
if(xOxygenEventGroupCheckBit(&global_event, EVENT_BEEP_MUTE))
{
ui->alarm_led_show = 1;// 亮起蓝灯
}else{
ui->alarm_led_show = 0;// 关闭蓝灯
}
}
/**
* @brief 校准/调试状态下的UI刷新STM_CARLIB/STM_DEBUG状态
* @param ui - UIElements类型指针指向UI配置结构体
* @retval NONE
* @note 1. 从全局队列读取传感器、ADC、状态机、边界值数据
* 2. 根据状态机子状态(压力、氧浓度、温压、电压、校准项)调用交替显示函数
* 3. 不同子状态对应显示不同的参数组合(如压力值+压力上限、氧浓度+流量等)
* @author wxe
* @date 2025.11.25
*/
static void on_stm_carlib_ui_refresh(UIElements* ui)
{
MsgQueueItem stm_item;
MsgQueueItem adc_item;
MsgQueueItem o2_item;
MsgQueueItem boundary_item;
peek_queue_node_by_type(&global_queue, MSG_TYPE_OXYGEN_SENSOR, &o2_item);
peek_queue_node_by_type(&global_queue, MSG_TYPE_AD_SAMPLING, &adc_item);
peek_queue_node_by_type(&global_queue, MSG_TYPE_OXG_STM, &stm_item);
peek_queue_node_by_type(&global_queue, MSG_TYPE_AD_BOUNDARY_SEND, &boundary_item);
switch(stm_item.data.state_machine.data)
{
case NETCODE_PRESS:
// 显示当前压力值及压力高低
switch_show_2_num(adc_item.data.adc_data.real_press, 14, boundary_item.data.adc_boundary.press_high_boundary, 15);
break;
case NETCODE_O2:
switch_show_2_num(o2_item.data.oxygen.concentration, 16, o2_item.data.oxygen.flow_rate, 17);
break;
case NETCODE_TEMP_PRESS:
switch_show_2_num(adc_item.data.adc_data.real_ntc, 16, adc_item.data.adc_data.real_press, 14);
break;
case NETCODE_ELEC:
switch_show_2_num(adc_item.data.adc_data.real_elec/10, 16, boundary_item.data.adc_boundary.elec_low_boundary/10, 13);
break;
case NETCODE_E1_CALIB:
switch_show_2_num(adc_item.data.adc_data.real_press, 14,boundary_item.data.adc_boundary.press_low_boundary, 13); // 13-L 14-P 15-H 16-C 17-F 18-U 19-A
break;
case NETCODE_E2_CALIB:
switch_show_2_num(adc_item.data.adc_data.real_press, 14, boundary_item.data.adc_boundary.press_high_boundary, 15);
break;
case NETCODE_E4_CALIB:
switch_show_2_num(adc_item.data.adc_data.real_elec/10, 19, boundary_item.data.adc_boundary.elec_high_boundary/10, 15);
break;
case NETCODE_E7_CALIB:
switch_show_2_num(adc_item.data.adc_data.real_220V, 18, boundary_item.data.adc_boundary.net220v_low_boundary, 13);
break;
default:
break;
}
}
/**
* @brief HT16K33屏幕刷新任务函数队列数据处理+屏幕刷新)
* @param queue - MessageQueue类型指针指向消息队列实际未直接使用依赖全局队列global_queue
* @retval NONE
* @note 1. 任务核心功能从消息队列中提取氧传感器数据更新UI配置后刷新屏幕
* 2. 队列操作从全局队列global_queue中取出MSG_TYPE_OXYGEN_SENSOR类型的消息获取氧浓度和流量数据
* 3. 数据更新:若成功获取队列数据,将氧浓度(oxygen_val)和流量(flow_val)更新到全局UI结构体ui中
* 4. 屏幕刷新无论是否获取到新数据均调用ht16k33_refresh_screen()函数刷新屏幕显示
* 5. 依赖项需确保全局队列global_queue已初始化MSG_TYPE_OXYGEN_SENSOR消息类型已定义
* 全局UI结构体ui及氧传感器数据结构体含concentration/flow_rate字段已声明
* @author wxe
* @date 2025.11.25
*/
void ht16k33_refresh_task(void)
{
MsgQueueItem stm_item;
static uint16_t ok_blink_count = 0;
static uint16_t blue_blink_count = 0;
// 根据状态机设置当前显示模式 根据现实模式确定ui.dispinfo 中的内容
if(peek_queue_node_by_type(&global_queue, MSG_TYPE_OXG_STM, &stm_item))
{
switch(stm_item.data.state_machine.oxg_stm)
{
case STM_INIT:
on_stm_init_ui_refresh(&ui); // 初始4s时进行ui变量中内容进行更新
break;
case STM_NOM:
on_stm_nom_ui_refresh(&ui); // 这正常状态下显示氧浓度
break;
case STM_ERROR_SHOUNTDOWN: // 故障到停机状态时
case STM_ERROR_NONE_STOP: // 发生E7故障 显示E7但是不停机
on_stm_error_ui_refresh(&ui);
break;
case STM_CARLIB: // 若当前为校准状态
case STM_DEBUG:
on_stm_carlib_ui_refresh(&ui);
break;
default:
break;
}
}else{
return;
}
if(xOxygenEventGroupCheckBit(&global_event, EVENT_REPLACE_FILTER_NOTIFY))
{
ui.replace_box_show = 1;
}else{
ui.replace_box_show = 0;
}
if(xOxygenEventGroupCheckBit(&global_event, EVENT_CLEAN_FILTER_NOTIFY))
{
ui.clean_filter_show = 1;
}else{
ui.clean_filter_show = 0;
}
if(xOxygenEventGroupCheckBit(&global_event, EVENT_OK_BLINK)) // 若需要进行OK灯闪烁
{
ok_blink_count++;
if(ok_blink_count % 2 == 0) // 每隔0.2秒闪烁一次
{
ui.ok_led_show = !ui.ok_led_show;
}
if(ok_blink_count == 9)
{
ok_blink_count = 0;
vOxygenEventGroupClearBits(&global_event, EVENT_OK_BLINK);
}
}
if(xOxygenEventGroupCheckBit(&global_event, EVENT_BLUE_BLINK))
{
blue_blink_count++;
if(blue_blink_count % 2 == 0) // 每隔0.2秒闪烁一次
{
ui.alarm_led_show = !ui.alarm_led_show;
}
if(blue_blink_count == 9)
{
blue_blink_count = 0;
vOxygenEventGroupClearBits(&global_event, EVENT_BLUE_BLINK);
}
}
ht16k33_refresh_screen(); // 刷新屏幕
}
/************************ (C) COPYRIGHT YUWELL *****END OF FILE****/