18 августа 2012 г.

Обработка прерываний. Регистр GIFR.

Чтобы вызвать прерывание от внешнего источника, на соответствующей ножке МК нужно создать необходимые электрические условия. Например, прерывание можно вызвать изменением потенциала с высокого на низкий. При этом, чтобы был вызван обработчик, прерывания в МК, необходимы следующие условия:
  • прерывания должны быть разрешены глобально (бит I в регистре SREG должен быть установлен);
  • конкретное прерывание должно быть разрешено (установлен соответствующий прерыванию бит в регистре GIMSK).

Низкий уровень на входе INT0, передний фронт импульса, задний фронт импульса или любые изменения на INT0 могут (по выбору программиста) вызвать прерывание. Необходимое поведение задается с помощью битов ISC01 и  ISC00 регистра MCUCR.

ISC01 ISC00 Описание
0 0 Низкий уровень на INT0 генерирует прерывание.
0 1 Любое изменение на INT0 генерирует прерывание.
1 0 Прерывание возникает по заднему фронту на INT0.
1 1 Прерывание возникает по переднему фронту на INT0.

В МК ATiny26 есть только одно внешнее прерывание INT0. Далее речь будет идти о нем.

Прерывания в МК автоматически запрещаются при входе в обработчик -  бит I в регистре SREG сбрасывается. Чтобы разрешить вложенные прерывания, его нужно установить программно с помощью команды sei. Когда производится выход из прерывания (reti), бит автоматически устанавливается.

Также сбрасывается бит INT0 в регистре GIFR. Его можно сбросить и программно, записав единицу. Этот бит устанавливается автоматически, когда возникают электрические условия для прерывания. Если при этом бит I и/или бит INT0 в GIMSK сброшены, обработчик прерывания не будет вызван.

Если в период, когда прерывание запрещено, хотя бы однажды возникнут электрические условия для прерывания, и бит INT0 в регистре GIFR установится в единицу, то когда прерывание будет разрешено, автоматически запустится его обработчик.

Как было сказано выше, GIMSK сбрасывается записью единиц. Но единица, записанная программно, именно сбрасывает GIMSK и не может стать причиной вызова обработчика.

Я написал программу, которая по возникновению прерывания INT0 запрещает это прерывание и устанавливает таймер на 4 с. По истечении времени прерывание снова разрешается. При этом в одном случае перед разрешением прерывания я сбрасываю GIMSK, а в другом - нет. На видео будет показано, что во втором случае, если во время задержки нажать на кнопку, бит INT0 в GIMSK установиться в единицу, а по окончании задержки снова будет вызван обработчик прерывания.

Первый вариант (со сбросом GIMSK перед разрешением прерываний):

.device ATiny26
.include "tn26def.inc"

.def temp = r16
.def Delay_count = r17
.def GIFR_var = r18
.def Interrupt = r19

rjmp RESET ; Reset handler
rjmp EXT_INT0 ; IRQ0 handler
reti; rjmp PIN_CHANGE ; Pin change handler
reti; rjmp TIM1_CMP1A ; Timer1 compare match 1A
reti; rjmp TIM1_CMP1B ; Timer1 compare match 1B
reti; rjmp TIM1_OVF ; Timer1 overflow handler
rjmp TIM0_OVF ; Timer0 overflow handler
reti; rjmp USI_STRT ; USI Start handler
reti; rjmp USI_OVF ; USI Overflow handler
reti; rjmp EE_RDY ; EEPROM Ready handler
reti; rjmp ANA_COMP ; Analog Comparator handler
reti; rjmp ADC ; ADC Conversion Handler
;

TIM0_OVF:
    rcall OUT_DEBUG;

    dec Delay_count
    brne TIMER_NOT_END

    ;stop timer
    clr temp
    out TCCR0, temp

    ;allow external interrupt INT0
    ldi temp, (1<<INT0)
    out GIMSK, temp

    ;the flag can be cleared by writing a logical one to it.
    ;ldi temp, (1<<INT0)
    ;clear GIFR
    ldi temp, $FF
    out GIFR, temp
    
    ldi Interrupt, 0b00000000
    rcall OUT_DEBUG;

TIMER_NOT_END:
    reti

EXT_INT0:
    ldi Interrupt, 0b00000001
    rcall OUT_DEBUG;

    ;disallow INT0 interrupt
    clr temp
    out GIMSK, temp
    
    rcall START_TIMER
        
    reti

START_TIMER:
    ldi Delay_count, 255
    ldi Temp, 0b00000011
    out TCCR0, Temp            ;start timer
    ret;

OUT_DEBUG:
    in GIFR_var, GIFR
    rol GIFR_var
    rol GIFR_var
    or GIFR_var, Interrupt
    out PORTA, GIFR_var
    ret

RESET:
    ldi r16, RAMEND ; Main program start
    out SP, r16

    ldi Interrupt, 0b00000000

    ;set all A port contacts to OUT mode
    ldi temp, 0b11111111
    out DDRA, temp

    ;pull-up resistor
    ldi temp, 0b00000010
    out PORTB, temp

    /*
    ISC01 ISC00 Description
      0        0    The low level of INT0 generates an interrupt request.
      0        1    Any change on INT0 generates an interrupt request.
      1        0    The falling edge of INT0 generates an interrupt request.
      1        1    The rising edge of INT0 generates an interrupt request.
    */
    ldi temp, (1<<ISC01)
    out MCUCR, temp

    ;allow external interrupt INT0
    ldi temp, (1<<INT0)
    out GIMSK, temp

    ;allow timer 0 interrupt
    ldi temp, (1<<TOIE0)
    out TIMSK, temp

    sei

MAIN_CYCLE:
rjmp MAIN_CYCLE

На видео верхний диод зажигается, когда запускается обработчик прерывания и выключается по окончании задержки. Второй диод показывает бит INT0 в GIFR.


Я не буду приводить весь текст программы. Чтобы получить второй вариант (без сброса GIMSK перед разрешением прерываний) нужно просто закомментировать соответствующие строки.

    ;ldi temp, $FF
    ;out GIFR, temp


В конце ролика ерунду сказал. Естественно, вызывается не прерывание, а обработчик.

Комментариев нет:

Отправить комментарий