打印

RealView MDK中宏的使用方法

[复制链接]
1705|0
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
xiong_gang|  楼主 | 2008-5-6 09:28 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式

宏可以减少源代码长度,结构清晰,可以给宏起个与功能相关的名字,增加可读性。另外在RealView MDK中可以用宏来处理中断。即把一个普通函数作为中断服务子程序。例如AT91RM9200这块芯片采用的ARM9内核。ARM9核有多种模式,进入中断时需要进行模式切换。因此中断服务子程序时,除了保护现场之外还需要在进入中断之间保存当前模式,在中断子程序执行完时恢复到中断之前的模式。但是我们知道普通函数并不会保存进入之前的模式也不会恢复到进入之前的模式。为将一个普通函数改造为一个中断函数,我们需要在进入函数之前和退出函数之后加上一小段汇编代码来进行模式的保存和恢复。这就是ARM9核惯用的中断处理方法之一。下面介绍在MDK中如何使用宏。
宏的定义格式:
MACRO
{$label}  macroname   {$parameter}{$parameter }…
          ;宏定义体
MEND
其中  $label 宏指令被展开时,label可被替换成相应的符号,通常为一个标号在一个符号前使用$表示被汇编时使用相应的值替代$后的符号。
Macroname 所定义的宏的名称。
$parameter 宏指令的参数。当宏指令被展开时将被替换成相应的值,类似于函数中的形参。

下面举个例子,这个例子中先定义一个宏,然后调用该宏,并显示调用该宏之后展开的汇编代码。让读者深刻理解宏的使用方法。
        MACRO
$Lab    DivMod  $Div,$Top,$Bot,$Temp
        ASSERT  $Top <> $Bot             ; Produce an error message if the
        ASSERT  $Top <> $Temp            ; registers supplied are
        ASSERT  $Bot <> $Temp            ; not all different
        IF      "$Div" <> ""
            ASSERT  $Div <> $Top         ; These three only matter if $Div
            ASSERT  $Div <> $Bot         ; is not null ("")
            ASSERT  $Div <> $Temp        ;
        ENDIF
$Lab
        MOV     $Temp, $Bot              ; Put divisor in $Temp
        CMP     $Temp, $Top, LSR #1      ; double it until
90      MOVLS   $Temp, $Temp, LSL #1     ; 2 * $Temp > $Top
        CMP     $Temp, $Top, LSR #1
        BLS     %b90                     ; The b means search backwards
        IF      "$Div" <> ""             ; Omit next instruction if $Div is null
            MOV     $Div, #0             ; Initialize quotient
        ENDIF
91      CMP     $Top, $Temp              ; Can we subtract $Temp?
        SUBCS   $Top, $Top,$Temp         ; If we can, do so
        IF      "$Div" <> ""             ; Omit next instruction if $Div is null
            ADC     $Div, $Div, $Div     ; Double $Div
        ENDIF
        MOV     $Temp, $Temp, LSR #1     ; Halve $Temp,
        CMP     $Temp, $Bot              ; and loop until
        BHS     %b91                     ; less than divisor
        MEND

调用该宏语句:
ratio  DivMod  r0,r5,r4,r2

该宏展开后的汇编代码如下:
        ASSERT  r5 <> r4                   ; Produce an error if the
        ASSERT  r5 <> r2                   ; registers supplied are
        ASSERT  r4 <> r2                   ; not all different
        ASSERT  r0 <> r5                   ; These three only matter if $Div
        ASSERT  r0 <> r4                   ; is not null ("")
        ASSERT  r0 <> r2                   ;
ratio
        MOV     r2, r4                     ; Put divisor in $Temp
        CMP     r2, r5, LSR #1             ; double it until
90      MOVLS   r2, r2, LSL #1             ; 2 * r2 > r5
        CMP     r2, r5, LSR #1
        BLS     %b90                       ; The b means search backwards
        MOV     r0, #0                     ; Initialize quotient
91      CMP     r5, r2                     ; Can we subtract r2?
        SUBCS   r5, r5, r2                 ; If we can, do so
        ADC     r0, r0, r0                 ; Double r0

        MOV     r2, r2, LSR #1             ; Halve r2,
        CMP     r2, r4                     ; and loop until
        BHS     %b91                       ; less than divisor
看完以上例子,读者应该对MDK下宏的使用很清晰了。
下面以AT91RM9200为例,说明如何将一个普通函数用作中断服务子程序。
普通函数定义如下:
void AT91F_ST_HANDLER(void)
{
volatile int StStatus;
    // Read the system timer status register
    StStatus = *(AT91C_ST_SR);
    StTick++;
}
处理中断的宏为:
    MACRO 
    IRQHandle $in_handle,$out_handle
    EXTERN $in_handle
    GLOBAL $out_handle


;#- Adjust and save LR_irq in IRQ stack
    sub         r14, r14, #4
    stmfd       sp!, {r14}

;#- Write in the IVR to support Protect Mode
;#- No effect in Normal Mode
;#- De-assert the NIRQ and clear the source in Protect Mode
    ldr         r14, =AT91C_BASE_AIC
    str         r14, [r14, #AT91C_AIC_IVR-AT91C_BASE_AIC]

;#- Save SPSR and r0 in IRQ stack
    mrs         r14, SPSR
    stmfd       sp!, {r0, r14}

;#- Enable Interrupt and Switch in SYS Mode
    mrs         r0, CPSR
    bic         r0, r0, #I_BIT
    orr         r0, r0, #ARM_MODE_SYS
    msr         CPSR_c, r0
    
;#- Save scratch/used registers and LR in User Stack
    stmfd       sp!, { r1-r3, r12, r14}
    
    ldr     r1,     =$in_handle
    mov     lr,     pc
    bx      r1

;#- Restore scratch/used registers and LR from User Stack
    ldmia       sp!, { r1-r3, r12, r14}
    
;#- Disable Interrupt and switch back in IRQ mode
    mrs         r0, CPSR
    bic         r0, r0, #ARM_MODE_SYS
    orr         r0, r0, #I_BIT|ARM_MODE_IRQ
    msr         CPSR_c, r0

;#- Mark the End of Interrupt on the AIC
    ldr         r0, =AT91C_BASE_AIC
    str         r0, [r0, #AT91C_AIC_EOICR-AT91C_BASE_AIC]

;#- Restore SPSR_irq and r0 from IRQ stack
    ldmia       sp!, {r0, r14}
    msr         SPSR_cxsf, r14

;#- Restore adjusted  LR_irq from IRQ stack directly in the PC
    ldmia       sp!, {pc}^
    
    MEND
    GLOBAL      AT91F_ST_ASM_HANDLER 
AT91F_ST_ASM_HANDLER    
    IRQHandle AT91F_ST_HANDLER,AT91F_ST_ASM_HANDLER
以上红色部分即为利用定义的宏来处理中断。其中AT91F_ST_ASM_HANDLER是一个全局标号,可在其它文件中引用。AT91F_ST_ASM_HANDLER是前面定义的C函数。中断发生时道先找到标号AT91F_ST_ASM_HANDLER并执行其展开的汇编代码,保存PC及模式之后跳到C函数void AT91F_ST_HANDLER(void)。在返回该函数时有恢复PC和模式。这样就完成了一次中断的调用过程。
注:读者可能会注意到宏IRQHandle前面并没有标号,这也是允许的,该标号是可选的。

附:
MDK中还允许直接使用中断函数,而无须使用汇编代码来处理中断。只须在中断服务子程序加上一些关键字来标识它是一个中断服务子程序,这样在编译器编译的时候会自动在服务子程序的入口和出口加上一些与中断相关的现场保护与恢复汇编指令。
中断标识符与您所选的编译器有关。强大的RealView MDK支持三种主流编译器。它们是RealView编译器、Keil CARM编译器以及GNU编译器。在使用不同的编译器时,中断标识符有所不同。现通过举例来说明不同编译器下中断标识符的用法:
RealView编译器:
__irq void IRQ_Handler (void) {
   /* the interrupt code */
}
Keil CARM编译器:
void IRQ_Handler (void) __irq  {
  /* the interrupt code */}
GNU编译器:
void IRQ_Handler (void) __attribute__ ((interrupt)); // Generate Interrupt 

void IRQ_Handler (void) {
   /* the interrupt code */
}
 

相关帖子

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

本版积分规则

2

主题

4

帖子

0

粉丝