第一步 准备一个可编译成功的串口输出裸机工程
在裸机工程目录下创建文件夹rt_thread复制源码中这三个文件夹过来,然后再创建三个文件board.c board.h rtconfig.h(建议从已有同款芯片的工程中复制,这样就不用疯狂手敲了) libcpu是已经移植好的cpu部分,找到自己同款芯片的文件,其他的删除 
第二步 将准备好的文件添加到工程中,同时还有头文件路径配置
这是我的文件结构,不需要一样,源文件加进来就行 libcpu文件夹中有三个启动文件对应三种编译工具,添加时选择符合自己IDE的。 
然后编译工程,会出现报错,这是rtconfig.h中部分声明造成的,现在需要暂时屏蔽不需要的组件,只留下内核部分  
这时候编译,通常情况下会出现这样报错  这是因为操作系统内核接管了这两个中断函数 HardFault_Handler 和 PendSV_Handler ,我们需要将这两个函数从原本的裸机工程中移除。在此编译应该就没有报错了,此时操作系统内核已经编译,但是没有调用。
第三步 进入操作系统
将rtconfig中这些宏打开后进行编译,不出意外的话应该会报错提示没有rt_hw_board_init   具体原因可以去查书中关于操作系统启动过程的内容,这里直接在新建的borad.c文件中构建这个函数。 同时把原先在主函数中的硬件初始化过程剪切过来。 再次编译此时操作系统已经添加,不过没有添加启动时间调度,所以不能创建线程,也基本上没啥用。 目前可以先下载到开发板,如果没有错程序则可以跑到用户main函数中

可是通常情况下我们会发现,当前只是没有报错,但是程序不会执行用户main函数,通过调试发现程序会死在这里。  Debug中Call Stack + Locals窗口可以推出程序逻辑在re_application_init()中出问题。   因为rtconfig中配置中宏定义了RT_USING_HEAP,但是程序中还没有打开动态内存管理,所以解决问题的方法有两种:1关闭RT_USING_HEAP,采用静态方法创建主应用;2定义一块大内存,打开动态内存管理。
方法1:静态内存方法 直接在rtconfig文件中注释RT_USING_HEAP。然后在main中闪烁一个灯验证一下。下载完成后灯开始循环闪烁,证明main函数已经开始在跑了。 
方法2:动态内存方法 首先定义一块大内存打开rtconfig中RT_USING_HEAP宏,然后调用rt_system_heap_init()完成动态内存初始化,重新下载验证可行性,同样可以进入main函数。  不过通常情况下,systerm_stack不会用定义的数组,而是利用编译剩下的全部内存,具体原理我也不太清楚,但是我知道通常可以从已有的工程中board.h找到这部分内容。  复制过来以后把动态内存的初始化参数对应修改,同样还要把刚才定义的数组注释掉。搞定后编译下载就ok了。 
第四步 打开时间调度
这里需要使用一个定时中断回调函数周期性的调用rt_tick_increase,通常Cortex M的芯片会使用SysTick_Handler()来作为时间调度器的触发者。
首先在rt_hw_board_init()中配置滴答定时器  然后通过完成SysTick_Handler()实现时钟管理  完成后如何验证是否成功,rt_thread提供的延时函数都是依赖系统tick的,所以如果可以正常使用rt_thread_delay()和rt_thread_mdelay()就可以表示实现时钟管理成功。主函数改写如,下载后我的开发板开始一闪一闪: 
第五步 实现控制台输出
控制台是我认为rt-thread最好用的地方,通过串口可以像PC端的终端一样打印好多信息。
Rt-thread提供rt_kprintf()和rt_hw_console_output()函数,用来输出调试信息。 rt_kprintf()的底层可以基于设备框架的字符设备,也可以通过rt_hw_console_output()函数输出,所以这里直接选择实现rt_hw_console_output()  编译下载,如果移植成功,通过串口调试会出现rt-thread的logo 
第六步 实现动态内存管理完成动态线程创建
其实在上面第三步时已经实现了动态内存管理,可回翻再看下。现在直接创建两个线程验证rtt内核移植成功。这里主线程依旧是小灯闪烁,然后线程1和线程2分别在控制台打印不同字符串。
线程创建代码如下:
void thread1_entry(void *parameter)
{
while(1)
{
rt_kprintf("this is thread1\n");
rt_thread_mdelay(1000);
}
}
void thread2_entry(void *parameter)
{
while(1)
{
rt_kprintf("这是线程2\n");
rt_thread_mdelay(1000);
}
}
int main(void)
{
rt_thread_t tid;
tid = rt_thread_create("thread1", thread1_entry, RT_NULL, 512, RT_MAIN_THREAD_PRIORITY-1, 100);
if(tid != RT_NULL)
{
rt_kprintf("thread1 create sucessful\n");
rt_thread_startup(tid);
}
else
rt_kprintf("thread1 create falled\n");
tid = rt_thread_create("thread2", thread2_entry, RT_NULL, 512, RT_MAIN_THREAD_PRIORITY-1, 100);
if(tid != RT_NULL)
{
rt_kprintf("thread2 create sucessful\n");
rt_thread_startup(tid);
}
else
rt_kprintf("thread2 create falled\n");
while(1)
{
gd_eval_led_on(LED1);
gd_eval_led_on(LED2);
gd_eval_led_on(LED3);
rt_thread_delay(1000);
gd_eval_led_off(LED1);
gd_eval_led_off(LED2);
gd_eval_led_off(LED3);
rt_thread_mdelay(1000);
}
}
编译下载,串口调试结果符合,内核移植完成。 
|