uCOS-II移植之os_cpu_a.asm文件说明

uCOS-II移植需要需要改动的文件主要有有os_cpu_a.asm、 .c、os_cpu.h三个文件,首先来讲一下汇编文件os_cpu_a.asm。
os_cpu_a.asm中用到的汇编知识及语法:
1、EXTERN:用于声明变脸,表示被声明的变量在其他文件中定义,在本文件中使用
2、EXPORT:用于定义变量,且此变量被其他文件所使用的,与之相对的是IMPORT,表示只能在本文件中使用。
3、MRS/MSR:读/写特殊功能寄存器(xPSR,PRIMASK,FAULTMASK,BASEPRI,CONTROL,这些特殊功能寄存器只能被这两个指令访问)
4、CPSID/CPSIE     I/F:开启/关闭中断/异常,这是CM3内核专门设置的一条快速开关指令。
.......
其他一些指令为汇编常用指令。
另外再讲一个知识点:PSP和MSP
堆栈,一般是程序运行时用来临时存储数据的存储结构,符合后进先出的原则,就像抽屉一样,后放进的东西总是要先拿出来。
PSP:进程堆栈指针,一般任务运行时使用的是这个堆栈
MSP:主堆栈指针,系统级的代码运行于这个堆栈,比如异常/中断例程,内核。
首先来说一下OS_CPU_SR_Save函数,代码如下:
OS_CPU_SR_Save
    MRS     R0, PRIMASK             ;保存PRIMASK(中断屏蔽寄存器)内容到R0中                            
    CPSID   I                       ;关闭中断                           
    BX      LR                      ;BX为跳转指令,跳转到LR所指向的地址。相当于退出此函数                       
PRIMASK这个寄存器是只有单一比特的寄存器,置1表示关掉所有可屏蔽的异常,置0则不关闭任何中断。LR寄存器为连接寄存器,它用于在调用子程序时存储返回地址。OS_CPU_SR_Save()函数被OS_ENTER_CRITICAL()函数调用,而OS_ENTER_CRITICAL()的作用是关闭所有中断或者说进入临界段,防止某些不允许被中断的代码收到干扰,所以在关闭中断时需要调用OS_CPU_SR_Save()函数将与中断有关的PRIMASK保存到堆栈中,当下次重新开启中断时再将他从堆栈中恢复到PRIMASK中,就好像没发生什么一样。
既然有OS_CPU_SR_Save()保存函数,就肯定有恢复函数OS_CPU_SR_Restore
OS_CPU_SR_Restore
    MSR     PRIMASK, R0                                         ;将之前PRIMASK保存的信息重新还给PRIMASK
    BX      LR                                                  ;跳转至LR所给地址,相当于退出此函数。
此函数功能就像之前所述的将保存的内容恢复到PRIMASK中。此函数一般被OS_EXIT_CRITICAL()函数调用,OS_EXIT_CRITICAL()的作用是开启中断或者说退出临界段。
接着来说说OSStartHighRdy函数,代码如下:
OSStartHighRdy
    LDR     R0, =NVIC_SYSPRI14                                  ; 
    LDR     R1, =NVIC_PENDSV_PRI                                ; 
    STRB    R1, [R0]                                            ; 将PendSV优先级保存到优先级寄存器NVIC_SYSPRI14中

    MOVS    R0, #0                                              ; 
    MSR     PSP, R0                                             ;PSP=0 

    LDR     R0, =OSRunning                                      ; 
    MOVS    R1, #1
    STRB    R1, [R0]                                            ;OSRunning=1

    LDR     R0, =NVIC_INT_CTRL                                  ; 
    LDR     R1, =NVIC_PENDSVSET
    STR     R1, [R0]                                            ;NVIC_INT_CTRL=NVIC_PENDSVSET,即触发一次PendSV中断。

    CPSIE   I                                                   ; 开启中断
OSStartHighRdy由OSStart()调用,用来启动最高优先级任务,当然任务必须在OSStart()调用前被创建。其中PSP=0是用来告诉具体的任务切换程序这是第一次进行任务切换,做过切换后PSP就不会是0了。需要注意的是,程序中出现了=NVIC_SYSPRI14,NVIC_SYSPRI14表示0xE000ED22这个数,所以LDR     R0, =NVIC_SYSPRI14这条指令就相当于是吧0xE000ED22这个地址保存到R0中,即RO指向0xE000ED22这个地址中的内容,不加“=”会把0xE000ED22地址中所存储的内容放到R0中去。
另外,需要说明的是在CM3内核中,真正的任务之间的切换是由PendSV中断来处理的,而不是OSCtxSw或者OSIntCtxSw。
既然讲到了OSCtxSw和OSIntCtxSw,那就来讲讲它们。代码如下:
OSCtxSw
    LDR     R0, =NVIC_INT_CTRL                                  ; 
    LDR     R1, =NVIC_PENDSVSET
    STR     R1, [R0]                  ;NVIC_INT_CTRL=NVIC_PENDSVSET,即触发一次PendSV中断,进行任务调度。
    BX      LR
OSIntCtxSw
    LDR     R0, =NVIC_INT_CTRL                             ; 
    LDR     R1, =NVIC_PENDSVSET
    STR     R1, [R0]                  ;NVIC_INT_CTRL=NVIC_PENDSVSET,即触发一次PendSV中断,进行任务调度。
    BX      LR
这两位是孪生兄弟,长得一模一样,但是它们的含义却不相同。OSCtxSw用于正常的任务切换,切位任务级调度,而OSIntCtxSw用于在中断结束后进行任务调度,称为中断级调度。
最后我们来讲讲真正的任务切换函数OS_CPU_PendSVHandler函数。在此之前先来说明一下CM3的寄存器。
CM3有通用目的寄存器R0~R12,用于暂时存储数据;堆栈指针寄存器R13;链接寄存器R14,即LR;程序寄存器R15,存储着下一条要执行的指令的地址。
函数代码如下: 
OS_CPU_PendSVHandler
    CPSID   I                              ; 关闭中断
    MRS     R0, PSP                        ; PSP is process stack pointer
    CBZ     R0, OS_CPU_PendSVHandler_nosave         ; PSP是否为0,不为0继续执行,为0则ti奥转到OS_CPU_PendSVHandler_nosave处,即判断是否为第一次进行任务切换。

    SUBS    R0, R0, #0x20                  ; 切换到存储R4~R11寄存器的堆栈地址
    STM     R0, {R4-R11}                   ; 存储R4~R11寄存器

    LDR     R1, =OSTCBCur                  ; OSTCBCur->OSTCBStkPtr = SP;
    LDR     R1, [R1]
    STR     R0, [R1]                       ; OSTCBCur->OSTCBStkPtr指向SP,即切换到下一个任务。
OS_CPU_PendSVHandler_nosave
    PUSH    {R14}                                        ; Save LR exc_return value
    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}        ; 恢复R4~R11
    ADDS    R0, R0, #0x20       ; 调整任务堆栈指针
    MSR     PSP, R0             ; Load PSP with new process SP
    ORR     LR, LR, #0x04       ; Ensure exception return uses process stack
    CPSIE   I
    BX      LR                  ; Exception return will restore remaining context
由于CM3内核自动将R0~R3,R12保存到任务堆栈,所以只需要手动保存R4~R11即可。其他说明见代码注释。
<think>好的,我现在要解决用户在Keil编译时遇到的os_cpu_a.asm文件的A1167E错误。首先,我需要了解这个错误的具体原因。用户提到的错误信息是A1167E: Invalid line start,这通常与汇编文件中的语法问题有关。根据Keil的文档,这种错误可能出现在行首有不合法的字符,比如空格或Tab使用不当,或者在指令前缺少必要的符号。 接下来,我需要查看用户提供的引用内容,特别是涉及到UCOSII的相关代码和设置。引用[3]提到修改sys.h文件中的SYSTEM_SUPPORT_UCOS宏定义为1,这说明用户可能在配置UCOSII时进行了相关设置,但可能没有正确配置汇编文件。引用[2]中的OSMemGet和OSMemPut函数属于UCOSII的内存管理模块,可能涉及到汇编代码的调用,但问题更可能出在汇编文件本身的语法上。 用户提供的引用[1]是关于CPU中断处理的代码片段,这可能与操作系统底层相关,但不确定是否直接关联到当前的汇编错误。不过,汇编文件中的中断服务例程如果有语法错误,也可能导致类似问题。需要检查os_cpu_a.asm中的每一行代码,尤其是行首是否有不符合ARM汇编语法的内容。 常见的解决方案可能包括检查行首的空格和Tab是否正确,确保指令对齐,以及处理可能存在的字符编码问题。另外,UCOSII移植到不同处理器时,可能需要修改特定的汇编指令或伪指令,比如处理器模式切换、堆栈操作等,这些如果格式不正确,也会触发A1167E错误。 还需要考虑用户是否使用了正确版本的UCOSII,以及Keil的编译器是否支持该版本的语法。有些旧版本的UCOSII可能需要针对特定编译器进行调整,比如使用不同的注释符号或指令格式。 最后,建议用户检查具体的错误行号,根据Keil输出的错误信息定位到os_cpu_a.asm中的具体行,查看是否有明显的语法错误,比如缺少冒号、错误的操作符,或者使用了不被支持的汇编指令。</think>### Keil UCOSII os_cpu_a.asm A1167E 错误解决方案 #### 错误原因分析 A1167E错误表示**汇编文件行首存在非法字符或语法格式错误**,常见于以下情况: 1. **行首包含多余空格或Tab**:ARM汇编器要求指令部分必须从行首开始,不能有缩进[^1] 2. **伪指令使用错误**:如`MACRO`/`MEND`未正确配对,或伪指令缺少参数 3. **特殊符号冲突**:例如`$`符号未转义导致语法解析错误 4. **编码格式问题**:文件使用非ASCII编码保存(如UTF-8 BOM格式) --- #### 具体解决步骤 1. **检查错误行定位** - 查看Keil编译输出的**具体行号**,例如: ``` os_cpu_a.asm(123): error A1167E: Invalid line start ``` 2. **检查行首格式** ```asm ; 错误示例(含缩进) LDR R0, =OS_CPU_IRQ_ISR ; 行首有空格 ; 正确格式 LDR R0, =OS_CPU_IRQ_ISR ; 指令从行首开始 ``` 3. **检查伪指令语法** ```asm ; 错误示例(缺少参数) MACRO ; 正确格式 MACRO $HandlerLabel ``` 4. **处理特殊符号** ```asm ; 错误示例(直接使用$) MOV R0, #$8000 ; 正确格式(使用16进制标识符) MOV R0, #0x8000 ``` 5. **检查文件编码** - 用记事本打开`os_cpu_a.asm` - 另存为时选择编码格式为**ANSI** --- #### UCOSII移植注意事项 1. 确认`OS_CPU_A.ASM`是否为对应处理器版本(如Cortex-M3需使用专用移植文件) 2. 检查中断服务程序是否包含正确的EXPORT声明: ```asm EXPORT OS_CPU_PendSVHandler OS_CPU_PendSVHandler ; 上下文切换代码 ``` 3. 若使用旧版UCOSII,需添加`PRESERVE8`指令: ```asm AREA |.text|, CODE, READONLY, ALIGN=2 PRESERVE8 ; 8字节对齐要求 ``` --- #### 验证方法 1. 新建空白汇编文件测试基础语法 2. 逐段注释UCOSII代码排查问题段落 3. 参考官方移植模板对比差异(如TI、ST官方BSP中的UCOSII移植文件
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值