本人之前从事嵌入式软件开发(主要使用STM32,C8051,NXP片子)将近5年,最近由于工作变动开始搞LINUX应用层软件开发(多了点算法),为了使自己不把这为数不多的兴趣爱好丢了,开始写点文章,主要从使用RT-thread入手(我之前没用过),渐渐加一些之前项目中的内容,比如各种器件操作、IAP、很久之前由于项目中遇到的实时性要求“不得不”自己写个微OS内核(说白了当初不会移植OS...)等,最终效果实现把之前积攒的"库"完全加到OS中去。
本人完全菜鸟一个,该博客不是为了任何人所写,只是为了自己当初一点微不足道的兴趣...
一. 基于STM32F103ZE芯片的RT-thread 操作系统移植
1. 硬件环境:
(1). STM32F103ZE开发板(使用的是自己设计的项目板子),板载12MHz晶振
(2). JLINK,串口线
2. 软件环境:win8.1 ,keil5,SecureCRt
3. 源文件:rt-thread-master v2.1.0,官网相关文档
4. 实验现象:点亮一个LED,以及串口1输出相关LOG
5. 操作说明:
(1).拿到源文件后,首先要阅读文件结构,之后开始大删(因为我比较喜欢用SOURCE INSINGHT看文件)
(PS: 由于单位网络看着比较紧不让上传文件图片,只能描述...)
(2). 打开BSP文件夹,把除了stm32f10x的文件夹外,其余全部删除
(3). 打开libcpu文件夹,进入arm,保留common,cortex-m3,其余全部删除;
(4). 打开libcpu,除了arm文件夹,其余全删
(5). 保留bsp、components 、include、 src、 libcpu其余全删
6. 移植操作
完成上述删除工作,下面开始移植,方法有2:1.用bsp下现成project 2.自己新建个工程,本文以第一种方式进行阐述;
(1). 打开project.uvprojx,在option for targt 的C/C++配置中deine项添加USE_STDPERIPH_DRIVER,STM32F10X_MD(如果是其他型号此处根据所选芯片填写)
(2). 在DEBUG页,选"硬件调试",JLINK等等(别忘了配置芯片FLASH,以及是JTAG还是SW)
(3). 在led.c 文件下 修改你板子上LED硬件端口配置;
(4). 在usart.c 下根据板子情况修改使用串口配置,简单方法比如使用UART1,115200,8,N,1,z只需要定义RT_USING_UART1便可
(5). 前几步完成了基本操作,为了验证操作是否成功,打开application.c 此文件是专门给用户准备的,在里面定义如下变量与函数:
static rt_uint8_t pstext_stack1[512];
static struct rt_thread pstext_thread1;
static void pstext_thread_entry1(void *parameter)
{
unsigned int cnt=0;
rt_kprintf("::file=%s,func=%s,line=%d\n",__FILE__,__FUNCTION__,__LINE__);
while(1)
{
rt_kprintf("func=%s.cnt=%d\n",__FUNCTION__,cnt++);
rt_thread_delay(20);
}
}
static rt_uint8_t pstext_stack2[512];
static struct rt_thread pstext_thread2;
static void pstext_thread_entry2(void *parameter)
{
unsigned int cnt=0;
rt_kprintf("::file=%s,func=%s,line=%d\n",__FILE__,__FUNCTION__,__LINE__);
while(1)
{
rt_kprintf("func=%s.cnt=%d\n",__FUNCTION__,cnt++);
rt_thread_delay(20);
}
}
这2个函数主要功能是自定义2个测试线程,分别打印cnt变量,注意在RT-thread中,rt_kprintf用法与printf用法及其相似;
进入int rt_application_init(void)函数,在此函数体内完成用户线程注册:
result =rt_thread_init(&pstext_thread1,"test1",pstext_thread_entry1,RT_NULL,(rt_uint8_t*)&pstext_stack1[0],sizeof(pstext_stack1),6,5);
if(result == RT_EOK)
{
rt_thread_startup(&pstext_thread1);
}
result =rt_thread_init(&pstext_thread2,"test2",pstext_thread_entry2,RT_NULL,(rt_uint8_t*)&pstext_stack2[0],sizeof(pstext_stack2),4,5);
if(result == RT_EOK)
{
rt_thread_startup(&pstext_thread2);
}
rt_thread_init函数是静态注册一个线程(RT-thread中有动态注册,相比动态,静态在注册时已经分配好相关数据,实时性要高),pstext_thread2参数是线程控制块参数保存一些相关信息,对应着rt_thread 类型;pstext_thread_entry2是线程执行函数,pstext_stack2线程堆栈大小;6代表线程优先级,RT-thread一共支持255个优先级,最低的为空闲线程使用,意思在没用用户任务时不让OS退出,在rt_config.h文件中RT_THREAD_PRIORITY_MAX可配置系统支持的优先级个数;5代表所得时间片暨获得几个系统滴答时钟,RT_TICK_PER_SECOND宏设置时间片,公式:SysTick_Config( SystemCoreClock / RT_TICK_PER_SECOND)。
RT-thread系统是抢占式调度,在相同优先级情况下,只有时间片用完方可释放CPU;不同优先级,谁高(数值低优先)谁执行。
(6). 编译运行,接上串口线,配置SecureCRt,LED亮,并且串口软件上有相关版本信息输出:
func=pstext_thread_entry2.cnt=1357
func=pstext_thread_entry1.cnt=1357
func=pstext_thread_entry2.cnt=1358
func=pstext_thread_entry1.cnt=1358
func=pstext_thread_entry2.cnt=1359
func=pstext_thread_entry1.cnt=1359
func=pstext_thread_entry2.cnt=1360
func=pstext_thread_entry1.cnt=1360
func=pstext_thread_entry2.cnt=1361
func=pstext_thread_entry1.cnt=1361
func=pstext_thread_entry2.cnt=1362
func=pstext_thread_entry1.cnt=1362
func=pstext_thread_entry2.cnt=1363
func=pstext_thread_entry1.cnt=1363
func=pstext_thread_entry2.cnt=1364
func=pstext_thread_entry1.cnt=1364
func=pstext_thread_entry2.cnt=1365
func=pstext_thread_entry1.cnt=1365
func=pstext_thread_entry2.cnt=1366
func=pstext_thread_entry1.cnt=1366
func=pstext_thread_entry2.cnt=1367
func=pstext_thread_entry1.cnt=1367
可以看出线程2的优先级高于线程1先执行...