第2章 开发环境搭建:Keil/IAR环境配置,STM32CubeMX生成基础工程,RTOS内核移植步骤详解

好,咱们直接进入正题。做嵌入式开发,环境搭不好,后面全是坑。我见过太多新手卡在编译报错上,一查发现是工具链没配对。这一章,我带你一步步把Keil和IAR的环境搞定,再用STM32CubeMX生成一个干净的基础工程,最后把RTOS内核移植进去。

2.1 Keil MDK环境配置

Keil MDK,说白了就是ARM开发的老牌工具。我个人习惯用MDK 5.38版本,太新的版本反而容易有兼容性问题。

2.1.1 安装与激活

  • 去ARM官网下载MDK-ARM安装包,注意选对版本
  • 安装路径不要有中文,不要有空格。我见过有人装在“程序文件”目录下,结果编译报错找半天
  • 激活时用管理员权限运行,否则注册信息写不进去
注意: 安装完成后,记得装对应芯片的器件包。STM32F4系列就装Keil.STM32F4xx_DFP.2.17.1,版本别太老。

2.1.2 工程配置要点

新建工程时,选芯片型号要精确到具体后缀。比如STM32F407VGT6和STM32F407ZGT6,Flash大小不一样,选错了后面调试会出问题。

嗯,这里有个小技巧:在Target标签页里,把ARM Compiler选成V6版本。V5虽然稳定,但V6编译出来的代码更小,跑起来更快。我在做扫地机导航算法时,用V6编译后RAM占用少了12%。

2.2 IAR Embedded Workbench配置

IAR的优化能力比Keil强,但界面没那么友好。我个人建议:如果你做量产项目,用IAR;如果只是学习,Keil就够了。

2.2.1 安装注意事项

  • IAR的License管理比较严格,建议用浮动License
  • 安装路径同样不要有中文
  • 装完后检查一下C-SPY调试器的驱动,有时候需要手动更新
经验之谈: 我在项目中遇到过IAR编译通过但Keil报错的情况。原因是IAR对C99标准的支持更宽松。所以如果你用IAR,写代码时可以稍微“任性”一点,但移植到Keil时就要小心了。

2.3 STM32CubeMX生成基础工程

STM32CubeMX是个好东西,能省掉你80%的初始化代码。但记住,它生成的只是骨架,血肉还得你自己填。

2.3.1 配置步骤

  1. 打开CubeMX,选择芯片型号。我建议直接搜“STM32F407VG”
  2. 配置时钟树:外部晶振8MHz,主频168MHz。别问我为什么是168MHz,这是F4系列的经典频率
  3. 配置GPIO:至少留一个串口(USART1)和一个LED灯(PA5)
  4. 配置NVIC:把PendSV和SysTick的中断优先级设好
核心操作: 在Project Manager标签页里,Toolchain/IDE选MDK-ARM V5。这样生成的工程Keil和IAR都能用。如果你选V6,IAR可能打不开。

2.3.2 生成代码

点击GENERATE CODE,等几秒钟。你会得到一个包含HAL库、启动文件、main.c的工程。这时候别急着写代码,先编译一次,确保0错误0警告。

// 这是CubeMX生成的main.c骨架
int main(void)
{
  HAL_Init();
  SystemClock_Config();
  MX_GPIO_Init();
  MX_USART1_UART_Init();
  // 你的代码从这里开始
  while (1)
  {
  }
}

2.4 RTOS内核移植步骤详解

移植RTOS,说白了就是把内核的调度器、任务管理、内存管理这些模块,跟你的硬件平台对接起来。我用FreeRTOS举例,因为它是开源的,资料也多。

2.4.1 移植前的准备

  • 下载FreeRTOS源码,我推荐V10.4.6版本,稳定且文档齐全
  • 找到portable文件夹,里面有针对不同编译器的移植文件
  • 确认你的芯片是Cortex-M4,带FPU

2.4.2 核心文件复制

你需要把以下文件复制到工程目录:

文件 来源路径 说明
list.c, queue.c, tasks.c FreeRTOS/Source/ 内核核心代码
port.c, portmacro.h FreeRTOS/Source/portable/RVDS/ARM_CM4F/ 移植层,注意选带F的(支持FPU)
heap_4.c FreeRTOS/Source/portable/MemMang/ 内存管理,heap_4支持碎片合并
FreeRTOSConfig.h Demo/CORTEX_M4F_STM32F407ZG-SK/ 配置文件,需要自己修改
避坑指南: 我曾经把port.c选成了ARM_CM3的版本,结果任务切换时FPU寄存器没保存,浮点运算全乱套。所以一定要选ARM_CM4F,带F的就是支持FPU的。

2.4.3 修改FreeRTOSConfig.h

这个配置文件决定了RTOS的行为。我一般这样改:

#define configUSE_PREEMPTION          1   // 抢占式调度
#define configCPU_CLOCK_HZ            ((unsigned long)168000000)  // 168MHz
#define configTICK_RATE_HZ            ((TickType_t)1000)  // 1ms一个tick
#define configMAX_PRIORITIES          5   // 优先级数量,够用就行
#define configMINIMAL_STACK_SIZE      ((unsigned short)128)  // 最小栈大小
#define configTOTAL_HEAP_SIZE         ((size_t)20 * 1024)  // 20KB堆空间

为什么tick设1000Hz?因为扫地机的运动控制需要1ms的精度。如果你做的是温湿度采集,100Hz就够了。

2.4.4 中断处理适配

RTOS接管了SysTick和PendSV中断。你需要确保:

  • SysTick_Handler()里调用xPortSysTickHandler()
  • PendSV_Handler()里调用xPortPendSVHandler()
  • SVC_Handler()里调用vPortSVCHandler()

嗯,这里要注意:如果你用了HAL库的HAL_Delay(),它依赖SysTick。但RTOS也用了SysTick。解决办法是:把HAL_Delay()改成基于RTOS的vTaskDelay()。我在第一个扫地机项目里就踩过这个坑,两个延时函数混用,结果任务调度全乱。

2.4.5 验证移植是否成功

写一个简单的测试任务:

void vTaskLED(void *pvParameters)
{
    for(;;)
    {
        HAL_GPIO_TogglePin(GPIOA, GPIO_PIN_5);
        vTaskDelay(pdMS_TO_TICKS(500));  // 500ms翻转一次
    }
}

int main(void)
{
    HAL_Init();
    SystemClock_Config();
    MX_GPIO_Init();
    
    xTaskCreate(vTaskLED, "LED", 128, NULL, 1, NULL);
    vTaskStartScheduler();  // 启动调度器
    
    while(1);  // 正常情况下不会跑到这里
}

下载到板子上,如果LED以1Hz的频率闪烁,恭喜你,移植成功了。

个人经验: 如果LED不闪,先检查vTaskStartScheduler()有没有执行到。可以在它前面加个串口打印。如果打印了但LED不闪,八成是中断优先级配置有问题。把HAL_NVIC_SetPriority()里的抢占优先级设成最低(15),因为RTOS需要最高优先级。

2.5 本章小结

环境搭建这一步,说白了就是磨刀。刀磨好了,砍柴才快。Keil和IAR各有优劣,我建议你两个都装,但主力用一个。CubeMX生成的工程是很好的起点,别嫌弃它代码啰嗦。RTOS移植看着步骤多,其实核心就三件事:复制文件、改配置、调中断。

下一章,我们会创建第一个任务——扫地机的电机控制任务。到时候你就知道,今天搭的环境有多重要了。