/****************************************************************************** * Copyright (C) 2021, Xiaohua Semiconductor Co., Ltd. All rights reserved. * * This software component is licensed by XHSC under BSD 3-Clause license * (the "License"); You may not use this file except in compliance with the * License. You may obtain a copy of the License at: * opensource.org/licenses/BSD-3-Clause * ******************************************************************************/ /****************************************************************************** * @file main.c * * @brief Source file for GPIO example * * @author MADS Team * ******************************************************************************/ /****************************************************************************** * Include files ******************************************************************************/ #include "board.h" #include "tdc_tof.h" #include "smp_shell.h" #include "param.h" #include "lcd_disp.h" #include "bill.h" #include "app_nb.h" #include "check.h" #include "alarm.h" #include "rtc_run.h" #include "stat.h" #include "ymodem.h" /****************************************************************************** * Local pre-processor symbols/macros ('#define') ******************************************************************************/ /* 按键参数(可直接修改) */ #define SHORT_PRESS_MS 50 // 短按阈值 #define LONG_PRESS_MS 5000 // 长按阈值 #define INVALID_LONG_PRESS_MS 20000 // 无效长按阈值 #define DEBOUNCE_US 5000 // 消抖时间 #define KEY_SLEEP_BLOCK_MS 800 // 按键后延迟允许休眠时间 /* 全局变量(仅保留必要的) */ struct rt_semaphore lcd_isr_sem; // lcd 中断信号量 struct rt_semaphore rtc_isr_sem; // rtc 中断信号量 struct rt_semaphore tdc_isr_sem; // TDC 中断信号量:中断->线程通知 static rt_timer_t key_timer = RT_NULL; // 按键计时定时器 static uint32_t key_press_time = 0; // 按键按下时长(ms) volatile rt_tick_t g_key_last_active_tick = 0; // add by HBR 20260623,按键最后活动时间,用于睡眠门禁 uint8_t KEY_SHORT_PRESS_FLAG = 0; // 短按标志,用于翻屏 uint8_t KEY_LONG_PRESS_FLAG = 0; // 长按标志,用于触发上报 /************************** 线程配置 **************************/ #define TDC_THREAD_STACK 1024 // 核心线程栈1K,满足需求 堆栈大小 #define TDC_THREAD_PRIORITY 1 // 高优先级,确保及时响应 #define TDC_THREAD_TIMESLICE 500 #define RTC_THREAD_STACK 1024 // 核心线程栈1K,满足需求 #define RTC_THREAD_PRIORITY 2 // 高优先级,确保及时响应 #define RTC_THREAD_TIMESLICE 500 #define NB_THREAD_STACK 2048 // 核心线程栈1K,满足需求 #define NB_THREAD_PRIORITY 3 // 高优先级,确保及时响应 #define NB_THREAD_TIMESLICE 500 #define LCD_THREAD_STACK 1024 // 核心线程栈1K,满足需求 #define LCD_THREAD_PRIORITY 4 // 高优先级,确保及时响应 #define LCD_THREAD_TIMESLICE 500 /****************************************************************************** * Global variable definitions (declared in header file with 'extern') ******************************************************************************/ uint8_t G_NB_CAN_DEEP_SLEEP = 1; // NB是否允许进入休眠,1:允许,0:不允许 uint8_t G_KEY_CAN_DEEP_SLEEP = 1; // KEY是否允许进入休眠,1:允许,0:不允许 uint8_t G_TDC_DEEP_SLEEP = 1; /****************************************************************************** * Local type definitions ('typedef') ******************************************************************************/ /****************************************************************************** * Local function prototypes ('static') ******************************************************************************/ /****************************************************************************** * Local variable definitions ('static') * ******************************************************************************/ /****************************************************************************** * Local pre-processor symbols/macros ('#define') ******************************************************************************/ /***************************************************************************** * Function implementation - global ('extern') and local ('static') ******************************************************************************/ /** ****************************************************************************** ** \brief Main function of project ** ** \return uint32_t return value, if needed ** ** This sample ** ******************************************************************************/ // uint32_t SOFT_VER = 0x56313031; // V101 uint32_t BUILD_DATE = ((2025 << 16) | (12 << 8) | 16); uint16_t BUILD_TIME = ((17 << 8) | 23); //////////wxl test low power//////////// ///////////////////////////////////////// void show_stack_by_name(char *name) { rt_thread_t thread = rt_thread_find(name); if (thread == RT_NULL) { LOG("no thread name is:%s\n!", name); return; } rt_uint32_t total = thread->stack_size; rt_uint32_t free = (rt_uint32_t)thread->sp - (rt_uint32_t)thread->stack_addr; rt_uint32_t used = total - free; LOG("[name] %-8s | total:%4d | used:%4d | free:%4d\n", thread->name, total, used, free); } extern void test_dtof_var(void); void PeriodProc(void) // 测试更改提交 2026-5-19-1-2-3 { rtc_run_t *rr; get_rtc_run(&rr); if (rr->flag_1s) { rr->flag_1s = 0; // add by yuewei 20260519 start if (alarm_status(ERR_BLANK_PIPE)) { // 空管,设置RTC中断2秒1次 if ((uint8_t)get_rtc_interrupt_time() != 2) { set_rtc_interrupt_time(2); } } else { // 不是空管,设置RTC中断0.5秒1次 if ((get_rtc_interrupt_time() == 2) || (get_rtc_interrupt_time() == 1)) { set_rtc_interrupt_time(0.5); // 0.5 } } // add by yuewei 20260519 start // LOG("Diff=%.6f, AvgDiff=%.6f, T=%.6f, vol=%.6f\n", SerPlot.f.Data[0], SerPlot.f.Data[3], WaterPrmt[0], FlowVol * 0.000001f * 3600); // rt_thread_mdelay(10); // LOG("main-sec = %d ", rr->second); // rt_thread_mdelay(2); // add by yuewei 20260416 to countdown LcdTimer per 1 second lcd_timer_countdown(); rt_sem_release(&lcd_isr_sem); bw_flow_monitor(); // 反向计量检测 large_flow_monitor(); // 大流量检测 cnti_flow_monitor(); // 持续流量检测 leak_flow_monitor(); // 渗漏流量检测 high_water_temp_monitor(); // 高温检测 low_water_temp_monitor(); // 低温检测 tdc_rw_err_monitor(); // 传感器异常检测 eeprom_rw_err_monitor(); // 存储器异常检测 // LOG("ch-%d:%.6f,%.6f,%.6f,%.6f\n",get_mux_ch(),SerPlot.f.Data[0],SerPlot.f.Data[1],SerPlot.f.Data[2],SerPlot.f.Data[3]); CheckRxTimeout(); CheckingCoundown(); nb_task_run_check(); // nb上报触发判断 update_run_app_info(); app_bill_run(); // 各种阶段计量定时存储 run_clk_task_handler(); // wxl2026-5-8 一秒一次一些零星任务放在这个里面 AlarmHandle(); // wxl2026-4-30 feed_dog(); } if (rr->flag_1m) { rr->flag_1m = 0; adc_get(); // 采集电池电压与压力 low_power_monitor(); // 低电压检测 high_pressure_monitor(); // 高压检测 low_pressure_monitor(); // 低压检测 // 跨00点 if (rr->hour == 0 && rr->minute == 0) { // 清除今日日最高流量,同时将今日最高流量赋值给昨日最高流量 clr_max_flow_rate(); } } } /** * @brief 磁阻按键中断函数,下降沿触发 * @param 无 * @retval 无 */ // void PORTB_IRQHandler(void) //{ // if (GpioGetEvents(GpioPortB, GpioBitMsk0)) // { // // 简单消抖 // us_delay(DEBOUNCE_US); // if (GpioInPins(GpioPortB, GpioBitMsk0) == 0) // { // key_press_time = 0; // rt_timer_start(key_timer); // 启动计时 // G_KEY_CAN_DEEP_SLEEP = 0; // 按键不允许休眠 // } // M0P_GPIO->PB_ICLR &= ~(1 << 0); // } //} uint8_t key_handle_runing = 0; uint8_t key_irq_handler(void) // WXL2026-5-22 { if (GpioGetEvents(GpioPortD, GpioBitMsk4)) { if (key_handle_runing != 0 || key_press_time != 0) // waiting for key task complete wxl2026-5-26 { // M0P_GPIO->PD_ICLR &= ~(1 << 4); M0P_GPIO->PD_ICLR = (1u << 4); return 0; } // us_delay(DEBOUNCE_US); if (GpioInPins(GpioPortD, GpioBitMsk4) != 0) { key_press_time = 0; G_KEY_CAN_DEEP_SLEEP = 0; // 按键不允许休眠 g_key_last_active_tick = rt_tick_get(); //add by HBR 20260623,按键中断触发记录一次时间 rt_timer_stop(key_timer); rt_timer_start(key_timer); } // M0P_GPIO->PD_ICLR &= ~(1 << 4); M0P_GPIO->PD_ICLR = (1u << 4); return 1; } return 0; } /** * @brief 定时器回调:1ms计时,检测释放并判断长短按 */ static void key_timer_cb(void *param) { key_press_time++; // 累计按下时间 if (GpioInPins(GpioPortD, GpioBitMsk4) == 0) // WXL2026-5-20 { rt_timer_stop(key_timer); // 停止计时 G_KEY_CAN_DEEP_SLEEP = 0; // modify by HBR 20260622,按键,不允许休眠 g_key_last_active_tick = rt_tick_get(); //add by HBR 20260622,判断出短按记录一次时间 // 核心:判断长短按 if (key_press_time >= SHORT_PRESS_MS && key_press_time < LONG_PRESS_MS) { LOG("short press:%dms\n", key_press_time); // 短按逻辑 KEY_SHORT_PRESS_FLAG = 1; // 翻屏 } key_press_time = 0; // 重置计时 } if ((G_KEY_CAN_DEEP_SLEEP == 0) && (key_press_time >= LONG_PRESS_MS)) { G_KEY_CAN_DEEP_SLEEP = 1; // 允许休眠 LOG("long press:%dms\n", key_press_time); // 长按逻辑 KEY_LONG_PRESS_FLAG = 1; // 上报 rt_timer_stop(key_timer); // 停止计时 key_press_time = 0; } } /** * @brief RTC相关业务线程 * @param parameter: 线程参数,RT_NULL * @retval 无 */ static void rtc_business_thread(void *parameter) { rt_err_t ret = RT_EOK; while (1) { /* 等待中断信号(永久等待,不占用CPU) */ if (rt_sem_take(&rtc_isr_sem, RT_WAITING_FOREVER) == RT_EOK) { rtc_interrupt_deal_tdc(); //add by HBR 20260617 to deal static ot count do not display when set stativ ot if(G_STATIC_OT_SETTING_FLAG == 1) { rt_sem_release(&lcd_isr_sem); } } } } /** * @brief 创建rtc线程 * @retval 0-成功,-1-失败 */ static int rtc_thread_create(void) { rt_thread_t rtc_thread = RT_NULL; rtc_thread = rt_thread_create("rtc_thread", rtc_business_thread, RT_NULL, RTC_THREAD_STACK, RTC_THREAD_PRIORITY, RTC_THREAD_TIMESLICE); if (rtc_thread == RT_NULL) { LOG("RTC thread create failed!\n"); return -1; } rt_thread_startup(rtc_thread); LOG("RTC thread create success!\n"); return 0; } /** * @brief TDC相关业务线程 * @param parameter: 线程参数,RT_NULL * @retval 无 */ static void tdc_business_thread(void *parameter) { rt_err_t ret = RT_EOK; uint32_t cnt; while (1) { // LOG("tdc_business_thread\n"); /* 等待中断信号(永久等待,不占用CPU) */ if (rt_sem_take(&tdc_isr_sem, RT_WAITING_FOREVER) == RT_EOK) { // LOG("rt_sem_take\n"); tof_isr_convert_tof_to_flow(); } if (TdcIntStep == 0xff) { // cnt=SYS_MS_CNT; #ifdef APP_DEBUG_PRINT G_TDC_DEEP_SLEEP = 0; // rt_thread_mdelay(15); rtc_run_t *rr; get_rtc_run(&rr); #ifdef DEBUG_TDC LOG("Diff=%.6f, AvgDiff=%.6f, T=%.6f, vol=%.6f\n", SerPlot.f.Data[0], SerPlot.f.Data[3], WaterPrmt[0], FlowVol * 0.000001f * 3600); // LOG("diff=%.6f, U=%.6f, D=%.6f\n", SerPlot.f.Data[0], SerPlot.f.Data[1], SerPlot.f.Data[2]); rt_thread_mdelay(4); #endif #endif G_TDC_DEEP_SLEEP = 1; // rt_thread_mdelay(10); NB_CHECK_TDC_COMPLETE_FLAG = 1; // get_time[gtcnt++]=SYS_MS_CNT-cnt; // gtcnt&=0x1f; } // G_TDC_DEEP_SLEEP = 1; } } /** * @brief 创建tdc线程 * @retval 0-成功,-1-失败 */ static int tdc_thread_create(void) { rt_thread_t tdc_thread = RT_NULL; tdc_thread = rt_thread_create("tdc_thread", tdc_business_thread, RT_NULL, TDC_THREAD_STACK, TDC_THREAD_PRIORITY, TDC_THREAD_TIMESLICE); if (tdc_thread == RT_NULL) { LOG("TDC thread create failed!\n"); return -1; } rt_thread_startup(tdc_thread); LOG("TDC thread create success!\n"); return 0; } /** * @brief NB相关业务线程 * @param parameter: 线程参数,RT_NULL * @retval 无 */ // extern rt_sem_t Nb_thread_run_sem; static void nb_business_thread(void *parameter) { NbTread(); // 满足上报条件后就上报,否则持续挂起等待 } /** * @brief 创建NB线程 * @retval 0-成功,-1-失败 */ static int nb_thread_create(void) { rt_thread_t nb_thread = RT_NULL; nb_thread = rt_thread_create("nb_thread", nb_business_thread, RT_NULL, NB_THREAD_STACK, NB_THREAD_PRIORITY, NB_THREAD_TIMESLICE); if (nb_thread == RT_NULL) { LOG("NB thread create failed!\n"); return -1; } rt_thread_startup(nb_thread); LOG("NB thread create success!\n"); return 0; } /** * @brief LCD相关业务线程 * @param parameter: 线程参数,RT_NULL * @retval 无 */ static void lcd_business_thread(void *parameter) { while (1) { if (rt_sem_take(&lcd_isr_sem, RT_WAITING_FOREVER) == RT_EOK) { if (lcd_disp_pass_flag != 0) { lcd_disp_pass_flag--; lcd_show_key_trigger_report_pass(); } else if (G_OT_ERROR_FLAG_CNT != 0) { G_OT_ERROR_FLAG_CNT--; lcd_show_ot_error(); } else LcdHandler(); key_handle_runing = 0; } } } /** * @brief 创建LCD线程 * @retval 0-成功,-1-失败 */ static int lcd_thread_create(void) { rt_thread_t lcd_thread = RT_NULL; lcd_thread = rt_thread_create("lcd_thread", lcd_business_thread, RT_NULL, LCD_THREAD_STACK, LCD_THREAD_PRIORITY, LCD_THREAD_TIMESLICE); if (lcd_thread == RT_NULL) { LOG("LCD thread create failed!\n"); return -1; } rt_thread_startup(lcd_thread); LOG("LCD thread create success!\n"); return 0; } /** * @brief 创建按键时长检测定时器 * @retval 0-成功,-1-失败 */ static int Key_press_timer_create(void) { // 创建1ms周期的软件定时器(RTOS自带,无需配置硬件) key_timer = rt_timer_create("key_timer", key_timer_cb, RT_NULL, 1, RT_TIMER_FLAG_PERIODIC); if (key_timer == RT_NULL) { LOG("key_timer create failed!\n"); return -1; } LOG("key_timer create success!\n"); return 0; } int32_t main(void) { int32_t res; uint8_t ch_num; uint8_t ch; uint8_t i; uint32_t flag; rt_base_t level; #ifdef DOG_ENABLE watchdog_init(WdtResetEn, WdtT6s55); // 看门狗初始化 #endif feed_dog(); rt_enter_critical(); //!!!!!do not delete!!!!!! set_tof_state(TOF_ST_INIT); HardwareIdInit(); // 初始化硬件ID接口 ButtonInit(); // 初始化磁组按键 tdc_tof_init(); // ms1030 spi驱动 finsh_drv_init(); // 红外串口初始化波特率2400、8位数据、1位停止位、even偶校验 lpuart0_drv_init(0); // NB串口初始化波特率9600、8位数据、1位停止位、none校验 nb_run_init(); // NB运行数据初始化 McuFlash_init(); // FLASH读写初始化 #ifdef APP_DEBUG_PRINT debug_uart_init(); // 初始化调试打印接口,波特率9600、8位数据、1位停止位、none校验 #endif // DEBUG set_check_rx_itf(CheckRxData); // 设置红外中断处理函数为CheckRxData check_init(); // 初始化红外接收数据结构 lcd_init(); // 液晶屏初始化 // LCD_DispDouble(1234.567890, 0, 10, 4); lcd_show_all(); // 液晶屏全显 LCD_Update(); us_delay(100); // 延时大约1秒 feed_dog(); adc_init(); // adc初始化,电压采集 adc_get(); // 采集一次数据 eeprom_init(); // EEPROM驱动初始化,I2C rtc_run_t *rr; get_rtc_run(&rr); // 获取RTC时间 rtc_init(rr); // 设置RTC系统时钟源为外部32K(ms1030),同时设置定时中断为1秒钟一次 set_tof_state(TOF_ST_VOID); // 设置ms1030状态,降低功耗 // // set_tof_state(TOF_ST_METER); ParamInit(); // 初始化全局变量参数 set_tdc_itf(&TdcItf); //!!!!!!do not move!!!!!!only can be after ParamInit()!!!!!!! LOG("------------TFUW001------------\n"); ch_num = GetChNum(); tof_filter_init(ch_num); // 20260429 LOG("ch_num = %d\n", ch_num); // LOG("TdcItf.ch_num = %d\n", TdcItf.ch_num); // LOG("pTdcItf->ch_num = %d\n", pTdcItf->ch_num); for (i = 0; i < ch_num; i++) { ch = TdcItf.mux_ch_no[i]; LOG("Ch%d TdcPathSkew = %.3fns.\n", ch, TdcItf.path_skew[ch]); } LOG("pressure_sensor = %d\n", Paramx.pressure_sensor); LOG("PipeCoe = %.6f\n", TdcItf.pipe_coe); LOG("dev_id =%02x%02x%02x%02x%02x%02x\n", Paramx.dev_id[0], Paramx.dev_id[1], Paramx.dev_id[2], Paramx.dev_id[3], Paramx.dev_id[4], Paramx.dev_id[5]); LOG("b=%d,n=%d,q=%d,r=%d\n", Paramx.b, Paramx.n, Paramx.q, Paramx.r); int tmp = 0; for (i = 0; i < CHECK_POINT_NUM; i++) { LOG("spdlvl[%d][%d] = %d\n", tmp, i, Paramx.check_spdLvl_c[tmp].spd_lvl[i]); LOG("base c[%d][%d] = %d\n", tmp, i, Paramx.check_spdLvl_c[tmp].base_c[i]); LOG("current c[%d][%d] = %d\n", tmp, i, Paramx.check_spdLvl_c[tmp].cur_c[i]); } ///////////debug////////////////////// // set_rtc_interrupt_time(1); /////////////////////////////// rtc_thread_create(); // 创建RTC线程 tdc_thread_create(); // 创建TDC线程 nb_thread_create(); // 创建NB线程 lcd_thread_create(); // 创建LCD线程 Key_press_timer_create(); // 创建按键时长检测定时器 rt_sem_init(&tdc_isr_sem, "tdc_isr", 0, RT_IPC_FLAG_PRIO); rt_sem_init(&rtc_isr_sem, "rtc_isr", 0, RT_IPC_FLAG_PRIO); rt_sem_init(&lcd_isr_sem, "lcd_isr", 0, RT_IPC_FLAG_PRIO); rt_exit_critical(); ////!!!!!do not delete!!!!!! // us_delay(10000); // nb_tx_str(NB_AT); // us_delay(10000); // nb_tx_str(NB_AT); // us_delay(10000); while (1) { PeriodProc(); CheckRxHandler(); // 红外接收指令处理 // 翻屏 if (KEY_SHORT_PRESS_FLAG == 1) { key_handle_runing = 1; if (GetCheckState() == CHECKING) { // add by yuewei 20260416 to toggle display fw and bw cumflow in checking mode g_lcd_display_check_bw_cum_flow = ~g_lcd_display_check_bw_cum_flow; rt_sem_release(&lcd_isr_sem); //add by HBR 20260622 } else { lcd_page_switch(); rt_sem_release(&lcd_isr_sem); // LcdHandler();//add by yw 20260126 } KEY_SHORT_PRESS_FLAG = 0; // 清除标志 } // 长按 if (KEY_LONG_PRESS_FLAG == 1) { key_handle_runing = 1; LOG("-----------------------------LcdPage=%d\n", LcdPage); // 处于检表状态 if (GetCheckState() == CHECKING) { // 进入正常模式 check_enter_normal_mode_by_key_press(); LOG("check_enter_normal_mode_by_key_press!\n"); } else { // 液晶屏当前处于时间显示页面 if (LcdPage == disp_time) { // 进入检表模式 check_enter_check_mode_by_key_press(); LOG("check_enter_check_mode_by_key_press!\n"); } else { // add by yuewei 20260416 // lcd_show_key_trigger_report_pass(); // us_delay(100000000); lcd_disp_pass_flag = 2; // wxl20260423 触发显示pass // rt_sem_release(&lcd_isr_sem); // 在此处触发NB上报 upload_dat.manual_runflag = 1; // wxl 2026-4-1 这个标志会触发手动上报 } } KEY_LONG_PRESS_FLAG = 0; } // show_stack_by_name("tidle"); // show_stack_by_name("main"); // show_stack_by_name("lcd_thread"); // show_stack_by_name("nb_thread"); if ((G_KEY_CAN_DEEP_SLEEP == 0) && //add by HBR 20260623 (key_press_time == 0) && (rt_tick_get() - g_key_last_active_tick > rt_tick_from_millisecond(KEY_SLEEP_BLOCK_MS))) //按键按下后延迟800ms并且按键计数为0后,才进入休眠 { G_KEY_CAN_DEEP_SLEEP = 1; } if (G_NB_CAN_DEEP_SLEEP && G_TDC_DEEP_SLEEP && G_KEY_CAN_DEEP_SLEEP) { EnterLowPower(); } } } /****************************************************************************** * EOF (not truncated) ******************************************************************************/ #ifdef DEBUG_LOW_POWER uint32_t get_time[32]; uint8_t gtcnt; // void get_internal_time(void); // extern uint16_t tms; #endif