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 обратно к циклу. Я не вижу, в чем может быть проблема, так как я определил параметр глобально. Кто-нибудь может протянуть мне руку помощи, это, честно говоря, последнее, что мне нужно, чтобы закончить эту часть проекта, и я, честно говоря, в некоторой растерянности на данный момент.
@Kevin Sullivan, 👍4
1 ответ
Лучший ответ:
Вы хотели бы, чтобы ваш 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 применимо и здесь.
- Использование millis() и micros() внутри процедуры прерывания
- Подсчет импульсов с прерыванием
- Получить доступ к EEPROM ATtiny с помощью кода Arduino?
- Устранение дребезга кнопки с помощью прерывания
- Программа arduino выдаёт ошибку expected //primary-expression before ')' token error: //expected ';' before '}' token E
- Почему необходимо использовать ключевое слово volatile для глобальных переменных при обработке прерываний в ардуино?
- Как сгенерировать аппаратное прерывание в mpu6050 для пробуждения Arduino из режима SLEEP_MODE_PWR_DOWN?
- Записать во флэш-память с помощью PROGMEM
Святое дерьмо, это действительно безумие! Я никогда не думал, что компилятор поступит со мной так грязно! Я действительно ценю этого человека, который буквально все исправил!, @Kevin Sullivan
Хорошая сделка. Вероятно, позже я добавлю к вышесказанному, что если вы используете attachInterrupt () или библиотеку PinChangeInterrupt, все, что сказано о "volatile", остается в силе. Потому что с этими. любая функция обратного вызова, которую вы предоставляете, также выполняется в контексте прерывания, ничем не отличаясь от вашего более прямого кода ISR()., @timemage