Блок обработки команды фотоприемника
// Считываем содержимое приемного // Первый символ адреса модуля. // Второй символ адреса модуля. |
Пока RA6 в высоком состоянии («1»), ничего не надо делать. Проверим состояние USART. Если обращение идет не к нашему модулю, делать ничего не надо, вернемся к определению состояния RA6.
Я, как мне кажется, весьма рассудительно подошел к этому блоку программы. Но сомнения остаются. Я не уверен, будет ли она вообще работать, переключаясь с прослушивания от одного источника к другому.
Я, пожалуй, выделю блок и проверю его до написания остальной части программы. Не забудьте установить нужное состояние конфигурации (3FlAh) по адресу 2007Ы Мне понадобится файл заголовка:
#define MODULNAMESIM "С" #define CMDSIM "$"
Void putch (unsigned char); unsigned char getch (void) ; int init_comms () ; int sim_num_adr () ; int cmd();
И файл программы:
#include <picl6f62xa. h> #include <stdio. h> #include "irjrec. h"
Unsigned char input; регистра.
Unsigned char M0D_SIM1; unsigned char M0D_SIM2;
Unsigned char COMMAND; // Символ команды.
Int MOD_ADDR; // Заданный адрес модуля, как число.
Int siml_num = 0;
Int sim2_num = 0;
Int sim_end_num = 0;
Int MOD_NUM; // Полученный адрес модуля, как число.
Int PHOTO;
Int PHOTOCOME = 0;
Int FLAG = 0; // Временная переменная.
Unsigned char getch() {
While(!RCIF); // Устанавливается, когда регистр не
Пуст.
Continue; return RCREG;
}
// Вывод одного байта.
Void putch (unsigned char byte) {
PORTB = 1; // Переключим драйвер RS485 на передачу. TXEN = 1; // Разрешаем передачу.
While(!TXIF) // Устанавливается, когда регистр пуст.
Continue; TXREG = byte;
Int init_comms () { |
}
// Инициализация модуля.
OxCO; 0x7; OxCO; 0xF6; 0x90; 0x4; 0x16; |
PORTA CMCON TRISA TRISB RCSTA TXSTA SPBRG INTCON=0; PORTB = 0 } Int cmdO { |
// Настройка портов А и В.
// Настройка приемника. // Настройка передатчика.
// |
// Настройка режима приема-передачи. Запретить прерывания. // Выключим передатчик драйвера RS485.
// Получение и выполнение команды.
Input = getchO;
}
// Преобразуем символьный адрес в число.
Int sim_num_adr() {
Sim_end_num = 0;
Siml_num = getchO; // Чтение первого символа номера.
M0D_SIM1 = siml_num; // Сохраним первый символ. sim2_num = getchO; // Чтение второго символа номера.
M0D_SIM2 = sim2_num; // Сохраним второй символ. siml_num = siml_num - 0x30; sim2_num = sim2_num - 0x30; sim_end_nuni = siml_num*0x0A + sim2_num; return sim_end_num;
}
// Начнем работать.
Void main (void) {
Init_comms (); // Инициализация модуля.
//Прочитаем и преобразуем номер модуля.
M0D_ADDR = PORTB; // Номер модуля в старших битах. MOD_ADDR=MOD_ADDR»4; // Сдвинем на четыре бита.
Start: PHOTO = PORTA; // Начинаем работать,
If (PHOTO&0x40) PHOTOCOME = 0; // Нет ИК-сигнала. if (• (PHOTO&0x40)) PHOTOCOME = 1; // Появился ИК-сигнал.
While (PHOTOCOME == 1) // Обработаем ИК команду.
{
FLAG = 1 ;
Break; }
// Нет ИК-сигнала, проверим сеть, input = getch();
While(input == MODULNAMESIM) {
FLAG = 0 ; break;
}
В файл input. txt запишем:
"R03$0Nn "C03$0S -
Перед началом проверки запустим RB4 Set High (Установить в высокое состояние), RB5 Set High и RA6 Set High на Stimulus Controller (я изменил адрес с 01 на 03, а фотоприемник на вводе RA6 перевел в пассивное состояние).
Переменная FLAG • покажет, попадаем ли мы в нужное место программы. Впоследствии мы вместо нее будем вызывать подпрограммы обработки команды cmd () и ИК-команды ir_cmd(). Кнопками RA6 Set High и RA6 Set Low мы будем имитировать изменение состояния фотоприемника. Вроде бы работает. Хотя, признаюсь, получилось не сразу - я забыл задать слово конфигурации по адресу 2007h.
Перейдем к обработке ИК-команды (обработку команд сети, я думаю, возьмем из предыдущей программы, подправим, попробуем).
После получения заголовка и паузы в 0,55 мс мы можем ожидать прихода всего двух сочетаний импульс-пауза. Ноль - это импульс-пауза одинаковой длительности 0,55 мс. Единица - импульс-пауза, где импульс - 1,1 мс, а пауза - 0,55 мс.
Таким образом, нам нужны два интервала в 1,1 мс и 0,55 мс. Для получения интервалов можно воспользоваться таймерами или отсчитать их в пустом цикле. Я воспользуюсь второй возможностью.
Все команды, кроме переходов, выполняются за один машинный цикл (200 не при частоте 20 МГц). Положим, что при частоте 4 МГц машинный цикл равен 1 мкс. Тогда нам нужно досчитать до 1100 для промежутка в 1,1 мс и до 550 для короткого промежутка.
Сравнивая эти промежутки времени с приходящими импульсами, мы можем определить, единицу или ноль получаем в ИК-команде. Более того, мы можем установить только один счетчик до 550 (для верности возьмем 560) для вычисления байта команды.
На практике при первой проверке я успевал нажать кнопку RA6, чтобы имитировать приход ИК-команды. Теперь не успеваю. Слишком все быстро. Попробуем поискать, не может ли MPLAB помочь с симуляцией, как это было в случае с USART.
И действительно, как любил повторять один из моих знакомых, «Грамотная программа!». В разделе Debugger SCL Generator (Отладчик SCL генератор) находим New Workbook (Новая рабочая тетрадь). Щелкаем и открываем окошко, в котором можно настроить имитатор работы фотоприемника.
В очередной раз наступив на грабли (вместо того чтобы почитать описание), я начинаю понимать, что в первой колонке нужно задать текущее время, выделяя события. Для этого следует, щелкнув кнопку со стрелкой правее надписи Time Units (Единицы времени), выбрать мкс (us). Событием в данном случае будет изменение состояния вывода RA6 порта А, которое мы зададим, щелкнув большую клавишу Click here to Add Signals (Щелкните здесь, чтобы добавить сигналы). Из раскрывающегося меню выберем RA6 и получим еще одну колонку с этим именем. Теперь в колонку Time (dec) впишем десятичные значения текущего времени (мкс), а в колонку RA6 - состояния входа, соединенного с фотоприемником. Не следует забывать, что в отсутствие активности фотоприемник на выходе имеет высокий логический уровень («1»). Первый ИК-сигнал, с которым я хочу работать, выглядит так: 10000000.
Если вернуться к разговору об ИК-командах и выбранному решению по системным командам, то сигнал, поступающий с фотоприемника на вход RA6, должен выглядеть, как показано на рис. 1.49 и 1.50.
Значения, которые следует проставить, представлены в табл. 1.5.
Нет сигнала Заголовок 2750 мкс Единица 1100 мкс Ноль 550 мкс Ноль 550 мкс
550 мкс 550 мкс 550 мкс Проверка и т. д. 8 бит
Рис. 1.49. Схема сканирования бит ИК-команды
С—-, -■ ^ — ~ — «*. - '= • • - - *>- --- - « Г-: - с-....,. -- ' < ' і Рис. 1.50. Заполнение SCLWorkbook |
Таблица 1.5. Задание временных интервалов в сценарии ИК-команды
|
ИК-команда имеет вид 10000000 или 80h.
Теперь нужно сохранить Workbook и, щелкнув кнопку Generate SCL From Workbook (Іенерировать SCL из рабочей тетради), сохранить. scl-файл. Для использования этой имитации ИК-команды в Simulus Controller необходимо щелкнуть кнопку Attach (Прикрепить), указав сохраненный прежде. scl-файл. После сохранения Workspace все необходимое будет появляться при загрузке проекта.
Спасибо создателям программы MPLAB, которые позаботились о возможности работы с портами ввода-вывода в разных их применениях!
Теперь я собираюсь проделать следующее: при активизации RA6 я перейду к функции обработки ИК-команды:
• пропускаем заголовок;
• пропускаем перебивку в 550 мкс;
• при активизации RA6 приходящим битом запустим «ожидалку» на 560 мкс (немного длиннее импульса в 550 мкс), после которой проверим состояние ввода RA6; если это ноль, бит «1», если единица, - бит «0» (на схеме сканирования это обозначено словом «Проверка»);
• если единица, еще раз включим «ожидалку», если ноль, не будем этого делать;
• запишем бит в переменную IRCOMMAND, сдвинем влево на одну позицию;
• все это проделаем с 8 битами ИК-команды.
Поскольку я человек ленивый, моя «ожидалка» самая простая:
For (і = 0; і<560; ++і); // Ждем-с
Было ли это наказанием за лень, или я не справился с настройками сценария, но, промучившись несколько Часов, выяснил, что во время выполнения цикла for сценарий подхватывает циклы команды и успевает выполниться весь, прежде чем осуществляется выход из цикла ожидания. Я пробовал в качестве единиц измерения Time Units и циклы (сус) и мкс (us) - не помогло.
Конечно, хорошо бы выяснить, в чем проблема, но единожды наказанный за лень, я решил из двух зол выбрать меньшее,
организовав «ожидалку» на таймере, как это и положено. Пока я, чертыхаясь, пытался оживить программу с циклом for, я заметил, что цикл while не мешает работе сценария.
В конечном счете, эта часть программы (в состоянии проверки) получилась следующей:
#include <picl6f62xa. h> #include <stdio. h> #include "ir__rec. h"
Unsigned char input; регистра.
Int PH0T0C0ME = 0; int M0D_ADDR; int IRCOMMAND = 0;
// Считываем содержимое приемного
// Флаг активности фотоприемника. // Заданный адрес модуля, как число.
Unsigned char getch () {
While(!RCIF) // Устанавливается, когда регистр не пуст.
Continue; return RCREG;
}
// Вывод одного байта.
Void putch (unsigned char byte) {
PORTB = 1; // Переключим драйвер RS485 на передачу. TXEN = 1; // Разрешаем передачу.
While(!TXIF) // Устанавливается, когда регистр пуст.
Continue; TXREG = byte;
Int init_comms () { PORTA = OxCO; CMCON = 0x7; TRISA = OxCO; TRISB = 0xF6; RCSTA = 0x90; TXSTA = 0x4; SPBRG = 0x16; INTCON=0; // PORTB = 0; |
}
// Инициализация модуля.
// Настройка портов А и В.
// Настройка приемника. // Настройка передатчика.
// Настройка режима приема-передачи. Запретить прерывания. // Выключим передатчик драйвера RS485.
PIEl = 0; // Настройка таймера 1, запрет прерывания.
ТІ CON =0; // Выбор внутреннего генератора, бит 1 в ноль. }
Int ir_cmd () {
Int b = 0;
While ((PORTA&0x40)== 0) continue;// Ждем окончания заголовка.
For (b=0; b<8;++b) // Обработаем наши импульсы.
{
While ((PORTA&0x40) != 0) continue; // Дождемся импульса.
Time (); // Включаем таймер 1.
While (ITMR1IF); // Дождемся такта.
ТІ CON = 0x0; // Выключаем таймер.
TMR1IF = 0x0; // Сбросим флаг.
If ((PORTA&0x40) ==0) // Если низкий уровень, то
{
IRCOMMAND = IRCOMMAND +1; // Запишем это. time (); // Включаем таймер 1.
While (1TMR1IF); // Дождемся такта.
T1CON = 0x0; // Выключаем таймер.
TMR1IF = 0x0; // Сбросим флаг.
} else // Высокий уровень, значит "0".
{
IRCOMMAND = IRCOMMAND +0; // Запишем это. }
IRCOMMAND = IRCOMMAND«l; //Сместимся влево на
Бит.
}
// Уехали мы в смещениях на бит далеко, поэтому
IRCOMMAND = IRCOMMAND»l; // сместимся вправо на
Бит.
PHOTOCOME = 0;
}
Int cmd() {
Input = getch(); // switch (COMMAND = getchO) {
// case "N": rel_on(REL_NUM);
11 break;
11 case "F": rel_off (REL_NUM) ;
11 break;
11 case "S": rel_stat (REL_NUM) ;
11 break;
// }
Void time() {
TMR1H = OxFD; переполнения. TMR1L = 0xA7; TlCON = 0x1;
}
// Установка числа циклов до
// FFFF минус 600 (258h). // Включение таймера.
Void main (void) {
Init_comms(); // Инициализация модуля.
//Прочитаем и преобразуем номер модуля.
MOD_ADDR = PORTB; // Номер модуля в старших битах. MOD_ADDR=MOD_ADDR»4; // Сдвинем на четыре бита, start: if (PORTA&0x40) PHOTOCOME =0; //Нет ИК сигнала.
If (I(PORTA&0x40)) PHOTOCOME =1; // Появился ИК - сигнал.
While (PHOTOCOME == 1) {
// Обработаем ИК команду.
Ir_cmd ();
Break; }
// Нет ИК-сигнала, проверим сеть.
Input = getch () ;
While (input == MODULNAMESIM) {
Cmd (); break;
// Начнем работать. |
}
Все это получилось не сразу, но, вроде бы, заработало. Теперь осталось проверить программу с другими вариантами
|
С командой 10000011, или 83h:
|
И можно двигаться дальше. Предстоит добавить обработку запросов по системной сети, преобразование позиционного кода ИК-команды в символьный вид. Функцию обработки запросов по системной сети, подправив, я возьму из версии программы для предыдущего модуля.
Можно слегка изменить и сценарий имитации работы фотоприемника, добавив задание адреса модуля (пусть он пока остается «03») и подключение к порту А фотоприемника RA6 = 1.
Для этого в Workbook SCL Generator кнопкой Click here to Add Signals добавим еще три столбца: RB4, RB5 и PORTA, а в конце (как в табл. 1.5.) - информацию, представленную в табл. 1.6.
Таблица 1.6. Добавление к таблице временных интервалов
|
Теперь при запуске программы будет задан адрес модуля, и фотоприемник установит вывод RA6 в «1».
В итоге получилась (не слишком аккуратная) следующая программа:
#include <picl6f62xa. h> #include <stdio. h> #include nir_rec. hn
Unsigned char input; // Считываем содержимое приемного регистра.
Unsigned char M0D_SIM1; // Первый символ адреса модуля, unsigned char M0D_SIM2; // Второй символ адреса модуля, unsigned char IRSIM1; unsigned char IRSIM2; unsigned char IRSIM3;
Int PHOTOCOME = 0; // Флаг активности фотоприемника.
Int MOD_ADDR; // Заданный адрес модуля, как число.
Int IRCOMMAND = 0;
Int siml_num = 0;
Int sim2_num = 0;
Int sim_end_num = 0;
Int MOD_NUM; // Полученный адрес модуля, как число.
Unsigned char getch() {
While(!RCIF) // Устанавливается, когда регистр не пуст.
Continue; return RCREG;
}
// Вывод одного байта.
Void putch (unsigned char byte) {
PORTB = 1; // Переключим драйвер RS485 на передачу. TXEN =1; // Разрешаем передачу.
Int init_comms () { PORTA = OxCO; CMCON = 0x7; TRISA = OxCO; TRISB = 0xF6; RCSTA = 0x90; TXSTA = 0x4; SPBRG = 0x16; |
While(!TXIF) // Устанавливается, когда регистр пуст.
Continue; TXREG = byte;
}
// Инициализация модуля.
// Настройка портов А и В.
// Настройка приемника. // Настройка передатчика.
// Настройка режима приема-передачи.
INTCON=0; // Запретить прерывания. PORTB = 0; // Выключим передатчик драйвера RS485.
PIE1 = 0; // Настройка таймера 1, запрет прерывания.
T1C0N =0; // Выбор внутреннего генератора, бит 1 в ноль.
// Определим номер модуля. MOD_ADDR = PORTB; // Номер модуля в старших битах.
MOD_ADDR=MOD_ADDR»4; // Сдвинем на четыре бита. }
// Преобразуем символьный адрес в число.
Int sim_num_adrO {
Sim_end__num = 0; siml_num = getchO; //
MODJSIM1 = siml_num; // sim2_num = getchO; //
MOD_SIM2 = sim2_num; // siml_num = siml_num - 0x30; sim2_num = sim2_num - 0x30; sim_end_num = siml_num*0x0A return sim_end_num;
}
Int ir_cmd () {
Int b = 0;
Чтение первого символа номера. Сохраним первый символ. Чтение второго символа номера. Сохраним второй символ.
+ sim2_num;
While ((PORTA&0x40)== 0) continue;// Ждем окончания заголовка.
For (b=0; b<8;++b) //Обработаем наши импульсы.
{
While ((PORTA&0x40) != 0) continue; // Дождемся импульса.
Time О; // Включаем таймер 1.
While (ITMR1IF); // Дождемся такта.
T1CON = 0x0; // Выключаем таймер.
TMR1IF = 0x0; // Сбросим флаг.
If ((PORTA&0x40) ==0) // Если низкий уровень, то
П ^ п
{
IRCOMMAND = IRCOMMAND +1; // Запишем это. time О; // Включаем таймер 1.
While (ITMR1IF); // Дождемся такта.
T1CON = 0x0; // Выключаем таймер.
TMR1IF = 0x0; // Сбросим флаг.
} else // Высокий уровень, значит "0".
{
IRCOMMAND = IRCOMMAND +0; // Запишем это. }
IRCOMMAND = IRCOMMAND«l; //Сместимся влево на бит.
}
// Уехали мы в смещениях на бит далеко, поэтому
IRCOMMAND = IRCOMMAND»!; // сместимся вправо на бит.
PHOTOCOME = 0;
}
Int cmd() {
MOD_NUM = sim_num_adr(); //Чтение из сети (файла).
If (MOD_NUM == MOD_ADDR) // Если наш адрес модуля. {
Input = getch ();
If (input =="$") // Если символ команды. {
While ((input = getch())!= "S"); // Ждем.
Ir_stat(); }
}
}
Void time()
TMR1H = OxFD; переполнения. TMR1L = 0xA7; T1CON = 0x1;
Int ir_stat() {
Int ircom; int ircoml; int ircom2; int ігсотЗ;
Ircom = IRCOMMAND; // Преобразуем команду в символы.
Ircoml = ircom/0x64;
IRSIM1 = ircoml + 0x30;
Ircom2 = (ircom - ircoml*0x64)/OxA;
IRSIM2 = ircom2 + 0x30;
ІгсотЗ = (ircom - ircoml*0x64 - ircom2*0xA);
IRSIM3 = ігсотЗ + 0x30;
Putch ("С");
Putch (MOD_SIMl);
Putch (MOD_SIM2);
// Установка числа циклов до // FFFF минус 600 (258h). // Включение таймера. |
If (IRCOMMAND ==0) // Команда не менялась.
{
Putch("#"); putch("f"); putch(" f");
Putch(ОхОА); // Только для вывода в файл! I! I!!
} else //За время между двумя запросами пришла ИК
Команда. {
Putch(IRSIM1); putch(IRSIM2); putch(IRSIM3);
Putch (ОхОА); // Только для вывода в файл!!!!!!
}
IRCOMMAND =0; //Мы передали ИК команду, она нам больше не нужна.
Void main (void) // Начнем работать. {
Init_comms(); // Инициализация модуля.
// Прочитаем и преобразуем номер модуля.
MOD_ADDR = PORTB; // Номер модуля в старших битах. MOD_ADDR=MOD_ADDR»4; // Сдвинем на четыре бита. // Начинаем работать
Start: if (PORTA&0x40) PHOTOCOME =0; //Нет ИК-сигнала.
If (! (PORTA&0x40)) PHOTOCOME = 1; // Появился ИК - сигнал.
While (PHOTOCOME == 1) {
// Обработаем ИК-команду.
Ir_cmd (); break; )
// Нет ИК-сигнала, проверим сеть, input = getch ();
While (input == MODULNAMESIM) // Если в сети обращение к
Модулю. {
Cmd (); // Обработаем сетевую команду,
Break;
}
В настоящий момент меня уже одолевают сомнения. Пока активность в сети не мешает приему ИК-команд. Не будет ли она мешать, когда появится функция обработки запросов? Можно попытаться рассчитать это, но расчеты могут оказаться достаточно сложными. И не будет ли мешать прием ИК - команд системным запросам? Не стану считать, попробуем, посмотрим.
В крайнем случае, если не забуду, в основной программе системы при отсылке запроса о состоянии модуля ИК-приемника, сделаю паузу и при отсутствии ответа повторю запрос.
Есть и еще одно сомнение - не будет ли в реальных условиях неправильно считываться ИК-команда? Здесь тоже можно что-нибудь придумать. Например, повторять системную команду трижды, а в модуль добавить три считывания, принимая в качестве команды ту, которая совпадает дважды. На данном этапе я предпочту отложить решение проблем до момента их появления, хотя, как мне кажется, самое время обо всем позаботиться.
Работа выше приведенной программы происходит при input. txt такого вида:
"R03$0N" "R01$0N" "C03$0Sn
Опция Rewind Input на странице Uartl Ю установлена. В результате имитация сетевых команд постоянно «крутится», принимая три команды, последняя из которых запрашивает состояние модуля фотоприемника.
Результат работы программы выводится, как и раньше, в файл output. txt:
C03#ff// Команда не менялась
С03131// Пришла команда 131 (десятичная) или 10000011 двоичная
C03#ff// Команда не менялась после последнего запроса C03#ff// Команда не менялась C03#ff// Команда не менялась
По дороге я еще раз наступил на грабли. Внеся исправления в программу, я обнаружил, что она не желает работать. Во всяком случае, работать так, как мне хочется. Пытаясь понять, в чем дело, я много раз просматривал программу, пока не обнаружил, что один из операторов имеет тот же цвет, что и комментарий. Как это получилось? Я использую много однострочных комментариев (тоже следствие лени) и если в конце строчного комментария не нажат ввод при удалении промежутков между операторами программы, следующий воспринимается программой как продолжение комментария.
{
Cmd (); // Обработаем сетевую команду. ++COONTER; break;
}
Goto start;
Результат эксперимента - за время прохождения двух ИК - команд (83h и 2Oh) счетчик досчитал до 5.
Файл input. txt, который «крутится» без перерывов выглядит так:
"R03$ON" "R01S0N"
"C03$0S" Обращение к модулю приемника ИК команд
"R03$0N"
"R01$0N"
"C03$0S" Обращение к модулю приемника ИК команд
Файл output. txt получился следующим:
C03#ff Команда не обновлялась С03131 Команда 131 (83h) C03#ff Команда не обновлялась C03#ff Команда не обновлялась С03032 Команда 32 (20h)
Таким образом:
• ИК-команды не потерялись за непрерывно следующими сетевыми командами;
• ИК-команды прочитались правильно;
• между приходами ИК-команд запрос статуса правильно отображал отсутствие обновления.
Посмотрим, как это все выглядит во времени. Первая ИК-команда приходит через 10 мс и заканчивается через 24 мс, вторая приходит через 60 мс и заканчивается через 73 мс после начала работы. Сетевые команды начинают поступать сразу после начала работы. Каждая сетевая команда занимает около 6,3 мс (имеет 60 бит, проходящих со скоростью -9600 бит/ с).
До начала ИК-команды при такой скорости должна пройти одна сетевая команда. За время считывания первой
ИК-команды ((10 мс - 6 мс) + 24 мс) = 28 мс пройдет 4 команды. После завершения первой и до конца второй пройдет 73 мс - 24 мс = 49 мс, что соответствует 7-8 командам. Таким образом, за все время пройдет 12-13 команд. То есть, файл input прочи- тается дважды или немного более. Что дает 4-5 обращений к модулю приемника ИК-команд. Файл output фиксирует 5 обращений, счетчик фиксирует 5 обращений.
Для большей уверенности посмотрим, как распределяются сетевые обращения:
• за время от начала работы до завершения считывания ИК-команды проходит 5 системных запросов, среди которых один к ИК-приемнику;
• в выходном файле этому соответствует один ответ об отсутствии обновления команд;
• следующая команда во входном файле это запрос к модулю;
• в выходном файле на втором месте стоит ответ о приходе команды 131 (к этому моменту считывание первой команды завершено);
• до завершения считывания второй команды проходит семь системных команд, среди которых два обращения к модулю приемника;
• в выходном файле два ответа - команда не обновлялась (предыдущая уже передана);
• следующая сетевая команда - запрос о состоянии модуля приемника, который следует сразу за завершением считывания ИК-команды, ответ - команда 32 в выходном файле.
Если я ничего не напутал, даже при короткой основной программе (центрального управляющего устройства), когда запросы о состоянии модуля приемника идут достаточно часто, ответ успевает доходить до адресата. Если учесть, что в тестовом варианте две команды проходят с интервалом в 36 мс, что в 10-30 раз быстрее реально отправляемых команд (интервал между двумя нажатиями на одну и ту же клавишу составляет от 0,3 до 1 с), то запас, я думаю, есть.
Конечно, мои подсчеты не страдают излишней продуманностью и точностью, но я на время решил успокоиться.