Прерывание смены контакта — внешнее прерывание с внутренним прерыванием

Здесь, в программе, я переключаю светодиоды, используя прерывание по таймеру, и если кто-то нажимает переключатель, он должен остановить первое прерывание и запустить второе, которое должно зажечь светодиод в соответствии с нажатым переключателем. Здесь я немного смущен, какое прерывание вызывается. Я сослался на несколько книг по прерыванию смены контакта и написал несколько строк для настройки PCMSK2. Вывод, который я получаю, таков: «изначально все светодиоды зацикливаются, когда нажимается переключатель… цикличность светодиодов останавливается и начинается снова (что означает, что программа считывает ввод, просто не запуская второе прерывание). Он не останавливается. или пауза и не загорается следующий светодиод». Кто-нибудь может помочь, пожалуйста?

#include <avr/io.h>
#include <avr/interrupt.h>
#define PINK_MASK \
    ((1<<PINK0)|(1<<PINK1)|(1<<PINK2)|(1<<PINK3)|(1<<PINK4)|(1<<PINK5)|(1<<PINK6)|(1<<PINK7))


volatile unsigned int intrs, i=1;

void enable_ports(void);
void delay(void);

extern void __vector_23 (void) __attribute__ ((interrupt));

extern void __vector_25 (void) __attribute__ ((signal));

void enable_ports()
{
    DDRB = 0xff;   //PORTB as output for leds

    PORTB = 0xff;

    DDRK = 0x00;  //PORTK as input from switches

    PORTK |= PINK_MASK;

    PCMSK2 = PINK_MASK;     //ENABLE PCMSK2, Setting interrupts

    PCICR = 0x04;

    PCIFR = 0x04;

    TCCR0B = 0x03;      //Setting TIMER

    TIMSK0 = 0x01;

    TCNT0 = 0x00;

    intrs = 0;
}
void __vector_23 (void)
{
    intrs++;
    if(intrs > 60)
    {
        intrs = 0;
        PORTB = (0xff<<i);

        i++ ;
        if(i == 10 )
        {
            PORTB = 0xff;
            i = 1 ;
        }
    }
}

void __vector_25 (void)
{
    unsigned char switches;

    switches = ((~PINK) & (PINK_MASK)); //Reading from switches

    if(switches & (1<<PINK0))
        PORTB = (PORTB<<PINK0);

    else if (switches & (1<<PINK1))
        PORTB = (PORTB<<PINK1);

    else if (switches & (1<<PINK2))
        PORTB = (PORTB<<PINK2);

    else if (switches & (1<<PINK3))
        PORTB = (PORTB<<PINK3);

    else if (switches & (1<<PINK4))
        PORTB = (PORTB<<PINK4);

    else if (switches & (1<<PINK5))
        PORTB = (PORTB<<PINK5);

    else if (switches & (1<<PINK6))
        PORTB = (PORTB<<PINK6);

    else if (switches & (1<<PINK7))
        PORTB = (PORTB<<PINK7);
}

int main(void)
{
    enable_ports();
    sei();

    while(1)
    {

    }
}

Спасибо за вашу поддержку.


person sneezy    schedule 09.11.2010    source источник
comment
Извините, я не использую Atmel µC. Но, возможно, вы могли бы спросить на embdev.net.   -  person AndreKR    schedule 10.11.2010
comment
На какое конкретное устройство вы ориентируетесь?   -  person John Leimon    schedule 09.01.2011


Ответы (2)


Внешние прерывания в архитектуре AVR сбивают с толку, но не невозможны. Я обнаружил, что лучшим ресурсом для меня является страница AVR libc по прерываниям. Я думаю, вы сделали код слишком сложным для того, что вы хотите. Начнем с нуля:

#include <avr/io.h>
#include <stdint.h>
#include <avr/interrupt.h>

void main() {
    sei();
    while(1) {};
}

AVR libc фактически делает обработку прерываний довольно безболезненной. На странице, на которую я ссылался выше, есть список всех поддерживаемых векторов прерываний на каждом чипе AVR. Предположим, вы используете Mega32 и теперь хотите, чтобы светодиоды мигали с помощью прерывания таймера. Добавим в программу:

uint8_t led_state;

ISR(TIMER0_COMP_vect) {
    led_state = ~led_state;
    PORTB = led_state;
}

void setup_timer_interrupt() {
    TCCR0B = 0x03;
    TIMSK0 = 0x01;
    TCNT0 = 0x00;
}

При этом светодиоды на PORTB должны мигать каждый раз, когда происходит прерывание таймера. Обратите внимание, что его нужно настроить с помощью макроса ISR(...); вызовы __vector_..., которые вы используете, устарели и немного сбивают с толку.

Наконец, вы хотите использовать набор переключателей для включения светодиода, если я правильно понимаю ваш вопрос. На самом деле я бы не стал использовать для этого внешнее прерывание, просто прочитал значение переключателей, используя PINK во время ISR(TIMER0_COMP_vect), но мы можем использовать его, если хотите. Нам нужно добавить следующий код:

uint8_t switch_state;

void setup_switch_interrupt() {
    // I'm assuming this code to enable external interrupts works.
    DDRK = 0x00;
    PORTK = 0xff;
    PCMSK2 = 0xff; // set to all 1s
    PCICR = 0x04;
    PCIFR = 0x04;
}

ISR(INT0_vect) {
    switch_state = PINK;
}

Что это делает? Мы будем хранить состояние переключателей в switch_state, считывая каждый раз, когда срабатывает внешнее прерывание (я предполагаю, что вы установили это для обоих переходов 0-> 1 и 1-> 0). Осталось только сделать выход светодиода зависимым от значения switch_state. Мы сделаем это в прерывании таймера, так как именно там мы переключали светодиоды до сих пор. Новая версия выглядит так:

ISR(TIMER0_COMP_vect) {
    led_state = ~led_state | switch_state;
    PORTB = led_state;
}

И это должно сделать это!

Сноска: ранее я говорил, что использование внешнего прерывания для чтения переключателей на самом деле не обязательно. Это потому, что вы можете просто прочитать значения переключателя во время прерывания таймера, используя PINK. Вы можете избавиться от switch_state, setup_switch_interrupt() и ISR(INT0_vect) и просто изменить прерывание таймера следующим образом:

ISR(TIMER0_COMP_vect) {
    led_state = ~led_state | PINK;
    PORTB = led_state;
}

Это должно сделать программу немного проще.

person Devrin    schedule 01.02.2011

Поэтому, несмотря ни на что, каждый раз, когда выполняется ваш __vector_23, вы циклически переключаете светодиоды, назначенные PORTB, увеличивая i. Если я понимаю, что вы пытаетесь сделать, то вы должны увеличивать i только в __vector_25, когда переключатель нажат.

person Mark Elliot    schedule 10.11.2010
comment
@sneezy, я думаю, что ошибся, когда впервые ответил, пожалуйста, посмотрите мое редактирование с некоторыми лучшими инструкциями. - person Mark Elliot; 10.11.2010
comment
Я не вижу вызова __vector_25. - person sneezy; 10.11.2010