|
看完 RT-Thread 在 STM32 移植这一章节后觉得总归组织的不是特别清晰. 我个人认为这一章节应该是一个给出的一个实例, 应该起到抛砖引玉或者对于任何一款单片机移植方法借鉴的作用. 这一章节我个人觉得应该独立于所有章节.
目前<<编程指南>>此章节表达的并不是特别的完善, 因此根据自己的理解稍微进行了一些增补:
=============================================================
![](http://cache.amobbs.com/bbs_upload782111/files_28/ourdev_542350.png)
(原文件名:移植.png)
由于 Cotex-M3 的内核特性, STM32 的移植部分的代码可在 STM32 文件夹下述文件中找到:fault.c, stack.c , context_rvds.S ,fault_rvds.S , start_rvds.s , serial.C , CPU.C , interrupt.C 。
其中具体表述为:
CPU.C: 芯片相关代码
interrupt.C: 中断相关代码
start.S: 启动代码汇编代码
stack.C: 栈处理C语言代码.
context_rvds.S: 上下文处理汇编代码
fault.C: 故障处理 C 语言代码
fault_rvds.S: 故障处理汇编代码
serial.C: 串口处理(使用 finsh 组件)
除了 CPU.C 和 interrupt.C 函数我们无需修改外, 其余移植部分需要修改的分为四个方面:
启动代码处理. start.S
上下文处理, 保证任务调度器现场保护. stack.C, context_rvds.S
异常状态处理, 获得出错处的栈指针, 提供钩子函数提示故障信息. fault.C fault_rvds.S
系统信息处理, 串口 finsh 组件有关.serial.C
下文将细化这四个方面:
启动代码处理
这里需要设置程序的堆和栈的大小.
具体详见 start_rvds.S 文件.
这里我们使用 MDK 的 Configuration Wizard 模式设置
堆大小: 0x0000 0000
栈大小: 0x0000 0200
上下文处理
stack.C 中我们需要移植一个地方
硬件栈初始化, 这个也是和根据 Cotex-M3 内核固有特性设计出的栈结构进行初始化的代码. 初始状态是模拟一次中断发生后的堆栈情况.
rt_hw_stack_init() 函数是由 rt_thread_init() 函数调用的, 即 RT-Thread 创建任务是调用的.
rt_hw_stack_init() 函数只需要三个输入传递参数, 返回当前任务的栈初始化结束的栈地址.
![](http://cache.amobbs.com/bbs_upload782111/files_28/ourdev_542351.jpg)
(原文件名:堆栈.jpg)
rt_uint8_t *rt_hw_stack_init(void *tentry, void *parameter,
rt_uint8_t *stack_addr, void *texit)
{
unsigned long *stk;
stk = (unsigned long *)stack_addr;
*(stk) = 0x01000000L; /* PSR */
*(--stk) = (unsigned long)tentry; /* entry point, pc */
*(--stk) = (unsigned long)texit; /* lr */
*(--stk) = 0; /* r12 */
*(--stk) = 0; /* r3 */
*(--stk) = 0; /* r2 */
*(--stk) = 0; /* r1 */
*(--stk) = (unsigned long)parameter; /* r0 : argument */
*(--stk) = 0; /* r11 */
*(--stk) = 0; /* r10 */
*(--stk) = 0; /* r9 */
*(--stk) = 0; /* r8 */
*(--stk) = 0; /* r7 */
*(--stk) = 0; /* r6 */
*(--stk) = 0; /* r5 */
*(--stk) = 0; /* r4 */
/* return task's current stack address */
return (rt_uint8_t *)stk;
}
context_rvds.S: 上下文处理汇编代码
与其他实时系统一样,RT-Thread 在进入系统临界代码区之前也需要关闭中断来保护核心数据不被多任务环境下的其他任务或中断破坏。因为 MDK 支持嵌入汇编语句,所以加入关闭/打开中断的语句是很方便的。
RT-Thread 定义了两个函数用来关闭/打开中断:rt_hw_interrupt_disable() 和rt_hw_interrupt_enable()。
我们知道在处理此处的临界状态最简单的办法就是关闭中断, 处理完临界状态后再打开中断但这种方法有一个隐患,如果在关闭中断后调用 RT-Thread 函数,当函数返回后,中断将被打开!
执行 rt_hw_interrupt_disable() 的最有效的方法是先将中断关闭的状态保存到堆栈中,然后关闭中断。与之对应的 rt_hw_interrupt_enable() 的操作是从堆栈中恢复中断状态。采用此方法,不管用户是在中断关闭还是允许的情况下调用 RT-Thread 中的函数,在调用过程中都不会改变中断状态。如果用户在中断关闭的情况下调用 RT-Thread 函数,其实是延长了中断响应时间。虽然 rt_hw_interrupt_disable() 和 rt_hw_interrupt_enable() 可以保护代码的临界段。但如此用法要小心,特别是在调用系统延时一类函数之前关闭了中断。此时任务将处于延时挂起状态,等待时钟中断,但此时时钟中断是禁止的!则系统可能会崩溃。很明显,所有的会让当前任务挂起的函数的调用都会涉及到这个问题,必须十分小心。所以建议用户调用 RT-Thread 的系统函数之前打开中断。
具体参考实例和使用办法可以参考 7.1 章节 "关闭中断".
(接下来内容即原编程指南中的这一章节中的上下文处理部分)
(3) 异常状态处理
fault.C 异常处理中我们需要移植两个地方, 这个也和 Cotex-M3 固有特性有关, 我们只需要获得出错时芯片 R0,R1,R2,R3,R12,LR,PC,PSR 的值.
由于Cortex-M3在进入异常服务例程时,自动压栈了 R0-R3, R12, LR, PSR 和PC,并且在
返回时自动弹出它们,所以我们的工作不需要自己保存这些值, 只需要异常处理中打印出这些反映程序状态的关键寄存器即可.
PSR: 程序状态字
PC: 程序计数器, 即程序指针
LR: 连接寄存器
R12: 通用高组寄存器
R0-R3: 通用低组寄存器
(1) 栈内容
struct stack_contex
{
rt_uint32_t r0;
rt_uint32_t r1;
rt_uint32_t r2;
rt_uint32_t r3;
rt_uint32_t r12;
rt_uint32_t lr;
rt_uint32_t pc;
rt_uint32_t psr;
};
(2) 异常状态下的系统内容提示
void rt_hw_hard_fault_exception(struct stack_contex* contex)
{
rt_kprintf("psr: 0x%08x\n", contex->psr);
rt_kprintf(" pc: 0x%08x\n", contex->pc);
rt_kprintf(" lr: 0x%08x\n", contex->lr);
rt_kprintf("r12: 0x%08x\n", contex->r12);
rt_kprintf("r03: 0x%08x\n", contex->r3);
rt_kprintf("r02: 0x%08x\n", contex->r2);
rt_kprintf("r01: 0x%08x\n", contex->r1);
rt_kprintf("r00: 0x%08x\n", contex->r0);
rt_kprintf("hard fault on thread: %s\n", rt_current_thread->name);
#ifdef RT_USING_FINSH
list_thread();
#endif
while (1);
}
(4) 系统信息处理
串口 |
阿莫论坛20周年了!感谢大家的支持与爱护!!
该献的血还是要献的。你不献他不献。难道让我去献? --- 出自坛友:lovejp1981
|