1. 项目概述与核心价值
最近在整理一个老旧的嵌入式项目,目标板是一块基于ARM Cortex-M3的工控板,资源相当有限。客户要求必须使用RT-Thread这个国产的实时操作系统,而开发环境则被限定在了一台老旧的工控机上,系统是Ubuntu 16.04 LTS 32位英文版。这个组合听起来就有点“复古”,对吧?但恰恰是这种看似边缘的场景,最能考验一个开发环境搭建流程的健壮性和通用性。很多教程都基于最新的Ubuntu 22.04或者Windows下的图形化工具,对于这种特定版本、特定架构的老系统,往往一笔带过或者直接建议“升级系统”。然而,在工业现场、遗留项目维护或者特定硬件限制下,我们没得选。因此,今天我就来详细拆解一遍,如何在Ubuntu 16.04 LTS 32bit英文版这个“经典”环境中,从零开始搭建一套完整、可用的RT-Thread开发环境。这个过程不仅适用于RT-Thread,其思路和方法对于在老旧Linux系统上搭建任何嵌入式开发工具链,都有很高的参考价值。
2. 环境准备与系统基础配置
在开始安装任何开发工具之前,我们必须先让这个“年迈”的Ubuntu系统本身处于一个健康且可用的状态。Ubuntu 16.04 Xenial Xerus发布于2016年,其官方的软件源早已停止维护,直接使用默认源会导致apt-get update失败,这是我们需要解决的第一个拦路虎。
2.1 解决老旧系统软件源失效问题
Ubuntu官方为EOL(生命周期结束)的系统提供了旧版本归档源,我们需要将系统的软件源指向old-releases.ubuntu.com。这是整个搭建过程的基石,这一步错了,后面全都无法进行。
首先,备份原有的源列表文件是一个好习惯:
sudo cp /etc/apt/sources.list /etc/apt/sources.list.backup接下来,编辑源列表文件。这里有一个关键点:Ubuntu 16.04 32位系统。我们必须确保源中的架构是i386,而不是amd64。使用sudo vim /etc/apt/sources.list或sudo nano /etc/apt/sources.list,将文件内容替换为以下内容:
deb http://old-releases.ubuntu.com/ubuntu/ xenial main restricted universe multiverse deb http://old-releases.ubuntu.com/ubuntu/ xenial-updates main restricted universe multiverse deb http://old-releases.ubuntu.com/ubuntu/ xenial-security main restricted universe multiverse替换完成后,执行更新:
sudo apt-get update如果遇到某些GPG密钥错误,可以暂时忽略,或者使用sudo apt-get update --allow-insecure-repositories。更新成功后,建议先升级一下已有的软件包:sudo apt-get upgrade -y。
注意:
old-releases源的速度可能较慢,且不保证所有软件包都完整无缺。如果遇到某个特定包无法下载,可能需要单独寻找替代方案,这是使用老旧系统无法避免的挑战。
2.2 安装基础编译与开发工具
RT-Thread的开发,特别是基于scons的构建系统,需要一系列基础编译工具和Python环境。在Ubuntu 16.04上,Python 2.7是系统默认版本,而scons需要Python 2.7或3.x。我们选择同时安装Python 2.7和3.5(16.04仓库中的版本),并安装必要工具。
执行以下命令安装基础套件:
sudo apt-get install -y build-essential # 包含gcc, g++, make等 sudo apt-get install -y git wget curl # 版本管理和下载工具 sudo apt-get install -y python python3 python3-pip python-pip sudo apt-get install -y libncurses5-dev # menuconfig图形配置界面依赖 sudo apt-get install -y device-tree-compiler # 某些BSP需要设备树编译工具安装完成后,验证关键工具版本:
gcc --version # 应显示gcc 5.x python --version # 应显示Python 2.7.x python3 --version # 应显示Python 3.5.x这些版本虽然不算新,但对于RT-Thread开发已经完全足够。
3. 交叉编译工具链的安装与配置
嵌入式开发的核心是交叉编译工具链。我们的目标板是ARM Cortex-M3,因此需要ARM-none-eabi-gcc工具链。在32位系统上,我们必须寻找预编译的32位版本工具链,或者从源码编译。这里强烈推荐使用GNU Arm Embedded Toolchain的预编译版本,并手动下载配置。
3.1 选择与下载适配32位系统的工具链
访问ARM官方开发者网站或国内镜像站,寻找历史版本。对于32位主机系统,我们需要寻找文件名中包含linux-i686或linux-i386的tar包。例如,一个较旧但稳定的版本是gcc-arm-none-eabi-7-2018-q2-update-linux.tar.bz2。我们可以使用wget下载:
cd ~ wget https://developer.arm.com/-/media/Files/downloads/gnu-rm/7-2018q2/gcc-arm-none-eabi-7-2018-q2-update-linux.tar.bz2如果官方链接失效,可以在搜索引擎中搜索“gcc-arm-none-eabi-7-2018-q2-update-linux-i686”寻找国内高校或社区的镜像资源。
3.2 安装与全局配置工具链
下载完成后,解压并安装到/opt目录下,这是一个存放第三方软件的标准位置:
sudo tar -xjf gcc-arm-none-eabi-7-2018-q2-update-linux.tar.bz2 -C /opt解压后,工具链的路径通常是/opt/gcc-arm-none-eabi-7-2018-q2-update。为了让系统任何地方都能使用这个工具链,我们需要将其bin目录添加到系统的PATH环境变量中。
编辑当前用户的profile文件(例如~/.bashrc):
echo 'export PATH=$PATH:/opt/gcc-arm-none-eabi-7-2018-q2-update/bin' >> ~/.bashrc source ~/.bashrc验证安装是否成功:
arm-none-eabi-gcc --version如果成功,将输出GCC的版本信息,如“gcc version 7.3.1 20180622 (release) [ARM/embedded-7-branch revision 261907]”。这一步的成功,标志着我们具备了编译ARM裸机或RTOS程序的能力。
实操心得:在32位系统上,切勿尝试安装太新的工具链(如10.x以上),其依赖的glibc库版本可能过高,导致无法运行。选择7.x或8.x的版本最为稳妥。如果遇到“No such file or directory”错误,通常是因为该二进制文件是64位的,使用
file命令检查一下,确认下载的是32位版本。
4. RT-Thread源码获取与ENV工具搭建
有了交叉编译工具链,接下来就需要RT-Thread操作系统本身的源码,以及一个强大的辅助配置和构建工具:RT-Thread Env工具(以下简称env)。
4.1 获取RT-Thread源码
RT-Thread的源码托管在GitHub和Gitee上。考虑到国内网络环境,从Gitee克隆速度更快。我们克隆长期支持(LTS)版本,它更稳定。
cd ~ git clone https://gitee.com/rtthread/rt-thread.git cd rt-thread git checkout lts-v3.1.x # 切换到3.1.x LTS分支,这是一个非常稳定的版本源码目录rt-thread包含了操作系统的内核、组件、驱动框架以及众多芯片厂商的BSP(板级支持包)。BSP是我们开发具体板卡的起点。
4.2 安装与配置ENV工具
Env工具是RT-Thread的“命令行中心”,它集成了scons构建系统、menuconfig图形化配置、pkgs包管理器等功能。在Linux下,我们需要下载并运行其安装脚本。
首先,确保安装了git和python(前面已安装)。然后通过git克隆env仓库并安装:
cd ~ git clone https://gitee.com/rtthread/env.git rt-thread-env cd rt-thread-env在rt-thread-env目录下,你会看到针对不同系统的脚本。对于Linux,我们关心的是env.sh。这个脚本主要功能是设置一些环境变量,并将工具目录添加到PATH。我们通常不直接运行它,而是将其配置到shell的启动文件中。
将env工具路径添加到~/.bashrc:
echo 'export RTT_ROOT=~/rt-thread' >> ~/.bashrc echo 'export PATH=$PATH:~/rt-thread-env/tools' >> ~/.bashrc source ~/.bashrc这里,RTT_ROOT环境变量非常重要,它告诉env工具RT-Thread源码的根目录在哪里。
接下来,安装env工具依赖的Python包。env工具的核心是scons和kconfiglib(用于menuconfig)。由于系统Python包可能较旧,我们使用pip安装指定版本:
pip install --user scons==3.1.2 pip3 install --user kconfiglib==14.1.0注意事项:务必使用
--user参数在当前用户目录下安装,避免与系统Python包冲突。在Ubuntu 16.04上,直接使用sudo pip install可能会破坏系统Python环境。如果提示pip命令找不到,请使用python -m pip install --user。
安装完成后,在终端任意位置输入scons --version和menuconfig(如果提示命令未找到,尝试新开一个终端),应该能看到相应版本信息和一个基于ncurses的配置界面。至此,RT-Thread的开发“脚手架”已经搭建完毕。
5. 构建第一个示例工程(以STM32 BSP为例)
理论准备就绪,现在进入实战环节。我们以RT-Thread源码中自带的bsp/stm32/stm32f103-blue-pill这个BSP为例,它对应着市面上极其常见的“蓝色药丸”STM32F103C8T6最小系统板,成本低廉,非常适合学习和验证。
5.1 进入BSP目录与工程配置
cd ~/rt-thread/bsp/stm32/stm32f103-blue-pill ls -la你会看到目录下有一些关键文件:rtconfig.py(工程构建配置)、SConstruct(scons构建脚本)、board目录(板级硬件相关文件)以及libraries(HAL库)。
首先,我们需要通过menuconfig来配置工程。这是RT-Thread强大和灵活性的体现。
menuconfig一个蓝色的文本图形界面会出现。在这个界面中,我们可以:
- 使用上下箭头键移动。
- 使用左右箭头键选择底部的
<Select>,< Exit >,< Help >,< Save >,< Load >。 - 使用空格键勾选或取消选项(
[*]表示已编译进内核,[M]表示编译为模块,[ ]表示不启用)。
对于第一次尝试,我们进行一个最小化配置:
- 进入
RT-Thread Kernel --->,确保内核定时器、线程调度器、信号量、互斥锁等基础功能是开启的(默认就是)。 - 进入
Hardware Drivers Config --->->On-chip Peripheral Drivers --->->Enable UART,确保至少一个串口(如USART1)是开启的,用于后续打印信息。 - 进入
RT-Thread Components --->->Device Drivers,确保Using serial device drivers已开启。 - 按左右箭头键选择
< Save >,回车,使用默认配置文件路径(.config),再次回车保存。 - 选择
< Exit >退出menuconfig。
5.2 使用scons构建工程
配置保存后,生成rtconfig.h头文件(该文件由.config自动生成,包含了所有宏定义)。现在开始编译:
sconsscons会读取SConstruct和rtconfig.py,开始编译整个工程。你会看到大量的编译命令滚动。第一次编译时间会稍长,因为它需要编译RT-Thread内核、设备驱动、C库以及BSP相关的启动文件和HAL库。
编译成功的标志是在最后看到类似下面的输出:
LINK rtthread.elf arm-none-eabi-objcopy -O binary rtthread.elf rtthread.bin arm-none-eabi-size rtthread.elf text data bss dec hex filename XXXXX XXXX XXXX XXXXX XXXXX rtthread.elf scons: done building targets.rtthread.elf是生成的调试文件,rtthread.bin是纯二进制镜像文件,可以直接烧录。arm-none-eabi-size显示的信息告诉我们固件占用了多少Flash(text+data)和RAM(data+bss)。
5.3 生成工程文件与烧录
虽然可以直接使用bin文件,但有时我们希望在IDE中查看和调试代码。Env工具支持生成多种IDE的工程。
例如,生成Makefile项目(适用于任何支持Makefile的编辑器或命令行):
scons --target=makefile生成后,可以直接使用make命令来编译,效果与scons相同。
对于STM32,最常用的烧录方式是使用ST-Link调试器。我们可以使用OpenOCD(开源片上调试器)进行烧录。首先安装OpenOCD:
sudo apt-get install -y openocd然后,编写一个简单的OpenOCD配置文件(例如stlink.cfg),或者使用BSP目录下可能自带的脚本。一个最基础的烧录命令如下(假设ST-Link已连接,且是V2版本):
openocd -f interface/stlink-v2.cfg -f target/stm32f1x.cfg -c "program rtthread.bin 0x08000000 verify reset exit"这条命令的含义是:使用ST-Link V2接口,连接STM32F1系列目标,将rtthread.bin文件烧录到Flash起始地址0x08000000,并进行校验,完成后复位芯片并退出。
常见问题排查:
scons编译报错,提示arm-none-eabi-gcc: not found:说明工具链路径未正确添加到PATH。请检查~/.bashrc中的export语句,并执行source ~/.bashrc或重新打开终端。menuconfig无法打开,提示缺少_curses模块:这是因为Python的curses模块未安装或有问题。尝试安装:sudo apt-get install libncurses5-dev python-dev,然后重新安装kconfiglib。- 编译时提示某个头文件找不到(如
#include <rtthread.h>):这通常是因为RTT_ROOT环境变量设置错误,导致scons找不到源码根目录。请检查echo $RTT_ROOT输出是否正确。- OpenOCD连接失败:检查ST-Link是否被系统识别(
lsusb查看是否有ST-Link设备),检查接线(SWDIO, SWCLK, GND, 3.3V),并尝试使用sudo权限运行OpenOCD。
6. 串口调试与系统功能验证
固件烧录成功后,最后一步就是验证系统是否正常运行。串口是最基本、最重要的调试手段。
6.1 连接串口与查看输出
将STM32板子的串口1(通常是PA9/PA10)通过USB转TTL模块连接到电脑的USB口。在Ubuntu下,该串口会表现为/dev/ttyUSB0或/dev/ttyACM0。
安装一个串口终端工具,如minicom或picocom:
sudo apt-get install -y minicom配置minicom(以ttyUSB0为例,波特率115200):
sudo minicom -s在配置界面中,选择“Serial port setup”,设置:
- Serial Device:
/dev/ttyUSB0 - Bps/Par/Bits: 115200 8N1
- Hardware Flow Control: No
- Software Flow Control: No
然后选择“Save setup as dfl”保存为默认配置,最后选择“Exit”。
给板子上电,如果RT-Thread系统成功启动,你将在minicom中看到RT-Thread的Logo和命令提示符:
\ | / - RT - Thread Operating System / | \ 3.1.3 build May 10 2024 2006 - 2022 Copyright by RT-Thread team msh />msh是RT-Thread的微型Shell,你可以在这里输入命令与系统交互。
6.2 基础功能测试与组件使用
在msh中,尝试一些内置命令,验证系统基本功能:
list_thread:查看当前系统中所有线程的状态、优先级和堆栈使用情况。这是分析系统负载和调试线程问题的首要命令。free:查看系统内存堆的剩余情况。对于资源紧张的MCU,时刻关注内存使用至关重要。ps或ps -l:类似于Linux的ps命令,也是查看线程信息。
如果你在menuconfig中开启了更多的组件,例如文件系统(FATFS)、网络协议栈(lwIP)或设备驱动框架,就可以进行更复杂的测试。例如,挂载一个SD卡,或者连接一个以太网模块。
实操心得:调试信息分级管理:在
menuconfig的Kernel配置中,可以调整日志级别(如RT_DEBUG和RT_DEBUG_LOG)。在开发初期,可以将级别调高(如RT_DEBUG_LOG),以便看到更详细的初始化信息和运行日志。在产品发布前,务必将其关闭或调至最低,以减小代码体积并提升性能。通过ulog组件,可以更灵活地控制不同模块的日志输出,这是RT-Thread一个非常实用的功能。
7. 进阶配置:使用包管理器与软件包生态
RT-Thread的强大之处在于其丰富的软件包生态。通过Env工具中的pkgs命令,可以像Linux的包管理器一样,在线下载、添加和管理数百个中间件、协议栈、驱动和工具软件包。
7.1 更新与使用包管理器
首先,进入你的BSP目录,更新软件包索引:
cd ~/rt-thread/bsp/stm32/stm32f103-blue-pill pkgs --update这个命令会从RT-Thread的包仓库拉取最新的包列表。
然后,我们可以搜索需要的包。例如,想找一个JSON解析库:
pkgs --search json它会列出所有包含“json”关键词的软件包,如cJSON。
7.2 添加一个软件包到工程
假设我们想添加cJSON库到当前工程:
pkgs --add cJSON这个命令会做两件事:1. 在rt-thread根目录的packages文件夹下下载cJSON软件包源码。2. 在当前BSP目录下的packages文件夹中,创建一个指向该软件包的Kconfig文件和一个SConscript构建脚本的链接(或生成相应配置)。
接下来,需要再次运行menuconfig,在配置界面中,你会发现在RT-Thread online packages菜单下,多出了一个miscellaneous packages或者tools packages之类的子菜单,里面就有cJSON的选项。进入后,可以将其启用(按空格键选择[*])。
保存配置退出后,重新执行scons编译工程。scons会自动将cJSON包的源代码加入到编译列表中。编译成功后,你就可以在应用程序中#include <cJSON.h>并使用其API了。
注意事项:软件包管理是RT-Thread开发中提升效率的利器,但需要注意版本兼容性。某些较新的软件包可能依赖更高版本的RT-Thread内核特性,在LTS版本上可能无法直接使用。添加包后如果编译出错,可以查看该软件包目录下的
README.md,确认其依赖的RT-Thread版本。必要时,可以回退到软件包的某个历史提交版本。
8. 开发工作流总结与个性化优化
至此,一个完整的、在Ubuntu 16.04 32位系统上的RT-Thread开发环境已经搭建并验证成功。回顾整个流程,我们可以梳理出一个清晰的工作流:
- 环境初始化:解决软件源问题,安装系统级基础工具(git, python, build-essential)。
- 工具链部署:获取并安装32位主机兼容的ARM GCC工具链,并配置全局环境变量。
- 源码与工具获取:克隆RT-Thread LTS版本源码和Env工具,配置
RTT_ROOT和PATH。 - Python依赖安装:使用
pip --user安装scons和kconfiglib。 - 工程开发循环:
cd到目标BSP目录。menuconfig配置内核和组件。scons编译生成固件。- 使用OpenOCD/J-Link等工具烧录。
- 通过串口终端(minicom)验证和调试。
为了让这个环境用起来更顺手,可以进行一些个性化优化:
- 别名(Alias):在
~/.bashrc中添加一些别名,提高效率。alias mc='menuconfig' alias sc='scons' alias sc-c='scons -c' # 清理编译产物 - 串口终端快速启动:写一个脚本,自动启动minicom并连接到指定串口。
- 版本控制:将你修改过的BSP目录(特别是
board目录下的驱动和链接脚本)纳入git管理,但注意忽略build(编译输出)文件夹和.config文件(可以保存为.config.default作为默认配置)。
这套环境虽然建立在“老旧”的系统之上,但其工具链和方法论是经久不衰的。它剥离了花哨的图形界面,迫使开发者更深入地理解构建过程的每一个环节,从交叉编译的原理到操作系统的配置裁剪,这种理解对于嵌入式开发者的成长至关重要。当你能在这个环境中游刃有余时,切换到更新的系统或更复杂的IDE,将会感到轻而易举。