入门物联网操作系统(上)
2019-09-10
一般嵌入式操作系统因为它的特殊性,往往和硬件平台密切相关,具体的嵌入式操作系统往往只能在特定的硬件上运行。对于刚接触 RT-Thread 操作系统的读者来说并不容易马上就获得一个和 RT-Thread 操作系统相配套的硬件模块,但随着计算机技术的发展,我们可以采用软件方式来模拟一个能够运行 RT-Thread 操作系统的硬件模块,这就是 ARM 公司的 MDK-ARM 仿真模拟环境。
MDK-ARM(MDK-ARM Micro
controller Development Kit)軟件是一套完整的集成开发环境(IDE),它出自 ARM 公司,包括了针对 ARM 芯片(ARM7、ARM9、Cortex-M 系列、Cortex-R 系列等)的高效 C/C++ 编译器;针对各类 ARM 设备、评估板的工程向导、工程管理;用于软件模拟运行硬件平台的模拟器;以及与市面上常见的如 ST-Link、JLink 等在线仿真器相连接以配合调试目标板的调试器。MDK-ARM 软件中的软件仿真模拟器,采用完全软件模拟方式解释执行 ARM 的机器指令,并实现外围的一些外设逻辑,从而构成一套完整的虚拟硬件环境,使得用户能够不借助真实的硬件平台就能够在电脑上执行相应的目标程序。
MDK-ARM 集成开发环境因为其完全的 STM32F103 软件仿真环境,也让我们有机会在不使用真实硬件环境的情况下直接在电脑上运行目标代码。这套软件仿真模拟器能够完整地虚拟出 ARM Cortex-M3 的各种运行模式、外设,如中断异常、时钟定时器、串口等,这几乎和真实的硬件环境完全一致。实践也证明,本文使用到的这份 RT-Thread 入门例程,在编译成二进制代码后,不仅能够在模拟器上实现软件模拟运行,也能够不需要修改而在真实硬件平台上正常运行。
下面我们将选择 MDK-ARM 集成开发环境作为目标硬件平台来观察 RT-Thread 操作系统是如何运行的。
一、准备工作
MDK 开发环境:需要安装 MDK-ARM 5.24 (正式版或评估版,5.14 版本及以上版本均可),这个版本也是当前比较新的版本,它能够提供相对比较完善的调试功能。安装方法可以参考Keil MDK安装。
Keil MDK安装链接:
https://www.rt-thread.org/docum
ent/site/tutorial/quick-start/keil/keil/
二、初识 RT-Thread
作为一个操作系统,RT-Thread 的代码规模怎么样呢?我们找到RT-Thread压缩包文件,将它解压,我们这里解压到 D盘,解压完成后的目录结构如图1所示:
各个目录所包含的文件类型的描述如图2所示:
在目录下,有一个 project.uvprojx 文件,它是本文内容所引述的例程中的一个 MDK5 工程文件,双击 “project.uvprojx” 图标,打开此工程文件(图3):
在工程主窗口的左侧 “Project” 栏里可以看到该工程的文件列表,这些文件被分别存放到如下几个组内,分别是(图4):
现在我们点击一下窗口上方工具栏中的按钮,对该工程进行编译,如图5所示:
编译的结果显示在窗口下方的 “Build” 栏中,没什么意外的话,最后一行会显示“0 Error(s), * Warning(s).”,即无任何错误和警告。
在编译完 RT-Thread/STM32 后,我们可以通过 MDK-ARM 的模拟器来仿真运行 RT-Thread。点击窗口右上方的按钮或直接按 “Ctrl+F5” 进入仿真界面,再按 F5 开始运行,然后点击该图工具栏中的按钮或者选择菜单栏中的 “View→Serial Windows→UART#1”,打开串口 1 窗口,可以看到串口的输出只显示了 RT-Thread 的 LOGO,这是因为用户代码是空的,其模拟运行的结果如图6、7所示:
三、系统启动代码
一般了解一份代码大多从启动部分开始,同样这里也采用这种方式,先寻找启动的源头。以 MDK-ARM 为例,MDK-ARM 的用户程序入口为 main() 函数,位于 main.c 文件中。系统启动后先从汇编代码 startup_stm32f103xe.s 开始运行,然后跳转到 C 代码,进行 RT-Thread 系统功能初始化,最后进入用户程序入口 main()。
下面我们来看看在 components.c 中定义的这段代码:
1//components.c中定义
2/*re-define main function */
3int $Sub$$main(void)
4{
5rt_hw_interrupt_disable();
6rtthread_startup();
7return 0;
8}
在这里$Sub$$main函数仅仅调用了rtthread_startup()函数。RT-Thread 支持多种平台和多种编译器,而rtthread_startup()函数是 RT-Thread 规定的统一入口,所以$Sub$$main函数只需调用rtthread_startup()函数即可。例如采用 GNU GCC 编译器编译的 RT-Thread,就是直接从汇编启动代码部分跳转到rtthread_startup()函数中,并开始第一个 C 代码的执行的。在 components.c 的代码中找到rtthread_startup()函数,我们将可以看到 RT-Thread 的启动流程:
1int rtthread_startup(void)
2{
3rt_hw_interrupt_disable();
4
5/*board level initalization
6*NOTE:please initialize heap inside board initialization.
7*/
8rt_hw_board_init();
9
10/*show RT-Thread version?*/
11 rt_show_version();
12
13/*timer system initialization*/
14rt_system_timer_init();
15
16/*scheduler system initializati on*/
17rt_system_scheduler_init();
18
19#ifdef RT_USING_SIGNALS
20/*signal system initialization */
21rt_system_signal_init();
22#endif
23
24/* create init_thread */
25rt_application_init();
26
27/* timer thread initialization*/
28rt_system_timer_thread_init();
29
30/* idle thread initialization*/
31rt_thread_idle_init();
32
33/* start scheduler */
34rt_system_scheduler_start();
35
36/* never reach here */
37return 0;
38}
這部分启动代码,大致可以分为四个部分:
初始化与系统相关的硬件;
初始化系统内核对象,例如定时器、调度器;
初始化系统设备,这个主要是为 RT-Thread 的设备框架做的初始化;
初始化各个应用线程,并启动调度器。
四、用户入口代码
上面的启动代码基本上可以说都是和 RT-Thread 系统相关的,那么用户如何加入自己的应用程序的初始化代码呢?RT-Thread 将 main 函数作为了用户代码入口,只需要在 main 函数里添加自己的代码即可。
1int main(void)
2{
3/* user app entry*/
4return 0;
5}
ARM教程链接:
http://infocenter.arm.com/help/index.jsp topic=/com.arm.doc.dui0377g/pge1362065967698.html
五、跑马灯的例子
对于从事电子方面开发的技术工程师来说,跑马灯大概是最简单的例子,就类似于每种编程语言中程序员接触的第一个程序 Hello World 一样,所以这个例子就从跑马灯开始。让它定时地对 LED 进行更新(关或灭)。
我们 在UART#1 中输入 msh 命令:led 然后回车就可以运行起来了,如图8所示:
六、跑马灯例子
1/*
2*程序清单:跑马灯例程
3*
4*跑马灯大概是最简单的例子,就类似于每种编程语言中程序员接触的第一个程序
5*Hello World 一样,所以这个例子就从跑马灯开始。创建一个线程,让它定时地对
6*LED进行更新(关或灭)
7*/
8
9int led(void)
10{
11 rt_uint8_t count;
12
13rt_pin_mode(LED_PIN,PIN_MODE_OUTPUT);
14
15for(count=0;count<10;count++)
16{
17rt_pin_write(LED_PIN,PIN_HIG
H);
18rt_kprintf(“led on,count:%d”,count);
19rt_thread_mdelay(500);
20
21rt_pin_write(LED_PIN,PIN_LO
W);
22rt_kprintf(“led off
”);
23rt_thread_mdelay(500);
24}
25return 0;
26}
27MSH_CMD_EXPORT(led,RT-Thread first led sample);
七、常见问题
出现如下编译错误
1rt-threadsrckservice.c(823):error:#929:incorrect use of vaarg fieldwidth=aarg(args,int);
2rt-threadsrckservice.c(842):error:#929:incorrect use of vaarg precision=aarg(args,int);
3………
原因:这类问题基本上都是因为安装了 ADS 导致,ADS 与 keil共存,va_start 所在的头文件指向了 ADS 的文件夹。
八、解决办法
1.删除 ADS 环境变量;
2.卸载 ADS 和Keil,重启电脑,重装Keil。