/** * BootLoader 程序入口 * * 功能: * 1. 上电后最先运行,初始化硬件 * 2. 检查 W25Q64 外部 Flash 中的 OTA 信息区 * 3. 根据信息区状态选择: * - DOWNLOADED: 新固件已下载,备份当前 APP1 后写入新固件 * - INSTALLED: 已安装新固件,等待 APP 确认;超过重试次数则回退 * - ROLLBACK: 已回退到旧固件,直接跳转 * - 无有效标志: 直接跳转 APP1 运行 * 4. BootLoader 不设置 VTOR 偏移(默认0x08000000) * * Flash 布局 (STM32F103RCT6 256KB): * 0x08000000 ─ BootLoader (32KB) * 0x08008000 ─ APP1 运行区 (224KB) * * W25Q64 外部 Flash 布局: * 0x000000 ─ 槽 A: 旧固件备份 (256KB) * 0x040000 ─ 槽 B: 新固件下载 (256KB) * 0x080000 ─ 信息区 A (4KB) * 0x081000 ─ 信息区 B (4KB) */ #include "main.h" #include "ota_boot.h" #include "../HardWare/W25Q64/w25q64.h" #include "../HardWare/LOG/log.h" /* 串口句柄 */ UART_HandleTypeDef huart1; /* USART1: 调试日志输出 */ UART_HandleTypeDef huart3; /* USART3: OTA 数据通信 */ DMA_HandleTypeDef hdma_usart3_rx; /* USART3 RX DMA */ IWDG_HandleTypeDef hiwdg; /* 独立看门狗:防止擦写/拷贝流程卡死 */ /* OTA 协议接收缓冲区 */ uint8_t Rx_Flag = 0; /* 收到一帧数据标志 */ uint16_t Rx_Len = 0; /* 当前帧数据长度 */ uint8_t Rx_Buf[Rx_Max] = {0}; /* DMA 接收缓冲区 */ void SystemClock_Config(void); static void MX_GPIO_Init(void); static void MX_DMA_Init(void); static void MX_USART1_UART_Init(void); static void MX_USART3_UART_Init(void); static void MX_IWDG_Init(void); /** * @brief BootLoader 主函数 * * 启动流程: * 硬件初始化 → 启动串口DMA → 打印横幅 * → 等待1秒OTA数据 → 读模式 → 分模式处理 → 跳转APP1 * * @note BootLoader 不设置 VTOR 偏移(默认0x08000000) */ int main(void) { HAL_Init(); SystemClock_Config(); MX_GPIO_Init(); MX_DMA_Init(); MX_USART1_UART_Init(); MX_USART3_UART_Init(); /* USART3: 启动 DMA + IDLE 中断接收 OTA 数据帧 */ HAL_UART_Receive_DMA(&huart3, Rx_Buf, Rx_Max); __HAL_UART_ENABLE_IT(&huart3, UART_IT_IDLE); /* 启动独立看门狗:跳转 APP 后由 APP 继续喂狗 */ MX_IWDG_Init(); /* 核心启动逻辑 */ if (W25Q64_Init() != 0) { log_error("[BL] W25Q64 初始化失败!"); } Start_BootLoader(); } /** * @brief 系统时钟配置: HSE(8MHz) → PLL×9 → 72MHz SYSCLK */ void SystemClock_Config(void) { RCC_OscInitTypeDef RCC_OscInitStruct = {0}; RCC_ClkInitTypeDef RCC_ClkInitStruct = {0}; RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE; RCC_OscInitStruct.HSEState = RCC_HSE_ON; RCC_OscInitStruct.HSEPredivValue = RCC_HSE_PREDIV_DIV1; RCC_OscInitStruct.HSIState = RCC_HSI_ON; RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON; RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE; RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL9; if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK) Error_Handler(); RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK|RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2; RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK; RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1; RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2; RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1; if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2) != HAL_OK) Error_Handler(); } /** * @brief USART1 初始化: 调试日志输出, PA9(TX)/PA10(RX), 115200-8N1 */ static void MX_USART1_UART_Init(void) { huart1.Instance = USART1; huart1.Init.BaudRate = 115200; huart1.Init.WordLength = UART_WORDLENGTH_8B; huart1.Init.StopBits = UART_STOPBITS_1; huart1.Init.Parity = UART_PARITY_NONE; huart1.Init.Mode = UART_MODE_TX_RX; huart1.Init.HwFlowCtl = UART_HWCONTROL_NONE; huart1.Init.OverSampling = UART_OVERSAMPLING_16; if (HAL_UART_Init(&huart1) != HAL_OK) Error_Handler(); } /** * @brief USART3 初始化: OTA 数据通信, PB10(TX)/PB11(RX), 921600-8N1, DMA+IDLE * @note 波特率与 APP 侧 4G 模块配置(AT+IPR=921600)保持一致 */ static void MX_USART3_UART_Init(void) { huart3.Instance = USART3; huart3.Init.BaudRate = 921600; huart3.Init.WordLength = UART_WORDLENGTH_8B; huart3.Init.StopBits = UART_STOPBITS_1; huart3.Init.Parity = UART_PARITY_NONE; huart3.Init.Mode = UART_MODE_TX_RX; huart3.Init.HwFlowCtl = UART_HWCONTROL_NONE; huart3.Init.OverSampling = UART_OVERSAMPLING_16; if (HAL_UART_Init(&huart3) != HAL_OK) Error_Handler(); } /** * @brief DMA 初始化: USART3 RX 使用 DMA1 通道3 */ static void MX_DMA_Init(void) { __HAL_RCC_DMA1_CLK_ENABLE(); HAL_NVIC_SetPriority(DMA1_Channel3_IRQn, 0, 0); HAL_NVIC_EnableIRQ(DMA1_Channel3_IRQn); } /** * @brief GPIO 初始化: GPIOA/USART1, GPIOB/USART3 引脚, GPIOD/其他 */ static void MX_GPIO_Init(void) { GPIO_InitTypeDef g = {0}; __HAL_RCC_GPIOD_CLK_ENABLE(); __HAL_RCC_GPIOA_CLK_ENABLE(); __HAL_RCC_GPIOB_CLK_ENABLE(); __HAL_RCC_GPIOC_CLK_ENABLE(); __HAL_RCC_AFIO_CLK_ENABLE(); /* 关闭 JTAG,保留 SWD,释放 PA15/PB3/PB4 给 W25Q64 */ __HAL_AFIO_REMAP_SWJ_NOJTAG(); /* PC13(LED2):外部看门狗 TPL5010 的 DONE 喂狗引脚 */ g.Pin = GPIO_PIN_13; g.Mode = GPIO_MODE_OUTPUT_PP; g.Pull = GPIO_NOPULL; g.Speed = GPIO_SPEED_FREQ_LOW; /* PC13 驱动能力弱,只能低速 */ HAL_GPIO_Init(GPIOC, &g); /* BL 期间保持 4G/WiFi 模块断电(门禁等待时不空耗法拉电容/电池): * V1.7.0 新板: PC1(CAT_PWR)接 DCDC 使能脚, 开漏输出, 拉低=DCDC 停输出模块掉电; * PA6(ESP_EN) 拉低=WiFi 断电。 * APP 启动后由 APP 的 MX_GPIO_Init 重新配置接管 */ g.Pull = GPIO_NOPULL; g.Speed = GPIO_SPEED_FREQ_LOW; HAL_GPIO_WritePin(GPIOC, GPIO_PIN_1, GPIO_PIN_RESET); g.Pin = GPIO_PIN_1; g.Mode = GPIO_MODE_OUTPUT_OD; HAL_GPIO_Init(GPIOC, &g); HAL_GPIO_WritePin(GPIOA, GPIO_PIN_6, GPIO_PIN_RESET); g.Pin = GPIO_PIN_6; g.Mode = GPIO_MODE_OUTPUT_PP; HAL_GPIO_Init(GPIOA, &g); } /** * @brief 独立看门狗初始化: LSI(~40kHz) / 256分频 / 重载4095 → 超时约26s * @note LSI 实际频率 30~60kHz 漂移,最短超时约 17s; * 单个最长阻塞操作(W25Q64 忙等待 5s)远小于该值 */ static void MX_IWDG_Init(void) { hiwdg.Instance = IWDG; hiwdg.Init.Prescaler = IWDG_PRESCALER_256; hiwdg.Init.Reload = 4095; if (HAL_IWDG_Init(&hiwdg) != HAL_OK) Error_Handler(); } void Error_Handler(void) { /* 初始化失败:停在这里,避免带着未就绪的硬件继续运行 */ __disable_irq(); while (1) { } }