- peripheral_main: sim_snap_spi 模拟快照落盘节奏 (20ms 64B页写 + 每64条切扇区擦除+头写) - cfig_flash: magic不匹配时跳过 factory SPI_Flash_Write, 用内存默认值继续 (SPI写触发硬件复位死循环止血: 参数区0x00写也崩, RST=0x00000000) - 诊断修正: CH32V208GBU6 QFN28 无 NRST 引脚, 共线假设作废; SPI写/擦除→电流尖峰→VDD跌落→内部POR/PDR(2.4V)复位; BLE射频+SPI电流叠加是头号嫌疑
508 lines
16 KiB
C
508 lines
16 KiB
C
/********************************** (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 <string.h>
|
||
#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 ******************************/
|