Как сделать задержку в коде

Есть программа, которая для одной буквы выдает букву в азбуке морза (точка, тире - черех звук). Букву ввожу с клавиатуры. Перед “озвучивание” заодно вывожу на экран эту букву - ‘a’.

Проблема в том, что в этой функции → ( ISR(PCINT2_vect) ), которая срабатывает при нажатии клавы, не включаются delay(), т.е. Зуммер просто пищит, без пауз. (Да и пищит он вроде нестолько, сколько нужно).

Я так понимаю это как-то связано с прерываниеями, их приоритетностью и тд…

Как быть ?

(Я только начинаю разбираться во всем этом (чайник) )

#define CLOCK 6 //D-
#define DATA 7  //D+

int audio10 = 10;      // к контакту 10 подключен зуммер
int note = 1200;      // music note/pitch (используемый тон)
int dotLen = 500;     // length of the morse code 'dot' (длительность звучания точки)
int dashLen = dotLen * 3;    // length of the morse code 'dash' (длительность звучания тире)
int te_to = dotLen*4;

#include <LiquidCrystal.h>  //Best imported by library manager

const int rs = 12, en = 11, d4 = 5, d5 = 4, d6 = 3, d7 = 2;
LiquidCrystal lcd(rs, en, d4, d5, d6, d7);

void MorseDot()
{  
  tone(audio10, note, dotLen);// start playing a tone
  delay(dotLen);              // hold in this position
}
void MorseDash()
{
  tone(audio10, note, dashLen);  // start playing a tone
  delay(dashLen);               // hold in this position
}


void setup()
{
  Serial.begin(9600);
  pinMode(CLOCK, INPUT_PULLUP); //For most keyboards the builtin pullups are sufficient, so the 10k pullups can be omitted
  pinMode(DATA, INPUT_PULLUP);
  pinMode(13, OUTPUT);
  pinMode(audio10, OUTPUT);
  lcd.begin(16, 2);
  lcd.cursor();
  lcd.blink();
  bitSet(PCICR, PCIE2); // Enable pin change interrupts on pin D0-D7
  bitSet(PCMSK2, CLOCK); // Pin change interrupt on Clock pin
 }

uint8_t lastscan;
uint8_t line = 0, col = 0;


ISR(PCINT2_vect)
{
  uint16_t scanval = 0;
  for(int i = 0; i<11; i++)
  {
    while(digitalRead(CLOCK));
    scanval |= digitalRead(DATA) << i;
    while(!digitalRead(CLOCK));
  }
  scanval >>= 1;
  scanval &= 0xFF;
  Serial.println(scanval, HEX);
  if(lastscan != 0xF0 && scanval != 0xF0)
  switch(scanval)
  {
    case 0x1C: 
    lcd.clear();
    lcd.print('a');
    MorseDot(); // точка. короткий звук
    delay(te_to);  // Пауза между точкой и тире
    MorseDash(); // тире. длинный звук
    delay(te_to); // Пауза между точкой и тире
    break;
    default:
       break;
  }
  lastscan = scanval;
  bitSet(PCIFR, PCIF2);
}

void loop()
{
}

Перетащи всё из прерывания в loop. В прерывании только выставляй флаг. В loop проверяй флаг и если флаг установлен снимай его и выполняй всё перетащеное.

1 лайк

спасибо, помогло!

У меня есть автоматический Морзе-маяк

Морзя - YouTube

На гитхабе его текст выложен

Есть 2 вопроса.
У меня Clock - 6 пин, заводится на прерываеие.
Через это подключается клавиатура и там вроде 11 бит. Вот прерывание происходит 11 раз?

Можно было бы все таки в самой функции ISR() реализовать азбуку морза? Почему там delay() не работает?

Потому что delay - это millis, который в свою очередь ISR для Timer0. А при нахождении программного указателя в ISR другой ISR не вызывается. Millis стоит, delay не работает.

Крокодил, соответственно, не ловится, не растений кокос.

Ваш код работы с клавиатурой в целом - чисто демонстрационный. Для устойчивой работы он плохо подходит, т.к. единичный сбой может привести к зависанию, из которого выход только по reset.
Распространен подход, когда устройство обмена (Клавиатура, Serial и т.д.) в прерывании помещает полученные символы в кольцевой буфер, а программа в loop проверяет наличие новых данных и обрабатывает их. Для клавиатуры есть довольно простые библиотеки с таким подходом.

А что за библиотеки?

Например PS2Keyboard - Arduino Reference

Остальные 10 прерываний единственно что сделают - выставят флаг, который при выполнении длинного цикла луп никак не повлияет. Трудности могут возникнуть если при выдаче сигнала Морзе придёт ещё один код. Тогда с большой вероятностью его или не примет программа или он будет оборван и программа зависнет до прихода следующего кода, который дополнит до 11 бит предыдущий и тоже будет оборван… Строки 47 - 56 как раз и отвечают за приём символа и они запрограммированы так, что прерывание необходимо только для первого фронта символа. По хорошему после входа в прерывание прерывания надо запретить, считать символ в прерывании и поместить его в буфер. Разрешить прерывания. В цикле loop проверять буфер и если в нем есть символы передавать их до освобождения буфера.

Запрещать не имеет смысла, т.к. AVR автоматом запрещает прерывания в ISR и разрешает при выходе.