本章开始,先新建一个基于野火stm32全系列(包含m3/4/7)开发板的的rt-thread的工程模板,让rt-thread先跑起来。以后所有的rt- thread相关的例程我们都在此模板上修改和添加代码,不用再反反复复地新建。
1. 获取stm32的裸机工程模板 stm32的裸机工程模板我们直接使用野火stm32开发板配套的固件库例程即可。这里我们选取比较简单的例 程—“gpio输出—使用固件库点亮led”作为裸机工程模板。该裸机工程模板均可以在对应板子的a盘/程序源码 /固件库例程的目录下获取到,下面以野火f103-霸道板子的光盘目录为例,具体见图 stm32裸机工程模板在光盘资料中的位置。
2. 下载rt-thread nano 源码 nano是master的精简版,去掉了一些组件和各种开发板的bsp,保留了os的核心功能,但足够我们使用。版本已经更新到了3.0.3版本,与master的版本号一致。
rt-thread master的源码可从rt-thread github仓库地址:https://github.com/rt-thread/rt-thread下载到,nano就是从里面扣出来的。rt- thread官方并没有将抠出来的nano放到他们的官方网站,而是作为一个package放在了keil网站—http://www.keil.com/dd2/pack/中,供用户下载, 具体见图 rt-thread nano package,目前的版本号是3.0.3,如果以后更新到更高的版本则以最新的版本为准。
3. 安装rt-thread package 下载下来之后是一个以exe为后缀的文件,点击安装即可,安装目录与你的keil安装目录一样,安装成功之后, 可以在keil的pack目录下找到刚刚安装的package的所有的文件,具体见图 安装文件。
这样安装成功之后,就可以在keil里面的软件包管理器中将rt-thread nano 直接添加到工程里面, 具体见图 从keil的软件包管理器中选择rt-thread。
4. 往裸机工程添加rt-thread源码 4.1. 拷贝rt-thread package到裸机工程根目录 使用这种方法打包的rt-thread 工程,拷贝到一台没有安装rt-thread package的电脑上面是使用不了的, 会提示找不到rt-thread的源文件。鉴于rt-thread package容量很小,我们直接将安装在keil pack 目 录下的整个rt-thread文件夹拷贝到我们的stm32裸机工程里面,让整个rt-thread package 跟随我们的 工程一起发布,具体见图 拷贝package到裸机工程。
图 拷贝package到裸机工程 中rt-thread文件夹下就是rt-thread nano 的所有东西,该文件夹下的具体内容见下表。
表格 rt-dhread 文件夹内容组成
文件夹
文件夹
描述
rtthread/3.0.3
bsp
板级支持包
components/finsh
rt-thread组件
include
头文件
include/libc
头文件
libcpu/arm/cortex-m0
与处理器相关的接口文件
libcpu/arm/cortex-m3
与处理器相关的接口文件
libcpu/arm/cortex-m4
与处理器相关的接口文件
libcpu/arm/cortex-m7
与处理器相关的接口文件
src
rt-thread内核源码
4.2. 拷贝rtconfig.h文件到user文件夹 将rt-thread/3.0.3/bsp文件夹下面的rtconfig.h配套文件拷贝到工程根目录下面的user文件夹,等下我们需要对这个文件进行修改。
用户可以通过修改这个rt-thread内核的配置头文件来裁剪rt-thread的功能,所以我们把它拷贝一份放在user这个文件夹下面。user,见名之义我们就可以知道里面存放的文件都是用户自己编写的。
4.3. 拷贝board.c文件到user文件夹 将rt-thread/3.0.3/bsp文件夹下面的board.c配套文件拷贝到工程根目录下面的user文件夹,等下我们需要对这个board.c进行修改。
4.4. rt-thread文件夹内容简介 接下来我们对rt-thread文件夹下面的内容做个简单的介绍,好让我们能够更顺心地使用rt-thread。
4.4.1. bsp文件夹简介 bsp文件夹里面存放的是板级支持包,即board support package的英文缩写。rt-thread为了推广自己, 会给各种半导体厂商的评估板写好驱动程序,这些驱动程序就放在bsp这个目录下,我们这里用的是nano版本, 只有几款开发板的驱动,具体见图 bsp文件夹内容,如果是master版本,则存放了非常多的开发板的驱动,具体见 图 master文件夹内容。bsp文件夹下面的board.c这是rt-thread用来初始化开发板硬件的相关函数。rtconfig.h是 rt-thread功能的配置头文件,里面定义了很多宏,通过这些宏定义,我们可以裁剪rt-thread的功能。 用户在使用rt-thread的时候,用户只需要修改board.c和rtconfig.h这两个文件的内容即可,其它文件 我们不需要改动。如果为了减小工程的大小,bsp文件夹下面除了board.c和rtconfig.h这两个文件要保 留外,其它的统统可以删除。
4.4.2. components文件夹简介 在rt-thread看来,除了内核,其它第三方加进来的软件都是组件,比如gui、fatfs、lwip和finsh等。那么这些组件就放在components这个文件夹内,目前nano版本只放了finsh,其它的都被删除了,master版本则放了非常多的组件。finsh是rt- thread组件里面最具特色的,它通过串口打印的方式来输出各种信息,方便我们调试程序。
4.4.3. include文件夹简介 include目录下面存放的是rt-thread内核的头文件,是内核不可分割的一部分。
4.4.4. libcpu文件夹简介 rt-thread是一个软件,单片机是一个硬件,rt- thread要想运行在一个单片机上面,它们就必须关联在一起,那么怎么关联?还是得通过写代码来关联,这部分 关联的文件叫接口文件,通常由汇编和c联合编写。这些接口文件都是跟硬件密切相关的,不同的硬件接口文件是 不一样的,但都大同小异。编写这些接口文件的过程我们就叫移植,移植的过程通常由rt-thread和mcu原厂的人 来负责,移植好的这些接口文件就放在libcpu这个文件夹的目录下。rt-thread nano目 前在libcpu目录下只放了cortex-m0、m3、m4和m7内核的单片机的接口文件,只要是使用了这些内核的mcu都可 以使用里面的接口文件。通常网络上出现的叫“移植某某某rtos到某某某mcu”的教程,其实准确来说,不能够叫 移植,应该叫使用官方的移植,因为这些跟硬件相关的接口文件,rtos官方都已经写好了,我们只是使用而已。 我们本章讲的移植也是使用rt-thread官方的移植,关于这些底层的移植文件我们已经在第一部分“从0到1教你 写rt-thread内核”有非常详细的讲解,这里我们直接使用即可。
4.4.5. src文件夹简介 src目录下面存放的是rt-thread内核的源文件,是内核的核心,我们在第一部分“从0到1教你写rt-thread内核”里面讲解的就是这里面内容。
4.5. 添加rt-thread源码到工程组文件夹 在上一步我们只是将rt-thread的源码放到了本地工程目录下,还没有添加到开发环境里面的组文件夹里面。
4.5.1. 新建rtt/source和rtt/ports组 接下来我们在开发环境里面新建rtt/source和rtt/ports两个组文件夹,其中rtt/source用于存放src文件夹的内容,rtt/ports用于存放libcpu/arm/cortex-m?文件夹的内容,“?”表示3、4或者7,具体选择哪个得看你使用的是野火哪个型号的stm32开发板,具体 见下表。
表格 野火stm32开发板型号对应rt-thread的接口文件
野火stm32开发板型号
具体芯片型号
rt-thread不同内核的接口文件
mini
stm32f103rct6
libcpu/arm/cortex-m3
指南者
stm32f103vet6
libcpu/arm/cortex-m3
霸道
stm32f103zet6
libcpu/arm/cortex-m3
霸天虎
stm32f407zgt6
libcpu/arm/cortex-m4
f429-挑战者
stm32f429igt6
libcpu/arm/cortex-m4
f767-挑战者
stm32f767igt6
libcpu/arm/cortex-m7
h743-挑战者
stm32h743iit6
libcpu/arm/cortex-m7
bsp里面的rtconfig.h和board.c添加到user组文件夹下,其中rtconfig.h用于配置rt-thread的功能, board.c用于存放硬件相关的初始化函数。源码添加完毕之后,具体见图 添加rt-thread源码到工程组文件夹。
2.4.5.2. 指定rt-thread头文件的路径 rt-thread的源码已经添加到开发环境的组文件夹下面,编译的时候需要为这些源文件指定头文件的路径,不然编译会报错。rt-thread的源码里面只有rt-thread3.0.3componentsfinsh、rt-thread3.0.3include和rt- thread3.0.3includelibc这三个文件夹下面有头文件,只需要将这三个头文件的路径在开发环境里面指定即可。同时我们还将rt-thread3.0.3bsp里面的rtconfig.h这个头文件拷贝到了工程根目录下的user文件夹下,所以user的路径也要加到开发环境里面。rt- thread头文件的路径添加完成后的效果具体见图 在开发环境中指定rt-thread的头文件的路径。
2.5. 修改rtconfig.h rtconfig.h是直接从rt-thread/3.0.3/bsp文件夹下面拷贝过来的,该头文件对裁剪整个rt-thread所需的功能的宏均做了定义,有些宏定义被使能,有些宏定义被失能,一开始我们只需要配置最简单的功能即可。要想随心所欲的配置rt- thread的功能,我们必须对这些宏定义的功能有所掌握,下面我们先简单的介绍下这些宏定义的含义,然后再对这些宏定义进行修改。
2.5.1. rtconfig.h文件内容讲解 代码清单:移植rtt-1 (1) :头文件rte_components.h是在mdk中添加rt-thead package时由mdk自动生成的, 目前我们没有使用mdk中自带的rt-thread的package,所以这个头文件不存在,如果包含了该头文件,编译的时 候会报错,等下修改rtconfig.h的时候需要注释掉该头文件。
代码清单:移植rtt-1 (2) : use configuration wizard in context menu: 在上下文中使用 配置向导来配置rtconfig.h中的宏定义。接下来代码中夹杂的“”、“”“”、“”和“”这些符号是mdk自带的配置向导控制符号,使用这些符号控制的代码可以生成一个对应的图形界面的配置 向导,rtconfig.h对应的配置向导具体见图 rtconfig.h对应的配置向导。有关配置向导的语法,可在mdk的帮助文档里面找到, 在搜索栏输入configuration wizard 即可搜索到,具体见图 configuration-wizard。具体每一个符号的语法我们这里不 做细讲,有兴趣的可以深究下。 对于我个人,还是倾向于直接修改rtconfig.h中的源码,而不是通过这个配置 向导来修改,就好比一个老烟枪抽烟的时候你要给他加个过滤嘴,那是不可能的,这辈子都是不可能的。
代码清单:移植rtt-1 (3) :rt-thread的基本配置,要想rt-thread准确无误的跑起来,这些基本配置必须得有且正确。
代码清单:移植rtt-1 (3)-1 :rt_thread_priority_max这个宏表示rt-thread支持多少个优先级, 取值范围为8~~~256,默认为32。
代码清单:移植rtt-1 (3)-2:rt_tick_per_second 表示操作系统每秒钟有多少个tick,tick即是操 作系统的时钟周期,默认为1000,即操作系统的时钟周期tick等于1ms。
代码清单:移植rtt-1 (3)-3:rt_align_size这个宏表示cpu处理的数据需要多少个字节对齐,默认为4个字节。
代码清单:移植rtt-1 (3)-4:rt_name_max这个宏表示内核对象名字的最大长度,取值范围为2~~~16,默认为8。
代码清单:移植rtt-1 (3)-5:使用rt-thread组件初始化,默认使能。
代码清单:移植rtt-1 (3)-6:使用用户main函数,默认打开。
代码清单:移植rtt-1 (3)-7:main线程栈大小,取值范围为1~~~4086,单位为字节,默认为512。
代码清单:移植rtt-1 (4):调试配置。包括了内核调试配置,组件调试配置和线程栈溢出检测,目前全部关闭。
代码清单:移植rtt-1 (5):钩子函数配置,目前全部关闭。
代码清单:移植rtt-1 (6):软件定时器配置,目前关闭,不使用软件定时器。
代码清单:移植rtt-1 (7):内部通信配置,包括信号量、互斥量、事件、邮箱和消息队列,根据需要配置。
代码清单:移植rtt-1 (8):内存管理配置。
代码清单:移植rtt-1 (8)-1:rt_using_mempool这个宏用于表示是否使用内存池,目前关闭,不使用内存池。
代码清单:移植rtt-1 (8)-2:rt_using_heap这个宏用于表示是否堆,目前关闭,不使用堆。
代码清单:移植rtt-1 (8)-3:rt_using_small_mem这个宏用于表示是否使用小内存,目前使能。
代码清单:移植rtt-1 (8)-4:rt_using_tiny_size这个宏用于表示是否使用极小内存,目前关闭,不使用。
代码清单:移植rtt-1 (9):控制台配置。控制台即是rt_kprintf()函数调试输出的设备,通常使用串口。
代码清单:移植rtt-1 (10):finsh配置。
代码清单:移植rtt-1 (11):设备配置。
代码清单:移植rtt-1 (12):rtconfig.h配置结束。
2.5.2. rtconfig.h文件修改 rtconfig.h头文件的内容修改的不多,具体是:注释掉头文件rte_components.h、修改了 rt_thread_priority_max、rt_tick_per_second和rt_main_thread_stack_size这三个宏 的大小,具体见 代码清单:移植rtt-2 的高亮部分。
2.6. 修改board.c 2.6.1. board.c文件内容讲解 board.c是直接从rt-thread/3.0.3/bsp文件夹下面拷贝过来的,里面存放的是与硬件相关的初始化函数, 整个 board.c中的内容具体见 代码清单:移植rtt-3。
代码清单:移植rtt-3 (1):rt-thread相关头文件,rthw.h是处理器相关,rtthread与内核相关。
代码清单:移植rtt-3 (2):systick相关的寄存器定义和初始化函数,这个是跟处理器相关的,等下我们直接 使用固件库函数,可以把这部分注释掉,也可以保留,看个人喜好。
代码清单:移植rtt-3 (3):rt-thread堆配置,如果同时定义了rt_using_user_main和 rt_using_heap这两 个宏,表示rt-thread里面创建内核对象时使用动态内存分配方案。堆可以是内部的sram也可以是外部的sram或 sdram,目前的方法是从内部sram里面分配一部分静态内存来作为堆空间,这里配置为4kb。rt_heap_begin_get() 和rt_heap_end_get()这两个函数表示堆的起始地址和结束地址。这两个函数前面的宏rt_weak的原型是关键字 __weak,表示若定义,即其它地方定义了rt_heap_begin_get()和rt_heap_end_get()这两个函数实体, 被__weak修饰的函数就会被覆盖。
rt_using_user_main和rt_using_heap这两个宏在rtconfig.h中定义,rt_using_user_main默认使能,通过使能或者失能rt_using_heap这个宏来选择使用静态或者动态内存。无论是使用静态还是动态内存方案,使用的都是内部的sram,区别是使用的内存是 在程序编译的时候分配还是在运行的时候分配。
2.6.1.1. rt_hw_board_init()函数 代码清单:移植rtt-3 (4):rt-thread启动的时候会调用一个名为rt_hw_board_init()的函数,从函数名称 我们可以知道它是用来初始化开发板硬件的,比如时钟,比如串口等,具体初始化什么由用户选择。当这些硬件 初始化好之后,rt-thread才继续往下启动。至于rt-thread是哪个文件里面的哪个函数会调 用rt_hw_board_init(),我们在本章先不细讲,留到接下来的“rt-thread的启动流程”章节再深究,这里我们 只需要知道我们用户要自己编写一个rt_hw_board_init()的函数供rt-thread启动的时候调用即可。
代码清单:移植rtt-3 (4)-1:更新系统时钟,如果硬件已经能够跑起来都表示系统时钟是没有问题的,该函数一般由固件库提供。
代码清单:移植rtt-3 (4)-2:初始化系统定时器systick,systick给操作系统提供时基,1个时基我们称之 为一个tick,tick是操作系统最小的时间单位。rt_tick_per_second是一个在rtconfig.h中定义的宏,用于 配置systick每秒中断多少次,这里配置为1000,即1秒钟内systick会中断1000次,即中断周期为1ms。 这部 分功能等下我们会用固件库函数systick_config()来代替。
代码清单:移植rtt-3 (4)-3:硬件bsp初始化统统放在这里,比如led,串口,lcd等。目前我们暂时没有初始化任何开发板的硬件。
代码清单:移植rtt-3 (4)-4:这部分是rt-thread为开发板组件提供的一个初始化函数,该函数在 components.c里面实现,由rtconfig.h里面的宏rt_using_components_init决定是否调用,默认是开启。
代码清单:移植rtt-3 (4)-5:rt_console_set_device()是rt-thread提供的一个控制台设置函数,它将指定rt_kprintf()函数 的输出内容具体从什么设备打印出来。该函数在kservice.c里面实现,由rtconfig.h里面的rt_using_console和rt_using_device这两个宏决定是否调用,目前我们暂时不用。
代码清单:移植rtt-3 (4)-6:rt_system_heap_init()是rt-thread提供的一个内存初始化函数, 只有在使用rt-thread提供的动态内存分配函数时才需要使用到。该函数在mem.c里面实现,由rtconfig.h里面的rt_using_heap和rt_using_user_main这两个决定是否调用,目前我们暂时不用。
2.6.1.2. systick_handler()函数 代码清单:移植rtt-3 (5):systick中断服务函数是一个非常重要的函数,rt-thread所有跟时间相关的事 情都在里面处理,具体实现见 代码清单:移植rtt-4。
代码清单:移植rtt-4 systick_handler()函数
代码清单:移植rtt-4 (1):进入中断,对中断计数器rt_interrupt_nest加1操作。
代码清单:移植rtt-4(2):rt_tick_increase()用于更新时基,实现时间片,扫描系统定时器。
代码清单:移植rtt-4(3) :退出中断,对中断计数器rt_interrupt_nest减1操作。
2.6.2. board.c文件修改 board.c文件内容修改的并不多,具体见代码清单:移植rtt-5的高亮部分。
代码清单:移植rtt-5 修改(1):在user目录下新建一个board.h头文件,用来包含固件库和bsp相关的 头文件和存放board.c里面的函数声明,具体见 代码清单:移植rtt-6。
代码清单:移植rtt-5 修改(2):systick相关的寄存器和初始化函数统统屏蔽掉,将由固件库文件core_cm3/4/7里面的替代。
代码清单:移植rtt-5 修改(3):systick初始化函数由固件库文件core_cm3/4/7里面的systick_config()函数替代。
如果使用的是hal库(目前野火只在stm32 m7系列中使用hal库),则必须添加系统时钟初始化函数,这个函数在 我们利用stm32cubemx代码生成工具配置工程时会自动给我们生成,我们只需添加到rt_hw_board_init()函数进 行初始化即可。
代码清单:移植rtt-7 (2):初始化系统时钟之后,需要对systick进行初始化,因为系统时钟初始化函数会 在最后将systick的时钟也进行初始化为hal库中默认的时钟,不满足我们系统的要求,所以我们只能使用 hal_systick_config将systick重新初始化,根据我们的rt_tick_per_second宏定义进行配置。保证系统正常运行。
2.7. 添加core_delay.c和core_delay.h文件 只有在使用hal库时才需要添加core_delay.c和core_delay.h文件。野火只在其m7系列的开发板使用了hal,m4和m3使用的是标准库,不需要添加。
在st的cortex-m7内核系列的单片机中,就不再支持标准库而是推出了hal库,目前,野火只在stm32 m7系列中使用hal库。
hal是意思是hardware abstraction layer,即硬件抽象层。用一句话概括就是现在这个库与标准库相比,与底 层硬件的相关性大大地降低,程序可移植性大大提高,电工写程序更easy,可以像计算机的码农那样写代码。对于 小白来说,coding的门槛虽然降低了,但是hal带来的占用内存大,编译慢是很多老手不喜欢的,特别是我,我就 很不喜欢,编译一次7分钟,简直是要了我的老命。鉴于hal的优缺点,我个人观点是比较适合st cortex-m7内核 系列这种大内存,高性能的mcu,虽然cortex-m3/m4也有hal库,但是还是使用标准库比较好。
hal库驱动中,由于某些外设的驱动需要使用超时判断(比如i2c、spi、sdio等),需要精确延时(精度为1ms), 使用的是systick,但是在操作系统里面,我们需要使用systick来提供系统时基,那么就冲突了,怎么办?我们 采取的做法是重写hal库里面延时相关的函数,只有三个:hal_inittick()、hal_gettick()和hal_delay(), 这三个函数在hal库中都是弱定义函数(函数开头带__weak关键字),弱定义的意思是只要用户重写这三个函数, 原来hal库里面的就会无效。
在cortex-m内核里面有一个外设叫dwt(data watchpoint and trace), 该外设有一个32位的寄存器叫cyccnt, 它是一个向上的计数器, 记录的是内核时钟运行的个数,最长能记录的时间为: 10.74s = 2的32次方/400000000 (cycnnt从0开始计数到溢出,最长的延时时间与内核的频率有关,假设内核频率为400m,内核时钟跳一次的时间 大概为1/400m=2.5ns),当cyccnt溢出之后,会清0重新开始向上计数。这种延时方案不仅精确,而且还不占用单 片机的外设资源,非常方便。所以hal库里面刚刚讲到的需要重写的三个函数我们都基于cyccnt的方案来实现,具 体的实现见代码清单:移植rtt-8和代码清单13‑9的高亮部分,其中core_delay.c和core_delay.h这两个文件我们已经 写好,放在user文件夹下即可,具体的使用方法看注释。
代码清单:移植rtt-8 (1):重写hal_inittick()函数。
代码清单:移植rtt-8 (2):重写hal_gettick ()函数。
代码清单:移植rtt-9 (3):重写hal_delay ()函数。
2.8. 修改main.c 我们将原来裸机工程里面main.c的文件内容全部删除,新增如下内容,具体见 代码清单:移植rtt-10。
怎样用L293D控制直流电动机
软方电子:基于RK6570A的Android工业屏
uCOS-II系统移植
三极管放大电路原理详解
基于ACR/Tbit路由器的硬件抽象层的通用性软件结构设计
移植RT-Thread到STM32开发板的详细步骤例程
引起DC/DC电路电感啸叫的因素
扩大芯片厂合作 Imagination抢食可穿戴大饼
DAC使控制器可编程
什么是纳米技术?纳米技术的应用
英人工智能研讨会举行_硬件厂商成为AI研讨会要角
什么是调频(FM)、调幅(AM)、短波(SW)、长波(LW)
物联网有望为企业和员工提供重要的价值主张
iphone5采用nano-SIM卡 普通SIM卡不能剪成nano-SIM卡
GitHub上开源了个集众多数据源于一身的爬虫工具箱——InfoSpider
物联网芯片有哪些种类
交换机的基本配置
【虹科方案】虹科数字化仪——机械测量的最佳方案!(二)
远离蚊子骚扰,灭蚊灯有效果吗
LD7830的典型应用电路图