Отладка модуля
Прототип я делаю на той же макетной плате, на которой собирал релейный модуль. По этой причине я включаю фотоприемник на вход RB3. Для индикации приема ИК-команд дополнительно использую вывод RA0, к которому уже подключен светодиод. Когда устанавливается флаг прихода ИК - команды, светодиод включается. Флаг снимается - светодиод выключается.
If (!(RB3&0x0D) {
PHOTOCOME = 1; RAO = 0x01;
}
Else
{
PHOTOCOME = 0; RAO = 0x00;
}
Вот и очередные «грабли»!
Приоритет приема ИК-команд хорошо бы закрепить однозначно. Например, так: {
While((1RCIF)&(RB3 == 1))
/* устанавливается, когда регистр не пуст */ continue; return RCREG;
}
Я выделил добавленный фрагмент. У прототипа фотоприемник подключен к выводу RB3. Смысл добавленного фрагмента в том, что на сетевые запросы ответ будет, если модуль не занят приемом ИК-команд, то есть RB3 = 1.
Неплохо было бы защитить модуль от помех по RB3. При включении может «проскочить» короткий нулевой импульс (на чем я и споткнулся), и модуль перейдет в режим приема ИК-команды, которой нет. Поправил я это так:
Start: if (RB3&0x01) // Нет ИК сигнала. {
PHOTOCOME = 0;
RAO = 0x00;
Break;
}
If (! (RB3&0x01)) // Появился ИК сигнал.
{
For (k=0; k<30; ++k); // Поставим задержку.
If (! (RB3&0x01)) // ИК сигнал не пропал?
{
PHOTOCOME = 1;
RAO = 0x01;
}
} else
{
PHOTOCOME = 0;
RAO = 0x00;
Break;
}
Я добавляю задержку, а затем еще раз проверяю наличие активности фотоприемника. Короткий случайный импульс проскочит за время задержки, а длинный импульс команды приведет механизм считывания в действие. Помогло. Если в первый раз после включения питания (сразу или через несколько секунд) модуль «подвисал» на приеме ИК-команды, то после исправления перестал.
Итак, модуль получает команду. Возможно, распознает ее. Он даже передает ее по запросу через RS485. А что получится, если команда не единичная, то есть в том случае, когда идет поток одинаковых команд? Что будет, если команды не системные, а чужие? Не знаю.
Первое, что приходит в голову - запустить еще один таймер сразу после завершения считывания ИК-команды. И пока он не завершит отсчет времени больший, чем занимают 2-3 команды, не принимать ИК-коды. Попутно я пытаюсь воспроизвести код, аналогичный тому, что использовал в отладчике MPLAB, из программы WinLIRC, аппаратный модуль которой имеет излучающий светодиод.
Проходит два дня. Результаты не впечатляют. Коды прочитываются не слишком уверено. Я увеличиваю интервал сканирования с 600 до 700 мкс, но совсем не удовлетворен стабильностью работы модуля. В отличие от релейного, модуль приема системных ИК-команд мне совсем не нравится. Он, вроде бы, работает, но...
В конечном счете, я меняю излучатель кодов. Использую оригинал - старый пульт от видеомагнитофона Sony. Меняю второй таймер на обычный цикл в команде считывания кодов (выделено в тексте программы), попутно решаю внести исправления в сдвиг на один бит. У меня получился лишний сдвиг, который я после получения ИК-команды убирал обратным сдвигом. После замены переменной IRCOMMAND с типа int на unsigned char я стал терять старший бит..
Замена всех этих «шатаний из стороны в сторону» оказалась простой (изменение выделено). Если я не ошибаюсь, этого достаточно.
Int ir_cmd О {
Int b = 0;
While (RB3 == 0) continue; // Ждем окончания заголовка.
For (b=0; b<8; b++) //Обработаем наши импульсы.
{
While (RB3 1= 0)
Continue; // Дождемся импульса,
Time (); // Включаем таймер 1. while (1TMR1IF); // Дождемся сброса таймера. T1C0N = 0x0; // Выключаем таймер. TMR1IF = 0x0; // Сбросим флаг.
If (RB3 ==0) // Если низкий уровень, то "1". {
IRCOMMAND = IRCOMMAND +1; // Запишем это. time (); // Включаем таймер 1.
While (ITMR1IF); // Дождемся сброса таймера. T1CON = 0x0; // Выключаем таймер. TMR1IF = 0x0; // Сбросим флаг. } else // Высокий уровень, значит И0И.
IRCOMMAND = IRCOMMAND; // Запишем это.
If (b<7) IRCOMMAND = IRCOMMAND«l; //Сместимся влево
На бит.
}
PHOTOCOME = 0x0;
RA0 = 0x0; RAl=0xl;
For (ш=0; m<3; ++ш) // Перестанем щзинимать ИК-ходы.
For (1=0; 1<10000; ++1); RA1 = 0x0;
}
Здесь на выводе порта A RA1 я использую еще один свето - диод, чтобы видеть окончание команды.
В результате модуль хорошо распознает команды от пульта видеомагнитофона. Чувствительность высокая, распознавание стабильное (рис. 1.51).
Рис. 1.51. Прием ИК-кодов модулем |
Не удовлетворяют меня только два момента:
• иногда во время паузы между приемами ИК-команд запрос по сети «подвешивает» модуль в нераспознанном месте (помогло сокращение времени этой паузы);
• коды с пульта плохо согласуются с моими ожиданиями.
Вот сравнение кодов, прочитанных с помощью WinLIRC в режиме распознавания, и кодов, которые транслирует модуль в ответ на запрос по сети RS485:
Номер канала 1.
Модуль возвращает номер команды 001 - 00000001 (С14001 - в ответ на запрос статуса).
7F2h - 11111110010 (инверсия 00000001101), без последних трех бит 0000001.
. Номер канала 2.
Прочитано модулем 129 - 10000001
3F2h - 01111110010 (инверсия 10000001101), без последних трех бит 10000001.
Номер канала 3.
Прочитано модулем 65 - 1000001
5F2h - 10111110010 (инверсия 01000001101), без последних трех бит 1000001
Номер канала 6.
Прочитано модулем 161 - 10100001
2F2h - 01011110010 (инверсия 10100001101), без последних трех бит 10100001.
Номер канала 9.
Прочитано модулем 017 - 10001
772h - 11101110010 (инверсия 00010001101), без последних трех бит 00010001.
Power.
Прочитано модулем169 -10101001
2B2h - 1010110010 (инверсия 0101001101), без последних трех бит 0101001.
Команда включения канала 1 в записи WinLIRC - 7F2h, но я определяю единицу и ноль, как это описано в технической информации о кодах Sony, которой располагаю, а в WinLIRC сделано наоборот. Можно инвертировать код. Кроме того, я использую 8-битовый код, а оригинальный код - 11-битовый. Тоже не страшно, отбросим три последних бита. Смущающим меня обстоятельством служит то, что команды каналов 1 и 2 не совпадают, как я ожидал после превращений с инверсией и отбрасыванием. Аналогичная история с командой power. Пока я не понимаю, как это происходит.
Шутка калькулятора, который отбрасывает первые нули! Но понял я это позже.
Последнее, что я делаю, измученный борьбой с «собственной гениальностью», - проверяю работу модуля в заготовке под среду программирования. Модуль работает. Я использую два кода: номер включения канала 1 - для тестирования команды, а канала 2 - для включения лампы. Оба кода стабильно работают.
Думаю, пока следует остановиться. Основная задача - получить модуль считывания системных команд, которые стабильно распознавались бы системой, достигнута. При этом, как это и планировалось, используется старенький пульт от видеомагнитофона. А если что-то осталось непонятно, что ж... Будем надеяться, что это позже прояснится (рис. 1.52).
Рис. 1.52. Работа модуля с основной программой |
Самым непонятным для меня оказалось то, что попытка использовать третий светодиод для индикации включенного модуля - я несколько раз вытаскивал микросхему из панельки без отключения питающего напряжения - успехом не увенчалась. Я попробовал разные варианты включения. Ничего не получилось! Мистика!?
В данный момент программа модуля приема системных кодов получилась такой. Файл заголовка:
#define MODULNAMESIM ИСИ
Unsigned char getch(void); •
Int init_canms () ;
Int ir_cmd ();
Int and ();
Void time ();
Void time_cmd();
Int ir_stat ();
Файл основной программы:
// Считываем содержимое приемного |
// Первый символ адреса модуля. // Второй символ адреса модуля. |
#include <picl6f62xa. h> #include <stdio. h> #include "ir_rec_01.h"
Unsigned char input; регистра.
Unsigned char M0DJSIM1; unsigned char M0D_SIM2; unsigned char IRSIM1; unsigned char IRSIM2; unsigned char IRSIM3;
Unsigned char command_reciev [6]; // Массив для полученной команды.
Int PHOTOCOME =0; // Флаг активности фотоприемника.
Int MOD_ADDR; // Заданный адрес модуля, как число.
Unsigned char IRCOMMAND = 0;
Int siml_num = 0;
Int sim2_num = 0;
Int sim_end_num = 0;
Int MOD_NUM; // Полученный адрес модуля, как число.
Int і int k int 1 int m
Unsigned char getch() {
While((!RCIF)&(RB3 == 1)
Continue; return RCREG;
}
// Когда регистр не пуст. |
// Вывод одного байта
// Когда регистр пуст. |
Void putch(unsigned char byte) {
While(ITXIF) continue; TXREG = byte;
}
Int init_comms () {
PORTA = 0x00; CMCON = 0x7; TRISA = 0x00; TRISB = OxFE; PCON = OxFF; RCSTA = OblOOlOOOO; TXSTA = ObOOOOOllO; SPBRG = 0x68; N,8,1.
INTCON = 0x0; RB0 = 0x0; PIE1 = 0x0; T1CON = 0x0; ноль.
TMR1H = 0x00; TMR1L = 0x00; IRCOMMAND = 0x0; MOD_jADDR = PORTB;
MOD_ADDR=MOD__ADDR»4 ; }
// Инициализация модуля. |
// Настройка портов А и В. |
Тактовая частота 4 МГц. // Настройка приемника. // Настройка передатчика. //Настройка режима приема-передачи 2400, // Запретить прерывания. Выключим передатчик драйвера RS485. Настройка таймера 1, запрет прерывания. |
// |
И // II Выбор внутреннего генератора, бит 1 в |
// Обнулим таймер. /* Определим номер модуля */ // Номер модуля в старших битах. // Сдвинем на четыре бита. |
// Преобразуем символьный адрес в число, int sim_num_adr()
{
Sim_end_num = 0x0; M0D_SIM1 = command_reciev [1]; M0DJSIM2 = command_reciev [2]; M0D_SIM1 = M0DJSIM1 - 0x30; MOD_SIM2 = MOD_SIM2 - 0x30; sim_end_num = MOD_SIMl*OxOA + MOD return sim_end_num;
}
Int ir_cmd () {
Int b = 0;
While (RB3 == 0) continue; // Ждем окончания заголовка.
For (b=0; b<8; b++) //Обработаем наши импульсы.
{
While (RB3 != 0)
Continue; // Дождемся импульса,
Time (); // Включаем таймер 1. while (1TMR1IF); // Дождемся сброса таймера. T1CON = 0x0; // Выключаем таймер. TMR1IF = 0x0; // Сбросим флаг.
If (RB3 == 0) // Если низкий уровень, то "І". {
IRCOMMAND = IRCOMMAND +1; // Запишем это. time ()? // Включаем таймер 1.
While (ITMR1IF); // Дождемся сброса.
T1CON = 0x0; // Выключаем таймер.
TMR1IF = 0x0; // Сбросим флаг.
} else // Высокий уровень, значит "О".
IRCOMMAND = IRCOMMAND; // Запишем это.
If (b<7) IRCOMMAND = IRCOMMANDccl; //Сместимся влево
На бит. }
PHOTOCOME = 0x0; RAO = 0x0; RAl=0xl;
For (m=0; m<3; ++m) // Перестанем принимать ИК.
// Первый символ номера. // Второй символ номера. SIM2; |
For (1=0; 1<10000; ++1); RA1 = 0x0;
Int cmd() {
If (commanderееіev [5] = "S") ir_stat();
}
Void timeO {
TMR1H = OxFD; переполнения. TMR1L = 0x43; T1CON = 0x1;
} // Таймер 1 для чтения ИК.
// Установка числа циклов до
// FFFF минус 700 (2BCh). // Включение таймера.
Int ir_stat() {
Int ircom; int ircoml; int ircom2; int ігсотЗ;
Command_reciev[0] = "C"; command_reciev[l] = MOD_SIMl+0x30; command_reciev[2] = MOD_SIM2+Ox3Q;
Ircom = IRCOMMAND; // Преобразуем команду в символы.
Ircoml = ircom/0x64;
IRSIMl = ircoml + 0x30;
Ircom2 = (ircom - ircoml*0x64)/OxA;
IRSIM2 = ircom2 + 0x30;
ІгсотЗ = (ircom - ircoml*0x64 - ircom2*0xA); IRSIM3 = ігсотЗ + 0x30;
If (IRCOMMAND == 0) {
Command_reciev[3j = n#n; // Команда не менялась. command_reciev[4] = "f"; command_reciev[5] = "f"-;
CREN =0x0; // Запрещаем прием.
RB0 = 0x1; //Переключим драйвер RS485 на передачу.
TXEN = 0x1; // Разрешаем передачу.
For (i=0; i<6; ++i) putch (commander ее iev [ і ]);
For (i=0;i<1000;i++); // Задержка для вывода, for (i=0; i<6; ++i) command_reciev [і] = " RBO = 0x0; // Выключаем драйвер RS485 на передачу.
TXEN = 0x0; // Запрещаем передачу. CREN =0x1; // Разрешаем прием.
} else //За время между двумя запросами пришла ИК
Команда. {
Cominand_reciev[3] = IRSIMl; command_reciev[4] = IRSIM2; command_reciev[5] = IRSIM3;
CREN =0x0; // Запрещаем прием.
RBO = 0x1; //Переключим драйвер RS485 на передачу.
TXEN =0x1; // Разрешаем передачу.
For (i=0; i<6; ++i) putch (command_reciev [і]) ;
For (i=0;iclOOO;i++); // Задержка для вывода.
For (i=0; i<6; ++i) command_reciev [i] = " ";
RBO = 0x0; // Выключаем драйвер RS485 на передачу.
TXEN =0x0; // Запрещаем передачу.
CREN =0x1; // Разрешаем прием. }
IRCOMMAND = 0x0; //Мы передали команду, она больше не
Нужна. }
Void main (void) // Начнем работать.
{
Init_comms(); // Инициализация модуля,
For (i=0; i<6; ++i) command_reciev [і] = " "; command_reciev [0] = "С";
//Прочитаем и преобразуем номер модуля. MOD_ADDR = PORTB; // Номер модуля в старших битах. MOD_ADDR=MOD_ADDR»4; // Сдвинем на четыре бита. RA2 = 0x1; // Чтобы видно, что модуль включен.
// Так и не получилось!!
// Начинаем работать.
Start: if (RB3) // Нет ИК-сигнала.
{
PHOTOCOME = 0x0; RAO = 0x00;
If (!RB3) // Появился ИК-сигнал {
For (k=0; k<30;++k); // Поставим задержку.
If (!RB3) // ИК-сигнал не пропал?
{
PHOTOCOME = 0x1? RAO = 0x01;
}
} else {
PHOTOCOME = 0x0; RAO = 0x00;
}
While (PHOTOCOME == 1) {
// Обработаем ИК-команду.
Ir_cmd ();
Break; }
// Нет ИК-сигнала, проверим сеть. CREN =0x1; input = getch();
Switch (input) {
Case "С": // Если обращение к фото модулю.
For (i=l; i<6; ++i) // Запишем команду в массив.
{
Input = getch(); command_reciev [і] = input;
}
MOD_NUM = sim_num_adr (); // Чтение из сети.
If (MODJNUM!= MOD_ADDR) break; // Если не наш адрес.
Else
If (commanderееіev [3] = n$n) cmd(); // Если
Команда.
Default: goto start;
}