|
本帖最后由 鸣涧_GC96O 于 2020-11-26 21:26 编辑
移植目录结构在 rtthread-nano 源码中,与移植相关的文件位于下图中有颜色标记的路径下(黄色表示 libcpu 移植相关的文件,绿色部分表示板级移植相关的文件):
libcpu 移植RT-Thread 的 libcpu 抽象层向下提供了一套统一的 CPU 架构移植接口,这部分接口包含了全局中断开关函数、线程上下文切换函数、时钟节拍的配置和中断函数、Cache 等等内容,RT-Thread 支持的 cpu 架构在源码的 libcpu 文件夹下。
启动文件 startup.s启动文件由芯片厂商提供,位于芯片固件库中。每款芯片都有相对应的启动文件,在不同开发环境下启动文件也不相同。当系统加入 RT-Thread 之后,会将 RT-Thread 的启动放在调用 main() 函数之前,如下图所示:
startup.s:主要完成初始化时钟、配置中断向量表;完成全局 / 静态变量的初始化工作;初始化堆栈;库函数的初始化;程序的跳转等内容。
程序跳转:芯片在 KEIL MDK 与 IAR 下的启动文件不用做修改,会自动转到 RT-Thread 系统启动函数 rtthread_startup() 。GCC 下的启动文件需要修改,让其跳转到 RT-Thread 提供的 entry() 函数,其中 entry() 函数调用了 RT-Thread 系统启动函数 rtthread_startup()。
举例: stm32 在 GCC 开发环境下的启动文件,修改 GCC 启动文件,使其跳转到 entry 函数。以下是启动文件的代码片段:
//修改前: bl SystemInit bl main//修改后: bl SystemInit bl entry /* 修改此处,由 main 改为 entry */RT-Thread 在 entry 函数中实现了 GCC 环境下的 RT-Thread 启动:
- <div>int entry(void)</div><div>{
- </div><div> rtthread_startup();</div><div> return 0;</div><div>}</div>
复制代码
最终调用 main() 函数进入用户 main()。
上下文切换 context_xx.s上下文切换表示 CPU 从一个线程切换到另一个线程、或者线程与中断之间的切换等。在上下文切换过程中,CPU 一般会停止处理当前运行的代码,并保存当前程序运行的具体位置以便之后继续运行。
在该文件中除了实现上下文切换的函数外,还需完成全局开关中断函数,详见编程指南 《内核移植》 - CPU 架构移植 章节中的 “实现全局开关中断 ” 小节与 “实现上下文切换” 小节。
需实现的函数 | 描述 | rt_base_t rt_hw_interrupt_disable(void); | 关闭全局中断 | void rt_hw_interrupt_enable(rt_base_t level); | 打开全局中断 | void rt_hw_context_switch_to(rt_uint32 to); | 没有来源线程的上下文切换,在调度器启动第一个线程的时候调用,以及在 signal 里面会调用 | void rt_hw_context_switch(rt_uint32 from, rt_uint32 to); | 从 from 线程切换到 to 线程,用于线程和线程之间的切换 | void rt_hw_context_switch_interrupt(rt_uint32 from, rt_uint32 to); | 从 from 线程切换到 to 线程,用于中断里面进行切换的时候使用 | 注意:在 Cortex-M 中,PendSV 中断处理函数是 PendSV_Handler(),线程切换的实际工作在 PendSV_Handler() 里完成。
线程栈初始化 cpuport.c在 RT-Thread 中,线程具有独立的栈,当进行线程切换时,会将当前线程的上下文存在栈中,当线程要恢复运行时,再从栈中读取上下文信息,进行恢复。
故障异常处理函数 rt_hw_hard_fault_exception(),在发生硬件错误时,执行 HardFault_Handler 中断,会执行该函数。
该文件中主要实现线程栈的初始化 rt_hw_stack_init() 与 hard fault 异常处理函数,线程栈初始化函数的参数以及实现的步骤详见编程指南 《内核移植》 - CPU 架构移植 章节中的 ”实现线程栈初始化“ 小节。
[td]需实现的函数 | 描述 | rt_hw_stack_init() | 实现线程栈的初始化 | rt_hw_hard_fault_exception() | 异常函数:系统硬件错误 | 中断与异常挂接 interrupt.c注意事项 注意:在 Cortex-M 内核上,所有中断都采用中断向量表的方式进行处理,即当一个中断触发时,处理器将直接判定是哪个中断源,然后直接跳转到相应的固定位置进行处理,不需要再自行实现中断管理。
在一些非 Cortex-M 架构中,系统没有实现类似中断向量表的功能,物理中断要和用户的中断服务例程相关联,就需要使用中断管理接口对中断进行管理,这样当发生中断时就可以触发相应的中断,执行中断服务例程。
详见编程指南 《中断管理》 章节。
[td]需实现的中断管理接口 | 描述 | rt_hw_interrupt_init() | 硬件中断初始化 | rt_hw_interrupt_install() | 中断服务程序挂接 | rt_hw_interrupt_mask() | 屏蔽指定的中断源 | rt_hw_interrupt_umask() | 打开被屏蔽的中断源 | 板级移植 board.c注意事项 board.c、rtconfig.h 是与硬件 / 板级相关的文件,在移植时需自行实现。Cortex M 架构可参考 Nano 源码 bsp 文件夹中已有的的 board.c、rtconfig.h 。
板级移植主要是针对 rt_hw_board_init() 函数内容的实现,该函数在板级配置文件 board.c 中,函数中做了许多系统启动必要的工作,其中包含:
- 配置系统时钟。
- 实现 OS 节拍。
- 初始化外设:如 GPIO/UART 等等。
- 初始化系统内存堆,实现动态堆内存管理。
- 板级自动初始化,使用 INIT_BOARD_EXPORT() 自动初始化的函数会在此处被初始化。
- 其他必要的初始化,如 MMU 配置(需要时请自行在 rt_hw_board_init 函数中调用应用函数实现)。
- <div>/* board.c */</div><div>void rt_hw_board_init(void)</div><div>{
- </div><div> /* System Clock Update */
- </div><div> SystemCoreClockUpdate();
- </div><div> /* System Tick Configuration */
- </div><div> _SysTick_Config(SystemCoreClock /</div><div> RT_TICK_PER_SECOND);</div><div>#if defined(RT_USING_USER_MAIN) && defined(RT_USING_HEAP)</div><div> rt_system_heap_init(rt_heap_begin_get(), rt_heap_end_get());</div><div>#endif
- </div><div> /* Call components board initial (use INIT_BOARD_EXPORT()) */</div><div>#ifdef RT_USING_COMPONENTS_INIT</div><div> rt_components_board_init();</div><div>#endif</div><div>}</div>
复制代码
配置系统时钟系统时钟是给各个硬件模块提供工作时钟的基础,一般在 rt_hw_board_init() 函数中完成,可以调用库函数实现配置,也可以自行实现。
如下是 stm32 配置系统时钟调用示例(调用库函数 SystemCoreClockUpdate()):
/* board.c */void rt_hw_board_init(){ SystemCoreClockUpdate(); // 在无库函数使用时,一般使用 rt_hw_clock_init() 配置,函数名不做要求,函数自行实现 ...}实现 OS 节拍OS 节拍也叫时钟节拍或 OS tick。任何操作系统都需要提供一个时钟节拍,以供系统处理所有和时间有关的事件。
时钟节拍的实现:通过硬件 timer 实现周期性中断,在定时器中断中调用 rt_tick_increase() 函数实现全局变量 rt_tick 自加,从而实现时钟节拍。一般地,在 Cortex M 上直接使用内部的滴答定时器 Systick 实现。
示例:如下是 stm32 配置 OS 节拍示例,在初始化时钟节拍后,直接在 SysTick_Handler() 中断服务例程中调用 rt_tick_increase()。
- <div>/* board.c */</div><div>void rt_hw_board_init()</div><div>{
- </div><div> ...
- </div><div> _SysTick_Config(SystemCoreClock / RT_TICK_PER_SECOND);
- </div><div> // 使用 SysTick 实现时钟节拍
- </div><div> ...</div><div>}</div><div>
- </div><div>/* 中断服务例程 */</div><div>void SysTick_Handler(void)</div><div>{
- </div><div> /* enter interrupt */</div><div> rt_interrupt_enter();</div><div> rt_tick_increase();</div><div> /* leave interrupt */
- </div><div> rt_interrupt_leave();</div><div>}</div>
复制代码
对于使用了 RT-Thread 中断管理的 CPU 架构,中断服务例程需要通过 rt_hw_interrupt_install() 进行装载(关于中断及其装载,详见本文档的” 中断管理 “ 小节),如下示例:
- <div>/* board.c */</div><div>void rt_hw_board_init()</div><div>{
- </div><div> ...
- </div><div> rt_hw_timer_init(); // 使用 硬件定时器 实现时钟节拍,一般命名为</div><div> rt_hw_timer_init()
- </div><div> ...</div><div>}</div><div>
- </div><div>int rt_hw_timer_init(void) // 函数自行实现,并需要装载中断服务例程</div><div>{
- </div><div> ...
- </div><div> rt_hw_interrupt_install(IRQ_PBA8_TIMER2_3, rt_hw_timer_isr, RT_NULL, "tick");</div><div> rt_hw_interrupt_umask(IRQ_PBA8_TIMER2_3);</div><div>}</div><div>/* 中断服务例程 */</div><div>static void rt_hw_timer_isr(int vector, void *param)</div><div>{</div><div> rt_interrupt_enter();</div><div> rt_tick_increase();</div><div> rt_interrupt_leave();</div><div>}</div>
复制代码
|
+10
|