打印
[MCU]

学了两三年单片机直到用了操作系统才发现的一个问题

[复制链接]
1226|23
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
飛饵|  楼主 | 2020-5-1 22:35 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
用了RTOS才思考这个问题,就是中断返回的标志到底是什么?
1、以前写裸机程序的时候只有一个思维,整个程序只有main里的大循环和中断服务。而中断的过程就是保存main里执行节点的数据(中断嵌套时另外说),进入中断服务里执行代码,然后恢复main执行节点的数据,我认为此时中断返回标志就是恢复到main里那个被打断的执行节点。即便是在中断服务中调用了函数,但依然认为还在中断中,因为还未恢复到main里。不知道这么理解对不对?
2、现在用了抢占式的RTOS,如uCOS,在中断服务最后会进行任务调度,这个调度视情况而定有时是返回到被打断的任务有时是跳到其他任务,对于前者可以说和上面差不多,但后者呢,这时候算中断返回了吗?
所以我想知道,在CPU内核层面是不是有什么东西能知道中断返回了??

使用特权

评论回复

相关帖子

沙发
William1994| | 2020-5-2 09:25 | 只看该作者
本帖最后由 William1994 于 2020-5-2 09:26 编辑

1980年停止开发并公开了部分文档的8051里面有说明,有个叫中断优先级控制器的东西。(可是8051只有一个中断优先级。正常是中断源通过优先级去申请中断,而不是直接申请)。
CPU里面有这个东西,标记着他已经进入了中断,即使串口仍然在申请中断(因为软件处理完需要一定的时间,处理完对应的寄存器串口外设的寄存器对应的数字电路才会撤销中断),它也不会再重复进入中断。
在收到串口中断,进入中断服务程序(ISR)后,ISR还没有处理完数据,串口还没有撤销中断的时候,如果CPU里面没有这个东西,就会再次进入中断,不停的进入会导致堆栈溢出,软件崩溃。
所以RETI指令和RET指令是两个指令。


RTOS里面的定时器中断,是保存正在执行的A进程的现场,恢复另外一个之前保存的B进程的现场,然后中断返回,cpu和B软件都认为没有中断过。A进程也不知道自己中间被暂停执行了。
RTOS是进程A和B的上帝,A和B都发现不了它。

使用特权

评论回复
板凳
飛饵|  楼主 | 2020-5-2 16:16 | 只看该作者
William1994 发表于 2020-5-2 09:25
1980年停止开发并公开了部分文档的8051里面有说明,有个叫中断优先级控制器的东西。(可是8051只有一个中断 ...

翻了下CM3权威手册,原来CM3的中断返回是用BX指令来识别返回动作,类似51的RETI,只是CM3更加灵活还可以用别的指令,只要往PC里填入正确的EXC_RETURN就行。
然后又看了下uCOS的 OSIntExit() 执行过程,貌似也是在中断服务的最后执行中断返回才进行任务调度,但是单步跟踪时每次现象都不同,不太清楚怎么回事

使用特权

评论回复
地板
William1994| | 2020-5-2 16:55 | 只看该作者
飛饵 发表于 2020-5-2 16:16
翻了下CM3权威手册,原来CM3的中断返回是用BX指令来识别返回动作,类似51的RETI,只是CM3更加灵活还可以 ...

对于这个地方,ARM7的权威手册里面有个PSR寄存器,能看的当前是在哪个优先级的中断里面。实测可以在IRQ中直接修改PSR,让CPU再次嵌套进入IRQ。
如果有NVIC的,就又是另外一说了。

使用特权

评论回复
5
William1994| | 2020-5-2 17:01 | 只看该作者
ldmia sp!, { r0-r12,pc }^  
其中最后面的那个^,就是是否从堆栈恢复PSR。一旦PSR恢复了, 中断优先级 又可以申请中断了 。

使用特权

评论回复
6
飛饵|  楼主 | 2020-5-2 18:22 | 只看该作者
William1994 发表于 2020-5-2 17:01
ldmia sp!, { r0-r12,pc }^  
其中最后面的那个^,就是是否从堆栈恢复PSR。一旦PSR恢复了, 中断优先级 又 ...

汇编代码里中断服务最后有一段指令是弹栈,我看了下所弹出堆栈数据,确实是把正确的值填入PC了,看来就是这时候中断返回的吧。不过用的POP,等效于你说的 ldmia 吧

使用特权

评论回复
7
William1994| | 2020-5-2 19:05 | 只看该作者
好像是不行哦。你仔细看看权威手册,里面应该有。或者at一下论坛里的高手,比如xxx

使用特权

评论回复
8
wanyy| | 2020-5-2 21:10 | 只看该作者
C51的我现在用的比较少,Cortex-M的程序执行分为特权模式和非特权模式,然后内核的系统栈也分为两种,MSP/PSP。一般ucos会让线程模式工作使用PSP,然后将任务栈恢复到PSP,当执行任务调度的时候,优先级高的任务恢复到PSP,退出中断自动就可以执行恢复的任务了。如果进入中断之前的任务优先级高,当然退出中断就执行之前的程序,如果有更高优先级的程序得以执行,当然就进入其他程序执行了。systick就是可以让高优先级得程序得以满足执行条件得一个基本中断。

使用特权

评论回复
9
elife| | 2020-5-2 22:15 | 只看该作者
楼主担心的是中断中进行的任务调度会不会导致硬件中断的逻辑异常。这个是操作系统考虑的,所以区分中断中调度和非中断中调度。而且中断嵌套,操作系统也要考虑进去。目前的任务调度还是软件调度,相信将来都会做硬件任务调度,毕竟可以提高速度。也可能现在的电脑CPU已经考虑了硬件任务调度。

使用特权

评论回复
10
飛饵|  楼主 | 2020-5-2 22:46 | 只看该作者
wanyy 发表于 2020-5-2 21:10
C51的我现在用的比较少,Cortex-M的程序执行分为特权模式和非特权模式,然后内核的系统栈也分为两种,MSP/P ...

额,ucos的调度机制我清楚。只是想知道内核的异常(中断)是如何返回的,CM3权威手册说通过BX LR或者其他指令把exc_return填入PC寄存器都能识别返回动作,应该没理解错吧。
我debug看了汇编代码里中断服务的最后一句是POP { R3-R5, PC },后面的PC应该就是填入exc_return了吧?接着就是跳到了任务函数里

使用特权

评论回复
评论
雪山飞狐D 2020-5-4 21:50 回复TA
@飛饵 :有部分是硬件电路实现的,PC也是一个逻辑电路,会根据返回的信息返回到指定的MSP或者PSP,MSP和PSP 实际隐形地址对用户不可见,对用户而已,这两个都是同一个地址,反回去以后就可以出栈了,也就回到原来中断之前的程序 
飛饵 2020-5-3 12:19 回复TA
@wanyy :还有个不解的地方,中断返回是往pc填了exc_return(值是0xFFFFFFFD),为什么下一步pc就能指到任务函数的地址去了?这中间是不是还有什么操作看不到的?? 
飛饵 2020-5-3 12:18 回复TA
@wanyy :不清楚为什么只pop了 r3-r5和pc。 
wanyy 2020-5-2 23:54 回复TA
是把PSP/MSP的内容给POP到系统寄存器里面,更正上面一句话 
wanyy 2020-5-2 23:53 回复TA
其实你把PSP/MSP设置好了,IRET之后,会自动将对应的堆栈POP到PSP/MSP中,主要是PSP吧!应该不需要手动POP,这个地方应该是12个寄存器好像 
wanyy 2020-5-2 23:46 回复TA
是的,任务调度后就是靠PC指针了,去到想要的函数恢复地址。有些rtos就是靠goto的原理来实现的!中断其实也可以理解为优先级比较高的任务了 
11
操作系统没有中断的概念,中断是底层的驱动处理为申请标志,系统扫描申请标志执行对应程序

使用特权

评论回复
评论
William1994 2020-5-4 13:46 回复TA
没有中断,操作系统怎么打断正在运行的任务,并把它调度出去的? 
12
ayb_ice| | 2020-5-4 08:34 | 只看该作者
RTOS是一种非常规的操作,每个任务有自己的堆栈,所以可以返回到不同的任务,但需要大量堆栈空间

RETI是中断返回指令,

使用特权

评论回复
13
William1994| | 2020-5-4 13:45 | 只看该作者
你看的是这个版本的os_cpu_a.asm吗?

;********************************************************************************************************
;                                         HANDLE PendSV EXCEPTION
;                                     void OS_CPU_PendSVHandler(void)
;
; Note(s) : 1) PendSV is used to cause a context switch.  This is a recommended method for performing
;              context switches with Cortex-M3.  This is because the Cortex-M3 auto-saves half of the
;              processor context on any exception, and restores same on return from exception.  So only
;              saving of R4-R11 is required and fixing up the stack pointers.  Using the PendSV exception
;              this way means that context saving and restoring is identical whether it is initiated from
;              a thread or occurs due to an interrupt or exception.
;
;           2) Pseudo-code is:
;              a) Get the process SP, if 0 then skip (goto d) the saving part (first context switch);
;              b) Save remaining regs r4-r11 on process stack;
;              c) Save the process SP in its TCB, OSTCBCur->OSTCBStkPtr = SP;
;              d) Call OSTaskSwHook();
;              e) Get current high priority, OSPrioCur = OSPrioHighRdy;
;              f) Get current ready thread TCB, OSTCBCur = OSTCBHighRdy;
;              g) Get new process SP from TCB, SP = OSTCBHighRdy->OSTCBStkPtr;
;              h) Restore R4-R11 from new process stack;
;              i) Perform exception return which will restore remaining context.
;
;           3) On entry into PendSV handler:
;              a) The following have been saved on the process stack (by processor):
;                 xPSR, PC, LR, R12, R0-R3
;              b) Processor mode is switched to Handler mode (from Thread mode)
;              c) Stack is Main stack (switched from Process stack)
;              d) OSTCBCur      points to the OS_TCB of the task to suspend
;                 OSTCBHighRdy  points to the OS_TCB of the task to resume
;
;           4) Since PendSV is set to lowest priority in the system (by OSStartHighRdy() above), we
;              know that it will only be run when no other exception or interrupt is active, and
;              therefore safe to assume that context being switched out was using the process stack (PSP).
;********************************************************************************************************

OS_CPU_PendSVHandler
    CPSID   I                                  ; 上下文切换期间防止中断-关中断
    MRS     R0, PSP                            ; 获得PSP
    CBZ     R0, OS_CPU_PendSVHandler_nosave ;PSP为0跳到OS_CPU_PendSVHandler_nosave,即不保存上文,直接进入下文。
                                                    ;问什么呢,因为首次调用,是没有上文的。否则,全部执行
                                                                                                                                                                   
                                                    ;接下来是保存上文
    SUBS    R0, R0, #0x20       ;因为寄存器是32位的,4字节对齐,自动压栈的寄存器有8个,所以偏移为8*0x04=0x20
    STM     R0, {R4-R11}                                ;除去自动压栈的寄存器外,需手动将R4-R11压栈

    LDR     R1, =OSTCBCur       ; OSTCBCur->OSTCBStkPtr = SP;保存上文的SP指针 OSTCBCur->OSTCBStkPtr = SP;
    LDR     R1, [R1]
    STR     R0, [R1]            ; R0 is SP of process being switched out

                                ; At this point, entire context of process has been saved
OS_CPU_PendSVHandler_nosave ;切换下文
    PUSH    {R14}              ; Save LR exc_return value, LR压栈,下面要调用C函数
    LDR     R0, =OSTaskSwHook          ; OSTaskSwHook();
    BLX     R0
    POP     {R14}

    LDR     R0, =OSPrioCur               ; OSPrioCur = OSPrioHighRdy;
    LDR     R1, =OSPrioHighRdy
    LDRB    R2, [R1]
    STRB    R2, [R0]

    LDR     R0, =OSTCBCur            ; OSTCBCur  = OSTCBHighRdy;
    LDR     R1, =OSTCBHighRdy
    LDR     R2, [R1]
    STR     R2, [R0]

    LDR     R0, [R2]                ; R0 is new process SP; SP = OSTCBHighRdy->OSTCBStkPtr;
    LDM     R0, {R4-R11}            ; Restore r4-11 from new process stack
    ADDS    R0, R0, #0x20
    MSR     PSP, R0                 ; Load PSP with new process SP ,PSP = 新任务SP
    ORR     LR, LR, #0x04   ; Ensure exception return uses process stack,确保异常返回后使用PSP
    CPSIE   I
    BX      LR      ; Exception return will restore remaining context,
                        ;退出异常,从PSP弹出xPSR,PC,LR,R0-R3,进入新任务运行
    END

使用特权

评论回复
评论
飛饵 2020-5-4 18:07 回复TA
是的 
14
duhemayi| | 2020-5-4 16:06 | 只看该作者

使用特权

评论回复
15
William1994| | 2020-5-4 23:47 | 只看该作者
中间都是看的到的,你贴个截图,左边有寄存器,右边上面有反汇编窗口。右中有源代码,右下有memory窗口显示堆栈内容。
在反汇编窗口按最后一个单步就回到你的任务。
这个时候抓个图。

使用特权

评论回复
16
wanyy| | 2020-5-5 14:32 | 只看该作者
Oxfffffffd是lr寄存器的值,低位表示线程的执行模式之类的。问题的关键还是psp,函数返回会自动弹栈,所以任务调度设置了PSP,任务栈是存储了任务被中断的现场,切换任务栈也就切换了任务调度之后的返回路径了!

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

18

主题

117

帖子

1

粉丝