ARM9 与 A7移植差不多,都比STM32简单,区别在于ARM9开关中断不一样,一些新指令不支持,并且NUC970没有VFP,无法使用硬件浮点运算,因此无需保存S0-S31这些寄存器,堆栈消耗也会少一些。
//在.S文件中使用UCOS提供的中断管理
startus.s
;/***************************************************************************
; * *
; * Copyright (c) 2020 NUC970裸机启动文件. *
; * *
; ***************************************************************************/
;
VFPEnable EQU (0x40000000) ;VFP使能设置
;为用户堆预留空间
Heap_Size EQU (512*1024) ;堆大小
AREA HEAP, NOINIT, READWRITE, ALIGN=3
__heap_base
Heap_Mem SPACE Heap_Size
__heap_limit
;用户模式堆栈大小位所有堆栈预留空间
USR_Stack_Size EQU 1024 ;用户模式堆栈大小
OTH_Stack_Size EQU 256 ;其他模式堆栈大小
AREA STACK, NOINIT, READWRITE, ALIGN=3 ;堆栈设置为8字节对齐
UND_Stack_Mem SPACE OTH_Stack_Size
Abort_Stack_Mem SPACE OTH_Stack_Size
FIQ_Stack_Mem SPACE OTH_Stack_Size
IRQ_Stack_Mem SPACE OTH_Stack_Size
SVC_Stack_Mem SPACE OTH_Stack_Size
USR_Stack_Mem SPACE USR_Stack_Size
__initial_sp
PRESERVE8
AREA NUC_INIT, CODE, READONLY
;--------------------------------------------
; Mode bits and interrupt flag (I&F) defines
;--------------------------------------------
USR_MODE EQU 0x10
FIQ_MODE EQU 0x11
IRQ_MODE EQU 0x12
SVC_MODE EQU 0x13
ABT_MODE EQU 0x17
UDF_MODE EQU 0x1B
SYS_MODE EQU 0x1F
I_BIT EQU 0x80
F_BIT EQU 0x40
ENTRY
EXPORT Reset_Go
IMPORT OS_CPU_ARM_ExceptUndefInstrHndlr
IMPORT OS_CPU_ARM_ExceptSwiHndlr
IMPORT OS_CPU_ARM_ExceptPrefetchAbortHndlr
IMPORT OS_CPU_ARM_ExceptDataAbortHndlr
IMPORT OS_CPU_ARM_ExceptIrqHndlr
IMPORT OS_CPU_ARM_ExceptFiqHndlr
EXPORT Vector_Table
Vector_Table
B Reset_Go ; Modified to be relative jumb for external boot
LDR PC, Undefined_Addr
LDR PC, SWI_Addr
LDR PC, Prefetch_Addr
LDR PC, Abort_Addr
DCD 0x0
LDR PC, IRQ_Addr
LDR PC, FIQ_Addr
Reset_Addr DCD Reset_Go
Undefined_Addr DCD OS_CPU_ARM_ExceptUndefInstrHndlr; ;未定义指令异常
SWI_Addr DCD OS_CPU_ARM_ExceptSwiHndlr ;软件中断,管理模式
Prefetch_Addr DCD OS_CPU_ARM_ExceptPrefetchAbortHndlr ;中止预取模式
Abort_Addr DCD OS_CPU_ARM_ExceptDataAbortHndlr ;中止数据模式
DCD 0
IRQ_Addr DCD OS_CPU_ARM_ExceptIrqHndlr ;中断模式
FIQ_Addr DCD OS_CPU_ARM_ExceptFiqHndlr ;快速中断模式
; ************************
; Exception Handlers
; ************************
Reset_Go
;关闭中断
MRS R0, CPSR ;读取CPSR的值
ORR R0, R0, #0x80 ;将IRQ中断禁止位I置1,即屏蔽IRQ中断
MSR CPSR_c, R0 ;设置CPSR的值
;--------------------------------
; Initial Stack Pointer register
;--------------------------------
;INIT_STACK
MSR CPSR_c, #UDF_MODE :OR: I_BIT :OR: F_BIT
LDR SP, =(UND_Stack_Mem+OTH_Stack_Size)
MSR CPSR_c, #ABT_MODE :OR: I_BIT :OR: F_BIT
LDR SP, =(Abort_Stack_Mem+OTH_Stack_Size)
MSR CPSR_c, #IRQ_MODE :OR: I_BIT :OR: F_BIT
LDR SP, =(IRQ_Stack_Mem+OTH_Stack_Size)
MSR CPSR_c, #FIQ_MODE :OR: I_BIT :OR: F_BIT
LDR SP, =(FIQ_Stack_Mem+OTH_Stack_Size)
MSR CPSR_c, #SVC_MODE :OR: I_BIT :OR: F_BIT
LDR SP, =(SVC_Stack_Mem+OTH_Stack_Size)
MSR CPSR_c, #SYS_MODE :OR: I_BIT :OR: F_BIT
LDR SP, =(USR_Stack_Mem+USR_Stack_Size)
; Reset SCTLR Settings
MRC p15, 0, R0, c1, c0, 0 ; Read CP15 System Control register
; BIC R0, R0, #(0x1 << 12) ; Clear I bit 12 to disable I Cache 当数据cache与指令cache是分开的,此处关闭指令cache
ORR R0, R0, #(0x1 << 12) ; 开启指令cache
BIC R0, R0, #(0x1 << 13) ; Clear V bit 13 选择低端异常中断向量 0x0~0x1c
; BIC R0, R0, #(0x1 << 2) ; Clear C bit 2 to disable D Cache 关闭数据cache
ORR R0, R0, #(0x1 << 2) ; 开启数据cache
; BIC R0, R0, #0x2 ; Clear A bit 1 to disable strict alignment 关闭地址对齐检查
; BIC R0, R0, #(0x1 << 11) ; Clear Z bit 11 to disable branch prediction 关闭跳转分支预测
ORR R0, R0, #(0x1 << 11) ; 开启跳转分支预测
BIC R0, R0, #0x1 ; Clear M bit 0 to disable MMU 关闭MMU
MCR p15, 0, R0, c1, c0, 0 ; Write value back to CP15 System Control register
;初始化中断向量表位置
;LDR R0, =Vector_Table;
;MCR p15, 0, R0, c12, c0, 0;//设置VBAR
;开启系统总中断=>在进入系统前请不要开启中断
; MRS R0, CPSR ;读取CPSR的值
; BIC R0, R0, #0x80 ;将IRQ中断禁止位I清零,即允许IRQ中断
; MSR CPSR_c, R0 ;设置CPSR的值
IMPORT __main
;-----------------------------
; enter the C code
;-----------------------------
B __main
AbortHndlr
MSR CPSR_c, #SVC_MODE :OR: I_BIT :OR: F_BIT
B Reset_Go
;用户堆初始化,如果不初始化为出现异常
EXPORT __user_initial_stackheap
__user_initial_stackheap
LDR R0, = Heap_Mem ;r0 中的堆基址
LDR R1, = (USR_Stack_Mem + USR_Stack_Size) ;r1 中的堆栈基址,即堆栈区中的最高地址
LDR R2, = (Heap_Mem + Heap_Size) ;r2 中的堆限制
LDR R3, = UND_Stack_Mem ;r3 中的堆栈限制,即堆栈区中的最低地址。
BX LR
ALIGN
END
然后实现一个用户中断管理接口
/*************************************************************************************************************************
* 函数 : void OS_CPU_ExceptHndlr(u32 os_cpu_except)
* 功能 : 系统中断处理(有操作系统接口)
* 参数 : os_cpu_except:中断类型
* 返回 : 无
* 依赖 : 操作系统获取到中断后调用此接口,并将中断类型写入到寄存器R0,如果是快速中断或中断,需要自己读取IAR值获取中断编号,
执行结束后告知GIC中断处理完毕
* 作者 : cp1300@139.com
* 时间 : 2020-08-21
* 最后修改时间 : 2020-08-21
* 说明 : 中断类型在os_cpu_a.asm os_cpu.h 中进行了定义,会直接在os_cpu_a.s中调用
OS_CPU_ARM_EXCEPT_RESET EQU 0x00
OS_CPU_ARM_EXCEPT_UNDEF_INSTR EQU 0x01
OS_CPU_ARM_EXCEPT_SWI EQU 0x02
OS_CPU_ARM_EXCEPT_PREFETCH_ABORT EQU 0x03
OS_CPU_ARM_EXCEPT_DATA_ABORT EQU 0x04
OS_CPU_ARM_EXCEPT_ADDR_ABORT EQU 0x05
OS_CPU_ARM_EXCEPT_IRQ EQU 0x06
OS_CPU_ARM_EXCEPT_FIQ EQU 0x07
*************************************************************************************************************************/
void OS_CPU_ExceptHndlr(u32 os_cpu_except)
{
switch(os_cpu_except)
{
case OS_CPU_ARM_EXCEPT_RESET: //复位
{
}break;
case OS_CPU_ARM_EXCEPT_UNDEF_INSTR: //未知
{
}break;
case OS_CPU_ARM_EXCEPT_SWI: //软件切换
{
}break;
case OS_CPU_ARM_EXCEPT_PREFETCH_ABORT://指令中止
{
}break;
case OS_CPU_ARM_EXCEPT_DATA_ABORT: //数据中止
{
}break;
case OS_CPU_ARM_EXCEPT_ADDR_ABORT: //地址错误
{
}break;
case OS_CPU_ARM_EXCEPT_IRQ: //普通中断
case OS_CPU_ARM_EXCEPT_FIQ: //快速中断
{
u32 mISNR;
u32 intNum;
mISNR = AIC->IPER; //需要先读取IPER,否则ISNR可能没更新,IPER的值>>2位是等于ISNR的,必须先读取IPRE
mISNR >>= 2;
//mISNR = AIC->ISNR; //这个寄存器只能读取一次,下次读取会被清零的,ISNR有时候没有及时更新
intNum = mISNR & 0X3F;
if ((intNum == 0) || (intNum>=NUMBER_OF_INT_VECTORS))
{
AIC->EOSCR = 1; //写入任何值,告诉AIC中断执行完毕了
return;
}
//执行中断服务程序
((void (*)(void)) sg_IRQHandlerTable[intNum])(); //执行中断服务程序
AIC->EOSCR = 1; //写入任何值,告诉AIC中断执行完毕了
}break;
default:break;
}
}
//初始化OS滴答时钟,1ms,并在中断中调用 OSTimeTick(); 即可
/*************************************************************************************************************************
* 函数 : void OS_Tack_Time2_IRQHandler(void)
* 功能 : 定时器2中断服务程序,用于OS滴答时钟
* 参数 : 无
* 返回 : 无
* 依赖 : 底层与OS
* 作者 : cp1300@139.com
* 时间 : 2020-08-21
* 最后修改时间 : 2020-08-21
* 说明 : 中断服务程序是由ucos中断服务程序先进行了处理,此处只需要调用OSTimeTick()并清除中断源即可
*************************************************************************************************************************/
void __inline OS_Tack_Time_IRQHandler(void)
{
OSTimeTick(); //Call uC/OS-II's OSTimeTick()
}
/*************************************************************************************************************************
* 函数 : void SYS_OS_TickInit(void)
* 功能 : 系统OS滴答时钟初始化(使用的TIM2)
* 参数 : 无
* 返回 : 无
* 依赖 : 底层与OS
* 作者 : cp1300@139.com
* 时间 : 2020-08-21
* 最后修改时间 : 2020-08-21
* 说明 : 使用定时器2作为时钟源,1ms触发一次中断,用于ucosiii时钟源
*************************************************************************************************************************/
void SYS_OS_TickInit(void)
{
TIMER_CONFIG mTimConfig;
//定时器测试
mTimConfig.ComparedValue = 1000; //比较值
mTimConfig.mode = TIMER_MODE_PERIODIC; //工作模式-周期
mTimConfig.pIntCallBack = OS_Tack_Time_IRQHandler; //中断回调函数
mTimConfig.isStart = TRUE; //是否开始计数
mTimConfig.ClockPre = 12-1; //时钟预分配,分频值=设置值+1,比如设置为1,则为2分频
mTimConfig.isEnableInt = TRUE; //是否使能定时器中断
mTimConfig.IntPro = SYS_INT_OS_TICK_PRO; //中断优先级
TIMERx_Config(SYS_OS_TICK_TIME_CH, &mTimConfig); //定时器配置
}
//OS初始化以及任务建立
//任务堆栈大小定义
#define SYSTEM_STK_SIZE (256) //系统主进程
#define LED_STK_SIZE (128) //LED
//任务堆栈声明-建议OS堆栈使用8字节对齐,否则使用64位数打印时会出现各种问题
__align(8) static CPU_STK TASK_SYSTEM_STK[SYSTEM_STK_SIZE]; //系统主线程
__align(8) static CPU_STK TASK_LED_STK[LED_STK_SIZE]; //LED线程
//任务控制块
OS_TCB SYSTEM_Task_TCB;
OS_TCB LED_Task_TCB;
//任务列表
void TaskSystem(void *pdata); //任务:系统主线程
void TaskLED(void *pdata); //任务:LED线程
//OS初始化,它是第一个运行的函数,初始化各种的全局变量,例如中断嵌套计数器、优先级、存储器
OSInit(&err);
//创建任务1
OSTaskCreate( (OS_TCB *)&SYSTEM_Task_TCB, //任务控制块
(CPU_CHAR *)"Task1", //任务的名字
(OS_TASK_PTR)TaskSystem, //任务函数
(void *)0, //传递参数
(OS_PRIO)3, //任务的优先级
(CPU_STK *)TASK_SYSTEM_STK, //任务堆栈基地址
(CPU_STK_SIZE)SYSTEM_STK_SIZE/10, //任务堆栈深度限位,用到这个位置,任务不能再继续使用
(CPU_STK_SIZE)SYSTEM_STK_SIZE, //任务堆栈大小
(OS_MSG_QTY)0, //禁止任务消息队列
(OS_TICK)0, //默认时间片长度
(void *)0, //不需要补充用户存储区
(OS_OPT)OS_OPT_TASK_NONE, //没有任何选项
&err //返回的错误码
);
SYS_OS_TickInit();
SYS_EnableIrq(); //CPU总中断开启
//启动OS,进行任务调度
OSStart(&err);
//任务1:系统任务
void TaskSystem(void *pdata)
{
OS_ERR err;
OS_BoardInit();
//创建任务2
OSTaskCreate( (OS_TCB *)&LED_Task_TCB, //任务控制块
(CPU_CHAR *)"Task2", //任务的名字
(OS_TASK_PTR)TaskLED, //任务函数
(void *)0, //传递参数
(OS_PRIO)5, //任务的优先级
(CPU_STK *)TASK_LED_STK, //任务堆栈基地址
(CPU_STK_SIZE)TASK_LED_STK/10, //任务堆栈深度限位,用到这个位置,任务不能再继续使用
(CPU_STK_SIZE)TASK_LED_STK, //任务堆栈大小
(OS_MSG_QTY)0, //禁止任务消息队列
(OS_TICK)0, //默认时间片长度
(void *)0, //不需要补充用户存储区
(OS_OPT)OS_OPT_TASK_NONE, //没有任何选项
&err //返回的错误码
);
while(1)
{
LED2_FLASH();
Sleep(500);
}
}