第一章:项目全景与开发环境搭建

大家好,我是你们的嵌入式系统讲师。今天咱们正式开始《FreeRTOS实战项目:从设计到交付》的第一课。

说实话,我见过太多工程师学RTOS时卡在第一步——环境没搭好,源码移植失败,然后就放弃了。这很可惜。所以这一章,我会带着你一步步把地基打牢。

1.1 FreeRTOS是什么?为什么选它?

FreeRTOS,全称是Free Real-Time Operating System。说白了,它是一个轻量级的实时内核,专门给单片机用的。

你可能会问:「我裸机跑得挺好的,干嘛要上系统?」

嗯,这个问题我十年前也问过自己。当时我在做一个智能家居网关的原型,功能越来越多:Wi-Fi通信、传感器采集、LCD显示、按键扫描……裸机while(1)循环里塞了七八个任务,中断嵌套搞得我头皮发麻。后来上了FreeRTOS,世界清净了。

FreeRTOS的核心价值就三点:

  • 任务调度:多个任务看起来「同时」运行,实际上CPU在快速切换
  • 资源管理:队列、信号量、互斥锁,帮你搞定数据同步
  • 低开销:内核最小可以裁剪到4KB ROM、1KB RAM

我个人习惯:选RTOS时先看生态。FreeRTOS的文档、社区、移植例程都是最全的。STM32官方SDK直接集成了FreeRTOS,省心。

1.2 项目需求分析:智能家居网关

我们这个课程要做的项目,是一个智能家居网关。它负责把家里的各种传感器、开关、家电连到云端,同时支持本地控制。

先看看需求清单:

功能模块 具体需求 优先级
传感器采集 温湿度、光照、人体红外
网络通信 Wi-Fi连接、MQTT上报云端
本地显示 OLED屏显示实时数据
本地控制 按键控制继电器、LED
远程控制 手机APP下发指令
异常报警 温度超限、设备离线告警

你想想看,如果裸机实现这些功能,光任务调度就够你喝一壶的。传感器采集要定时,Wi-Fi通信可能阻塞,按键要防抖……每个模块的时序要求都不一样。

用FreeRTOS之后,每个功能独立成一个任务,各管各的。采集任务只管读传感器,通信任务只管发数据,互不干扰。

1.3 硬件平台选型:为什么是STM32?

硬件选型这件事,我踩过不少坑。曾经为了省几块钱选了一款小众MCU,结果开发工具链难用,社区没人,遇到问题连个问的人都没有。

所以这次我选STM32F407VET6,原因很简单:

  • 性能够用:Cortex-M4内核,168MHz主频,512KB Flash,192KB RAM
  • 外设丰富:3个SPI、3个I2C、6个USART、2个CAN
  • 生态成熟:Keil、STM32CubeIDE、HAL库、LL库,随便选
  • 价格合理:批量采购20元左右,性价比很高

避坑指南:我曾经在项目中期发现Flash不够用,被迫换芯片,重新画板子,浪费了两周。所以选型时建议留出30%的余量。

其他硬件清单:

  • ESP8266/ESP32模块(Wi-Fi通信)
  • DHT22温湿度传感器
  • 0.96寸OLED屏(I2C接口)
  • 继电器模块、LED、按键
  • 5V/3.3V电源模块

1.4 开发工具链安装与配置

工欲善其事,必先利其器。这里我推荐两种IDE,你选一个就行。

方案一:Keil MDK-ARM

老牌工具,稳定可靠。我用了快十年了。

  1. 去ARM官网下载Keil MDK v5.38或更新版本
  2. 安装时勾选「Cortex-M4」器件支持包
  3. 安装STM32F4的DFP包(Device Family Pack)
  4. 破解(嗯,你懂的)

方案二:STM32CubeIDE

ST官方出品,免费,功能越来越强。

  1. 去ST官网下载STM32CubeIDE
  2. 安装时选择默认路径
  3. 首次启动会自动下载STM32F4的固件包

我个人建议:新手用STM32CubeIDE,省去破解的麻烦。老手用Keil,编译速度快,调试顺手。

1.5 FreeRTOS源码移植

好,重头戏来了。移植FreeRTOS其实没那么玄乎,说白了就是三步:

  1. 拿源码:从FreeRTOS官网或GitHub下载v10.4.6版本
  2. 挑文件:只复制需要的文件到工程目录
  3. 配接口:配置中断、时钟、堆栈

具体操作:

第一步:复制核心源码

从下载的FreeRTOS文件夹中,找到以下目录:

FreeRTOS/Source/
├── include/          // 头文件,全部复制
├── portable/         // 移植层,挑对应MCU的
│   └── RVDS/ARM_CM4F/  // Keil用的
│   └── GCC/ARM_CM4F/   // CubeIDE用的
├── tasks.c
├── queue.c
├── list.c
├── timers.c
├── event_groups.c
└── croutine.c        // 协程,一般不用

第二步:配置FreeRTOSConfig.h

这个文件是FreeRTOS的灵魂。我直接给你一个模板:

#ifndef FREERTOS_CONFIG_H
#define FREERTOS_CONFIG_H

/* 基础配置 */
#define configUSE_PREEMPTION          1   // 抢占式调度
#define configUSE_IDLE_HOOK           0   // 空闲钩子
#define configUSE_TICK_HOOK           0   // 时钟钩子
#define configCPU_CLOCK_HZ            ( ( unsigned long ) 168000000 )  // 168MHz
#define configTICK_RATE_HZ            ( ( TickType_t ) 1000 )         // 1ms一个tick
#define configMAX_PRIORITIES          ( 5 )                           // 5个优先级
#define configMINIMAL_STACK_SIZE      ( ( unsigned short ) 128 )
#define configTOTAL_HEAP_SIZE         ( ( size_t ) ( 30 * 1024 ) )   // 30KB堆
#define configMAX_TASK_NAME_LEN       ( 16 )

/* 启用功能 */
#define INCLUDE_vTaskDelay            1
#define INCLUDE_xTaskDelayUntil       1
#define INCLUDE_vTaskDelete           1
#define INCLUDE_xSemaphoreTake        1
#define INCLUDE_xQueueSend            1

/* 中断配置 */
#define configPRIO_BITS               4       // STM32使用4位优先级
#define configLIBRARY_LOWEST_INTERRUPT_PRIORITY    0xf
#define configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY 5

#endif /* FREERTOS_CONFIG_H */

注意:configCPU_CLOCK_HZ一定要和你的MCU实际主频一致。我见过有人写168MHz但实际跑72MHz,结果延时全乱了。

第三步:修改中断处理

在stm32f4xx_it.c中,找到PendSV_Handler和SysTick_Handler,改成:

// 在文件开头包含FreeRTOS头文件
#include "FreeRTOS.h"
#include "task.h"

// 修改PendSV中断
void PendSV_Handler(void)
{
    vPortSVCHandler();
}

// 修改SysTick中断
void SysTick_Handler(void)
{
    if(xTaskGetSchedulerState() != taskSCHEDULER_NOT_STARTED)
    {
        xPortSysTickHandler();
    }
}

第四步:验证移植是否成功

写一个最简单的测试代码:

#include "FreeRTOS.h"
#include "task.h"

void vTask1(void *pvParameters)
{
    while(1)
    {
        // 任务代码
        vTaskDelay(pdMS_TO_TICKS(1000));  // 延时1秒
    }
}

int main(void)
{
    // 硬件初始化
    HAL_Init();
    SystemClock_Config();
    
    // 创建任务
    xTaskCreate(vTask1, "Task1", 128, NULL, 1, NULL);
    
    // 启动调度器
    vTaskStartScheduler();
    
    // 正常情况下不会执行到这里
    while(1);
}

编译下载后,如果程序能跑起来,没有死机,说明移植成功了。

避坑指南:我曾经在移植时忘了改中断优先级分组,结果FreeRTOS的临界区保护失效,任务切换时数据被破坏。记得在SystemClock_Config()之后调用NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4)。

1.6 本章小结

这一章我们做了四件事:

  • 理解了FreeRTOS的核心价值
  • 明确了智能家居网关的需求
  • 选定了STM32F407作为硬件平台
  • 完成了开发环境搭建和FreeRTOS移植

下一章,我们会创建第一个任务,让LED闪烁起来。你会发现,原来多任务编程这么简单。

好,今天就到这里。有任何问题,欢迎在课程群里交流。


公众号:蓝海资料掘金营,微信deep3321