2025-06-17 16:04:24 +08:00
|
|
|
|
# 📦 第八层:项目实战与工具链
|
2025-06-17 15:46:15 +08:00
|
|
|
|
|
2025-06-17 16:04:24 +08:00
|
|
|
|
## ✅ 工程管理
|
|
|
|
|
|
|
|
|
|
|
|
### 🔹 Git 版本控制
|
|
|
|
|
|
|
|
|
|
|
|
#### 1. **Git 分支策略**
|
|
|
|
|
|
- **主干分支(main/master)**:
|
|
|
|
|
|
永远代表可发布的稳定版本,仅接受通过CI/CD验证的代码。
|
|
|
|
|
|
- **开发分支(develop)**:
|
|
|
|
|
|
集成所有新功能的开发,是日常开发的基础分支。
|
|
|
|
|
|
- **特性分支(feature/*)**:
|
|
|
|
|
|
从develop分支创建,用于开发单个新功能或修复问题,完成后合并回develop。
|
|
|
|
|
|
- **发布分支(release/*)**:
|
|
|
|
|
|
从develop分支创建,用于准备发布版本,进行最后的测试和Bug修复。
|
|
|
|
|
|
- **热修复分支(hotfix/*)**:
|
|
|
|
|
|
从main分支创建,用于紧急修复生产环境问题,修复后合并回main和develop。
|
|
|
|
|
|
|
|
|
|
|
|
#### 2. **提交规范**
|
|
|
|
|
|
采用Conventional Commits规范:
|
|
|
|
|
|
```
|
|
|
|
|
|
<类型>[可选范围]: <描述>
|
|
|
|
|
|
|
|
|
|
|
|
[可选正文]
|
|
|
|
|
|
|
|
|
|
|
|
[可选脚注]
|
|
|
|
|
|
```
|
|
|
|
|
|
- **常见类型**:
|
|
|
|
|
|
- `feat`:新功能
|
|
|
|
|
|
- `fix`:修复Bug
|
|
|
|
|
|
- `docs`:文档更新
|
|
|
|
|
|
- `style`:代码格式调整(不影响功能)
|
|
|
|
|
|
- `refactor`:代码重构
|
|
|
|
|
|
- `test`:添加或修改测试
|
|
|
|
|
|
- `chore`:构建或辅助工具的变动
|
|
|
|
|
|
|
|
|
|
|
|
#### 3. **标签管理**
|
|
|
|
|
|
使用语义化版本(SemVer)打标签:
|
|
|
|
|
|
```bash
|
|
|
|
|
|
# 创建标签
|
|
|
|
|
|
git tag v1.0.0
|
|
|
|
|
|
|
|
|
|
|
|
# 推送标签到远程
|
|
|
|
|
|
git push origin v1.0.0
|
|
|
|
|
|
|
|
|
|
|
|
# 查看所有标签
|
|
|
|
|
|
git tag -l
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
### 🔹 Makefile、CMake 构建工具
|
|
|
|
|
|
|
|
|
|
|
|
#### 1. **Makefile 基础**
|
|
|
|
|
|
- **简单示例**:
|
|
|
|
|
|
```makefile
|
|
|
|
|
|
CC = arm-none-eabi-gcc
|
|
|
|
|
|
CFLAGS = -Wall -O2 -mcpu=cortex-m4 -mthumb
|
|
|
|
|
|
LDFLAGS = -Tstm32f4.ld
|
|
|
|
|
|
|
|
|
|
|
|
SRCS = $(wildcard *.c)
|
|
|
|
|
|
OBJS = $(SRCS:.c=.o)
|
|
|
|
|
|
TARGET = firmware.elf
|
|
|
|
|
|
|
|
|
|
|
|
all: $(TARGET)
|
|
|
|
|
|
|
|
|
|
|
|
$(TARGET): $(OBJS)
|
|
|
|
|
|
$(CC) $(LDFLAGS) $(OBJS) -o $@
|
|
|
|
|
|
|
|
|
|
|
|
%.o: %.c
|
|
|
|
|
|
$(CC) $(CFLAGS) -c $< -o $@
|
|
|
|
|
|
|
|
|
|
|
|
clean:
|
|
|
|
|
|
rm -f $(OBJS) $(TARGET)
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
#### 2. **CMake 高级应用**
|
|
|
|
|
|
- **跨平台配置**:
|
|
|
|
|
|
```cmake
|
|
|
|
|
|
cmake_minimum_required(VERSION 3.10)
|
|
|
|
|
|
project(EmbeddedProject C)
|
|
|
|
|
|
|
|
|
|
|
|
# 设置交叉编译工具链
|
|
|
|
|
|
set(CMAKE_SYSTEM_NAME Generic)
|
|
|
|
|
|
set(CMAKE_C_COMPILER arm-none-eabi-gcc)
|
|
|
|
|
|
set(CMAKE_CXX_COMPILER arm-none-eabi-g++)
|
|
|
|
|
|
set(CMAKE_ASM_COMPILER arm-none-eabi-gcc)
|
|
|
|
|
|
set(CMAKE_OBJCOPY arm-none-eabi-objcopy)
|
|
|
|
|
|
|
|
|
|
|
|
# 添加编译选项
|
|
|
|
|
|
add_compile_options(
|
|
|
|
|
|
-mcpu=cortex-m4
|
|
|
|
|
|
-mthumb
|
|
|
|
|
|
-mfloat-abi=hard
|
|
|
|
|
|
-mfpu=fpv4-sp-d16
|
|
|
|
|
|
-Wall
|
|
|
|
|
|
-Wextra
|
|
|
|
|
|
-Os
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
# 添加链接选项
|
|
|
|
|
|
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -T${CMAKE_SOURCE_DIR}/STM32F407VGTx_FLASH.ld")
|
|
|
|
|
|
|
|
|
|
|
|
# 添加源文件
|
|
|
|
|
|
file(GLOB_RECURSE SOURCES "src/*.c" "drivers/*.c")
|
|
|
|
|
|
|
|
|
|
|
|
# 添加可执行文件
|
|
|
|
|
|
add_executable(${PROJECT_NAME}.elf ${SOURCES})
|
|
|
|
|
|
|
|
|
|
|
|
# 添加目标文件
|
|
|
|
|
|
add_custom_target(${PROJECT_NAME}.bin
|
|
|
|
|
|
COMMAND ${CMAKE_OBJCOPY} -O binary ${PROJECT_NAME}.elf ${PROJECT_NAME}.bin
|
|
|
|
|
|
DEPENDS ${PROJECT_NAME}.elf
|
|
|
|
|
|
)
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
### 🔹 Jenkins/GitHub Actions CI 流水线
|
|
|
|
|
|
|
|
|
|
|
|
#### 1. **GitHub Actions 配置**
|
|
|
|
|
|
- **编译与测试工作流**:
|
|
|
|
|
|
```yaml
|
|
|
|
|
|
name: Build and Test
|
|
|
|
|
|
|
|
|
|
|
|
on:
|
|
|
|
|
|
push:
|
|
|
|
|
|
branches: [ main, develop ]
|
|
|
|
|
|
pull_request:
|
|
|
|
|
|
branches: [ main, develop ]
|
|
|
|
|
|
|
|
|
|
|
|
jobs:
|
|
|
|
|
|
build:
|
|
|
|
|
|
runs-on: ubuntu-latest
|
|
|
|
|
|
|
|
|
|
|
|
steps:
|
|
|
|
|
|
- name: Checkout code
|
|
|
|
|
|
uses: actions/checkout@v3
|
|
|
|
|
|
|
|
|
|
|
|
- name: Set up Python
|
|
|
|
|
|
uses: actions/setup-python@v4
|
|
|
|
|
|
with:
|
|
|
|
|
|
python-version: 3.9
|
|
|
|
|
|
|
|
|
|
|
|
- name: Install dependencies
|
|
|
|
|
|
run: |
|
|
|
|
|
|
sudo apt-get update
|
|
|
|
|
|
sudo apt-get install -y gcc-arm-none-eabi cmake ninja-build
|
|
|
|
|
|
|
|
|
|
|
|
- name: Configure CMake
|
|
|
|
|
|
run: cmake -B build -G Ninja
|
|
|
|
|
|
|
|
|
|
|
|
- name: Build
|
|
|
|
|
|
run: cmake --build build
|
|
|
|
|
|
|
|
|
|
|
|
- name: Run tests
|
|
|
|
|
|
run: |
|
|
|
|
|
|
cd build
|
|
|
|
|
|
ctest --output-on-failure
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
#### 2. **Jenkins 集成**
|
|
|
|
|
|
- **构建脚本示例**:
|
|
|
|
|
|
```groovy
|
|
|
|
|
|
pipeline {
|
|
|
|
|
|
agent any
|
|
|
|
|
|
|
|
|
|
|
|
stages {
|
|
|
|
|
|
stage('Checkout') {
|
|
|
|
|
|
steps {
|
|
|
|
|
|
checkout scm
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
stage('Build') {
|
|
|
|
|
|
steps {
|
|
|
|
|
|
sh 'make clean all'
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
stage('Test') {
|
|
|
|
|
|
steps {
|
|
|
|
|
|
sh 'make test'
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
stage('Code Coverage') {
|
|
|
|
|
|
steps {
|
|
|
|
|
|
sh 'make coverage'
|
|
|
|
|
|
}
|
|
|
|
|
|
post {
|
|
|
|
|
|
always {
|
|
|
|
|
|
junit 'build/test-results/*.xml'
|
|
|
|
|
|
publishCoverage adapters: [coberturaAdapter('build/coverage/coverage.xml')]
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
stage('Deploy') {
|
|
|
|
|
|
when {
|
|
|
|
|
|
branch 'main'
|
|
|
|
|
|
}
|
|
|
|
|
|
steps {
|
|
|
|
|
|
sh 'make deploy'
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
## ✅ 项目实践
|
|
|
|
|
|
|
|
|
|
|
|
### 🔹 嵌入式应用框架设计
|
|
|
|
|
|
|
|
|
|
|
|
#### 1. **分层架构**
|
|
|
|
|
|
```
|
|
|
|
|
|
+----------------------+
|
|
|
|
|
|
| 应用层 |
|
|
|
|
|
|
| (业务逻辑、算法) |
|
|
|
|
|
|
+----------------------+
|
|
|
|
|
|
| 服务层 |
|
|
|
|
|
|
| (任务管理、事件) |
|
|
|
|
|
|
+----------------------+
|
|
|
|
|
|
| 驱动层 |
|
|
|
|
|
|
| (硬件抽象、BSP) |
|
|
|
|
|
|
+----------------------+
|
|
|
|
|
|
| 硬件层 |
|
|
|
|
|
|
| (MCU、外设) |
|
|
|
|
|
|
+----------------------+
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
#### 2. **组件化设计**
|
|
|
|
|
|
- **核心组件**:
|
|
|
|
|
|
- 任务管理器:负责任务创建、调度和通信。
|
|
|
|
|
|
- 事件系统:处理异步事件和回调。
|
|
|
|
|
|
- 配置管理:加载和保存系统配置。
|
|
|
|
|
|
- 日志系统:分级日志记录和输出。
|
|
|
|
|
|
|
|
|
|
|
|
#### 3. **代码结构示例**
|
|
|
|
|
|
```
|
|
|
|
|
|
project/
|
|
|
|
|
|
├── app/ # 应用层
|
|
|
|
|
|
│ ├── main.c # 主程序入口
|
|
|
|
|
|
│ ├── modules/ # 功能模块
|
|
|
|
|
|
│ │ ├── sensor/ # 传感器处理
|
|
|
|
|
|
│ │ ├── comm/ # 通信处理
|
|
|
|
|
|
│ │ └── control/ # 控制逻辑
|
|
|
|
|
|
│ └── config/ # 配置文件
|
|
|
|
|
|
├── services/ # 服务层
|
|
|
|
|
|
│ ├── task_mgr/ # 任务管理器
|
|
|
|
|
|
│ ├── event/ # 事件系统
|
|
|
|
|
|
│ └── utils/ # 工具函数
|
|
|
|
|
|
├── drivers/ # 驱动层
|
|
|
|
|
|
│ ├── bsp/ # 板级支持包
|
|
|
|
|
|
│ ├── hal/ # 硬件抽象层
|
|
|
|
|
|
│ └── periph/ # 外设驱动
|
|
|
|
|
|
└── build/ # 构建系统
|
|
|
|
|
|
├── cmake/ # CMake配置
|
|
|
|
|
|
└── Makefile # Makefile
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
### 🔹 通用 BSP 构建
|
|
|
|
|
|
|
|
|
|
|
|
#### 1. **设计原则**
|
|
|
|
|
|
- **硬件无关性**:上层代码不直接访问硬件寄存器。
|
|
|
|
|
|
- **可移植性**:相同功能代码可在不同硬件平台复用。
|
|
|
|
|
|
- **配置化**:通过配置文件而非修改代码适配不同硬件。
|
|
|
|
|
|
|
|
|
|
|
|
#### 2. **BSP 实现示例**
|
|
|
|
|
|
```c
|
|
|
|
|
|
// bsp_led.h
|
|
|
|
|
|
#ifndef BSP_LED_H
|
|
|
|
|
|
#define BSP_LED_H
|
|
|
|
|
|
|
|
|
|
|
|
#include <stdint.h>
|
|
|
|
|
|
|
|
|
|
|
|
typedef enum {
|
|
|
|
|
|
LED_RED,
|
|
|
|
|
|
LED_GREEN,
|
|
|
|
|
|
LED_BLUE
|
|
|
|
|
|
} led_t;
|
|
|
|
|
|
|
|
|
|
|
|
typedef enum {
|
|
|
|
|
|
LED_OFF,
|
|
|
|
|
|
LED_ON,
|
|
|
|
|
|
LED_TOGGLE
|
|
|
|
|
|
} led_state_t;
|
|
|
|
|
|
|
|
|
|
|
|
// 初始化LED
|
|
|
|
|
|
void bsp_led_init(void);
|
|
|
|
|
|
|
|
|
|
|
|
// 设置LED状态
|
|
|
|
|
|
void bsp_led_set(led_t led, led_state_t state);
|
|
|
|
|
|
|
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
|
|
// bsp_led.c (STM32实现)
|
|
|
|
|
|
#include "bsp_led.h"
|
|
|
|
|
|
#include "stm32f4xx_hal.h"
|
|
|
|
|
|
|
|
|
|
|
|
// LED GPIO定义
|
|
|
|
|
|
#define LED_RED_PIN GPIO_PIN_14
|
|
|
|
|
|
#define LED_RED_PORT GPIOG
|
|
|
|
|
|
#define LED_GREEN_PIN GPIO_PIN_13
|
|
|
|
|
|
#define LED_GREEN_PORT GPIOG
|
|
|
|
|
|
#define LED_BLUE_PIN GPIO_PIN_15
|
|
|
|
|
|
#define LED_BLUE_PORT GPIOG
|
|
|
|
|
|
|
|
|
|
|
|
void bsp_led_init(void) {
|
|
|
|
|
|
GPIO_InitTypeDef GPIO_InitStruct = {0};
|
|
|
|
|
|
|
|
|
|
|
|
// 使能GPIO时钟
|
|
|
|
|
|
__HAL_RCC_GPIOG_CLK_ENABLE();
|
|
|
|
|
|
|
|
|
|
|
|
// 配置GPIO引脚
|
|
|
|
|
|
GPIO_InitStruct.Pin = LED_RED_PIN | LED_GREEN_PIN | LED_BLUE_PIN;
|
|
|
|
|
|
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
|
|
|
|
|
|
GPIO_InitStruct.Pull = GPIO_NOPULL;
|
|
|
|
|
|
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
|
|
|
|
|
|
HAL_GPIO_Init(GPIOG, &GPIO_InitStruct);
|
|
|
|
|
|
|
|
|
|
|
|
// 默认关闭所有LED
|
|
|
|
|
|
HAL_GPIO_WritePin(LED_RED_PORT, LED_RED_PIN, GPIO_PIN_RESET);
|
|
|
|
|
|
HAL_GPIO_WritePin(LED_GREEN_PORT, LED_GREEN_PIN, GPIO_PIN_RESET);
|
|
|
|
|
|
HAL_GPIO_WritePin(LED_BLUE_PORT, LED_BLUE_PIN, GPIO_PIN_RESET);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void bsp_led_set(led_t led, led_state_t state) {
|
|
|
|
|
|
GPIO_TypeDef *port;
|
|
|
|
|
|
uint16_t pin;
|
|
|
|
|
|
|
|
|
|
|
|
// 根据LED类型选择GPIO
|
|
|
|
|
|
switch (led) {
|
|
|
|
|
|
case LED_RED:
|
|
|
|
|
|
port = LED_RED_PORT;
|
|
|
|
|
|
pin = LED_RED_PIN;
|
|
|
|
|
|
break;
|
|
|
|
|
|
case LED_GREEN:
|
|
|
|
|
|
port = LED_GREEN_PORT;
|
|
|
|
|
|
pin = LED_GREEN_PIN;
|
|
|
|
|
|
break;
|
|
|
|
|
|
case LED_BLUE:
|
|
|
|
|
|
port = LED_BLUE_PORT;
|
|
|
|
|
|
pin = LED_BLUE_PIN;
|
|
|
|
|
|
break;
|
|
|
|
|
|
default:
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 设置LED状态
|
|
|
|
|
|
switch (state) {
|
|
|
|
|
|
case LED_OFF:
|
|
|
|
|
|
HAL_GPIO_WritePin(port, pin, GPIO_PIN_RESET);
|
|
|
|
|
|
break;
|
|
|
|
|
|
case LED_ON:
|
|
|
|
|
|
HAL_GPIO_WritePin(port, pin, GPIO_PIN_SET);
|
|
|
|
|
|
break;
|
|
|
|
|
|
case LED_TOGGLE:
|
|
|
|
|
|
HAL_GPIO_TogglePin(port, pin);
|
|
|
|
|
|
break;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
### 🔹 模块化驱动结构
|
|
|
|
|
|
|
|
|
|
|
|
#### 1. **驱动分层**
|
|
|
|
|
|
- **硬件层**:直接操作寄存器的低级驱动。
|
|
|
|
|
|
- **抽象层**:提供统一接口的高级驱动。
|
|
|
|
|
|
- **适配层**:连接抽象层和硬件层的中间层。
|
|
|
|
|
|
|
|
|
|
|
|
#### 2. **SPI驱动示例**
|
|
|
|
|
|
```c
|
|
|
|
|
|
// spi_interface.h (抽象接口)
|
|
|
|
|
|
#ifndef SPI_INTERFACE_H
|
|
|
|
|
|
#define SPI_INTERFACE_H
|
|
|
|
|
|
|
|
|
|
|
|
#include <stdint.h>
|
|
|
|
|
|
|
|
|
|
|
|
typedef struct {
|
|
|
|
|
|
// 初始化SPI
|
|
|
|
|
|
void (*init)(uint32_t baudrate);
|
|
|
|
|
|
|
|
|
|
|
|
// 发送数据
|
|
|
|
|
|
void (*send)(const uint8_t *data, uint32_t length);
|
|
|
|
|
|
|
|
|
|
|
|
// 接收数据
|
|
|
|
|
|
void (*receive)(uint8_t *data, uint32_t length);
|
|
|
|
|
|
|
|
|
|
|
|
// 发送并接收数据
|
|
|
|
|
|
void (*transfer)(const uint8_t *tx_data, uint8_t *rx_data, uint32_t length);
|
|
|
|
|
|
} spi_interface_t;
|
|
|
|
|
|
|
|
|
|
|
|
// 获取SPI接口实例
|
|
|
|
|
|
const spi_interface_t* spi_get_interface(void);
|
|
|
|
|
|
|
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
|
|
// spi_stm32.c (STM32实现)
|
|
|
|
|
|
#include "spi_interface.h"
|
|
|
|
|
|
#include "stm32f4xx_hal.h"
|
|
|
|
|
|
|
|
|
|
|
|
static SPI_HandleTypeDef hspi1;
|
|
|
|
|
|
|
|
|
|
|
|
static void spi_init(uint32_t baudrate) {
|
|
|
|
|
|
// 配置SPI参数
|
|
|
|
|
|
hspi1.Instance = SPI1;
|
|
|
|
|
|
hspi1.Init.Mode = SPI_MODE_MASTER;
|
|
|
|
|
|
hspi1.Init.Direction = SPI_DIRECTION_2LINES;
|
|
|
|
|
|
hspi1.Init.DataSize = SPI_DATASIZE_8BIT;
|
|
|
|
|
|
hspi1.Init.CLKPolarity = SPI_POLARITY_LOW;
|
|
|
|
|
|
hspi1.Init.CLKPhase = SPI_PHASE_1EDGE;
|
|
|
|
|
|
hspi1.Init.NSS = SPI_NSS_SOFT;
|
|
|
|
|
|
|
|
|
|
|
|
// 根据波特率计算分频系数
|
|
|
|
|
|
uint32_t prescaler = SPI_BAUDRATEPRESCALER_2;
|
|
|
|
|
|
if (baudrate < 1000000) prescaler = SPI_BAUDRATEPRESCALER_128;
|
|
|
|
|
|
else if (baudrate < 2000000) prescaler = SPI_BAUDRATEPRESCALER_64;
|
|
|
|
|
|
else if (baudrate < 4000000) prescaler = SPI_BAUDRATEPRESCALER_32;
|
|
|
|
|
|
else if (baudrate < 8000000) prescaler = SPI_BAUDRATEPRESCALER_16;
|
|
|
|
|
|
else if (baudrate < 16000000) prescaler = SPI_BAUDRATEPRESCALER_8;
|
|
|
|
|
|
else if (baudrate < 32000000) prescaler = SPI_BAUDRATEPRESCALER_4;
|
|
|
|
|
|
|
|
|
|
|
|
hspi1.Init.BaudRatePrescaler = prescaler;
|
|
|
|
|
|
hspi1.Init.FirstBit = SPI_FIRSTBIT_MSB;
|
|
|
|
|
|
hspi1.Init.TIMode = SPI_TIMODE_DISABLE;
|
|
|
|
|
|
hspi1.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE;
|
|
|
|
|
|
hspi1.Init.CRCPolynomial = 10;
|
|
|
|
|
|
|
|
|
|
|
|
// 初始化SPI
|
|
|
|
|
|
HAL_SPI_Init(&hspi1);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void spi_send(const uint8_t *data, uint32_t length) {
|
|
|
|
|
|
HAL_SPI_Transmit(&hspi1, (uint8_t*)data, length, 1000);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void spi_receive(uint8_t *data, uint32_t length) {
|
|
|
|
|
|
HAL_SPI_Receive(&hspi1, data, length, 1000);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void spi_transfer(const uint8_t *tx_data, uint8_t *rx_data, uint32_t length) {
|
|
|
|
|
|
HAL_SPI_TransmitReceive(&hspi1, (uint8_t*)tx_data, rx_data, length, 1000);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// SPI接口实现
|
|
|
|
|
|
static const spi_interface_t spi_impl = {
|
|
|
|
|
|
.init = spi_init,
|
|
|
|
|
|
.send = spi_send,
|
|
|
|
|
|
.receive = spi_receive,
|
|
|
|
|
|
.transfer = spi_transfer
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
// 获取SPI接口实例
|
|
|
|
|
|
const spi_interface_t* spi_get_interface(void) {
|
|
|
|
|
|
return &spi_impl;
|
|
|
|
|
|
}
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
### 🔹 OTA 升级方案设计
|
|
|
|
|
|
|
|
|
|
|
|
#### 1. **双分区架构**
|
|
|
|
|
|
```
|
|
|
|
|
|
Flash布局:
|
|
|
|
|
|
+-------------------+ 0x08000000
|
|
|
|
|
|
| Bootloader | (80KB)
|
|
|
|
|
|
+-------------------+ 0x08014000
|
|
|
|
|
|
| Application A | (448KB)
|
|
|
|
|
|
+-------------------+ 0x08084000
|
|
|
|
|
|
| Application B | (448KB)
|
|
|
|
|
|
+-------------------+ 0x08104000
|
|
|
|
|
|
| Configuration | (16KB)
|
|
|
|
|
|
+-------------------+
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
#### 2. **OTA状态机**
|
|
|
|
|
|
```c
|
|
|
|
|
|
typedef enum {
|
|
|
|
|
|
OTA_IDLE, // 空闲状态
|
|
|
|
|
|
OTA_CHECKING, // 检查更新
|
|
|
|
|
|
OTA_DOWNLOADING, // 下载中
|
|
|
|
|
|
OTA_DOWNLOAD_PAUSED, // 下载暂停
|
|
|
|
|
|
OTA_VERIFYING, // 校验中
|
|
|
|
|
|
OTA_READY, // 准备重启
|
|
|
|
|
|
OTA_UPGRADING, // 升级中
|
|
|
|
|
|
OTA_FAILED // 升级失败
|
|
|
|
|
|
} ota_state_t;
|
|
|
|
|
|
|
|
|
|
|
|
typedef struct {
|
|
|
|
|
|
ota_state_t state;
|
|
|
|
|
|
uint32_t total_size;
|
|
|
|
|
|
uint32_t downloaded_size;
|
|
|
|
|
|
uint8_t progress;
|
|
|
|
|
|
char error_msg[64];
|
|
|
|
|
|
uint8_t firmware_hash[32];
|
|
|
|
|
|
} ota_context_t;
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
#### 3. **OTA流程**
|
|
|
|
|
|
1. **检查更新**:
|
|
|
|
|
|
```c
|
|
|
|
|
|
bool ota_check_update(void) {
|
|
|
|
|
|
// 从服务器获取版本信息
|
|
|
|
|
|
http_response_t response = http_get(UPDATE_SERVER_URL "/version");
|
|
|
|
|
|
if (response.status != 200) {
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 解析服务器版本
|
|
|
|
|
|
uint32_t server_version = parse_version(response.body);
|
|
|
|
|
|
uint32_t current_version = get_current_version();
|
|
|
|
|
|
|
|
|
|
|
|
// 比较版本
|
|
|
|
|
|
return (server_version > current_version);
|
|
|
|
|
|
}
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
2. **下载固件**:
|
|
|
|
|
|
```c
|
|
|
|
|
|
void ota_download_firmware(void) {
|
|
|
|
|
|
// 打开固件下载URL
|
|
|
|
|
|
http_client_t client = http_open(UPDATE_SERVER_URL "/firmware.bin");
|
|
|
|
|
|
if (!client) {
|
|
|
|
|
|
ota_set_state(OTA_FAILED, "Failed to open URL");
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 获取文件大小
|
|
|
|
|
|
uint32_t file_size = http_get_content_length(client);
|
|
|
|
|
|
ota_set_total_size(file_size);
|
|
|
|
|
|
|
|
|
|
|
|
// 开始下载
|
|
|
|
|
|
uint8_t buffer[512];
|
|
|
|
|
|
uint32_t bytes_received = 0;
|
|
|
|
|
|
uint32_t bytes_written = 0;
|
|
|
|
|
|
|
|
|
|
|
|
while ((bytes_received = http_read(client, buffer, 512)) > 0) {
|
|
|
|
|
|
// 写入到备份区
|
|
|
|
|
|
if (!flash_write(APPLICATION_B_ADDRESS + bytes_written, buffer, bytes_received)) {
|
|
|
|
|
|
ota_set_state(OTA_FAILED, "Flash write failed");
|
|
|
|
|
|
http_close(client);
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bytes_written += bytes_received;
|
|
|
|
|
|
ota_update_progress(bytes_written * 100 / file_size);
|
|
|
|
|
|
|
|
|
|
|
|
// 检查是否需要暂停
|
|
|
|
|
|
if (ota_should_pause()) {
|
|
|
|
|
|
http_close(client);
|
|
|
|
|
|
ota_set_state(OTA_DOWNLOAD_PAUSED, "Download paused");
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
http_close(client);
|
|
|
|
|
|
ota_set_state(OTA_VERIFYING, "Verifying firmware");
|
|
|
|
|
|
}
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
3. **验证与应用**:
|
|
|
|
|
|
```c
|
|
|
|
|
|
bool ota_verify_firmware(void) {
|
|
|
|
|
|
// 计算下载固件的哈希值
|
|
|
|
|
|
uint8_t calculated_hash[32];
|
|
|
|
|
|
calculate_firmware_hash(APPLICATION_B_ADDRESS, APPLICATION_SIZE, calculated_hash);
|
|
|
|
|
|
|
|
|
|
|
|
// 与服务器提供的哈希值比较
|
|
|
|
|
|
if (memcmp(calculated_hash, ota_get_expected_hash(), 32) != 0) {
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 验证向量表
|
|
|
|
|
|
uint32_t *vector_table = (uint32_t*)APPLICATION_B_ADDRESS;
|
|
|
|
|
|
if (vector_table[0] == 0 || vector_table[1] == 0) {
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void ota_apply_update(void) {
|
|
|
|
|
|
// 设置升级标志
|
|
|
|
|
|
set_update_flag(1);
|
|
|
|
|
|
|
|
|
|
|
|
// 保存新固件版本
|
|
|
|
|
|
save_new_version(get_server_version());
|
|
|
|
|
|
|
|
|
|
|
|
// 重启系统
|
|
|
|
|
|
NVIC_SystemReset();
|
|
|
|
|
|
}
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
# 开发工具链安装指南
|
|
|
|
|
|
## 1. **IDE推荐**
|
|
|
|
|
|
|
|
|
|
|
|
### VS Code + PlatformIO
|
|
|
|
|
|
|
|
|
|
|
|
**官网链接**:
|
|
|
|
|
|
- [VS Code](https://code.visualstudio.com/)
|
|
|
|
|
|
- [PlatformIO](https://platformio.org/)
|
|
|
|
|
|
|
|
|
|
|
|
**安装步骤**:
|
|
|
|
|
|
1. 下载并安装 [VS Code](https://code.visualstudio.com/Download)
|
|
|
|
|
|
2. 打开VS Code,点击左侧扩展图标(或按 `Ctrl+Shift+X`)
|
|
|
|
|
|
3. 搜索并安装 **PlatformIO IDE** 扩展
|
|
|
|
|
|
4. 安装完成后,重启VS Code
|
|
|
|
|
|
5. PlatformIO会自动安装所需的工具链和依赖
|
|
|
|
|
|
|
|
|
|
|
|
**验证安装**:
|
|
|
|
|
|
打开VS Code,点击左下角的 **PlatformIO Home** 图标,若能正常打开则安装成功。
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
### STM32CubeIDE
|
|
|
|
|
|
|
|
|
|
|
|
**官网链接**:
|
|
|
|
|
|
- [STM32CubeIDE](https://www.st.com/en/development-tools/stm32cubeide.html)
|
|
|
|
|
|
|
|
|
|
|
|
**安装步骤**:
|
|
|
|
|
|
1. 访问官网,点击 **Get Software** 下载对应操作系统的安装包
|
|
|
|
|
|
2. 运行安装程序,按照向导完成安装
|
|
|
|
|
|
3. 安装过程中会自动下载并配置STM32CubeMX
|
|
|
|
|
|
|
|
|
|
|
|
**验证安装**:
|
|
|
|
|
|
启动STM32CubeIDE,创建一个新的STM32项目,若能正常编译则安装成功。
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
### CLion
|
|
|
|
|
|
|
|
|
|
|
|
**官网链接**:
|
|
|
|
|
|
- [CLion](https://www.jetbrains.com/clion/)
|
|
|
|
|
|
|
|
|
|
|
|
**安装步骤**:
|
|
|
|
|
|
1. 下载并安装 [CLion](https://www.jetbrains.com/clion/download/)
|
|
|
|
|
|
2. 安装CMake和MinGW(Windows用户需要):
|
|
|
|
|
|
- CMake:从 [官网](https://cmake.org/download/) 下载并安装
|
|
|
|
|
|
- MinGW:推荐使用 [MSYS2](https://www.msys2.org/) 安装
|
|
|
|
|
|
|
|
|
|
|
|
**验证安装**:
|
|
|
|
|
|
启动CLion,创建一个新的C/C++项目,选择CMake工具链,若能正常编译则安装成功。
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
## 2. **调试工具**
|
|
|
|
|
|
|
|
|
|
|
|
### OpenOCD
|
|
|
|
|
|
|
|
|
|
|
|
**官网链接**:
|
|
|
|
|
|
- [OpenOCD](http://openocd.org/)
|
|
|
|
|
|
|
|
|
|
|
|
**安装步骤**:
|
|
|
|
|
|
- **Windows**:
|
|
|
|
|
|
1. 从 [GNU MCU Eclipse](https://github.com/gnu-mcu-eclipse/openocd/releases) 下载预编译二进制包
|
|
|
|
|
|
2. 解压到指定目录(如 `C:\openocd`)
|
|
|
|
|
|
3. 将 `bin` 目录添加到系统环境变量
|
|
|
|
|
|
|
|
|
|
|
|
- **Linux**:
|
|
|
|
|
|
```bash
|
|
|
|
|
|
sudo apt-get install openocd # Ubuntu/Debian
|
|
|
|
|
|
sudo yum install openocd # CentOS/RHEL
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
- **macOS**:
|
|
|
|
|
|
```bash
|
|
|
|
|
|
brew install open-ocd
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
**验证安装**:
|
|
|
|
|
|
在终端中运行 `openocd --version`,若显示版本信息则安装成功。
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
### GDB
|
|
|
|
|
|
|
|
|
|
|
|
**官网链接**:
|
|
|
|
|
|
- [GDB](https://www.gnu.org/software/gdb/)
|
|
|
|
|
|
- [ARM GCC Toolchain](https://developer.arm.com/tools-and-software/open-source-software/developer-tools/gnu-toolchain/gnu-rm)
|
|
|
|
|
|
|
|
|
|
|
|
**安装步骤**:
|
|
|
|
|
|
1. 下载并安装 [ARM GCC Toolchain](https://developer.arm.com/tools-and-software/open-source-software/developer-tools/gnu-toolchain/gnu-rm/downloads)
|
|
|
|
|
|
2. 将 `bin` 目录添加到系统环境变量
|
|
|
|
|
|
|
|
|
|
|
|
**验证安装**:
|
|
|
|
|
|
在终端中运行 `arm-none-eabi-gdb --version`,若显示版本信息则安装成功。
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
### ST-Link/V2
|
|
|
|
|
|
|
|
|
|
|
|
**官网链接**:
|
|
|
|
|
|
- [ST-Link](https://www.st.com/en/development-tools/st-link-v2.html)
|
|
|
|
|
|
|
|
|
|
|
|
**安装步骤**:
|
|
|
|
|
|
- **Windows**:
|
|
|
|
|
|
1. 从 [ST官网](https://www.st.com/en/development-tools/stsw-link004.html) 下载并安装ST-Link驱动
|
|
|
|
|
|
2. 安装完成后,将ST-Link/V2调试器连接到电脑
|
|
|
|
|
|
|
|
|
|
|
|
- **Linux**:
|
|
|
|
|
|
```bash
|
|
|
|
|
|
sudo apt-get install stlink-tools # Ubuntu/Debian
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
**验证安装**:
|
|
|
|
|
|
在终端中运行 `st-info --version`,若显示版本信息则安装成功。
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
## 3. **静态代码分析**
|
|
|
|
|
|
|
|
|
|
|
|
### CppCheck
|
|
|
|
|
|
|
|
|
|
|
|
**官网链接**:
|
|
|
|
|
|
- [CppCheck](https://cppcheck.sourceforge.io/)
|
|
|
|
|
|
|
|
|
|
|
|
**安装步骤**:
|
|
|
|
|
|
- **Windows**:
|
|
|
|
|
|
1. 从 [官网](https://cppcheck.sourceforge.io/) 下载安装包
|
|
|
|
|
|
2. 运行安装程序,按照向导完成安装
|
|
|
|
|
|
|
|
|
|
|
|
- **Linux**:
|
|
|
|
|
|
```bash
|
|
|
|
|
|
sudo apt-get install cppcheck # Ubuntu/Debian
|
|
|
|
|
|
sudo yum install cppcheck # CentOS/RHEL
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
- **macOS**:
|
|
|
|
|
|
```bash
|
|
|
|
|
|
brew install cppcheck
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
**验证安装**:
|
|
|
|
|
|
在终端中运行 `cppcheck --version`,若显示版本信息则安装成功。
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
### Clang-Tidy
|
|
|
|
|
|
|
|
|
|
|
|
**官网链接**:
|
|
|
|
|
|
- [Clang-Tidy](https://clang.llvm.org/extra/clang-tidy/)
|
|
|
|
|
|
|
|
|
|
|
|
**安装步骤**:
|
|
|
|
|
|
- **Windows**:
|
|
|
|
|
|
1. 安装 [LLVM](https://releases.llvm.org/download.html)
|
|
|
|
|
|
2. Clang-Tidy会随LLVM一起安装
|
|
|
|
|
|
|
|
|
|
|
|
- **Linux**:
|
|
|
|
|
|
```bash
|
|
|
|
|
|
sudo apt-get install clang-tidy # Ubuntu/Debian
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
- **macOS**:
|
|
|
|
|
|
```bash
|
|
|
|
|
|
brew install llvm
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
**验证安装**:
|
|
|
|
|
|
在终端中运行 `clang-tidy --version`,若显示版本信息则安装成功。
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
### SonarQube
|
|
|
|
|
|
|
|
|
|
|
|
**官网链接**:
|
|
|
|
|
|
- [SonarQube](https://www.sonarqube.org/)
|
|
|
|
|
|
|
|
|
|
|
|
**安装步骤**:
|
|
|
|
|
|
1. 下载并安装 [Docker](https://www.docker.com/get-started)
|
|
|
|
|
|
2. 运行SonarQube容器:
|
|
|
|
|
|
```bash
|
|
|
|
|
|
docker run -d --name sonarqube -p 9000:9000 sonarqube
|
|
|
|
|
|
```
|
|
|
|
|
|
3. 访问 [http://localhost:9000](http://localhost:9000),使用默认账号(admin/admin)登录
|
|
|
|
|
|
|
|
|
|
|
|
**验证安装**:
|
|
|
|
|
|
在浏览器中打开 [http://localhost:9000](http://localhost:9000),若能看到SonarQube界面则安装成功。
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
## 4. **单元测试**
|
|
|
|
|
|
|
|
|
|
|
|
### Unity
|
|
|
|
|
|
|
|
|
|
|
|
**官网链接**:
|
|
|
|
|
|
- [Unity](https://github.com/ThrowTheSwitch/Unity)
|
|
|
|
|
|
|
|
|
|
|
|
**安装步骤**:
|
|
|
|
|
|
1. 从GitHub下载Unity源码:
|
|
|
|
|
|
```bash
|
|
|
|
|
|
git clone https://github.com/ThrowTheSwitch/Unity.git
|
|
|
|
|
|
```
|
|
|
|
|
|
2. 将 `src` 目录添加到项目的头文件搜索路径
|
|
|
|
|
|
|
|
|
|
|
|
**验证安装**:
|
|
|
|
|
|
创建一个简单的测试文件,包含Unity头文件,若能正常编译则安装成功。
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
### CMock
|
|
|
|
|
|
|
|
|
|
|
|
**官网链接**:
|
|
|
|
|
|
- [CMock](https://github.com/ThrowTheSwitch/CMock)
|
|
|
|
|
|
|
|
|
|
|
|
**安装步骤**:
|
|
|
|
|
|
1. 从GitHub下载CMock源码:
|
|
|
|
|
|
```bash
|
|
|
|
|
|
git clone https://github.com/ThrowTheSwitch/CMock.git
|
|
|
|
|
|
```
|
|
|
|
|
|
2. 将 `src` 目录添加到项目的头文件搜索路径
|
|
|
|
|
|
|
|
|
|
|
|
**验证安装**:
|
|
|
|
|
|
创建一个简单的测试文件,包含CMock头文件,若能正常编译则安装成功。
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
### Google Test
|
|
|
|
|
|
|
|
|
|
|
|
**官网链接**:
|
|
|
|
|
|
- [Google Test](https://github.com/google/googletest)
|
|
|
|
|
|
|
|
|
|
|
|
**安装步骤**:
|
|
|
|
|
|
1. 从GitHub下载Google Test源码:
|
|
|
|
|
|
```bash
|
|
|
|
|
|
git clone https://github.com/google/googletest.git
|
|
|
|
|
|
```
|
|
|
|
|
|
2. 使用CMake构建并安装:
|
|
|
|
|
|
```bash
|
|
|
|
|
|
cd googletest
|
|
|
|
|
|
mkdir build
|
|
|
|
|
|
cd build
|
|
|
|
|
|
cmake ..
|
|
|
|
|
|
make
|
|
|
|
|
|
sudo make install
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
**验证安装**:
|
|
|
|
|
|
创建一个简单的测试文件,包含Google Test头文件,若能正常编译则安装成功。
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
## 📚 资源汇总
|
|
|
|
|
|
|
|
|
|
|
|
| **工具** | **官网链接** | **安装指南** |
|
|
|
|
|
|
|------------------|---------------------------------------------|-------------------------------------------|
|
|
|
|
|
|
| VS Code | https://code.visualstudio.com/ | 直接下载安装包 |
|
|
|
|
|
|
| PlatformIO | https://platformio.org/ | VS Code扩展市场安装 |
|
|
|
|
|
|
| STM32CubeIDE | https://www.st.com/en/development-tools/stm32cubeide.html | 官网下载安装包 |
|
|
|
|
|
|
| CLion | https://www.jetbrains.com/clion/ | 官网下载安装包 |
|
|
|
|
|
|
| OpenOCD | http://openocd.org/ | 包管理器或预编译二进制包 |
|
|
|
|
|
|
| GDB | https://www.gnu.org/software/gdb/ | 随ARM GCC Toolchain安装 |
|
|
|
|
|
|
| ST-Link/V2 | https://www.st.com/en/development-tools/st-link-v2.html | 官网下载驱动 |
|
|
|
|
|
|
| CppCheck | https://cppcheck.sourceforge.io/ | 包管理器或安装包 |
|
|
|
|
|
|
| Clang-Tidy | https://clang.llvm.org/extra/clang-tidy/ | 随LLVM安装 |
|
|
|
|
|
|
| SonarQube | https://www.sonarqube.org/ | Docker容器或独立安装 |
|
|
|
|
|
|
| Unity | https://github.com/ThrowTheSwitch/Unity | 从GitHub下载源码 |
|
|
|
|
|
|
| CMock | https://github.com/ThrowTheSwitch/CMock | 从GitHub下载源码 |
|
|
|
|
|
|
| Google Test | https://github.com/google/googletest | CMake构建并安装 |
|