创建简单的 TCNT0 程序 ATMEGA 328 P-PU

Creating simple TCNT0 program ATMEGA 328 P-PU

我的 Arduino + ATMEGA 328 P-PU 初学者程序在 CTC 模式下使用 tcnt0 时遇到问题,我在其中使用 OCR0A 产生的溢出来产生一些可见的延迟。看起来,该计数器以某种方式工作,因为 OC0A 亮起,但是由于 "strange lit of apropriate diode",使用 TOV0 或 OCF0A 进行的延迟不起作用。你能帮我找出程序中的错误吗?感谢大家的帮助!

顺便说一句:您知道 Linux 下一些经过验证的优秀调试器吗?

有错误的程序:

            ; TCCR0 registers addresses
    .equ    tccr0a,     0x44
    .equ    tccr0b,     0x45
    .equ    tcnt0,      0x46
    .equ    ocr0a,      0x47
    .equ    ocr0b,      0x48
    .equ    tifr0,      0x35
    .equ    timsk0,     0x6E

    .equ    io_tccr0a,  0x24
    .equ    io_tccr0b,  0x25
    .equ    io_tcnt0,   0x26
    .equ    io_ocr0a,   0x27
    .equ    io_ocr0b,   0x28
    .equ    io_tifr0,   0x15

            ; PORT registers addresses
    .equ    pinb,       0x23
    .equ    io_pinb,    0x3
    .equ    ddrb,       0x24
    .equ    io_ddrb,    0x4
    .equ    portb,      0x25
    .equ    io_portb,   0x5

    .equ    pinc,       0x26
    .equ    io_pinc,    0x6
    .equ    ddrc,       0x27
    .equ    io_ddrc,    0x7
    .equ    portc,      0x28
    .equ    io_portc,   0x8

    .equ    pind,       0x29
    .equ    io_pind,    0x9
    .equ    ddrd,       0x2A
    .equ    io_ddrd,    0xA
    .equ    portd,      0x2B
    .equ    io_portd,   0xB

            ; SREG and STACK registers addresses
    .equ    sreg,       0x5F
    .equ    io_sreg,    0x3F

    .equ    sph,        0x5E
    .equ    io_sph,     0x3E
    .equ    spl,        0x5D
    .equ    io_spl,     0x3D

            ; Constants
    .equ    RAMEND,     0x8FF       ; End of SRAM for ATMEGA328P
    .equ    MILIS_VALUE,    0x7C        ; EDIT: FUCK THE TIME, TCNT0 HAS NO 128 PRESCALER, PRESCALER WILL BE 1024 JUST BECAUSE I WANT PREVIOUS COMMENT HAS NO MEANING NOW ---> ; Time in milisecond counted for 16MHz with 128 prescaler        





        .org    0   ; RESTART interupt vector
    rjmp    INITIALIZATION  ; Go to the start of the program

        .org    0x1C    ; TOV0 interupt vector
    rjmp    DELAY       ; Create visible delay from counter interval


        .org    0x34    ; Start the program right behind interupt vector table
INITIALIZATION:
    ldi r16,    hi8(RAMEND) ; High 8 bit value for SPH
    out io_sph, r16     ; Store SPH value to SRAM
    ldi r16,    lo8(RAMEND) ; Low 8 bit value for SPL
    out io_spl, r16     ; Store SPL value to SRAM
    clr r16         ; R16   = 0
    out io_sreg, r16        ; SREG  = 0

    ldi r16,    0xF0        ; Only high nibble is set
    out io_ddrc, r16        ; High nibble in PORTB is set as output because of possibility to check possible error with ease
    ldi r16,    0xF0        ; Byte for Output
    out io_ddrd, r16        ; Pin OC0A and other pins (PORTD.7-4 included) is set as output
    rcall   TOOGLE_LED      ; Inicialization LED and delay register(s) to the start condition

TCNT_INIT:              ; TCNT0 Initialization - not used label
    clr r16         ; Prepare value for counter stop, no FOC, third mode bit WGM2 is 0
    out io_tccr0b,  r16 ; Stop counter
    out io_tcnt0,   r16 ; Counting register = 0
    ldi r16,    0x42        ; Set OC0A to toogle and CTC mode for TCNT0
    out io_tccr0a, r16      ; Set control register a for timer 0
    ldi r16,    0x1     ; Interupt on TOV0
    sts timsk0, r16     ; Enable overflow interupt
    ldi r16,    MILIS_VALUE ; Count number for 125 count cycles
    out io_ocr0a, r16       ; Set top value for counter 0
    sei             ; Enable ingerupts globaly
    out io_tccr0b, 0x05     ; Set divisor to 1024, counter is running now!

MAIN_LOOP:
    rjmp    MAIN_LOOP       ; Infinite loop because of getting interupt from TCNT0

DELAY:                  ; TOV0 interupt subroutine
    in  r20,    io_sreg
    clr r21
    out io_sreg, R21
    inc r18
    cpi r18,    100
    brlt    END_DELAY
    out io_sreg, R21
    clr r18
    inc r17
    cpi r17,    100
    brlt    END_DELAY
    out io_sreg, R21
    clr r17
    inc r19
    cpi r19,    1
    brlt    END_DELAY
    rcall   TOOGLE_LED  
    out io_sreg, r20    
END_DELAY:
    reti    

TOOGLE_LED:
    ldi r17, 0x8        ; The highest bit of the lowest nibble is set for xor mask
    in  r16, io_portc       ; Get actual value of port B
    eor r16, r17        ; Toogle the highest pin of port B
    out io_portc, r16       ; write changed value to the port B
    clr r17         ; R17 = 0 <--- register is used for delay so it must be reseted
    clr r18         ; R18 = 0 <--- register is used for delay so it must be reseted
    clr r19         ; R19 = 0 <--- register is used for delay so it must be reseted
    ret

我会是高级程序员,之前有人会在这里回答帮助。 ;-)

代码中有几处错误。

1) 我不应该使用 TOV0 中断,但我必须在 TCNT_INIT 部分设置 OCIE0A,因此使用适当的中断向量(我部分在错误的代码中做了什么,因为我已经测试了两者,你可以见旧评论)。

2) 在 TCNT_INIT 序列的末尾,我尝试在 CTC 模式下 运行 计数器。这是绝对错误的,因为我把值放到了寄存器地址应该在的地方。因为0x5是寄存器的值,所以编译成功

3) 最严重的错误——中断向量。我直接从数据表中复制了它们,所以我认为它们是绝对正确的。然而,在查找了一些代码示例之后,我看到非常不同的数字作为中断向量。问题是,数据表中的信息显示了中断向量,但是闪存中的每个地址都指向两个字节,因此数据表中的向量地址必须乘以二才能用于汇编程序。

应该工作的代码包含在下面。应该纠正错误。此外,还有几行可以擦除,因为它们用于测试。

祝你有愉快的一天。

            ; TCCR0 registers addresses
    .equ    tccr0a,     0x44
    .equ    tccr0b,     0x45
    .equ    tcnt0,      0x46
    .equ    ocr0a,      0x47
    .equ    ocr0b,      0x48
    .equ    tifr0,      0x35
    .equ    timsk0,     0x6E

    .equ    io_tccr0a,  0x24
    .equ    io_tccr0b,  0x25
    .equ    io_tcnt0,   0x26
    .equ    io_ocr0a,   0x27
    .equ    io_ocr0b,   0x28
    .equ    io_tifr0,   0x15

            ; PORT registers addresses
    .equ    pinb,       0x23
    .equ    io_pinb,    0x3
    .equ    ddrb,       0x24
    .equ    io_ddrb,    0x4
    .equ    portb,      0x25
    .equ    io_portb,   0x5

    .equ    pinc,       0x26
    .equ    io_pinc,    0x6
    .equ    ddrc,       0x27
    .equ    io_ddrc,    0x7
    .equ    portc,      0x28
    .equ    io_portc,   0x8

    .equ    pind,       0x29
    .equ    io_pind,    0x9
    .equ    ddrd,       0x2A
    .equ    io_ddrd,    0xA
    .equ    portd,      0x2B
    .equ    io_portd,   0xB

            ; SREG and STACK registers addresses
    .equ    sreg,       0x5F
    .equ    io_sreg,    0x3F

    .equ    sph,        0x5E
    .equ    io_sph,     0x3E
    .equ    spl,        0x5D
    .equ    io_spl,     0x3D

    .equ    mcucr,      0x55
    .equ    io_mcucr,   0x35

            ; Constants
    .equ    RAMEND,     0x8FF       ; End of SRAM for ATMEGA328P
    .equ    MILIS_VALUE,    0x7C        ; EDIT: FUCK THE TIME, TCNT0 HAS NO 128 PRESCALER, PRESCALER WILL BE 1024 JUST BECAUSE I WANT PREVIOUS COMMENT HAS NO MEANING NOW ---> ; Time in milisecond counted for 16MHz with 128 prescaler        





        .org    0   ; RESTART interupt vector
    jmp INITIALIZATION  ; Go to the start of the program

        .org    0x38    ; OCF0A interupt vector <--- IT LOOKS, THAT ADDRESSES FROM DATASHEET MUST BE DOUBLED BECAUSE OF CALL INSTRUCTION! SEE /usr/lib/avr/include/avr/iom328p.h ,io.h AND OTHER INCLUDE FILES! THIS INFORMATION MUST BE VERIFIED!
    jmp DELAY       ; Create visible delay from counter interval


        .org    0x68    ; Start the program right behind interupt vector table
INITIALIZATION:
    ldi r16,    hi8(RAMEND) ; High 8 bit value for SPH
    out io_sph, r16     ; Store SPH value to SRAM
    ldi r16,    lo8(RAMEND) ; Low 8 bit value for SPL
    out io_spl, r16     ; Store SPL value to SRAM
    clr r16         ; R16   = 0
    out io_sreg, r16        ; SREG  = 0
    out io_mcucr,   r16 ; MCUCR = 0

    ldi r16,    0xFF        ; Only high nibble is set
    out io_ddrc, r16        ; High nibble in PORTB is set as output because of possibility to check possible error with ease
    ldi r16,    0xF0        ; Byte for Output
    out io_ddrd, r16        ; Pin OC0A and other pins (PORTD.7-4 included) is set as output
    rcall   DELAY_INIT      ; Inicialization of DELAY subroutine

TCNT_INIT:              ; TCNT0 Initialization - not used label
    clr r16         ; Prepare value for counter stop, no FOC, third mode bit WGM2 is 0
    out io_tccr0b,  r16 ; Stop counter
    out io_tcnt0,   r16 ; Counting register = 0
    ldi r16,    0x42        ; Set OC0A to toogle and CTC mode for TCNT0
    out io_tccr0a, r16      ; Set control register a for timer 0
    ldi r16,    0x2     ; Interupt on OCIE0A
    sts timsk0, r16     ; Enable overflow created by OCR0A interupt
    ldi r16,    MILIS_VALUE ; Count number for 125 count cycles
    out io_ocr0a, r16       ; Set top value for counter 0
    sei             ; Enable global interupts
    ldi r16, 0x05       
    out io_tccr0b, r16      ; Set divisor to 1024, counter is running now!  <<<-------THERE WAS ANOTHER MISTAKE!!!!
;   in  r16, io_sreg            ; TEST ONLY !!!! SREG test, control of enabling interupts
;   out io_portd, r16           ; TEST ONLY !!!! SREG test, control of enabling interupts
;   sbi io_portc, 0         ; TEST ONLY !!!! Test of signal that DELAY subroutine was activated.    

MAIN_LOOP:
    rjmp    MAIN_LOOP       ; Infinite loop because of getting interupt from TCNT0

DELAY:                  ; TOV0 interupt subroutine
    in  r20,    io_sreg
    sbi io_portc, 0     ; Tell me, that this interupt was working one times at minimum!
    clr r21
    out io_sreg, R21
    inc r18
    cpi r18,    100
    brlt    END_DELAY
    out io_sreg, R21
    clr r18
    inc r17
    cpi r17,    1
    brlt    END_DELAY
    out io_sreg, R21
    clr r17
    inc r19
    cpi r19,    1
    brlt    END_DELAY
    rcall   TOOGLE_LED  
    out io_sreg, r20    
END_DELAY:
    reti    

TOOGLE_LED:
    ldi r17, 0x8        ; The highest bit of the lowest nibble is set for xor mask
    in  r16, io_portc       ; Get actual value of port B
    eor r16, r17        ; Toogle the highest pin of port B
    out io_portc, r16       ; write changed value to the port B
DELAY_INIT:
    clr r17         ; R17 = 0 <--- register is used for delay so it must be reseted
    clr r18         ; R18 = 0 <--- register is used for delay so it must be reseted
    clr r19         ; R19 = 0 <--- register is used for delay so it must be reseted
    ret