/********************************** (C) COPYRIGHT ******************************* * File Name : main.c * Author : WCH * Version : V1.1 * Date : 2020/08/06 * Description : Peripheral slave application main function and task system initialization ********************************************************************************* * Copyright (c) 2021 Nanjing Qinheng Microelectronics Co., Ltd. * Attention: This software (modified or not) and binary are used for * microcontroller manufactured by Nanjing Qinheng Microelectronics. *******************************************************************************/ /******************************************************************************/ /* Header file contains */ #include "CONFIG.h" #include "HAL.h" #include "gattprofile.h" #include "peripheral.h" #include "cmcng.h" #include #include "dbn_ble_srv.h" #include "eth_driver.h" #include "net_srv.h" #include "storage.h" #include "offlog.h" #include "snapshot.h" #include "tcp_json_srv.h" #include "loop_uart_proto.h" #include "iot_mqtt_srv.h" /********************************************************************* * GLOBAL TYPEDEFS */ __attribute__((aligned(4))) uint32_t MEM_BUF[BLE_MEMHEAP_SIZE / 4]; #define KEY_GPIO (RCC_APB2Periph_GPIOA) #define KEY_BV BV(0) #define KEY_IN (GPIO_ReadInputDataBit(KEY_GPIO, KEY_BV)==0) #define HAL_PUSH_BUTTON() (KEY1_IN) //Add custom button #if(defined(BLE_MAC)) && (BLE_MAC == TRUE) const uint8_t MacAddr[6] = {0x84, 0xC2, 0xE4, 0x03, 0x02, 0x02}; // #else // uint8_t MACAddr[6] = {0x84, 0xC2, 0xE4, 0x03, 0x02, 0x02}; #endif uint8_t gMacAddr[6] = { 0x38, 0x3B, 0x26, 0x11, 0xA4 ,0x35 }; /* Used for app judgment file effectiveness */ const uint32_t Address = 0xFFFFFFFF; __attribute__((aligned(4))) uint32_t Image_Flag __attribute__((section(".ImageFlag"))) = (uint32_t)&Address; uint8_t g_dev_number[6] = ""; // �豸��� ��Ʒ��� uint8_t g_dev_password[6] = {0x31, 0x32, 0x33, 0x34, 0x35, 0x36}; uint8_t g_ble_safe_flag = 0; uint32_t g_ble_safe_counter_ori = 0; uint32_t g_ble_safe_counter_dst = 0; char g_flag_debug = 1; uint8_t g_dg_device_type = DDType_DLD950V4; // ???����????????????? uint8_t g_dg_sub_dev_type = DDType_DLD950V4; Sub_Code_Enable g_sub_code_enable = {0,}; uint32_t g_activ_counter = 0; uint32_t g_counter_bt_timeout = 0; uint8_t g_flag_bt_state = 0; uint8_t g_flag_bt_disable = 0; uint8_t g_max_counter_bt_min = 0; uint32_t g_max_counter_bt_timeout = 0; //BT_DISABLE_IDLE_TIMEOUT * 60 * 1000; // ms unit __IO uint32_t TimingDelayInc; __IO uint32_t TimingDelayDec; uint8_t trigB; Pkg_Uart g_pkg_uart_1 = { 0, 0, 0, "", 0}; Pkg_Uart g_pkg_uart_2 = { 0, 0, 0, "", 0}; Flag_Counter g_flag_counter_key = {0, 0, 0}; Flag_Counter g_flag_counter_ota = {0, 0, 0}; uint8_t g_storage_uart_num = 0; uint32_t g_storage_uart_baud = 19200;//9600; uint8_t g_storage_uart_num_2 = 1; uint32_t g_storage_uart_baud_2 = 115200; //115200; uint32_t mstick(void){ return TimingDelayInc; } void InitPkgUart(Pkg_Uart * pkg){ memset(pkg->pkg, 0, BUFF_STACK_SIZE); pkg->flag = 0; pkg->tick = 0; pkg->len = 0; pkg->offset = 0; } /********************************************************************* * @fn TIM2_Init * * @brief Initializes TIM2. * * @return none */ void TIM2_Init( void ) { TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure={0}; RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE); TIM_TimeBaseStructure.TIM_Period = SystemCoreClock / 1000000; TIM_TimeBaseStructure.TIM_Prescaler = WCHNETTIMERPERIOD * 1000 - 1; TIM_TimeBaseStructure.TIM_ClockDivision = 0; TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure); TIM_ITConfig(TIM2, TIM_IT_Update ,ENABLE); TIM_Cmd(TIM2, ENABLE); TIM_ClearITPendingBit(TIM2, TIM_IT_Update ); NVIC_EnableIRQ(TIM2_IRQn); } /********************************************************************* * @fn TIM3_Init * * @brief Initializes TIM3. * * @return none */ void TIM3_Init(void) { NVIC_InitTypeDef NVIC_InitStructure={0}; TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure = { 0 }; RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE); ////10ms // TIM_TimeBaseStructure.TIM_Period = SystemCoreClock / 1000000; // TIM_TimeBaseStructure.TIM_Prescaler = 10 * 1000 - 1; //1ms TIM_TimeBaseStructure.TIM_Period = SystemCoreClock / 1000000; TIM_TimeBaseStructure.TIM_Prescaler = 1 * 1000 - 1; TIM_TimeBaseStructure.TIM_ClockDivision = 0; TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure); TIM_ITConfig(TIM3, TIM_IT_Update, ENABLE); TIM_Cmd(TIM3, ENABLE); TIM_ClearITPendingBit(TIM3, TIM_IT_Update); NVIC_EnableIRQ(TIM3_IRQn); } void key_event_srv(void){ // if(g_flag_counter_key.tick > 1000){ // PRINT("key_tick_timeup_______: %d\n", GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_0)); // g_flag_counter_key.tick = 0; // } if(g_flag_counter_key.flag){ uint8_t _pkg[7] = {0}; uint8_t i = 0; switch (g_flag_counter_key.flag) { case KEY_ET_BLE_ENABLE: { PRINT("______KEY_ET_BLE_ENABLE\n"); NVIC_SystemReset(); } break; case KEY_ET_REBOOT: { PRINT("__________KEY_ET_REBOOT\n"); _pkg[i++] = 0x7F; _pkg[i++] = 0x00; _pkg[i++] = 0x01; _pkg[i++] = 0x6D; _pkg[i++] = 0x6C; _pkg[i++] = 0x6E; UART2_SendString(_pkg, i); //1 send loop mcu reboot; Delay_Ms(10); // 2 DBN dev reboot NVIC_SystemReset(); } break; case KEY_ET_FACTORY: { PRINT("_____________KEY_ET_FACTORY\n"); _pkg[i++] = 0x7F; _pkg[i++] = 0x00; _pkg[i++] = 0x01; _pkg[i++] = 0x92; _pkg[i++] = 0x93; _pkg[i++] = 0x93; UART2_SendString(_pkg, i); //1 send loop mcu factory init } break; case KEY_ET_NETBLE_FACTORY: { //2 DBN dev factory init; factory_dev_info(); Delay_Ms(10); NVIC_SystemReset(); } break; } g_flag_counter_key.flag = 0; } } /********************************************************************* * @fn sim_snap_spi * * @brief 实验(2026-08-13 第一步): 模拟快照区 SPI 写密度 * * 当前固件: snap_init 已 #if 0 跳过 (纯读基座, 日志读写正常)。 * 本函数恢复快照落盘的写节奏, 隔离"SPI 写频率" vs "快照逻辑 bug": * - 每 20ms: 64B 页写 (SPI_Flash_Write_NoCheck, 模拟 snap_write_raw) * - 每 64 条: 切扇区序列 (读首条 magic + 擦除 45ms + * 头区 SPI_Flash_Write 读-改-写整扇区, 模拟 snap_flush_head) * 复现复位 → SPI 高频写触发硬件问题 (PA7/NRST 共线怀疑坐实); * 不复现 → 快照区有具体逻辑 bug, 转向代码审查。 * 注意: 会覆盖快照区旧数据, 恢复快照功能前需 snap_clear。 * * @return none */ #define SIM_SNAP_HEAD_BASE 0x110000UL #define SIM_SNAP_DATA_BASE 0x111000UL #define SIM_SNAP_DATA_SIZE (4096UL * 751UL) /* 与 snapshot.c 数据扇区数一致 */ static uint32_t _sim_snap_wr = SIM_SNAP_DATA_BASE; static uint32_t _sim_snap_cnt = 0; static uint32_t _sim_snap_last = 0; void sim_snap_spi(void) { uint32_t _now = mstick(); if ((uint32_t)(_now - _sim_snap_last) < 20) return; /* 20ms 节拍 = 50Hz 帧率 */ _sim_snap_last = _now; /* 切扇区: 每 64 条 (4096/64) 一次, 与 snap_write_raw 同节奏 */ if (_sim_snap_cnt > 0 && (_sim_snap_cnt & 63) == 0) { uint8_t _r[64]; uint8_t _h[32]; /* 1) snap_sector_has_data: 读目标扇区首条 magic */ SPI_Flash_Read(_r, _sim_snap_wr, 64); /* 2) SPI_Flash_Erase_Sector (~45ms) */ SPI_Flash_Erase_Sector(_sim_snap_wr / 4096); /* 3) snap_flush_head: 头区读-改-写 (SPI_Flash_Write 内部 读4096 + 擦 + 写回4096, ~100ms) */ memset(_h, 0xFF, sizeof(_h)); _h[0] = 'S'; _h[1] = 'N'; _h[2] = 'P'; _h[3] = 'M'; _h[8] = (uint8_t)(_sim_snap_cnt & 0xFF); _h[9] = (uint8_t)((_sim_snap_cnt >> 8) & 0xFF); _h[10] = (uint8_t)((_sim_snap_cnt >> 16) & 0xFF); _h[11] = (uint8_t)((_sim_snap_cnt >> 24) & 0xFF); SPI_Flash_Write(_h, SIM_SNAP_HEAD_BASE, 32); } /* 模拟 snap_write_raw: 64B 页写 */ { static uint8_t _b[64]; uint8_t _i; for (_i = 0; _i < 64; _i++) _b[_i] = (uint8_t)(_sim_snap_cnt + _i); _b[0] = 'S'; _b[1] = 'N'; _b[2] = 'P'; _b[3] = 'M'; SPI_Flash_Write_NoCheck(_b, _sim_snap_wr, 64); _sim_snap_wr += 64; if (_sim_snap_wr >= SIM_SNAP_DATA_BASE + SIM_SNAP_DATA_SIZE) _sim_snap_wr = SIM_SNAP_DATA_BASE; _sim_snap_cnt++; if ((_sim_snap_cnt & 0x3F) == 0) PRINT("SIM: cnt=%lu wr=0x%08lx\n", (unsigned long)_sim_snap_cnt, (unsigned long)_sim_snap_wr); } } /********************************************************************* * @fn Main_Circulation * * @brief Main loop * * @return none */ __attribute__((section(".highcode"))) __attribute__((noinline)) void Main_Circulation(void) { uint32_t _counter = 0; while(1) { TMOS_SystemProcess(); if(g_net_state.flag < 2) { net_srv_init(); } if(g_net_state.flag == 2) { if(g_sub_code_enable.iot_enable){ if(iot_net_info.mode == IOT_Addr_IP_Mode){ if(get_ipstr_to_array(iot_net_info.remote_addr, RemoteIP) == 0){ WCHNET_CreateTcpMqttSocket(); } } else{ //DNS } } else { WCHNET_CreateTcpSocket(); } } /*Ethernet library main task function, * which needs to be called cyclically*/ WCHNET_MainTask(); /*Query the Ethernet global interrupt, * if there is an interrupt, call the global interrupt handler*/ if(WCHNET_QueryGlobalInt()) { WCHNET_HandleGlobalInt(); } uart_srv(); snap_flush(); /* 快照落盘: 无条件挂主循环 (原在 iot_enable 分支, TCP 模式不落盘) */ sim_snap_spi(); /* 实验第一步(2026-08-13): 模拟快照区 SPI 写密度, 定位复位触发源 */ poll_dbn_ble(); if (g_sub_code_enable.iot_enable) { iot_mqtt_publish_sensor(); // Push 0xC0 sensor data to MQTT broker iot_mqtt_poll(); // IoT MQTT state machine + PINGREQ keepalive } else { tcp_json_push_sensor(); // Push 0xC0 sensor data to TCP JSON client tcp_json_poll(); } key_event_srv(); } } /********************************************************************* * @fn main * * @brief Main function * * @return none */ int main(void) { SystemCoreClockUpdate(); Delay_Init(); #ifdef DEBUG // USART_Printf_Init( 115200 ); USART_Printf_Init( 256000 ); #endif /* 诊断: 复位原因寄存器 (bit31=IWDG bit30=WWDG bit29=LPWR bit26=NRST bit25=POR bit24=软复位) 每次上电必打, 区分看门狗死锁 vs 掉电 vs 外部复位 */ PRINT("RST_REASON: 0x%08lx\n", (unsigned long)OFFLOG_RCC_RSTSCKR); PRINT("%s\n", VER_LIB); PRINT("SystemCoreClock:%d\n", SystemCoreClock); /* ===== 实验: 禁用全部 Flash/SPI 操作 (2026-08-12) ===== 结论: 禁用后设备稳定 → SPI 操作触发复位+乱码实锤 软件缓解: storage.c SPI 时钟降档 + GPIO 缓边沿 (见 SPI_Flash_Init) 硬件根治: NRST 加 100nF 电容 / SPI 走线远离 NRST / 串阻缓边沿 #if 0 = 恢复全部 SPI; 保留本块便于回退实验 */ #if 0 PRINT("ISOLATE: flash/SPI disabled (flash isolation test)\n"); #else GetMacAddr(gMacAddr); storage_init(); PRINT("INIT: storage ok\n"); offlog_init(); PRINT("INIT: offlog ok\n"); /* ===== 实验: 跳过 snap_init (2026-08-12) ===== 目的: SPI 序列还原为旧固件(无快照SPI), 区分触发源 稳定 → 快照SPI(读/擦/写头)触发; 崩 → offlog_boot本身(旧固件也该崩) */ #if 0 snap_init(); PRINT("INIT: snap ok\n"); #else PRINT("INIT: snap skipped (offlog-only SPI seq)\n"); #endif /* ===== 实验: 跳过 offlog_boot SPI 写 (2026-08-12) ===== 目的: 二分定位 — 跳过 snap 仍崩 → 是否是 offlog_boot 32B 写触发 不崩 → offlog_boot 写(含切扇区擦除)触发; 崩 → 更早 SPI 读操作或非SPI */ { uint32_t rcc_rst = OFFLOG_RCC_RSTSCKR; #if 0 offlog_boot(rcc_rst); #else PRINT("SKIP: offlog_boot write skipped\n"); #endif OFFLOG_RCC_RSTSCKR |= OFFLOG_RST_RMVF; /* RMVF (bit24): 清复位标志 */ } load_cfg_from_flash(); output_cfg_from_flash(); #endif PRINT("MAC: %02X %02X %02X %02X %02X %02X\r\n", gMacAddr[0],gMacAddr[1], gMacAddr[2], gMacAddr[3],gMacAddr[4],gMacAddr[5]); PRINT("net version:%x\n", WCHNET_GetVer()); WCHBLE_Init(); HAL_Init(); uart_init(); /* ===== 实验: 禁用 UART2 (Loop 口) 隔离测试 (2026-08-12) ===== 结论: UART2 禁用后仍复位 → 与 Loop 数据路径无关 (后定位为 SPI 串扰) #if 0 = 恢复 UART2 正常 */ #if 0 { GPIO_InitTypeDef gpio; USART_ITConfig(USART2, USART_IT_RXNE, DISABLE); USART_Cmd(USART2, DISABLE); RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); gpio.GPIO_Pin = GPIO_Pin_3; gpio.GPIO_Mode = GPIO_Mode_IPU; /* PA3 脱离 UART2, 上拉输入 */ gpio.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOA, &gpio); PRINT("ISOLATE: UART2 disabled (Loop isolation test)\n"); } #endif TIM3_Init(); TIM2_Init(); GAPRole_PeripheralInit(); Peripheral_Init(); Main_Circulation(); } void TIM3_IRQHandler(void) __attribute__((interrupt("WCH-Interrupt-fast"))); /********************************************************************* * @fn TIM3_IRQHandler, 1ms * * @brief This function handles TIM2 exception. * * @return none */ void TIM3_IRQHandler(void) { TimingDelayInc++; g_activ_counter++; if(g_pkg_uart_2.offset){ if(g_pkg_uart_2.flag == 0){ g_pkg_uart_2.tick++; if(g_pkg_uart_2.tick > 8){ g_pkg_uart_2.flag = 1; } } } // Safety: reset frame parser if stuck in non-IDLE state too long if(g_lup_parser.state != LUP_FRAME_STATE_IDLE){ if(g_pkg_uart_2.tick > 20){ lup_frame_reset(); g_pkg_uart_2.tick = 0; } } if(GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_0) == 0){//HAL_PUSH_BUTTON()){ g_flag_counter_key.tick++; } else{ if(g_flag_counter_key.tick){ if(g_flag_counter_key.tick < 2000){ g_flag_counter_key.flag = KEY_ET_BLE_ENABLE; } else if(g_flag_counter_key.tick < 6000){ g_flag_counter_key.flag = KEY_ET_REBOOT; } else if(g_flag_counter_key.tick < 10000){ g_flag_counter_key.flag = KEY_ET_FACTORY; } else{ g_flag_counter_key.flag = KEY_ET_NETBLE_FACTORY; } g_flag_counter_key.tick = 0; } } TIM_ClearITPendingBit(TIM3, TIM_IT_Update); } /******************************** endfile @ main ******************************/