+ All Categories
Home > Documents > RT-Thread 快速入门文档 - Elecfansfile.elecfans.com/web1/M00/7F/D5/pIYBAFwm-1CAG_Q0... ·...

RT-Thread 快速入门文档 - Elecfansfile.elecfans.com/web1/M00/7F/D5/pIYBAFwm-1CAG_Q0... ·...

Date post: 01-Oct-2020
Category:
Upload: others
View: 7 times
Download: 0 times
Share this document with a friend
33
RT-Thread 快速入门文档 本文档适用于初学者观察RT-Thread操作系统是如何运行的,并了解RT-Thread的最新系统工程是如何搭建的。 配合15天入门RT-Thread内核视频教程,可让读者学习RT-Thread的内核。 RT-Thread 快速入门文档 RT-Thread 快速入门实例教程 准备环境 初识 RT-Thread 系统启动代码 用户入口代码 跑马灯的例子 生产者 - 消费者问题 其他例子 Keil下搭建RT-Thread最小系统工程 准备工作 下载固件库 下载RT-Thread最新版本的源码 拷贝需要的文件 搭建最小系统工程 MDK中新建工程 工程中添加文件 添加包含文件路径 工程中的其他设置 相关资源 RT-Thread 快速入门实例教程 一般嵌入式操作系统因为它的特殊性,往往和硬件平台密切相关连,具体的嵌入式操作系统往往只能在特定的硬件 上运行。对于刚接触 RT-Thread 操作系统的读者并不容易马上就获得一个和 RT-Thread 操作系统相配套的硬件模 块,但随着计算机技术的发展,我们可以采用软件方式来模拟一个能够运行 RT-Thread 操作系统的硬件模块,这就 ARM 公司的 MDK-ARM 仿真模拟环境。 MDK-ARMMDK-ARM Microcontroller Development Kit)软件是一套完整的集成开发环境(IDE),它出自 ARM 公司,包括了针对 ARM 芯片(ARM7ARM9Cortex-M 系列,Cortex-R 系列等)的高效 C/C++ 编译器; 针对各类 ARM 设备、评估板的工程向导,工程管理;用于软件模拟运行硬件平台的模拟器;以及与市面上常见的 ST-LinkJLink 等在线仿真器相连接以配合调试目标板的调试器。MDK-ARM 软件中的软件仿真模拟器,采用完 全软件模拟方式解释执行 ARM 的机器指令,并实现外围的一些外设逻辑,从而构成一套完整的虚拟硬件环境,使 得用户能够不借助真实的硬件平台就能够在电脑上执行相应的目标程序。
Transcript
Page 1: RT-Thread 快速入门文档 - Elecfansfile.elecfans.com/web1/M00/7F/D5/pIYBAFwm-1CAG_Q0... · RT-Thread 将 main 函数作为了用户代码入口,只需要在 main 函数里添加自己的代码即可。

RT-Thread 快速入门文档 本文档适用于初学者观察RT-Thread操作系统是如何运行的,并了解RT-Thread的最新系统工程是如何搭建的。

配合15天入门RT-Thread内核视频教程,可让读者学习RT-Thread的内核。

RT-Thread 快速入门文档RT-Thread 快速入门实例教程

准备环境初识 RT-Thread系统启动代码用户入口代码跑马灯的例子生产者 - 消费者问题其他例子

Keil下搭建RT-Thread最小系统工程准备工作

下载固件库下载RT-Thread最新版本的源码拷贝需要的文件

搭建最小系统工程MDK中新建工程工程中添加文件添加包含文件路径工程中的其他设置

相关资源

 

RT-Thread 快速入门实例教程 一般嵌入式操作系统因为它的特殊性,往往和硬件平台密切相关连,具体的嵌入式操作系统往往只能在特定的硬件上运行。对于刚接触 RT-Thread 操作系统的读者并不容易马上就获得一个和 RT-Thread 操作系统相配套的硬件模块,但随着计算机技术的发展,我们可以采用软件方式来模拟一个能够运行 RT-Thread 操作系统的硬件模块,这就是 ARM 公司的 MDK-ARM 仿真模拟环境。

MDK-ARM(MDK-ARM Microcontroller Development Kit)软件是一套完整的集成开发环境(IDE),它出自ARM 公司,包括了针对 ARM 芯片(ARM7,ARM9,Cortex-M 系列,Cortex-R 系列等)的高效 C/C++ 编译器;针对各类 ARM 设备、评估板的工程向导,工程管理;用于软件模拟运行硬件平台的模拟器;以及与市面上常见的如 ST-Link,JLink 等在线仿真器相连接以配合调试目标板的调试器。MDK-ARM 软件中的软件仿真模拟器,采用完全软件模拟方式解释执行 ARM 的机器指令,并实现外围的一些外设逻辑,从而构成一套完整的虚拟硬件环境,使得用户能够不借助真实的硬件平台就能够在电脑上执行相应的目标程序。

Page 2: RT-Thread 快速入门文档 - Elecfansfile.elecfans.com/web1/M00/7F/D5/pIYBAFwm-1CAG_Q0... · RT-Thread 将 main 函数作为了用户代码入口,只需要在 main 函数里添加自己的代码即可。

MDK-ARM 集成开发环境因为其完全的 STM32F103 软件仿真环境,也让我们有机会在不使用真实硬件环境的情况下直接在电脑上运行目标代码。这套软件仿真模拟器能够完整地虚拟出 ARM Cortex-M3 的各种运行模式、外设,如中断异常,时钟定时器,串口等,这几乎和真实的硬件环境完全一致。实践也证明,本文使用到的这份 RT-Thread 入门例程,在编译成二进制代码后,不仅能够在模拟器上软件模拟运行,也能够不需要修改地在真实硬件平台上正常运行。

下面我们将选择 MDK-ARM 集成开发环境作为目标硬件平台来观察 RT-Thread 操作系统是如何运行的。

准备环境

在运行 RT-Thread 操作系统前,我们需要安装 MDK-ARM 5.24(正式版或评估版,5.14 版本及以上版本均可),这个版本也是当前比较新的版本,它能够提供相对比较完善的调试功能。这里采用了 16k 编译代码限制的评估版5.24 版本,如果要解除 16k 编译代码限制,请购买 MDK-ARM 正式版。 先从 www.keil.com 官方网站下载 MDK-ARM 评估版: http://www.keil.com/download/

在下载时,需要填一些个人基本信息,请填写相应的完整信息,然后开始下载。下载完成后,鼠标双击运行,会出现如图所示的软件安装画面:

这是 MDK-ARM 的安装说明,点击 “Next>>” 进入下一画面,如图所示。

Page 3: RT-Thread 快速入门文档 - Elecfansfile.elecfans.com/web1/M00/7F/D5/pIYBAFwm-1CAG_Q0... · RT-Thread 将 main 函数作为了用户代码入口,只需要在 main 函数里添加自己的代码即可。

在 “I agree to all the terms of the preceding License Agreement” 前的选择框中点击选择 “√”,并点击”Next >>”进入下一步安装,如图所示:

点击 “Browse…” 选择 MDK-ARM 的安装目录或者直接在 “Destination Folder” 下的文本框中输入安装路径,这里我们这里我们默认 “C:/Keil” 即可,然后点击”“Next>>”进入下一步安装,如图所示:

Page 4: RT-Thread 快速入门文档 - Elecfansfile.elecfans.com/web1/M00/7F/D5/pIYBAFwm-1CAG_Q0... · RT-Thread 将 main 函数作为了用户代码入口,只需要在 main 函数里添加自己的代码即可。

在 “First Name” 后输入您的名字,“Last Name”后输入您的姓,“Company Name”后输入您的公司名称,“E-mail”后输入您的邮箱地址,然后点击 “Next>>” 进行安装,等待一段时间后,安装结束,出现如图所示画面:

图中的默认选择不需改动,直接点击 “Next” 进入如图所示画面。

Page 5: RT-Thread 快速入门文档 - Elecfansfile.elecfans.com/web1/M00/7F/D5/pIYBAFwm-1CAG_Q0... · RT-Thread 将 main 函数作为了用户代码入口,只需要在 main 函数里添加自己的代码即可。

在这里可以点击 “Finish” 完成整个 MDK-ARM 软件的安装。

有了 MDK-ARM 利器,就可以轻松开始 RT-Thread 操作系统之旅,一起探索实时操作系统的奥秘。

注:MDK-ARM 正式版是收费的,如果您希望能够编译出更大体积的二进制文件,请购买 MDK-ARM 正式版。RT-Thread 操作系统也支持自由软件基金会的 GNU GCC 编译器,这是一款开源的编译器,想要了解如何使用 GNU 的相关工具请参考 RT-Thread 网站上的相关文档。

初识 RT-Thread

作为一个操作系统,RT-Thread 的代码规模怎么样呢?在弄清楚这些之前,我们先要做的就是获得与本文相对应的RT-Thread for STM32F1 的例子,这份例子可以从以下链接获得:

RT-Thread for STM32F1 Simulator 例程

这个例子是一个压缩包文件,将它解压,我们这里解压到 D:/。解压完成后的目录结构如下图所示:

Page 6: RT-Thread 快速入门文档 - Elecfansfile.elecfans.com/web1/M00/7F/D5/pIYBAFwm-1CAG_Q0... · RT-Thread 将 main 函数作为了用户代码入口,只需要在 main 函数里添加自己的代码即可。

目录名 描述

applications RT-Thread 的用户例程,其中包括各个例程的工程文件。

rt-thread RT-Thread 的源文件。

- components RT-Thread 的各个组件代码,例如 finsh,gui 等。

- include RT-Thread 内核的头文件。

- libcpu 各类芯片的移植代码,此处包含了 STM32 的移植文件。

- src RT-Thread 内核的源文件。

- tools RT-Thread 命令构建工具的脚本文件。

drivers RT-Thread 的驱动,不同平台的底层驱动具体实现。

Libraries ST 的 STM32 固件库文件。

packages RT-Thread 的软件包,里面是一些内核例程。

各个目录所包含的文件类型的描述如下表所示:

在目录下,有一个 project.uvprojx 文件,它是本文内容所引述的例程中的一个 MDK5 工程文件,双击“project.uvprojx” 图标,打开此工程文件:

Page 7: RT-Thread 快速入门文档 - Elecfansfile.elecfans.com/web1/M00/7F/D5/pIYBAFwm-1CAG_Q0... · RT-Thread 将 main 函数作为了用户代码入口,只需要在 main 函数里添加自己的代码即可。

在工程主窗口的左侧 “Project” 栏里可以看到该工程的文件列表,这些文件被分别存放到如下几个组内,分别是:

Page 8: RT-Thread 快速入门文档 - Elecfansfile.elecfans.com/web1/M00/7F/D5/pIYBAFwm-1CAG_Q0... · RT-Thread 将 main 函数作为了用户代码入口,只需要在 main 函数里添加自己的代码即可。

目录组 描述

Applications 对应的目录为 rt-thread-stm32f10x-HAL/applications,它用于存放用户应用代码。

Drivers对应的目录为 rt-thread-stm32f10x-HAL/drivers,它用于存放 RT-Thread 底层的驱动代码。

STM32_HAL对应的目录为 rt-thread-stm32f10x-HAL/Libraries/CMSIS/Device/ST/STM32F1xx,它用于存放 STM32 的固件库文件。

samples 对应的目录为 rt-thread-stm32f10x-HAL/drivers,它用于存放 RT-Thread 的内核例程。

Kernel 对应的目录为 rt-thread-stm32f10x-HAL/src,它用于存放 RT-Thread 内核核心代码。

CORTEX-M3对应的目录为 rt-thread-stm32f10x-HAL/rt-thread/libcpu,它用于存放 ARM Cortex-M3 移植代码。

DeviceDrivers对应的目录为 rt-thread-stm32f10x-HAL/rt-thread/components/drivers,它用于存放 RT-Thread 驱动框架源码。

finsh对应的目录为 rt-thread-stm32f10x-HAL/rt-thread/components/finsh,它用于存放 RT-Thread 命令行 finsh 命令行组件。

libc对应的目录为 rt-thread-stm32f10x-HAL/rt-thread/components/libc,它用于存放 RT-Thread 使用的 C 库函数。

现在我们点击一下窗口上方工具栏中的按钮

,对该工程进行编译,如图所示:

Page 9: RT-Thread 快速入门文档 - Elecfansfile.elecfans.com/web1/M00/7F/D5/pIYBAFwm-1CAG_Q0... · RT-Thread 将 main 函数作为了用户代码入口,只需要在 main 函数里添加自己的代码即可。

编译的结果显示在窗口下方的 “Build” 栏中,没什么意外的话,最后一行会显示“0 Error(s), * Warning(s).”,即无任何错误和警告。

在编译完 RT-Thread/STM32 后,我们可以通过 MDK-ARM 的模拟器来仿真运行 RT-Thread。点击窗口右上方的按钮

或直接按 “Ctrl+F5” 进入仿真界面,再按 F5 开始运行,其模拟运行的结果如图所示:

Page 10: RT-Thread 快速入门文档 - Elecfansfile.elecfans.com/web1/M00/7F/D5/pIYBAFwm-1CAG_Q0... · RT-Thread 将 main 函数作为了用户代码入口,只需要在 main 函数里添加自己的代码即可。

然后点击该图工具栏中的按钮或者选择菜单栏中的 “View→Serial Windows→UART#1”,打开串口 1 窗口,可以看到串口的输出只显示了 RT-Thread 的 LOGO,这是因为用户代码是空的。

我们可以通过输入 help + 回车 输出当前系统所支持的所有命令。

系统启动代码

一般了解一份代码大多从启动部分开始,同样这里也采用这种方式,先寻找启动的源头,因为 MDK-ARM 的用户程序入口为 main() 函数,所以先看看 main() 函数在哪个文件中。这里的 main() 函数位于 Startup 组的 startup.c 文件中,它位于 STM32 的启动汇编代码后,与 C 代码的入口跳转前。启动汇编在 STM32_HAL 组的startup_stm32f103xe.s 中。

下面我们来看看 main() 函数中的这段代码:

//components.c 中定义/* re-define main function */int $Sub$$main(void){ rt_hw_interrupt_disable(); rtthread_startup(); return 0;}

Page 11: RT-Thread 快速入门文档 - Elecfansfile.elecfans.com/web1/M00/7F/D5/pIYBAFwm-1CAG_Q0... · RT-Thread 将 main 函数作为了用户代码入口,只需要在 main 函数里添加自己的代码即可。

在这里 $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 的启动流程:

** 这部分启动代码,大致可以分为四个部分 **:

初始化与系统相关的硬件;初始化系统内核对象,例如定时器,调度器;初始化系统设备,这个主要是为 RT-Thread 的设备框架做的初始化;

int rtthread_startup(void){ rt_hw_interrupt_disable(); /* board level initalization * NOTE: please initialize heap inside board initialization. */ rt_hw_board_init(); /* show RT-Thread version */ rt_show_version(); /* timer system initialization */ rt_system_timer_init(); /* scheduler system initialization */ rt_system_scheduler_init(); #ifdef RT_USING_SIGNALS /* signal system initialization */ rt_system_signal_init();#endif /* create init_thread */ rt_application_init(); /* timer thread initialization */ rt_system_timer_thread_init(); /* idle thread initialization */ rt_thread_idle_init(); /* start scheduler */ rt_system_scheduler_start(); /* never reach here */ return 0;}#endif#endif

Page 12: RT-Thread 快速入门文档 - Elecfansfile.elecfans.com/web1/M00/7F/D5/pIYBAFwm-1CAG_Q0... · RT-Thread 将 main 函数作为了用户代码入口,只需要在 main 函数里添加自己的代码即可。

初始化各个应用线程,并启动调度器。

用户入口代码

上面的启动代码基本上可以说都是和 RT-Thread 系统相关的,那么用户如何加入自己的应用程序的初始化代码呢?RT-Thread 将 main 函数作为了用户代码入口,只需要在 main 函数里添加自己的代码即可。

为了在进入 main 程序之前,完成系统功能初始化,可以使用 和 super 函数标识符在进入主程序之前调用另外一个例程,这样可以让用户不用去管 main() 之前的系统初始化操作。详见 ARM® Compiler v5.06 forµVision® armlink User Guide。

跑马灯的例子

对于从事电子方面开发的技术工程师来说,跑马灯大概是最简单的例子,就类似于每种编程语言中程序员接触的第一个程序 Hello World 一样,所以这个例子就从跑马灯开始。创建一个线程,让它定时地对 LED 进行更新(关或灭),例子对应的工程文件位于 samples 目录下。

我们 UART#1 中输入 msh 命令:led_sample_init 然后回车就可以运行起来了,如图所示:

int main(void){ /* user app entry */ return 0;}

Page 13: RT-Thread 快速入门文档 - Elecfansfile.elecfans.com/web1/M00/7F/D5/pIYBAFwm-1CAG_Q0... · RT-Thread 将 main 函数作为了用户代码入口,只需要在 main 函数里添加自己的代码即可。

** 跑马灯例子 **

/* * 程序清单:跑马灯例程 * * 跑马灯大概是最简单的例子,就类似于每种编程语言中程序员接触的第一个程序 * Hello World 一样,所以这个例子就从跑马灯开始。创建一个线程,让它定时地对 * LED 进行更新(关或灭) */ #include <rtthread.h>#include <rtdevice.h> ALIGN(RT_ALIGN_SIZE)static rt_uint8_t led_stack[512];/* 线程的 TCB 控制块 */static struct rt_thread led_thread; void rt_hw_led_init(void){ rt_pin_mode(LED_0_PIN, PIN_MODE_INPUT); rt_pin_mode(LED_1_PIN, PIN_MODE_INPUT);} void rt_hw_led_on(rt_uint32_t n){ switch (n) { case 0: rt_pin_write(LED_0_PIN, 1); break; case 1: rt_pin_write(LED_0_PIN, 1); break; default: break; }} void rt_hw_led_off(rt_uint32_t n){ switch (n) { case 0: rt_pin_write(LED_0_PIN, 0); break; case 1: rt_pin_write(LED_0_PIN, 0); break; default: break; }}

Page 14: RT-Thread 快速入门文档 - Elecfansfile.elecfans.com/web1/M00/7F/D5/pIYBAFwm-1CAG_Q0... · RT-Thread 将 main 函数作为了用户代码入口,只需要在 main 函数里添加自己的代码即可。

static rt_uint8_t led_inited = 0;void led(rt_uint32_t led, rt_uint32_t value){ /* init led configuration if it's not inited. */ if (!led_inited) { rt_hw_led_init(); led_inited = 1; } if (led == 0) { /* set led status */ switch (value) { case 0: rt_hw_led_off(0); break; case 1: rt_hw_led_on(0); break; default: break; } } if (led == 1) { /* set led status */ switch (value) { case 0: rt_hw_led_off(1); break; case 1: rt_hw_led_on(1); break; default: break; } }} static void led_thread_entry(void *parameter){ unsigned int count = 0; rt_hw_led_init(); while (1) { /* led1 on */

Page 15: RT-Thread 快速入门文档 - Elecfansfile.elecfans.com/web1/M00/7F/D5/pIYBAFwm-1CAG_Q0... · RT-Thread 将 main 函数作为了用户代码入口,只需要在 main 函数里添加自己的代码即可。

这里的 rt_thread_delay(RT_TICK_PER_SECOND/2) 函数的作用是延迟一段时间, 即让 led 线程休眠 50 个 tick(按照 rtconfig.h 中的配置,1 秒 = RT_TICK_PER_SECOND 个 tick = 100 tick,即在这份代码中延迟时间等于500ms)。在休眠的这段时间内,如果没有其他线程运行,操作系统会切换到 idle 线程运行。

生产者 - 消费者问题

生产者 - 消费者问题是操作系统中的一个经典问题,在嵌入式操作系统中也经常能够遇到,例如当串口接收到数据后,就将数据交给一个任务统一的进行处理。这里串口就如同一个生产者产生数据,任务则如同一个消费者消费数据。 在该例子中,我们将用 RT-Thread 的编程模式编写一段代码来解决生产者 - 消费者问题。

我们 UART#1 中输入 msh 命令:semaphore_producer_consumer_init 然后回车就可以运行起来了,如图所示:

rt_kprintf("led on, count : %d\r\n", count); count++; rt_hw_led_on(0); rt_thread_delay(RT_TICK_PER_SECOND / 2); /* sleep 0.5 second and switch to other thread */ /* led1 off */ rt_kprintf("led off\r\n"); rt_hw_led_off(0); rt_thread_delay(RT_TICK_PER_SECOND / 2); }} int led_sample_init(void){ rt_err_t result; /* init led thread */ result = rt_thread_init(&led_thread, "led", led_thread_entry, RT_NULL, (rt_uint8_t *)&led_stack[0], sizeof(led_stack), 20, 5); if (result == RT_EOK) { rt_thread_startup(&led_thread); } return 0;}/* 如果设置了 RT_SAMPLES_AUTORUN,则加入到初始化线程中自动运行 */#if defined (RT_SAMPLES_AUTORUN) && defined(RT_USING_COMPONENTS_INIT) INIT_APP_EXPORT(led_sample_init);#endif/* 导出到 msh 命令列表中 */MSH_CMD_EXPORT(led_sample_init, led sample);

Page 16: RT-Thread 快速入门文档 - Elecfansfile.elecfans.com/web1/M00/7F/D5/pIYBAFwm-1CAG_Q0... · RT-Thread 将 main 函数作为了用户代码入口,只需要在 main 函数里添加自己的代码即可。

/* * 程序清单:生产者消费者例子 * * 这个例子中将创建两个线程用于实现生产者消费者问题 */#include <rtthread.h> #define THREAD_PRIORITY 6#define THREAD_STACK_SIZE 512#define THREAD_TIMESLICE 5 /* 定义最大 5 个元素能够被产生 */#define MAXSEM 5 /* 用于放置生产的整数数组 */rt_uint32_t array[MAXSEM];/* 指向生产者、消费者在 array 数组中的读写位置 */static rt_uint32_t set, get; /* 指向线程控制块的指针 */static rt_thread_t producer_tid = RT_NULL;static rt_thread_t consumer_tid = RT_NULL; struct rt_semaphore sem_lock;struct rt_semaphore sem_empty, sem_full;

Page 17: RT-Thread 快速入门文档 - Elecfansfile.elecfans.com/web1/M00/7F/D5/pIYBAFwm-1CAG_Q0... · RT-Thread 将 main 函数作为了用户代码入口,只需要在 main 函数里添加自己的代码即可。

/* 生成者线程入口 */void producer_thread_entry(void *parameter){ int cnt = 0; /* 运行 10 次 */ while (cnt < 10) { /* 获取一个空位 */ rt_sem_take(&sem_empty, RT_WAITING_FOREVER); /* 修改 array 内容,上锁 */ rt_sem_take(&sem_lock, RT_WAITING_FOREVER); array[set % MAXSEM] = cnt + 1; rt_kprintf("the producer generates a number: %d\n", array[set % MAXSEM]); set++; rt_sem_release(&sem_lock); /* 发布一个满位 */ rt_sem_release(&sem_full); cnt++; /* 暂停一段时间 */ rt_thread_delay(50); } rt_kprintf("the producer exit!\n");} /* 消费者线程入口 */void consumer_thread_entry(void *parameter){ rt_uint32_t no; rt_uint32_t sum; /* 第 n 个线程,由入口参数传进来 */ no = (rt_uint32_t)parameter; sum = 0; while (1) { /* 获取一个满位 */ rt_sem_take(&sem_full, RT_WAITING_FOREVER); /* 临界区,上锁进行操作 */ rt_sem_take(&sem_lock, RT_WAITING_FOREVER); sum += array[get % MAXSEM]; rt_kprintf("the consumer[%d] get a number: %d\n", (get % MAXSEM), array[get % MAXSEM]); get++; rt_sem_release(&sem_lock); /* 释放一个空位 */

Page 18: RT-Thread 快速入门文档 - Elecfansfile.elecfans.com/web1/M00/7F/D5/pIYBAFwm-1CAG_Q0... · RT-Thread 将 main 函数作为了用户代码入口,只需要在 main 函数里添加自己的代码即可。

该例创建了两个线程,一个作为生产者,一个作为消费者。

生产者线程将 cnt 值每次加 1 并循环存入 array 数组的 5 个成员内;消费者线程将生产者中生产的数值打印出来,并累加求和。

其他例子

其他更多的内核例子可以从 packages/samples 目录下找到,需要把对应的 C 文件加入到工程中就能运行起来。

rt_sem_release(&sem_empty); /* 生产者生产到 10 个数目,停止,消费者线程相应停止 */ if (get == 10) break; /* 暂停一小会时间 */ rt_thread_delay(10); } rt_kprintf("the consumer[%d] sum is %d \n", no, sum); rt_kprintf("the consumer[%d] exit!\n");} int semaphore_producer_consumer_init(){ /* 初始化 3 个信号量 */ rt_sem_init(&sem_lock, "lock", 1, RT_IPC_FLAG_FIFO); rt_sem_init(&sem_empty, "empty", MAXSEM, RT_IPC_FLAG_FIFO); rt_sem_init(&sem_full, "full", 0, RT_IPC_FLAG_FIFO); /* 创建线程 1 */ producer_tid = rt_thread_create("producer", producer_thread_entry, RT_NULL, /* 线程入口是 producer_thread_entry, 入口参数是 RT_NULL */ THREAD_STACK_SIZE, THREAD_PRIORITY - 1, THREAD_TIMESLICE); if (producer_tid != RT_NULL) rt_thread_startup(producer_tid); /* 创建线程 2 */ consumer_tid = rt_thread_create("consumer", consumer_thread_entry, RT_NULL, /* 线程入口是 consumer_thread_entry, 入口参数是 RT_NULL */ THREAD_STACK_SIZE, THREAD_PRIORITY + 1, THREAD_TIMESLICE); if (consumer_tid != RT_NULL) rt_thread_startup(consumer_tid); return 0;}/* 如果设置了 RT_SAMPLES_AUTORUN,则加入到初始化线程中自动运行 */#if defined (RT_SAMPLES_AUTORUN) && defined(RT_USING_COMPONENTS_INIT) INIT_APP_EXPORT(semaphore_producer_consumer_init);#endif/* 导出到 msh 命令列表中 */MSH_CMD_EXPORT(semaphore_producer_consumer_init, producer_consumer sample);

Page 19: RT-Thread 快速入门文档 - Elecfansfile.elecfans.com/web1/M00/7F/D5/pIYBAFwm-1CAG_Q0... · RT-Thread 将 main 函数作为了用户代码入口,只需要在 main 函数里添加自己的代码即可。

 

 

Keil下搭建RT-Thread最小系统工程 对于初次接触RT-Thread的朋友来说,要想自己建立一个Keil下的工程,可能会觉得不知所措,本文将一STM32F103为例介绍最小系统的建立过程。

本文所使用的方法是非常传统的 ,只适用于非常简单的工程建立,用于初学者了解RT-Thread最小系统工程的建立,不建议在实际开发中使用该方式。

实际开发中推荐使用env和scons 辅助工具来裁剪功能和组织文件 ,这部分将不在本部分说明,建议读者在熟悉RT-Thread内核后再去学习scons使用。

准备工作

下载固件库

ST的STM32系列芯片,官方网站上为开发者提供了非常方便的开发库。到目前为止,有标准外设库(STD库)、HAL库、LL库 三种。前两者都是常用的库,后面的LL库是ST最近才添加,随HAL源码包一起提供,目前支持的芯片也偏少。其中,STD库和HAL库两者相互独立,互不兼容。LL库和HAL库两者相互独立,只不过LL库更底层。而且,部分HAL库会调用LL库(例如:USB驱动)。同样,LL库也会调用HAL库 。 这里,笔者将使用STM32F1xx系列的HAL固件库建立最小工程。我们从官网下载STM32CubeF1,这个软件包里就包含了我们需要的HAL库。

 

下载RT-Thread最新版本的源码

RT-Thread是完全开源、开放的物联网操作系统,可以从官网下载相关的源代码和辅助开发工具。RT-Thread项目源代码都保存在Git仓库中; 您可以将源文件下载为tar.gz文件,或者使用Git克隆和检出命令:

国外github:https://github.com/RT-Thread/rt-thread;国内gitee:https://gitee.com/rtthread/rt-thread ;

由于国外服务器github速度较慢,建议从gitee上下载,也可以选择从备份的网盘中下载RT-Thread发布的各个版本源代码:

百度网盘:https://pan.baidu.com/s/1mgIAyWo#list/path=%2F

说明:RT-Thread源代码下载包较大,因其包含有将近90个BSP(实际你只需要提取自己的板卡对应的BSP即可),而Git仓库不提供单独下载某个文件,请下载源码时多一些耐心。

 

拷贝需要的文件

我们先建立4个文件夹并命名如下,将之前下载到的固件库和RT-Thread源码提取部分需要的文件,拷贝并按照下面的文件夹结构重新组织文件。

Page 20: RT-Thread 快速入门文档 - Elecfansfile.elecfans.com/web1/M00/7F/D5/pIYBAFwm-1CAG_Q0... · RT-Thread 将 main 函数作为了用户代码入口,只需要在 main 函数里添加自己的代码即可。

文件夹 说明

根目录 存放MDK的工程文件及rtconfig.h文件

applications 存放一些用户的应用代码文件

drivers 用于存放设备驱动的底层驱动实现

Libraries 用于存放我们从芯片官网下载的固件库

rt-thread 用于存放RT-Thread的源码包

在动手之前,先对上面的文件夹结构做个简短的说明:

具体的拷贝内容如下:

根目录:从RT-Thread源码的目录里rt-thread-3.0.3\bsp\stm32f10x-HAL\里复制 rt-config.h 文件。 rt-

config.h :RT-Thread的配置文件,通过宏定义打开/关闭各功能组件。

Page 21: RT-Thread 快速入门文档 - Elecfansfile.elecfans.com/web1/M00/7F/D5/pIYBAFwm-1CAG_Q0... · RT-Thread 将 main 函数作为了用户代码入口,只需要在 main 函数里添加自己的代码即可。

drivers:从下载的RT-Thread源码的目录里rt-thread-3.0.3\bsp\stm32f10x-HAL\drivers里复制文件:

drv_usart.c , drv_usart.h :串口的底层实现,因为我们需要通过串口输入finsh/msh命令和打印系统信息;board.h , board.c :电路板的初始化配置文件;stm32f1xx_it.h , stm32f1xx_it.c :中断配置文件;stm32f1xx_hal_conf.h :官方固件库的配置文件,可以配置需要使用到的固件库。

Page 22: RT-Thread 快速入门文档 - Elecfansfile.elecfans.com/web1/M00/7F/D5/pIYBAFwm-1CAG_Q0... · RT-Thread 将 main 函数作为了用户代码入口,只需要在 main 函数里添加自己的代码即可。

Libraries:把下载的固件库里的 Libraries 文件夹里的内容全部放到我们建立的 Libraries 文件夹下。

rt-thread:把下载的RT-Thread源码目录下的 components , libcpu , include , src 四个文件夹拷贝到这个目录下。

components 里包含了协议驱动的上层实现libcpu包含了对各种厂家内核芯片的支持文件include 和 src 包含了系统的各种内核组件的源码和头文件。

Page 23: RT-Thread 快速入门文档 - Elecfansfile.elecfans.com/web1/M00/7F/D5/pIYBAFwm-1CAG_Q0... · RT-Thread 将 main 函数作为了用户代码入口,只需要在 main 函数里添加自己的代码即可。

applications:从rt-thread-3.0.3\bsp\stm32f10x-HAL\applications文件中复制 main.c 文件到我们的建立的文件夹applications中。

 

搭建最小系统工程

MDK中新建工程

Page 24: RT-Thread 快速入门文档 - Elecfansfile.elecfans.com/web1/M00/7F/D5/pIYBAFwm-1CAG_Q0... · RT-Thread 将 main 函数作为了用户代码入口,只需要在 main 函数里添加自己的代码即可。

1. 从MDK中新建工程项目,并选择保存在我们建立的根目录下:

2. 选择所使用的具体芯片型号后点击“OK”确定选择:

Page 25: RT-Thread 快速入门文档 - Elecfansfile.elecfans.com/web1/M00/7F/D5/pIYBAFwm-1CAG_Q0... · RT-Thread 将 main 函数作为了用户代码入口,只需要在 main 函数里添加自己的代码即可。

1. Manage Run-Time Environment配置页选择cancel。

Page 26: RT-Thread 快速入门文档 - Elecfansfile.elecfans.com/web1/M00/7F/D5/pIYBAFwm-1CAG_Q0... · RT-Thread 将 main 函数作为了用户代码入口,只需要在 main 函数里添加自己的代码即可。

1. 修改工程属性,并建立分组:

 

Page 27: RT-Thread 快速入门文档 - Elecfansfile.elecfans.com/web1/M00/7F/D5/pIYBAFwm-1CAG_Q0... · RT-Thread 将 main 函数作为了用户代码入口,只需要在 main 函数里添加自己的代码即可。

groups 需要添加的文件

Applications rt-thread-3.0.3\bsp\stm32f10x-HAL\applications文件下的main.c文件。

Drivers drivers 文件夹下的 board.c 、 drv_usart.c 、 stm32f1xx_it.c 文件。

STM32_HAL

官方固件库下的: Libraries\STM32F1xx_HAL_Driver 文件夹中的所有.c文件;Libraries\CMSIS\Device\ST\STM32F1xx\Source\Templates 目录下的 system_stm32f1xx.c

文件; Libraries\CMSIS\Device\ST\STM32F1xx\Source\Templates\arm 目录下的startup_stm32f103xe.s 文件。

Kernel rt-thread\src 目录下的所有.C文件

CORTEX-M3rt-thread\libcpu\arm 目录下 common 文件夹中 backtrace.c , div0.c 和 showmem.c 文

件;目录 cortex-m3 下的 context_rvds.S 和 cpuport.c 文件。

DeviceDrivers rt-thread\components\drivers 目录下 src 和 serial 三个文件夹中的所有.C文件

finsh rt-thread\components\finsh 目录下的所有.C文件。

libc rt-thread\components\libc\compilers\armlibc 目录下的所有.C文件。

工程中添加文件

在上一小节中新建的分组里添加文件,详见下表:

 

添加包含文件路径

添加完文件,接下来在项目设置里的 C/C++ 页里的 Include Path 添加这些源文件包含的头文件的路径。

 

工程中的其他设置

Page 28: RT-Thread 快速入门文档 - Elecfansfile.elecfans.com/web1/M00/7F/D5/pIYBAFwm-1CAG_Q0... · RT-Thread 将 main 函数作为了用户代码入口,只需要在 main 函数里添加自己的代码即可。

在下图所示区域设置器件类型和制定使用stm32固件库: USE_HAL_DRIVER, STM32F103xE 。

最后加入编译控制 --keep *.o(.rti_fn.*) --keep *.o(FSymTab) ,防止一些没被调用的函数被连接器移除(使用finsh时很重要)

Page 29: RT-Thread 快速入门文档 - Elecfansfile.elecfans.com/web1/M00/7F/D5/pIYBAFwm-1CAG_Q0... · RT-Thread 将 main 函数作为了用户代码入口,只需要在 main 函数里添加自己的代码即可。

设置好之后编译无错误。

如下图,选择软件仿真后点 OK 确定:

Page 30: RT-Thread 快速入门文档 - Elecfansfile.elecfans.com/web1/M00/7F/D5/pIYBAFwm-1CAG_Q0... · RT-Thread 将 main 函数作为了用户代码入口,只需要在 main 函数里添加自己的代码即可。

仿真运行,命令窗口有错误输出:

Page 31: RT-Thread 快速入门文档 - Elecfansfile.elecfans.com/web1/M00/7F/D5/pIYBAFwm-1CAG_Q0... · RT-Thread 将 main 函数作为了用户代码入口,只需要在 main 函数里添加自己的代码即可。

这个错误是由 rtconfig.h 里设置的芯片和刚开始我们建立工程时所选器件型号不一致,导致的存储器地址访问超范围。将定义改为:

再次编译运行无错误:

#define STM32F103C8

Page 32: RT-Thread 快速入门文档 - Elecfansfile.elecfans.com/web1/M00/7F/D5/pIYBAFwm-1CAG_Q0... · RT-Thread 将 main 函数作为了用户代码入口,只需要在 main 函数里添加自己的代码即可。

 

 

再次重申本文所使用的方法仅用于初学者了解RT-Thread最小系统工程的建立,不建议在实际开发中使用该方式。

实际开发中推荐使用env和scons 辅助工具来裁剪功能和组织文件 ,这部分将不在本部分说明,建议读者在熟悉RT-Thread内核后再去学习scons使用。

 

相关资源

Page 33: RT-Thread 快速入门文档 - Elecfansfile.elecfans.com/web1/M00/7F/D5/pIYBAFwm-1CAG_Q0... · RT-Thread 将 main 函数作为了用户代码入口,只需要在 main 函数里添加自己的代码即可。

15天入门RT-Thread内核视频教程观看链接:https://m.weike.fm/channel/185235RT-Thread源码仓库:https://github.com/RT-Thread/rt-thread RT-Thread编程指南:http://download.rt-thread.org/download/manual/rtthread_manual.zh.pdf


Recommended