创建简单的 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
我的 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