Прерывание AVR "Интерференция"

В настоящее время я работаю над получением сигналов от пульта дистанционного управления. Я использовал контакты с A8 по A15 на своей плате Arduino Mega.

Порты и прерывания настраиваются с помощью этого кода:

DDRK = 0b00000000;      // Set port A8-A15 to input
PCICR |= (1<<PCIE2);    // Enable interrupt on PCMSK2 containing receiver pins
PORTK = 0b11111111;     
PCMSK2 = 0b11111111;    // Enable all interrupts on receiver pins

Затем я настроил прерывание, которое находит прерванный контакт и измеряет время импульса.

// Interrupts whenever a signal is received.
ISR(PCINT2_vect) {  
static uint32_t startTime[8];
static uint8_t lastState;
uint8_t mask;
uint8_t changedPin;

mask = PINK ^ lastState;
lastState = PINK;

if(mask & (1 << 0)) changedPin = 0;
if(mask & (1 << 1)) changedPin = 1;
if(mask & (1 << 2)) changedPin = 2;
if(mask & (1 << 3)) changedPin = 3;
if(mask & (1 << 4)) changedPin = 4;
if(mask & (1 << 5)) changedPin = 5;
if(mask & (1 << 6)) changedPin = 6;
if(mask & (1 << 7)) changedPin = 7; 

// Check if the changed pin is high
if(lastState & (1 << changedPin)) {
    // Start counting if it is
    startTime[changedPin] = micros();
} else {
    // Save if it has gone down again
    rcValue[changedPin] = micros() - startTime[changedPin];
}
}

На самом деле это работает очень хорошо, за исключением той части, когда я подключаю в общей сложности 4 или более контактов.

Пример:
A8: Работает нормально
A8–A9: Работает нормально
A8–A10: Работает нормально
A8–A11: A9 и A11 работают нормально, A10 не регистрирует никаких входных данных, A8 подскакивает от нормальных значений (1000–2000) до значений, которые около 17000р.

Я предполагаю, что это может иметь какое-то отношение к маскировке и обнаружению булавки оттуда, но я искал по всему всемогущему Интернету, и он не дал мне четкого ответа.

Ответы (1)

Я вижу три проблемы с вашим кодом:

Переменная волатильность и масштаб

Переменные ниже будут повреждены из-за того, что вы определяете их только в ISR. Но вы явно хотите сохранить их значение между прерываниями. Создайте следующие переменные volatileи определите их глобально, а не в ISR:

  • uint8_t lastState;
  • uint32_t startTime[8];

Точное время

Запустите критический по времени код как можно скорее после возникновения прерывания. Считайте micros()во временную переменную в начале ISR, чтобы весь условный код не влиял на время. Затем назначьте временную переменную, startTime[changedPin]как только ваш код решит, куда ее поместить.

Одновременные края

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

Большое спасибо за очень информативный и познавательный ответ, я проверю ваши изменения в течение нескольких часов, а затем приму их, если они сработают.