STM32F407嵌入式系统架构MVP设计
·
第一部分 MVP框架程序设计
1. 系统启动流程
1.1 主函数 main()
/**
* @file main.c
* @brief 系统主入口
*/
int main(void)
{
/* 1. 系统初始化 */
SystemInit(); // 时钟、中断配置
DWT_Init(); // 性能计数器初始化
/* 2. 静态内存池初始化 */
static_memory_pool_init(); // 初始化所有静态内存池
/* 3. uCOS-II初始化 */
OSInit(); // RTOS内核初始化
/* 4. 创建启动任务 */
OSTaskCreate(startup_task, // 任务函数
NULL, // 参数
&startup_stack[0], // 栈地址
TASK_STACK_SIZE_MEDIUM,
TASK_PRIO_MAIN, // 优先级3
&startup_tcb); // TCB指针
/* 5. 启动多任务调度 */
OSStart(); // 永不返回
return 0;
}
1.2 系统启动流程图
┌─────────────────────────────────────────────────────────────────────┐ │ 系统启动流程 │ └─────────────────────────────────────────────────────────────────────┘ [上电复位] │ ▼ ┌───────────────────────────────────────────────────────────────────┐ │ 1. 硬件初始化 (SystemInit) │ │ ├── 配置系统时钟 168MHz │ │ ├── 初始化中断向量表 │ │ ├── 配置FPU(浮点单元) │ │ └── 初始化DWT性能计数器 │ └───────────────────────────────────────────────────────────────────┘ │ ▼ ┌───────────────────────────────────────────────────────────────────┐ │ 2. 静态内存池初始化 (static_memory_pool_init) │ │ ├── 小对象池: 32B × 48 = 1.5KB │ │ ├── 中对象池: 256B × 24 = 6KB │ │ ├── 大对象池: 1KB × 12 = 12KB │ │ ├── DMA池: 2KB × 6 = 12KB (SRAM, 32B对齐) │ │ └── 任务栈池: 512B × 20 = 10KB (CCM RAM) │ └───────────────────────────────────────────────────────────────────┘ │ ▼ ┌───────────────────────────────────────────────────────────────────┐ │ 3. uCOS-II内核初始化 (OSInit) │ │ ├── 初始化任务控制块链表 │ │ ├── 初始化事件控制块链表 │ │ ├── 创建空闲任务 │ │ └── 创建统计任务 │ └───────────────────────────────────────────────────────────────────┘ │ ▼ ┌───────────────────────────────────────────────────────────────────┐ │ 4. 创建启动任务 (OSTaskCreate) │ │ └── 优先级3,栈大小512字节 │ └───────────────────────────────────────────────────────────────────┘ │ ▼ ┌───────────────────────────────────────────────────────────────────┐ │ 5. 启动调度器 (OSStart) │ │ └── 开始运行最高优先级就绪任务 │ └───────────────────────────────────────────────────────────────────┘ │ ▼ ┌───────────────────────────────────────────────────────────────────┐ │ 6. 运行启动任务 (startup_task) │ │ ├── HAL层初始化 │ │ ├── 创建应用任务 │ │ └── 删除自身 │ └───────────────────────────────────────────────────────────────────┘
1.3 启动任务函数
/**
* @brief 启动任务 - 第一个运行的用户任务
* @param p_arg 任务参数(未使用)
*
* 函数流程:
* 1. 初始化硬件抽象层
* 2. 创建应用任务
* 3. 打印系统信息
* 4. 删除自身
*/
static void startup_task(void *p_arg)
{
(void)p_arg;
/* 1. 初始化硬件抽象层 */
hal_spi_init(); // 初始化SPI1/2/3
hal_uart_init(); // 初始化USART1/2/3/4/5/6
hal_adc_init(); // 初始化ADC1/2/3
/* 2. 创建应用任务 */
task_manager_create("SPI_Task", spi_task, NULL,
TASK_PRIO_SPI_DRIVER, TASK_STACK_SIZE_MEDIUM);
task_manager_create("UART_Task", uart_task, NULL,
TASK_PRIO_UART_DRIVER, TASK_STACK_SIZE_MEDIUM);
task_manager_create("ADC_Task", adc_task, NULL,
TASK_PRIO_ADC_DRIVER, TASK_STACK_SIZE_MEDIUM);
task_manager_create("Presenter", device_presenter_task, NULL,
TASK_PRIO_PRESENTER, TASK_STACK_SIZE_LARGE);
task_manager_create("Monitor", system_monitor_task, NULL,
TASK_PRIO_MONITOR, TASK_STACK_SIZE_SMALL);
/* 3. 打印系统信息 */
printf("\n========== System Ready ==========\n");
printf("CPU: 168MHz, RTOS: uCOS-II\n");
printf("Memory: SRAM 112KB, CCM RAM 64KB\n");
printf("Tasks: %d created\n", task_manager_get_task_count());
static_pool_print_stats();
/* 4. 删除自身 */
OSTaskDel(OS_PRIO_SELF);
}
2. 硬件抽象层运行流程
2.1 SPI设备创建与配置流程
/**
* @brief SPI设备创建函数
* @param spi_id SPI总线ID (1-3)
* @return SPI设备指针
*
* 函数流程:
* 1. 从静态内存池分配设备结构体
* 2. 初始化函数指针
* 3. 创建互斥锁
* 4. 获取BSP句柄
*/
HalSpiDevice_t* hal_spi_create(uint32_t spi_id)
{
HalSpiDevice_t *dev;
HalSpiPriv_t *priv;
/* 1. 分配内存(从静态池) */
dev = (HalSpiDevice_t *)static_pool_alloc(MEM_POOL_SMALL);
if (dev == NULL) return NULL;
priv = (HalSpiPriv_t *)static_pool_alloc(MEM_POOL_SMALL);
if (priv == NULL) {
static_pool_free(dev);
return NULL;
}
/* 2. 初始化设备结构体 */
memset(dev, 0, sizeof(HalSpiDevice_t));
memset(priv, 0, sizeof(HalSpiPriv_t));
dev->id = spi_id;
sprintf(dev->name, "spi%d", spi_id);
dev->open = hal_spi_open;
dev->close = hal_spi_close;
dev->config = hal_spi_config;
dev->transfer = hal_spi_transfer;
dev->transfer_double_buf = hal_spi_transfer_double_buf;
dev->priv_data = priv;
/* 3. 创建互斥锁 */
dev->mutex = OSMutexCreate(0, &err);
/* 4. 获取BSP句柄 */
priv->bsp_handle = bsp_spi_get_handle(spi_id);
return dev;
}
2.2 SPI配置流程图
┌─────────────────────────────────────────────────────────────────────┐
│ SPI配置流程 (hal_spi_config) │
└─────────────────────────────────────────────────────────────────────┘
[调用] hal_spi_config(dev, &cfg)
│
▼
┌───────────────────────────────────────────────────────────────────┐
│ 1. 参数验证 │
│ if (priv == NULL || cfg == NULL) return ERROR; │
└───────────────────────────────────────────────────────────────────┘
│
▼
┌───────────────────────────────────────────────────────────────────┐
│ 2. 临界区保护 │
│ OS_ENTER_CRITICAL(); │
└───────────────────────────────────────────────────────────────────┘
│
▼
┌───────────────────────────────────────────────────────────────────┐
│ 3. 停止当前运行 │
│ if (priv->is_running) { │
│ bsp_spi_stop(priv->bsp_handle); │
│ priv->is_running = false; │
│ } │
└───────────────────────────────────────────────────────────────────┘
│
▼
┌───────────────────────────────────────────────────────────────────┐
│ 4. 转换配置参数(适配器模式) │
│ bsp_cfg.mode = cfg->mode; │
│ bsp_cfg.baud_rate = cfg->baudRate; │
│ bsp_cfg.data_width = cfg->dataWidth; │
│ bsp_cfg.cpol = cfg->cpol; │
│ bsp_cfg.cpha = cfg->cpha; │
└───────────────────────────────────────────────────────────────────┘
│
▼
┌───────────────────────────────────────────────────────────────────┐
│ 5. 调用BSP层配置 │
│ result = bsp_spi_config(priv->bsp_handle, &bsp_cfg); │
└───────────────────────────────────────────────────────────────────┘
│
▼
┌───────────────────────────────────────────────────────────────────┐
│ 6. DMA初始化(如果启用) │
│ if (cfg->useDma && priv->dma_tx == NULL) { │
│ priv->dma_tx = dma_manager_alloc(DMA_CHANNEL_SPI_TX); │
│ priv->dma_rx = dma_manager_alloc(DMA_CHANNEL_SPI_RX); │
│ } │
└───────────────────────────────────────────────────────────────────┘
│
▼
┌───────────────────────────────────────────────────────────────────┐
│ 7. 退出临界区 │
│ OS_EXIT_CRITICAL(); │
└───────────────────────────────────────────────────────────────────┘
│
▼
[返回] result
2.3 SPI双缓冲DMA传输流程
/**
* @brief SPI DMA双缓冲传输实现
* @param dev SPI设备指针
* @param buf0 缓冲区0
* @param buf1 缓冲区1
* @param size 缓冲区大小
* @param callback 完成回调
* @return 操作结果
*/
static HalSpiResult_t hal_spi_transfer_double_buf(HalSpiDevice_t *dev,
uint8_t *buf0,
uint8_t *buf1,
uint32_t size,
void (*callback)(void *arg))
{
HalSpiPriv_t *priv = (HalSpiPriv_t *)dev->priv_data;
DmaConfig_t dma_cfg;
/* 1. 创建双缓冲管理器 */
DoubleBufferConfig_t db_cfg = {
.buffer_size = size,
.buffer0_mem = buf0,
.buffer1_mem = buf1,
.use_semaphore = true
};
priv->double_buffer = double_buffer_create(&db_cfg);
/* 2. 配置DMA接收通道(外设到内存) */
dma_cfg.src_addr = (uint32_t)&(priv->bsp_handle->regs->DR);
dma_cfg.dst_addr = (uint32_t)buf0;
dma_cfg.size = size;
dma_cfg.src_inc = DMA_ADDR_FIXED;
dma_cfg.dst_inc = DMA_ADDR_INC;
dma_cfg.dir = DMA_PERIPH_TO_MEM;
dma_cfg.complete_callback = spi_dma_callback;
dma_cfg.callback_arg = dev;
dma_manager_config(priv->dma_rx, &dma_cfg);
/* 3. 配置双缓冲模式 */
DMA_DoubleBufferModeConfig(priv->dma_rx->channel_num,
(uint32_t)buf1,
DMA_Memory_0);
DMA_DoubleBufferCmd(priv->dma_rx->channel_num, ENABLE);
/* 4. 启动DMA */
dma_manager_start(priv->dma_rx);
/* 5. 使能SPI DMA接收 */
bsp_spi_dma_enable(priv->bsp_handle, true);
/* 6. 启动SPI硬件 */
bsp_spi_start(priv->bsp_handle);
return HAL_SPI_OK;
}
3. 双缓冲DMA数据流
3.1 双缓冲数据流图
┌─────────────────────────────────────────────────────────────────────────┐
│ 双缓冲DMA数据流 │
└─────────────────────────────────────────────────────────────────────────┘
[SPI硬件寄存器]
│
│ 硬件自动传输
▼
┌─────────────────────────────────────────────────────────────────┐
│ DMA控制器 │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ 双缓冲模式 │ │
│ │ │ │
│ │ 缓冲区0 (2KB) 缓冲区1 (2KB) │ │
│ │ ┌─────────────┐ ┌─────────────┐ │ │
│ │ │ 数据填充中 │ ───► │ 空闲 │ │ │
│ │ └─────────────┘ └─────────────┘ │ │
│ │ │ ▲ │ │
│ │ │ 缓冲区满 │ │ │
│ │ ▼ │ │ │
│ │ ┌─────────────┐ ┌─────────────┐ │ │
│ │ │ 已满 │ ───► │ 数据填充中 │ │ │
│ │ └─────────────┘ └─────────────┘ │ │
│ │ │ │ │ │
│ │ │ 触发中断 │ │ │
│ │ ▼ │ │ │
│ └─────────┼────────────────────┼─────────────────────────┘ │
└────────────┼────────────────────┼─────────────────────────────┘
│ │
│ 中断 │
▼ ▼
┌─────────────────────────────────────────────────────────────────┐
│ DMA中断服务程序 │
│ │
│ void DMA2_Stream0_IRQHandler(void) │
│ { │
│ OSIntEnter(); │
│ if (DMA_GetFlagStatus(...)) { │
│ DMA_ClearFlag(...); │
│ double_buffer_switch(db); // 切换缓冲区 │
│ OSSemPost(sem_data_ready); // 唤醒任务 │
│ } │
│ OSIntExit(); │
│ } │
└─────────────────────────────────────────────────────────────────┘
│
│ 信号量
▼
┌─────────────────────────────────────────────────────────────────┐
│ SPI数据处理任务 │
│ │
│ void spi_task(void *p_arg) │
│ { │
│ while (1) { │
│ OSSemPend(sem_data_ready, 0); // 等待数据 │
│ buf = double_buffer_get_ready(); // 获取就绪缓冲区 │
│ process_spi_data(buf); // 处理数据 │
│ double_buffer_release(buf); // 释放缓冲区 │
│ } │
│ } │
└─────────────────────────────────────────────────────────────────┘
3.2 缓冲区切换状态机
/**
* @brief 双缓冲切换函数(在DMA中断中调用)
* @param db 双缓冲管理器指针
* @return 新的活动缓冲区索引
*/
uint8_t double_buffer_switch(DoubleBuffer_t *db)
{
uint8_t new_active;
uint8_t old_active;
/* 进入临界区 */
OS_ENTER_CRITICAL();
old_active = db->active_buf;
new_active = (old_active == 0) ? 1 : 0;
/* 状态转换:当前缓冲区从FILLING变为FULL */
if (old_active == 0) {
db->state0 = BUF_STATE_FULL;
db->valid_data_len = db->buffer_size;
} else {
db->state1 = BUF_STATE_FULL;
db->valid_data_len = db->buffer_size;
}
/* 检查新缓冲区状态 */
if (new_active == 0) {
if (db->state0 != BUF_STATE_EMPTY) {
db->overflow_count++;
OS_EXIT_CRITICAL();
return old_active; // 溢出,不切换
}
db->state0 = BUF_STATE_FILLING;
} else {
if (db->state1 != BUF_STATE_EMPTY) {
db->overflow_count++;
OS_EXIT_CRITICAL();
return old_active;
}
db->state1 = BUF_STATE_FILLING;
}
db->active_buf = new_active;
db->switch_count++;
/* 发送信号量唤醒任务 */
OSSemPost(db->sem_data_ready);
OS_EXIT_CRITICAL();
return new_active;
}
4. 静态内存池管理
4.1 内存池初始化函数
/**
* @brief 初始化单个内存池
* @param pool 内存池指针
* @param data 内存池数据区
* @param block_size 块大小
* @param block_count 块数量
* @return true-成功, false-失败
*/
static bool init_single_pool(StaticPool_t *pool,
uint8_t *data,
uint32_t block_size,
uint32_t block_count)
{
uint32_t i;
PoolBlock_t *block;
/* 1. 初始化控制块 */
pool->pool_start = data;
pool->block_size = block_size;
pool->block_count = block_count;
pool->used_count = 0;
pool->alloc_count = 0;
pool->free_count = 0;
pool->fail_count = 0;
/* 2. 构建空闲链表 */
pool->free_list = (PoolBlock_t *)data;
block = pool->free_list;
for (i = 0; i < block_count - 1; i++) {
block->next = (PoolBlock_t *)(data + (i + 1) * block_size);
block = block->next;
}
block->next = NULL; /* 最后一个块指向NULL */
/* 3. 创建互斥锁 */
pool->mutex = OSMutexCreate(0, &err);
pool->initialized = true;
return true;
}
4.2 内存池分配流程图
┌─────────────────────────────────────────────────────────────────────┐
│ 静态内存池分配流程 │
└─────────────────────────────────────────────────────────────────────┘
[调用] static_pool_alloc(MEM_POOL_SMALL)
│
▼
┌───────────────────────────────────────────────────────────────────┐
│ 1. 获取互斥锁 │
│ OSMutexPend(pool->mutex, 0); │
└───────────────────────────────────────────────────────────────────┘
│
▼
┌───────────────────────────────────────────────────────────────────┐
│ 2. 检查空闲链表 │
│ if (pool->free_list == NULL) { │
│ pool->fail_count++; │
│ OSMutexPost(pool->mutex); │
│ return NULL; // 分配失败 │
│ } │
└───────────────────────────────────────────────────────────────────┘
│
▼
┌───────────────────────────────────────────────────────────────────┐
│ 3. 从链表头取块 │
│ block = pool->free_list; │
│ pool->free_list = block->next; │
└───────────────────────────────────────────────────────────────────┘
│
▼
┌───────────────────────────────────────────────────────────────────┐
│ 4. 更新统计信息 │
│ pool->used_count++; │
│ pool->alloc_count++; │
│ if (pool->used_count > pool->peak_used) { │
│ pool->peak_used = pool->used_count; │
│ } │
└───────────────────────────────────────────────────────────────────┘
│
▼
┌───────────────────────────────────────────────────────────────────┐
│ 5. 释放互斥锁 │
│ OSMutexPost(pool->mutex); │
└───────────────────────────────────────────────────────────────────┘
│
▼
[返回] (void *)block
4.3 内存池释放函数
/**
* @brief 释放内存回池
* @param ptr 内存指针
*
* 函数流程:
* 1. 根据地址确定所属池
* 2. 获取互斥锁
* 3. 将块放回链表头
* 4. 更新统计
*/
void static_pool_free(void *ptr)
{
StaticPool_t *pool = NULL;
PoolBlock_t *block;
uint32_t addr = (uint32_t)ptr;
/* 1. 确定所属池 */
for (uint32_t i = 0; i < MEM_POOL_MAX; i++) {
uint32_t start = (uint32_t)g_pools[i].pool_start;
uint32_t end = start + g_pools[i].block_size * g_pools[i].block_count;
if (addr >= start && addr < end) {
pool = &g_pools[i];
break;
}
}
if (pool == NULL) return;
/* 2. 获取互斥锁 */
OSMutexPend(pool->mutex, 0, &err);
/* 3. 放回链表头 */
block = (PoolBlock_t *)ptr;
block->next = pool->free_list;
pool->free_list = block;
/* 4. 更新统计 */
pool->used_count--;
pool->free_count++;
OSMutexPost(pool->mutex);
}
5. MVP架构数据流
5.1 数据模型更新流程
/**
* @brief SPI数据更新(生产者调用)
* @param model 数据模型指针
* @param data SPI传感器数据
* @return true-成功, false-失败
*/
bool data_model_update_spi(DataModel_t *model, SpiSensorData_t *data)
{
uint8_t next_idx;
/* 1. 获取互斥锁 */
OSMutexPend(model->mutex_data, 0, &err);
/* 2. 计算下一个写指针 */
next_idx = (model->spi_write_idx + 1) % 8;
/* 3. 缓冲区满处理(覆盖最旧数据) */
if (next_idx == model->spi_read_idx) {
model->data_drop_count++;
model->spi_read_idx = (model->spi_read_idx + 1) % 8;
}
/* 4. 写入数据 */
memcpy(&model->spi_data[model->spi_write_idx], data, sizeof(SpiSensorData_t));
model->spi_write_idx = next_idx;
/* 5. 释放互斥锁 */
OSMutexPost(model->mutex_data);
/* 6. 通知所有观察者 */
DataObserver_t *obs = model->observers[DATA_TYPE_SPI];
while (obs != NULL) {
if (obs->on_data_changed != NULL) {
obs->on_data_changed(obs, DATA_TYPE_SPI, data, sizeof(SpiSensorData_t));
}
obs = obs->next;
}
/* 7. 发送信号量唤醒消费者 */
OSSemPost(model->sem_spi_ready);
return true;
}
5.2 MVP数据流图
┌─────────────────────────────────────────────────────────────────────────┐
│ MVP完整数据流 │
└─────────────────────────────────────────────────────────────────────────┘
[硬件中断/DMA]
│
│ 原始数据
▼
┌─────────────────────────────────────────────────────────────────┐
│ Model (数据模型) │
│ │
│ data_model_update_spi(data) │
│ │ │
│ ├── 环形缓冲区写入 │
│ ├── 更新写指针 │
│ └── notify_observers() ──────────────────────┐ │
└─────────────────────────────────────────────────────┼───────────┘
│
│ 观察者通知
▼
┌─────────────────────────────────────────────────────────────────┐
│ Presenter (数据展示器) │
│ │
│ on_spi_data_changed(observer, data) │
│ │ │
│ ├── 数据校验 │
│ ├── 格式转换 │
│ ├── 告警检查 │
│ └── view->update_display(data) ─────────────────┐ │
└───────────────────────────────────────────────────────┼────────┘
│
│ 显示更新
▼
┌─────────────────────────────────────────────────────────────────┐
│ View (视图层) │
│ │
│ console_update_display(view, type, data) │
│ │ │
│ ├── printf("SPI Sensor: ID=%u, Data=", sensor_id) │
│ ├── for (i=0; i<len; i++) printf("%02X ", data[i]) │
│ └── printf("\n") │
└─────────────────────────────────────────────────────────────────┘
5.3 数据观察者注册流程
/**
* @brief 注册数据观察者
* @param model 数据模型
* @param type 数据类型
* @param observer 观察者指针
*/
void data_model_register_observer(DataModel_t *model,
DataType_t type,
DataObserver_t *observer)
{
OS_ERR err;
if (model == NULL || observer == NULL) {
return;
}
/* 获取互斥锁 */
OSMutexPend(model->mutex_data, 0, &err);
/* 添加到观察者链表头部 */
observer->next = model->observers[type];
model->observers[type] = observer;
OSMutexPost(model->mutex_data);
}
6. 任务间通信流程
6.1 消息发送流程
/**
* @brief 发送消息(异步)
* @param msg 消息指针
* @return true-成功, false-失败
*/
bool msg_center_send(SystemMessage_t *msg)
{
MsgCenter_t *center = msg_center_get_instance();
OS_ERR err;
bool result = false;
if (center == NULL || msg == NULL) {
return false;
}
/* 1. 获取互斥锁 */
OSMutexPend(center->mutex_subscriber, 0, &err);
/* 2. 确定目标队列 */
if (msg->dst_task == 0) {
/* 广播模式:发送给所有订阅者 */
Subscriber_t *sub = center->subscribers[msg->type];
while (sub != NULL) {
OSQPost(center->task_queues[sub->task_id], msg);
sub = sub->next;
}
result = true;
} else {
/* 单播模式:发送给指定任务 */
if (center->task_queues[msg->dst_task] != NULL) {
OSQPost(center->task_queues[msg->dst_task], msg);
result = true;
}
}
/* 3. 更新统计 */
center->total_messages++;
OSMutexPost(center->mutex_subscriber);
return result;
}
6.2 消息接收流程
/**
* @brief 接收消息(阻塞)
* @param task_id 任务ID
* @param timeout 超时时间
* @return 消息指针
*/
SystemMessage_t* msg_center_receive(uint32_t task_id, uint32_t timeout)
{
MsgCenter_t *center = msg_center_get_instance();
SystemMessage_t *msg = NULL;
OS_ERR err;
if (center == NULL || task_id >= TASK_MAX_NUM) {
return NULL;
}
/* 从消息队列接收 */
msg = (SystemMessage_t *)OSQPend(center->task_queues[task_id], timeout, &err);
return msg;
}
6.3 任务间通信流程图
┌─────────────────────────────────────────────────────────────────────────┐ │ 任务间通信流程 │ └─────────────────────────────────────────────────────────────────────────┘ [任务A] │ │ 创建消息 ▼ ┌─────────────────────────────────────────────────────────────────┐ │ msg.type = MSG_TYPE_DATA_READY; │ │ msg.dst_task = TASK_B_ID; │ │ msg.data = &data; │ └─────────────────────────────────────────────────────────────────┘ │ │ msg_center_send(&msg) ▼ ┌─────────────────────────────────────────────────────────────────┐ │ 消息中心 │ │ │ │ ┌─────────────────────────────────────────────────────────┐ │ │ │ 1. 查找目标任务队列 │ │ │ │ 2. 互斥锁保护 │ │ │ │ 3. OSQPost(队列, 消息) │ │ │ │ 4. 更新统计 │ │ │ └─────────────────────────────────────────────────────────┘ │ └─────────────────────────────────────────────────────────────────┘ │ │ 消息入队 ▼ ┌─────────────────────────────────────────────────────────────────┐ │ [任务B的消息队列] │ │ ┌─────┐ ┌─────┐ ┌─────┐ ┌─────┐ │ │ │ msg1│→│ msg2│→│ msg3│→│ ... │ │ │ └─────┘ └─────┘ └─────┘ └─────┘ │ └─────────────────────────────────────────────────────────────────┘ │ │ OSQPend() ▼ [任务B] 接收并处理消息
7. 中断服务程序流程
7.1 DMA中断处理流程
/**
* @file stm32f4xx_it.c
* @brief DMA2 Stream0中断处理(ADC DMA)
*/
void DMA2_Stream0_IRQHandler(void)
{
/* 1. 进入中断,通知uCOS-II */
OSIntEnter();
/* 2. 检查传输完成中断标志 */
if (DMA_GetFlagStatus(DMA2_Stream0, DMA_FLAG_TCIF0) != RESET) {
/* 3. 清除中断标志 */
DMA_ClearFlag(DMA2_Stream0, DMA_FLAG_TCIF0);
/* 4. 调用DMA管理器中断处理 */
dma_manager_irq_handler(DMA2_Stream0);
/* 5. 缓冲区切换(在dma_manager中调用) */
// double_buffer_switch(db);
/* 6. 发送信号量唤醒任务 */
// OSSemPost(sem_data_ready);
}
/* 7. 退出中断,触发任务调度 */
OSIntExit();
}
7.2 UART中断处理流程
/**
* @brief USART1中断处理
*/
void USART1_IRQHandler(void)
{
OSIntEnter();
/* 1. 接收数据寄存器非空中断 */
if (USART_GetITStatus(USART1, USART_IT_RXNE) != RESET) {
uint8_t data = USART_ReceiveData(USART1);
/* 2. 调用HAL层回调 */
hal_uart_rx_isr_handler(uart_device[0], data);
/* 3. 写入环形缓冲区 */
ring_buffer_write(rx_ring, data);
/* 4. 发送信号量 */
OSSemPost(sem_rx_data);
}
/* 5. 发送完成中断 */
if (USART_GetITStatus(USART1, USART_IT_TC) != RESET) {
USART_ClearITPendingBit(USART1, USART_IT_TC);
hal_uart_tc_isr_handler(uart_device[0]);
OSSemPost(sem_tx_complete);
}
/* 6. 错误中断处理 */
if (USART_GetITStatus(USART1, USART_IT_ORE) != RESET) {
USART_ClearITPendingBit(USART1, USART_IT_ORE);
uart_error_handler(uart_device[0], UART_ERROR_OVERRUN);
}
OSIntExit();
}
7.3 中断处理流程图
┌─────────────────────────────────────────────────────────────────────────┐ │ 中断处理完整流程 │ └─────────────────────────────────────────────────────────────────────────┘ [硬件触发中断] │ ▼ ┌─────────────────────────────────────────────────────────────────┐ │ 1. CPU自动压栈 │ │ - PC, LR, R0-R3, R12, xPSR │ │ - 进入Handler模式 │ └─────────────────────────────────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────────────────┐ │ 2. 中断向量表跳转 │ │ - 从0x08000000 + 4*IRQn读取向量 │ │ - 跳转到中断服务函数 │ └─────────────────────────────────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────────────────┐ │ 3. OSIntEnter() - 通知RTOS │ │ - OSIntNesting++ │ │ - 禁止任务调度 │ └─────────────────────────────────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────────────────┐ │ 4. 中断处理函数体 │ │ ├── 读取状态寄存器 │ │ ├── 清除中断标志 │ │ ├── 处理数据(最小化) │ │ ├── 写入环形缓冲区 │ │ └── 发送信号量 │ └─────────────────────────────────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────────────────┐ │ 5. OSIntExit() - 退出RTOS │ │ - OSIntNesting-- │ │ - if (OSIntNesting == 0) 检查任务调度 │ │ - 如果有更高优先级任务就绪,触发PendSV │ └─────────────────────────────────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────────────────┐ │ 6. 中断返回 │ │ - 出栈恢复现场 │ │ - 返回被中断的任务 │ └─────────────────────────────────────────────────────────────────┘
8. 系统监控与故障恢复
8.1 监控任务完整代码
/**
* @file monitor_task.c
* @brief 系统监控任务
*/
void system_monitor_task(void *p_arg)
{
(void)p_arg;
/* 初始化监控数据 */
MonitorData_t *monitor = &g_monitor_data;
monitor->cpu_alarm_threshold = 90;
monitor->mem_alarm_threshold = 4096;
/* 初始化看门狗 */
IWDG_WriteAccessCmd(IWDG_WriteAccess_Enable);
IWDG_SetPrescaler(IWDG_Prescaler_64);
IWDG_SetReload(1250); // 2秒超时
IWDG_Enable();
/* 主监控循环 */
while (1) {
/* 1. 计算CPU使用率 */
uint32_t idle_count = OSIdleCtr;
uint32_t total_count = OSIdleCtrMax;
if (total_count > monitor->total_counter_last) {
uint32_t idle_delta = idle_count - monitor->idle_counter_last;
uint32_t total_delta = total_count - monitor->total_counter_last;
if (total_delta > 0) {
monitor->cpu_usage_percent = 100 - (idle_delta * 100 / total_delta);
/* CPU过载检测 */
if (monitor->cpu_usage_percent > monitor->cpu_alarm_threshold) {
if (!monitor->is_cpu_overload) {
monitor->is_cpu_overload = true;
SystemMessage_t msg = {
.type = MSG_TYPE_ERROR_REPORT,
.priority = MSG_PRIO_HIGH,
.data = &monitor->cpu_usage_percent
};
msg_center_send(&msg);
}
} else {
monitor->is_cpu_overload = false;
}
}
}
monitor->idle_counter_last = idle_count;
monitor->total_counter_last = total_count;
/* 2. 检查内存使用 */
StaticPoolStats_t stats;
static_pool_get_stats(MEM_POOL_SMALL, &stats);
if (stats.free_blocks < 10) { // 少于10个空闲块
if (!monitor->is_memory_low) {
monitor->is_memory_low = true;
SystemMessage_t msg = {
.type = MSG_TYPE_ERROR_REPORT,
.priority = MSG_PRIO_HIGH,
.data_len = sizeof(uint32_t),
.data = &stats.free_blocks
};
msg_center_send(&msg);
}
} else {
monitor->is_memory_low = false;
}
/* 3. 检查任务看门狗 */
TaskInfo_t tasks[OS_MAX_TASKS];
uint32_t task_count = task_manager_get_all_info(tasks, OS_MAX_TASKS);
for (uint32_t i = 0; i < task_count; i++) {
/* 检查栈溢出 */
if (tasks[i].stack_used > tasks[i].stack_size - 64) {
SystemMessage_t msg = {
.type = MSG_TYPE_ERROR_REPORT,
.priority = MSG_PRIO_HIGH,
.data_len = sizeof(uint32_t) * 2,
};
msg_center_send(&msg);
}
}
/* 4. 更新系统状态到数据模型 */
DataModel_t *model = data_model_get_instance();
SystemStatus_t status = {
.uptime_ms = OSTimeGet(),
.cpu_usage = monitor->cpu_usage_percent,
.free_heap = stats.free_blocks * stats.block_size,
.task_count = task_count
};
data_model_update_system(model, &status);
/* 5. 喂看门狗 */
IWDG_ReloadCounter();
/* 6. 延时100ms */
OSTimeDlyHMSM(0, 0, 0, 100);
}
}
8.2 监控任务流程图
┌─────────────────────────────────────────────────────────────────────────┐ │ 系统监控任务流程图 │ └─────────────────────────────────────────────────────────────────────────┘ [任务启动] │ ▼ ┌───────────────────────────────────────────────────────────────────┐ │ 初始化监控数据 │ │ - CPU告警阈值: 90% │ │ - 内存告警阈值: 4KB │ │ - 初始化看门狗: 2秒超时 │ └───────────────────────────────────────────────────────────────────┘ │ ▼ ┌───────────────────────────────────────────────────────────────┐ │ 主循环(每100ms) │ └───────────────────────────────────────────────────────────────┘ │ ▼ ┌───────────────────────────────────────────────────────────────────┐ │ 1. 计算CPU使用率 │ │ ├── 读取空闲任务计数器 │ │ ├── 计算使用率 = 100 - (空闲增量/总增量) │ │ └── 如果 >90% 发送告警 │ └───────────────────────────────────────────────────────────────────┘ │ ▼ ┌───────────────────────────────────────────────────────────────────┐ │ 2. 检查内存使用 │ │ ├── 获取静态内存池统计 │ │ ├── 如果空闲块 <10 发送告警 │ │ └── 更新最小空闲记录 │ └───────────────────────────────────────────────────────────────────┘ │ ▼ ┌───────────────────────────────────────────────────────────────────┐ │ 3. 检查任务看门狗 │ │ ├── 遍历所有任务 │ │ ├── 检查栈使用率 >90% 告警 │ │ └── 检查任务是否挂死 │ └───────────────────────────────────────────────────────────────────┘ │ ▼ ┌───────────────────────────────────────────────────────────────────┐ │ 4. 更新系统状态到数据模型 │ │ ├── CPU使用率 │ │ ├── 空闲内存 │ │ └── 任务数量 │ └───────────────────────────────────────────────────────────────────┘ │ ▼ ┌───────────────────────────────────────────────────────────────────┐ │ 5. 喂看门狗 │ │ └── IWDG_ReloadCounter() │ └───────────────────────────────────────────────────────────────────┘ │ ▼ ┌───────────────────────────────────────────────────────────────────┐ │ 6. 延时100ms │ │ └── OSTimeDlyHMSM(0,0,0,100) │ └───────────────────────────────────────────────────────────────────┘ │ └──────────────────→ 返回主循环
9. 完整运行示例
9.1 系统启动到运行完整时序
时间线 事件 函数调用 ───────────────────────────────────────────────────────────────────────────── 0ms [上电复位] │ 1us ├── SystemInit() 时钟配置 │ └── 设置PLL: 8MHz * 336 / 8 / 2 = 168MHz │ 10us ├── static_memory_pool_init() 内存池初始化 │ ├── init_single_pool(SMALL) 1.5KB │ ├── init_single_pool(MEDIUM) 6KB │ ├── init_single_pool(LARGE) 12KB │ ├── init_single_pool(DMA) 12KB │ └── init_single_pool(STACK) 10KB │ 20us ├── OSInit() uCOS-II初始化 │ ├── 创建空闲任务 │ └── 创建统计任务 │ 30us ├── OSTaskCreate(startup_task) 创建启动任务 │ └── 优先级3,栈512B │ 40us ├── OSStart() 启动调度器 │ │ │ ▼ 50us ├── [启动任务运行] │ ├── hal_spi_init() SPI初始化 │ │ ├── hal_spi_create(1) SPI1创建 │ │ │ └── static_pool_alloc() 分配设备结构体 │ │ ├── hal_spi_create(2) SPI2创建 │ │ └── hal_spi_create(3) SPI3创建 │ │ │ ├── hal_uart_init() UART初始化 │ │ ├── hal_uart_create(1) USART1 │ │ ├── hal_uart_create(2) USART2 │ │ └── ... USART3/4/5/6 │ │ │ ├── hal_adc_init() ADC初始化 │ │ ├── hal_adc_create(1) ADC1 │ │ └── hal_adc_create(2) ADC2 │ │ │ ├── task_manager_create() 创建应用任务 │ │ ├── SPI_Task (优先级5) │ │ ├── UART_Task (优先级6) │ │ ├── ADC_Task (优先级7) │ │ ├── Presenter (优先级10) │ │ └── Monitor (优先级20) │ │ │ └── OSTaskDel(OS_PRIO_SELF) 删除自身 │ 100ms ├── [SPI_Task运行] │ ├── hal_spi_config(10MHz, DMA) │ ├── double_buffer_create() │ └── spi_dev->transfer_double_buf() │ └── dma_manager_start() 启动DMA接收 │ 101ms ├── [DMA中断] SPI数据到达 │ ├── double_buffer_switch() 缓冲区切换 │ └── OSSemPost(sem_data_ready) 唤醒SPI_Task │ 102ms ├── [SPI_Task处理数据] │ ├── double_buffer_get_ready() 获取缓冲区 │ ├── spi_parse_frame() 解析数据 │ └── data_model_update_spi() 更新数据模型 │ └── notify_observers() 通知Presenter │ 103ms ├── [Presenter处理] │ ├── on_spi_data_changed() 数据变化回调 │ ├── 数据格式化 │ └── view->update_display() 显示数据 │ 104ms ├── [UART_Task运行] │ ├── 等待接收中断 │ └── 处理串口命令 │ 105ms ├── [ADC_Task运行] │ ├── ADC_DMA_IRQHandler() ADC采样完成 │ └── data_model_update_adc() 更新ADC数据 │ 106ms ├── [Monitor_Task运行] │ ├── 计算CPU使用率: 42% │ ├── 检查内存: 空闲12KB │ ├── 更新系统状态 │ └── IWDG_ReloadCounter() 喂狗 │ └── 系统持续运行...
9.2 典型数据流示例
/* ============================================================================
* 示例:SPI传感器数据采集完整流程
* ============================================================================ */
/* 1. 硬件层 - DMA自动接收数据 */
// SPI硬件接收到数据 → DMA自动搬运到缓冲区0
// 缓冲区0满 → DMA中断触发
// DMA中断 → double_buffer_switch() 切换到缓冲区1
// DMA继续填充缓冲区1
/* 2. 驱动层 - 中断处理 */
void DMA2_Stream3_IRQHandler(void)
{
OSIntEnter();
// 清除中断标志
DMA_ClearFlag(DMA2_Stream3, DMA_FLAG_TCIF3);
// 切换缓冲区
double_buffer_switch(spi_db);
// 唤醒任务
OSSemPost(spi_sem_ready);
OSIntExit();
}
/* 3. 任务层 - SPI数据处理任务 */
void spi_task(void *p_arg)
{
while (1) {
// 等待数据就绪
OSSemPend(spi_sem_ready, 0);
// 获取就绪缓冲区
int8_t buf_idx = double_buffer_get_ready(spi_db, &len);
uint8_t *data = double_buffer_get_ptr(spi_db, buf_idx);
// 解析数据帧
if (data[0] == 0xAA && data[1] == 0x55) {
SpiSensorData_t sensor_data;
sensor_data.sensor_id = data[2];
sensor_data.data_len = data[3];
memcpy(sensor_data.data, &data[4], sensor_data.data_len);
sensor_data.crc = *(uint16_t*)&data[4 + sensor_data.data_len];
// 更新数据模型
data_model_update_spi(model, &sensor_data);
}
// 释放缓冲区
double_buffer_release(spi_db, buf_idx);
}
}
/* 4. 模型层 - 数据模型更新 */
bool data_model_update_spi(DataModel_t *model, SpiSensorData_t *data)
{
// 环形缓冲区写入
memcpy(&model->spi_data[model->spi_write_idx], data, sizeof(SpiSensorData_t));
model->spi_write_idx = (model->spi_write_idx + 1) % 8;
// 通知观察者
DataObserver_t *obs = model->observers[DATA_TYPE_SPI];
while (obs) {
obs->on_data_changed(obs, DATA_TYPE_SPI, data, sizeof(SpiSensorData_t));
obs = obs->next;
}
return true;
}
/* 5. 展示层 - Presenter处理 */
void on_spi_data_changed(DataObserver_t *obs, DataType_t type, void *data, uint32_t len)
{
SpiSensorData_t *sensor = (SpiSensorData_t *)data;
DataPresenter_t *presenter = (DataPresenter_t *)obs->context;
// 数据校验
if (!sensor->is_valid) {
presenter->view->show_alert(presenter->view, 0x1001, "CRC Error");
return;
}
// 格式转换
char display_buf[128];
sprintf(display_buf, "Sensor %d: ", sensor->sensor_id);
for (int i = 0; i < sensor->data_len && i < 16; i++) {
sprintf(&display_buf[strlen(display_buf)], "%02X ", sensor->data[i]);
}
// 显示
presenter->view->update_display(presenter->view, type, display_buf);
}
10. 编译配置与链接脚本
10.1 链接脚本完整配置
/* stm32f4xx_flash.ld - 完整链接脚本 */
/* 内存区域定义 */
MEMORY
{
FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 1024K
SRAM (xrw) : ORIGIN = 0x20000000, LENGTH = 112K
CCMRAM (rw) : ORIGIN = 0x10000000, LENGTH = 64K
}
/* 栈顶地址 */
_estack = ORIGIN(CCMRAM) + LENGTH(CCMRAM);
SECTIONS
{
/* 中断向量表 */
.isr_vector : {
. = ALIGN(4);
KEEP(*(.isr_vector))
. = ALIGN(4);
} >FLASH
/* 代码段 */
.text : {
. = ALIGN(4);
*(.text)
*(.text.*)
*(.glue_7)
*(.glue_7t)
*(.eh_frame)
KEEP(*(.init))
KEEP(*(.fini))
. = ALIGN(4);
_etext = .;
} >FLASH
/* 只读数据段 */
.rodata : {
. = ALIGN(4);
*(.rodata)
*(.rodata.*)
. = ALIGN(4);
} >FLASH
/* 初始化数据段 */
.data : {
. = ALIGN(4);
_sdata = .;
*(.data)
*(.data.*)
. = ALIGN(4);
_edata = .;
} >SRAM AT> FLASH
/* 未初始化数据段 */
.bss : {
. = ALIGN(4);
_sbss = .;
*(.bss)
*(.bss.*)
*(COMMON)
. = ALIGN(4);
_ebss = .;
} >SRAM
/* DMA缓冲区段 - 32字节对齐,放在SRAM */
.dma_ram (NOLOAD) : {
. = ALIGN(32);
_sdma = .;
*(.dma_ram)
*(.dma_ram.*)
. = ALIGN(32);
_edma = .;
} >SRAM
/* CCM RAM段 - CPU独占,零等待 */
.ccmram (NOLOAD) : {
. = ALIGN(8);
_sccm = .;
*(.ccmram)
*(.ccmram.*)
. = ALIGN(8);
_eccm = .;
} >CCMRAM
/* 任务栈段 - 放在CCM RAM */
.stack (NOLOAD) : {
. = ALIGN(8);
_stack_start = .;
*(.stack)
. = ALIGN(8);
_stack_end = .;
} >CCMRAM
}
第二部分 STM32F407嵌入式系统架构bug思路集
bug思路与详细解答
问题1:开机要延时多久,不会因为开机电压震荡到程序数据紊乱?
bug思路:
/**
* @brief 开机延时设计 - 解决电源稳定问题
*
* 分析:STM32F407上电后,外部晶振需要稳定时间(典型2ms)
* 电源电压建立需要时间(取决于外部电容)
* 外设初始化需要等待稳定
*/
/* 解决方案:分级延时 + 状态检测 */
void SystemPowerOnSequence(void)
{
/* 1. 硬件级延时 - 等待电源稳定 */
// 方法A:简单延时(保守设计)
for (volatile uint32_t i = 0; i < 100000; i++); // 约10ms @168MHz
// 方法B:检测电源电压(推荐)
while (PWR_GetFlagStatus(PWR_FLAG_VOS) == RESET); // 等待电压调节器稳定
/* 2. 时钟稳定检测 */
RCC_HSEConfig(RCC_HSE_ON);
while (RCC_WaitForHSEStartUp() == ERROR); // 等待外部晶振稳定
/* 3. 外设稳定延时 */
HAL_Delay(10); // 10ms延时,确保外设电源稳定
/* 4. 内存初始化前等待 */
__DSB(); // 数据同步屏障
__ISB(); // 指令同步屏障
}
开机时序图:
┌─────────────────────────────────────────────────────────────────────────┐ │ 开机时序分析 │ └─────────────────────────────────────────────────────────────────────────┘ VDD 3.3V ┤ │ ┌───────────────────────────────────────────────────── │ │ │ │ 电压上升时间 │ │ (典型1-5ms) │ │ 0V └──────────┴───────────────────────────────────────────────────── ↑ ↑ │ │ 上电时刻 电压稳定点 │ │ │ │ HSE起振时间 │ │ (典型2ms) │ │ │ └──┬──┐ │ │ │ │ │ │ │ └──┴────────────────────────────────────────────── │ ↑ │ │ │ 晶振稳定 │ │ │ │ 外设初始化 │ │ (5-10ms) │ │ └──────────────────┴──────────────────────────────────────────── ↑ │ 系统启动 (总延时: 10-20ms)
问题2:中断与DMA如何巧妙设计才不会数据覆盖而没有取走?
bug思路:
/**
* @brief 双缓冲+环形缓冲区设计 - 防止数据覆盖
*
* 核心思想:
* 1. DMA使用双缓冲模式,自动切换
* 2. 应用层使用环形缓冲区二次缓冲
* 3. 使用信号量同步,避免竞态
*/
/*==============================================================================
* 方案1:DMA双缓冲 + 环形缓冲区(推荐)
*============================================================================*/
typedef struct {
/* DMA双缓冲 */
uint8_t dma_buf0[2048] __attribute__((aligned(32)));
uint8_t dma_buf1[2048] __attribute__((aligned(32)));
volatile uint8_t active_dma_buf; // 0或1
/* 环形缓冲区 */
uint8_t ring_buf[8192];
volatile uint32_t write_ptr;
volatile uint32_t read_ptr;
/* 同步信号量 */
OS_EVENT *sem_data_ready;
OS_EVENT *sem_buffer_free;
} SafeBuffer_t;
/**
* @brief DMA中断处理 - 安全切换
*/
void DMA_IRQHandler(void)
{
OSIntEnter();
if (DMA_GetFlagStatus(DMA_Stream, DMA_FLAG_TCIF)) {
DMA_ClearFlag(DMA_Stream, DMA_FLAG_TCIF);
/* 1. 获取当前完成的缓冲区索引 */
uint8_t completed_buf = active_dma_buf;
/* 2. 切换到另一个缓冲区 */
active_dma_buf ^= 1;
DMA_SetCurrDataCounter(DMA_Stream, BUFFER_SIZE);
/* 3. 将已完成的数据写入环形缓冲区(快速操作) */
uint8_t *src = (completed_buf == 0) ? dma_buf0 : dma_buf1;
uint32_t bytes_to_copy = BUFFER_SIZE;
/* 检查环形缓冲区是否有足够空间 */
uint32_t free_space = RING_BUFFER_SIZE - (write_ptr - read_ptr);
if (free_space >= bytes_to_copy) {
/* 有空间,写入环形缓冲区 */
for (uint32_t i = 0; i < bytes_to_copy; i++) {
ring_buf[write_ptr++ & RING_BUFFER_MASK] = src[i];
}
/* 通知任务有数据 */
OSSemPost(sem_data_ready);
} else {
/* 缓冲区满,丢弃数据并记录溢出 */
overflow_count++;
}
}
OSIntExit();
}
/**
* @brief 应用任务 - 安全读取数据
*/
void DataProcessTask(void *p_arg)
{
while (1) {
/* 等待数据就绪(超时机制) */
if (OSSemPend(sem_data_ready, 100, &err) == OS_ERR_NONE) {
/* 从环形缓冲区读取数据 */
while (read_ptr < write_ptr) {
uint8_t data = ring_buf[read_ptr++ & RING_BUFFER_MASK];
ProcessData(data);
}
}
}
}
数据流防覆盖设计图:
┌─────────────────────────────────────────────────────────────────────────┐ │ 数据流防覆盖设计 │ └─────────────────────────────────────────────────────────────────────────┘ [SPI硬件] │ │ 硬件自动传输 ▼ ┌─────────────────────────────────────────────────────────────────┐ │ DMA双缓冲 │ │ ┌─────────────────┐ ┌─────────────────┐ │ │ │ 缓冲区0 │ │ 缓冲区1 │ │ │ │ (2KB) │ │ (2KB) │ │ │ │ │ │ │ │ │ │ [DMA填充中] │ │ [空闲] │ │ │ └─────────────────┘ └─────────────────┘ │ │ │ ▲ │ │ │ 缓冲区满 │ │ │ ▼ │ │ │ ┌─────────────────┐ ┌─────────────────┐ │ │ │ 缓冲区0 │ │ 缓冲区1 │ │ │ │ [已满] │───▶│ [DMA填充中] │ │ │ └─────────────────┘ └─────────────────┘ │ │ │ │ │ │ │ DMA中断触发 │ │ │ ▼ │ │ └─────────┼──────────────────────┼──────────────────────────────┘ │ │ │ 快速拷贝(关中断时间最小化) ▼ ▼ ┌─────────────────────────────────────────────────────────────────┐ │ 环形缓冲区 (8KB) │ │ ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┐ │ │ │ 0 │ 1 │ 2 │ 3 │ 4 │ 5 │ 6 │ 7 │ 8 │ 9 │...│ N │ │ │ └───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┘ │ │ ▲ ▲ │ │ │ │ │ │ 读指针 写指针 │ │ (任务读取) (中断写入) │ └─────────────────────────────────────────────────────────────────┘ │ │ 信号量同步 ▼ [应用任务] 安全读取,不会覆盖
问题3:任务优先级反转是那种场景下发生?
bug思路:
/**
* @brief 优先级反转场景分析
*
* 定义:高优先级任务被低优先级任务阻塞,而中优先级任务占用CPU
*/
/*==============================================================================
* 优先级反转场景演示
*============================================================================*/
/* 三个任务:高、中、低优先级 */
#define TASK_PRIO_HIGH 5
#define TASK_PRIO_MID 10
#define TASK_PRIO_LOW 15
/* 共享资源互斥锁 */
OS_EVENT *shared_mutex;
/* 低优先级任务 - 占用共享资源 */
void LowPriorityTask(void *p_arg)
{
while (1) {
/* 获取共享资源 */
OSMutexPend(shared_mutex, 0, &err);
/* 长时间操作共享资源(如SPI通信100ms) */
Spi_Transfer(large_buffer, 1024);
/* 释放资源 */
OSMutexPost(shared_mutex);
OSTimeDly(100);
}
}
/* 中优先级任务 - 不访问共享资源,但占用CPU */
void MidPriorityTask(void *p_arg)
{
while (1) {
/* 大量计算,长时间占用CPU */
for (int i = 0; i < 1000000; i++) {
Calculate();
}
OSTimeDly(10);
}
}
/* 高优先级任务 - 需要访问共享资源 */
void HighPriorityTask(void *p_arg)
{
while (1) {
/* 尝试获取共享资源 */
OSMutexPend(shared_mutex, 0, &err);
/* 快速操作 */
ProcessData();
OSMutexPost(shared_mutex);
OSTimeDly(10);
}
}
优先级反转时序图:
┌─────────────────────────────────────────────────────────────────────────┐ │ 优先级反转时序图 │ └─────────────────────────────────────────────────────────────────────────┘ 时间 → │ │ 低优先级任务(L) 中优先级任务(M) 高优先级任务(H) │ 0ms ├── [L获取互斥锁] ───────────────────────────────────────────────────── │ │ │ │ 执行长操作 │ │ 10ms├──────┼────────────────────────────────────── [H就绪,等待互斥锁] ──┐ │ │ │ │ │ │ │ 被阻塞 │ │ │ │ │ 20ms├──────┼────────────────── [M抢占CPU] ──────────────────────────────┤ │ │ │ │ │ │ │ 执行计算 │ │ │ │ (占用CPU) │ │ │ │ │ 30ms├──────┼──────────────────────┼─────────────────────────────────────┤ │ │ │ │ │ │ │ │ 40ms├──────┼──────────────────────┼─────────────────────────────────────┤ │ │ │ │ │ │ │ │ 50ms├──────┼──────────────────────┼─────────────────────────────────────┤ │ │ │ │ │ │ │ │ 60ms├──────┼──────────────────────┼─────────────────────────────────────┤ │ │ │ │ │ │ │ │ 70ms├──────┼──────────────────────┼─────────────────────────────────────┤ │ │ │ │ │ │ │ │ 80ms├──────┼──────────────────────┼─────────────────────────────────────┤ │ │ │ │ │ │ [L释放互斥锁] │ │ │ ▼ │ │ │ ┌──────────────────────────┴─────────────────────────────────────┐ │ │ [H获得互斥锁,开始执行] │ │ └────────────────────────────────────────────────────────────────┘ │ │ └── 结论:H任务被M任务间接阻塞了70ms,尽管M优先级低于H 【解决方案】:使用优先级继承协议
解决方案:优先级继承
/**
* @brief 使用uCOS-II的互斥锁(内置优先级继承)
*/
void PriorityInheritanceExample(void)
{
/* uCOS-II的互斥锁自动实现优先级继承 */
OS_EVENT *mutex;
mutex = OSMutexCreate(0, &err); // 创建互斥锁
/* 当低优先级任务持有互斥锁时,其优先级临时提升到等待该锁的最高优先级 */
/* 释放后恢复原优先级 */
}
问题4:你的程序会不会因为缓存区管理产生死锁?
bug思路:
/**
* @brief 死锁场景分析与预防
*
* 死锁必要条件:
* 1. 互斥条件
* 2. 持有并等待
* 3. 不可剥夺
* 4. 循环等待
*/
/*==============================================================================
* 可能导致死锁的场景
*============================================================================*/
/* 场景1:双缓冲区的生产者-消费者死锁 */
void PotentialDeadlock_ProducerConsumer(void)
{
/* 错误设计:生产者等待空闲缓冲区,消费者等待满缓冲区 */
// 生产者:
OSSemPend(sem_empty, 0); // 等待空缓冲区
OSSemPend(sem_full, 0); // 错误:不应等待满缓冲区
// 可能导致:生产者持有空缓冲区信号量,等待满缓冲区
// 消费者:
OSSemPend(sem_full, 0); // 等待满缓冲区
OSSemPend(sem_empty, 0); // 错误:不应等待空缓冲区
// 可能导致:消费者持有满缓冲区信号量,等待空缓冲区
}
/* 正确设计:避免循环等待 */
void Correct_ProducerConsumer(void)
{
// 生产者:只等待空缓冲区
OSSemPend(sem_empty, 0); // 等待空缓冲区
// 写入数据
OSSemPost(sem_full); // 通知消费者
// 消费者:只等待满缓冲区
OSSemPend(sem_full, 0); // 等待满缓冲区
// 读取数据
OSSemPost(sem_empty); // 通知生产者
}
死锁预防设计:
/**
* @brief 安全缓冲区管理器 - 预防死锁
*/
typedef struct {
/* 双缓冲 */
uint8_t *buf0;
uint8_t *buf1;
volatile uint8_t active;
volatile uint8_t ready;
/* 信号量 - 单一方向等待 */
OS_EVENT *sem_producer; // 生产者等待空缓冲区
OS_EVENT *sem_consumer; // 消费者等待满缓冲区
/* 超时机制 */
uint32_t timeout_ms;
} SafeDoubleBuffer_t;
/**
* @brief 安全写入(生产者)
* @return true-成功, false-超时
*/
bool SafeDoubleBuffer_Write(SafeDoubleBuffer_t *db, uint8_t *data, uint32_t len)
{
OS_ERR err;
/* 1. 等待空缓冲区(带超时) */
if (OSSemPend(db->sem_producer, db->timeout_ms, &err) != OS_ERR_NONE) {
return false; // 超时返回,不会死锁
}
/* 2. 写入数据(临界区保护) */
OS_ENTER_CRITICAL();
uint8_t write_buf = (db->active == 0) ? 1 : 0;
if (write_buf == 0) {
memcpy(db->buf0, data, len);
} else {
memcpy(db->buf1, data, len);
}
db->ready = write_buf;
OS_EXIT_CRITICAL();
/* 3. 通知消费者 */
OSSemPost(db->sem_consumer);
return true;
}
/**
* @brief 安全读取(消费者)
* @return true-成功, false-超时
*/
bool SafeDoubleBuffer_Read(SafeDoubleBuffer_t *db, uint8_t *data, uint32_t *len)
{
OS_ERR err;
/* 1. 等待满缓冲区(带超时) */
if (OSSemPend(db->sem_consumer, db->timeout_ms, &err) != OS_ERR_NONE) {
return false; // 超时返回
}
/* 2. 读取数据 */
OS_ENTER_CRITICAL();
uint8_t read_buf = db->ready;
if (read_buf == 0) {
memcpy(data, db->buf0, BUFFER_SIZE);
} else {
memcpy(data, db->buf1, BUFFER_SIZE);
}
db->active = read_buf;
OS_EXIT_CRITICAL();
/* 3. 通知生产者有空缓冲区 */
OSSemPost(db->sem_producer);
return true;
}
死锁预防策略总结:
/* 预防死锁的5个策略 */
typedef enum {
DEADLOCK_PREVENTION_1 = 0, // 破坏互斥条件(使用无锁设计)
DEADLOCK_PREVENTION_2, // 破坏持有并等待(一次性申请所有资源)
DEADLOCK_PREVENTION_3, // 破坏不可剥夺(超时机制)
DEADLOCK_PREVENTION_4, // 破坏循环等待(固定资源顺序)
DEADLOCK_PREVENTION_5 // 银行家算法(资源预分配)
} DeadlockPrevention_t;
/* 本系统采用的策略 */
void SystemDeadlockPrevention(void)
{
/* 1. 破坏循环等待:统一资源申请顺序 */
// 规则:总是先申请DMA通道,再申请缓冲区,最后申请互斥锁
/* 2. 破坏不可剥夺:所有资源申请都带超时 */
OSMutexPend(mutex, timeout, &err); // 带超时
/* 3. 破坏持有并等待:使用一次性申请 */
// 在任务开始时一次性申请所有需要的资源
/* 4. 使用uCOS-II的内置机制 */
// - 互斥锁的优先级继承
// - 信号量的超时机制
}
问题5:操作系统特性还有哪些没有使用,如果全部要用,又如何设计?
bug思路:
/**
* @brief uCOS-II完整特性应用设计
*
* 未使用的特性清单及设计方案
*/
/*==============================================================================
* 1. 事件标志组 (Event Flags) - 多事件同步
*============================================================================*/
/* 应用场景:多个外设数据就绪后才处理 */
void EventFlagsDesign(void)
{
OS_FLAG_GRP *data_ready_flags;
/* 创建事件标志组 */
data_ready_flags = OSFlagCreate(0, &err);
/* SPI任务:数据就绪时设置标志 */
void spi_task(void *p_arg)
{
while (1) {
WaitForData();
OSFlagPost(data_ready_flags, 0x01, OS_FLAG_SET, &err);
}
}
/* ADC任务:数据就绪时设置标志 */
void adc_task(void *p_arg)
{
while (1) {
WaitForData();
OSFlagPost(data_ready_flags, 0x02, OS_FLAG_SET, &err);
}
}
/* UART任务:数据就绪时设置标志 */
void uart_task(void *p_arg)
{
while (1) {
WaitForData();
OSFlagPost(data_ready_flags, 0x04, OS_FLAG_SET, &err);
}
}
/* 融合任务:等待所有数据就绪 */
void fusion_task(void *p_arg)
{
while (1) {
/* 等待SPI、ADC、UART全部就绪 */
OSFlagPend(data_ready_flags, 0x07, OS_FLAG_WAIT_SET_ALL, 0, &err);
/* 所有数据就绪,执行融合处理 */
DataFusion();
/* 清除标志 */
OSFlagPost(data_ready_flags, 0x07, OS_FLAG_CLR, &err);
}
}
}
/*==============================================================================
* 2. 任务内建消息队列 (Task Built-in Queue)
*============================================================================*/
/* 应用场景:每个任务独立的消息队列,减少全局消息中心 */
void BuiltInQueueDesign(void)
{
/* 配置:使能任务内建消息队列 */
// os_cfg.h: #define OS_TASK_Q_EN 1
/* 创建任务时指定消息队列大小 */
OSTaskCreateExt(task_func,
NULL,
&stack[0],
TASK_PRIO,
TASK_PRIO,
&stack[STACK_SIZE-1],
STACK_SIZE,
(void *)0,
OS_TASK_OPT_STK_CLR | OS_TASK_OPT_STK_CHK,
&err);
/* 发送消息到指定任务 */
void SendMessageToTask(OS_TCB *task_tcb, void *msg)
{
OSTaskQPost(task_tcb, msg, sizeof(msg), &err);
}
/* 接收消息 */
void task_func(void *p_arg)
{
void *msg;
INT8U msg_size;
while (1) {
msg = OSTaskQPend(0, &msg_size, &err);
ProcessMessage(msg);
}
}
}
/*==============================================================================
* 3. 定时器服务 (Timer Service)
*============================================================================*/
/* 应用场景:周期性任务、超时检测、看门狗刷新 */
void TimerServiceDesign(void)
{
/* 创建定时器 */
OS_TMR *timer;
timer = OSTmrCreate(10, // 初始延迟10个节拍
100, // 周期100个节拍
OS_TMR_OPT_PERIODIC, // 周期性
TimerCallback, // 回调函数
"MyTimer",
&err);
/* 启动定时器 */
OSTmrStart(timer, &err);
/* 定时器回调(在定时器任务上下文中执行) */
void TimerCallback(void *p_arg)
{
/* 执行周期性任务,如传感器数据采集 */
CollectSensorData();
/* 或刷新看门狗 */
IWDG_ReloadCounter();
/* 或发送消息到任务 */
OSTaskQPost(sensor_task_tcb, &data, sizeof(data), &err);
}
}
/*==============================================================================
* 4. 内存分区管理 (Memory Partition)
*============================================================================*/
/* 应用场景:固定大小内存块管理,替代静态池 */
void MemoryPartitionDesign(void)
{
OS_MEM *mem_partition;
INT8U partition_mem[100 * 64]; // 100个64字节块
/* 创建内存分区 */
mem_partition = OSMemCreate(partition_mem, 100, 64, &err);
/* 分配内存 */
void *block = OSMemGet(mem_partition, &err);
/* 释放内存 */
OSMemPut(mem_partition, block);
/* 优点:比静态池更灵活,可动态创建/删除分区 */
}
/*==============================================================================
* 5. 钩子函数 (Hooks)
*============================================================================*/
/* 应用场景:系统调试、性能监控、任务跟踪 */
void HooksDesign(void)
{
/* 空闲任务钩子 - 低功耗模式 */
void App_IdleTaskHook(void)
{
/* 进入低功耗模式 */
__WFI(); // 等待中断唤醒
}
/* 任务切换钩子 - 任务跟踪 */
void App_TaskSwHook(void)
{
/* 记录任务切换时间 */
uint32_t now = DWT->CYCCNT;
TaskSwitchTime = now - last_switch_time;
last_switch_time = now;
/* 更新任务运行时间 */
current_task->runtime += TaskSwitchTime;
}
/* 统计任务钩子 - CPU使用率 */
void App_StatTaskHook(void)
{
/* 计算每个任务的CPU使用率 */
CalculateTaskCPUUsage();
}
/* 系统启动钩子 */
void App_StartupHook(void)
{
/* 初始化硬件 */
SystemClock_Config();
/* 打印启动信息 */
printf("uCOS-II Startup\n");
}
}
完整特性集成设计:
/**
* @file full_ucos_features.c
* @brief uCOS-II全部特性集成设计
*/
typedef struct {
/* 1. 任务管理 */
OS_TCB tasks[TASK_MAX];
uint8_t task_priorities[TASK_MAX];
/* 2. 同步机制 */
OS_EVENT *mutex_list[MUTEX_MAX];
OS_EVENT *sem_list[SEM_MAX];
OS_FLAG_GRP *flag_groups[FLAG_MAX];
/* 3. 通信机制 */
OS_Q *queues[QUEUE_MAX];
OS_MEM *mem_partitions[MEM_PART_MAX];
/* 4. 定时器 */
OS_TMR *timers[TIMER_MAX];
/* 5. 任务内建队列 */
uint8_t task_q_enabled[TASK_MAX];
} FullUcosSystem_t;
/**
* @brief 完整特性系统初始化
*/
void FullUcosSystemInit(void)
{
OS_ERR err;
/* 1. 初始化内核 */
OSInit();
/* 2. 创建内存分区 */
OS_MEM *msg_pool = OSMemCreate(msg_mem, MSG_COUNT, MSG_SIZE, &err);
OS_MEM *data_pool = OSMemCreate(data_mem, DATA_COUNT, DATA_SIZE, &err);
/* 3. 创建事件标志组 */
OS_FLAG_GRP *sync_flags = OSFlagCreate(0, &err);
/* 4. 创建互斥锁(带优先级继承) */
OS_EVENT *shared_mutex = OSMutexCreate(0, &err);
/* 5. 创建消息队列 */
OS_Q *cmd_queue = OSQCreate(cmd_queue_buf, CMD_QUEUE_SIZE);
/* 6. 创建定时器 */
OS_TMR *heartbeat = OSTmrCreate(0, 100, OS_TMR_OPT_PERIODIC,
HeartbeatCallback, NULL, "Heartbeat", &err);
OSTmrStart(heartbeat, &err);
/* 7. 创建任务(启用内建消息队列) */
OSTaskCreateExt(Task1,
NULL,
&stack1[0],
TASK1_PRIO,
TASK1_PRIO,
&stack1[STACK_SIZE-1],
STACK_SIZE,
(void *)0,
OS_TASK_OPT_STK_CLR | OS_TASK_OPT_STK_CHK | OS_TASK_OPT_TASK_Q_EN,
&err);
/* 8. 启动系统 */
OSStart();
}
/**
* @brief 完整特性应用示例
*/
void FullFeatureApplication(void)
{
/* 场景1:多源数据同步采集 */
void DataSyncTask(void *p_arg)
{
OS_FLAG_GRP *sync_flags;
OS_ERR err;
while (1) {
/* 等待SPI、ADC、UART三个标志全部置位 */
OSFlagPend(sync_flags, 0x07, OS_FLAG_WAIT_SET_ALL, 0, &err);
/* 从内存分区获取缓冲区 */
void *buf = OSMemGet(mem_partition, &err);
/* 数据融合处理 */
DataFusion(buf);
/* 通过消息队列发送结果 */
OSQPost(cmd_queue, buf);
/* 清除标志 */
OSFlagPost(sync_flags, 0x07, OS_FLAG_CLR, &err);
}
}
/* 场景2:周期性任务 + 超时检测 */
void TimerTask(void *p_arg)
{
OS_TMR *timer;
OS_ERR err;
timer = OSTmrCreate(0, 1000, OS_TMR_OPT_PERIODIC,
TimerCallback, NULL, "Periodic", &err);
OSTmrStart(timer, &err);
while (1) {
/* 等待消息队列,带超时 */
void *msg = OSQPend(cmd_queue, 100, &err);
if (err == OS_ERR_TIMEOUT) {
/* 超时处理 */
HandleTimeout();
} else {
ProcessMessage(msg);
}
}
}
/* 场景3:钩子函数应用 */
void HooksApplication(void)
{
/* 空闲任务钩子 - 进入睡眠模式 */
OSTaskIdleHook = IdleHook;
/* 统计任务钩子 - 记录任务CPU使用率 */
OSTaskStatHook = StatHook;
/* 任务切换钩子 - 任务跟踪 */
OSTaskSwHook = TaskSwitchHook;
}
}
bug思路总结清单
| 问题类别 | 问题内容 | 回答要点 |
|---|---|---|
| 启动时序 | 开机延时设计 | 10-20ms分级延时,电压检测,晶振稳定 |
| 数据保护 | 中断/DMA防覆盖 | 双缓冲+环形缓冲区+信号量同步 |
| 调度问题 | 优先级反转 | 互斥锁优先级继承机制 |
| 死锁预防 | 缓冲区死锁 | 单向等待+超时机制+资源顺序 |
| 系统特性 | 未使用特性 | 事件标志组、任务队列、定时器、内存分区、钩子函数 |
| 架构设计 | HAL+双缓冲+MVP | 分层架构、设计模式应用 |
| 竞态处理 | 多环境竞态 | 临界区、互斥锁、信号量、禁用中断 |
更多推荐



所有评论(0)