21.8. Generic Assembly Code for Interrupt Control

For C/C++ language users, a function library for interrupt control is already provided in the CMSIS. The CMSIS-CORE APIs are included in the device driver libraries from all major microcontroller vendors and is openly accessible. More details of the CMSIS are covered in Chapter 3—Introduction to Embedded Software Development (Section 3.5—Cortex® Microcontroller Software Interface Standard).
For users programming the Cortex-M0 or Cortex-M0+ processor using assembly language, it could be handy to have a set of generic functions for handling interrupt control with the Nested Vectored Interrupt Controller (NVIC).

21.8.1. Enable and Disable Interrupts

The enable and disable of interrupts is quite simple. The following functions “nvic_set_enable” and “nvic_clr_enable” require the interrupt number as input, which is stored in R0 before the function call.
    ;-------------------------
    ; Enable IRQ
    ; - input R0 : IRQ number. E.g. IRQ#0 = 0
    ALIGN
nvic_set_enable FUNCTION
    PUSH    {R1, R2}
    LDR     R1,=0xE000E100 ; NVIC SETENA
    MOVS    R2, #1
    LSLS    R2, R2, R0
    STR     R2, [R1]
    POP     {R1, R2}
    BX      LR     ; Return
    ENDFUNC
    ;-------------------------
    ; Disable IRQ
    ; - input R0 : IRQ number. E.g. IRQ#0 = 0
    ALIGN
nvic_clr_enable FUNCTION
    PUSH    {R1, R2}
    LDR     R1,=0xE000E180 ; NVIC CLRENA
    MOVS    R2, #1
    LSLS    R2, R2, R0
    STR     R2, [R1]
    POP     {R1, R2}
    BX      LR     ; Return
    ENDFUNC
    ;-------------------------
To use the functions, just put the interrupt number in R0, and call the function. For example,
MOVS    R0, #3 ; Enable Interrupt #3
BL      nvic_set_enable
The FUNCTION and ENDFUNC keywords are used to identify start and end of a function in ARM® assembler (including Keil MDK-ARM). This is optional. The “ALIGN” keyword ensures correct alignment of the starting of the function.

21.8.2. Set and Clear Interrupt Pending Status

The assembly functions for setting and clear of interrupt pending status are very similar to the ones for enable and disable interrupts. The only changes are labels and NVIC register address values.
    ;-------------------------
    ; Set IRQ Pending status
    ; - input R0 : IRQ number. E.g. IRQ#0 = 0
    ALIGN
nvic_set_pending FUNCTION
    PUSH    {R1, R2}
    LDR     R1,=0xE000E200 ;  NVIC SETPEND
    MOVS    R2, #1
    LSLS    R2, R2, R0
    STR     R2, [R1]
    POP     {R1, R2}
    BX      LR         ; Return
    ENDFUNC
    ;-------------------------
    ; Clear IRQ Pending
    ; - input R0 : IRQ number. E.g. IRQ#0 = 0
    ALIGN
nvic_clr_pending FUNCTION
    PUSH    {R1, R2}
    LDR     R1,=0xE000E280 ; NVIC CLRPEND
    MOVS    R2, #1
    LSLS    R2, R2, R0
    STR     R2, [R1]
    POP     {R1, R2}

    BX      LR         ; Return
    ENDFUNC
    ;-------------------------
Note that sometimes clearing of pending status of an interrupt might not be enough to stop the interrupt from happening. If the interrupt source generates an interrupt request continuously (level output), then the pending status could remain high even if you try to clear it at the NVIC.

21.8.3. Setting Up Interrupt Priority Level

The assembly function to set up priority level for as interrupt is a bit more complex. First, it requires two input parameters: the interrupt number and the new priority level. Secondly, the priority level register address has to be calculated as there are up to eight priority registers. And finally, the function needs to perform a read-modify-write operation to the correct byte inside the 32-bit priority level register, as the priority level registers are word access only.
    ;-------------------------
    ; Set interrupt priority
    ; - input R0 : IRQ number. E.g. IRQ#0 = 0
    ; - input R1 : Priority level
    ALIGN
nvic_set_priority FUNCTION
    PUSH    {R2-R5}
    LDR     R2,=0xE000E400 ; NVIC Interrupt Priority #0
    MOV     R3, R0  ; Make a copy of IRQ number
    MOVS    R4, #3  ; clear lowest  two bit of IRQ number
    BICS    R3, R4
    ADDS    R2, R3  ; address of priority register in R2
    ANDS    R4, R0  ; byte number (0 to 3) in priority register
    LSLS    R4, R4, #3 ; Number of bits to shift for priority & mask
    MOVS    R5, #0xFF  ; byte mask
    LSLS    R5, R5, R4 ; byte mask shift to right location
    MOVS    R3, R1
    LSLS    R3, R3, R4 ; Priority shift to right location
    LDR     R4, [R2]   ; Read existing priority level
    BICS    R4, R5     ; Clear existing priority value
    ORRS    R4, R3     ; Set new level
    STR     R4, [R2]   ; Write back
    POP     {R2-R5}
    BX      LR         ; Return
    ENDFUNC
    ;-------------------------
In most applications, however, you can use a much simpler code to set up priority levels of multiple interrupts in one go at the beginning of the program. For example, you can predefine the priority levels in a table of constant values, and then copy it to the NVIC priority level registers using a short instruction sequence:
    LDR     R0,=PrioritySettings ; address of priority setting table
    LDR     R1,=0xE000E400 ; address of interrupt priority registers
    LDMIA   R0!,{R2-R5} ; Read Interrupt Priority 0-15
    STMIA   R1!,{R2-R5} ; Write Interrupt Priority 0-15
    LDMIA   R0!,{R2-R5} ; Read Interrupt Priority 16-31
    STMIA   R1!,{R2-R5} ; Write Interrupt Priority 16-31
    
    ALIGN 4 ; Ensure that the table is word aligned
PrioritySettings  ; Table of priority level values (example values)
    DCD     0xC0804000 ; IRQ  3- 2- 1- 0
    DCD     0x80808080 ; IRQ  7- 6- 5- 4
    DCD     0xC0C0C0C0 ; IRQ 11-10- 9- 8
    DCD     0x40404040 ; IRQ 15-14-13-12
    DCD     0x40404080 ; IRQ 19-18-17-16
    DCD     0x404040C0 ; IRQ 23-22-21-20
    DCD     0x4040C0C0 ; IRQ 27-26-25-24
    DCD     0x004080C0 ; IRQ 31-30-29-28 
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.149.27.202