ATtiny13 Проблема прерывания

Недавно я работал над проектом, который изначально разрабатывал с помощью Arduino Nano, и мне удалось создать функционирующий код для моего приложения, но для сокращения затрат и пространства я решил портировать приложение на ATtiny13 (потому что у меня было несколько под рукой). Проблема в том, что по какой-то причине я не могу заставить прерывание на устройстве вести себя должным образом, и мне было интересно, может ли кто-нибудь помочь мне. Я уже провел несколько экспериментов, чтобы сузить проблему, и думаю, что понимаю природу проблемы, но понятия не имею, как ее смягчить.

Вот примерная схема моей установки:

А вот код, с которого я начал:

// Pin определяет
#define ALERT_LED 4

// Переменные
bool LED_STATE = false;


ISR(PCINT0_vect) {
  LED_STATE = true;
}

void setup() {
  pinMode(ALERT_LED, OUTPUT);

  // Настройте кнопку-штифт так, чтобы он имел подтягивание
  DDRB |= (1<<DDB3);
  PORTB |= (1<<PORTB3);

  // Включить прерывание смены контакта на PCINT3 (falling-edge)
  MCUCR |= (1 << ISC01);  // Эта и следующая настройка строки для падающего края
  MCUCR &= ~(1 << ISC00);
  GIMSK |= (1 << PCIE);   // Включить прерывания смены контактов
  PCMSK |= (1 << PCINT3); // Настройте pin-маску так, чтобы PB3 запускал прерывание
  sei();                  // включите все прерывания
  
}

void loop() {
  if(LED_STATE == true) {
    digitalWrite(ALERT_LED, HIGH);
    delay(2000);
    digitalWrite(ALERT_LED, LOW);
    delay(2000);
    LED_STATE = false;
  }
}

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

Теперь, прежде чем я пойду дальше, вот что я проверил до этого момента:

1.) Светодиодная схема определенно подключена правильно, потому что я могу вручную установить флаг "LED_STATE" на true в основном цикле, и он будет постоянно мигать примерно раз в секунду (учитывая тактовую частоту 600 кГц).

2.) Подтягивание определенно настраивается правильно, так как я вижу, что напряжение находится на уровне 5 В, пока я не нажму кнопку, и в этот момент напряжение упадет до 0 В.

Поэтому я решил немного поэкспериментировать, установил светодиод высоко внутри прерывания и удалил эту строку из основного цикла, чтобы посмотреть, работает ли прерывание, и, к моему удивлению, светодиод действительно включился, но не выключился:

// Pin определяет
#define ALERT_LED 4

// Переменные
bool LED_STATE = false;


ISR(PCINT0_vect) {
  LED_STATE = true;
  digitalWrite(ALERT_LED, HIGH);
}

void setup() {
  pinMode(ALERT_LED, OUTPUT);

  // Настройте кнопку-штифт так, чтобы он имел подтягивание
  DDRB |= (1<<DDB3);
  PORTB |= (1<<PORTB3);

  // Включить прерывание смены контакта на PCINT3 (falling-edge)
  MCUCR |= (1 << ISC01);  // Эта и следующая настройка строки для падающего края
  MCUCR &= ~(1 << ISC00);
  GIMSK |= (1 << PCIE);   // Включить прерывания смены контактов
  PCMSK |= (1 << PCINT3); // Настройте pin-маску так, чтобы PB3 запускал прерывание
  sei();                  // включите все прерывания
  
}

void loop() {
  if(LED_STATE == true) {
    //digitalWrite(ALERT_LED, ВЫСОКИЙ);
    delay(2000);
    digitalWrite(ALERT_LED, LOW);
    delay(2000);
    LED_STATE = false;
  }
}

В этот момент я начал думать, что ISR не возвращается в основной цикл или что-то в этом роде, но, добавив случай "else", чтобы отключить светодиод в основном цикле, я увидел, что цикл определенно работает после ISR, потому что светодиод не включается (подразумевая, что основной цикл должен был отключить его в тот момент, когда ISR вернулся в цикл).

// Pin определяет
#define ALERT_LED 4

// Переменные
bool LED_STATE = false;


ISR(PCINT0_vect) {
  LED_STATE = true;
  digitalWrite(ALERT_LED, HIGH);
}

void setup() {
  pinMode(ALERT_LED, OUTPUT);

  // Настройте кнопку-штифт так, чтобы он имел подтягивание
  DDRB |= (1<<DDB3);
  PORTB |= (1<<PORTB3);

  // Включить прерывание смены контакта на PCINT3 (falling-edge)
  MCUCR |= (1 << ISC01);  // Эта и следующая настройка строки для падающего края
  MCUCR &= ~(1 << ISC00);
  GIMSK |= (1 << PCIE);   // Включить прерывания смены контактов
  PCMSK |= (1 << PCINT3); // Настройте pin-маску так, чтобы PB3 запускал прерывание
  sei();                  // включите все прерывания
  
}

void loop() {
  if(LED_STATE == true) {
    //digitalWrite(ALERT_LED, ВЫСОКИЙ);
    delay(2000);
    digitalWrite(ALERT_LED, LOW);
    delay(2000);
    LED_STATE = false;
  }
  else {
    digitalWrite(ALER_LED, LOW);
  }
}

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

, 👍4


1 ответ


Лучший ответ:

7

Вы хотели бы, чтобы ваш bool LED_STATE = false; был volatile квалифицирован как volatile bool LED_STATE = false; в противном случае оптимизатор компилятора может предположить, что значение не может меняться между нагрузками, и это может привести к ошибкам в сгенерированном коде. Это звучит как основная проблема, с которой вы столкнулись.

ISC01 и ISC00 в MCUCR предназначены для управления ребрами, обнаруженными на выводе INT0 "внешнего прерывания", который является PB1, выводом пакета 6. По сути, это то, что делает attachInterrupt() , который должен быть доступен вам в пакете поддержки платы. Если вы можете, то, вероятно, лучше всего просто использовать это. В случае использования attachInterrupt() использование volatile будет по-прежнему применяться.

Другие контакты, как и тот, который вы выбрали, выполняют только прерывания Pin Change, следовательно, PCINTn, а не INTn . Прерывания смены контакта срабатывают только для того, чтобы сообщить об изменении контакта, но не о том, какой вывод (вне группы) или в каком направлении он изменился. То есть он игнорирует то, что вы установили в MCUCR.

Если вы хотите, чтобы внешнее прерывание могло обнаруживать падающие или поднимающиеся ребра с помощью прерываний PCINTn, вы должны эффективно синтезировать это в программном обеспечении, отслеживая старое и новое состояния выводов, фиксируя их, чтобы определить, что изменилось, а затем просматривая старое или новое состояние, чтобы определить, в каком направлении. Библиотека NicoHood PinChangeInterrupt делает это за васи, похоже, поддерживает ATtiny13. Точно так же использование volatile применимо и здесь.

,

Святое дерьмо, это действительно безумие! Я никогда не думал, что компилятор поступит со мной так грязно! Я действительно ценю этого человека, который буквально все исправил!, @Kevin Sullivan

Хорошая сделка. Вероятно, позже я добавлю к вышесказанному, что если вы используете attachInterrupt () или библиотеку PinChangeInterrupt, все, что сказано о "volatile", остается в силе. Потому что с этими. любая функция обратного вызова, которую вы предоставляете, также выполняется в контексте прерывания, ничем не отличаясь от вашего более прямого кода ISR()., @timemage