第2章:NuttX源码获取与编译环境搭建
好,咱们直接进入正题。这一章,我带你亲手把NuttX的源码拉下来,再把编译环境搭好。说白了,就是让电脑能跑NuttX。别小看这一步,我见过太多人卡在这里,折腾一整天都编译不过。
2.1 从GitHub拉取源码
NuttX的源码托管在GitHub上。我个人习惯用命令行操作,因为后面要频繁切换分支、打补丁。当然,你用图形工具也行,但命令行更直接。
核心仓库地址:
- 主仓库:
https://github.com/apache/nuttx - 应用仓库:
https://github.com/apache/nuttx-apps
为什么要两个仓库?NuttX把内核和应用分开了。内核是nuttx,应用是apps。你想想看,这样设计的好处是——你换板子时,应用代码不用动。
# 克隆内核源码
git clone https://github.com/apache/nuttx.git nuttx
# 克隆应用源码
git clone https://github.com/apache/nuttx-apps.git apps
嗯,这里要注意:两个目录必须放在同一级。比如你建个nuttx_workspace文件夹,里面放nuttx和apps。我曾经见过有人把apps放到nuttx目录里面,结果编译时各种找不到头文件。
我的小技巧:拉取时加上--depth=1参数,只拉取最新版本。这样速度快,省流量。除非你要研究历史提交,否则没必要拉全量。
2.2 安装交叉编译工具链
交叉编译,说白了就是在PC上编译出能在ARM、RISC-V等嵌入式芯片上运行的代码。你不可能在STM32上跑gcc吧?所以需要交叉工具链。
不同的芯片架构,用的工具链不一样。我整理了一张表,你对着选:
| 目标架构 | 工具链名称 | 安装方式(Ubuntu) |
|---|---|---|
| ARM Cortex-M | arm-none-eabi-gcc | sudo apt install gcc-arm-none-eabi |
| ARM Cortex-A | arm-linux-gnueabihf-gcc | sudo apt install gcc-arm-linux-gnueabihf |
| RISC-V 32位 | riscv32-unknown-elf-gcc | 需从xPack或SiFive官网下载 |
| RISC-V 64位 | riscv64-unknown-elf-gcc | 同上 |
我个人最常用的是arm-none-eabi-gcc,因为NuttX在STM32上跑得最多。安装完后,记得验证一下:
arm-none-eabi-gcc --version
如果看到版本号,说明装好了。如果提示"command not found",别慌,检查一下环境变量。我曾经在Ubuntu 20.04上遇到过apt源里没有最新版的情况,后来直接去ARM官网下载了压缩包,手动解压到/opt目录,再配一下PATH。
避坑指南:我曾经因为装了多个版本的gcc,导致编译时链接器报错。建议只保留一个版本,或者用update-alternatives管理默认版本。
2.3 配置Kconfig
NuttX用Kconfig来做配置管理。你想想看,一个RTOS要支持几十种芯片、上百种外设,不可能一个配置打天下。Kconfig就是让你按需裁剪。
配置命令很简单:
cd nuttx
make distclean
./tools/configure.sh <board>:<config>
比如,你要配置STM32F4Discovery开发板:
./tools/configure.sh stm32f4discovery:nsh
这里的nsh是配置名称,代表NuttX Shell。还有其他配置,比如ostest(操作系统测试)、netnsh(带网络功能的Shell)。
配置完后,你可以用make menuconfig来微调:
make menuconfig
这会打开一个图形界面。嗯,说实话,第一次用的时候我有点懵,因为选项太多了。但用多了你会发现,其实常用的就那几个:
- System Type:选择芯片型号、时钟频率
- Device Drivers:开启UART、SPI、I2C等外设
- Networking:网络协议栈配置
- File Systems:文件系统支持
我的习惯:先选一个最接近的默认配置,然后只改我需要的部分。不要从头开始配,那样太容易漏东西。
2.4 使用make编译
配置好了,就该编译了。命令简单到离谱:
make -j$(nproc)
-j$(nproc)的意思是使用所有CPU核心并行编译。我一般用-j4或-j8,看你的电脑配置。第一次编译会比较慢,因为要编译整个内核和所有选中的组件。
编译成功后,你会看到类似这样的输出:
LD: nuttx
CP: nuttx.hex
CP: nuttx.bin
生成的nuttx.bin就是最终的固件,可以直接烧录到开发板上。
如果编译过程中出现错误,别急着改代码。先看看错误信息,大部分问题都是配置不对或者工具链版本不匹配。
2.5 常见编译错误解决
这部分我得多说几句。编译错误是家常便饭,我几乎每次搭建新环境都会遇到。下面是我总结的几个高频问题:
错误1:找不到头文件
fatal error: sys/types.h: No such file or directory
原因:工具链没装全,或者路径没配好。
解决:检查arm-none-eabi-gcc是否在PATH中。如果装了还是报错,试试安装libnewlib-arm-none-eabi包。
错误2:链接器报错
undefined reference to `__aeabi_uidiv'
原因:缺少libgcc库,或者工具链版本太老。
解决:更新工具链到最新版。我遇到过Ubuntu 18.04自带的gcc版本太旧,换成9.3.0就好了。
错误3:配置冲突
error: conflicting types for 'xxx'
原因:Kconfig里同时开启了两个互斥的功能。
解决:运行make olddefconfig,让系统自动解决冲突。或者手动进menuconfig检查。
我的经验:遇到编译错误,先看最后几行输出。真正的错误原因往往在第一个error之前。我曾经花了一小时查一个链接错误,结果发现是忘记运行make distclean了。
好了,环境搭好了,源码也拉下来了。下一章,咱们就正式开始分析NuttX的启动流程。到时候我会带着你一步步看代码,从复位向量到第一个任务创建,把整个过程捋清楚。