как установить переполнение таймера для ATmega328p в сборке?

Я пытался создать программу на ассемблере, которая поворачивает один бит влево или вправо каждый раз, когда происходит прерывание по переполнению таймера. Вот мой код, но он почему-то не работает. Я новичок в этом мире программирования сборки, и я хотел бы, чтобы кто-нибудь помог мне здесь, потому что я застрял. Вот мой код:

/*
 * lab3b.asm
 *
 *  Created: 10/18/2014 8:03:22 PM
 *   Author: Isaac Vilchez
 */ 

.include "m328pdef.inc"
.org 0x0000 jmp main
.org 0x0020 jmp TIM0_OVF_ISR

main:
    .org 0x0033

//Config de Stack Pointer y Timer0
    ldi R16, high(RAMEND)
    out SPH, R16
    ldi R16, low(RAMEND)
    out SPL, R16
    sei

//Inicializando los puertos D y B como salida
ldi r16, 0x00
out DDRC, r16
ldi r16, 0xFF
out DDRD, r16
out DDRB, r16

//Configurando timer0 al intervalo maximo de duracion al timer0
    ldi R19, 0x00
    out TCNT0, R19 //Para que el conteo empiece en 0
    ldi R20, 0x05
    out TCCR0B, R20
    ldi R23, 0x01
    sts TIMSK0, R23 //habitlita el timer0

//Registros proposito general
ldi r16, 0x01   ;registro puerto D
ldi r22, 0x10   ;mascara para ejecutar rot_izqPB
ldi r23, 0x80   ;mascara PD
ldi r20, 0x01   ;registro puerto B
ldi r21, 0x00   ;mascara de 0

out PORTB, r21  ;Posicion inicial puerto B
out PORTD, r21  ;Posicion inicial puerto D

//Cargando a R16 con 0x01 para comenzar la rotacion hacia la izquierda
TIM0_OVF_ISR:
    in r24, PINC    ;Se guarda el valor del puerto C en R24
    andi r24, 0x01  ;Se enmascara el puerto C
    cpi r24, 0x01   ;Se compara con 0x01 para ver su estado
    breq rot_derPB  ;Si esta encendido, rota a la derecha
    rjmp rot_izqPD  ;Si esta apagado rota a la izquierda


//Rotacion hacia la izquierda
rot_izqPD:
    ldi r16, 0x01
    rjmp rot_izqPD1

rot_izqPD1:
    out PORTD, r16
    cpi r16, 0x00
    breq rot_izqPB
    ROL r16
    rjmp wait

//Rotacion hacia la izquierda
rot_izqPB:
    ldi r20, 0x01
    rjmp rot_izqPB1

rot_izqPB1:
    out PORTB, r20
    LSL r20
    cp r20, r22
    breq rot_izqPB1
    rjmp wait

//Rotacion hacia la derecha
rot_derPB:
    ldi r16, 0x08
    rjmp rot_derPB1

rot_derPB1:
    out PORTB, r16
    LSR r16
    cpi r16, 0x00
    breq rot_derPD
    rjmp wait

//Rotacion hacia la derecha
rot_derPD:
    ldi r16, 0x80

rot_derPD1:
    out PORTB, r21
    out PORTD, r16
    LSR r16
    cp r16, r21
    ;breq wait
    rjmp wait

wait:
    brvs TIM0_OVF_ISR
    rjmp wait

РЕДАКТИРОВАТЬ: Вот мой новый код. Этот работает:

/*
 * lab3b.asm
 *
 *  Created: 10/18/2014 8:03:22 PM
 *   Author: Isaac Vilchez
 */ 

.include "m328pdef.inc"
.org 0x0000 jmp begin
.org 0x0020 jmp TIM0_OVF_ISR

begin:
    .org 0x0033

//Config de Stack Pointer y Timer0
ldi R16, high(RAMEND)
out SPH, R16
ldi R16, low(RAMEND)
out SPL, R16

//Inicializando los puertos D y B como salida
ldi r16,    0x00
out DDRC,   r16
ldi r16,    0xFF
out DDRD,   r16
out DDRB,   r16

//Configurando timer0 al intervalo maximo de duracion al timer0
ldi R19,    0x00
out TCNT0,  R19 //Para que el conteo empiece en 0
ldi R20,    0x05
out TCCR0B, R20
ldi R23,    0x01
sts TIMSK0, R23 //habitlita el timer0

//Habilitando las interrupciones globales
SEI

//Configuracion de Registros
main:
    ldi r16, 0x01
    ldi r17, 0x00
    ldi r19, 0x80
    mov r20, r16
    ldi r21, 0
    mov r23, r19
    ldi r25, 0x01

//Loop de espera en lo que se realiza la interrupcion
wait:
    rjmp wait

//Enciende el led de PB4 y espera a la interrupcion
onPB:
    ldi r22, 0x80
    out PORTB, r22
    rjmp wait

//Enciende el led de PB4 y espera a la interrupcion
ledpb4:
    ldi r22, 0x80
    out PORTB, r22
    reti

reset:
    ldi r16,    0x01
    ldi r18,    0x08
    ldi r19,    0x80
    mov r20,    r16
    ldi r21,    0
    mov r23,    r19
    reti

//Rotacion hacia la izquierda
rot_izqPD:
    out PORTB,  r21
    out PORTD,  r16
    ldi r17, 0x00
    LSL r16
    cp  r16,    r17
    breq    rot_izqPB
    reti

//Rotacion hacia la izquierda
rot_izqPB:
    out PORTB,  r20
    LSL r20
    ldi r17, 0x10
    cp r20, r17
    breq    reset
    reti

//Rotacion hacia la derecha
rot_derPB:
    out PORTB,  r18
    ldi r17, 0x00
    cp r18, r17
    breq rot_derPD
    LSR r18
    reti

//Rotacion hacia la derecha
rot_derPD:
    out PORTB,  r21
    out PORTD,  r19
    cp  r19,    r21
    breq reset
    LSR r19
    reti

TIM0_OVF_ISR:
    in  r24,    PINC    ;Se guarda el valor del puerto C en R24
    andi r24,   0x01    ;Se enmascara el puerto C
    cp  r24,    r25     ;Se compara con 0x01 para ver su estado
    breq    rot_derPB   ;Si esta encendido, rota a la derecha
    rcall   rot_izqPD   ;Si esta apagado rota a la izquierda
    reti

person Isaac Vilchez    schedule 19.10.2014    source источник


Ответы (1)


Вы пытаетесь использовать процедуру прерывания, а также опросить флаг прерывания. Вы должны делать только один из них. Я предлагаю процедуру прерывания.

(Кроме того, вы пытаетесь опросить неправильный флаг. brvs проверяет флаг на переполнение в арифметической операции. Флаг переполнения таймера — это что-то другое.)

У вас есть переход к подпрограмме в правильном месте в таблице прерываний .org 0x0020 jmp TIM0_OVF_ISR. И вы разрешаете прерывания в main. Это выглядит нормально для меня на первый взгляд.

Когда происходит прерывание, адрес следующей инструкции (PC) помещается в стек, флаг прерывания сбрасывается, прерывания запрещаются, и поток управления переходит к адресу в таблице.

Но вы никогда не возвращаетесь из прерывания. В конце подпрограммы вы должны вызвать reti. Это приведет к тому, что поток вернется туда, где он был, когда он был прерван путем извлечения адреса из стека, и снова разрешит прерывания. Вы не должны входить в петлю занятости в подпрограмме прерывания, как в нижней части этой программы.

Структура программы должна быть больше похожа на

.include "m328pdef.inc"
.org 0x0000 jmp main
.org 0x0020 jmp TIM0_OVF_ISR

main: 
    Configure all the ports and timer and interrupts.
    sei
wait:
    rjmp wait

TIM0_OVF_ISR: 
    Do the routine
    reti

Также обратите внимание, что вам не нужны некоторые инструкции rjmp в вашем коде.

rot_izqPD:
    ldi r16, 0x01
    rjmp rot_izqPD1   ;;; not needed. This is the next instruction anyway.

rot_izqPD1:
    out PORTD, r16

РЕДАКТИРОВАТЬ: с подпрограммами

Если вам нужны подпрограммы для вращений, вы можете call их, если они заканчиваются на ret. Использование rjmp везде действительно плохо, и это доставляет вам неприятности.

.include "m328pdef.inc"
.org 0x0000 jmp main
.org 0x0020 jmp TIM0_OVF_ISR

main: 
    Configure and initialize all the ports and timer and interrupts.
    call rot_derPB   ;; called the subroutine from main. flow returns here.
    sei
wait:
    rjmp wait

rot_derPB:
    do the rotation
    ret

TIM0_OVF_ISR: 
    Do the routine. e.g.,
    call rot_derPB   ;; called the subroutine in the handler. flow returns here.
    reti

РЕДАКТИРОВАТЬ 2: if/else ветвление

Обычный трюк для ветвления if/else состоит в том, чтобы пропустить вперед предложение «if» и сначала запустить предложение «else». Например, вместо

/Cargando a R16 con 0x01 para comenzar la rotacion hacia la izquierda
TIM0_OVF_ISR:
    in r24, PINC    ;Se guarda el valor del puerto C en R24
    andi r24, 0x01  ;Se enmascara el puerto C
    cpi r24, 0x01   ;Se compara con 0x01 para ver su estado
    breq rot_derPB  ;Si esta encendido, rota a la derecha
    rjmp rot_izqPD  ;Si esta apagado rota a la izquierda

измените порядок и запишите их как calls. Обязательно завершайте функции с помощью ret.

/Cargando a R16 con 0x01 para comenzar la rotacion hacia la izquierda
TIM0_OVF_ISR:
    in r24, PINC    ;Se guarda el valor del puerto C en R24
    andi r24, 0x01  ;Se enmascara el puerto C
    cpi r24, 0x01   ;Se compara con 0x01 para ver su estado
    breq go_derPB
    call rot_izqPD  ;Si esta apagado rota a la izquierda
    bra TIMO_end
go_derPB:   
    call rot_derPB  ;Si esta encendido, rota a la derecha
TIM0_end:
    reti

Обратите внимание, что ветки относятся к местам внутри одной и той же функции. Чтобы выйти из функции, вы либо вызываете другую функцию, либо возвращаетесь из функции. Чего вы не хотите, так это "спагетти-кода", разветвляющегося на большие расстояния по программе.

person uncleO    schedule 19.10.2014
comment
Эй дядяО! Пожалуйста, посмотрите на новый ответ, который я только что опубликовал. Я немного исправил код, как вы мне сказали, но до сих пор не удалось его обработать. Пожалуйста, проверь это - person Isaac Vilchez; 20.10.2014
comment
Привет, Исаак, тебе следует отредактировать свой вопрос, а не публиковать ответ. Но новый код по-прежнему выглядит неправильно. Возможно, вам стоит подумать об использовании инструкций call и ret, а не rjmp. это все еще выглядит так, как будто вы входите в цикл занятости с отключенными прерываниями. - person uncleO; 20.10.2014
comment
Я думаю, что он просто остается в цикле ожидания, и всякий раз, когда я прошу его вернуться из прерывания, он просто возвращается в цикл. - person Isaac Vilchez; 20.10.2014
comment
Я отредактировал ответ, чтобы добавить подпрограммы. Похоже, вы хотите повернуться в основном один раз, а затем в обработчике прерываний. - person uncleO; 20.10.2014
comment
Я стер цикл ожидания. Я ясно вижу, что прерывание действительно работает. как сделать, чтобы вращение происходило как раз при переполнении таймера? - person Isaac Vilchez; 20.10.2014
comment
Ok! позвольте мне попробовать, и я дам вам знать. - person Isaac Vilchez; 20.10.2014
comment
Эй дядяО! Я пытался исправить код и структурировать его, как вы мне сказали, но должно быть что-то еще, что я упускаю. Я отредактировал свой вопрос с последним кодом, который я пробовал. Можешь взглянуть? - person Isaac Vilchez; 20.10.2014
comment
Вы все еще используете ветвление вместо вызовов в некоторых местах. Я добавлю больше к моему ответу. - person uncleO; 20.10.2014