feat: 完善Mesh继电器OTA与地址协议
- 增加STM8 Bootloader工程及出厂固件恢复流程\n- 增加W25Q64外部Flash驱动和OTA分包接收、CRC16校验、断点续传状态管理\n- 增加固件版本查询、升级结果上报与主机确认机制\n- 将业务帧从55 AA帧头调整为发送方Mesh短地址+CMD+LEN+DATA+CRC16\n- 修正JDY-28M外层地址与业务发送方地址的职责划分,支持广播请求定向应答\n- 同步更新硬件引脚、存储分区、OTA流程和主机通信协议文档\n- 已使用SDCC重新编译,应用代码大小为5512字节,链接地址为0x8800
This commit is contained in:
+71
-12
@@ -8,11 +8,18 @@
|
||||
#include <stdint.h>
|
||||
#include "board.h"
|
||||
#include "stm8s003_registers.h"
|
||||
#include "system_tick.h"
|
||||
|
||||
/** 当前 PA1 继电器控制输出的逻辑状态。 */
|
||||
static uint8_t relay_state;
|
||||
/** 当前 PD3 LED3 输出的逻辑状态。 */
|
||||
/** 当前 PB5 蓝牙组网/故障指示灯的逻辑状态。 */
|
||||
static uint8_t led3_state;
|
||||
/** 当前 PD3 继电器状态灯的逻辑状态。 */
|
||||
static uint8_t relay_led_state;
|
||||
/** PB4程序状态灯当前逻辑状态,1表示逻辑点亮。 */
|
||||
static uint8_t status_led_state;
|
||||
/** 上次翻转PB4状态灯的系统时间。 */
|
||||
static uint32_t status_led_last_ms;
|
||||
|
||||
/** 初始化板级资源,并将继电器置于安全断开状态。 */
|
||||
void Board_Init(void)
|
||||
@@ -28,13 +35,23 @@ void Board_Init(void)
|
||||
REG8(GPIOC_CR1_ADDR) &= (uint8_t)~(PC3_MASK | PC4_MASK);
|
||||
REG8(GPIOC_CR2_ADDR) &= (uint8_t)~(PC3_MASK | PC4_MASK);
|
||||
|
||||
/* PC5/PWRC 和 PC6/RESET 上电默认高电平,避免模块误进入 AT 或复位。 */
|
||||
REG8(GPIOC_ODR_ADDR) |= (uint8_t)(PC5_MASK | PC6_MASK);
|
||||
REG8(GPIOC_DDR_ADDR) |= (uint8_t)(PC5_MASK | PC6_MASK);
|
||||
REG8(GPIOC_CR1_ADDR) |= (uint8_t)(PC5_MASK | PC6_MASK);
|
||||
REG8(GPIOC_CR2_ADDR) &= (uint8_t)~(PC5_MASK | PC6_MASK);
|
||||
/* PA2/PWRC 和 PD2/RESET 上电默认高电平。PC5~PC7释放给硬件SPI。 */
|
||||
REG8(GPIOA_ODR_ADDR) |= PA2_MASK;
|
||||
REG8(GPIOA_DDR_ADDR) |= PA2_MASK;
|
||||
REG8(GPIOA_CR1_ADDR) |= PA2_MASK;
|
||||
REG8(GPIOA_CR2_ADDR) &= (uint8_t)~PA2_MASK;
|
||||
REG8(GPIOD_ODR_ADDR) |= PD2_MASK;
|
||||
REG8(GPIOD_DDR_ADDR) |= PD2_MASK;
|
||||
REG8(GPIOD_CR1_ADDR) |= PD2_MASK;
|
||||
REG8(GPIOD_CR2_ADDR) &= (uint8_t)~PD2_MASK;
|
||||
|
||||
/* PD3:推挽输出,默认熄灭 LED3。 */
|
||||
/* PB5:低电平点亮,默认熄灭蓝牙组网/故障指示灯。 */
|
||||
REG8(GPIOB_DDR_ADDR) |= PB5_MASK;
|
||||
REG8(GPIOB_CR1_ADDR) |= PB5_MASK;
|
||||
REG8(GPIOB_CR2_ADDR) &= (uint8_t)~PB5_MASK;
|
||||
REG8(GPIOB_ODR_ADDR) |= PB5_MASK;
|
||||
|
||||
/* PD3:高电平点亮,默认熄灭继电器状态灯。 */
|
||||
REG8(GPIOD_DDR_ADDR) |= PD3_MASK;
|
||||
REG8(GPIOD_CR1_ADDR) |= PD3_MASK;
|
||||
REG8(GPIOD_CR2_ADDR) &= (uint8_t)~PD3_MASK;
|
||||
@@ -42,6 +59,15 @@ void Board_Init(void)
|
||||
|
||||
relay_state = 0u;
|
||||
led3_state = 0u;
|
||||
relay_led_state = 0u;
|
||||
status_led_state = 0u;
|
||||
status_led_last_ms = 0ul;
|
||||
|
||||
/* PB4:低电平点亮,默认熄灭,用于程序运行/OTA状态指示。 */
|
||||
REG8(GPIOB_DDR_ADDR) |= PB4_MASK;
|
||||
REG8(GPIOB_CR1_ADDR) |= PB4_MASK;
|
||||
REG8(GPIOB_CR2_ADDR) &= (uint8_t)~PB4_MASK;
|
||||
REG8(GPIOB_ODR_ADDR) |= PB4_MASK;
|
||||
}
|
||||
|
||||
/** 设置继电器状态,后续接入 PA1 寄存器时保持高电平吸合约定。 */
|
||||
@@ -70,11 +96,25 @@ uint8_t Board_ReadMeshJoined(void)
|
||||
return ((REG8(GPIOC_IDR_ADDR) & PC3_MASK) != 0u) ? 1u : 0u;
|
||||
}
|
||||
|
||||
/** 设置 LED3 状态,后续接入 PD3 寄存器时保持高电平点亮约定。 */
|
||||
/** 设置PB5蓝牙组网/故障指示灯;由于低电平点亮,逻辑值需要反相输出。 */
|
||||
void Board_SetLed3(uint8_t on)
|
||||
{
|
||||
led3_state = (on != 0u) ? 1u : 0u;
|
||||
if (led3_state != 0u)
|
||||
{
|
||||
REG8(GPIOB_ODR_ADDR) &= (uint8_t)~PB5_MASK;
|
||||
}
|
||||
else
|
||||
{
|
||||
REG8(GPIOB_ODR_ADDR) |= PB5_MASK;
|
||||
}
|
||||
}
|
||||
|
||||
/** 设置PD3继电器状态灯;高电平点亮,与逻辑值保持同相。 */
|
||||
void Board_SetRelayLed(uint8_t on)
|
||||
{
|
||||
relay_led_state = (on != 0u) ? 1u : 0u;
|
||||
if (relay_led_state != 0u)
|
||||
{
|
||||
REG8(GPIOD_ODR_ADDR) |= PD3_MASK;
|
||||
}
|
||||
@@ -84,16 +124,35 @@ void Board_SetLed3(uint8_t on)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 周期驱动PB4程序状态灯。
|
||||
* @param ota_active 1表示OTA接收/校验中,0表示正常运行。
|
||||
*/
|
||||
void Board_StatusLedTask(uint8_t ota_active)
|
||||
{
|
||||
uint32_t now = SystemTick_NowMs();
|
||||
uint16_t period = (ota_active != 0u) ? 100u : 500u;
|
||||
if ((uint32_t)(now - status_led_last_ms) >= period)
|
||||
{
|
||||
status_led_last_ms = now;
|
||||
status_led_state = (status_led_state == 0u) ? 1u : 0u;
|
||||
if (status_led_state != 0u)
|
||||
REG8(GPIOB_ODR_ADDR) &= (uint8_t)~PB4_MASK;
|
||||
else
|
||||
REG8(GPIOB_ODR_ADDR) |= PB4_MASK;
|
||||
}
|
||||
}
|
||||
|
||||
/** 控制 PWRC;低电平使能 AT 指令窗口。 */
|
||||
void Board_SetBluetoothAtWindow(uint8_t enabled)
|
||||
{
|
||||
if (enabled != 0u) REG8(GPIOC_ODR_ADDR) &= (uint8_t)~PC5_MASK;
|
||||
else REG8(GPIOC_ODR_ADDR) |= PC5_MASK;
|
||||
if (enabled != 0u) REG8(GPIOA_ODR_ADDR) &= (uint8_t)~PA2_MASK;
|
||||
else REG8(GPIOA_ODR_ADDR) |= PA2_MASK;
|
||||
}
|
||||
|
||||
/** 控制 RESET;低电平复位,高电平正常工作。 */
|
||||
void Board_SetBluetoothReset(uint8_t reset_active)
|
||||
{
|
||||
if (reset_active != 0u) REG8(GPIOC_ODR_ADDR) &= (uint8_t)~PC6_MASK;
|
||||
else REG8(GPIOC_ODR_ADDR) |= PC6_MASK;
|
||||
if (reset_active != 0u) REG8(GPIOD_ODR_ADDR) &= (uint8_t)~PD2_MASK;
|
||||
else REG8(GPIOD_ODR_ADDR) |= PD2_MASK;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
/**
|
||||
* @file interrupt_control.c
|
||||
* @brief STM8 全局中断控制封装实现。
|
||||
*
|
||||
* 这里保留极少量与 CPU 架构相关的汇编指令,业务代码不再直接出现
|
||||
* `__asm`、`rim` 或 `sim`,从而提高主程序可读性,也方便以后更换编译器
|
||||
* 时集中修改底层适配代码。
|
||||
*/
|
||||
#include "interrupt_control.h"
|
||||
|
||||
/**
|
||||
* 开启全局中断。
|
||||
*
|
||||
* STM8 `rim` 指令会清除 CPU 的中断屏蔽状态。该函数应在 TIM4、UART 等
|
||||
* 外设完成初始化后调用,避免外设尚未配置完整时提前进入中断服务程序。
|
||||
*/
|
||||
void Interrupts_Enable(void)
|
||||
{
|
||||
__asm__("rim");
|
||||
}
|
||||
|
||||
/**
|
||||
* 关闭全局中断。
|
||||
*
|
||||
* STM8 `sim` 指令会屏蔽可屏蔽中断。临界区应尽可能短,不能在关闭中断后
|
||||
* 执行 UART 等待、EEPROM 写入或长时间循环,否则可能导致系统节拍丢失。
|
||||
*/
|
||||
void Interrupts_Disable(void)
|
||||
{
|
||||
__asm__("sim");
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
/** @file w25q64_app.c @brief 主程序侧W25Q64只读结果访问驱动。 */
|
||||
#include "w25q64_app.h"
|
||||
#include "stm8s003_registers.h"
|
||||
|
||||
#define W25_CS_MASK PD4_MASK
|
||||
#define W25_CMD_READ 0x03u
|
||||
#define W25_CMD_PROGRAM 0x02u
|
||||
#define W25_CMD_WREN 0x06u
|
||||
#define W25_CMD_RDSR 0x05u
|
||||
#define W25_CMD_ERASE_4K 0x20u
|
||||
#define W25_BUSY 0x01u
|
||||
#define W25_RESULT_ADDR 0x030010ul
|
||||
|
||||
static uint8_t Xfer(uint8_t value)
|
||||
{
|
||||
while ((SPI1_SR & SPI_SR_TXE) == 0u) {}
|
||||
SPI1_DR = value;
|
||||
while ((SPI1_SR & SPI_SR_RXNE) == 0u) {}
|
||||
return SPI1_DR;
|
||||
}
|
||||
static void Select(void){REG8(GPIOD_ODR_ADDR)&=(uint8_t)~W25_CS_MASK;}
|
||||
static void Deselect(void){REG8(GPIOD_ODR_ADDR)|=W25_CS_MASK;}
|
||||
static uint8_t Ready(void)
|
||||
{
|
||||
uint32_t timeout=200000ul;
|
||||
uint8_t value;
|
||||
do { Select(); Xfer(W25_CMD_RDSR); value=Xfer(0xFFu); Deselect(); }
|
||||
while ((value&W25_BUSY)!=0u && timeout--!=0ul);
|
||||
return (value&W25_BUSY)==0u ? 1u : 0u;
|
||||
}
|
||||
static void WriteEnable(void){Select();Xfer(W25_CMD_WREN);Deselect();}
|
||||
|
||||
void W25Q64App_Init(void)
|
||||
{
|
||||
REG8(GPIOC_DDR_ADDR)|=(uint8_t)(PC5_MASK|PC6_MASK);
|
||||
REG8(GPIOC_CR1_ADDR)|=(uint8_t)(PC5_MASK|PC6_MASK);
|
||||
REG8(GPIOC_DDR_ADDR)&=(uint8_t)~PC7_MASK;
|
||||
REG8(GPIOC_CR1_ADDR)&=(uint8_t)~PC7_MASK;
|
||||
REG8(GPIOD_DDR_ADDR)|=W25_CS_MASK;
|
||||
REG8(GPIOD_CR1_ADDR)|=W25_CS_MASK;
|
||||
Deselect();
|
||||
SPI1_CR1=(uint8_t)(SPI_CR1_MSTR|SPI_CR1_BR2|SPI_CR1_BR1|SPI_CR1_BR0|SPI_CR1_SPE);
|
||||
SPI1_CR2=(uint8_t)(SPI_CR2_SSM|SPI_CR2_SSI);
|
||||
}
|
||||
uint8_t W25Q64App_ReadByte(uint32_t address,uint8_t *value)
|
||||
{
|
||||
if(value==(uint8_t *)0)return 0u;
|
||||
Select(); Xfer(W25_CMD_READ); Xfer((uint8_t)(address>>16));
|
||||
Xfer((uint8_t)(address>>8)); Xfer((uint8_t)address); *value=Xfer(0xFFu); Deselect();
|
||||
return 1u;
|
||||
}
|
||||
uint8_t W25Q64App_Read(uint32_t address,uint8_t *buffer,uint16_t length)
|
||||
{
|
||||
uint16_t index;
|
||||
if(buffer==(uint8_t *)0)return 0u;
|
||||
Select(); Xfer(W25_CMD_READ); Xfer((uint8_t)(address>>16));
|
||||
Xfer((uint8_t)(address>>8)); Xfer((uint8_t)address);
|
||||
for(index=0u;index<length;index++)buffer[index]=Xfer(0xFFu);
|
||||
Deselect();
|
||||
return 1u;
|
||||
}
|
||||
uint8_t W25Q64App_EraseSector(uint32_t address)
|
||||
{
|
||||
WriteEnable(); Select(); Xfer(W25_CMD_ERASE_4K);
|
||||
Xfer((uint8_t)(address>>16)); Xfer((uint8_t)(address>>8));
|
||||
Xfer((uint8_t)address); Deselect();
|
||||
return Ready();
|
||||
}
|
||||
uint8_t W25Q64App_PageProgram(uint32_t address,const uint8_t *data,
|
||||
uint16_t length)
|
||||
{
|
||||
uint16_t index;
|
||||
if(data==(const uint8_t *)0 || length==0u || length>256u ||
|
||||
(((uint16_t)address&0xFFu)+length)>256u)return 0u;
|
||||
WriteEnable(); Select(); Xfer(W25_CMD_PROGRAM);
|
||||
Xfer((uint8_t)(address>>16)); Xfer((uint8_t)(address>>8));
|
||||
Xfer((uint8_t)address);
|
||||
for(index=0u;index<length;index++)Xfer(data[index]);
|
||||
Deselect();
|
||||
return Ready();
|
||||
}
|
||||
uint8_t W25Q64App_ClearUpgradeResult(void)
|
||||
{
|
||||
uint8_t zero=0u;
|
||||
WriteEnable(); Select(); Xfer(W25_CMD_PROGRAM); Xfer(0x03u); Xfer(0x00u); Xfer(0x10u); Xfer(zero); Deselect();
|
||||
return Ready();
|
||||
}
|
||||
+10
-3
@@ -18,6 +18,8 @@
|
||||
#include "nonvolatile_config.h"
|
||||
#include "bluetooth_manager.h"
|
||||
#include "watchdog.h"
|
||||
#include "interrupt_control.h"
|
||||
#include "ota_manager.h"
|
||||
|
||||
/**
|
||||
* @brief 固件主函数。
|
||||
@@ -32,15 +34,14 @@ int main(void)
|
||||
{
|
||||
Board_Init();
|
||||
SystemTick_Init();
|
||||
__asm
|
||||
rim
|
||||
__endasm;
|
||||
Interrupts_Enable();
|
||||
Uart1_Init();
|
||||
Current_Init();
|
||||
BusinessProtocol_Init();
|
||||
MeshFrame_Init();
|
||||
FaultManager_Init();
|
||||
BluetoothManager_Init();
|
||||
OtaManager_Init();
|
||||
Watchdog_Init();
|
||||
|
||||
while (1)
|
||||
@@ -57,6 +58,12 @@ int main(void)
|
||||
BluetoothManager_Task();
|
||||
}
|
||||
FaultManager_Task();
|
||||
/* 蓝牙完成初始化后再上报Bootloader升级结果,避免结果帧丢在AT阶段。 */
|
||||
if (BluetoothManager_IsReady() != 0u)
|
||||
{
|
||||
OtaManager_Task();
|
||||
}
|
||||
Board_StatusLedTask(OtaManager_IsActive());
|
||||
Watchdog_Refresh();
|
||||
}
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
* 2. 若不是 ROLE2,发送 ROLE2 和 RESET;
|
||||
* 3. 再次确认 ROLE;
|
||||
* 4. 查询 MADDR,保存 2 字节短地址到 RAM;
|
||||
* 5. 初始化完成后由 PC3 决定 LED3 常亮或 1 秒闪烁。
|
||||
* 5. 初始化完成后由 PC3 决定 PB5 蓝牙状态灯常亮或 1 秒闪烁。
|
||||
*
|
||||
* 初始化过程不使用阻塞延时。UART 返回内容只识别关键字符和超时,具体
|
||||
* AT 返回格式仍应在首次联调时用串口工具记录并校准。
|
||||
|
||||
@@ -11,14 +11,15 @@
|
||||
#include "mesh_frame.h"
|
||||
#include "fault_manager.h"
|
||||
#include "bluetooth_manager.h"
|
||||
#include "ota_manager.h"
|
||||
|
||||
/** 业务帧解析状态。 */
|
||||
typedef enum
|
||||
{
|
||||
/** 等待业务帧头第一个字节 0x55。 */
|
||||
PARSER_WAIT_HEAD_0 = 0,
|
||||
/** 已收到 0x55,等待帧头第二个字节 0xAA。 */
|
||||
PARSER_WAIT_HEAD_1,
|
||||
/** 等待发送方短地址高字节。 */
|
||||
PARSER_WAIT_SOURCE_HIGH = 0,
|
||||
/** 等待发送方短地址低字节。 */
|
||||
PARSER_WAIT_SOURCE_LOW,
|
||||
/** 等待命令字节。 */
|
||||
PARSER_WAIT_CMD,
|
||||
/** 等待 DATA 长度字节。 */
|
||||
@@ -35,6 +36,8 @@ typedef enum
|
||||
static BusinessParserState parser_state;
|
||||
/** 当前正在接收的命令。 */
|
||||
static uint8_t parser_command;
|
||||
/** 当前正在接收的发送方 Mesh 短地址。 */
|
||||
static uint16_t parser_source_address;
|
||||
/** 当前正在接收的数据长度。 */
|
||||
static uint8_t parser_length;
|
||||
/** 当前已经接收的数据字节数。 */
|
||||
@@ -43,10 +46,12 @@ static uint8_t parser_data_index;
|
||||
static uint8_t parser_data[BUSINESS_MAX_DATA_LENGTH];
|
||||
/** 当前正在接收的 CRC。 */
|
||||
static uint16_t parser_received_crc;
|
||||
/** CRC 计算临时区,依次保存 CMD、LEN 和 DATA。 */
|
||||
static uint8_t parser_crc_input[2u + BUSINESS_MAX_DATA_LENGTH];
|
||||
/** CRC 计算临时区,依次保存 SRC、CMD、LEN 和 DATA。 */
|
||||
static uint8_t parser_crc_input[4u + BUSINESS_MAX_DATA_LENGTH];
|
||||
/** 最近一帧合法业务帧的内容。 */
|
||||
static uint8_t last_command;
|
||||
/** 最近一帧合法业务帧的发送方短地址。 */
|
||||
static uint16_t last_source_address;
|
||||
/** 最近一帧合法业务帧的 DATA 长度。 */
|
||||
static uint8_t last_length;
|
||||
/** 最近一帧合法业务帧的 DATA 内容。 */
|
||||
@@ -63,7 +68,7 @@ static void BusinessProtocol_SendByteResponse(uint16_t address,
|
||||
/** 初始化业务协议状态机;完整解析器在第二阶段实现。 */
|
||||
void BusinessProtocol_Init(void)
|
||||
{
|
||||
parser_state = PARSER_WAIT_HEAD_0;
|
||||
parser_state = PARSER_WAIT_SOURCE_HIGH;
|
||||
parser_command = 0u;
|
||||
parser_length = 0u;
|
||||
parser_data_index = 0u;
|
||||
@@ -88,9 +93,9 @@ void BusinessProtocol_Task(void)
|
||||
/**
|
||||
* 向业务帧状态机输入一个字节。
|
||||
*
|
||||
* 帧格式为 `55 AA CMD LEN DATA CRC16`。CRC 计算范围为 CMD、LEN 和
|
||||
* DATA;线上 CRC 低字节在前。CRC 错误会恢复到等待帧头状态,不执行
|
||||
* 任何控制动作。
|
||||
* 帧格式为 `SRC_H SRC_L CMD LEN DATA CRC16`。CRC 计算范围为 SRC、CMD、
|
||||
* LEN 和 DATA;线上 CRC 低字节在前。CRC 错误会恢复到等待地址状态,
|
||||
* 不执行任何控制动作。
|
||||
*/
|
||||
uint8_t BusinessProtocol_InputByte(uint8_t value)
|
||||
{
|
||||
@@ -99,16 +104,14 @@ uint8_t BusinessProtocol_InputByte(uint8_t value)
|
||||
|
||||
switch (parser_state)
|
||||
{
|
||||
case PARSER_WAIT_HEAD_0:
|
||||
if (value == BUSINESS_FRAME_HEAD_0)
|
||||
{
|
||||
parser_state = PARSER_WAIT_HEAD_1;
|
||||
}
|
||||
case PARSER_WAIT_SOURCE_HIGH:
|
||||
parser_source_address = (uint16_t)value << 8u;
|
||||
parser_state = PARSER_WAIT_SOURCE_LOW;
|
||||
break;
|
||||
|
||||
case PARSER_WAIT_HEAD_1:
|
||||
parser_state = (value == BUSINESS_FRAME_HEAD_1) ?
|
||||
PARSER_WAIT_CMD : PARSER_WAIT_HEAD_0;
|
||||
case PARSER_WAIT_SOURCE_LOW:
|
||||
parser_source_address |= value;
|
||||
parser_state = PARSER_WAIT_CMD;
|
||||
break;
|
||||
|
||||
case PARSER_WAIT_CMD:
|
||||
@@ -121,7 +124,7 @@ uint8_t BusinessProtocol_InputByte(uint8_t value)
|
||||
parser_data_index = 0u;
|
||||
if (parser_length > BUSINESS_MAX_DATA_LENGTH)
|
||||
{
|
||||
parser_state = PARSER_WAIT_HEAD_0;
|
||||
parser_state = PARSER_WAIT_SOURCE_HIGH;
|
||||
}
|
||||
else if (parser_length == 0u)
|
||||
{
|
||||
@@ -148,21 +151,24 @@ uint8_t BusinessProtocol_InputByte(uint8_t value)
|
||||
|
||||
case PARSER_WAIT_CRC_HIGH:
|
||||
parser_received_crc |= (uint16_t)value << 8u;
|
||||
parser_crc_input[0] = parser_command;
|
||||
parser_crc_input[1] = parser_length;
|
||||
parser_crc_input[0] = (uint8_t)(parser_source_address >> 8u);
|
||||
parser_crc_input[1] = (uint8_t)parser_source_address;
|
||||
parser_crc_input[2] = parser_command;
|
||||
parser_crc_input[3] = parser_length;
|
||||
for (index = 0u; index < parser_length; ++index)
|
||||
{
|
||||
parser_crc_input[2u + index] = parser_data[index];
|
||||
parser_crc_input[4u + index] = parser_data[index];
|
||||
}
|
||||
expected_crc = BusinessProtocol_Crc16Modbus(parser_crc_input,
|
||||
(uint16_t)(2u + parser_length));
|
||||
parser_state = PARSER_WAIT_HEAD_0;
|
||||
(uint16_t)(4u + parser_length));
|
||||
parser_state = PARSER_WAIT_SOURCE_HIGH;
|
||||
if (expected_crc != parser_received_crc)
|
||||
{
|
||||
return 0u;
|
||||
}
|
||||
|
||||
last_command = parser_command;
|
||||
last_source_address = parser_source_address;
|
||||
last_length = parser_length;
|
||||
for (index = 0u; index < last_length; ++index)
|
||||
{
|
||||
@@ -171,7 +177,7 @@ uint8_t BusinessProtocol_InputByte(uint8_t value)
|
||||
return 1u;
|
||||
|
||||
default:
|
||||
parser_state = PARSER_WAIT_HEAD_0;
|
||||
parser_state = PARSER_WAIT_SOURCE_HIGH;
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -201,6 +207,12 @@ uint8_t BusinessProtocol_GetDataByte(uint8_t index, uint8_t *value)
|
||||
return 1u;
|
||||
}
|
||||
|
||||
/** 读取最近一帧合法业务帧携带的发送方 Mesh 短地址。 */
|
||||
uint16_t BusinessProtocol_GetSourceAddress(void)
|
||||
{
|
||||
return last_source_address;
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行当前合法业务帧。
|
||||
*
|
||||
@@ -208,8 +220,9 @@ uint8_t BusinessProtocol_GetDataByte(uint8_t index, uint8_t *value)
|
||||
* 和实际电流查询。控制命令只有 DATA=00/01 才会改变 PA1;其他参数只返
|
||||
* 回错误应答,不会误动作。应答目标使用接收帧中的源短地址。
|
||||
*/
|
||||
void BusinessProtocol_HandleFrame(uint16_t source_address)
|
||||
void BusinessProtocol_HandleFrame(void)
|
||||
{
|
||||
uint16_t source_address = last_source_address;
|
||||
uint8_t value;
|
||||
uint8_t current_data[2u];
|
||||
uint16_t current;
|
||||
@@ -225,6 +238,7 @@ void BusinessProtocol_HandleFrame(uint16_t source_address)
|
||||
break;
|
||||
}
|
||||
Board_SetRelay(last_data[0]);
|
||||
Board_SetRelayLed(Board_GetRelay());
|
||||
BusinessProtocol_SendByteResponse(source_address,
|
||||
CMD_RELAY_CONTROL | CMD_RESPONSE_FLAG,
|
||||
Board_GetRelay());
|
||||
@@ -257,6 +271,21 @@ void BusinessProtocol_HandleFrame(uint16_t source_address)
|
||||
current_data, 2u);
|
||||
break;
|
||||
|
||||
case CMD_FIRMWARE_VERSION_QUERY:
|
||||
if (last_length != 0u)
|
||||
{
|
||||
value = 0x02u;
|
||||
BusinessProtocol_SendByteResponse(source_address,
|
||||
CMD_ERROR_RESPONSE, value);
|
||||
break;
|
||||
}
|
||||
current_data[0] = (uint8_t)FIRMWARE_VERSION;
|
||||
current_data[1] = (uint8_t)(FIRMWARE_VERSION >> 8u);
|
||||
(void)MeshFrame_SendBusiness(source_address,
|
||||
CMD_FIRMWARE_VERSION_RESPONSE,
|
||||
current_data, 2u);
|
||||
break;
|
||||
|
||||
case (CMD_FAULT_REPORT | CMD_RESPONSE_FLAG):
|
||||
if (last_length == 1u)
|
||||
{
|
||||
@@ -264,6 +293,42 @@ void BusinessProtocol_HandleFrame(uint16_t source_address)
|
||||
}
|
||||
break;
|
||||
|
||||
case CMD_OTA_RESULT_ACK:
|
||||
if (last_length == 1u)
|
||||
{
|
||||
OtaManager_AcknowledgeBootloaderResult();
|
||||
}
|
||||
break;
|
||||
|
||||
case 0x08u:
|
||||
case 0x09u:
|
||||
case 0x0Au:
|
||||
case 0x0Bu:
|
||||
case 0x0Cu:
|
||||
{
|
||||
uint8_t ota_status = OtaManager_HandleCommand(last_command,
|
||||
last_data,
|
||||
last_length) ? 0x00u : 0x01u;
|
||||
uint8_t ota_response[3u];
|
||||
uint16_t next_offset = 0u;
|
||||
uint8_t response_command = (uint8_t)(last_command | CMD_RESPONSE_FLAG);
|
||||
if (last_command == 0x09u)
|
||||
{
|
||||
(void)OtaManager_GetExpectedOffset(&next_offset);
|
||||
ota_response[0] = ota_status;
|
||||
ota_response[1] = (uint8_t)(next_offset >> 8u);
|
||||
ota_response[2] = (uint8_t)next_offset;
|
||||
(void)MeshFrame_SendBusiness(source_address, response_command,
|
||||
ota_response, 3u);
|
||||
}
|
||||
else
|
||||
{
|
||||
BusinessProtocol_SendByteResponse(source_address,
|
||||
response_command, ota_status);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case CMD_THRESHOLD_CONFIG:
|
||||
if (last_length != 2u)
|
||||
{
|
||||
|
||||
+30
-28
@@ -3,10 +3,12 @@
|
||||
* @brief JDY-28M Mesh 外层帧实现。
|
||||
*
|
||||
* 发送格式:41 54 2B AA A1 | 目标地址高低字节 | A0 | 业务帧 | 0D 0A。
|
||||
* 接收格式:F1 DD 09 00 0A | 源地址高低字节 | 业务帧 | 0D 0A。
|
||||
* 接收格式:F1 DD 09 00 0A | 目标地址高低字节 | 业务帧 | 0D 0A。
|
||||
* 发送方地址由业务帧 SRC_H/SRC_L 携带;JDY-28M 外层接收地址不是发送方地址。
|
||||
*/
|
||||
#include <stdint.h>
|
||||
#include "business_protocol.h"
|
||||
#include "bluetooth_manager.h"
|
||||
#include "mesh_frame.h"
|
||||
#include "uart1.h"
|
||||
|
||||
@@ -37,10 +39,10 @@ typedef enum
|
||||
MESH_WAIT_PREFIX_3,
|
||||
/** 等待固定头第五字节 0A。 */
|
||||
MESH_WAIT_PREFIX_4,
|
||||
/** 接收源地址高字节。 */
|
||||
MESH_READ_SOURCE_HIGH,
|
||||
/** 接收源地址低字节。 */
|
||||
MESH_READ_SOURCE_LOW,
|
||||
/** 接收外层目标地址高字节。 */
|
||||
MESH_READ_TARGET_HIGH,
|
||||
/** 接收外层目标地址低字节。 */
|
||||
MESH_READ_TARGET_LOW,
|
||||
/** 接收内层业务帧。 */
|
||||
MESH_READ_BUSINESS,
|
||||
/** 等待外层帧尾 0D。 */
|
||||
@@ -51,8 +53,8 @@ typedef enum
|
||||
|
||||
/** JDY-28M 外层接收状态机当前状态。 */
|
||||
static MeshParserState mesh_state;
|
||||
/** 最近一帧合法外层数据携带的源短地址。 */
|
||||
static uint16_t last_source_address;
|
||||
/** 最近一帧合法外层数据携带的目标短地址,仅用于诊断。 */
|
||||
static uint16_t last_target_address;
|
||||
/** 当前正在接收的业务帧缓冲区。 */
|
||||
static uint8_t business_buffer[BUSINESS_MAX_DATA_LENGTH + 6u];
|
||||
/** 当前已经接收的业务帧字节数。 */
|
||||
@@ -71,7 +73,7 @@ static void MeshFrame_ResetParser(void)
|
||||
/** 初始化外层帧解析器。 */
|
||||
void MeshFrame_Init(void)
|
||||
{
|
||||
last_source_address = 0u;
|
||||
last_target_address = 0u;
|
||||
MeshFrame_ResetParser();
|
||||
}
|
||||
|
||||
@@ -100,14 +102,14 @@ static void MeshFrame_InputByte(uint8_t value)
|
||||
mesh_state = (value == RX_PREFIX_3) ? MESH_WAIT_PREFIX_4 : MESH_WAIT_PREFIX_0;
|
||||
break;
|
||||
case MESH_WAIT_PREFIX_4:
|
||||
mesh_state = (value == RX_PREFIX_4) ? MESH_READ_SOURCE_HIGH : MESH_WAIT_PREFIX_0;
|
||||
mesh_state = (value == RX_PREFIX_4) ? MESH_READ_TARGET_HIGH : MESH_WAIT_PREFIX_0;
|
||||
break;
|
||||
case MESH_READ_SOURCE_HIGH:
|
||||
last_source_address = (uint16_t)value << 8u;
|
||||
mesh_state = MESH_READ_SOURCE_LOW;
|
||||
case MESH_READ_TARGET_HIGH:
|
||||
last_target_address = (uint16_t)value << 8u;
|
||||
mesh_state = MESH_READ_TARGET_LOW;
|
||||
break;
|
||||
case MESH_READ_SOURCE_LOW:
|
||||
last_source_address |= value;
|
||||
case MESH_READ_TARGET_LOW:
|
||||
last_target_address |= value;
|
||||
business_length = 0u;
|
||||
mesh_state = MESH_READ_BUSINESS;
|
||||
break;
|
||||
@@ -120,11 +122,6 @@ static void MeshFrame_InputByte(uint8_t value)
|
||||
business_buffer[business_length++] = value;
|
||||
if (business_length >= 4u)
|
||||
{
|
||||
if ((business_buffer[0] != 0x55u) || (business_buffer[1] != 0xAAu))
|
||||
{
|
||||
MeshFrame_ResetParser();
|
||||
break;
|
||||
}
|
||||
expected_length = (uint8_t)(business_buffer[3] + 6u);
|
||||
business_expected_length = expected_length;
|
||||
if ((business_buffer[3] > BUSINESS_MAX_DATA_LENGTH) ||
|
||||
@@ -157,7 +154,7 @@ static void MeshFrame_InputByte(uint8_t value)
|
||||
}
|
||||
if (frame_ready != 0u)
|
||||
{
|
||||
BusinessProtocol_HandleFrame(last_source_address);
|
||||
BusinessProtocol_HandleFrame();
|
||||
}
|
||||
}
|
||||
MeshFrame_ResetParser();
|
||||
@@ -179,9 +176,9 @@ void MeshFrame_Task(void)
|
||||
}
|
||||
|
||||
/** 返回最近一帧合法外层数据的源地址。 */
|
||||
uint16_t MeshFrame_GetLastSourceAddress(void)
|
||||
uint16_t MeshFrame_GetLastTargetAddress(void)
|
||||
{
|
||||
return last_source_address;
|
||||
return last_target_address;
|
||||
}
|
||||
|
||||
/** 封装并发送一帧业务数据。 */
|
||||
@@ -190,7 +187,8 @@ uint8_t MeshFrame_SendBusiness(uint16_t target_address,
|
||||
const uint8_t *data,
|
||||
uint8_t length)
|
||||
{
|
||||
uint8_t crc_input[2u + BUSINESS_MAX_DATA_LENGTH];
|
||||
uint8_t crc_input[4u + BUSINESS_MAX_DATA_LENGTH];
|
||||
uint16_t source_address;
|
||||
uint16_t crc;
|
||||
uint8_t index;
|
||||
|
||||
@@ -199,10 +197,13 @@ uint8_t MeshFrame_SendBusiness(uint16_t target_address,
|
||||
{
|
||||
return 0u;
|
||||
}
|
||||
crc_input[0] = command;
|
||||
crc_input[1] = length;
|
||||
for (index = 0u; index < length; ++index) crc_input[2u + index] = data[index];
|
||||
crc = BusinessProtocol_Crc16Modbus(crc_input, (uint16_t)(2u + length));
|
||||
source_address = BluetoothManager_GetShortAddress();
|
||||
crc_input[0] = (uint8_t)(source_address >> 8u);
|
||||
crc_input[1] = (uint8_t)source_address;
|
||||
crc_input[2] = command;
|
||||
crc_input[3] = length;
|
||||
for (index = 0u; index < length; ++index) crc_input[4u + index] = data[index];
|
||||
crc = BusinessProtocol_Crc16Modbus(crc_input, (uint16_t)(4u + length));
|
||||
|
||||
Uart1_WriteByte(TX_PREFIX_0); Uart1_WriteByte(TX_PREFIX_1);
|
||||
Uart1_WriteByte(TX_PREFIX_2); Uart1_WriteByte(TX_PREFIX_3);
|
||||
@@ -210,7 +211,8 @@ uint8_t MeshFrame_SendBusiness(uint16_t target_address,
|
||||
Uart1_WriteByte((uint8_t)(target_address >> 8u));
|
||||
Uart1_WriteByte((uint8_t)target_address);
|
||||
Uart1_WriteByte(MESH_DATA_FLAG);
|
||||
Uart1_WriteByte(0x55u); Uart1_WriteByte(0xAAu);
|
||||
Uart1_WriteByte((uint8_t)(source_address >> 8u));
|
||||
Uart1_WriteByte((uint8_t)source_address);
|
||||
Uart1_WriteByte(command); Uart1_WriteByte(length);
|
||||
for (index = 0u; index < length; ++index) Uart1_WriteByte(data[index]);
|
||||
Uart1_WriteByte((uint8_t)crc); Uart1_WriteByte((uint8_t)(crc >> 8u));
|
||||
|
||||
@@ -0,0 +1,240 @@
|
||||
/**
|
||||
* @file ota_manager.c
|
||||
* @brief 应用程序侧 OTA 下载状态机。
|
||||
*
|
||||
* 重要职责边界:
|
||||
* - 本模块属于 Application,不属于 Bootloader;
|
||||
* - 本模块接收 Mesh OTA 数据并写入外部 W25Q64;
|
||||
* - 本模块不擦写 STM8 内部程序 Flash;
|
||||
* - 下载完成并完成整包 CRC16/MODBUS 校验后,才设置升级请求;
|
||||
* - 复位后由 Bootloader 读取升级请求并执行最终搬运。
|
||||
*
|
||||
* 当前代码先建立稳定的状态机和接口边界。W25Q64 的 SPI 底层驱动需要
|
||||
* 在实际硬件确认片选、SPI模式、时钟和读写时序后接入。
|
||||
*/
|
||||
#include "ota_manager.h"
|
||||
#include "w25q64_app.h"
|
||||
#include "mesh_frame.h"
|
||||
#include "business_protocol.h"
|
||||
|
||||
#define OTA_IMAGE_ADDRESS 0x020000ul
|
||||
#define OTA_METADATA_ADDRESS 0x030000ul
|
||||
|
||||
#define OTA_COMMAND_START 0x08u
|
||||
#define OTA_COMMAND_DATA 0x09u
|
||||
#define OTA_COMMAND_FINISH 0x0Au
|
||||
#define OTA_COMMAND_CANCEL 0x0Cu
|
||||
|
||||
#define OTA_STATE_IDLE 0u
|
||||
#define OTA_STATE_RECEIVING 1u
|
||||
#define OTA_STATE_READY 2u
|
||||
|
||||
/** OTA 状态机当前状态。 */
|
||||
static uint8_t ota_state;
|
||||
/** 已经写入外部 Flash 的固件字节数。 */
|
||||
static uint16_t ota_received_size;
|
||||
/** 主机声明的固件总长度。 */
|
||||
static uint16_t ota_expected_size;
|
||||
/** 主机声明的完整固件 CRC16/MODBUS。 */
|
||||
static uint16_t ota_expected_crc16;
|
||||
/** Bootloader升级结果缓存;实际值由W25Q64驱动读取后填充。 */
|
||||
static uint8_t ota_bootloader_result;
|
||||
/** 升级结果是否已经等待主程序上报。 */
|
||||
static uint8_t ota_bootloader_result_pending;
|
||||
/** 防止主循环每次重复发送同一条启动结果。 */
|
||||
static uint8_t ota_bootloader_result_reported;
|
||||
static uint16_t ota_expected_offset;
|
||||
static uint16_t ota_image_size;
|
||||
static uint16_t ota_image_version;
|
||||
static uint16_t ota_expected_crc16;
|
||||
|
||||
/**
|
||||
* @brief 初始化 OTA 状态机。
|
||||
*/
|
||||
void OtaManager_Init(void)
|
||||
{
|
||||
ota_state = OTA_STATE_IDLE;
|
||||
ota_received_size = 0u;
|
||||
ota_expected_size = 0u;
|
||||
ota_bootloader_result = 0u;
|
||||
ota_bootloader_result_pending = 0u;
|
||||
ota_bootloader_result_reported = 0u;
|
||||
ota_expected_offset = 0u;
|
||||
ota_image_size = 0u;
|
||||
ota_image_version = 0u;
|
||||
W25Q64App_Init();
|
||||
if (W25Q64App_ReadByte(0x030010ul, &ota_bootloader_result) != 0u &&
|
||||
ota_bootloader_result != 0u)
|
||||
{
|
||||
ota_bootloader_result_pending = 1u;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 周期性执行 OTA 状态机。
|
||||
*
|
||||
* 擦除、页编程和状态轮询都应拆成小步骤,以免触发5秒看门狗复位。
|
||||
*/
|
||||
void OtaManager_Task(void)
|
||||
{
|
||||
uint8_t result;
|
||||
if (ota_bootloader_result_pending != 0u &&
|
||||
ota_bootloader_result_reported == 0u)
|
||||
{
|
||||
result = ota_bootloader_result;
|
||||
if (MeshFrame_SendBusiness(0xFFFFu, CMD_OTA_RESULT_REPORT,
|
||||
&result, 1u) != 0u)
|
||||
{
|
||||
ota_bootloader_result_reported = 1u;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 处理应用程序侧 OTA 命令。
|
||||
* @param command OTA命令号。
|
||||
* @param data 命令数据指针。
|
||||
* @param length 命令数据长度。
|
||||
* @return 1表示命令格式被接受,0表示命令非法或当前状态不允许。
|
||||
*/
|
||||
uint8_t OtaManager_HandleCommand(uint8_t command,
|
||||
const uint8_t *data,
|
||||
uint8_t length)
|
||||
{
|
||||
if (command == OTA_COMMAND_START)
|
||||
{
|
||||
uint8_t sector;
|
||||
if (length != 4u)
|
||||
{
|
||||
return 0u;
|
||||
}
|
||||
ota_expected_size=(uint16_t)data[0]|((uint16_t)data[1]<<8);
|
||||
ota_image_version=(uint16_t)data[2]|((uint16_t)data[3]<<8);
|
||||
if(ota_expected_size==0u)
|
||||
return 0u;
|
||||
/* 先清除OTA元数据,避免上一次升级请求残留。 */
|
||||
if(W25Q64App_EraseSector(OTA_METADATA_ADDRESS)==0u)
|
||||
return 0u;
|
||||
for(sector=0u;sector<16u;sector++)
|
||||
if(W25Q64App_EraseSector(OTA_IMAGE_ADDRESS+((uint32_t)sector*0x1000ul))==0u)
|
||||
return 0u;
|
||||
ota_image_size=(uint16_t)ota_expected_size;
|
||||
ota_received_size=0u; ota_expected_offset=0u;
|
||||
ota_expected_crc16=0u;
|
||||
ota_state = OTA_STATE_RECEIVING;
|
||||
return 1u;
|
||||
}
|
||||
|
||||
if (command == OTA_COMMAND_DATA)
|
||||
{
|
||||
if (ota_state != OTA_STATE_RECEIVING || data == (const uint8_t *)0 ||
|
||||
(length != 3u && length != 4u))
|
||||
{
|
||||
return 0u;
|
||||
}
|
||||
{
|
||||
uint16_t offset=(uint16_t)((uint16_t)data[0]<<8)|data[1];
|
||||
uint8_t payload_length=(uint8_t)(length-2u);
|
||||
if(offset!=ota_expected_offset ||
|
||||
(uint16_t)(offset+payload_length)>ota_expected_size)return 0u;
|
||||
if(W25Q64App_PageProgram(OTA_IMAGE_ADDRESS+offset,&data[2],
|
||||
payload_length)==0u)
|
||||
return 0u;
|
||||
ota_expected_offset=(uint16_t)(offset+payload_length);
|
||||
ota_received_size=(uint16_t)(ota_received_size+payload_length);
|
||||
}
|
||||
return 1u;
|
||||
}
|
||||
|
||||
if (command == OTA_COMMAND_FINISH)
|
||||
{
|
||||
/* CMD=0x0A的DATA为完整固件CRC16低字节、高字节。 */
|
||||
if (ota_state != OTA_STATE_RECEIVING || data == (const uint8_t *)0 ||
|
||||
length != 2u)
|
||||
{
|
||||
return 0u;
|
||||
}
|
||||
|
||||
ota_expected_crc16 = (uint16_t)data[0] | ((uint16_t)data[1] << 8);
|
||||
ota_state = OTA_STATE_READY;
|
||||
return 1u;
|
||||
}
|
||||
|
||||
if (command == 0x0Bu)
|
||||
{
|
||||
uint8_t buffer[16]; uint16_t offset=0u; uint16_t crc=0xFFFFu;
|
||||
uint16_t chunk;
|
||||
uint8_t metadata[18];
|
||||
if(ota_state!=OTA_STATE_READY||ota_received_size!=ota_expected_size)
|
||||
return 0u;
|
||||
while(offset<ota_image_size){
|
||||
chunk=(uint16_t)(ota_image_size-offset); if(chunk>16u)chunk=16u;
|
||||
if(W25Q64App_Read(OTA_IMAGE_ADDRESS+offset,buffer,chunk)==0u)return 0u;
|
||||
{
|
||||
uint16_t index;
|
||||
for(index=0u;index<chunk;index++)
|
||||
{
|
||||
uint8_t bit; crc^=buffer[index];
|
||||
for(bit=0u;bit<8u;bit++)
|
||||
crc=(crc&1u)?(uint16_t)((crc>>1)^0xA001u):(uint16_t)(crc>>1);
|
||||
}
|
||||
}
|
||||
offset=(uint16_t)(offset+chunk);
|
||||
}
|
||||
if(crc!=ota_expected_crc16)return 0u;
|
||||
metadata[0]=0x31u; metadata[1]=0x41u; metadata[2]=0x54u; metadata[3]=0x4Fu;
|
||||
metadata[4]=(uint8_t)ota_image_size; metadata[5]=(uint8_t)(ota_image_size>>8);
|
||||
metadata[6]=0u; metadata[7]=0u;
|
||||
metadata[8]=(uint8_t)ota_image_version; metadata[9]=(uint8_t)(ota_image_version>>8);
|
||||
metadata[10]=(uint8_t)crc; metadata[11]=(uint8_t)(crc>>8);
|
||||
metadata[12]=0u; metadata[13]=0u; metadata[14]=0xA5u;
|
||||
metadata[15]=0xA5u; metadata[16]=0u; metadata[17]=0u;
|
||||
if(W25Q64App_PageProgram(OTA_METADATA_ADDRESS,metadata,16u)==0u)return 0u;
|
||||
if(W25Q64App_PageProgram(OTA_METADATA_ADDRESS+16ul,&metadata[16],2u)==0u)return 0u;
|
||||
ota_state=OTA_STATE_IDLE;
|
||||
return 1u;
|
||||
}
|
||||
|
||||
if (command == OTA_COMMAND_CANCEL)
|
||||
{
|
||||
ota_state = OTA_STATE_IDLE;
|
||||
ota_received_size = 0u;
|
||||
return 1u;
|
||||
}
|
||||
|
||||
return 0u;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 查询OTA是否正在进行。
|
||||
* @return 1表示已进入OTA下载或完成等待状态,0表示普通运行状态。
|
||||
*/
|
||||
uint8_t OtaManager_IsActive(void)
|
||||
{
|
||||
return (ota_state != OTA_STATE_IDLE) ? 1u : 0u;
|
||||
}
|
||||
|
||||
uint8_t OtaManager_GetExpectedOffset(uint16_t *offset)
|
||||
{
|
||||
if(offset==(uint16_t *)0)return 0u;
|
||||
*offset=ota_expected_offset; return 1u;
|
||||
}
|
||||
|
||||
uint8_t OtaManager_GetBootloaderResult(void)
|
||||
{
|
||||
/* W25Q64读取接口接入后,在此处加载result字段并置pending。 */
|
||||
return (ota_bootloader_result_pending != 0u) ? ota_bootloader_result : 0u;
|
||||
}
|
||||
|
||||
void OtaManager_AcknowledgeBootloaderResult(void)
|
||||
{
|
||||
if (ota_bootloader_result_pending != 0u)
|
||||
{
|
||||
if (W25Q64App_ClearUpgradeResult() != 0u)
|
||||
{
|
||||
ota_bootloader_result = 0u;
|
||||
ota_bootloader_result_pending = 0u;
|
||||
ota_bootloader_result_reported = 0u;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user