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
+45
View File
@@ -0,0 +1,45 @@
# STM8S003F3P6TR PB5独立LED测试工程。
# 本工程不包含Bootloader;测试主程序按0x8500链接,用于和汇编Bootloader合并。
PROJECT := pb5_led_test
BUILD_DIR := build
CC := sdcc
PACKIHX := packihx
CFLAGS := -mstm8 --std-c99 --opt-code-size -Iinclude
LDFLAGS := -mstm8 --out-fmt-ihx --code-loc 0x8000
APP_LDFLAGS := -mstm8 --out-fmt-ihx --code-loc 0x8500
OBJECT := $(BUILD_DIR)/main.rel
IHX := $(BUILD_DIR)/$(PROJECT).ihx
HEX := $(BUILD_DIR)/$(PROJECT).hex
APP_IHX := $(BUILD_DIR)/$(PROJECT)_app.ihx
APP_HEX := $(BUILD_DIR)/$(PROJECT)_app.hex
.PHONY: all app clean
all: app
# 生成供Bootloader跳转测试使用的主程序镜像,链接地址必须为0x8500。
app: $(APP_HEX)
$(APP_HEX): $(APP_IHX)
$(PACKIHX) $< > $@
$(APP_IHX): $(OBJECT)
@mkdir -p $(BUILD_DIR)
$(CC) $(APP_LDFLAGS) -o $@ $<
$(HEX): $(IHX)
$(PACKIHX) $< > $@
$(IHX): $(OBJECT)
@mkdir -p $(BUILD_DIR)
$(CC) $(LDFLAGS) -o $@ $<
$(OBJECT): src/main.c
@mkdir -p $(BUILD_DIR)
$(CC) $(CFLAGS) -c $< -o $@
clean:
rm -rf $(BUILD_DIR)
+66
View File
@@ -0,0 +1,66 @@
/**
* @file stm8_test_registers.h
* @brief PB5 LED测试工程使用的最小GPIO寄存器定义。
*/
#ifndef STM8_TEST_REGISTERS_H
#define STM8_TEST_REGISTERS_H
#include <stdint.h>
/** GPIOA输出数据寄存器地址。 */
#define GPIOA_ODR_ADDR 0x5000u
/** GPIOA数据方向寄存器地址。 */
#define GPIOA_DDR_ADDR 0x5002u
/** GPIOA控制寄存器1地址。 */
#define GPIOA_CR1_ADDR 0x5003u
/** GPIOA控制寄存器2地址。 */
#define GPIOA_CR2_ADDR 0x5004u
/** GPIOB输出数据寄存器地址。 */
#define GPIOB_ODR_ADDR 0x5005u
/** GPIOB数据方向寄存器地址。 */
#define GPIOB_DDR_ADDR 0x5007u
/** GPIOB控制寄存器1地址,置1表示推挽输出。 */
#define GPIOB_CR1_ADDR 0x5008u
/** GPIOB控制寄存器2地址,置0表示低速输出。 */
#define GPIOB_CR2_ADDR 0x5009u
/** GPIOC输出数据寄存器地址。 */
#define GPIOC_ODR_ADDR 0x500Au
/** GPIOC数据方向寄存器地址。 */
#define GPIOC_DDR_ADDR 0x500Cu
/** GPIOC控制寄存器1地址。 */
#define GPIOC_CR1_ADDR 0x500Du
/** GPIOC控制寄存器2地址。 */
#define GPIOC_CR2_ADDR 0x500Eu
/** GPIOD输出数据寄存器地址。 */
#define GPIOD_ODR_ADDR 0x500Fu
/** GPIOD数据方向寄存器地址。 */
#define GPIOD_DDR_ADDR 0x5011u
/** GPIOD控制寄存器1地址。 */
#define GPIOD_CR1_ADDR 0x5012u
/** GPIOD控制寄存器2地址。 */
#define GPIOD_CR2_ADDR 0x5013u
/** 时钟分频寄存器地址。 */
#define CLK_CKDIVR_ADDR 0x50C6u
/** TIM4控制寄存器1地址。 */
#define TIM4_CR1_ADDR 0x5340u
/** TIM4中断使能寄存器地址。 */
#define TIM4_IER_ADDR 0x5343u
/** TIM4状态寄存器1地址。 */
#define TIM4_SR1_ADDR 0x5344u
/** TIM4预分频寄存器地址。 */
#define TIM4_PSCR_ADDR 0x5347u
/** TIM4自动重装载寄存器地址。 */
#define TIM4_ARR_ADDR 0x5348u
/** TIM4计数器地址。 */
#define TIM4_CNTR_ADDR 0x5346u
/** TIM4事件产生寄存器地址。 */
#define TIM4_EGR_ADDR 0x5345u
/** PB5对应的位掩码。 */
#define PB5_MASK 0x20u
/** 通过地址访问8位STM8外设寄存器。 */
#define REG8(address) (*((volatile uint8_t *)(address)))
#endif
+76
View File
@@ -0,0 +1,76 @@
/**
* @file main.c
* @brief STM8S003F3P6TR PB5独立硬件测试程序。
*
* 本测试程序只把PB5配置为推挽输出,并使用TIM4中断以约500ms周期翻转。
* 程序不初始化UART、SPI、ADC、蓝牙或看门狗,用于验证应用中断向量。
*
* 当前测试工程直接从0x8000链接,烧录后不依赖原项目的Bootloader。
*/
#include <stdint.h>
#include "stm8_test_registers.h"
/**
* 产生约500ms的软件延时。
*
* 该延时用于LED肉眼观察,不作为正式产品的精确定时基准。实际周期会
* 受到MCU主频、编译器优化和芯片时钟配置影响,但足以判断LED是否翻转。
*/
static volatile uint16_t tim4_count;
/**
* TIM4更新中断服务函数。
*
* SDCC会根据IRQ23把该函数放入应用程序向量槽0x8564。每次中断先清除
* TIM4更新标志,再累计1ms节拍;累计500次后直接翻转PB5,避免引入其他
* 模块,从而只验证Bootloader到应用程序的TIM4中断链路。
*/
void Test_TIM4_ISR(void) __interrupt(23)
{
REG8(TIM4_SR1_ADDR) = 0x00u;
++tim4_count;
if (tim4_count >= 500u)
{
tim4_count = 0u;
REG8(GPIOB_ODR_ADDR) ^= PB5_MASK;
}
}
/** 初始化TIM4为约1ms更新事件:16MHz / 128 / 125 = 1kHz。 */
static void Test_TIM4_Init(void)
{
REG8(CLK_CKDIVR_ADDR) = 0x00u;
REG8(TIM4_CR1_ADDR) = 0x00u;
REG8(TIM4_IER_ADDR) = 0x00u;
REG8(TIM4_PSCR_ADDR) = 0x07u;
REG8(TIM4_ARR_ADDR) = 124u;
REG8(TIM4_CNTR_ADDR) = 0u;
REG8(TIM4_EGR_ADDR) = 0x01u;
REG8(TIM4_SR1_ADDR) = 0x00u;
/* 配置完成后允许TIM4更新中断。 */
REG8(TIM4_IER_ADDR) = 0x01u;
REG8(TIM4_CR1_ADDR) = 0x01u;
}
/**
* 固件入口函数。
*
* PB5采用推挽输出,初始电平为低电平,TIM4中断累计500次后翻转一次。
*/
int main(void)
{
/* 先写入低电平,再切换为推挽输出,减少上电瞬间的错误脉冲。 */
REG8(GPIOB_ODR_ADDR) &= (uint8_t)~PB5_MASK;
REG8(GPIOB_DDR_ADDR) |= PB5_MASK;
REG8(GPIOB_CR1_ADDR) |= PB5_MASK;
REG8(GPIOB_CR2_ADDR) &= (uint8_t)~PB5_MASK;
tim4_count = 0u;
Test_TIM4_Init();
__asm__("rim");
while (1)
{
/* PB5翻转全部由TIM4 ISR完成,主循环保持空闲。 */
}
}