ИК-пульт дистанционного управления на базе Arduino — проблема с передачей данных

Я создал схему 25-кнопочного ИК-пульта дистанционного управления для моего устройства RuneAudio. Устройство представляет собой автономный ATmega328P-PU, работающий от 3 В (2 батарейки AAA) с внутренней частотой 8 МГц. Схема имеет 5 строк и 5 столбцов и использует библиотеку клавиатуры Arduino. Код в основном является копией кода Ника Гэммона (http://www.gammon.com.au/forum/?id=11497&reply=4#reply4), с небольшими изменениями для моих целей.

Схема выглядит следующим образом (R1=1K, R2=10K, R3=62 Ом):

Ниже приведен код, который я использую:

#include <Keypad.h>
#include <avr/sleep.h>
#include <avr/power.h>
#include <IRremote.h>

const byte ROWS = 5;
const byte COLS = 5;
byte keynum;

char keys[ROWS][COLS] =
{
  {'E', 'D', 'C', 'B', 'A'},
  {'J', 'I', 'H', 'G', 'F'},
  {'O', 'N', 'M', 'L', 'K'},
  {'T', 'S', 'R', 'Q', 'P'},
  {'Y', 'X', 'W', 'V', 'U'}
};

byte rowPins[ROWS] = {9, 10, 11, 12, 13}; // Контакты ряда клавиатуры
byte colPins[COLS] = {4, 5, 6, 7, 8};     // Контакты столбцов клавиатуры

unsigned long IRCode[25] =  {0xB54A38C7, 0xB54A30CF, 0xB54AE191, 0xB54ADCDD, 0xB54AD6C3, 0xB54AE5ED, 0xB54A50AF, 0xB54A9867, 0xB54AB04F, 0xB54A48B7, 0xB54AD02F, 0xB54AFCF7, 0xB54AF56C, 0xB54AEFEE, 0xB54AEAA0, 0xB54A02FD, 0xB54B176F, 0xB54B100D, 0xB54B08F6, 0xB54B031D, 0xB54A42BD, 0xB54AC23D, 0xB54A1AE5, 0xB54A7887, 0xB54A827D};

// Количество элементов в массиве
#define NUMITEMS(arg) ((unsigned int) (sizeof (arg) / sizeof (arg [0])))

// Настройка ИК (пульт дистанционного управления NEC)
const byte IRledPin = 3;
const int NumBits = 32;


// Определять
Keypad kpd = Keypad( makeKeymap(keys), rowPins, colPins, ROWS, COLS );
IRsend irsend;

// отключаем прерывания, пока не будем готовы
ISR (PCINT0_vect)
  {
  PCICR = 0;  // отменить прерывания смены пина
  } // конец ISR (PCINT0_vect)

ISR (PCINT1_vect)
  {
  PCICR = 0;  // отменить прерывания смены пина
  } // конец ISR (PCINT1_vect)

ISR (PCINT2_vect)
  {
  PCICR = 0;  // отменить прерывания смены пина
  } // конец ISR (PCINT2_vect)

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

  // Маски прерываний смены выводов (см. список в оригинальном коде Ника Гэммонса)
  PCMSK0 |= bit (PCINT1);   // Контакт 9
  PCMSK0 |= bit (PCINT2);   // Контакт 10
  PCMSK0 |= bit (PCINT3);   // Контакт 11
  PCMSK0 |= bit (PCINT4);   // Контакт 12
  PCMSK0 |= bit (PCINT5);   // Контакт 13
  Serial.begin(9600);
}


void reconfigurePins ()
{
Serial.print("1 Reconfig pins"); 
Serial.println("");
  byte i;
  for (i = 0; i < NUMITEMS (colPins); i++)
  {
    pinMode (colPins [i], OUTPUT);
    digitalWrite (colPins [i], HIGH);
  }

  for (i = 0; i < NUMITEMS (rowPins); i++)
    {
    pinMode (rowPins [i], INPUT_PULLUP);
    }   // конец для каждой строки
Serial.print("2 DONE Reconfig pins"); 
Serial.println("");
  }  // конец reconfigurePins


void goToSleep ()
{
Serial.print("3 inside SLEEP"); 
Serial.println("");

  // Настройка обнаружения нажатия клавиш
  byte i;
  for (i = 0; i < NUMITEMS (colPins); i++)
  {
    pinMode (colPins [i], OUTPUT);
    digitalWrite (colPins [i], LOW);
  }

  for (i = 0; i < NUMITEMS (rowPins); i++)
  {
    pinMode (rowPins [i], INPUT_PULLUP);
  }  // конец для каждой строки

  // Проверить, не нажаты ли контакты (в противном случае выход из спящего режима при отпускании клавиши)
  for (i = 0; i < NUMITEMS (rowPins); i++)
  {
    if (digitalRead (rowPins [i]) == LOW)
    {
  Serial.print("ROW LOW: "); 
  Serial.print(rowPins [i]); 
Serial.println("");
      reconfigurePins ();
      return;
    }
  }

  // Преодоление любых задержек, связанных с дребезгом, встроенных в библиотеку клавиатуры
  delay (10);

  // В этот момент нажатие клавиши должно соединить верхний уровень в ряду с
  // к нижнему значению в столбце и вызвать смену пина
Serial.print("4 Going to SLEEP"); 
Serial.println("");

  set_sleep_mode (SLEEP_MODE_PWR_DOWN);
  sleep_enable();

Serial.print("5 AWAKE"); 
Serial.println("");

  byte old_ADCSRA = ADCSRA;
  // Отключить АЦП для экономии энергии
  ADCSRA = 0;

  power_all_disable ();  // отключить различные модули

  PCIFR  |= bit (PCIF0) | bit (PCIF1) | bit (PCIF2);   // очистить все невыполненные прерывания
  PCICR  |= bit (PCIE0) | bit (PCIE1) | bit (PCIE2);   // включить прерывания по смене контакта

  // Отключить функцию отключения питания в программном обеспечении
  MCUCR = bit (BODS) | bit (BODSE);
  MCUCR = bit (BODS);
  sleep_cpu ();

  // Отмените сон в качестве меры предосторожности
  sleep_disable();
  power_all_enable ();   // снова включаем модули
  ADCSRA = old_ADCSRA;   // повторно включить преобразование АЦП

  // Верните контакты клавиатуры на место, как они должны быть.
  reconfigurePins ();
Serial.print("6 Leaving SLEEP"); 
Serial.println("");

}

void loop ()
{

  byte key =  kpd.getKey();
  if (!key)
  {
Serial.print("CALLING SLEEP"); 
Serial.println("");

    // Спящий режим, если ни одна клавиша не нажата
    goToSleep ();
    return;
 Serial.print("After RETURN"); 
Serial.println("");

  }

  // Вычитаем значение ASCII из первого значения ключа, чтобы получить аргумент массива HEX
  keynum = key - 'A';
Serial.print("SENDING IR....................................................................................."); 
Serial.println("");

  // sendNEC(беззнаковые длинные данные, int nbits);
  irsend.sendNEC(IRCode[keynum], NumBits);
Serial.print(IRCode[keynum]); 
Serial.println("");

}

Скетч отлично компилируется и загружается. Он определяет нажатия клавиш и посылает ИК-сигналы. Но пропускает около половины нажатий клавиш. Я тестирую на макетной плате, используя одну клавишу - R1C5 (S5 на схеме).

Я должен признать, что, несмотря на то, что потратил много времени на изучение спецификации ATmega328 и блогов Ника об энергосбережении, я не могу полностью понять функции энергосбережения из-за моих не очень зрелых навыков и сложности всего проекта (Rune Audio на базе Raspberry Pi + TDA7439 контроллер тональности + выключатель питания на базе ATmega для Pi + 2-канальный аудиоусилитель + этот ИК-пульт дистанционного управления).

Я добавил несколько последовательных отпечатков, чтобы проверить, что происходит. Последовательные выходы, похоже, не синхронизированы, и последнее сообщение перед сном — «CALLING SLEEP», хотя я ожидал, что это будет «4 Going to SLEEP». А добавление последовательной печати загадочным образом уменьшает количество промахов.

Ниже приведен последовательный вывод сразу после включения питания и нажатия одной клавиши:

EP
3 inside SLEEP
4 Going to SLEEP
5 AWAKE
1 Reconfig pins
2 DONE Reconfig pins
6 Leaving SLEEP
SENDING IR.....................................................................................
3041540295
CALLING SLEEP
3 inside SLEEP
ROW LOW: 9
1 Reconfig pins
2 DONE Reconfig

После многократного нажатия клавиш происходит следующее:

CALLING SLEEP
3 inside SLEEP
ROW LOW: 9
1 Reconfig pins
2 DONE Reconfig pins
SENDING IR.....................................................................................
3041540295
CALLING SLEEP
3 inside SLEEP
ROW LOW: 9
1 Reconfig pins
2 DONE Reconfig pins
CALLING SLEEP
3 inside SLEEP
ROW LOW: 9
1 Reconfig pins
2 DONE Reconfig
CALLING SLEEP
3 inside SLEEP
4 Going to SLEEP
5 AWAKE
1 Reconfig pins
2 DONE Reconfig pins
6 Leaving SLEEP
CALLING SLEEP
3 inside SLEEP
ROW LOW: 9
1 Reconfig pins
2 DONE Reconfig
CALLING SLEEP
3 inside SLEEP
4 Going to SLEEP
5 AWAKE
1 Reconfig pins
2 DONE Reconfig pins
6 Leaving 
CALLING SLEEP
3 inside SLEEP
4 Going to SLEEP
5 AWAKE
1 Reconfig pins
2 DONE Reconfig pins
6 Leaving 
CALLING SLEEP
3 inside SLEEP
4 Going to SLEEP
5 AWAKE
1 Reconfig pins
2 DONE Reconfig pins
6 Leaving 
CALLING SLEEP
3 inside SLEEP
4 Going to SLEEP
5 AWAKE
1 Reconfig pins
2 DONE Reconfig pins
6 Leaving 
CALLING SLEEP
3 inside SLEEP
4 Going to SLEEP
5 AWAKE
1 Reconfig pins
2 DONE Reconfig pins
6 Leaving SLEEP
CALLING SLEEP
3 inside SLEEP
ROW LOW: 9
1 Reconfig pins
2 DONE Reconfig pins
SENDING IR.....................................................................................
3041540295
CALLING SLEEP
3 inside SLEEP
ROW LOW: 9
1 Reconfig pins
2 DONE Reconfig

Было бы очень полезно, если бы кто-нибудь показал мне здесь свет.


Хорошо. Длинные строки серийной печати заменены на отдельные цифры, где они обозначают: 1= 1 Переконфигурировать контакты 2= 2 ГОТОВО Перенастроить контакты 3= 3 внутри СНА 4= РЯД НИЖНИЙ= 5= 4 Иду спать 6= 5 ПРОБУЖДЕННЫХ 7= 6 Выход из СНА 8= ВЫЗОВ КО СНУ .....= ОТПРАВКА ИК

Я вижу, где он терпит неудачу. Теперь выкладываю следующий сериал:

6
1
2
7
8
3
49
1
2
8
3
49
1
2
8
3
49
1
2
8
3
49

Таким образом, программа просыпается при нажатии клавиши (6), затем перенастраивает пины (1,2), выходит из функции сна (7) и включает основную (8). Здесь byte key = kpd.getKey() НЕ обнаруживает нажатие клавиши и снова вызывает sleep (3). Sleep обнаруживает нижнюю строку 9 (4-9). После этого он застревает в цикле основных и спящих функций, пока нажатие клавиши не закончится. На данный момент проблема, похоже, в kpd.getKey().

, 👍1


1 ответ


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

1

Я добавил несколько серийных отпечатков, чтобы проверить, что происходит. Серийные выходы, похоже, не синхронизированы, и последнее сообщение перед сном — «CALLING SLEEP», хотя я ожидал, что это будет «4 Going to SLEEP».

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

Ваши отпечатки выполняются, но не отправляются из очереди до того, как MCU перейдет в спящий режим.

Вы должны добавить

Serial.flush();

перед тем, как фактически заснуть, чтобы убедиться, что все данные из буфера TX были отправлены. Таким образом, ваши сообщения будут действительно осмысленными.

Кроме того, если вы отправляете более 64 байт данных через Serial за короткий промежуток времени, он заблокируется и будет ждать отправки некоторых данных из буфера TX. Размер буфера составляет всего 64 байта, поэтому, если места не осталось, он должен ждать прерывания. Это ожидание замедляет весь ваш скетч и может повлиять на то, как он работает. Немного похоже на квантовую физику — наблюдение за работой скетча изменяет работу скетча.

,