Cotex - M内核的三个中断屏蔽器 PRIMASK 只有一个单一比特的寄存器,被置一则关闭所有可屏蔽异常。只剩下MMI和硬FAULT可以响应。缺省为0,表示没有中断 FAULTMASK 只有一个单一比特的寄存器,被置一时,只有NMI才能响应。缺省为0,表示没有关异常 BASEPRI 最多有9位(由表达优先级的位数决定),它定义了被屏蔽优先级的阈值。 当它被设置为某个值后,所有优先级大于等于此值得中断都被关闭(优先级号越大,优先级越低)。缺省为0,表示不关闭任何中断。
Cortex-M内核的专门的CPS指令 CPSID I PRIMASK=1 ;关中断 CPSIE I PRIMASK=0 ;开中断 CPSID F FAULTMASK=1 ;关异常 CPSIE F FAULTMASK=0 ;开异常
当汇编函数在C文件中被调用的时候,如果有一个形参,则执行的时候会将这个形参传入到CPU寄存器R0,如果有两个形参,第二个形参则传入到R1。
全局变量和常量
;/**
; * @addtogroup cortex-m4
; */
;/*@{*/ //为Cortex-m4写得启动文件
SCB_VTOR EQU 0xE000ED08 ; Vector Table Offset Register ;向量表偏移寄存器
NVIC_INT_CTRL EQU 0xE000ED04 ; interrupt control state register
NVIC_SYSPRI2 EQU 0xE000ED20 ; system priority register (2)
NVIC_PENDSV_PRI EQU 0x00FF0000 ; PendSV priority value (lowest)
NVIC_PENDSVSET EQU 0x10000000 ; value to trigger PendSV exception
AREA |.text|, CODE, READONLY, ALIGN=2
THUMB
REQUIRE8
PRESERVE8
;全局变量的声明,在.c文件里定义过的
IMPORT rt_thread_switch_interrupt_flag
IMPORT rt_interrupt_from_thread
IMPORT rt_interrupt_to_thread
;--------省略
;--------省略 ;省略得部分,就是rt_hw_context_switch_to这些函数
;--------省略
;--------省略
ALIGN 4
END
第一次调度函数 rt_hw_context_switch_to()函数
;/*
; * void rt_hw_context_switch_to(rt_uint32 to);
; * r0 --> to
; * this fucntion is used to perform the first thread switch
; */
rt_hw_context_switch_to PROC ;子程序rt_hw_context_switch_to的定义格式
EXPORT rt_hw_context_switch_to ;全局变量导出,C文件里也可用
; set to thread
LDR r1, =rt_interrupt_to_thread ;ba rt_interrupt_to_thread的地址加载入到r1
STR r0, [r1] ;把r0里的值加载到r1中数据代表的地址,即rt_interrupt_to_thread
IF {FPU} != "SoftVFP"
; CLEAR CONTROL.FPCA
MRS r2, CONTROL ; read
BIC r2, #0x04 ; modify
MSR CONTROL, r2 ; write-back
ENDIF
; set from thread to 0
LDR r1, =rt_interrupt_from_thread ;把r1中数据加载为rt_interrupt_from_thread的地址
MOV r0, #0x0 ;把r0中的值设置为0
STR r0, [r1] ;把0写入到rt_interrupt_frome_thread中
; set interrupt flag to 1 ;设置中断标志位rt_thread_switch_interrupt_flag为1
LDR r1, =rt_thread_switch_interrupt_flag
MOV r0, #1
STR r0, [r1]
; set the PendSV exception priority ;设置PendSV的中断优先级
LDR r0, =NVIC_SYSPRI2 ;r0中加载中断优先级寄存器2的地址
LDR r1, =NVIC_PENDSV_PRI ;r1中加载设置PendSV中断为低优先级的数据地址
LDR.W r2, [r0,#0x00] ; read ;R2中存入中断优先级地址
ORR r1,r1,r2 ; modify ;r2与r1相与的结果存入r1中
STR r1, [r0] ; write-back ;设置PendSV中断优先级为低优先级
; trigger the PendSV exception (causes context switch)
LDR r0, =NVIC_INT_CTRL
LDR r1, =NVIC_PENDSVSET
STR r1, [r0] ;触发中断
; restore MSP
LDR r0, =SCB_VTOR
LDR r0, [r0]
LDR r0, [r0]
MSR msp, r0
; enable interrupts at processor level ;打开中断
CPSIE F
CPSIE I
; never reach here! ;永远不会到达这里,?难道是触发PendSV中断后,回不来了?
ENDP
运行中上下文的调度切换函数rt_hw_context_switch()函数
;/*
; * void rt_hw_context_switch(rt_uint32 from, rt_uint32 to);
; * r0 --> from
; * r1 --> to
; */
rt_hw_context_switch_interrupt
EXPORT rt_hw_context_switch_interrupt
rt_hw_context_switch PROC
EXPORT rt_hw_context_switch
; set rt_thread_switch_interrupt_flag to 1
LDR r2, =rt_thread_switch_interrupt_flag
LDR r3, [r2]
CMP r3, #1
BEQ _reswitch
MOV r3, #1
STR r3, [r2]
LDR r2, =rt_interrupt_from_thread ; set rt_interrupt_from_thread
STR r0, [r2]
_reswitch
LDR r2, =rt_interrupt_to_thread ; set rt_interrupt_to_thread
STR r1, [r2]
LDR r0, =NVIC_INT_CTRL ; trigger the PendSV exception (causes context switch)
LDR r1, =NVIC_PENDSVSET
STR r1, [r0]
BX LR
ENDP
真正执行的切换函数PendSV_Handler函数
; r0 --> switch from thread stack
; r1 --> switch to thread stack
; psr, pc, lr, r12, r3, r2, r1, r0 are pushed into [from] stack
PendSV_Handler PROC ;PendSV中断服务子程序
EXPORT PendSV_Handler ;全局标号导出,C文件可用
; disable interrupt to protect context switch
MRS r2, PRIMASK ;关中断,防止打扰上下文切换
CPSID I ;rt_hw_context_switch_to函数触发了中断,就来到这个函数
; get rt_thread_switch_interrupt_flag
LDR r0, =rt_thread_switch_interrupt_flag
LDR r1, [r0]
CBZ r1, pendsv_exit ; pendsv already handled ;判断是否真的rt_thread_switch_interrupt_flag = 0,若为0,则执行pendsv_exit函数
; clear rt_thread_switch_interrupt_flag to 0
MOV r1, #0x00 ;当然,通常应该是不为0的,因为中断之前设置过其为1,所以这一步将其清0
STR r1, [r0] ;很明显,清0是这一步的
LDR r0, =rt_interrupt_from_thread ;把将要切换的线程的地址加载到r0中
LDR r1, [r0]
CBZ r1, switch_to_thread ; skip register save at the first time ;第一次的话,rt_interrupt_from_thread为0,我们在上一个函数里设置过的
;-------------------上文保存---------------------
MRS r1, psp ; get from thread stack pointer ;加载xpsp寄存器数据到r1寄存器中
IF {FPU} != "SoftVFP"
TST lr, #0x10 ; if(!EXC_RETURN[4])
VSTMFDEQ r1!, {d8 - d15} ; push FPU register s16~s31
ENDIF
STMFD r1!, {r4 - r11} ; push r4 - r11 register ;很明显这里是手动加载的
IF {FPU} != "SoftVFP"
MOV r4, #0x00 ; flag = 0
TST lr, #0x10 ; if(!EXC_RETURN[4])
MOVEQ r4, #0x01 ; flag = 1
STMFD r1!, {r4} ; push flag
ENDIF
LDR r0, [r0] ;加载r0指向值到r0,即r0=rt_interrupt_from_thread,这里在上文保存前就已经能存入到r0寄存器了了
STR r1, [r0] ; update from thread stack pointer ;将r1的值存储到r0,即更新线程栈sp
;-------------------下文切换---------------------
switch_to_thread
LDR r1, =rt_interrupt_to_thread //加载将要执行的线程栈指针sp地址到r1中
LDR r1, [r1] ;很明显,把sp指针指向的地址写入r1
LDR r1, [r1] ; load thread stack pointer ;很好,这一步已经到了sp
IF {FPU} != "SoftVFP"
LDMFD r1!, {r3} ; pop flag
ENDIF
LDMFD r1!, {r4 - r11} ; pop r4 - r11 register
IF {FPU} != "SoftVFP"
CMP r3, #0 ; if(flag_r3 != 0)
VLDMFDNE r1!, {d8 - d15} ; pop FPU register s16~s31
ENDIF
MSR psp, r1 ; update stack pointer
IF {FPU} != "SoftVFP"
ORR lr, lr, #0x10 ; lr |= (1 << 4), clean FPCA.
CMP r3, #0 ; if(flag_r3 != 0)
BICNE lr, lr, #0x10 ; lr &= ~(1 << 4), set FPCA.
ENDIF
pendsv_exit
; restore interrupt
MSR PRIMASK, r2
ORR lr, lr, #0x04
BX lr
ENDP
嗯,情况大概就是这样,F4支持FPU浮点运算。当然,浮点运算这个部分,哈哈,我自然是不太清楚的。慢慢来嘛,等我对这里再有更深印象的时候,再回来发出来。