TM4C123GX - 为什么 16/32 位 Timer0A 中断不工作?

TM4C123GX - why isn't 16/32-bit Timer0A interrupt working?

我正在尝试使用 16/32 位 Timer0A 在程序集中对 TM4C123GX Launchpad 进行编程以定期打开外部 LED。我按照用户手册 (TM4C123GH6PM) 中的说明进行操作,但由于某种原因它无法正常工作。我打开定时器时钟,打开 NVIC 中的中断并设置中断级别,将定时器设置为超时中断,并配置其他部分。 GPIO 部分工作正常。 当我读取超时的 RIS 位(GPTMRIS 中的 TATORIS 位)时,它始终设置为 1,即使在 GPTMICR 中清除它之后也是如此。为什么中断不起作用?这是我的代码。

startup_rvmdk.S:

    ;...
    EXPORT  __Vectors
__Vectors
    DCD     StackMem + Stack            ; Top of Stack
    DCD     Reset_Handler               ; Reset Handler
    DCD     NmiSR                       ; NMI Handler
    ;...
    DCD     IntDefaultHandler           ; ADC Sequence 3
    DCD     IntDefaultHandler           ; Watchdog timer
    DCD     Timer0A_Handler             ; Timer 0 subtimer A
    DCD     IntDefaultHandler           ; Timer 0 subtimer B
    ;...
;I added the Timer0A_Handler here. 
Timer0A_Handler PROC

    EXPORT Timer0A_Handler

    ;write "high" to data register for port F pin 1 to turn on red LED. GPIODATA
    LDR r0, =AHB_PORTB
    LDR r1, =PB5_MASK ;Get RAM address of PB5 mask value
    LDR r2,[r1] ;Grab value of PB5 mask
    EOR r2, r2, #0xF0   ;Toggle mas to toggle PB5
    STR r2,[r1] ;Store the toggled mask to properly toggle next time
    STR r2,[r0,#GPIODATAPB5]
    ;LDR r2,[r0,#GPIODATAPB5]


    ;Clear RIS bit
    LDR r0, =NVIC_BASE
    LDR r1,[r0, #GPTMICR]

    BFC r1,#0,#1
    ORR r1,r1,#1

    STR r1,[r0, #GPTMICR]

    ;return to __main
    BX LR

    ENDP
    ;...

my_Constants.S:

        AREA My_Constants, CODE, READONLY
        THUMB

        ;defining addresses here for practice

    ;general base addresses
    AHB_PORTB   EQU 0x40059000
    SYS_CONTROL EQU 0x400FE000
    NVIC_BASE   EQU 0xE0000000
    SYS_PERIPH  EQU 0xE000E000
    TIMER16_0   EQU 0x40030000

    ;offsets
        ;RCGC Run-time offsets
    RCGCGPIO    EQU 0x608
    RCGCTIMER   EQU 0x604
        ;GPIO offsets
    GPIOHBCTL   EQU 0x06C
    GPIOCTL     EQU 0x52C
    GPIODIR     EQU 0X400
    GPIOAFSEL   EQU 0X420
    GPIODR2R    EQU 0X500
    GPIOPUR     EQU 0X510
    GPIODEN     EQU 0X51C
    GPIODATAPB5 EQU 0x3FC;0X080
        ;SysTick offsets
    STCTRL      EQU 0X010
    STRELOAD    EQU 0X014
    STCURRENT   EQU 0X018   
    SYSPRI3     EQU 0XD20
        ;NVIC offsets
    NVIC_ENn    EQU 0x100
    NVIC_DISn   EQU 0x180
    NVIC_PRIn   EQU 0x440
        ;Timer offsets
    GPTMCTL     EQU 0x00C
    GPTMCFG     EQU 0x000
    GPTMTAMR    EQU 0x004
    GPTMTBMR    EQU 0x008
    GPTMTAILR   EQU 0x028
    GPTMTBILR   EQU 0x02C
    GPTMTAPR    EQU 0x038
    GPTMTBPR    EQU 0x03C
    GPTMIMR     EQU 0x018
    GPTMRIS     EQU 0x01C
    GPTMICR     EQU 0x024


        END

my_Variables.S:

        AREA My_Variables, DATA, READWRITE
        THUMB

            EXPORT PB5_MASK
    PB5_MASK DCD 240;0x000000F0

        END

main.S:

        ;Include constants I define to main 
        INCLUDE my_Constants.s  
        IMPORT PB5_MASK


        AREA |.text|, CODE, READONLY
        THUMB
        EXPORT __main
        ENTRY

    ;Input arguments:
    ;   r0: IRQn
    ;   r1: 1 = Enable, 0 = Disable
    NVIC_Init   PROC
        PUSH {r4, LR} ;Push context onto stack

        AND r2, r0, #0x1F   ;Bitoffset
        MOV r3, #1
        LSL r3, r3, r2 ;Shift enable/disable bit to correct bit position
        LDR r4, =NVIC_BASE ;NVIC base address

        CMP r1, #0 ;Want to enable to disable?
        LDRNE r1, =NVIC_ENn
        LDREQ r1, =NVIC_DISn

        ADD r1, r4, r1 ;add offset to base address
        LSR r2, r0, #5 ;find register number n. IRQn/32
        LSL r2, r2, #2 ;Wordoffset with 0 extend. Finds register n address

        STR r3, [r1, r2]

        POP {r4,LR}
        BX LR

        ENDP

    ;Input Arguments:
    ;   r0: IRQn
    ;   r1: Priority level 0~7
    NVIC_Priority   PROC
        PUSH {r4, LR} ;Push context onto stack

        AND r2, r0, #0x03 ;Bitoffset for TBB (case index)

        LDR r3, =NVIC_BASE
        LDR r4, =NVIC_PRIn
        ADD r3, r3, r4
        LSR r4, r0, #2 ;
        LSL r4, r4, #2

        TBB [PC, r2]

    BranchTable
        DCB (Case_0 - BranchTable)/2 ;index 0: Case_0
        DCB (Case_0 - BranchTable)/2 ;index 1: Case_1
        DCB (Case_0 - BranchTable)/2 ;index 2: Case_2
        DCB (Case_0 - BranchTable)/2 ;index 3: Case_3
        ALIGN

    Case_0
        LSL r1, r1, #5 ;Shift to [7:5]
        B exit
    Case_1
        LSL r1, r1, #13 ;Shift to [15:13]
        B exit
    Case_2
        LSL r1, r1, #21 ;Shift to [23:21]
        B exit
    Case_3
        LSL r1, r1, #29 ;Shift to [31:29]
        B exit

    exit
        STR r1, [r3, r4]

        POP {r4,LR}
        BX LR

        ENDP


    GPIO_Init   PROC
        ;Save context
        PUSH {LR}


        ;select APB. GPIOHBCTL
        LDR r0, =SYS_CONTROL
        LDR r1,[r0, #GPIOHBCTL]

        ORR r1, r1, #(1<<1) ;Enable port B AHB instead. "Note that GPIO can only be accessed through the AHB aperture

        STR r1,[r0, #GPIOHBCTL]


        ;set to output. GPIODIR
        LDR r0, =AHB_PORTB
        LDR r1,[r0,#GPIODIR]

        ORR r1, r1, #(1<<5);pin5

        STR r1,[r0,#GPIODIR]


        ;set mode to GPIO (nor alternate function). GPIOAFSEL
        LDR r0, =AHB_PORTB
        LDR r1,[r0,#GPIOAFSEL]

        BFC r1,#0,#8 ;clears fields. 0 = GPIO

        STR r1,[r0,#GPIOAFSEL]


        ;to drive strength to 2mA. GPIODR2R
        LDR r0, =AHB_PORTB
        LDR r1,[r0,#GPIODR2R]

        ORR r1, r1, #(1<<5);pin5

        STR r1,[r0,#GPIODR2R]


        ;set to pull up. GPIOPUR
        LDR r0, =AHB_PORTB
        LDR r1,[r0,#GPIOPUR]

        ORR r1, r1, #(1<<5) ;pin5

        STR r1,[r0,#GPIOPUR]


        ;enable digital output. GPIODEN
        LDR r0, =AHB_PORTB
        LDR r1,[r0,#GPIODEN]

        ORR r1,r1, #(1<<5);pin 1 = digital output enable

        STR r1,[r0,#GPIODEN]


        ;write "high" to data register for port B pin 5 to turn on red LED. GPIODATA
        LDR r0, =AHB_PORTB
        LDR r1,[r0,#GPIODATAPB5]
        LDR r2, =PB5_MASK   ;get RAM address of PB5 mask (pointer)
        LDR r3,[r2] ;get the value of PB5 mask

        ORR r1, r1, r3  ;Set PB5 to 'high'
        STR r1,[r0,#GPIODATAPB5]


        ;Restore context
        POP {LR}
        BX LR
        ENDP


    ;Initializes RCGC for GPIO and Timer0 at run-time
    RCGC_Init   PROC
        ;Save context
        PUSH {LR}

        ;Initialize Timer0 for run-time via RCGC
        LDR r0, =SYS_CONTROL
        LDR r1,[r0, #RCGCTIMER]

        BFC r1,#0,#6 ;clear fields [5:0]
        ORR r1, r1, #1 ;R0 = 1: enable Timer0

        STR r1,[r0, #RCGCTIMER]


        ;Initialize GPIO PortB for run-time via RCGC
        ;Enable clock. RCGCGPIO
        LDR r0, =SYS_CONTROL 
        LDR r1,[r0,#RCGCGPIO]

        ORR r1, r1, #(1<<1) ;enable port B clock(bit 5)

        STR r1,[r0,#RCGCGPIO]


        ;Restore context
        POP {LR}
        BX LR
        ENDP

    ;Specifically for Timer0. Generalize this function      
    TIMER_Init  PROC
        ;Save context
        PUSH {LR}

        ;Disable Timer0A
        LDR r1, =TIMER16_0
        LDR r2,[r1, #GPTMCTL]

        BFC r2,#0,#1 ;clear TAEN to disable Timer0A

        STR r2,[r1, #GPTMCTL]


        ;Seperate timers A & B for 16 bit timers
        LDR r1, =TIMER16_0
        LDR r2,[r1, #GPTMCFG]

        BFC r2,#0,#2 ;Clear bits
        ORR r2, r2, #0x4 ;Seperate timers

        STR r2,[r1, #GPTMCFG]


        ;Set timer mode to periodic
        LDR r1, =TIMER16_0
        LDR r2,[r1, #GPTMTAMR]

        BFC r2,#7,#1 ;No snapshot mode

        BFC r2,#5,#1 ;Disable interrupts when counter == CCR
        ;ORR r2,r2,#(1<<5);Enable interrupts when counter == CCR

        BFC r2,#4,#1 ;count down

        BFC r2,#0,#2
        ORR r2, r2,#(0x02);Set periodic mode

        STR r2,[r1, #GPTMTAMR]


        ;Set ARR value
        LDR r1, =TIMER16_0
        LDR r2,[r1, #GPTMTAILR]

        BFC r2,#0,#32 ;
        MOV r2,#0x3E42

        STR r2,[r1, #GPTMTAILR]


        ;Set Prescale value
        LDR r1, =TIMER16_0
        LDR r2,[r1, #GPTMTAPR]

        BFC r2,#0,#8
        ORR r2,r2,#0xFF

        STR r2,[r1, #GPTMTAPR]


        ;Set interrupts at timeout
        LDR r1, =TIMER16_0
        LDR r2,[r1, #GPTMIMR]

        BFC r2,#0,#1
        ORR r2,r2,#1;Enable interrupts at timeout

        STR r2,[r1, #GPTMIMR]



        ;Prevents Timer0A from freezing during debug
        LDR r1, =TIMER16_0
        LDR r2,[r1, #GPTMCTL]

        BFC r2,#9,#1
        ORR r2,r2,#(1<<9) ;Set TASTAL to 1 to prevent stopping during debug

        BFC r2,#4,#1
        ORR r2,r2,#(1<<4) ;Set RCTEN to 1 to prevent stopping during debug

        STR r2,[r1, #GPTMCTL]



        ;Enable Timer0A
        LDR r1, =TIMER16_0
        LDR r2,[r1, #GPTMCTL]

        BFC r2,#0,#1 
        ORR r2,r2,#1 ;Set TAEN to enable Timer0A

        STR r2,[r1, #GPTMCTL]

        ;Restore context
        POP {LR}
        BX LR
        ENDP

    __main  PROC

        ;Setup clocks for GPIO and GPTM at run-time
        BL RCGC_Init

        ;Setup GPIO first
        BL GPIO_Init

        ;Enable Timer0A interrupt
        MOV r0, #19; Timer0A is IRQn = 19
        MOV r1, #1; r1 = 1: Enable
        BL NVIC_Init

        ;Setup priority level for Timer0A
        MOV r0, #19; Timer0A is IRQn = 19
        MOV r1, #1 ;priority #1
        BL NVIC_Priority

        ;Setup Timer0A
        BL TIMER_Init

    while
        ;Read interrupt bit to see if it's working...
        LDR r1, =NVIC_BASE
        LDR r2,[r1, #GPTMRIS]

        BFC r2,#1,#31 ;shows only TATORIS bit in r2. If 1 = interrupt. If 0 = no interrupt


        ;Clear RIS bit
        LDR r0, =NVIC_BASE
        LDR r1,[r0, #GPTMICR]

        BFC r1,#0,#1
        ORR r1,r1,#1

        STR r1,[r0, #GPTMICR]

        B while

        ENDP

        END

编辑 1:

我查看了屏蔽中断寄存器 GPTMMIR,寄存器肯定设置为触发中断(一段时间后该位变高),但程序没有进入处理程序。我认为重要的是要说我自己将处理程序的名称输入到向量 table 中。我不知道这是否是原因,但没有处理程序名称 (IntDefaultHandler)。它没有进入处理程序是有原因的吗?谢谢你。

编辑 2:

我也查看了 NVIC 中的 ACTIVE 寄存器,看看中断是否处于活动状态。我注意到我的 NVIC 基址错误,所以根本没有设置中断。我修复了它并尝试调试,但 ACTIVE 显示的 Timer0A 位为 0,即使 GPTMMIS 位设置为 1。

编辑 3:

找到问题了! 我为 NVIC 和 GPTMICR 使用了错误的基地址。中断没有工作,因为我根本没有访问 NVIC 寄存器,中断没有被清除,因为我也没有访问 GPTMICR。我将使用更新的代码进行最终编辑。

编辑 4:

这是更新后的代码。改了四个部分。

my_Constants.S:

NVIC_BASE EQU 0xE0000000

startup_rvmdk.S:

;清除RIS位

LDR r0,=NVIC_BASE

LDR r0,=TIMER16_0

LDR r1,[r0, #GPTMICR]

main.S:

分支表

DCB (Case_0 - BranchTable)/2 ;索引 0: Case_0

DCB (Case_0 - 分支表)/2 ;Case_0->Case_1

DCB (Case_0 - 分支表)/2 ;Case_0->Case_2

DCB (Case_0 - 分支表)/2 ;Case_0->Case_3

对齐

将 NVIC_BASE 的每个实例替换为 SYS_PERIPH。

所以我找到了解决方案。我在使用 NVIC 的基地址时犯了一个错误(两次)。分支 table 也错了,GPTMICR 的基地址也错了。这是更正。

my_Constants.S:

NVIC_BASE EQU 0xE0000000

startup_rvmdk.S:

LDR r0, =NVIC_BASE

;Clear RIS bit
LDR r0, =TIMER16_0
LDR r1,[r0, #GPTMICR]

main.S:

分支表

DCB (Case_0 - BranchTable)/2 ;index 0: Case_0
DCB (Case_1 - BranchTable)/2 ;Case_0->Case_1
DCB (Case_2 - BranchTable)/2 ;Case_0->Case_2
DCB (Case_3 - BranchTable)/2 ;Case_0->Case_3
ALIGN

将 NVIC_BASE 的每个实例替换为 SYS_PERIPH。