🌟 一、项目全景速览
1.1 系统核心功能
1.2 系统硬件电路原理图
🧠 二、FreeRTOS内核深度解析
2.1 任务调度机制
// FreeRTOSConfig.h 关键配置
#define configUSE_PREEMPTION 1 // 启用抢占式调度
#define configUSE_TIME_SLICING 1 // 时间片轮转
#define configTICK_RATE_HZ 1000 // 系统节拍1ms
#define configMINIMAL_STACK_SIZE 128 // 空闲任务栈大小任务状态迁移图

2.2 内存管理策略
// Heap_4 内存分配方案(32KB SRAM)
#define configTOTAL_HEAP_SIZE (32 * 1024) // 堆空间配置
#define configAPPLICATION_ALLOCATED_HEAP 1 // 使用自定义内存布局
// 内存分布示例:
// 任务栈空间:3x512B = 1.5KB
// 内核对象:1KB
// 用户堆:29.5KB⚙️ 三、核心代码全解析(mytask.c)
3.1 🧩 关键变量定义
#define UART1_DMA_RX_LEN 50 // DMA接收缓冲区长度
char gbuf_printf[UART1_DMA_RX_LEN]; // 串口发送缓冲区(地址:0x20000100)
char Read_data[UART1_DMA_RX_LEN]; // 原始接收缓冲区(volatile修饰,地址:0x20000132)
char LED_Read_data[UART1_DMA_RX_LEN]; // LED指令缓冲区(地址:0x20000164)
osSemaphoreId_t uart1_printf_gsemHandle; // 串口发送互斥信号量(二进制信号量)
osSemaphoreId_t uart1_rxok_gsemHandle; // 数据接收完成信号量(计数信号量)
osSemaphoreId_t LCD_refresh_gsemHandle; // LCD刷新触发信号量
uint8_t LED_Conctrl = 0; // LED控制模式标志位(0-自动闪烁,1-手动模式)3.2 🛠️ 核心函数解析
3.2.1 串口接收任务 StartUART1_recv_TaskFunction
void StartUART1_recv_TaskFunction(void *argument)
{
// 硬件初始化
HAL_UART_Receive_DMA(&huart1, (uint8_t *)rxBuffer, UART1_DMA_RX_LEN);
__HAL_UART_ENABLE_IT(&huart1, UART_IT_IDLE); // 使能空闲中断
while (1)
{
osSemaphoreAcquire(uart1_rxok_gsemHandle, osWaitForever); // 🔒 等待接收完成
// 计算有效数据长度
uint16_t now_dam_ip = UART1_DMA_RX_LEN - __HAL_DMA_GET_COUNTER(&hdma_usart1_rx);
// 数据搬运(环形缓冲区处理)
uint16_t rd_dma_ip = 0;
while (rd_dma_ip != now_dam_ip) {
Read_data[Read_data_len++] = rxBuffer[rd_dma_ip++];
if (rd_dma_ip >= UART1_DMA_RX_LEN) rd_dma_ip = 0; // 🔁 环形索引处理
}
// 触发下游处理
strcpy(LED_Read_data, Read_data); // 📤 数据拷贝到LED缓冲区
osSemaphoreRelease(LCD_refresh_gsemHandle); // 🚦 触发LCD刷新
}
}
3.2.2 LED控制任务 StartLED1TaskFunction
void StartLED1TaskFunction(void *argument)
{
for (;;) {
// 精确指令匹配(严格字符串比对)
if (strcmp(LED_Read_data, "LED_OFF") == 0) {
HAL_GPIO_WritePin(LED0_GPIO_Port, LED0_Pin, GPIO_PIN_SET); // 🔴 关闭红灯
HAL_GPIO_WritePin(LED1_GPIO_Port, LED1_Pin, GPIO_PIN_SET); // 🔵 关闭蓝灯
LED_Conctrl = 1; // 进入手动模式
}
else if (strcmp(LED_Read_data, "LED_ON") == 0) {
HAL_GPIO_WritePin(LED0_GPIO_Port, LED0_Pin, GPIO_PIN_RESET); // 🔴 开启红灯
HAL_GPIO_WritePin(LED1_GPIO_Port, LED1_Pin, GPIO_PIN_RESET); // 🔵 开启蓝灯
LED_Conctrl = 1;
}
else if (strcmp(LED_Read_data, "LED_AUTO") == 0) {
LED_Conctrl = 0; // ⚡ 进入自动模式
}
memset(LED_Read_data, 0, UART1_DMA_RX_LEN); // 🧹 清空指令缓存
osDelay(100); // ⏳ 100ms检测周期
}
}3.2.3 LCD显示任务 StartLCDDisplayTaskFunction
void StartLCDDisplayTaskFunction(void *argument)
{
lcd_init(); // 初始化LCD控制器(耗时约120ms)
uint8_t bg_index = 0; // 背景色索引
while (1) {
osSemaphoreAcquire(LCD_refresh_gsemHandle, osWaitForever); // 🚦 等待刷新信号
// 背景色循环(12色预定义)
switch(bg_index % 12) {
case 0: lcd_clear(WHITE); break; // ⚪ 白色背景
case 1: lcd_clear(BLACK); break; // ⚫ 黑色背景
// ...其他颜色处理
}
// 固定界面元素
lcd_show_string(10, 40, 240, 32, 32, "STM32F103ZET6", RED);
lcd_show_string(10, 150, 240, 16, 16, "UART Data:", BLACK);
lcd_show_string(10, 170, 240, 16, 16, Read_data, BLACK); // 📃 动态数据显示
bg_index++; // 更新颜色索引
}
}3.3 ⚡ 任务间通讯机制
信号量操作时序

⚙️ 四、系统运行时序分析
4.1 典型控制流场景(发送"LED_ON"指令)

4.2 关键时序参数
🧠 五、深入理解代码设计
5.1 环形缓冲区精妙设计
// DMA接收缓冲区示意图
rxBuffer[50]: [ ][ ][L][E][D][_][O][N][\r][\n][ ][ ]... // 环形填充
↑rd_dma_ip ↑now_dam_ip__HAL_DMA_GET_COUNTER:获取DMA剩余传输量,计算已接收数据长度环形索引计算:通过
rd_dma_ip = (rd_dma_ip + 1) % UART1_DMA_RX_LEN实现循环访问
5.2 信号量使用规范
osSemaphoreAcquire(sem, osWaitForever); // ❗ 永久等待模式
osSemaphoreRelease(sem); // 💡 必须成对出现
// 典型使用场景:
void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart) {
if (huart->Instance == USART1) {
osSemaphoreRelease(uart1_printf_gsemHandle); // 释放发送锁
}
}🚨 六、常见问题排查指南
6.1 数据接收不全
检查DMA配置:确保
hdma_usart1_rx.Init.Mode = DMA_CIRCULAR验证空闲中断:使用逻辑分析仪捕获IDLE信号
查看缓冲区溢出:增加
if(Read_data_len >= UART1_DMA_RX_LEN)保护
6.2 LCD显示异常
// 诊断步骤:
1. 检查背光控制信号(PB0)
2. 验证SPI时序参数(CubeMX配置)
3. 使用lcd_read_point()读取寄存器状态
4. 测量FSMC总线时序(适用于大屏驱动)🎯 结语:从理解到实践
通过本解析,您已经掌握:
✅ FreeRTOS任务划分原则
✅ DMA+空闲中断的最佳实践
✅ LCD驱动开发核心要点
✅ 工业级代码调试方法
下一步建议:
🔧 尝试添加触摸屏交互功能
🔧 实现JSON格式指令解析
🔧 移植LVGL图形界面库
推荐实践项目
📡 添加Wi-Fi模块实现远程控制
🎛️ 开发基于触摸屏的人机界面
📊 实现实时数据曲线显示功能
🔒 增加通信数据加密功能
⚡ 移植LVGL图形库提升UI体验
完整工程代码已整理至:[Github仓库链接]、[个人搭建gitee仓库连接]
(注:本文所有时序参数基于STM32F103ZET6 72MHz测试环境,实际表现可能因硬件差异略有不同)