/* 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 * *

© Copyright (c) 2025 Yuwell Software Danyang.Jiangsu.China. * All rights reserved.

* * 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****/