Pin Change Interrupt - внешнее прерывание с внутренним прерыванием - PullRequest
4 голосов
/ 09 ноября 2010

Здесь, в программе, я переключаю светодиоды, используя прерывание по таймеру, и если кто-то нажимает на переключатель, он должен остановить первое прерывание и запустить второе, которое должно зажечь светодиод в соответствии с нажатым переключателем.Здесь я немного запутался, какое прерывание вызывается.Я отослал некоторые книги для Pin Change Interrupt и написал несколько строк для настройки 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)
    {

    }
}

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

Ответы [ 2 ]

5 голосов
/ 01 февраля 2011

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

#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;
}

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

0 голосов
/ 10 ноября 2010

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

...