NuttX 是一个实时操作系统(RTOS),并且是对接口有着一定要求的系统。原生支持 POSIX 和 ANSI 标准接口,对于这些标准下不可用的接口功能,或不适合嵌入式环境的功能,则采用 Unix 和其他常见 RTOS(如 VxWorks)的标准 API。
Nuttx 支持从8位到32位芯片。采用的开源协议是 Apache2.0 ,也就是完全开源免费的协议。可以使用 menuconfig 进行图形化配置裁剪。
Nuttx 开发环境搭建及启动流程分析 | BalanceTWK的博客
nuttx 开发环境搭建(Ubuntu 20.04)
输入如下命令安装编译 nuttx 所需要的依赖组件:
sudo apt install bison flex gettext texinfo libncurses5-dev libncursesw5-dev gperf automake libtool pkg-config build-essential gperf genromfs libgmp-dev libmpc-dev libmpfr-dev libisl-dev binutils-dev libelf-dev libexpat-dev gcc-multilib g++-multilib picocom u-boot-tools util-linux
nuttx 使用 Kconfig 配置裁剪系统。所以需要安装 kconfig-frontends
软件。
apt install kconfig-frontends
工具链安装
apt install gcc-arm-none-eabi binutils-arm-none-eabi
下载 nuttx 应用与内核 代码
mkdir nuttx_project
cd nuttx_project
git clone https://github.com/apache/nuttx.git nuttx
git clone https://github.com/apache/nuttx-apps apps
查看已经支持的开发板
cd nuttx
./tools/configure.sh -L | less
将 nuttx 代码工程仓库配置成目标开发板,以 sim:nsh 为例:
cd nuttx
./tools/configure.sh -l sim:nsh
编译工程
cd nuttx/
make
编译完成后会在 nuttx 目录下出现 nuttx.bin 文件。
nuttx 模拟开发板配置
将 nuttx 代码工程仓库配置成目标开发板,以 sim:nsh 为例:
cd nuttx
./tools/configure.sh -l sim:nsh
配置裁剪系统
make menuconfig
已关闭 nuttx shell 账户密码登录为例:
配置完成后可退出保存。
编译工程
cd nuttx/
make -j
运行
./nuttx
输入完这个命令后就可以在 PC 端模拟 nuttx 了,如下图是 nut shell 终端组件的窗口。
nuttx 启动流程分析
nuttx 的启动流程一共有五个阶段,并且使用变量 g_nx_initstate
来标识。 g_nx_initstate
的值会在 void nx_start(void)
函数里分阶段改变。
/* Initialization state. OS bring-up occurs in several phases: */
enum nx_initstate_e
{
OSINIT_POWERUP = 0, /* Power-up. No initialization yet performed.
* Depends on .bss initialization logic for this
* value. */
OSINIT_BOOT = 1, /* Basic boot up initialization is complete. OS
* services and hardware resources are not yet
* available. */
OSINIT_TASKLISTS = 2, /* Head of ready-to-run/assigned task lists valid */
OSINIT_MEMORY = 3, /* The memory manager has been initialized */
OSINIT_HARDWARE = 4, /* MCU-specific hardware is initialized. Hardware
* resources such as timers and device drivers are
* now available. Low-level OS services sufficient
* to support the hardware are also available but
* the OS has not yet completed its full
* initialization. */
OSINIT_OSREADY = 5 /* The OS is fully initialized and multi-tasking is
* active. */
};
- MCU 执行到
nx_start
系统起来之后即为完成OSINIT_BOOT
阶段。 - 在
OSINIT_TASKLISTS
阶段会去初始化任务列表。 - 在
OSINIT_MEMORY
阶段需要率先初始化信号量(因为接下来很多系统组件需要使用信号量),然后初始化内存管理。 - 在
OSINIT_HARDWARE
阶段需要先初始化文件系统(因为后面的设备驱动需要使用到文件系统,这涉及到了 nuttx 的驱动管理方式,后面我会再讲。),然后配置 中断向量表、看门狗、时钟、系统 tick 、系统信号、系统消息队列、网络、线程栈内容初始化、注册标准的设备(如:/dev/null
,/dev/zero
,/dev/loop
./dev/random
)。 - 在
OSINIT_OSREADY
阶段需要完成多系统相关的初始化(如启动 IDLE 线程)。
除了上面的分析,网上还有其他博主对启动流程分析图,这个更为详细。如下所示:
/**************************************************************/
(出处:http://blog.csdn.net/zhumaill/article/details/23261543)
/**************************************************************/
__start-- #处理器执行的第一条指令
|
v
stm32_clockconfig()------ #初始化时钟
|
v
rcc_reset() #复位rcc
stm32_stdclockconfig() #初始化标准时钟
rcc_enableperipherals() #使能外设时钟
|
--------------------
|
v
stm32_fpuconfig() #配置fpu,shenzhou/nsh未调用
stm32_lowsetup() #基本初始化串口,之后可以使用up_lowputc()
stm32_gpioinit() #初始化gpio,只是调用stm32_gpioremap()设置重映射
up_earlyserialinit() #初始化串口,之后可以使用up_putc()
stm32_boardinitialize()-- #板级初始化
|
v
stm32_spiinitialize() #初始化spi,只是调用stm32_configgpio()设置gpio
stm32_usbinitialize() #初始化usb,只是调用stm32_configgpio()设置gpio
board_led_initialize() #初始化led,只是调用stm32_configgpio()设置gpio
|
--------------------
|
v
os_start()--------------- #初始化操作系统
|
v
dq_init() #初始化各种状态的任务列表(置为null)
g_pidhash[i]= #初始化唯一可以确定的元素--进程ID
g_pidhash[PIDHASH(0)]= #分配空闲任务的进程ID为0
g_idletcb= #初始化空闲任务的任务控制块
sem_initialize()-- #初始化信号量
|
v
dq_init() #将信号量队列置为null
sem_initholders() #初始化持有者结构以支持优先级继承,shenzhou/nsh未调用
|
--------
|
v
up_allocate_heap() #分配用户模式的堆(设置堆的起点和大小)
kumm_initialize() #初始化用户模式的堆
up_allocate_kheap() #分配内核模式的堆,shenzhou/nsh未调用
kmm_initialize() #初始化内核模式的堆,shenzhou/nsh未调用
task_initialize() #初始化任务数据结构,shenzhou/nsh未调用
irq_initialize() #将所有中断向量都指向同一个异常中断处理程序
wd_initialize() #初始化看门狗数据结构
clock_initialize() #初始化rtc
timer_initialize() #配置POSIX定时器
sig_initialize() #初始化信号
mq_initialize() #初始化命名消息队列
pthread_initialize() #初始化线程特定的数据,空函数
fs_initialize()--- #初始化文件系统
|
v
sem_init() #初始化节点信号量为1
files_initialize() #初始化文件数组,空函数
|
--------
|
v
net_initialize()-- #初始化网络
|
v
uip_initialize() #初始化uIP层
net_initroute() #初始化路由表,shenzhou/nsh未调用
netdev_seminit() #初始化网络设备信号量
arptimer_init() #初始化ARP定时器
|
--------
|
v
up_initialize()--- #处理器特定的初始化
|
v
up_calibratedelay()#校准定时器
up_addregion() #增加额外的内存段
up_irqinitialize() #设置中断优先级,关联硬件异常处理函数
up_pminitialize() #初始化电源管理,shenzhou/nsh未调用
up_dmainitialize() #初始化DMA,shenzhou/nsh未调用
up_timerinit() #初始化定时器中断
devnull_register() #注册/dev/null
devzero_register() #注册/dev/zero,shenzhou/nsh未调用
up_serialinit() #注册串口控制台/dev/console和串口/dev/ttyS0
up_rnginitialize() #初始化并注册随机数生成器,shenzhou/nsh未调用
up_netinitialize() #初始化网络,是arch/arm/src/chip/stm32_eth.c中的
up_usbinitialize() #初始化usb驱动,shenzhou/nsh未调用
board_led_on() #打开中断使能led,但很快会被其它地方的led操作改变状态
|
--------
|
v
lib_initialize() #初始化c库,空函数
group_allocate() #分配空闲组
group_setupidlefiles() #在空闲任务上创建stdout、stderr、stdin
group_initialize() #完全初始化空闲组
os_bringup()------ #创建初始任务
|
v
KEKERNEL_THREAD() #启动内核工作者线程
board_initialize() #最后一刻的板级初始化,shenzhou/nsh未调用
TASK_CREATE() #启动默认应用程序
|
--------
|
v
for up_idle() #空闲任务循环
|
--------------------
|
v
for(;;) #不应该到达这里