STM32嵌入式软件设计的完整流程

📋 完整设计流程图

需求分析 系统设计 硬件接口设计 软件架构设计 详细设计 编码实现 单元测试 集成测试 系统测试 文档编写 发布交付


🎯 第一阶段:需求分析与规划

步骤1:需求分析

目标:明确项目要做什么

输出物

  • 需求规格说明书
  • 功能需求列表
  • 性能指标要求

内容包括

  • ✅ 功能需求(做什么功能)
  • ✅ 性能需求(速度、精度、实时性)
  • ✅ 接口需求(与外部系统的接口)
  • ✅ 约束条件(成本、功耗、尺寸)
  • ✅ 可靠性需求(MTBF、容错能力)

示例

复制代码
项目:智能温控系统
功能需求:
  - 实时采集温湿度(每秒1次)
  - LCD显示当前温湿度
  - 温度超过30℃时启动风扇
  - 支持按键设置目标温度
  - 数据通过UART上传到上位机

性能需求:
  - 温度测量精度:±0.5℃
  - 响应时间:<100ms
  - 工作温度:-10℃~60℃

接口需求:
  - DHT22传感器(GPIO)
  - LCD1602显示屏(I2C)
  - 继电器控制(GPIO)
  - UART通信(115200bps)

步骤2:可行性分析

目标:确认技术方案可行

内容

  • 芯片选型(STM32F103? STM32F407?)
  • 外设资源评估(GPIO、定时器、通信接口是否够用)
  • 存储资源评估(Flash、RAM是否足够)
  • 功耗分析
  • 成本预算

芯片选型表

型号 Flash RAM 主频 外设 价格 是否满足
STM32F103C8T6 64KB 20KB 72MHz 2xUART, I2C, SPI ¥8
STM32F407VGT6 1MB 192KB 168MHz 多路UART, CAN ¥35 ✅ 性能过剩

🏗️ 第二阶段:系统设计

步骤3:系统架构设计

目标:设计整体架构和分层

输出物

  • 系统架构图
  • 分层设计文档
  • 模块划分方案

分层架构

层次 职责 模块示例
应用层 业务逻辑 app_main, app_temp_control, app_display
中间件层 通用服务 FreeRTOS, 协议解析, 数据处理算法
驱动层 设备驱动 drv_dht22, drv_lcd, drv_uart, drv_relay
BSP层 板级初始化 bsp_clock, bsp_gpio, bsp_init
HAL层 硬件抽象 STM32 HAL库

步骤4:硬件接口设计

目标:明确软硬件接口

输出物

  • 引脚分配表
  • 接口时序图
  • 硬件接口说明文档

引脚分配表示例

功能 引脚 配置 说明
DHT22数据 PA0 GPIO输入/输出 单总线协议
LCD_SDA PB7 I2C_SDA I2C从设备地址0x27
LCD_SCL PB6 I2C_SCL I2C速率100KHz
继电器控制 PA5 GPIO输出 高电平闭合
UART_TX PA9 USART1_TX 115200,8,N,1
UART_RX PA10 USART1_RX 115200,8,N,1
按键1 PC13 GPIO输入+上拉 低电平有效

步骤5:模块划分设计

目标:将系统分解为独立模块

输出物

  • 模块列表
  • 模块职责说明
  • 模块依赖关系图

模块划分示例

复制代码
应用层模块:
├── app_main.c          # 主程序入口
├── app_sensor.c        # 传感器数据采集任务
├── app_display.c       # 显示任务
├── app_control.c       # 温控逻辑任务
└── app_communication.c # 串口通信任务

驱动层模块:
├── drv_dht22.c         # DHT22温湿度传感器驱动
├── drv_lcd1602.c       # LCD1602显示驱动
├── drv_uart.c          # UART驱动封装
└── drv_relay.c         # 继电器驱动

工具模块:
├── util_ringbuffer.c   # 环形缓冲区
├── util_crc.c          # CRC校验
└── util_delay.c        # 延时函数

模块依赖关系图
app_main app_sensor app_display app_control app_communication drv_dht22 drv_lcd1602 drv_relay drv_uart HAL_GPIO HAL_I2C HAL_UART


📐 第三阶段:详细设计

步骤6:模块详细设计

目标:设计每个模块的内部实现

输出物

  • 模块设计文档
  • 接口定义(.h文件)
  • 内部流程图
  • 状态机图

模块设计文档模板

DHT22驱动模块设计

1. 模块概述

负责DHT22温湿度传感器的数据读取

2. 依赖关系

  • 依赖:HAL_GPIO, HAL_Delay
  • 被依赖:app_sensor

3. 接口定义

c 复制代码
// 初始化
DHT22_Status_t DHT22_Init(void);

// 读取数据
DHT22_Status_t DHT22_Read(float *temp, float *humi);

// 数据结构
typedef struct {
    float temperature;
    float humidity;
    uint32_t timestamp;
} DHT22_Data_t;

4. 内部流程

流程图

5. 时序要求

  • 起始信号:低电平18ms
  • 等待响应:80us低 + 80us高
  • 数据位:50us低 + 26~70us高

内部流程图示例
是 否 否 是 DHT22_Read开始 发送起始信号 等待DHT22响应 响应超时? 返回ERROR 读取40位数据 校验和验证 校验通过? 解析温湿度 返回OK


步骤7:接口设计(API设计)

目标:定义模块对外接口

设计原则

  • ✅ 接口简洁明了
  • ✅ 参数校验完整
  • ✅ 返回值统一(状态码)
  • ✅ 使用结构体传递复杂数据
  • ✅ 避免全局变量

接口设计示例

c 复制代码
/******************************************************************************
 * 文件:drv_dht22.h
 * 描述:DHT22温湿度传感器驱动接口
 ******************************************************************************/

#ifndef __DRV_DHT22_H
#define __DRV_DHT22_H

#include <stdint.h>

/* 返回状态码 */
typedef enum {
    DHT22_OK = 0,           // 成功
    DHT22_ERROR = -1,       // 一般错误
    DHT22_TIMEOUT = -2,     // 超时
    DHT22_CHECKSUM = -3,    // 校验和错误
    DHT22_NOT_INIT = -4     // 未初始化
} DHT22_Status_t;

/* 数据结构 */
typedef struct {
    float temperature;      // 温度,单位:℃
    float humidity;         // 湿度,单位:%RH
    uint32_t timestamp;     // 时间戳,单位:ms
} DHT22_Data_t;

/* 配置结构 */
typedef struct {
    GPIO_TypeDef *port;     // GPIO端口
    uint16_t pin;           // GPIO引脚
    uint32_t timeout_ms;    // 超时时间
} DHT22_Config_t;

/******************************************************************************
 * 函数名:DHT22_Init
 * 描述:  初始化DHT22传感器
 * 参数:  config - 配置参数
 * 返回:  DHT22_OK - 成功
 *         DHT22_ERROR - 失败
 ******************************************************************************/
DHT22_Status_t DHT22_Init(DHT22_Config_t *config);

/******************************************************************************
 * 函数名:DHT22_Read
 * 描述:  读取温湿度数据
 * 参数:  data - 输出数据指针
 * 返回:  DHT22_OK - 成功
 *         DHT22_TIMEOUT - 超时
 *         DHT22_CHECKSUM - 校验错误
 * 注意:  两次读取间隔应大于2秒
 ******************************************************************************/
DHT22_Status_t DHT22_Read(DHT22_Data_t *data);

/******************************************************************************
 * 函数名:DHT22_DeInit
 * 描述:  反初始化
 * 返回:  DHT22_OK - 成功
 ******************************************************************************/
DHT22_Status_t DHT22_DeInit(void);

#endif /* __DRV_DHT22_H */

步骤8:数据结构设计

目标:设计模块使用的数据结构

常用数据结构

  • 配置结构体
  • 数据结构体
  • 状态枚举
  • 错误码枚举
  • 回调函数类型

示例

c 复制代码
/* 传感器配置 */
typedef struct {
    uint8_t sample_rate;    // 采样率 Hz
    uint8_t filter_enable;  // 滤波使能
    float threshold_high;   // 高温阈值
    float threshold_low;    // 低温阈值
} SensorConfig_t;

/* 系统状态 */
typedef enum {
    SYS_IDLE = 0,
    SYS_RUNNING,
    SYS_ERROR,
    SYS_SLEEP
} SystemState_t;

/* 回调函数类型 */
typedef void (*EventCallback_t)(void *param);

💻 第四阶段:编码实现

步骤9:编码规范制定

目标:统一团队编码风格

内容

  • 命名规范
  • 代码格式
  • 注释规范
  • 文件组织

命名规范示例

类型 规范 示例
文件名 小写+下划线 drv_uart.c
宏定义 大写+下划线 #define MAX_BUFFER_SIZE 256
全局变量 g_前缀+小写驼峰 g_systemStatus
静态变量 s_前缀+小写驼峰 static uint8_t s_initialized;
函数名 模块名_功能 UART_Send()
类型定义 _t后缀 typedef struct {...} SensorData_t;

步骤10:按模块编码实现

编码顺序

  1. 先底层后上层(HAL → 驱动 → 中间件 → 应用)
  2. 先稳定模块后变化模块
  3. 先独立模块后依赖模块

编码示例(完整模块)

c 复制代码
/******************************************************************************
 * 文件:drv_dht22.c
 * 描述:DHT22温湿度传感器驱动实现
 * 作者:张三
 * 日期:2024-12-09
 * 版本:V1.0
 ******************************************************************************/

#include "drv_dht22.h"
#include "stm32f1xx_hal.h"

/* 私有宏定义 */
#define DHT22_TIMEOUT_MS    100
#define DHT22_START_SIGNAL  18   // ms
#define DHT22_DATA_BITS     40

/* 私有变量 */
static uint8_t s_initialized = 0;
static DHT22_Config_t s_config;
static uint8_t s_data_buffer[5];

/* 私有函数声明 */
static DHT22_Status_t dht22_send_start(void);
static DHT22_Status_t dht22_wait_response(void);
static DHT22_Status_t dht22_read_byte(uint8_t *byte);
static uint8_t dht22_verify_checksum(void);

/******************************************************************************
 * 函数名:DHT22_Init
 ******************************************************************************/
DHT22_Status_t DHT22_Init(DHT22_Config_t *config)
{
    if (config == NULL) {
        return DHT22_ERROR;
    }
    
    if (s_initialized) {
        return DHT22_OK;  // 已初始化
    }
    
    // 保存配置
    s_config = *config;
    
    // 配置GPIO
    GPIO_InitTypeDef GPIO_InitStruct = {0};
    GPIO_InitStruct.Pin = s_config.pin;
    GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
    GPIO_InitStruct.Pull = GPIO_PULLUP;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
    HAL_GPIO_Init(s_config.port, &GPIO_InitStruct);
    
    // 初始状态拉高
    HAL_GPIO_WritePin(s_config.port, s_config.pin, GPIO_PIN_SET);
    
    s_initialized = 1;
    return DHT22_OK;
}

/******************************************************************************
 * 函数名:DHT22_Read
 ******************************************************************************/
DHT22_Status_t DHT22_Read(DHT22_Data_t *data)
{
    if (!s_initialized) {
        return DHT22_NOT_INIT;
    }
    
    if (data == NULL) {
        return DHT22_ERROR;
    }
    
    // 1. 发送起始信号
    if (dht22_send_start() != DHT22_OK) {
        return DHT22_TIMEOUT;
    }
    
    // 2. 等待DHT22响应
    if (dht22_wait_response() != DHT22_OK) {
        return DHT22_TIMEOUT;
    }
    
    // 3. 读取40位数据
    for (int i = 0; i < 5; i++) {
        if (dht22_read_byte(&s_data_buffer[i]) != DHT22_OK) {
            return DHT22_TIMEOUT;
        }
    }
    
    // 4. 校验和验证
    if (!dht22_verify_checksum()) {
        return DHT22_CHECKSUM;
    }
    
    // 5. 解析数据
    uint16_t humi_raw = (s_data_buffer[0] << 8) | s_data_buffer[1];
    uint16_t temp_raw = (s_data_buffer[2] << 8) | s_data_buffer[3];
    
    data->humidity = humi_raw / 10.0f;
    data->temperature = temp_raw / 10.0f;
    data->timestamp = HAL_GetTick();
    
    return DHT22_OK;
}

/******************************************************************************
 * 私有函数实现
 ******************************************************************************/
static DHT22_Status_t dht22_send_start(void)
{
    // 拉低至少18ms
    HAL_GPIO_WritePin(s_config.port, s_config.pin, GPIO_PIN_RESET);
    HAL_Delay(DHT22_START_SIGNAL);
    
    // 拉高20-40us
    HAL_GPIO_WritePin(s_config.port, s_config.pin, GPIO_PIN_SET);
    delay_us(30);
    
    return DHT22_OK;
}

static uint8_t dht22_verify_checksum(void)
{
    uint8_t sum = s_data_buffer[0] + s_data_buffer[1] + 
                  s_data_buffer[2] + s_data_buffer[3];
    return (sum == s_data_buffer[4]);
}

🧪 第五阶段:测试验证

步骤11:单元测试

目标:测试每个模块功能

测试内容

  • 正常功能测试
  • 边界条件测试
  • 异常处理测试
  • 参数校验测试

测试用例示例

c 复制代码
/* DHT22驱动单元测试 */
void test_dht22_driver(void)
{
    DHT22_Config_t config;
    DHT22_Data_t data;
    DHT22_Status_t status;
    
    // 测试1:未初始化直接读取
    status = DHT22_Read(&data);
    assert(status == DHT22_NOT_INIT);
    
    // 测试2:正常初始化
    config.port = GPIOA;
    config.pin = GPIO_PIN_0;
    status = DHT22_Init(&config);
    assert(status == DHT22_OK);
    
    // 测试3:NULL指针测试
    status = DHT22_Read(NULL);
    assert(status == DHT22_ERROR);
    
    // 测试4:正常读取
    status = DHT22_Read(&data);
    assert(status == DHT22_OK);
    assert(data.temperature > -40 && data.temperature < 80);
    
    printf("DHT22 Driver Test: PASS\n");
}

步骤12:集成测试

目标:测试模块间协作

测试内容

  • 模块间接口测试
  • 数据流测试
  • 时序测试

步骤13:系统测试

目标:完整系统功能验证

测试内容

  • 功能测试(所有需求是否实现)
  • 性能测试(响应时间、精度)
  • 稳定性测试(长时间运行)
  • 压力测试(极限条件)

测试报告模板

系统测试报告

1. 测试环境

  • 硬件:STM32F103C8T6开发板
  • 编译器:MDK-ARM 5.36
  • 测试工具:示波器、串口调试助手

2. 功能测试

测试项 预期结果 实际结果 是否通过
温度采集 精度±0.5℃ 精度±0.3℃
LCD显示 刷新率1Hz 刷新率1Hz
风扇控制 >30℃启动 >30℃启动

3. 性能测试

指标 要求 测试结果 是否通过
响应时间 <100ms 85ms
CPU占用率 <50% 32%
内存占用 <16KB 12KB

4. 稳定性测试

  • 连续运行72小时无故障
  • 1000次读写操作无错误

5. 问题记录

6. 结论

系统通过所有测试,满足设计要求


📝 第六阶段:文档编写

步骤14:编写技术文档

必需文档

  1. 需求规格说明书 (SRS)
  2. 系统设计文档 (SDD)
  3. 详细设计文档 (DDD)
  4. 接口文档 (API Documentation)
  5. 测试报告 (Test Report)
  6. 用户手册 (User Manual)
  7. 维护手册 (Maintenance Manual)

代码注释(Doxygen格式)

c 复制代码
/**
 * @file    drv_dht22.c
 * @brief   DHT22温湿度传感器驱动
 * @author  张三
 * @date    2024-12-09
 * @version 1.0
 */

/**
 * @brief  读取DHT22温湿度数据
 * @param  data 输出数据指针
 * @retval DHT22_OK      成功
 * @retval DHT22_TIMEOUT 超时
 * @retval DHT22_CHECKSUM 校验错误
 * @note   两次读取间隔应大于2秒
 */
DHT22_Status_t DHT22_Read(DHT22_Data_t *data);

🚀 第七阶段:发布交付

步骤15:版本管理与发布

内容

  • 代码审查(Code Review)
  • 版本打标签(Git Tag)
  • 编译发布版本
  • 生成Hex/Bin文件

版本号规则

复制代码
V主版本.次版本.修订版本
例如:V1.2.3
- 主版本:重大架构变更
- 次版本:功能新增
- 修订版本:Bug修复

📊 完整流程总结表

阶段 步骤 输出物 工具
需求阶段 1. 需求分析 2. 可行性分析 需求规格书 芯片选型报告 Word, Excel
系统设计 3. 系统架构设计 4. 硬件接口设计 5. 模块划分 架构图 引脚分配表 模块列表 Draw.io, Visio
详细设计 6. 模块详细设计 7. 接口设计 8. 数据结构设计 详细设计文档 .h头文件 流程图 Markdown, PlantUML
编码实现 9. 编码规范 10. 模块编码 源代码 Makefile Keil, IAR, VSCode
测试验证 11. 单元测试 12. 集成测试 13. 系统测试 测试用例 测试报告 CUnit, 示波器
文档发布 14. 文档编写 15. 版本发布 技术文档 Hex文件 Doxygen, Git

⚡ 快速开发建议(针对不同项目规模)

小型项目(个人/学习项目)

可简化流程:

  1. 需求分析(简单列表)
  2. 模块划分
  3. 接口设计
  4. 编码实现
  5. 功能测试

中型项目(产品级项目)

标准流程:

  1. ✅ 需求分析
  2. ✅ 系统设计
  3. ✅ 详细设计
  4. ✅ 编码实现
  5. ✅ 测试验证
  6. ✅ 文档编写

大型项目(工业级项目)

完整流程 + 额外要求:

  • CMMI/ISO流程管理
  • 配置管理
  • 变更管理
  • 风险管理
  • 代码审查
  • 静态分析
  • 覆盖率测试

🛠️ 推荐工具链

用途 工具推荐
IDE Keil MDK, STM32CubeIDE, IAR
版本控制 Git + GitHub/Gitee
文档工具 Markdown, Doxygen, Word
画图工具 Draw.io, Visio, PlantUML, Mermaid
调试工具 ST-Link, J-Link, 逻辑分析仪
测试工具 CUnit, Unity, CppUTest
静态分析 PC-Lint, Cppcheck

这就是工业级STM32嵌入式软件开发的完整流程!你现在在做什么阶段的项目?我可以针对性地提供更详细的指导。



STM32F103C8T6 项目软件设计方案

根据你的需求,我来帮你做完整的软件设计分析和方案制定。


📋 第一步:需求分析与可行性评估

1.1 需求整理

需求项 详细说明 硬件接口 优先级
上位机通信 自定义协议,USART1通信 USART1 (PA9/PA10)
电池电量读取 通过IIC读取电量芯片 I2C1/I2C2
放大倍数控制 GPIO控制放大器增益 GPIO输出
参数保存 放大倍数等参数掉电保存 I2C EEPROM

1.2 资源评估

STM32F103C8T6 资源

  • Flash: 64KB
  • RAM: 20KB
  • 主频: 72MHz
  • USART: 3路
  • I2C: 2路
  • GPIO: 充足

是否需要RTOS判断

评估维度 分析 结论
任务复杂度 4个独立功能模块,相对独立 建议使用RTOS
实时性要求 串口通信需要及时响应 建议使用RTOS
并发需求 多个功能需要"同时"运行 建议使用RTOS
资源占用 FreeRTOS约占8-12KB Flash,2-4KB RAM ✅ 资源充足
开发效率 RTOS简化任务管理,代码更清晰 建议使用RTOS
可维护性 任务独立,便于调试和扩展 建议使用RTOS

结论:✅ 建议使用 FreeRTOS

理由

  1. 你的项目有多个独立功能模块,使用RTOS可以让代码结构更清晰
  2. 串口通信需要及时响应,RTOS的任务调度机制更合适
  3. STM32F103C8T6资源足够支持轻量级FreeRTOS
  4. 便于后期功能扩展和维护

🏗️ 第二步:系统架构设计

2.1 分层架构

复制代码
┌─────────────────────────────────────┐
│         应用层 (Application)         │
│  ┌──────────┐  ┌──────────┐         │
│  │串口通信  │  │电池管理  │         │
│  │任务      │  │任务      │         │
│  └──────────┘  └──────────┘         │
│  ┌──────────┐  ┌──────────┐         │
│  │增益控制  │  │参数管理  │         │
│  │任务      │  │任务      │         │
│  └──────────┘  └──────────┘         │
├─────────────────────────────────────┤
│       中间件层 (Middleware)          │
│  ┌─────────────────────────────┐   │
│  │  FreeRTOS (任务调度/队列)    │   │
│  └─────────────────────────────┘   │
│  ┌─────────────────────────────┐   │
│  │  协议解析模块                 │   │
│  └─────────────────────────────┘   │
├─────────────────────────────────────┤
│          驱动层 (Drivers)            │
│  ┌──────────┐  ┌──────────┐         │
│  │UART驱动  │  │I2C驱动   │         │
│  └──────────┘  └──────────┘         │
│  ┌──────────┐  ┌──────────┐         │
│  │GPIO驱动  │  │EEPROM驱动│         │
│  └──────────┘  └──────────┘         │
│  ┌──────────┐                       │
│  │电量芯片  │                       │
│  │驱动      │                       │
│  └──────────┘                       │
├─────────────────────────────────────┤
│         BSP层 (Board Support)        │
│  系统时钟/GPIO初始化/外设配置        │
├─────────────────────────────────────┤
│         HAL层 (STM32 HAL库)          │
└─────────────────────────────────────┘

2.2 任务架构设计

系统启动 硬件初始化 创建FreeRTOS任务 启动调度器 串口通信任务 电池监控任务 增益控制任务 串口接收队列 串口发送队列 电量数据队列 参数保存触发

FreeRTOS任务规划

任务名称 优先级 堆栈大小 运行周期 功能
串口通信任务 3 (高) 256字节 事件触发 接收解析上位机命令,发送响应
电池监控任务 2 (中) 128字节 1秒 读取电池电量,上报数据
增益控制任务 2 (中) 128字节 事件触发 控制放大倍数
参数保存任务 1 (低) 128字节 事件触发 保存参数到EEPROM

任务间通信

  • 使用消息队列:串口接收队列、电量数据队列
  • 使用信号量:EEPROM访问互斥、GPIO控制互斥
  • 使用事件标志组:参数保存请求、电量告警

📐 第三步:硬件接口设计

3.1 引脚分配表

功能 引脚 配置 说明
USART1通信
USART1_TX PA9 复用推挽输出 上位机通信发送
USART1_RX PA10 浮空输入 上位机通信接收
I2C接口
I2C1_SCL PB6 复用开漏输出 电量芯片/EEPROM时钟
I2C1_SDA PB7 复用开漏输出 电量芯片/EEPROM数据
增益控制
GAIN_CTRL0 PA0 推挽输出 增益控制位0
GAIN_CTRL1 PA1 推挽输出 增益控制位1
GAIN_CTRL2 PA2 推挽输出 增益控制位2 (可选)
状态指示
LED_STATUS PC13 推挽输出 系统状态指示

3.2 I2C设备地址规划

设备 I2C地址 说明
电量芯片 (如BQ27441) 0x55 7位地址
EEPROM (如AT24C02) 0x50 7位地址

3.3 增益控制编码

假设使用3个GPIO控制8档增益:

增益倍数 CTRL2 CTRL1 CTRL0 说明
1x 0 0 0 无放大
2x 0 0 1 2倍
5x 0 1 0 5倍
10x 0 1 1 10倍
20x 1 0 0 20倍
50x 1 0 1 50倍
100x 1 1 0 100倍
200x 1 1 1 200倍

🔧 第四步:模块划分设计

4.1 项目目录结构

复制代码
Project/
├── App/                        # 应用层
│   ├── app_main.c              # 主任务和系统初始化
│   ├── task_uart.c             # 串口通信任务
│   ├── task_battery.c          # 电池监控任务
│   ├── task_gain.c             # 增益控制任务
│   └── task_param.c            # 参数保存任务
│
├── Middleware/                 # 中间件层
│   ├── FreeRTOS/               # FreeRTOS源码
│   │   ├── Source/
│   │   └── portable/
│   └── Protocol/               # 协议解析
│       ├── protocol.c          # 自定义协议解析
│       └── protocol.h
│
├── Drivers/                    # 驱动层
│   ├── BSP/                    # 板级驱动
│   │   ├── bsp_uart.c          # UART驱动封装
│   │   ├── bsp_i2c.c           # I2C驱动封装
│   │   ├── bsp_gpio.c          # GPIO驱动封装
│   │   └── bsp_led.c           # LED驱动
│   ├── Device/                 # 设备驱动
│   │   ├── drv_battery.c       # 电量芯片驱动
│   │   ├── drv_eeprom.c        # EEPROM驱动
│   │   └── drv_gain.c          # 增益控制驱动
│   └── HAL/                    # STM32 HAL库
│
├── Common/                     # 公共组件
│   ├── ringbuffer.c            # 环形缓冲区
│   ├── crc.c                   # CRC校验
│   └── utilities.c             # 工具函数
│
├── Config/                     # 配置文件
│   ├── FreeRTOSConfig.h        # FreeRTOS配置
│   ├── system_config.h         # 系统配置
│   └── pin_config.h            # 引脚定义
│
└── Inc/                        # 所有头文件

4.2 模块依赖关系图

task_uart protocol bsp_uart task_battery drv_battery bsp_i2c task_gain drv_gain bsp_gpio task_param drv_eeprom CRC校验 HAL_UART HAL_I2C HAL_GPIO


📡 第五步:协议设计

5.1 自定义协议格式

数据帧格式

复制代码
┌────────┬────────┬────────┬────────┬────────┬────────┬────────┐
│ 帧头   │ 命令码 │ 数据长度│  数据  │ 校验和 │ 帧尾   │        │
│ (2字节)│ (1字节)│ (1字节) │(N字节) │ (2字节)│ (2字节)│        │
│  0xAA55│  CMD   │  LEN   │  DATA  │  CRC16 │  0x0D0A│        │
└────────┴────────┴────────┴────────┴────────┴────────┴────────┘

5.2 命令码定义

命令码 命令名称 方向 数据内容 说明
0x01 读取电量 上→下 请求读取电池电量
0x81 电量响应 下→上 电量百分比(1字节) 返回电量数据
0x02 设置增益 上→下 增益档位(1字节) 设置放大倍数
0x82 增益响应 下→上 当前增益(1字节) 返回设置结果
0x03 读取参数 上→下 参数ID(1字节) 读取保存的参数
0x83 参数响应 下→上 参数值(4字节) 返回参数数据
0x04 保存参数 上→下 参数ID+值(5字节) 保存参数到EEPROM
0x84 保存响应 下→上 结果(1字节) 0=成功 1=失败
0xFF 心跳包 双向 保持连接

5.3 协议示例

示例1:上位机请求读取电量

复制代码
发送: AA 55 01 00 [CRC16] 0D 0A
响应: AA 55 81 01 4B [CRC16] 0D 0A  (电量75%)

示例2:上位机设置增益为10x

复制代码
发送: AA 55 02 01 03 [CRC16] 0D 0A  (档位3=10x)
响应: AA 55 82 01 03 [CRC16] 0D 0A  (确认设置为档位3)

💻 第六步:详细设计(核心模块)现在我为你生成一份完整的系统配置文档:最后,我为你创建一个完整的main函数和系统初始化代码:---

📊 第七步:开发流程时间表

基于你的项目,我给出一个实际的开发计划:

阶段 工作内容 预计时间 输出物
第1周 需求分析、硬件选型、原理图设计 3天 需求文档、原理图
引脚分配、接口设计 2天 引脚分配表、接口文档
第2周 BSP层开发(时钟、GPIO、UART、I2C) 3天 BSP驱动代码
驱动层开发(电量芯片、EEPROM、增益控制) 2天 设备驱动代码
第3周 协议设计与实现 2天 协议文档、协议代码
FreeRTOS移植与配置 1天 FreeRTOS工程
串口通信任务开发 2天 串口任务代码
第4周 电池监控任务开发 1天 电池任务代码
增益控制任务开发 1天 增益任务代码
参数保存任务开发 1天 参数任务代码
任务集成与调试 2天 完整程序
第5周 单元测试 2天 测试用例
系统测试与优化 3天 测试报告
第6周 文档编写 2天 技术文档
代码整理与发布 1天 发布版本

总计:约6周完成


🎯 关键技术点总结

1. 为什么选择FreeRTOS?

对比项 裸机开发 FreeRTOS开发
任务管理 需要手写状态机,复杂 任务独立,代码清晰
实时响应 需要轮询,响应慢 事件驱动,响应快
代码复用 耦合严重,难复用 模块独立,易复用
扩展性 添加功能困难 添加任务即可
调试难度 问题难定位 任务独立,易调试
资源占用 多8-12KB Flash

你的项目适合FreeRTOS的理由

  • ✅ 有4个独立功能模块
  • ✅ 串口通信需要及时响应
  • ✅ 资源充足(64KB Flash,20KB RAM)
  • ✅ 便于后期扩展功能

2. 任务优先级分配原则

复制代码
优先级5 (最高)  - 预留
优先级4         - 预留
优先级3 (高)    - 串口通信任务(需要及时响应)
优先级2 (中)    - 电池监控、增益控制(周期性任务)
优先级1 (低)    - 参数保存(后台任务)
优先级0         - 空闲任务(系统自动创建)

3. 内存使用估算

项目 大小 说明
FreeRTOS内核 8-10KB 代码段
串口任务堆栈 256字节
电池任务堆栈 128字节
增益任务堆栈 128字节
参数任务堆栈 128字节
定时器任务堆栈 128字节
FreeRTOS堆 8KB 动态内存
RAM总计 约10KB 剩余10KB

资源充足,完全可行!


📝 下一步行动建议

立即开始的工作

  1. 第一步(本周)

    • 使用STM32CubeMX生成工程框架
    • 配置时钟、GPIO、UART1、I2C1
    • 移植FreeRTOS(STM32CubeMX可一键添加)
    • 测试LED闪烁和串口输出
  2. 第二步(下周)

    • 实现协议解析模块(我已经给你设计好了)
    • 开发串口通信任务
    • 测试上位机收发数据
  3. 第三步

    • 根据实际电量芯片型号编写驱动(BQ27441/MAX17048/等)
    • 开发电池监控任务
  4. 第四步

    • 实现增益控制驱动
    • 开发增益控制任务
  5. 第五步

    • 实现EEPROM读写
    • 开发参数保存任务

🛠️ 推荐开发工具

工具 用途
STM32CubeMX 工程生成、HAL库配置
Keil MDKSTM32CubeIDE 编译器和IDE
串口调试助手 测试UART通信
逻辑分析仪 调试I2C时序
ST-Link 下载和调试

总结

你的项目非常适合使用FreeRTOS

关键优势

  1. 代码结构清晰,模块独立
  2. 便于调试和扩展
  3. 资源占用合理(约占50%)
  4. 符合工业级开发规范

我已经为你提供了:

  • ✅ 完整的系统架构设计
  • ✅ 协议定义和实现框架
  • ✅ 增益控制驱动完整代码
  • ✅ FreeRTOS配置文件
  • ✅ 主程序框架

你可以直接基于这些代码开始开发。如果你需要:

  • 电池芯片的具体驱动(需要知道芯片型号)
  • EEPROM驱动的完整实现
  • 其他任务的详细代码


STM32F103C8T6 裸机程序设计方案(不使用RTOS)


📋 第一步:裸机架构分析

1.1 是否需要RTOS的重新评估

评估维度 分析 结论
任务复杂度 4个功能模块,逻辑相对简单 ✅ 裸机可行
实时性要求 串口通信需要及时响应,但可用中断处理 ✅ 裸机可行
并发需求 功能可以按优先级顺序执行 ✅ 裸机可行
资源占用 裸机节省8-12KB Flash和RAM ✅ 裸机优势
开发效率 项目简单,裸机代码更直观 ✅ 裸机可行
可维护性 状态机设计清晰,易于维护 ✅ 裸机可行

结论:✅ 裸机开发完全适合你的项目

裸机开发优势

  1. 代码更简洁,逻辑更直观
  2. 节省Flash和RAM资源
  3. 调试更容易(没有任务调度的复杂性)
  4. 功耗更低(可以精确控制休眠)
  5. 启动速度更快

🏗️ 第二步:裸机系统架构设计

2.1 核心设计思路

采用:主循环 + 中断 + 定时调度 + 状态机

复制代码
┌─────────────────────────────────────────┐
│           Main Loop (主循环)             │
│                                          │
│  ┌────────────────────────────────┐    │
│  │   1. 串口数据处理 (高优先级)    │    │
│  └────────────────────────────────┘    │
│  ┌────────────────────────────────┐    │
│  │   2. 电池电量监控 (1秒周期)     │    │
│  └────────────────────────────────┘    │
│  ┌────────────────────────────────┐    │
│  │   3. 增益控制处理 (事件触发)    │    │
│  └────────────────────────────────┘    │
│  ┌────────────────────────────────┐    │
│  │   4. 参数保存处理 (事件触发)    │    │
│  └────────────────────────────────┘    │
│  ┌────────────────────────────────┐    │
│  │   5. 系统状态更新               │    │
│  └────────────────────────────────┘    │
└─────────────────────────────────────────┘
         ↑                    ↑
         │                    │
    ┌────┴────┐         ┌────┴────┐
    │UART中断 │         │定时器中断│
    │接收数据 │         │提供时基  │
    └─────────┘         └─────────┘

2.2 系统分层架构(裸机版)

复制代码
┌─────────────────────────────────────────┐
│         应用层 (Application)             │
│  ┌──────────┐  ┌──────────┐             │
│  │主程序循环│  │状态机管理│             │
│  └──────────┘  └──────────┘             │
│  ┌──────────┐  ┌──────────┐             │
│  │协议解析  │  │业务逻辑  │             │
│  └──────────┘  └──────────┘             │
├─────────────────────────────────────────┤
│          服务层 (Service)                │
│  ┌─────────────────────────────────┐   │
│  │  定时调度器 (软件定时器)         │   │
│  └─────────────────────────────────┘   │
│  ┌─────────────────────────────────┐   │
│  │  环形缓冲区 / CRC校验            │   │
│  └─────────────────────────────────┘   │
├─────────────────────────────────────────┤
│          驱动层 (Drivers)                │
│  ┌──────────┐  ┌──────────┐             │
│  │UART驱动  │  │I2C驱动   │             │
│  └──────────┘  └──────────┘             │
│  ┌──────────┐  ┌──────────┐             │
│  │GPIO驱动  │  │EEPROM驱动│             │
│  └──────────┘  └──────────┘             │
│  ┌──────────┐                           │
│  │电量芯片  │                           │
│  │驱动      │                           │
│  └──────────┘                           │
├─────────────────────────────────────────┤
│         BSP层 (Board Support)            │
│  系统时钟/中断配置/外设初始化            │
├─────────────────────────────────────────┤
│         HAL层 (STM32 HAL库)              │
└─────────────────────────────────────────┘

📐 第三步:模块划分设计

3.1 项目目录结构(裸机版)

复制代码
Project_Baremetal/
├── App/                        # 应用层
│   ├── main.c                  # 主程序(主循环)
│   ├── app_uart.c              # 串口通信应用
│   ├── app_battery.c           # 电池监控应用
│   ├── app_gain.c              # 增益控制应用
│   └── app_param.c             # 参数管理应用
│
├── Service/                    # 服务层
│   ├── scheduler.c             # 软件定时调度器
│   ├── protocol.c              # 协议解析
│   ├── ringbuffer.c            # 环形缓冲区
│   └── crc.c                   # CRC校验
│
├── Drivers/                    # 驱动层
│   ├── BSP/
│   │   ├── bsp_uart.c          # UART驱动
│   │   ├── bsp_i2c.c           # I2C驱动
│   │   ├── bsp_gpio.c          # GPIO驱动
│   │   ├── bsp_timer.c         # 定时器驱动
│   │   └── bsp_led.c           # LED驱动
│   └── Device/
│       ├── drv_battery.c       # 电量芯片驱动
│       ├── drv_eeprom.c        # EEPROM驱动
│       └── drv_gain.c          # 增益控制驱动
│
├── Config/                     # 配置文件
│   ├── system_config.h         # 系统配置
│   └── pin_config.h            # 引脚定义
│
└── Inc/                        # 头文件

⏰ 第四步:定时调度器设计

4.1 软件定时器实现

cpp 复制代码
/**
 ******************************************************************************
 * @file    scheduler.h
 * @brief   软件定时调度器 - 用于裸机多任务调度
 * @author  Your Name
 * @date    2024-12-09
 * @version 1.0
 ******************************************************************************
 */

#ifndef __SCHEDULER_H
#define __SCHEDULER_H

#include <stdint.h>

/* 任务ID定义 */
typedef enum {
    TASK_UART = 0,      // 串口通信任务
    TASK_BATTERY,       // 电池监控任务
    TASK_GAIN,          // 增益控制任务
    TASK_PARAM,         // 参数保存任务
    TASK_MAX            // 任务数量
} TaskID_t;

/* 任务函数指针类型 */
typedef void (*TaskFunc_t)(void);

/* 任务结构体 */
typedef struct {
    TaskFunc_t  func;           // 任务函数指针
    uint32_t    period;         // 任务周期 (ms), 0=事件触发
    uint32_t    elapsed;        // 已经过时间
    uint8_t     enabled;        // 任务使能标志
    const char  *name;          // 任务名称
} Task_t;

/**
 * @brief  调度器初始化
 * @retval 0=成功 -1=失败
 */
int Scheduler_Init(void);

/**
 * @brief  注册任务
 * @param  id      任务ID
 * @param  func    任务函数指针
 * @param  period  任务周期(ms), 0=事件触发型
 * @param  name    任务名称
 * @retval 0=成功 -1=失败
 */
int Scheduler_RegisterTask(TaskID_t id, TaskFunc_t func, uint32_t period, const char *name);

/**
 * @brief  启用任务
 * @param  id 任务ID
 */
void Scheduler_EnableTask(TaskID_t id);

/**
 * @brief  禁用任务
 * @param  id 任务ID
 */
void Scheduler_DisableTask(TaskID_t id);

/**
 * @brief  触发事件型任务立即执行
 * @param  id 任务ID
 */
void Scheduler_TriggerTask(TaskID_t id);

/**
 * @brief  调度器主循环(在main函数while(1)中调用)
 */
void Scheduler_Run(void);

/**
 * @brief  1ms时基更新(在定时器中断中调用)
 */
void Scheduler_TickUpdate(void);

/**
 * @brief  获取系统运行时间(ms)
 * @retval 系统运行时间
 */
uint32_t Scheduler_GetTick(void);

#endif /* __SCHEDULER_H */
cpp 复制代码
/**
 ******************************************************************************
 * @file    scheduler.c
 * @brief   软件定时调度器实现
 ******************************************************************************
 */

#include "scheduler.h"
#include <string.h>

/* 系统时基计数器 (1ms递增) */
static volatile uint32_t system_tick = 0;

/* 任务列表 */
static Task_t task_list[TASK_MAX];

/**
 * @brief  调度器初始化
 */
int Scheduler_Init(void)
{
    /* 清空任务列表 */
    memset(task_list, 0, sizeof(task_list));
    
    /* 复位系统时基 */
    system_tick = 0;
    
    return 0;
}

/**
 * @brief  注册任务
 */
int Scheduler_RegisterTask(TaskID_t id, TaskFunc_t func, uint32_t period, const char *name)
{
    if (id >= TASK_MAX || func == NULL) {
        return -1;
    }
    
    task_list[id].func = func;
    task_list[id].period = period;
    task_list[id].elapsed = 0;
    task_list[id].enabled = 1;  // 默认使能
    task_list[id].name = name;
    
    return 0;
}

/**
 * @brief  启用任务
 */
void Scheduler_EnableTask(TaskID_t id)
{
    if (id < TASK_MAX) {
        task_list[id].enabled = 1;
    }
}

/**
 * @brief  禁用任务
 */
void Scheduler_DisableTask(TaskID_t id)
{
    if (id < TASK_MAX) {
        task_list[id].enabled = 0;
    }
}

/**
 * @brief  触发事件型任务
 */
void Scheduler_TriggerTask(TaskID_t id)
{
    if (id < TASK_MAX && task_list[id].enabled) {
        /* 立即执行该任务 */
        if (task_list[id].func != NULL) {
            task_list[id].func();
        }
    }
}

/**
 * @brief  调度器主循环
 */
void Scheduler_Run(void)
{
    for (int i = 0; i < TASK_MAX; i++) {
        /* 跳过未注册或未使能的任务 */
        if (task_list[i].func == NULL || !task_list[i].enabled) {
            continue;
        }
        
        /* 事件触发型任务不在这里执行 */
        if (task_list[i].period == 0) {
            continue;
        }
        
        /* 检查是否到达执行时间 */
        if (task_list[i].elapsed >= task_list[i].period) {
            /* 执行任务 */
            task_list[i].func();
            
            /* 重置计时器 */
            task_list[i].elapsed = 0;
        }
    }
}

/**
 * @brief  1ms时基更新 (在定时器中断中调用)
 */
void Scheduler_TickUpdate(void)
{
    /* 系统时基递增 */
    system_tick++;
    
    /* 更新所有周期性任务的计时器 */
    for (int i = 0; i < TASK_MAX; i++) {
        if (task_list[i].period > 0 && task_list[i].enabled) {
            task_list[i].elapsed++;
        }
    }
}

/**
 * @brief  获取系统运行时间
 */
uint32_t Scheduler_GetTick(void)
{
    return system_tick;
}

/**
 * @brief  延时函数(ms) - 非阻塞其他任务
 */
void Scheduler_Delay(uint32_t ms)
{
    uint32_t start = system_tick;
    while ((system_tick - start) < ms) {
        /* 在延时期间继续运行调度器 */
        Scheduler_Run();
    }
}

📡 第五步:串口通信模块(中断+缓冲区)---

cpp 复制代码
/**
 ******************************************************************************
 * @file    app_uart.c
 * @brief   串口通信应用模块 (裸机版)
 ******************************************************************************
 */

#include "app_uart.h"
#include "protocol.h"
#include "bsp_uart.h"
#include "ringbuffer.h"
#include "app_battery.h"
#include "app_gain.h"
#include "app_param.h"
#include <string.h>

/* 接收缓冲区 */
#define UART_RX_BUFFER_SIZE  256
static uint8_t uart_rx_buffer[UART_RX_BUFFER_SIZE];
static RingBuffer_t uart_rx_ring;

/* 协议帧解析缓冲区 */
#define FRAME_BUFFER_SIZE    128
static uint8_t frame_buffer[FRAME_BUFFER_SIZE];
static uint16_t frame_index = 0;

/* 新数据标志 */
static volatile uint8_t uart_new_data_flag = 0;

/**
 * @brief  串口接收中断回调 (在中断中被调用)
 * @param  data 接收到的字节
 */
void UART_RxCallback(uint8_t data)
{
    /* 将数据放入环形缓冲区 */
    RingBuffer_Write(&uart_rx_ring, data);
    
    /* 设置新数据标志 */
    uart_new_data_flag = 1;
}

/**
 * @brief  协议解析回调函数
 * @param  frame 解析出的完整帧
 */
static void protocol_parse_callback(ProtocolFrame_t *frame)
{
    if (frame == NULL) {
        return;
    }
    
    /* 根据命令码处理 */
    switch (frame->cmd) {
        case CMD_READ_BATTERY:
            /* 读取电池电量 */
            App_Battery_OnCommand(frame);
            break;
            
        case CMD_SET_GAIN:
            /* 设置增益 */
            App_Gain_OnCommand(frame);
            break;
            
        case CMD_READ_PARAM:
        case CMD_SAVE_PARAM:
            /* 参数读取/保存 */
            App_Param_OnCommand(frame);
            break;
            
        case CMD_HEARTBEAT:
            /* 心跳包响应 */
            Protocol_Send(CMD_HEARTBEAT, NULL, 0);
            break;
            
        default:
            /* 未知命令 */
            break;
    }
}

/**
 * @brief  串口通信模块初始化
 */
int App_UART_Init(void)
{
    /* 初始化环形缓冲区 */
    RingBuffer_Init(&uart_rx_ring, uart_rx_buffer, UART_RX_BUFFER_SIZE);
    
    /* 初始化协议模块 */
    Protocol_Init(protocol_parse_callback);
    
    /* 初始化UART硬件 */
    BSP_UART_Init();
    
    /* 清空帧缓冲区 */
    frame_index = 0;
    memset(frame_buffer, 0, FRAME_BUFFER_SIZE);
    
    return 0;
}

/**
 * @brief  串口通信任务 (在主循环中周期调用)
 */
void App_UART_Task(void)
{
    uint8_t data;
    
    /* 检查是否有新数据 */
    if (!uart_new_data_flag) {
        return;
    }
    
    /* 从环形缓冲区读取所有数据 */
    while (RingBuffer_Read(&uart_rx_ring, &data) == 0) {
        /* 数据放入帧缓冲区 */
        if (frame_index < FRAME_BUFFER_SIZE) {
            frame_buffer[frame_index++] = data;
        }
        
        /* 检测到帧尾 0x0D 0x0A */
        if (frame_index >= 2 && 
            frame_buffer[frame_index - 2] == 0x0D && 
            frame_buffer[frame_index - 1] == 0x0A) {
            
            /* 协议解析 */
            Protocol_Receive(frame_buffer, frame_index);
            
            /* 清空缓冲区 */
            frame_index = 0;
            memset(frame_buffer, 0, FRAME_BUFFER_SIZE);
        }
        
        /* 缓冲区溢出保护 */
        if (frame_index >= FRAME_BUFFER_SIZE) {
            frame_index = 0;
        }
    }
    
    /* 清除新数据标志 */
    if (RingBuffer_IsEmpty(&uart_rx_ring)) {
        uart_new_data_flag = 0;
    }
}

/**
 * @brief  发送数据到上位机
 * @param  cmd  命令码
 * @param  data 数据指针
 * @param  len  数据长度
 * @retval 0=成功 -1=失败
 */
int App_UART_Send(uint8_t cmd, uint8_t *data, uint8_t len)
{
    return Protocol_Send(cmd, data, len);
}

🔋 第六步:电池监控模块---

cpp 复制代码
/**
 ******************************************************************************
 * @file    app_battery.c
 * @brief   电池监控应用模块 (裸机版)
 ******************************************************************************
 */

#include "app_battery.h"
#include "drv_battery.h"
#include "protocol.h"
#include "app_uart.h"
#include "scheduler.h"

/* 电池状态 */
typedef struct {
    uint8_t  percentage;        // 电量百分比
    uint16_t voltage;           // 电压 (mV)
    int16_t  current;           // 电流 (mA)
    uint8_t  is_charging;       // 是否充电中
    uint32_t last_update_time;  // 上次更新时间
} BatteryStatus_t;

static BatteryStatus_t battery_status = {0};

/* 电量告警阈值 */
#define BATTERY_LOW_THRESHOLD   20   // 低电量告警 20%
#define BATTERY_CRITICAL        10   // 严重低电量 10%

/**
 * @brief  电池监控模块初始化
 */
int App_Battery_Init(void)
{
    /* 初始化电池芯片驱动 */
    if (Battery_Init() != BATTERY_OK) {
        return -1;
    }
    
    /* 初始化电池状态 */
    battery_status.percentage = 100;
    battery_status.voltage = 4200;
    battery_status.current = 0;
    battery_status.is_charging = 0;
    battery_status.last_update_time = 0;
    
    return 0;
}

/**
 * @brief  电池监控任务 (1秒周期调用)
 */
void App_Battery_Task(void)
{
    BatteryData_t battery_data;
    
    /* 读取电池数据 */
    if (Battery_Read(&battery_data) == BATTERY_OK) {
        /* 更新电池状态 */
        battery_status.percentage = battery_data.percentage;
        battery_status.voltage = battery_data.voltage;
        battery_status.current = battery_data.current;
        battery_status.is_charging = battery_data.is_charging;
        battery_status.last_update_time = Scheduler_GetTick();
        
        /* 低电量告警 */
        if (battery_status.percentage <= BATTERY_CRITICAL) {
            /* 严重低电量 - 可以触发保护措施 */
            // TODO: 进入低功耗模式或关键功能保护
        } else if (battery_status.percentage <= BATTERY_LOW_THRESHOLD) {
            /* 低电量告警 */
            // TODO: 上报告警信息
        }
    }
}

/**
 * @brief  处理上位机读取电量命令
 * @param  frame 协议帧
 */
void App_Battery_OnCommand(ProtocolFrame_t *frame)
{
    uint8_t response_data[4];
    
    if (frame->cmd == CMD_READ_BATTERY) {
        /* 封装响应数据 */
        response_data[0] = battery_status.percentage;        // 电量百分比
        response_data[1] = battery_status.voltage >> 8;      // 电压高字节
        response_data[2] = battery_status.voltage & 0xFF;    // 电压低字节
        response_data[3] = battery_status.is_charging;       // 充电状态
        
        /* 发送响应 */
        App_UART_Send(CMD_BATTERY_RESP, response_data, 4);
    }
}

/**
 * @brief  获取当前电池电量
 * @retval 电量百分比 (0-100)
 */
uint8_t App_Battery_GetPercentage(void)
{
    return battery_status.percentage;
}

/**
 * @brief  获取当前电池电压
 * @retval 电压 (mV)
 */
uint16_t App_Battery_GetVoltage(void)
{
    return battery_status.voltage;
}

/**
 * @brief  获取是否正在充电
 * @retval 1=充电中 0=未充电
 */
uint8_t App_Battery_IsCharging(void)
{
    return battery_status.is_charging;
}

🎚️ 第七步:增益控制模块---

cpp 复制代码
/**
 ******************************************************************************
 * @file    app_gain.c
 * @brief   增益控制应用模块 (裸机版)
 ******************************************************************************
 */

#include "app_gain.h"
#include "drv_gain.h"
#include "protocol.h"
#include "app_uart.h"
#include "app_param.h"
#include "scheduler.h"

/* 增益控制状态 */
typedef struct {
    GainLevel_t current_level;      // 当前增益档位
    uint8_t     need_save;          // 是否需要保存到EEPROM
    uint32_t    last_change_time;   // 上次修改时间
} GainStatus_t;

static GainStatus_t gain_status = {0};

/* 自动保存延迟时间 (5秒无变化后保存) */
#define AUTO_SAVE_DELAY_MS  5000

/**
 * @brief  增益控制模块初始化
 */
int App_Gain_Init(void)
{
    /* 初始化增益控制驱动 */
    if (Gain_Init() != GAIN_OK) {
        return -1;
    }
    
    /* 从EEPROM读取上次保存的增益档位 */
    GainLevel_t saved_gain = GAIN_1X;
    if (App_Param_ReadGain(&saved_gain) == 0) {
        /* 恢复上次的增益设置 */
        Gain_SetLevel(saved_gain);
        gain_status.current_level = saved_gain;
    } else {
        /* 使用默认增益 */
        gain_status.current_level = GAIN_1X;
    }
    
    gain_status.need_save = 0;
    gain_status.last_change_time = 0;
    
    return 0;
}

/**
 * @brief  增益控制任务 (事件触发型)
 */
void App_Gain_Task(void)
{
    /* 检查是否需要自动保存 */
    if (gain_status.need_save) {
        uint32_t elapsed = Scheduler_GetTick() - gain_status.last_change_time;
        
        /* 5秒无变化后保存 */
        if (elapsed >= AUTO_SAVE_DELAY_MS) {
            /* 保存到EEPROM */
            App_Param_SaveGain(gain_status.current_level);
            
            /* 清除保存标志 */
            gain_status.need_save = 0;
        }
    }
}

/**
 * @brief  处理上位机增益控制命令
 * @param  frame 协议帧
 */
void App_Gain_OnCommand(ProtocolFrame_t *frame)
{
    uint8_t response_data[1];
    
    if (frame->cmd == CMD_SET_GAIN) {
        /* 检查数据长度 */
        if (frame->len != 1) {
            return;
        }
        
        /* 获取增益档位 */
        GainLevel_t new_level = (GainLevel_t)frame->data[0];
        
        /* 检查档位有效性 */
        if (new_level >= GAIN_MAX) {
            /* 无效档位 */
            response_data[0] = 0xFF;  // 错误码
            App_UART_Send(CMD_GAIN_RESP, response_data, 1);
            return;
        }
        
        /* 设置新增益 */
        if (Gain_SetLevel(new_level) == GAIN_OK) {
            gain_status.current_level = new_level;
            gain_status.need_save = 1;
            gain_status.last_change_time = Scheduler_GetTick();
            
            /* 发送成功响应 */
            response_data[0] = new_level;
            App_UART_Send(CMD_GAIN_RESP, response_data, 1);
        } else {
            /* 设置失败 */
            response_data[0] = 0xFF;
            App_UART_Send(CMD_GAIN_RESP, response_data, 1);
        }
    }
}

/**
 * @brief  设置增益档位
 * @param  level 增益档位
 * @retval 0=成功 -1=失败
 */
int App_Gain_SetLevel(GainLevel_t level)
{
    if (level >= GAIN_MAX) {
        return -1;
    }
    
    if (Gain_SetLevel(level) == GAIN_OK) {
        gain_status.current_level = level;
        gain_status.need_save = 1;
        gain_status.last_change_time = Scheduler_GetTick();
        return 0;
    }
    
    return -1;
}

/**
 * @brief  获取当前增益档位
 * @retval 增益档位
 */
GainLevel_t App_Gain_GetLevel(void)
{
    return gain_status.current_level;
}

/**
 * @brief  获取当前增益倍数
 * @retval 增益倍数
 */
uint8_t App_Gain_GetValue(void)
{
    return Gain_GetValue(gain_status.current_level);
}

💾 第八步:参数保存模块---

cpp 复制代码
/**
 ******************************************************************************
 * @file    app_param.c
 * @brief   参数管理应用模块 (裸机版) - EEPROM参数保存
 ******************************************************************************
 */

#include "app_param.h"
#include "drv_eeprom.h"
#include "drv_gain.h"
#include "protocol.h"
#include "app_uart.h"
#include <string.h>

/* EEPROM地址分配 */
#define EEPROM_ADDR_MAGIC       0x0000  // 魔术字地址
#define EEPROM_ADDR_GAIN        0x0004  // 增益档位地址
#define EEPROM_ADDR_USER_PARAM1 0x0008  // 用户参数1
#define EEPROM_ADDR_USER_PARAM2 0x000C  // 用户参数2

/* 魔术字 - 用于判断EEPROM是否已初始化 */
#define PARAM_MAGIC_NUMBER      0x12345678

/* 参数结构体 */
typedef struct {
    uint32_t magic;             // 魔术字
    uint8_t  gain_level;        // 增益档位
    uint32_t user_param1;       // 用户参数1
    uint32_t user_param2;       // 用户参数2
    uint16_t crc;               // CRC校验
} SystemParam_t;

static SystemParam_t system_param = {0};

/**
 * @brief  计算参数CRC校验
 */
static uint16_t param_calc_crc(SystemParam_t *param)
{
    uint16_t crc = 0xFFFF;
    uint8_t *data = (uint8_t *)param;
    
    /* 计算除CRC字段外的所有数据 */
    for (int i = 0; i < sizeof(SystemParam_t) - sizeof(uint16_t); i++) {
        crc ^= data[i];
        for (int j = 0; j < 8; j++) {
            if (crc & 0x0001) {
                crc = (crc >> 1) ^ 0xA001;
            } else {
                crc = crc >> 1;
            }
        }
    }
    
    return crc;
}

/**
 * @brief  从EEPROM加载参数
 */
static int param_load_from_eeprom(void)
{
    uint8_t buffer[sizeof(SystemParam_t)];
    
    /* 从EEPROM读取参数 */
    if (EEPROM_Read(EEPROM_ADDR_MAGIC, buffer, sizeof(SystemParam_t)) != EEPROM_OK) {
        return -1;
    }
    
    /* 复制到参数结构体 */
    memcpy(&system_param, buffer, sizeof(SystemParam_t));
    
    /* 检查魔术字 */
    if (system_param.magic != PARAM_MAGIC_NUMBER) {
        return -1;  // EEPROM未初始化
    }
    
    /* 校验CRC */
    uint16_t calc_crc = param_calc_crc(&system_param);
    if (calc_crc != system_param.crc) {
        return -1;  // CRC校验失败
    }
    
    return 0;
}

/**
 * @brief  保存参数到EEPROM
 */
static int param_save_to_eeprom(void)
{
    uint8_t buffer[sizeof(SystemParam_t)];
    
    /* 计算CRC */
    system_param.crc = param_calc_crc(&system_param);
    
    /* 复制到缓冲区 */
    memcpy(buffer, &system_param, sizeof(SystemParam_t));
    
    /* 写入EEPROM */
    if (EEPROM_Write(EEPROM_ADDR_MAGIC, buffer, sizeof(SystemParam_t)) != EEPROM_OK) {
        return -1;
    }
    
    return 0;
}

/**
 * @brief  参数管理模块初始化
 */
int App_Param_Init(void)
{
    /* 初始化EEPROM驱动 */
    if (EEPROM_Init() != EEPROM_OK) {
        return -1;
    }
    
    /* 从EEPROM加载参数 */
    if (param_load_from_eeprom() != 0) {
        /* 加载失败,使用默认参数 */
        system_param.magic = PARAM_MAGIC_NUMBER;
        system_param.gain_level = GAIN_1X;
        system_param.user_param1 = 0;
        system_param.user_param2 = 0;
        
        /* 保存默认参数 */
        param_save_to_eeprom();
    }
    
    return 0;
}

/**
 * @brief  参数管理任务 (事件触发型)
 */
void App_Param_Task(void)
{
    /* 事件触发型任务,暂无周期性工作 */
}

/**
 * @brief  处理上位机参数命令
 */
void App_Param_OnCommand(ProtocolFrame_t *frame)
{
    uint8_t response_data[5];
    
    if (frame->cmd == CMD_READ_PARAM) {
        /* 读取参数 */
        if (frame->len != 1) {
            return;
        }
        
        uint8_t param_id = frame->data[0];
        
        switch (param_id) {
            case 0:  // 增益档位
                response_data[0] = param_id;
                response_data[1] = system_param.gain_level;
                App_UART_Send(CMD_PARAM_RESP, response_data, 2);
                break;
                
            case 1:  // 用户参数1
                response_data[0] = param_id;
                memcpy(&response_data[1], &system_param.user_param1, 4);
                App_UART_Send(CMD_PARAM_RESP, response_data, 5);
                break;
                
            default:
                break;
        }
        
    } else if (frame->cmd == CMD_SAVE_PARAM) {
        /* 保存参数 */
        if (frame->len < 2) {
            return;
        }
        
        uint8_t param_id = frame->data[0];
        uint8_t result = 0;  // 0=成功 1=失败
        
        switch (param_id) {
            case 0:  // 增益档位
                system_param.gain_level = frame->data[1];
                result = (param_save_to_eeprom() == 0) ? 0 : 1;
                break;
                
            case 1:  // 用户参数1
                if (frame->len >= 5) {
                    memcpy(&system_param.user_param1, &frame->data[1], 4);
                    result = (param_save_to_eeprom() == 0) ? 0 : 1;
                }
                break;
                
            default:
                result = 1;
                break;
        }
        
        /* 发送保存结果 */
        response_data[0] = result;
        App_UART_Send(CMD_SAVE_RESP, response_data, 1);
    }
}

/**
 * @brief  保存增益档位
 */
int App_Param_SaveGain(GainLevel_t level)
{
    system_param.gain_level = level;
    return param_save_to_eeprom();
}

/**
 * @brief  读取增益档位
 */
int App_Param_ReadGain(GainLevel_t *level)
{
    if (level == NULL) {
        return -1;
    }
    
    *level = (GainLevel_t)system_param.gain_level;
    return 0;
}

🚀 第九步:主程序实现---

cpp 复制代码
/**
 ******************************************************************************
 * @file    main.c
 * @brief   主程序入口 (裸机版)
 * @author  Your Name
 * @date    2024-12-09
 * @version 1.0
 ******************************************************************************
 */

#include "stm32f1xx_hal.h"
#include "scheduler.h"

/* 应用层模块 */
#include "app_uart.h"
#include "app_battery.h"
#include "app_gain.h"
#include "app_param.h"

/* 驱动层模块 */
#include "bsp_uart.h"
#include "bsp_i2c.h"
#include "bsp_gpio.h"
#include "bsp_timer.h"

/* 系统配置 */
#include "system_config.h"

/* 系统状态LED */
#define LED_PIN         GPIO_PIN_13
#define LED_PORT        GPIOC

/**
 * @brief  系统时钟配置 (72MHz)
 */
static void SystemClock_Config(void)
{
    RCC_OscInitTypeDef RCC_OscInitStruct = {0};
    RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};

    /* 使能HSE */
    RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
    RCC_OscInitStruct.HSEState = RCC_HSE_ON;
    RCC_OscInitStruct.HSEPredivValue = RCC_HSE_PREDIV_DIV1;
    RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
    RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
    RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL9;  // 8MHz * 9 = 72MHz
    HAL_RCC_OscConfig(&RCC_OscInitStruct);

    /* 配置系统时钟 */
    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;
    HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2);
}

/**
 * @brief  硬件初始化
 */
static void Hardware_Init(void)
{
    /* HAL库初始化 */
    HAL_Init();
    
    /* 系统时钟配置 */
    SystemClock_Config();
    
    /* GPIO初始化 */
    BSP_GPIO_Init();
    
    /* 定时器初始化 (1ms时基) */
    BSP_Timer_Init();
    
    /* UART初始化 */
    BSP_UART_Init();
    
    /* I2C初始化 */
    BSP_I2C_Init();
}

/**
 * @brief  应用层初始化
 */
static void Application_Init(void)
{
    /* 初始化参数管理模块 (最先初始化,其他模块依赖它) */
    if (App_Param_Init() != 0) {
        Error_Handler();
    }
    
    /* 初始化增益控制模块 */
    if (App_Gain_Init() != 0) {
        Error_Handler();
    }
    
    /* 初始化电池监控模块 */
    if (App_Battery_Init() != 0) {
        Error_Handler();
    }
    
    /* 初始化串口通信模块 */
    if (App_UART_Init() != 0) {
        Error_Handler();
    }
}

/**
 * @brief  调度器任务注册
 */
static void Scheduler_TaskRegister(void)
{
    /* 注册串口通信任务 (高优先级,事件触发) */
    Scheduler_RegisterTask(TASK_UART, App_UART_Task, 0, "UART");
    
    /* 注册电池监控任务 (1秒周期) */
    Scheduler_RegisterTask(TASK_BATTERY, App_Battery_Task, 1000, "Battery");
    
    /* 注册增益控制任务 (100ms周期,用于自动保存检测) */
    Scheduler_RegisterTask(TASK_GAIN, App_Gain_Task, 100, "Gain");
    
    /* 注册参数管理任务 (事件触发) */
    Scheduler_RegisterTask(TASK_PARAM, App_Param_Task, 0, "Param");
}

/**
 * @brief  系统启动信息打印
 */
static void Print_SystemInfo(void)
{
    printf("\r\n");
    printf("========================================\r\n");
    printf("  STM32F103C8T6 System Started\r\n");
    printf("  Mode: Bare-Metal (No RTOS)\r\n");
    printf("  Build: %s %s\r\n", __DATE__, __TIME__);
    printf("========================================\r\n");
    printf("Hardware Info:\r\n");
    printf("  CPU Freq:   72 MHz\r\n");
    printf("  Flash Size: 64 KB\r\n");
    printf("  RAM Size:   20 KB\r\n");
    printf("========================================\r\n");
    printf("Tasks Registered:\r\n");
    printf("  1. UART Task      (Event Trigger)\r\n");
    printf("  2. Battery Task   (1000 ms)\r\n");
    printf("  3. Gain Task      (100 ms)\r\n");
    printf("  4. Param Task     (Event Trigger)\r\n");
    printf("========================================\r\n");
    printf("System Running...\r\n\r\n");
}

/**
 * @brief  LED心跳闪烁
 */
static void LED_Heartbeat(void)
{
    static uint32_t last_toggle = 0;
    uint32_t current_tick = Scheduler_GetTick();
    
    /* 每500ms翻转一次 */
    if (current_tick - last_toggle >= 500) {
        HAL_GPIO_TogglePin(LED_PORT, LED_PIN);
        last_toggle = current_tick;
    }
}

/**
 * @brief  主函数
 */
int main(void)
{
    /* 1. 硬件初始化 */
    Hardware_Init();
    
    /* 2. 调度器初始化 */
    Scheduler_Init();
    
    /* 3. 应用层初始化 */
    Application_Init();
    
    /* 4. 注册任务 */
    Scheduler_TaskRegister();
    
    /* 5. 打印系统信息 */
    Print_SystemInfo();
    
    /* 6. 主循环 */
    while (1)
    {
        /* 串口任务 - 最高优先级,立即处理 */
        App_UART_Task();
        
        /* 调度器运行 - 处理周期性任务 */
        Scheduler_Run();
        
        /* LED心跳指示 */
        LED_Heartbeat();
        
        /* 可选:进入低功耗模式 */
        // __WFI();  // 等待中断
    }
}

/**
 * @brief  1ms定时器中断回调函数
 */
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
    /* 更新调度器时基 */
    Scheduler_TickUpdate();
}

/**
 * @brief  错误处理函数
 */
void Error_Handler(void)
{
    /* 关闭中断 */
    __disable_irq();
    
    /* LED快闪表示错误 */
    while (1) {
        HAL_GPIO_TogglePin(LED_PORT, LED_PIN);
        for (volatile uint32_t i = 0; i < 100000; i++);
    }
}

/**
 * @brief  Assert失败处理
 */
void assert_failed(uint8_t *file, uint32_t line)
{
    printf("Assert failed: file %s on line %d\r\n", file, line);
    Error_Handler();
}

📊 裸机方案总结对比

1. 裸机 v

s RTOS 对比表

对比项 裸机方案 FreeRTOS方案
代码复杂度 ⭐⭐⭐ 简单直观 ⭐⭐⭐⭐ 需要理解RTOS概念
Flash占用 ~30KB ~42KB (+12KB)
RAM占用 ~5KB ~10KB (+5KB)
实时响应 ⭐⭐⭐⭐ 中断响应快 ⭐⭐⭐⭐⭐ 任务调度更灵活
扩展性 ⭐⭐⭐ 需要修改主循环 ⭐⭐⭐⭐⭐ 添加任务即可
调试难度 ⭐⭐⭐⭐ 逻辑清晰 ⭐⭐⭐ 任务切换需要工具
功耗 ⭐⭐⭐⭐⭐ 更低 ⭐⭐⭐⭐ 略高
开发效率 ⭐⭐⭐⭐ 对简单项目快 ⭐⭐⭐⭐⭐ 对复杂项目快
适用场景 简单项目、资源受限 中大型项目、复杂逻辑

2. 裸机方案的核心设计

中断 数据缓冲 主循环处理 调度器 周期任务 事件任务 业务逻辑

关键设计点

  1. 中断 + 缓冲区:串口接收使用中断+环形缓冲区,避免数据丢失
  2. 软件调度器:实现周期任务和事件触发任务的统一调度
  3. 状态机:每个模块内部使用状态机管理复杂逻辑
  4. 优先级处理:主循环中按优先级顺序调用各模块

3. 资源占用估算(裸机版)

项目 Flash占用 RAM占用 说明
HAL库 ~15KB ~1KB STM32 HAL驱动
调度器 ~2KB ~200B 软件定时调度
协议模块 ~3KB ~512B 协议解析
串口应用 ~2KB ~512B 包含环形缓冲区
电池应用 ~2KB ~128B
增益应用 ~2KB ~128B
参数应用 ~3KB ~256B
驱动层 ~8KB ~512B UART/I2C/GPIO等
总计 ~37KB ~3KB
剩余 ~27KB ~17KB 充足!

资源占用更少,性能更优!


🎯 开发流程(裸机版)

第1周:基础框架搭建

复制代码
Day 1-2: STM32CubeMX工程生成
  - 配置时钟树 (72MHz)
  - 配置UART1、I2C1、GPIO
  - 配置TIM2作为1ms时基
  - 生成HAL库工程

Day 3-4: BSP层开发
  - 实现bsp_uart.c (中断接收)
  - 实现bsp_i2c.c
  - 实现bsp_gpio.c
  - 实现bsp_timer.c (1ms中断)

Day 5: 调度器开发
  - 实现scheduler.c
  - 测试定时任务调度

第2周:驱动层开发

复制代码
Day 1-2: 设备驱动
  - drv_gain.c (增益控制) ✅已提供
  - drv_battery.c (根据你的电量芯片型号)
  - drv_eeprom.c (AT24C02/AT24C08)

Day 3-4: 协议模块
  - protocol.c (协议解析) ✅已提供
  - ringbuffer.c (环形缓冲区)
  - crc.c (CRC16校验)

Day 5: 驱动测试
  - 单元测试各驱动模块

第3周:应用层开发

复制代码
Day 1: 串口通信应用 ✅已提供
  - app_uart.c
  - 测试上位机收发

Day 2: 电池监控应用 ✅已提供
  - app_battery.c
  - 测试电量读取

Day 3: 增益控制应用 ✅已提供
  - app_gain.c
  - 测试增益切换

Day 4: 参数管理应用 ✅已提供
  - app_param.c
  - 测试EEPROM读写

Day 5: 集成测试
  - 主程序main.c ✅已提供
  - 整体功能测试

第4周:优化与测试

复制代码
Day 1-2: 功能测试
  - 完整功能测试
  - 边界条件测试

Day 3-4: 性能优化
  - 代码优化
  - 内存优化

Day 5: 文档编写
  - 技术文档
  - 用户手册

📝 辅助工具代码---

✅ 总结:裸机方案完整设计

我已经为你提供了一套完整的裸机开发方案,包括:

已提供的完整代码:

  1. 调度器模块 (scheduler.c/h) - 软件定时调度核心
  2. 协议模块 (protocol.h) - 自定义通信协议
  3. 串口应用 (app_uart.c) - 中断+缓冲区方案
  4. 电池应用 (app_battery.c) - 周期监控
  5. 增益应用 (app_gain.c) - 事件触发控制
  6. 参数应用 (app_param.c) - EEPROM保存
  7. 增益驱动 (drv_gain.c/h) - GPIO控制
  8. 环形缓冲区 (ringbuffer.c/h) - 数据缓冲
  9. 主程序 (main.c) - 完整主循环框架

核心设计特点:

  1. 中断 + 主循环:串口中断接收,主循环处理
  2. 软件调度器:统一管理周期任务和事件任务
  3. 模块化设计:每个功能独立模块,低耦合
  4. 黑盒原则:模块内部变量static,只暴露API
  5. 资源占用低:Flash ~37KB,RAM ~3KB

开发建议:

推荐使用裸机方案,因为:

  • ✅ 你的项目功能相对简单
  • ✅ 资源占用更少(省12KB Flash)
  • ✅ 代码更直观,调试更容易
  • ✅ 功耗更低
  • ✅ 完全满足需求

下一步:

  1. 使用STM32CubeMX生成工程
  2. 将我提供的代码集成到工程
  3. 根据你的实际电量芯片型号 编写drv_battery.c
  4. 测试验证

STM32F103C8T6 裸机开发详细设计文档

📋 目录

  1. 系统整体架构
  2. 模块依赖关系
  3. 各层模块详细设计
  4. 任务派发清单

1. 系统整体架构

1.1 系统分层架构图

HAL层 驱动层 Driver Layer 服务层 Service Layer 应用层 Application Layer STM32 HAL库 bsp_uart.c
UART驱动 bsp_i2c.c
I2C驱动 bsp_gpio.c
GPIO驱动 bsp_timer.c
定时器驱动 drv_battery.c
电量芯片驱动 drv_eeprom.c
EEPROM驱动 drv_gain.c
增益控制驱动 scheduler.c
软件定时调度器 protocol.c
通信协议解析 ringbuffer.c
环形缓冲区 crc.c
CRC校验 app_main.c
主程序入口 app_uart.c
串口通信应用 app_battery.c
电池监控应用 app_gain.c
增益控制应用 app_param.c
参数管理应用

1.2 系统运行时序图

上电 main() 硬件初始化 应用初始化 调度器 中断服务 系统启动 Hardware_Init() 时钟配置(72MHz) GPIO初始化 UART初始化 I2C初始化 Timer初始化(1ms) Scheduler_Init() 清空任务列表 Application_Init() 参数模块初始化 增益模块初始化 电池模块初始化 串口模块初始化 注册任务 注册UART任务 注册电池任务(1s) 注册增益任务(100ms) 进入主循环 App_UART_Task() Scheduler_Run() 调用到期任务 1ms定时器中断 Tick计数++ UART接收中断 数据入环形缓冲区 loop [主循环] 上电 main() 硬件初始化 应用初始化 调度器 中断服务


2. 模块依赖关系

2.1 模块依赖关系矩阵

模块 依赖模块 被依赖模块
app_main.c scheduler, app_uart, app_battery, app_gain, app_param
scheduler.c app_main, app_uart, app_battery, app_gain
app_uart.c protocol, bsp_uart, ringbuffer app_main, app_battery, app_gain, app_param
app_battery.c drv_battery, scheduler app_main
app_gain.c drv_gain, app_param, scheduler app_main
app_param.c drv_eeprom app_main, app_gain
protocol.c crc app_uart
bsp_uart.c HAL_UART, ringbuffer app_uart
bsp_i2c.c HAL_I2C drv_battery, drv_eeprom
drv_battery.c bsp_i2c app_battery
drv_eeprom.c bsp_i2c app_param
drv_gain.c bsp_gpio app_gain

2.2 模块调用关系图

main.c scheduler.c app_uart.c app_battery.c app_gain.c app_param.c protocol.c bsp_uart.c ringbuffer.c drv_battery.c drv_gain.c drv_eeprom.c crc.c bsp_i2c.c bsp_gpio.c HAL_UART HAL_I2C HAL_GPIO


3. 各层模块详细设计


3.1 服务层模块


模块 1: scheduler.c - 软件定时调度器

1ms定时器中断 调度器 任务列表 时基更新流程 Scheduler_TickUpdate() system_tick++ 检查任务是否使能 elapsed++ alt [周期任务且使能] loop [遍历所有任务] 主循环调度流程 Scheduler_Run() 检查任务状态 跳过 跳过(由外部触发) 执行任务函数 elapsed = 0 alt [未注册或未使能] [事件触发型] [到达周期时间] loop [遍历所有任务] 1ms定时器中断 调度器 任务列表

模块职责
  • 提供1ms系统时基
  • 管理周期性任务调度
  • 支持事件触发型任务
  • 提供系统时间戳服务
数据结构设计
c 复制代码
/* 任务ID枚举 */
typedef enum {
    TASK_UART = 0,      // 串口通信任务
    TASK_BATTERY,       // 电池监控任务
    TASK_GAIN,          // 增益控制任务
    TASK_PARAM,         // 参数保存任务
    TASK_MAX            // 任务总数
} TaskID_t;

/* 任务函数指针 */
typedef void (*TaskFunc_t)(void);

/* 任务控制块 */
typedef struct {
    TaskFunc_t  func;       // 任务函数指针
    uint32_t    period;     // 任务周期(ms), 0=事件触发
    uint32_t    elapsed;    // 已经过时间(ms)
    uint8_t     enabled;    // 任务使能标志
    const char  *name;      // 任务名称(用于调试)
} Task_t;
对外API接口
函数原型 功能描述 调用位置 返回值
int Scheduler_Init(void) 初始化调度器 main()初始化阶段 0=成功 -1=失败
int Scheduler_RegisterTask(TaskID_t id, TaskFunc_t func, uint32_t period, const char *name) 注册任务 main()初始化阶段 0=成功 -1=失败
void Scheduler_EnableTask(TaskID_t id) 启用任务 任意位置
void Scheduler_DisableTask(TaskID_t id) 禁用任务 任意位置
void Scheduler_TriggerTask(TaskID_t id) 触发事件型任务立即执行 事件发生时
void Scheduler_Run(void) 调度器主循环 main()的while(1)
void Scheduler_TickUpdate(void) 1ms时基更新 定时器中断
uint32_t Scheduler_GetTick(void) 获取系统运行时间 任意位置 运行时间(ms)
内部实现细节
c 复制代码
/* 私有变量 */
static volatile uint32_t system_tick = 0;  // 系统时基计数
static Task_t task_list[TASK_MAX];         // 任务列表

/* 调度策略 */
- 周期任务:时间到达时自动执行
- 事件任务:通过Scheduler_TriggerTask()触发
- 优先级:按任务注册顺序执行(UART任务最先注册,优先级最高)
使用示例
c 复制代码
/* 在main()中初始化 */
Scheduler_Init();

/* 注册任务 */
Scheduler_RegisterTask(TASK_UART, App_UART_Task, 0, "UART");          // 事件触发
Scheduler_RegisterTask(TASK_BATTERY, App_Battery_Task, 1000, "BAT"); // 1秒周期

/* 在定时器中断中更新时基 */
void TIM2_IRQHandler(void) {
    if (__HAL_TIM_GET_FLAG(&htim2, TIM_FLAG_UPDATE)) {
        __HAL_TIM_CLEAR_FLAG(&htim2, TIM_FLAG_UPDATE);
        Scheduler_TickUpdate();
    }
}

/* 在main()主循环中运行 */
while(1) {
    Scheduler_Run();
}

模块 2: protocol.c - 通信协议解析

< 最小长度 >= 最小长度 不是0xAA55 是0xAA55 不是0x0D0A 是0x0D0A 不一致 一致 失败 成功 开始解析 检查长度 返回错误 检查帧头 检查帧尾 提取数据长度 验证长度 计算CRC16 校验CRC 解析命令码 提取数据部分 调用回调函数 返回成功

模块职责
  • 解析上位机
  • 发送的数据帧
  • 封装发送给上位机的数据帧
  • CRC16校验
  • 协议状态管理
协议格式定义
复制代码
帧格式:
┌─────────┬─────────┬─────────┬─────────┬─────────┬─────────┐
│  帧头   │ 命令码  │ 数据长度│  数据   │ CRC16   │  帧尾   │
│ (2字节) │ (1字节) │ (1字节) │(N字节)  │ (2字节) │ (2字节) │
│ 0xAA55  │  CMD    │  LEN    │  DATA   │  CRC    │ 0x0D0A  │
└─────────┴─────────┴─────────┴─────────┴─────────┴─────────┘

最小帧长:8字节 (无数据时)
最大帧长:8 + 128 = 136字节
命令码定义
c 复制代码
/* 命令码枚举 */
typedef enum {
    CMD_READ_BATTERY    = 0x01,  // 读取电量
    CMD_BATTERY_RESP    = 0x81,  // 电量响应
    
    CMD_SET_GAIN        = 0x02,  // 设置增益
    CMD_GAIN_RESP       = 0x82,  // 增益响应
    
    CMD_READ_PARAM      = 0x03,  // 读取参数
    CMD_PARAM_RESP      = 0x83,  // 参数响应
    
    CMD_SAVE_PARAM      = 0x04,  // 保存参数
    CMD_SAVE_RESP       = 0x84,  // 保存响应
    
    CMD_HEARTBEAT       = 0xFF   // 心跳包
} ProtocolCmd_t;
数据结构设计
c 复制代码
/* 协议帧结构 */
typedef struct {
    uint8_t  cmd;               // 命令码
    uint8_t  len;               // 数据长度
    uint8_t  data[128];         // 数据缓冲区
    uint16_t crc;               // CRC校验值
} ProtocolFrame_t;

/* 协议状态 */
typedef enum {
    PROTOCOL_OK = 0,
    PROTOCOL_ERROR_LENGTH,      // 长度错误
    PROTOCOL_ERROR_HEADER,      // 帧头错误
    PROTOCOL_ERROR_TAIL,        // 帧尾错误
    PROTOCOL_ERROR_CRC,         // CRC错误
} ProtocolStatus_t;

/* 回调函数类型 */
typedef void (*ProtocolCallback_t)(ProtocolFrame_t *frame);
对外API接口
函数原型 功能描述 调用位置 返回值
int Protocol_Init(ProtocolCallback_t callback) 初始化协议模块 app_uart初始化 0=成功
ProtocolStatus_t Protocol_Receive(uint8_t *data, uint16_t len) 接收并解析数据帧 UART接收完整帧后 状态码
int Protocol_Send(uint8_t cmd, uint8_t *data, uint8_t len) 封装并发送数据帧 需要发送数据时 0=成功 -1=失败
uint16_t Protocol_CalcCRC16(uint8_t *data, uint16_t len) 计算CRC16 内部使用 CRC值
协议示例
c 复制代码
/* 示例1:上位机读取电量 */
发送: AA 55 01 00 [CRC16] 0D 0A
接收: AA 55 81 04 4B 10 68 01 [CRC16] 0D 0A
      // 电量75% 电压4200mV 充电中

/* 示例2:上位机设置增益为10x */
发送: AA 55 02 01 03 [CRC16] 0D 0A  // 档位3
接收: AA 55 82 01 03 [CRC16] 0D 0A  // 确认设置为档位3

/* 示例3:保存参数到EEPROM */
发送: AA 55 04 05 00 00 00 00 0A [CRC16] 0D 0A
      // 参数ID=0, 值=10
接收: AA 55 84 01 00 [CRC16] 0D 0A  // 保存成功

模块 3: ringbuffer.c - 环形缓冲区

模块职责
  • 提供FIFO数据缓冲
  • 用于串口接收数据缓存
  • 线程安全(中断安全)
数据结构设计
c 复制代码
/* 环形缓冲区结构 */
typedef struct {
    uint8_t  *buffer;       // 缓冲区指针
    uint16_t  size;         // 缓冲区大小
    uint16_t  head;         // 写指针
    uint16_t  tail;         // 读指针
    uint16_t  count;        // 当前数据量
} RingBuffer_t;
对外API接口
函数原型 功能描述 调用位置 返回值
void RingBuffer_Init(RingBuffer_t *rb, uint8_t *buf, uint16_t size) 初始化环形缓冲区 模块初始化
int RingBuffer_Write(RingBuffer_t *rb, uint8_t data) 写入一个字节 中断中 0=成功 -1=缓冲区满
int RingBuffer_Read(RingBuffer_t *rb, uint8_t *data) 读取一个字节 主循环 0=成功 -1=缓冲区空
uint16_t RingBuffer_Available(RingBuffer_t *rb) 获取可读数据量 任意 字节数
uint8_t RingBuffer_IsEmpty(RingBuffer_t *rb) 判断是否为空 任意 1=空 0=非空
uint8_t RingBuffer_IsFull(RingBuffer_t *rb) 判断是否已满 任意 1=满 0=非满
void RingBuffer_Clear(RingBuffer_t *rb) 清空缓冲区 任意
内部流程图

是 否 是 否 中断接收数据 RingBuffer_Write 缓冲区满? 丢弃数据 写入buffer head++ 主循环读取 RingBuffer_Read 缓冲区空? 返回失败 读取buffer tail++


模块 4: crc.c - CRC16校验

模块职责
  • 计算CRC16-MODBUS校验值
  • 用于协议数据完整性校验
算法实现
c 复制代码
/* CRC16-MODBUS算法 */
多项式:0xA001
初始值:0xFFFF
对外API接口
函数原型 功能描述 调用位置 返回值
uint16_t CRC16_Calculate(uint8_t *data, uint16_t len) 计算CRC16 协议模块 CRC值
uint8_t CRC16_Verify(uint8_t *data, uint16_t len, uint16_t crc) 验证CRC 协议模块 1=正确 0=错误

3.2 驱动层模块(BSP层)


模块 5: bsp_uart.c - UART驱动

上位机 UART硬件 UART中断 环形缓冲区 主循环 发送字节1 触发接收中断 RingBuffer_Write(byte1) 设置新数据标志 发送字节2 触发接收中断 RingBuffer_Write(byte2) 发送字节N 触发接收中断 RingBuffer_Write(byteN) 主循环检测到新数据 RingBuffer_Read() 返回数据 协议解析 处理命令 上位机 UART硬件 UART中断 环形缓冲区 主循环

模块职责
  • 初始化UART1硬件(115200,8,N,1)
  • 配置中断接收
  • 提供发送接口
  • 管理接收环形缓冲区
硬件配置
c 复制代码
/* UART1配置 */
引脚:PA9(TX), PA10(RX)
波特率:115200
数据位:8
停止位:1
校验:None
模式:中断接收 + 阻塞发送
数据结构设计
c 复制代码
/* UART配置结构 */
typedef struct {
    USART_TypeDef   *instance;      // UART实例
    uint32_t        baudrate;       // 波特率
    IRQn_Type       irq_type;       // 中断类型
} BSP_UART_Config_t;

/* UART句柄 */
static UART_HandleTypeDef huart1;

/* 接收缓冲区 */
#define UART_RX_BUFFER_SIZE  256
static uint8_t uart_rx_buffer[UART_RX_BUFFER_SIZE];
static RingBuffer_t uart_rx_ring;
对外API接口
函数原型 功能描述 调用位置 返回值
int BSP_UART_Init(void) 初始化UART硬件 main()初始化 0=成功 -1=失败
int BSP_UART_Send(uint8_t *data, uint16_t len) 发送数据 协议发送时 0=成功 -1=失败
int BSP_UART_Read(uint8_t *data) 从缓冲区读取一字节 app_uart任务 0=成功 -1=无数据
uint16_t BSP_UART_Available(void) 获取可读数据量 任意 字节数
void BSP_UART_RxCallback(uint8_t data) 接收中断回调 中断内部调用
中断配置
c 复制代码
/* 中断优先级 */
NVIC_SetPriority(USART1_IRQn, 2);  // 较高优先级
NVIC_EnableIRQ(USART1_IRQn);

/* 中断处理函数 */
void USART1_IRQHandler(void) {
    if(__HAL_UART_GET_FLAG(&huart1, UART_FLAG_RXNE)) {
        uint8_t data = (uint8_t)(huart1.Instance->DR & 0xFF);
        RingBuffer_Write(&uart_rx_ring, data);
    }
}

模块 6: bsp_i2c.c - I2C驱动

模块职责
  • 初始化I2C1硬件
  • 提供读写字节/多字节接口
  • 支持多设备(电量芯片、EEPROM)
硬件配置
c 复制代码
/* I2C1配置 */
引脚:PB6(SCL), PB7(SDA)
速率:100KHz (标准模式)
模式:Master模式
地址模式:7位地址
数据结构设计
c 复制代码
/* I2C句柄 */
static I2C_HandleTypeDef hi2c1;

/* I2C设备地址 */
#define I2C_ADDR_BATTERY    0x55    // 电量芯片地址
#define I2C_ADDR_EEPROM     0x50    // EEPROM地址
对外API接口
函数原型 功能描述 调用位置 返回值
int BSP_I2C_Init(void) 初始化I2C硬件 main()初始化 0=成功 -1=失败
int BSP_I2C_WriteByte(uint8_t dev_addr, uint8_t reg_addr, uint8_t data) 写一个字节 设备驱动 0=成功 -1=失败
int BSP_I2C_ReadByte(uint8_t dev_addr, uint8_t reg_addr, uint8_t *data) 读一个字节 设备驱动 0=成功 -1=失败
int BSP_I2C_WriteBytes(uint8_t dev_addr, uint8_t reg_addr, uint8_t *data, uint16_t len) 写多字节 设备驱动 0=成功 -1=失败
int BSP_I2C_ReadBytes(uint8_t dev_addr, uint8_t reg_addr, uint8_t *data, uint16_t len) 读多字节 设备驱动 0=成功 -1=失败
时序图

CPU I2C硬件 I2C设备 写操作时序 发送START 发送设备地址+W ACK 发送寄存器地址 ACK 发送数据 ACK 发送STOP 读操作时序 发送START 发送设备地址+W ACK 发送寄存器地址 ACK 发送RESTART 发送设备地址+R ACK 发送数据 ACK 发送STOP CPU I2C硬件 I2C设备


模块 7: bsp_gpio.c - GPIO驱动

模块职责
  • 初始化所有GPIO引脚
  • 提供GPIO读写接口
  • 配置增益控制引脚
引脚配置表
引脚 功能 模式 初始状态
PA0 GAIN_CTRL0 推挽输出 低电平
PA1 GAIN_CTRL1 推挽输出 低电平
PA2 GAIN_CTRL2 推挽输出 低电平
PC13 LED_STATUS 推挽输出 高电平(LED灭)
对外API接口
函数原型 功能描述 调用位置 返回值
int BSP_GPIO_Init(void) 初始化所有GPIO main()初始化 0=成功
void BSP_GPIO_Write(GPIO_TypeDef *port, uint16_t pin, uint8_t state) 写GPIO 任意
uint8_t BSP_GPIO_Read(GPIO_TypeDef *port, uint16_t pin) 读GPIO 任意 引脚状态
void BSP_GPIO_Toggle(GPIO_TypeDef *port, uint16_t pin) 翻转GPIO 任意

模块 8: bsp_timer.c - 定时器驱动

模块职责
  • 配置TIM2产生1ms定时中断
  • 为调度器提供时基
定时器配置
c 复制代码
/* TIM2配置 */
时钟源:APB1 (72MHz / 2 = 36MHz)
预分频:36000 - 1  (得到1KHz)
计数周期:1 - 1     (1ms中断一次)
中断优先级:3 (较低)
对外API接口
函数原型 功能描述 调用位置 返回值
int BSP_Timer_Init(void) 初始化定时器 main()初始化 0=成功
void BSP_Timer_Start(void) 启动定时器 初始化完成后
void BSP_Timer_Stop(void) 停止定时器 需要时
中断处理
c 复制代码
void TIM2_IRQHandler(void) {
    if (__HAL_TIM_GET_FLAG(&htim2, TIM_FLAG_UPDATE)) {
        __HAL_TIM_CLEAR_FLAG(&htim2, TIM_FLAG_UPDATE);
        
        /* 调用调度器时基更新 */
        Scheduler_TickUpdate();
    }
}

3.3 驱动层模块(设备驱动)


模块 9: drv_battery.c - 电量芯片驱动

模块职责
  • 与电量芯片通信(I2C)
  • 读取电量百分比、电压、电流
  • 判断充电状态
支持的电量芯片
c 复制代码
/* 常见电量芯片 */
- BQ27441 (TI)
- MAX17048/MAX17049 (Maxim)
- LC709203F (ON Semiconductor)

/* 根据实际使用的芯片填写寄存器地址 */
数据结构设计
c 复制代码
/* 电量芯片状态码 */
typedef enum {
    BATTERY_OK = 0,
    BATTERY_ERROR = -1,
    BATTERY_ERROR_COMM = -2,    // 通信错误
    BATTERY_ERROR_TIMEOUT = -3  // 超时
} BatteryStatus_t;

/* 电池数据结构 */
typedef struct {
    uint8_t  percentage;        // 电量百分比 (0-100)
    uint16_t voltage;           // 电压 (mV)
    int16_t  current;           // 电流 (mA), 正=充电 负=放电
    uint8_t  is_charging;       // 充电状态 (1=充电中 0=未充电)
    uint32_t timestamp;         // 时间戳 (ms)
} BatteryData_t;

/* 寄存器地址 (以BQ27441为例) */
#define REG_STATE_OF_CHARGE     0x1C    // 电量百分比
#define REG_VOLTAGE             0x04    // 电压
#define REG_AVERAGE_CURRENT     0x10    // 电流
#define REG_FLAGS               0x06    // 状态标志
对外API接口
函数原型 功能描述 调用位置 返回值
BatteryStatus_t Battery_Init(void) 初始化电量芯片 app_battery初始化 状态码
BatteryStatus_t Battery_Read(BatteryData_t *data) 读取电池数据 app_battery任务 状态码
uint8_t Battery_GetPercentage(void) 快速读取电量百分比 任意 电量%
uint8_t Battery_IsCharging(void) 判断是否充电 任意 1=充电 0=未充电
内部流程图

否 是 否 是 否 是 否 是 Battery_Read I2C读取电量寄存器 通信成功? 返回ERROR_COMM I2C读取电压寄存器 通信成功? I2C读取电流寄存器 通信成功? I2C读取状态标志 通信成功? 解析充电状态 填充BatteryData_t 返回OK

使用示例
c 复制代码
/* 初始化 */
if (Battery_Init() != BATTERY_OK) {
    printf("Battery init failed!\r\n");
}

/* 读取电池数据 */
BatteryData_t battery_data;
if (Battery_Read(&battery_data) == BATTERY_OK) {
    printf("电量: %d%%\r\n", battery_data.percentage);
    printf("电压: %d mV\r\n", battery_data.voltage);
    printf("电流: %d mA\r\n", battery_data.current);
    printf("充电状态: %s\r\n", battery_data.is_charging ? "充电中" : "未充电");
}

模块 10: drv_eeprom.c - EEPROM驱动

模块职责
  • 与EEPROM通信(I2C)
  • 提供字节/页写入
  • 提供字节/连续读取
  • 掉电保存参数
支持的EEPROM型号
c 复制代码
/* 常见EEPROM芯片 */
- AT24C02 (256字节)
- AT24C08 (1KB)
- AT24C16 (2KB)
- AT24C64 (8KB)
数据结构设计
c 复制代码
/* EEPROM状态码 */
typedef enum {
    EEPROM_OK = 0,
    EEPROM_ERROR = -1,
    EEPROM_ERROR_COMM = -2,     // 通信错误
    EEPROM_ERROR_TIMEOUT = -3,  // 超时
    EEPROM_ERROR_ADDR = -4      // 地址越界
} EEPROMStatus_t;

/* EEPROM配置 */
#define EEPROM_I2C_ADDR     0x50    // EEPROM设备地址
#define EEPROM_SIZE         256     // EEPROM容量(字节)
#define EEPROM_PAGE_SIZE    8       // 页大小(字节)
对外API接口
函数原型 功能描述 调用位置 返回值
EEPROMStatus_t EEPROM_Init(void) 初始化EEPROM app_param初始化 状态码
EEPROMStatus_t EEPROM_WriteByte(uint16_t addr, uint8_t data) 写一个字节 参数保存 状态码
EEPROMStatus_t EEPROM_ReadByte(uint16_t addr, uint8_t *data) 读一个字节 参数读取 状态码
EEPROMStatus_t EEPROM_Write(uint16_t addr, uint8_t *data, uint16_t len) 写多字节 参数保存 状态码
EEPROMStatus_t EEPROM_Read(uint16_t addr, uint8_t *data, uint16_t len) 读多字节 参数读取 状态码
写入流程(页写入)

否 是 否 是 否 是 EEPROM_Write 地址+长度<容量? 返回ERROR_ADDR 计算起始页 是否跨页? 一次写入全部数据 分页写入 写入第一页剩余空间 写入中间完整页 写入最后页 等待写入完成5ms 验证写入 验证成功? 返回OK


模块 11: drv_gain.c - 增益控制驱动

模块职责
  • 通过GPIO控制放大器增益
  • 支持多档增益切换
  • 提供增益档位查询
硬件电路
复制代码
STM32                   放大器芯片
PA0 ─────> GAIN_CTRL0 ───┐
PA1 ─────> GAIN_CTRL1 ───┼──> 增益控制输入
PA2 ─────> GAIN_CTRL2 ───┘
数据结构设计
c 复制代码
/* 增益状态码 */
typedef enum {
    GAIN_OK = 0,
    GAIN_ERROR = -1,
    GAIN_ERROR_INVALID_LEVEL = -2  // 无效档位
} GainStatus_t;

/* 增益档位枚举 */
typedef enum {
    GAIN_1X = 0,    // 1倍   (000)
    GAIN_2X,        // 2倍   (001)
    GAIN_5X,        // 5倍   (010)
    GAIN_10X,       // 10倍  (011)
    GAIN_20X,       // 20倍  (100)
    GAIN_50X,       // 50倍  (101)
    GAIN_100X,      // 100倍 (110)
    GAIN_200X,      // 200倍 (111)
    GAIN_MAX        // 档位总数
} GainLevel_t;

/* 增益配置表 */
typedef struct {
    GainLevel_t level;  // 档位
    uint8_t     value;  // 倍数
    uint8_t     ctrl0;  // CTRL0状态
    uint8_t     ctrl1;  // CTRL1状态
    uint8_t     ctrl2;  // CTRL2状态
} GainConfig_t;

/* 增益配置表 */
static const GainConfig_t gain_table[GAIN_MAX] = {
    {GAIN_1X,   1,   0, 0, 0},
    {GAIN_2X,   2,   1, 0, 0},
    {GAIN_5X,   5,   0, 1, 0},
    {GAIN_10X,  10,  1, 1, 0},
    {GAIN_20X,  20,  0, 0, 1},
    {GAIN_50X,  50,  1, 0, 1},
    {GAIN_100X, 100, 0, 1, 1},
    {GAIN_200X, 200, 1, 1, 1}
};
对外API接口
函数原型 功能描述 调用位置 返回值
GainStatus_t Gain_Init(void) 初始化增益控制 app_gain初始化 状态码
GainStatus_t Gain_SetLevel(GainLevel_t level) 设置增益档位 app_gain 状态码
GainLevel_t Gain_GetLevel(void) 获取当前档位 任意 档位
uint8_t Gain_GetValue(GainLevel_t level) 根据档位获取倍数 任意 倍数
内部实现
c 复制代码
GainStatus_t Gain_SetLevel(GainLevel_t level) {
    if (level >= GAIN_MAX) {
        return GAIN_ERROR_INVALID_LEVEL;
    }
    
    /* 从配置表读取GPIO状态 */
    uint8_t ctrl0 = gain_table[level].ctrl0;
    uint8_t ctrl1 = gain_table[level].ctrl1;
    uint8_t ctrl2 = gain_table[level].ctrl2;
    
    /* 设置GPIO */
    HAL_GPIO_WritePin(GPIOA, GPIO_PIN_0, ctrl0 ? GPIO_PIN_SET : GPIO_PIN_RESET);
    HAL_GPIO_WritePin(GPIOA, GPIO_PIN_1, ctrl1 ? GPIO_PIN_SET : GPIO_PIN_RESET);
    HAL_GPIO_WritePin(GPIOA, GPIO_PIN_2, ctrl2 ? GPIO_PIN_SET : GPIO_PIN_RESET);
    
    /* 保存当前档位 */
    current_level = level;
    
    return GAIN_OK;
}

3.4 应用层模块


模块 12: app_uart.c - 串口通信应用#### 模块职责

  • 接收上位机命令
  • 解析协议帧
  • 分发命令到对应模块
  • 发送响应给上位机
数据结构设计
c 复制代码
/* 帧缓冲区 */
#define FRAME_BUFFER_SIZE   128
static uint8_t frame_buffer[FRAME_BUFFER_SIZE];
static uint16_t frame_index = 0;

/* 新数据标志 */
static volatile uint8_t uart_new_data_flag = 0;
对外API接口
函数原型 功能描述 调用位置 返回值
int App_UART_Init(void) 初始化串口应用 main()初始化 0=成功 -1=失败
void App_UART_Task(void) 串口通信任务 主循环高优先级调用
int App_UART_Send(uint8_t cmd, uint8_t *data, uint8_t len) 发送数据到上位机 其他模块 0=成功 -1=失败
void UART_RxCallback(uint8_t data) 接收中断回调 中断中调用

模块 13: app_battery.c - 电池监控应用

模块职责
  • 周期读取电池数据(1秒)
  • 处理上位机电量查询命令
  • 低电量告警
  • 电量数据上报
数据结构设计
c 复制代码
/* 电池状态结构 */
typedef struct {
    uint8_t  percentage;        // 电量百分比
    uint16_t voltage;           // 电压(mV)
    int16_t  current;           // 电流(mA)
    uint8_t  is_charging;       // 充电状态
    uint32_t last_update_time;  // 上次更新时间
} BatteryStatus_t;

static BatteryStatus_t battery_status = {0};

/* 告警阈值 */
#define BATTERY_LOW_THRESHOLD   20   // 低电量告警20%
#define BATTERY_CRITICAL        10   // 严重低电量10%
对外API接口
函数原型 功能描述 调用位置 返回值
int App_Battery_Init(void) 初始化电池应用 main()初始化 0=成功 -1=失败
void App_Battery_Task(void) 电池监控任务(1秒周期) 调度器
void App_Battery_OnCommand(ProtocolFrame_t *frame) 处理电量命令 协议回调
uint8_t App_Battery_GetPercentage(void) 获取当前电量 任意 电量%
uint16_t App_Battery_GetVoltage(void) 获取当前电压 任意 电压(mV)
uint8_t App_Battery_IsCharging(void) 获取充电状态 任意 1=充电 0=未充电
任务流程图

否 是 是 否 是 否 App_Battery_Task
1秒周期执行 调用Battery_Read 读取成功? 保持上次数据 更新battery_status 电量<10%? 严重低电量处理 电量<20%? 低电量告警 正常 可选:进入低功耗 可选:上报告警 任务结束


模块 14: app_gain.c - 增益控制应用

模块职责
  • 处理增益设置命令
  • 控制放大器增益
  • 自动保存增益参数
  • 提供增益查询接口
数据结构设计
c 复制代码
/* 增益控制状态 */
typedef struct {
    GainLevel_t current_level;      // 当前增益档位
    uint8_t     need_save;          // 是否需要保存
    uint32_t    last_change_time;   // 上次修改时间
} GainStatus_t;

static GainStatus_t gain_status = {0};

/* 自动保存延迟 */
#define AUTO_SAVE_DELAY_MS  5000    // 5秒无变化后保存
对外API接口
函数原型 功能描述 调用位置 返回值
int App_Gain_Init(void) 初始化增益应用 main()初始化 0=成功 -1=失败
void App_Gain_Task(void) 增益控制任务(100ms周期) 调度器
void App_Gain_OnCommand(ProtocolFrame_t *frame) 处理增益命令 协议回调
int App_Gain_SetLevel(GainLevel_t level) 设置增益档位 任意 0=成功 -1=失败
GainLevel_t App_Gain_GetLevel(void) 获取当前档位 任意 档位
uint8_t App_Gain_GetValue(void) 获取当前倍数 任意 倍数
任务流程图

否 是 否 是 App_Gain_Task
100ms周期执行 需要保存? 任务结束 计算距上次修改时间 时间>5秒? 保存到EEPROM 清除保存标志


模块 15: app_param.c - 参数管理应用

模块职责
  • 管理系统参数
  • EEPROM读写
  • 参数CRC校验
  • 处理参数读写命令
EEPROM地址分配
c 复制代码
/* EEPROM地址映射 */
#define EEPROM_ADDR_MAGIC       0x0000  // 魔术字(4字节)
#define EEPROM_ADDR_GAIN        0x0004  // 增益档位(1字节)
#define EEPROM_ADDR_USER_PARAM1 0x0008  // 用户参数1(4字节)
#define EEPROM_ADDR_USER_PARAM2 0x000C  // 用户参数2(4字节)
#define EEPROM_ADDR_CRC         0x0010  // CRC校验(2字节)

/* 魔术字 */
#define PARAM_MAGIC_NUMBER      0x12345678
数据结构设计
c 复制代码
/* 系统参数结构 */
typedef struct {
    uint32_t magic;             // 魔术字
    uint8_t  gain_level;        // 增益档位
    uint32_t user_param1;       // 用户参数1
    uint32_t user_param2;       // 用户参数2
    uint16_t crc;               // CRC校验
} SystemParam_t;

static SystemParam_t system_param = {0};
对外API接口
函数原型 功能描述 调用位置 返回值
int App_Param_Init(void) 初始化参数管理 main()初始化(最先) 0=成功 -1=失败
void App_Param_Task(void) 参数管理任务(事件触发) 调度器
void App_Param_OnCommand(ProtocolFrame_t *frame) 处理参数命令 协议回调
int App_Param_SaveGain(GainLevel_t level) 保存增益档位 app_gain 0=成功 -1=失败
int App_Param_ReadGain(GainLevel_t *level) 读取增益档位 app_gain初始化 0=成功 -1=失败
参数保存流程图

否 是 否 是 否 是 否 是 App_Param_SaveGain 更新system_param 计算CRC16 写入魔术字 写入成功? 返回失败 写入增益档位 写入成功? 写入用户参数 写入成功? 写入CRC 写入成功? 返回成功


模块 16: app_main.c - 主程序

模块职责
  • 系统启动初始化
  • 硬件初始化
  • 应用层初始化
  • 主循环调度
主函数流程图

系统上电 HAL_Init SystemClock_Config
配置为72MHz Hardware_Init BSP_GPIO_Init BSP_Timer_Init BSP_UART_Init BSP_I2C_Init Scheduler_Init Application_Init App_Param_Init
最先初始化 App_Gain_Init
从EEPROM恢复 App_Battery_Init App_UART_Init Scheduler_TaskRegister 打印启动信息 启动定时器 进入主循环 App_UART_Task
高优先级 Scheduler_Run
周期任务 LED_Heartbeat
心跳指示

对外API接口
函数原型 功能描述 调用位置 返回值
int main(void) 主函数入口 系统启动 不返回
void Error_Handler(void) 错误处理 初始化失败时 不返回
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) 定时器回调 HAL库调用

4. 任务派发清单

4.1 任务分解表

任务编号 任务名称 模块 预计工时 优先级 依赖任务
T001 工程创建与配置 STM32CubeMX 0.5天
T002 BSP_GPIO驱动 bsp_gpio.c 0.5天 T001
T003 BSP_Timer驱动 bsp_timer.c 0.5天 T001
T004 BSP_UART驱动 bsp_uart.c 1天 T001
T005 BSP_I2C驱动 bsp_i2c.c 1天 T001
T006 调度器模块 scheduler.c 1天 T003
T007 环形缓冲区 ringbuffer.c 0.5天
T008 CRC16模块 crc.c 0.5天
T009 协议模块 protocol.c 1天 T008
T010 增益控制驱动 drv_gain.c 0.5天 T002
T011 EEPROM驱动 drv_eeprom.c 1天 T005
T012 电量芯片驱动 drv_battery.c 1.5天 T005
T013 参数管理应用 app_param.c 1天 T011
T014 增益控制应用 app_gain.c 1天 T010,T013
T015 电池监控应用 app_battery.c 1天 T012
T016 串口通信应用 app_uart.c 1.5天 T004,T009
T017 主程序集成 app_main.c 1天 T013~T016
T018 单元测试 各模块 2天 T017
T019 系统集成测试 整体 2天 T018
T020 文档编写 技术文档 2天 T019

总计:约20个工作日


4.2 开发顺序建议

第一阶段:基础框架 (5天)
复制代码
Week 1:
├── Day 1: T001,T002,T003 (工程创建+GPIO+Timer)
├── Day 2: T004 (UART驱动+测试)
├── Day 3: T005 (I2C驱动+测试)
├── Day 4: T006,T007 (调度器+环形缓冲区)
└── Day 5: T008,T009 (CRC+协议模块)
第二阶段:设备驱动 (4天)
复制代码
Week 2:
├── Day 1: T010 (增益控制驱动+测试)
├── Day 2: T011 (EEPROM驱动+测试)
├── Day 3-4: T012 (电量芯片驱动+测试)
第三阶段:应用层 (5天)
复制代码
Week 3:
├── Day 1: T013 (参数管理应用)
├── Day 2: T014 (增益控制应用)
├── Day 3: T015 (电池监控应用)
├── Day 4: T016 (串口通信应用)
└── Day 5: T017 (主程序集成)
第四阶段:测试与文档 (6天)
复制代码
Week 4:
├── Day 1-2: T018 (单元测试)
├── Day 3-4: T019 (系统集成测试)
└── Day 5-6: T020 (技术文档编写)

4.3 详细任务卡片(示例)

我来重新绘制详细的任务卡片,使用更清晰的格式:


📋 嵌入式开发任务派发卡片


任务卡片 T001: 工程创建与配置

项目 内容
任务编号 T001
优先级 🔴 高
预计工时 0.5天
依赖任务
负责人 [待分配]
状态 ⏳ 待开始

📝 任务描述

使用STM32CubeMX创建STM32F103C8T6工程,配置系统时钟、外设引脚。

📦 交付物清单

  • STM32CubeMX配置文件(.ioc)
  • Keil/IAR工程文件
  • 基础HAL库代码
  • 引脚配置文档

🔧 技术要求

系统时钟配置:

复制代码
┌─────────────────────────────────┐
│ HSE:        8MHz 外部晶振       │
│ PLL倍频:    x9                  │
│ SYSCLK:     72MHz               │
│ AHB:        72MHz               │
│ APB1:       36MHz               │
│ APB2:       72MHz               │
└─────────────────────────────────┘

外设配置:

复制代码
┌──────────┬─────────────┬──────────────┬──────────┐
│ 外设     │ 引脚        │ 配置         │ 说明     │
├──────────┼─────────────┼──────────────┼──────────┤
│ USART1   │ PA9(TX)     │ 115200-8-N-1 │ 中断模式 │
│          │ PA10(RX)    │              │          │
├──────────┼─────────────┼──────────────┼──────────┤
│ I2C1     │ PB6(SCL)    │ 100KHz       │ 主机模式 │
│          │ PB7(SDA)    │              │          │
├──────────┼─────────────┼──────────────┼──────────┤
│ TIM2     │ -           │ 1ms定时      │ 中断使能 │
├──────────┼─────────────┼──────────────┼──────────┤
│ GPIO     │ PA0/PA1/PA2 │ 推挽输出     │ 增益控制 │
│          │ PC13        │ 推挽输出     │ LED指示  │
└──────────┴─────────────┴──────────────┴──────────┘

✅ 验收标准

  • 工程能正常编译通过
  • 系统时钟配置为72MHz
  • 所有外设时钟正确使能
  • 引脚功能配置正确
  • 生成的代码无编译警告

🧪 测试方法

c 复制代码
// 时钟验证
uint32_t sysclk = HAL_RCC_GetSysClockFreq();
assert(sysclk == 72000000);

// LED闪烁验证
while(1) {
    HAL_GPIO_TogglePin(GPIOC, GPIO_PIN_13);
    HAL_Delay(500);
}

任务卡片 T002: BSP_GPIO驱动

项目 内容
任务编号 T002
优先级 🔴 高
预计工时 0.5天
依赖任务 T001
负责人 [待分配]
状态 ⏳ 待开始

📝 任务描述

开发GPIO驱动模块,封装GPIO读写接口,支持增益控制和LED指示。

📦 交付物清单

  • Drivers/BSP/bsp_gpio.c - GPIO驱动实现
  • Drivers/BSP/bsp_gpio.h - GPIO驱动头文件
  • GPIO功能测试代码
  • 测试报告文档

🔧 技术要求

引脚配置表:

复制代码
┌────────┬──────────────┬────────────┬──────────┐
│ 引脚   │ 功能         │ 模式       │ 初始状态 │
├────────┼──────────────┼────────────┼──────────┤
│ PA0    │ GAIN_CTRL0   │ 推挽输出   │ 低电平   │
│ PA1    │ GAIN_CTRL1   │ 推挽输出   │ 低电平   │
│ PA2    │ GAIN_CTRL2   │ 推挽输出   │ 低电平   │
│ PC13   │ LED_STATUS   │ 推挽输出   │ 高电平   │
└────────┴──────────────┴────────────┴──────────┘

接口定义:

c 复制代码
/* 初始化所有GPIO */
int BSP_GPIO_Init(void);

/* GPIO写操作 */
void BSP_GPIO_Write(GPIO_TypeDef *port, uint16_t pin, uint8_t state);

/* GPIO读操作 */
uint8_t BSP_GPIO_Read(GPIO_TypeDef *port, uint16_t pin);

/* GPIO翻转 */
void BSP_GPIO_Toggle(GPIO_TypeDef *port, uint16_t pin);

代码规范要求:

  • 模块内部变量使用static修饰
  • 所有函数添加Doxygen注释
  • 错误返回值统一为-1
  • 成功返回值统一为0

✅ 验收标准

  • 所有GPIO能正常初始化
  • LED能够正常闪烁
  • 增益控制引脚能正确输出高低电平
  • 代码符合编码规范
  • 无内存泄漏
  • 无编译警告

🧪 测试用例

c 复制代码
/**
 * 测试1:LED闪烁测试
 */
void Test_LED_Blink(void)
{
    BSP_GPIO_Init();
    for(int i=0; i<10; i++) {
        BSP_GPIO_Toggle(GPIOC, GPIO_PIN_13);
        HAL_Delay(500);
    }
}

/**
 * 测试2:增益控制引脚测试
 */
void Test_Gain_Pins(void)
{
    // 测试所有组合(000~111)
    for(uint8_t i=0; i<8; i++) {
        BSP_GPIO_Write(GPIOA, GPIO_PIN_0, (i & 0x01) ? 1 : 0);
        BSP_GPIO_Write(GPIOA, GPIO_PIN_1, (i & 0x02) ? 1 : 0);
        BSP_GPIO_Write(GPIOA, GPIO_PIN_2, (i & 0x04) ? 1 : 0);
        HAL_Delay(100);
        // 使用示波器验证输出电平
    }
}

任务卡片 T003: BSP_Timer驱动

项目 内容
任务编号 T003
优先级 🔴 高
预计工时 0.5天
依赖任务 T001
负责人 [待分配]
状态 ⏳ 待开始

📝 任务描述

配置TIM2产生精确的1ms定时中断,为调度器提供系统时基。

📦 交付物清单

  • Drivers/BSP/bsp_timer.c - 定时器驱动实现
  • Drivers/BSP/bsp_timer.h - 定时器驱动头文件
  • 定时精度测试代码
  • 精度测试报告(示波器波形图)

🔧 技术要求

定时器参数计算:

复制代码
时钟源: APB1 Timer Clock = 72MHz
目标周期: 1ms = 1000Hz

预分频器 = 72 - 1 = 71
  (72MHz / 72 = 1MHz)

自动重载值 = 1000 - 1 = 999
  (1MHz / 1000 = 1000Hz = 1ms)

中断优先级: 抢占优先级3, 子优先级0

TIM2配置:

复制代码
┌──────────────────┬─────────────┐
│ 配置项           │ 值          │
├──────────────────┼─────────────┤
│ 定时器           │ TIM2        │
│ 时钟源           │ 内部时钟    │
│ 预分频器         │ 71          │
│ 计数模式         │ 向上计数    │
│ 自动重载值       │ 999         │
│ 时钟分频         │ 1           │
│ 重复计数器       │ 0           │
│ 中断使能         │ UPDATE      │
│ 抢占优先级       │ 3           │
│ 子优先级         │ 0           │
└──────────────────┴─────────────┘

接口定义:

c 复制代码
/* 初始化定时器 */
int BSP_Timer_Init(void);

/* 启动定时器 */
void BSP_Timer_Start(void);

/* 停止定时器 */
void BSP_Timer_Stop(void);

/* 定时器中断回调(弱定义) */
void BSP_Timer_Callback(void);

✅ 验收标准

  • 定时器能产生精确1ms中断
  • 定时精度误差 < 0.1%
  • 中断响应及时(< 10us)
  • 能正常启动/停止定时器
  • 长时间运行稳定(测试1小时无漂移)

🧪 测试用例

测试1:精度测试

c 复制代码
volatile uint32_t tick_counter = 0;

void TIM2_IRQHandler(void)
{
    if(__HAL_TIM_GET_FLAG(&htim2, TIM_FLAG_UPDATE)) {
        __HAL_TIM_CLEAR_FLAG(&htim2, TIM_FLAG_UPDATE);
        tick_counter++;
        
        // 每1000次翻转GPIO(理论1秒)
        if(tick_counter % 1000 == 0) {
            HAL_GPIO_TogglePin(GPIOC, GPIO_PIN_13);
        }
    }
}

// 使用示波器测量PC13周期,应为2秒±2ms

测试2:长时间稳定性测试

c 复制代码
// 运行1小时,计数应为:3600000 ± 360
uint32_t expected = 3600000;
uint32_t tolerance = 360; // 0.01%

// 运行1小时后验证
assert(tick_counter > expected - tolerance);
assert(tick_counter < expected + tolerance);

任务卡片 T004: BSP_UART驱动

项目 内容
任务编号 T004
优先级 🔴 高
预计工时 1天
依赖任务 T001
负责人 [待分配]
状态 ⏳ 待开始

📝 任务描述

开发UART1驱动,实现中断接收、阻塞发送,使用环形缓冲区防止数据丢失。

📦 交付物清单

  • Drivers/BSP/bsp_uart.c - UART驱动实现
  • Drivers/BSP/bsp_uart.h - UART驱动头文件
  • 串口收发压力测试代码
  • 通信测试报告

🔧 技术要求

UART硬件配置:

复制代码
┌──────────────────┬─────────────┐
│ 配置项           │ 值          │
├──────────────────┼─────────────┤
│ 实例             │ USART1      │
│ 引脚(TX)         │ PA9         │
│ 引脚(RX)         │ PA10        │
│ 波特率           │ 115200      │
│ 数据位           │ 8           │
│ 停止位           │ 1           │
│ 校验位           │ None        │
│ 模式             │ TX+RX       │
│ 接收模式         │ 中断        │
│ 发送模式         │ 阻塞        │
│ 中断优先级       │ 2/0         │
└──────────────────┴─────────────┘

环形缓冲区设计:

复制代码
大小: 256字节
类型: FIFO
线程安全: 中断安全

┌───────────────────────────────┐
│                               │
│    [空闲空间]                 │
│                               │
│  Tail ──>  [数据]  <── Head   │
│           [数据]              │
│           [数据]              │
│                               │
│    [空闲空间]                 │
│                               │
└───────────────────────────────┘

接口定义:

c 复制代码
/* 初始化UART */
int BSP_UART_Init(void);

/* 发送数据(阻塞) */
int BSP_UART_Send(uint8_t *data, uint16_t len);

/* 从缓冲区读取一字节 */
int BSP_UART_Read(uint8_t *data);

/* 获取可读字节数 */
uint16_t BSP_UART_Available(void);

/* 清空接收缓冲区 */
void BSP_UART_Flush(void);

/* 接收中断回调(内部使用) */
void BSP_UART_RxCallback(uint8_t data);

✅ 验收标准

  • UART能正常初始化
  • 能通过串口助手正常收发数据
  • 连续接收256字节不丢失数据
  • 发送速率达到115200bps
  • 缓冲区满时能正确处理(丢弃或覆盖)
  • 代码无内存泄漏
  • 支持printf重定向

🧪 测试用例

测试1:基础收发测试

c 复制代码
void Test_UART_EchoTest(void)
{
    BSP_UART_Init();
    
    char msg[] = "Hello STM32!\r\n";
    BSP_UART_Send((uint8_t*)msg, strlen(msg));
    
    // 回环测试:发送的数据应能从上位机收到
}

测试2:大数据量压力测试

c 复制代码
void Test_UART_StressTest(void)
{
    uint8_t test_data[256];
    uint8_t recv_data[256];
    uint16_t recv_count = 0;
    
    // 生成测试数据
    for(int i=0; i<256; i++) {
        test_data[i] = i;
    }
    
    // 上位机连续发送256字节
    // (需要上位机配合测试)
    
    // 等待接收
    uint32_t timeout = HAL_GetTick() + 2000;
    while(recv_count < 256 && HAL_GetTick() < timeout) {
        if(BSP_UART_Available() > 0) {
            BSP_UART_Read(&recv_data[recv_count++]);
        }
    }
    
    // 验证数据完整性
    assert(recv_count == 256);
    assert(memcmp(test_data, recv_data, 256) == 0);
}

测试3:缓冲区溢出测试

c 复制代码
void Test_UART_BufferOverflow(void)
{
    // 上位机连续发送300字节(超过256字节缓冲区)
    // 验证前256字节正确接收
    // 后续数据丢弃或覆盖
}

任务卡片 T005: BSP_I2C驱动

项目 内容
任务编号 T005
优先级 🔴 高
预计工时 1天
依赖任务 T001
负责人 [待分配]
状态 ⏳ 待开始

📝 任务描述

开发I2C1驱动,支持多设备读写,支持电量芯片和EEPROM通信。

📦 交付物清单

  • Drivers/BSP/bsp_i2c.c - I2C驱动实现
  • Drivers/BSP/bsp_i2c.h - I2C驱动头文件
  • I2C设备扫描工具代码
  • I2C通信测试报告

🔧 技术要求

I2C硬件配置:

复制代码
┌──────────────────┬─────────────┐
│ 配置项           │ 值          │
├──────────────────┼─────────────┤
│ 实例             │ I2C1        │
│ 引脚(SCL)        │ PB6         │
│ 引脚(SDA)        │ PB7         │
│ 速率             │ 100KHz      │
│ 模式             │ Standard    │
│ 主/从            │ Master      │
│ 地址模式         │ 7-bit       │
│ 占空比           │ 2           │
│ 应答使能         │ Yes         │
│ 超时时间         │ 1000ms      │
└──────────────────┴─────────────┘

设备地址表:

复制代码
┌─────────────────┬──────────┬──────────┐
│ 设备            │ 7位地址  │ 8位地址  │
├─────────────────┼──────────┼──────────┤
│ 电量芯片(BQ27441)│ 0x55     │ 0xAA/0xAB│
│ EEPROM(AT24C02) │ 0x50     │ 0xA0/0xA1│
│ (预留扩展)       │ -        │ -        │
└─────────────────┴──────────┴──────────┘

接口定义:

c 复制代码
/* 初始化I2C */
int BSP_I2C_Init(void);

/* 写单字节 */
int BSP_I2C_WriteByte(uint8_t dev_addr, uint8_t reg_addr, uint8_t data);

/* 读单字节 */
int BSP_I2C_ReadByte(uint8_t dev_addr, uint8_t reg_addr, uint8_t *data);

/* 写多字节 */
int BSP_I2C_WriteBytes(uint8_t dev_addr, uint8_t reg_addr, 
                       uint8_t *data, uint16_t len);

/* 读多字节 */
int BSP_I2C_ReadBytes(uint8_t dev_addr, uint8_t reg_addr, 
                      uint8_t *data, uint16_t len);

/* 检测设备是否存在 */
int BSP_I2C_IsDeviceReady(uint8_t dev_addr);

✅ 验收标准

  • I2C能正常初始化
  • 能扫描到总线上的I2C设备
  • 能正确读写I2C设备
  • 通信失败时返回正确错误码
  • 有超时保护机制
  • 支持多设备共享总线

🧪 测试用例

测试1:I2C设备扫描

c 复制代码
void Test_I2C_ScanDevices(void)
{
    BSP_I2C_Init();
    
    printf("Scanning I2C bus...\r\n");
    for(uint8_t addr = 0x01; addr < 0x7F; addr++) {
        if(BSP_I2C_IsDeviceReady(addr) == 0) {
            printf("Found device at 0x%02X\r\n", addr);
        }
    }
    printf("Scan complete.\r\n");
}

测试2:EEPROM读写测试

c 复制代码
void Test_I2C_EEPROM(void)
{
    uint8_t write_data = 0xAA;
    uint8_t read_data;
    
    // 写入数据
    BSP_I2C_WriteByte(0x50, 0x00, write_data);
    HAL_Delay(5); // EEPROM写入延迟
    
    // 读取数据
    BSP_I2C_ReadByte(0x50, 0x00, &read_data);
    
    // 验证数据
    assert(read_data == write_data);
    printf("EEPROM test: PASS\r\n");
}

测试3:超时测试

c 复制代码
void Test_I2C_Timeout(void)
{
    uint8_t data;
    
    // 尝试访问不存在的设备(应超时返回错误)
    int ret = BSP_I2C_ReadByte(0x7F, 0x00, &data);
    
    assert(ret != 0); // 应返回错误
    printf("Timeout test: PASS\r\n");
}

任务卡片 T006: 调度器模块

项目 内容
任务编号 T006
优先级 🔴 高
预计工时 1天
依赖任务 T003
负责人 [待分配]
状态 ⏳ 待开始

📝 任务描述

开发软件定时调度器,支持周期任务和事件触发任务,为裸机系统提供类RTOS的任务管理能力。

📦 交付物清单

  • Service/scheduler.c - 调度器实现
  • Service/scheduler.h - 调度器头文件
  • 多任务调度演示代码
  • 调度器使用手册

🔧 技术要求

调度器架构:

复制代码
┌────────────────────────────────────┐
│       Scheduler (调度器)            │
├────────────────────────────────────┤
│ - 系统时基(1ms)                    │
│ - 任务列表                         │
│ - 任务注册                         │
│ - 任务调度                         │
└────────────────────────────────────┘
         ↓          ↓          ↓
    ┌────────┐ ┌────────┐ ┌────────┐
    │ 任务1  │ │ 任务2  │ │ 任务3  │
    │ 10ms   │ │ 100ms  │ │ 事件   │
    └────────┘ └────────┘ └────────┘

任务类型:

复制代码
1. 周期任务 (Periodic Task)
   - 按固定周期执行
   - 例如:1ms, 10ms, 100ms, 1000ms

2. 事件任务 (Event-Triggered Task)
   - 由外部事件触发立即执行
   - 例如:串口接收、按键按下

数据结构设计:

c 复制代码
/* 任务ID枚举 */
typedef enum {
    TASK_UART = 0,      // 串口通信任务
    TASK_BATTERY,       // 电池监控任务(1秒)
    TASK_GAIN,          // 增益控制任务(100ms)
    TASK_PARAM,         // 参数保存任务(事件)
    TASK_MAX            // 任务总数
} TaskID_t;

/* 任务函数指针类型 */
typedef void (*TaskFunc_t)(void);

/* 任务控制块 */
typedef struct {
    TaskFunc_t  func;       // 任务函数指针
    uint32_t    period;     // 任务周期(ms), 0=事件触发
    uint32_t    elapsed;    // 已经过时间(ms)
    uint8_t     enabled;    // 任务使能标志
    const char  *name;      // 任务名称(调试用)
} Task_t;

接口定义:

c 复制代码
/* 初始化调度器 */
int Scheduler_Init(void);

/* 注册任务 */
int Scheduler_RegisterTask(TaskID_t id, TaskFunc_t func, 
                           uint32_t period, const char *name);

/* 启用任务 */
void Scheduler_EnableTask(TaskID_t id);

/* 禁用任务 */
void Scheduler_DisableTask(TaskID_t id);

/* 触发事件型任务 */
void Scheduler_TriggerTask(TaskID_t id);

/* 调度器主循环(在main的while(1)中调用) */
void Scheduler_Run(void);

/* 1ms时基更新(在定时器中断中调用) */
void Scheduler_TickUpdate(void);

/* 获取系统运行时间(ms) */
uint32_t Scheduler_GetTick(void);

✅ 验收标准

  • 能注册和管理至少8个任务
  • 周期任务误差 < 5ms
  • 事件任务能立即响应(< 1ms)
  • 系统时基精确(误差 < 0.1%)
  • 任务使能/禁用功能正常
  • 长时间运行稳定(测试24小时无崩溃)

🧪 测试用例

测试1:周期任务精度测试

c 复制代码
volatile uint32_t task1_count = 0;
volatile uint32_t task2_count = 0;

void task1_func(void) { task1_count++; }
void task2_func(void) { task2_count++; }

void Test_Scheduler_Precision(void)
{
    Scheduler_Init();
    Scheduler_RegisterTask(TASK_UART, task1_func, 10, "Task1");
    Scheduler_RegisterTask(TASK_BATTERY, task2_func, 100, "Task2");
    
    // 运行1秒
    uint32_t start = Scheduler_GetTick();
    while(Scheduler_GetTick() - start < 1000) {
        Scheduler_Run();
    }
    
    // 验证执行次数
    // Task1: 10ms周期,1秒应执行100次
    // Task2: 100ms周期,1秒应执行10次
    assert(task1_count >= 98 && task1_count <= 102);
    assert(task2_count >= 9 && task2_count <= 11);
    
    printf("Precision test: PASS\r\n");
    printf("Task1 executed: %d times\r\n", task1_count);
    printf("Task2 executed: %d times\r\n", task2_count);
}

测试2:事件触发测试

c 复制代码
volatile uint8_t event_executed = 0;

void event_task_func(void) {
    event_executed = 1;
}

void Test_Scheduler_EventTrigger(void)
{
    Scheduler_Init();
    Scheduler_RegisterTask(TASK_PARAM, event_task_func, 0, "Event");
    
    event_executed = 0;
    
    // 触发事件任务
    Scheduler_TriggerTask(TASK_PARAM);
    
    // 验证立即执行
    assert(event_executed == 1);
    
    printf("Event trigger test: PASS\r\
    n");
}

测试3:任务使能/禁用测试

c 复制代码
void Test_Scheduler_EnableDisable(void)
{
    task1_count = 0;
    
    Scheduler_Init();
    Scheduler_RegisterTask(TASK_UART, task1_func, 10, "Task1");
    
    // 禁用任务
    Scheduler_DisableTask(TASK_UART);
    
    // 运行100ms
    uint32_t start = Scheduler_GetTick();
    while(Scheduler_GetTick() - start < 100) {
        Scheduler_Run();
    }
    
    // 任务被禁用,不应执行
    assert(task1_count == 0);
    
    // 启用任务
    Scheduler_EnableTask(TASK_UART);
    
    // 再运行100ms
    start = Scheduler_GetTick();
    while(Scheduler_GetTick() - start < 100) {
        Scheduler_Run();
    }
    
    // 任务已启用,应执行约10次
    assert(task1_count >= 9 && task1_count <= 11);
    
    printf("Enable/Disable test: PASS\r\n");
}

任务统计表

任务编号 任务名称 优先级 工时 依赖 状态
T001 工程创建与配置 🔴 高 0.5天 ⏳ 待开始
T002 BSP_GPIO驱动 🔴 高 0.5天 T001 ⏳ 待开始
T003 BSP_Timer驱动 🔴 高 0.5天 T001 ⏳ 待开始
T004 BSP_UART驱动 🔴 高 1天 T001 ⏳ 待开始
T005 BSP_I2C驱动 🔴 高 1天 T001 ⏳ 待开始
T006 调度器模块 🔴 高 1天 T003 ⏳ 待开始
T007 环形缓冲区 🟡 中 0.5天 ⏳ 待开始
T008 CRC16模块 🟡 中 0.5天 ⏳ 待开始
T009 协议模块 🔴 高 1天 T008 ⏳ 待开始
T010 增益控制驱动 🟡 中 0.5天 T002 ⏳ 待开始
T011 EEPROM驱动 🟡 中 1天 T005 ⏳ 待开始
T012 电量芯片驱动 🟡 中 1.5天 T005 ⏳ 待开始
T013 参数管理应用 🟡 中 1天 T011 ⏳ 待开始
T014 增益控制应用 🟡 中 1天 T010,T013 ⏳ 待开始
T015 电池监控应用 🟡 中 1天 T012 ⏳ 待开始
T016 串口通信应用 🔴 高 1.5天 T004,T009 ⏳ 待开始
T017 主程序集成 🔴 高 1天 T013~T016 ⏳ 待开始
T018 单元测试 🟡 中 2天 T017 ⏳ 待开始
T019 系统集成测试 🔴 高 2天 T018 ⏳ 待开始
T020 文档编写 🟢 低 2天 T019 ⏳ 待开始

总计工时:约20个工作日


这是我为你设计的完整的STM32F103C8T6裸机开发方案。现在你有了:

完整的系统架构设计

详细的模块设计文档

清晰的模块依赖关系

20个可执行的开发任务卡片

每个任务的详细技术要求和验收标准

完整的测试用例

你可以直接按照这个方案开始开发。如果你需要:

  • 某个具体模块的完整代码实现
  • 更详细的测试方案
  • 调试技巧和常见问题解决方案
  • 上位机测试工具的设计

相关推荐
才鲸嵌入式13 小时前
STM32 USB协议栈源码分析
stm32·单片机·嵌入式·驱动·usb·硬件·phy
BT-BOX13 小时前
【普中】基于51单片机模拟风扇控制数码管显示设计
单片机·嵌入式硬件·51单片机
国科安芯13 小时前
商业航天级抗辐照MCU与电源芯片在硅微条探测器系统中的应用分析
单片机·嵌入式硬件·架构·安全威胁分析·安全性测试
TEC_INO13 小时前
Stm32_1
stm32·单片机·嵌入式硬件
Molesidy1 天前
【Embedded Development】【bootloader】基于MCU的bootloader详细介绍以及基于MCU串口的IAP实战详细教程
单片机·嵌入式硬件·bootloader
长安第一美人1 天前
php出现zend_mm_heap corrupted 或者Segment fault
开发语言·嵌入式硬件·php·zmq·工业应用开发
沐欣工作室_lvyiyi1 天前
基于单片机的两轮自平衡循迹小车(论文+源码)
单片机·嵌入式硬件·小车·两轮自平衡
清风6666661 天前
基于单片机的8路抢答器设计与实现
数据库·单片机·嵌入式硬件·毕业设计·课程设计·期末大作业
点灯小铭1 天前
基于单片机的智能污水有害气体电子鼻检测系统
数据库·单片机·嵌入式硬件·毕业设计·课程设计·期末大作业