UCOS-II中OS_CPU_IRQ_ISR移植过程分析

发布者:明理厚德最新更新时间:2021-07-05 来源: eefocus关键字:UCOS-II  OS_CPU_IRQ_ISR  移植过程 手机看文章 扫描二维码
随时随地手机看文章

简介:在讨论ARM的移植过程中,我觉得首先应该搞清楚每一种情况下CPU的工作模式,同时搞清楚寄存器的特殊性,同时搞清楚中断处理的一般过程。


在uc/os-II的移植过程中存在一个通用的irq中断处理函数,其中的实现过程如下:


OS_CPU_IRQ_ISR

STMFD SP!, {R1-R3} ; We will use R1-R3 as temporary registers

MOV R1, SP

ADD SP, SP, #12 ;Adjust IRQ stack pointer

SUB R2, LR, #4 ;Adjust PC for return address to task

MRS R3, SPSR ; Copy SPSR (Task CPSR)

MSR CPSR_cxsf, #SVCMODE|NOINT ;Change to SVC mode

; SAVE TASK''S CONTEXT ONTO OLD TASK''S STACK

STMFD SP!, {R2} ; Push task''s PC

STMFD SP!, {R4-R12, LR} ; Push task''s LR,R12-R4

LDMFD R1!, {R4-R6} ; Load Task''s R1-R3 from IRQ stack

STMFD SP!, {R4-R6} ; Push Task''s R1-R3 to SVC stack

STMFD SP!, {R0} ; Push Task''s R0 to SVC stack

STMFD SP!, {R3} ; Push task''s CPSR

LDR R0,=OSIntNesting ;OSIntNesting++

LDRB R1,[R0]

ADD R1,R1,#1

STRB R1,[R0]

CMP R1,#1 ;if(OSIntNesting==1){

BNE %F1

LDR R4,=OSTCBCur ;OSTCBHighRdy->OSTCBStkPtr=SP;

LDR R5,[R4]

STR SP,[R5] ;}

1 MSR CPSR_c,#IRQMODE|NOINT ;Change to IRQ mode to use IRQ stack to handle interrupt

LDR R0, =INTOFFSET

LDR R0, [R0]

LDR R1, IRQIsrVect

MOV LR, PC ; Save LR befor jump to the C function we need return back

LDR PC, [R1, R0, LSL #2] ; Call OS_CPU_IRQ_ISR_handler();

MSR CPSR_c,#SVCMODE|NOINT ;Change to SVC mode

BL OSIntExit ;Call OSIntExit

LDMFD SP!,{R4} ;POP the task''s CPSR

MSR SPSR_cxsf,R4

LDMFD SP!,{R0-R12,LR,PC}^ ;POP new Task''s context

IRQIsrVect DCD HandleEINT0


这个函数是irq中断的通用处理形式,我对其中的代码做一下简要的分析和讨论。


首先我需要简要的分析一下中断处理过程中我们应该完成的任务,首先cpu会自动完成一些操作,其中包括中断的关闭已经返回地址的保存,还有模式的切换等。我们程序员则需要完成一些寄存器的保存工作以及跳转到具体的处理函数中,最后完成返回控制。


需要注意的是,本文中的UC/OS-II任务都运行在SVC模式下,而不是SYS模式下。任务栈中保存的寄存器也是这种模式下对应的值。


我按照注释号对代码进行解释:

2、STMFD SP!, {R1-R3}; 主要是完成几个寄存器的压栈操作,为什么这么做呢?因为我们接下来将要使用这写寄存器。为什么需要呢?这是因为当前CPU工作的irq模式下,因此这里的SP并不是SVC模式下的SP指针,但是在UC/OS-II的移植过程中通常将各个任务工作在SVC模式下,同时还需要对寄存器的保存,在中断发生以后,需要保存所有的寄存器以及CPSR的值,而当前的SP并不是任务的栈,此时除了SP、R14的值发生了变化以外,其他的寄存器并没有发生变化。但是由于后面需要使用这些寄存器,因此需要压栈。


3、MOV R1, SP; 将R14_irq的值保存到R1中,这个SP的保存主要是为了通过这个值访问IRQ模式下的堆栈空间,实现对数据的访问。


4、ADD SP, SP, #12; 调整IRQ模式下的堆栈指针SP_irq,将这个指针指向IRQ堆栈的开始位置,方便下一次中断的处理操作。


5、SUB R2, LR, #4 ;这个操作主要是调整返回地址,了解异常返回地址的理解其中的含义,这时候的返回地址实质上就是任务的返回地址,也就是将来需要加载到PC中的值。


6、MRS R3, SPSR;因为发生了IRQ中断,此时CPU进入IRQ模式中,这时的SPSR_irq中保存了svc模式下的CPSR状态。而任务堆栈中保存的刚好是SVC模式下的状态寄存器,因此需要将SVC模式下的状态寄存器首先读出来,然后保存进任务的堆栈中,因此用R3来保存CPSR值。


7、MSR CPSR_cxsf, #SVCMODE|NOINT; 因为任务的堆栈空间位于SVC模式下,因此首先需要将CPU的状态切换到SVC模式下,然后进行任务情景的切换操作。


9、STMFD SP!, {R2}; 这时候的SP是指在SVC模式下的R13_svc。中断发生以后,SVC模式下的SP并没有发生改变,返回以后,该值仍然存在,仍然指向任务的栈顶位置。根据任务栈空间的分布,首先需要保存PC值,然后是R14-R0,CPSR的值,最后是保存SP到任务中,这种分布状态是和堆栈初始化过程的分布一致的。而通过调整的返回地址刚好就保存在了R2中,因此需要压栈保存PC值。


10、STMFD SP!, {R4-R12, LR};因为在之前的一系列操作中,并没有对R4-R12,R14的值进行破坏,因此可以直接进行压栈操作,实现任务堆栈中R4-R12,LR的保存操作。


接下来的几句代码是重点:实现了在对其他模式下堆栈空间的访问问题。


11、LDMFD R1!, {R4-R6};是指将R1地址处加载一些数据到R4-R6,这三个寄存器的值已经被压入栈中,对他们的修改并不会导致错误的产生,因此这三个寄存器实质上是作为中间地址。其中加载的顺序满足高地址对应高的高编号的寄存器值。R1我在前面就强调了用来访问IRQ模式堆栈空间的参考地址。从这个地址向上分别保存了压入栈中的R1-R3寄存器的值,也就是被中断任务R1-R3的值。


12、STMFD SP!, {R4-R6};也就是完成了对任务寄存器R1-R3的压栈操作。


13、STMFD SP!, {R0} ;前面的代码中并没有修改R0的值,因此其中的值仍然是任务的R0值,因此也需要压栈操作。通过上面的几句代码就完成了任务的R0-R15所有寄存器的保存操作。接下的就应该完成CPSR的保存。


14、STMFD SP!, {R3};当前的R3中保存了实际上是前面所说的SPCR_irq中的值,也就是SVC模式下的状态寄存器的值,因此可以认为就是完成了任务的状态寄存器的保存。


16-19、接下来的操作本应该是完成将SP的值保存到任务堆栈空间的,但是在UC/OS-II中存在一个全局变量OSIntNesting,它表明了中断嵌套的次数,因此需要对这个值进行一次加1操作。


21、接下来的操作就是判断是否在中断嵌套中,也就是对全局变量进行比较操作,如果这个值是1,则认为只有一个中断产生,如果不等于1,则认为实在中断嵌套中。


CMP R1,#1 ;if(OSIntNesting==1){


22、BNE %F1;如果是在中断嵌套中,则直接跳转到下面的中断处理函数中


23-25、


LDR R4,=OSTCBCur ;OSTCBHighRdy->OSTCBStkPtr=SP;


LDR R5,[R4]

STR SP,[R5]

说明是从任务到中断的过程,也就是只有一个中断产生,不是在中断嵌套中,这时就需要将SP的值保存到任务控制块中。 以上的操作也就完成了任务情景的保存操作,接下来的操作就应该是真正的中断处理函数啦。


27、1 MSR CPSR_c,#IRQMODE|NOINT;实质上是完成CPU模式的切换操作,进入到IRQ模式下。


接下来的实际处理过程就如前面两篇文章中讨论的中断处理过程。


29-30、


LDR R0, =INTOFFSET


LDR R0, [R0]


得到INTOFFSET的值,实际上就是得到偏移量,实质上就是中断号产生中断,通过这个寄存器可以快速的确定在二级向量表中该中断的向量位置,该向量表中就保存了对应中断处理函数的函数地址。


32、LDR R1, IRQIsrVect


43、IRQIsrVect DCD HandleEINT0


这两句说明了我前面的分析,IRQIsrVect实际上就是一个标号,其中存储了HandleEINT0,HandleEINT0又是我们IRQ中断向量的入口地址(前一篇文章已经说明),也就是说HandleEINT0是二级向量表的开始地址。因此此时R1中实际上就保存了HandleEINT0。


33、MOV LR, PC;就是完成简单的保存过程,这个过程实质上就是保存了函数调用的返回地址。


34、LDR PC, [R1, R0, LSL #2];这句代码的意义是将地址R1+R0*4处的内容加载到PC中,也就是实现函数的跳转,即函数的调用过程。其中R1=HandleEINT0,而R0恰好又是一个偏移量,每一个指针的空间是4个字节,那么R1+R0*4地址处刚好就是对应中断号的向量。其中就保存了对应中断函数的地址。因此PC就保存了这个调用函数的入口地址。也就是实现了处理函数调用过程。


35、 MSR CPSR_c,#SVCMODE|NOINT; 执行这句代码的前提就是被调用的函数执行完毕了,相关的入栈出栈操作也已经完成,恢复到了调用前的状态。此时需要将CPU的模式切换到SVC模式下。


36、BL OSIntExit ;这个操作完成了中断的切换,如果不是在中断嵌套中,那么最高优先级的任务就会被执行,进入最高优先的任务之后就不会再返回了,这是UC/OS-II中任务的特点,之后的代码也就不会执行了。这是特别需要注意的。但是如果任务处在中断嵌套中,那么OSIntExit只是减少中断嵌套的次数,并不完成其他的操作。那么这时候就需要恢复之前被中断的任务了,也就是需要完成任务堆栈的弹出操作。


39-41、


LDMFD SP!,{R4} ;POP the task''s CPSR


MSR SPSR_cxsf,R4


LDMFD SP!,{R0-R12,LR,PC}^


这几句代码的实现实质上是完成了在中断嵌套中时的任务切换操作。


讨论:


不知道我理解的对不对,我认为这段代码存在一定的问题,具体的问题如下,因为在中断嵌套中,CPU执行的肯定就是中断服务函数,此时的任务处于低优先级的,并不需要我们保存任务的信息。为什么这段代码能够运行的原因我认为主要是因为这种处理的方式是不可能导致中断嵌套问题产生的。因为我们在进入中断以后关闭了中断使能位,不会产生中断嵌套也就看不出问题所在。我认为如果在支持中断嵌套的CPU中,应该首先检测是否在中期嵌套中,如果在中断嵌套中,则不需要任务寄存器的保存,如果不在,则需要保存。


关闭中断的方式避免了中断嵌套产生的可能,这也说明一直需要保存任务的情景,使得这段代码是有效的。


关键字:UCOS-II  OS_CPU_IRQ_ISR  移植过程 引用地址:UCOS-II中OS_CPU_IRQ_ISR移植过程分析

上一篇:一种基于ARM11和RFID技术的智能物流管理系统设计
下一篇:ARM技术中间件技术探析

推荐阅读最新更新时间:2024-10-30 05:27

uCOS II在ARM处理器上移植过程中的中断处理
uCOS II是一个源码公开、可移植、可固化、可剪裁和抢占式的实时多任务操作系统,其大部分源码是用ANSI C编写,与处理器硬件相关的部分使用汇编语言编写。总量约200行的汇编语言部分被压缩到最低限度,以便于移植到任何一种其它的CPU上。 uCOS II最多可支持56个任务,其内核为占先式,总是执行就绪态的优先级最高的任务,并支持Semaphore (信号量)、Mailbox (邮箱)、MessageQueue(消息队列)等多种常用的进程间通信机制。与大多商用RTOS不同的是,uCOS II公开所有的源代码.并可以免费获得,只对商业应用收取少量License费用。 uCOS II移植跟OS_CUP_C.C、OS_
[单片机]
ucos-ii示例5:消息队列测试
环境: 主机:WIN8 开发环境:MDK4.72 ucgui版本:3.90 ucos版本:ucos-ii mcu: stm32f103VE 说明: 本示例中task1时间片为1s,task2时间片为8s。task1每秒往消息队列写入1个数据,task2全部读取出来。所以task2每次能读到8个数据。 注意: 1.消息队列需要一个指针数组指向消息 2.应该有一个固定的消息数组存储消息,不应将局部变量填入发送消息函数 3.QSQPost为先进先出函数(FIFO),QSQPostFornt为后进先出函数(LIFO) 效果图: 源码: #define TASK_STK_SIZE 512 /***********
[单片机]
<font color='red'>ucos-ii</font>示例5:消息队列测试
STM32F4 开发笔记9:USB CDC驱动程序的详细移植过程
本文详细介绍USB CDC驱动程序的移植过程。 1、按照 STM32F4 开发笔记8:解决USB CDC “该设备无法启动”问题 介绍的过程生成驱动程序后,找到如下图所示的文件加入到自己的项目工程中。 2、打开usbd_cdc_if.c文件,可以看到其中有,如下4个函数,其中Init、DeInit和Control函数都不需要改变,关键是Receive函数。 static int8_t CDC_Init_FS(void); static int8_t CDC_DeInit_FS(void); static int8_t CDC_Control_FS(uint8_t cmd, uint8_t* pbuf, ui
[单片机]
uCOS-II移植步骤
说明:将V2.92移植到STM32F107ZET6和STM32F107VCT6. 关于uCOS的移植,其实要说难也难,要说简单也简单。简单的是现在官方都已经移植好了,难的是自己写那个汇编的移植代码。废话不多说,直接上移植步骤和注意事项。 第一步:到官网下载最新版本的uCOS-II V2.92,在uCOS-II的文件夹下有三个这样的文件主要有这两个移植的文件,在DOC文件夹下主要是移植的说明和性能的测试结果, 其中uCOS-II-RefMan和uCOS-II-CfgMan比较有参考价值,第一个文件对每个函数进行了说明,重要的是给出了大部分函数的应用实例。第二个就是我们移植好后的配置文件了。还有一个重要的文件就是很重要的
[单片机]
小广播
设计资源 培训 开发板 精华推荐

最新单片机文章
何立民专栏 单片机及嵌入式宝典

北京航空航天大学教授,20余年来致力于单片机与嵌入式系统推广工作。

换一换 更多 相关热搜器件
随便看看

 
EEWorld订阅号

 
EEWorld服务号

 
汽车开发圈

电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved