feat: complete assembly mesh relay control path

- synchronize STM8 application and external flash layout updates\n- add assembly bootloader and assembly application test projects\n- add UART1 interrupt receive ring buffer\n- parse JDY-28M Mesh outer frames and source addresses\n- add CRC-16/MODBUS validation\n- implement relay control command 0x01 and response 0x81\n- support unicast and broadcast relay control frames\n- update host protocol, OTA documents, and serial tools
This commit is contained in:
十七
2026-11-05 16:12:51 +08:00
parent df1aad3d16
commit ebef6a8694
40 changed files with 2133 additions and 307 deletions
+1 -1
View File
@@ -6,7 +6,7 @@
```text
0x8000 ~ 0x83FF Bootloader,1KB
0x8400 ~ 0x9FFF Application,7KB
0x8500 ~ 0x9FFF Application,6.75KB
```
当前版本完成的基础功能:
+2 -2
View File
@@ -10,9 +10,9 @@
/** Bootloader 在 STM8 程序 Flash 中的起始地址。 */
#define BOOTLOADER_START_ADDRESS 0x8000u
/** Bootloader 预留空间,当前规划为 1KB。 */
#define BOOTLOADER_SIZE_BYTES 0x0800u
#define BOOTLOADER_SIZE_BYTES 0x0500u
/** 应用程序固定入口地址,必须与应用工程链接地址一致。 */
#define APPLICATION_START_ADDRESS 0x8800u
#define APPLICATION_START_ADDRESS 0x8500u
/** STM8S003F3P6 程序 Flash 的最后一个地址。 */
#define APPLICATION_END_ADDRESS 0x9FFFu
/** 应用程序允许使用的最大空间。 */
+1 -1
View File
@@ -8,7 +8,7 @@
#include <stdint.h>
/** 初始化STM8硬件SPI和W25Q64片选;当前片选引脚为PD4。 */
void W25Q64_Init(void);
uint8_t W25Q64_Init(void);
/** 读取W25Q64 JEDEC ID,便于启动时确认芯片存在。 */
uint32_t W25Q64_ReadJedecId(void);
/** 从W25Q64连续读取数据。 */
+13 -1
View File
@@ -391,7 +391,6 @@ void Bootloader_Init(void)
{
Bootloader_WatchdogInit();
Bootloader_ButtonInit();
W25Q64_Init();
}
/**
@@ -491,6 +490,19 @@ void Bootloader_Task(void)
uint8_t upgrade_requested = 0u;
uint8_t upgrade_success = 0u;
uint8_t factory_button_held;
uint8_t flash_ready;
flash_ready = W25Q64_Init();
if (flash_ready == 0u)
{
/* 外部Flash故障不能阻止已有主程序启动;只有应用也无效时,
* 才停止喂狗并等待硬件复位。 */
if (Bootloader_IsApplicationValid() != 0u)
{
Bootloader_JumpToApplication();
}
while (1) {}
}
factory_button_held = Bootloader_IsFactoryButtonHeld();
if (factory_button_held != 0u)
+24
View File
@@ -4,6 +4,30 @@
*/
#include "bootloader.h"
/**
* @brief Bootloader固定TIM4向量的转发入口。
*
* STM8S003F3的TIM4 IRQ23固定位于0x8064。Bootloader占用0x8000区域后,
* 应用程序位于0x8500,硬件不会自动改用应用程序向量表。因此这里将
* Bootloader的TIM4中断请求转发到应用程序TIM4向量槽0x8564。
*/
void Bootloader_TIM4_Vector(void) __interrupt(23)
{
/* 使用项目此前已经完成实物验证的普通中断转发方式。 */
__asm__("jpf 0x8564");
}
/**
* @brief Bootloader固定UART1接收向量的转发入口。
*
* UART1接收为IRQ18,固定硬件向量地址为0x8050。主程序对应向量槽为
* 0x8550,转发后再由主程序向量进入实际UART接收ISR。
*/
void Bootloader_UART1_RX_Vector(void) __interrupt(18)
{
__asm__("jpf 0x8550");
}
/**
* @brief Bootloader 入口函数。
* @return 理论上不会返回。
+12 -3
View File
@@ -41,9 +41,13 @@ static void W25Q64_Deselect(void)
*/
static uint8_t W25Q64_Transfer(uint8_t value)
{
while ((SPI1_SR & SPI_SR_TXE) == 0u) {}
uint16_t timeout = 60000u;
while ((SPI1_SR & SPI_SR_TXE) == 0u && timeout-- != 0u) {}
if (timeout == 0u) return 0xFFu;
SPI1_DR = value;
while ((SPI1_SR & SPI_SR_RXNE) == 0u) {}
timeout = 60000u;
while ((SPI1_SR & SPI_SR_RXNE) == 0u && timeout-- != 0u) {}
if (timeout == 0u) return 0xFFu;
return SPI1_DR;
}
@@ -93,7 +97,7 @@ static uint8_t W25Q64_WaitReady(void)
* SPI采用主机模式、模式0、MSB first,并使用较低初始时钟,降低上电阶段
* 外部Flash尚未稳定时出现通信错误的概率。
*/
void W25Q64_Init(void)
uint8_t W25Q64_Init(void)
{
/* PC5、PC6配置为推挽输出;PC7配置为输入;PD4配置为CS#输出。 */
GPIOC_DDR |= 0x60u;
@@ -106,6 +110,11 @@ void W25Q64_Init(void)
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);
if (W25Q64_ReadJedecId() != 0xEF4014ul)
{
return 0u;
}
return 1u;
}
/**