Программа модуля излучения ИК-кодов на языке С
Файл заголовка
#define MODULNAMESIM "І"
Unsigned char getch (void); int init_comms () ; int cmd (); void ir_trns();
Основной файл
#include <picl6f62xa. h> #include <stdio. h> # inc lude " і r_t rans_fnl. h"
Unsigned char input; // Считываем содержимое приемного регистра.
Unsigned char M0D_SIM1; // Первый символ адреса модуля, unsigned char M0D_SIM2; // Второй символ адреса модуля, unsigned char command_reciev [6]; // Массив для полученной команды.
Int MOD_ADDR; int MOD_ADDR; int sim_end_num int MODJNUM; int і = 0; unsigned char b int с = 0; int d = 0; int к = 0; int 1 = 0; int m = 0;
Unsigned char ir_cmd [60]; // Массив для прочитанной ИК - команды.
Unsigned char getchO {
While (І RCIF) // Устанавливается, когда регистр не пуст.
// Заданный адрес модуля, как число. // Заданный адрес модуля, как число.
= 0; // Полученный адрес модуля, как число. = 0; |
Continue; return RCREG;
Int init_comms () { |
}
// Инициализация модуля.
0x0; 0x7; 0x0; OxFE;
PORTA CMCON TRISA TRISB RCSTA TXSTA SPBRG RBO = 0; CREN =1; |
0Ы0010000; ObOOOOOllO; 0x68;
// Настройка портов А и В.
// Настройка приемника. // Настройка передатчика. // Настройка режима приема-передачи. // Выключаем драйвер RS485 на передачу.
// Определим номер модуля. MOD_ADDR = PORTB; // Номер модуля в старших битах.
MOD_ADDR=MOD_ADDR»4; // Сдвинем на четыре бита,
For (i=0; i<61; ++i) ir_cmd[i] = 0x00; і = 0;
RA2 = 1; }
// Преобразуем символьный адрес в число.
Int sim_num_adr() {
Sim_end_num = 0;
M0D_SIM1 = command_reciev [1]; 11 Первый символ номера.
MOD_SIM2 = command_reciev [2]; // Второй символ номера.
M0D_SIM1 = M0D_SIM1 - 0x30;
M0D_SIM2 = M0D_SIM2 - 0x30;
Sim_end_num = MOD_SIMl*OxOA + M0D_SIM2;
Return siitL_end_num;
Int cmd() i=0;
// Принимаем данные.
While ((input = getchO) != OxFF) {
Ir_cmd[i] = input;
++i; }
Ir_cmd[i] = input;
Void ir_trns() {
1 = 0;
For (1 = 0; 1<4; ++1) {
С = 3;
While (ir_cmd[c] != OxFF) // Наш байт окончания
Команды. {
// Импульс, b = ir_cmd[c]; • ++с;
While (b 1= 0) {
RA1 = 1; RAl = 1; RA1 = 1; RAl = 1; RAl = 1; RAl = 1;' RAl = 1;
RAl = 1; RAl = 1; RAl = 1; RAl = 1; RAl = 1; RAl = 0; RAl = 0;
—b; }
// Пауза, b = ir_cmd[c];
++C;
While (b 1= 0) {
|
—b; }
}
For (m=0; m<1563; ++m); // Пауза в 25 мс. }
For (i=0; i<61; ++i) ir_cmd[i] = 0x00; і = 0;
}
Void main(void) // Начнем работать.
{
Init_comms (); // Инициализация модуля,
For (k=0; k<6; ++k) command_reciev [к] = и и; commander ее і ev [0] = "Iй;
//Прочитаем и преобразуем номер модуля. MOD_ADDR = PORTB; 11 Номер модуля в старших битах -
M0D_ADDR=M0D1ADDR»4; // Сдвинем на четыре бита.
// Ждем прихода команды и данных.
Start: RA2 =1;
Input = getchO; switch (input)
{
Case "Iй: // Если обращение к модулю ИК команд.
For (k=l; k<6; ++k) // Запишем команду в массив.
{
Input = getch(); commancLreciev [k] = input;
}
MODJNUM = sim_num_adr(); // Определим, к кому адресуются, if (MOD_NUM!= MOD_ADDR) break; // Если не наш адрес, else
If (command_reciev [5] == ИРИ) {
Cmd (); / / Если команда.
Ir_trns(); // Отправим ИК команду на излучатель. }
Default: goto start;
}
Goto start;
}
НЕХ-файл для загрузки в программатор
:10000000830100308A0004282030840078300D20DD :1000100083012D2B04068001840A0406031D0A288F :020020000034АА
:1005520083018C1EA92A1A080800FC01FD01031060 :10056200FB0CFA0C031CBC2A7808FC077908031858 :10057200790AFD070310F80DF90D7A087B040319B7 :100582000034B02A8301AC01AD01D02A2C083C3ED4 :100592008400831323088000AC0A0319AD0AA92240 :1005A200A300230FC72A2C083C3E84008313230890 :1005B200800008008301B401B5013708A0003808A3 :1005C200A100D030A007A1070A30FA00FB012008E1 :1005D200F800F901AE2221087C07B4007D08031857
:1005E2007D0AB500F9003408F8000800830185018E
:1005F20007309F0083168501FE30860090308312FB
:10060200980006308316980068309900831206100D
:1006120018160608A400A5010430F800250DA50C43
:10062200A40CF80B0F2BAC01AD012D08803AF80099
:10063200803078023D3003192C020318292B2C0834
:100642003C3E840083138001AC0A0319AD0A162BC9
:10065200AC01AD0105150800F722AE01AF013A2B3E
:100662002E08363E8400831320308000AE0A031920
:10067200AF0A2F08803AF80080307802063003195A
:100682002E02031C312B4930B6000608A400A50136
:100692000430F800250DA50CA40CF80B4B2B7C2B79
:1006A200AE01AE0AAF012F08803AF800803078021E
:1006B200063003192E0203186A2BA922A3002E0862
:1006C200363E8400831323088000AE0A0319AF0A62
:1006D200542BDB227808A6007908A7002506031D03
:1006E200742B24082606031D7C2B3B08503A031D5D
:1006F2007C2BC32283230515A922A300493A03199F
:10070200512B7C2B8301B001B101B001B101DE2B71
:100712000330A800A901C52B28083C3E840083139E
:100722000008A200A80A0319A90AA2080319A92B02
:1007320085148514851485148514851485148514EF
:10074200851485148514851485108510A203962BB3
:1007520028083C3E840083130Q08A200A80A03195B
:10076200A90AA2080319C52B8510851085108510CA
:1007720085108510851085108510851085108510CF
:1007820085108510A203B22B28083C3E84008313F7
:10079200000F8D2BB201B3013308803AF800863086
:1007A20078021B30031932020318DB2BB20A031939
:1007B200B30ACD2BB00A0319B10A3108803AF80006
:1007C20080307802043003193002031C892BAC01FB
:1007D200AD012D08803AF800803078023D300319CF
:1007E2002C020318FD2B2C083C3E8400831380014D
:0E07F200AC0A0319AD0AEA2BAC01AD010800F8
:00000001FF
Возвращаясь к представлению в некотором «собственном формате», попробуем «прикинуть», как все будет выглядеть.
EEPROM хранит 128 байт. Если мы будем использовать модуль излучателя ИК-кодов для одного устройства, сколько управляющих команд нужно для него в системе? Возьмем, например, DVD-проигрыватель. Какие команды нужны пользователю, чтобы смотреть фильмы? Проиграть, Стоп,
Предыдущий фрагмент, Следующий фрагмент, Громче, Тише, Выключить звук, команды курсора Влево, Вправо, Вверх, Вниз, Ввод, Меню.
Для ровного счета возьмем 15 команд. Таким образом, на одну команду мы можем потратить 8 байт. Много это или мало? Ответ на вопрос следовало бы искать в следующем разделе - «Модуль считывания инфракрасных кодов», проведя считывание всех интересующих нас ИК-команд.
В данный момент я решаю, что нет смысла хранить коды в модуле воспроизведения ИК-команд, будем хранить их на компьютере. Предварительно примем формат команды вида, показанного в табл. 1.8.
Таблица 1.8. Предполагаемый формат ИК-кода
|
При этом в отличие от предыдущих модулей, модуль излучения должен получать по сети не только команду, но и данные, следующие за ней. Если мы определим префикс модуля с помощью латинской буквы «I», команда может выглядеть следующим образом: 1хх$пР и байты данных, где п - количество повторов. Я думаю, что повторов от 0 до 9 должно хватить.
Байты данных начнутся с байта коэффициента и закончатся нулевым байтом. Мы не знаем, сколько именно будет передано байт. Для определенности возьмем 50 байт. Значит, все данные будут переданы приблизительно через 50 мс. После приема данных модуль может начать воспроизведение ИК-кода. Таким образом, задержка между нажатием на клавишу управления и отправкой управляющего ИК-кода будет не более 0,1-0,2 с. Думаю, не слишком много.
Осталось два момента, которые мене сейчас не ясны. Первый - хватит ли места в регистрах контроллера для хранения команды. В первых двух банках памяти 176 байт отведено для регистров общего назначения. Полагаю, этого хватит, но хранение в двух банках...
Второй момент, смущающий меня, относится к некоторой вероятности того, что передаваемые данные, а это байты, совпадут с видом команды. Не думаю, что это очень вероятно, но эту неприятность мы можем отслеживать на этапе записи ИК-кода. Если, паче чаяния, это произойдет, воспользуемся коэффициентом пересчета, преобразовав данные к другому виду.
Теперь мы готовы начать разработку нового модуля.
За основу возьмем предыдущий модуль, у которого используем вывод RA6 на выход. К этому выводу порта мы будем подключать светодиод ИК-диапазона (излучатель, или ИК - эмиттер).
Как обычно, я создаю папку в рабочей папке MPLAB, которую называю irjxans, и копирую в нее все файлы предыдущего модуля. Теперь я открываю в программе MPLAB предыдущий проект, но уже из этой папки. Сохраняю его под новым именем - ir_trans, а также файл заголовков и программы, меняю их в менеджере проекта, включая файл todo. txt, настраиваю установки Debugger. Осталось поменять файлы сценария, удалить из папки все старые файлы, и можно начинать работу.
Вся эта процедура необязательна. Можно открыть новый проект, но при этом не следует забывать, что все его настройки следует сделать заново. Задать рабочую тактовую частоту контроллера и скорость выполнения анимации. Иначе можно наступить на грабли, которые подстерегали нас в самом начале работы.
В отличие от предыдущих данный модуль при настройке должен прочитывать из файла input. txt последовательность данных. Вернемся к рекомендациям изготовителя микроконтроллера PIC16F628A и после строки команды в файле input. txt впишем данные в виде шестнадцатеричных чисел, записанных в столбик и завершаемых командой OAh. Но сначала подготовим их. Возьмем код из раздела «Модуль считывания инфракрасных кодов».
Pulse 2455 Заголовок space 532
Pulse 1291 Единица space 506
Pulse | 664 | Ноль |
Space | 533 | |
Pulse | 1266 | Единица |
Space | 533 | |
Pulse | 665 | Ноль |
Space | 591 | |
Pulse | 1211 | Единица |
Space | 533 | |
Pulse | 685 | Ноль |
Space | 515 | |
Pulse | 693 | Ноль |
Space | 526 | |
Pulse | 1271 | Единица |
Space | 506 | |
Pulse | 1265 | Единица |
Space | 533 | |
Pulse | 666 | Ноль |
Space | 557 | |
Pulse | 1241 | Единица |
Space | 533 | |
Pulse | 664 | |
Input. txt |
ИК03$0Ы"
И103$5РИ Команда обращения к модулю трансляции ИК команды
01 Количество повторов
1 Коэффициент
25 Частота несущей 37 кГц
97 Младший байт импульса заголовка (шестнадцатеричного числа)
09 Старший байт импульса заголовка
14 Младший байт паузы
2 Старший байт паузы
ЕЗ Младший байт импульса единицы (1251 мкс - среднее)
04 Старший байт импульса единицы
4А Младший байт паузы единицы (586 мкс - среднее)
02 Старший байт паузы единицы
4А Младший байт импульса нуля (586 мкс - среднее)
02 Старший байт импульса нуля
4А Младший байт паузы нуля (586 мкс - среднее)
02 Старший байт паузы нуля
ЕЗ Младший байт импульса единицы (1251 мкс - среднее)
04 Старший байт импульса единицы
4А Младший байт паузы единицы (586 мкс - среднее)
02 | Старший | Байт | Паузы единицы |
4А | Младший | Байт | Импульса нуля (586 мкс - среднее) |
02 | Старший | Байт | Импульса нуля |
4А | Младший | Байт | Паузы нуля (586 мкс - среднее) |
02 | Старший | Байт | Паузы нуля |
ЕЗ | Младший | Байт | Импульса единицы (1251 мкс - среднее) |
04 | Старший | Байт | Импульса единицы |
4А | Младший | Байт | Паузы единицы (586 мкс - среднее) |
02 | Старший | Байт | Паузы единицы |
4А | Младший | Байт | Импульса нуля (586 мкс - среднее) |
02 | Старший | Байт | Импульса нуля |
4А | Младший | Байт | Паузы нуля (586 мкс - среднее) |
02 | Старший | Байт | Паузы нуля |
4А | Младший | Байт | Импульса нуля (586^мкс - среднее) |
02 | Старший | Байт | Импульса нуля |
4А | Младший | Байт | Паузы нуля (586 мкс - среднее) |
02 | Старший | Байт | Паузы нуля |
ЕЗ | Младший | Байт | Импульса единицы (1251 мкс - среднее) |
04 | Старший | Байт | Импульса единицы |
4А | Младший | Байт | Паузы единицы (586 мкс - среднее) |
02 | Старший | Байт | Паузы единицы |
ЕЗ | Младший | Байт | Импульса единицы (1251 мкс - среднее) |
04 | Старший | Байт | Импульса единицы |
4А | Младший | Байт | Паузы единицы (586 мкс - среднее) |
02 | Старший | Байт | Паузы единицы |
4А | Младший | Байт | Импульса нуля (586 мкс - среднее) |
02 | Старший | Байт | Импульса нуля |
4А | Младший | Байт | Паузы нуля (586 мкс - среднее) |
02 | Старший | Байт | Паузы нуля |
ЕЗ | Младший | Байт | Импульса единицы (1251 мкс - среднее) |
04 | Старший | Байт | Импульса единицы |
4А | Младший | Байт | Паузы единицы (586 мкс - среднее) |
02 | Старший | Байт | Паузы единицы |
4А | Младший | Байт | Импульса нуля (586 мкс - среднее) |
02 | Старший | Байт | Импульса нуля |
00 | Завершающий нулевой байт |
Конечно, файл не должен содержать моих пометок. Я использовал одинаковые усредненные числа. Прав ли я? Это можно будет проверить только при воспроизведении. На данном этапе это не играет роли. Позже, возможно, мы напишем программу, которая будет упаковывать считанные данные. Посмотрим.
Теперь нам нужно позаботиться о том, где мы будем хранить полученные данные. Для этой цели используем массив беззнаковых символов:
Unsigned char ir_cmd [53];
Я задал массив длинною в 53 байта. Вообще мы не знаем, какой длины получится массив, но можно вернуться к этому позже.
Основной переделке подвергается функция прочитыва - ния команды. За последним символом команды ' Р' следуют данные. Количество повторов мы получаем перед командой. Но мы оставили под это байт данных. Поэтому пока будем иг норировать количество повторов в команде. Ожидаем ' Р', начинаем заполнение массива. Основная часть программы:
// Начинаем работать. // Ждем прихода команды и данных, start: while (input!= MODULNAMESIM) input = getchO; // Ждем обращения к модулю.
Cmd (); // Обработаем сетевую команду,
Goto start;
// Обработка сетевой команды.
Int cmd () {
MODJNUM = sim_num_adr (); // Чтение из сети (файла) .
If (MOD_NUM == MOD__ADDR) // Если наш адрес модуля. {
While (input! = ИР") input = getchO; // Ждем.
If (input == ИР") // Если символ завершения команды. {
// Принимаем данные.
While (i<54) {
Input = getch();
Ir_cmd[i] = input;
++i; }
Перед запуском программы я не забываю установить в высокое состояние биты, имитирующие переключатель адреса модуля (RB4, RB5 в окне Stimulus Controller).
Очередные грабли, на которые я наступаю, - программа не работает. После первого символа в регистре приемника USART появляется 02. Это не номер модуля, дальше ничего не получается. Не пытаюсь разобраться, просто добавляю в файл inputtxt еще пару команд перед командой обращения к модулю.
"R02$1N" "R01$1N" "І03$5РИ
Теперь программа работает, массив заполняется. Добавив его в наблюдение, можно посмотреть, как он заполняется, и есть ли печатные символы в наших данных.
После заполнения массива мы готовы передать ИК-команду. У нас три временных интервала:
Pulse 2455 Заголовок space 532
Pulse 1291 Единица
Несущая частота - 37 кГц период -27 мс. Через время -13 мс будем менять состояние вывода RA6 порта А, если у нас в массиве записан импульс.
Можно, как в программе предыдущего модуля, использовать встроенные таймеры контроллера. Но для начала попробуем использовать простые циклы.
Void ir_trns() {
І = 0;
С = 3; //В первых трех байтах служебная информация.
While (ir_cmd[i] != 0) // Наш байт окончания команды. {
// Импульс.
B = ir_cmd[c+l]; // Сначала прочитаем старший байт,
B = b«8; // Сместимся на один байт,
B = b + ir_cmd[c]; //Сложим байты, получая длительность импульса.
С = с + 2; // Сместимся на два байта.
While'(b!= 0) {
For (d=0;d<14;++d) RA6 = 1; // Теперь с частотой в
37 кГц
For (d=0;d<14;++d) RA6 = 0; // будем создавать несущую // частоту. —Ь;
}
// Пауза, b = ir_cmd[c+l]; b = b«8;
B = b + ir_cmd[c]; //В этой переменной мы храним длительность паузы, с = с + 2;
While (b 1= 0) {
For (d=0;d<28;++d) RA6 = 0; // Передаем паузу.
—b; }
}
}
В глобальные переменные я добавил несколько индексных переменных. Их можно добавить локально, но глобальные переменные удобнее наблюдать.
Int і = 0; int b = 0; int с = 0; int d = 0;
Пытаясь передать массив, я обнаружил, что по умолчанию он не обнуляется, поэтому в раздел инициализации модуля я добавляю обнуление массива. Проблема, которая возникает без этого обнуления, состоит в том, что мы записываем байт, а работаем с двухбайтовыми числами. Нулевой байт записывается в конец массива, но второй байт не нулевой, тогда как условие
While (b!= 0) // где b - целое.
For (i=0; i<61; ++i) ir_cmd[i] =0; // Обнуление массива.
Теперь, вроде бы все работает, но мне хочется посмотреть, что транслируется излучателем, соединенным с выводом
RA6. Программа позволяет воспользоваться программным логическим анализатором. Для этого я выделяю всю часть void ir_trns (), правой кнопкой мыши вызываю раскрывающееся меню, в котором выбираю раздел Add Filter in Trace (Добавить фильтр в Трассировку). Теперь, во View (Вид) основного меню добавляю к рабочему окну Simulator Trace (Симулятор трассировки) и жду, когда заполненный массив начнет транслироваться.
Я ожидаю, что мне удастся увидеть весь передаваемый через RA6 код. Запускаю Simulator Logic Analyzer (Симулятор логического анализатора), в котором с помощью кнопки Channels (Каналы) вхожу в меню выбора отслеживаемых выводов. Затем выбираю RA6 и добавляю вывод в наблюдение - Add (Добавить).
Вопреки желаемому, потратив достаточно много времени, получаю очередное напоминание о «граблях». Удается зафиксировать только фрагменты. Пытаюсь добавить управление еще одним выводом порта А - RA0, устанавливая единицу перед формированием импульса и сбрасывая в ноль при паузе. Единственное, что получилось, - это ряд фрагментов, показанных на рис. 1.54-1.56.
Rao - | |||||
- т------------------- 1---------- 1------------- 1----------- 1---------- т-------------- 1----------- 1----------- 1----------- 1----------- 1----------- 1------------ 1----------- 1--------
5000000 1000000.0 15000000 |
Рис. 1.54. Первый фрагмент эксперимента с логическим анализатором |
Первый из фрагментов относится к моменту завершения передачи заголовка. Во втором следом за заголовком передается единица. На последнем фрагменте видно, что передача несущей сменяется паузой. Не густо.
Оставлю программу модуля, удалив управление выводом RA0, и проверю работу модуля, когда соберу его. У меня нет уверенности, что частота несущей - 37 кГц (или близка к ней),
Рис. 1.55. Второй фрагмент эксперимента с логическим анализатором
Тгадчюиммямо
0201303131020100028730313101
Рис. 1.56. Третий фрагмент эксперимента с логическим анализатором
А длительность импульсов и пауз не требует дополнительной калибровки. Пока я не могу сделать больше, оставлю все, как есть.
#include <picl6f62xa. h> #include <stdio. h> #include "ir_trans. h"
Unsigned char input; регистра.
Unsigned char M0DJSIM1; unsigned char M0DJSIM2; int MOD_ADDR; int MOD_ADDR; int siml_num = 0; int sim2_num = 0; int sim_end_num = 0; int MOD_NUM; int і = 0
// Считываем содержимое приемного
// Полученный адрес модуля, как число. |
// Первый символ адреса модуля. // Второй символ адреса модуля. // Заданный адрес модуля, как число. // Заданный адрес модуля, как число.
Int b int с
Int d = 0;
Unsigned char ir_cmd [60];
Unsigned char getch() {
While(!RCIF) // Устанавливается, когда регистр не пуст.
Continue; return RCREG;
}
// Вывод одного байта.
Void putch (unsigned char byte) {
//Переключим драйвер RS485 на передачу. // Разрешаем передачу.
// Устанавливается, когда регистр пуст.
}
Intinit_comms() // Инициализация модуля. {
PORTA = 0x0; // Настройка портов А и В. CMCON = 0x7; TRISA = 0x80 TRISB = 0xF6
RCSTA = 0x90; // Настройка приемника.
TXSTA = 0x4; // Настройка передатчика.
SPBRG = 0x16; // Настройка режима приема-передачи.
INTCON=0; // Запретить прерывания.
PORTB =0; // Выключим передатчик драйвера RS485.
// Определим номер модуля.
MOD_ADDR = PORTB; // Номер модуля в старших битах. MOD_ADDR=MOD_ADDR>>4; // Сдвинем на четыре бита.
For (i=0; i<61; ++i) ir_cmd[i] = 0; )
// Преобразуем символьный адрес в число.
Int sim_num_adr() {
Sim_end_num = 0;
Siml_num = getch(); //Чтение первого символа
Номера.
PORTB = 1; TXEN = 1; while(1TXIF) continue; TXREG = byte; |
MOD_SIMl = siml_num; // Сохраним первый символ. sim2_num = getch(); //Чтение второго символа номера.
M0D_SIM2 = sim2_num; // Сохраним второй символ.
Siml_num = siml_num - 0x30;
Sim2_num = sim2_num - 0x30;
Sim_end_num = siml_num*0x0A + sim2_num;
Return sim_end_num;
}
Int cmd О {
MOD_NUM = sim_nuni_adr (); // Чтение из сети (файла).
If (MOD_NUM == MOD_ADDR) // Если наш адрес модуля. {
While (input! = ИРИ) input = getchO; // Ждем.
If (input == ИР") // Если символ завершения команды. {
I=0;
// Принимаем данные, while (i<54)
{ input = getch (); ir_cmd[i] = input; ++i;
}
}
}
}
Void ir_trns() {
І = 0; с = 3;
While (ir_cmd[i] 1= 0) // Наш байт окончания команды. {
// Импульс, b = ir_cmd[c+l]; b = b«8;
B = b + ir_cmd[c]; с = с + 2; RAO = 1;
While (b!= 0) {
For (d=0;d<14;++d) RA6 = 1; for (d=0;d<14;++d) RA6 = 0; —b;
}
// Пауза, b = ir_cmd[c+l]; b = b«8;
B = b + ir_cmd[c]; с = с + 2; RAO = 0;
While (b!= 0) {
For (d=0;d<28;++d) RA6 = 0;
—b; }
}
Void main (void) // Начнем работать.
{
Init_comms (); // Инициализация модуля.
//Прочитаем и преобразуем номер модуля. M0D_ADDR = PORTB; // Номер модуля в старших битах. M0D_ADDR=M0D_ADDR»4; // Сдвинем на четыре бита.
// Начинаем работать. // Ждем прихода команды и данных, start: while(input!= MODULNAMESIM) input = getch(); // Ждем обращения к модулю.
Cmd (); // Обработаем сетевую команду.
Ir_trns(); II Отправим ИК команду на излучатель.
Goto start;
}
Все получается разумно и красиво, логично и грамотно. Ай, да я!
Смущает одно. Когда я проверяю времена, соответствующие картинкам анализатора, получаю совсем не то, что хотелось бы. В моем приборном арсенале нет записывающего осциллографа. Представив, как я пытаюсь поймать и измерить последовательность импульсов ИК-команды обычным осциллографом (если вам не приходилось, попробуйте), я решаю вернуться к MPLAB. Верю я тем, кто создал эту замечательную программу! И начинаю понимать, что совсем не «Ай, да я!». Где-то я ошибаюсь.
Напишем простую программку:
#include <picl6f62xa. h> #include <stdio. h>
Int d = 0; int с = 0;
Int init_comms () {
PORTA = 0x0; CMCON = 0x7; TRISA = 0x80; TRISB = 0xF6; RCSTA = 0x90; TXSTA = 0x4; SPBRG = 0x16; INTCON=0;
PORTB = 0; }
Void main (void) {
Init_comms (); // Здесь поставим точку останова.
// Начинаем работать.
Start: for (d=0;d<2;++d) RA6 = 1; for (c=0;c<2;++c) RA6 = 0;
Goto start;
}
Настроим, установив тактовую частоту контроллера 4 МГц, все, что настраиваем обычно. Выделим с помощью Add Filter in Trace строки, выделенные в тексте, и в пошаговом режиме сделаем четыре шага по программе. Посмотрим, что нам рисует логический анализатор (рис. 1.57).
// Инициализация модуля. // Настройка портов А и В. |
// Настройка приемника.
// Настройка передатчика. // Настройка режима приема-передачи. // Запретить прерывания. // Выключим передатчик драйвера RS485. |
Время, определяемое в циклах команд контроллера для состояния «0», - около 75 циклов, или 75 мкс. Но я устанавливаю, как я полагал, вывод RA6 в ноль на один цикл. В состоянии «1», что по моим предположениям тоже должно соответствовать 1 мкс, я «зависаю» на 78 мкс. Проверяю маркером (рис. 1.58).
1 | |||||||||
Ra6 - | |||||||||
1 | _J | ||||||||
1 1 1 1 ■ ' 1 1 1 ' » 1 1 1 ' ' 1 1 > ' ' Г 1 1 1 ' 1 1 ' ■ ' 1 1 1
ВОЛ 100.0 120.0 140.0 160.0 180.0 200.0 |
Рис. 1.57. Отображение логическим анализатором пошаговых команд |
Рис. 1.58. Измерение интервала времени маркером |
Так и есть. Не нравится мне это, но пока я не понимаю, в чем дело.
Включив в Simulator Trace режим просмотра в инженерных единицах, я получаю следующую картинку за один проход моих циклов for (вид программы в трассировщике MPLAB) - рис. 1.59. Это не полная картина трассировки, строки следуют до номера 45, что соответствует моменту времени в 115 мкс от начала процесса.
A DC г
ІЮ. | |||||||||
I I I I I I I I I I I I I Г I t I 1 I I I I » I I I I I I I I I I Г I I I I I I | I I I I I
700 75.0 80.0 85.0 90.0 950 100.0 105.0 110.0 115.0 |
Рис. 1.59. Определение интервала времени одного прохода цикла for И я медленно начинаю понимать...
Это в своем правильном логическом мире, описанном языком С, я за одну команду успеваю перейти от «0» к «1» и обратно. В реальном мире контроллера это занимает ровно столько времени, сколько требует команд. На 82-й микросекунде текущего времени я устанавливаю RA6 в единицу, что трассировщик отображает в 12-й строке командой BSF 0x5, 0x6 (рис. 1.60).
Спасибо программистам, создавшим программу MPLAB - сидеть бы мне за осциллографом, чертыхаясь, долго и безуспешно!
Спасибо тем, кто учил меня писать программы в машинных кодах, когда я начал заниматься микропроцессорной техникой; тем, кто терпеливо пояснял мне, как правильно отсчитывать «branch» в командах!
Посыпав голову пеплом, как и полагается, попробуем изменить ситуацию к лучшему. В первую очередь, постараемся добиться, чтобы циклы были одинаковы. Я же хочу получить меандр частотой 37 кГц.
Экран в данный момент выглядит, как показано на рис. 1.61 (я добавил внешний цикл for).
Изменив часть программы и манипулируя числами, я несколько меняю результат (рис. 1.62):
Start: а = 0;
While (а<100) {
For (d=0;d<3;++d) RA6 = 1; for (с=0;с<2;++с) RA6 = 0;
++a; }
Goto start;
Меандр я получаю, но частота несущей остается близка к 8 кГц, тогда как мне нужна частота раз в пять выше. Пока я вижу несколько решений - повысить в пять раз тактовую частоту контроллера, настроить внешний генератор на частоту 37 кГц или использовать внутренние резервы контроллера.
Попытка использовать таймер несколько улучшила ситуацию, но не в полной мере. Последний вариант, не затрагивающий коренных переделок всей программы или модуля, выглядит следующим образом:
Start: а = 0;
For (а=0; а<100;++а) {
RA6 = 1; RA6 = 1; RA6 = 1; RA6 = 1; RA6 = 1; RA6 = 1; RA6 = 1; RA6 = 1; RA6 = 1; RA6 = 1; RA6 = 0; RA6 = 0;
}
Goto start;
Период сигнала в 27 мкс соответствует частоте несущей 37 кГц. Это не самое изящное решение. В первую очередь, теперь следует отказаться от изменения несущей частоты ИК-команды «на лету». Модуль будет работать с фиксированной несущей частотой. Посмотрим, можно ли поправить программу модуля.
Не забудьте проверить модуль приема ИК-кодов. Нет ли и там подобной ошибки!
С несущей частотой положение несколько улучшилось, но времена посылок раз в 10 превышают реальные. Первое, что мне приходит в голову, - уменьшить в 10 раз времена в файле input. txt. В этом есть и привлекательная сторона - теперь массив, в котором я храню времена, становится не двухбайтовым, а однобайтовым. Но посылки все еще слишком длинные.
Укорачиваем их еще вдвое, разделив значение времени на два уже в программе. И последнее «заметание мусора под ковер» - деление в файле input. txt всех значений на 1,38.
Мне не нравится то, что я делаю. Но в итоге я получаю результат, показанный на рис. 1.64.
]
III і і і'I'И—м
11ШШЇ |
-I 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 Г
45000.0 50000.0 55000,0 60000.0
Рис. 1.64. Вид ИК-сигнала на экране логического анализатора
Это даже больше, чем я ожидал. Вернее, я ожидал этого в начале работы, но, не справившись с логическим анализатором, отказался от попыток получить подобную картинку. Здесь^несущая, если ее увеличить, выделив затемненный участок сигнала RA6 с помощью мыши и клавиши выбора на инструментальной панели (пятая слева), выгладит, как показано на рис. 1.65, заголовок имеет время, как на рис. 1.66, а единица и пауза, рис. 1.67 и 1.68, соответственно.
Рис. 1.65. Вид несущей частоты |
В? ЕГ '
Ra8 - ra0 -
--------------- ,--------- ,---------- ,--------- ,---------- 1------- 1------- 1-------- 1------- 1-------- 1------- 1------- 1-------- 1------- 1------- 1-------- 1------- 1-------- 1-------
.ЖШШЇ |
45000.0 50000.0 55000.0 60000.0
Рис. 1.66. Вид заголовка
■в- | " В? ЕЯЕі І | ||||||
Ras ra0 ■
J |
Ш | Ц
Ж |
І
1257 ■ |
III II
І™ |
IIII
1ЛЛГ |
Й
И |
J |
45000.0 | 5 | —і—і—і—і—і—і—і—і—і—і—і—г - і 0000 0 55000.0 $0000.0 i |
Рис. 1.67. Вид единицы |
Їв-: - д wf *
- 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 г 45000.0 50000 0 55000.0 60000л Рис. 1.68. Вид паузы |
Ras ra0 |
Времена близки к оригиналу. Последние грабли, на которые я умудряюсь наступить - в функции обработки массива:
Void ir_trns() {
І = 0; с = 3;
While (ir_cmd[i] != 0) // Наш байт окончания команды.
Забываю, заменить і на с. Вот так:
Void ir_trns() {
І = 0; с = 3;
While (ir_cmd[c] != 0) // Наш байт окончания команды.
Без этого, закончив передачу ИК-команды, программа меняет байт, из которого я начинаю позже вычитать, и, вычитая из нуля, я получаю опять ненулевой байт, который при проверке циклом while вместо завершения работы функции начинает «пороть чушь». Для отправки я распаковываю массив не с самого начала, а с третьего байта, поскольку в первых трех байтах (от 0 до 2) у меня служебная информация (с = 3), а в этом месте поменять я забыл.
Итоговая программа выглядит так:
#include <picl6f62xa. h> #include <stdio. h> #include "ir_trans. h"
Unsigned char input; // Считываем содержимое приемного регистра.
Unsigned char M0DJSIM1; // Первый символ адреса модуля, unsigned char M0DJSIM2; // Второй символ адреса модуля, int MOD__ADDR; // Заданный адрес модуля, как число,
Int M0D_ADDR; // Заданный адрес модуля, как число,
Int siml_num = 0; int sim2_num = 0; int sim_end_num = 0;
Int MOD_NUM; // Полученный адрес модуля, как число,
Int і = 0; int с = 0;
Unsigned char ir_cmd [60]; unsigned char b = 0;
Unsigned char getch() {
While(!RCIF) // Устанавливается, когда регистр не пуст.
Continue; return RCREG;
// Вывод одного байта.
Void putch (unsigned char byte) {
While(ITXIF) continue; TXREG = byte; |
Int init_comms () {
PORTA = 0x0; CMCON = 0x7; TRISA = 0x80 j TRISB = 0xF6 RCSTA = 0x90j TXSTA = 0x4; SPBRG = 0x16; INTCON=0; PORTB = 0; |
PORTB = 1; //Переключим драйвер RS485 на передачу. TXEN =1; // Разрешаем передачу.
// Устанавливается, когда регистр пуст.
}
// Инициализация модуля. // Настройка портов А и В.
// Настройка приемника. // Настройка передатчика.
// Настройка режима приема-передачи. // Запретить прерывания.
// Выключим передатчик драйвера RS485.
// Определим номер модуля. MOD_ADDR = PORTB; // Номер модуля в старших битах. MOD_ADDR=MOD_ADDR»4; // Сдвинем на четыре бита.
For (i=0; i<61; ++i) ir_cmd[i] = 0; }
// Преобразуем символьный адрес в число.
// Чтение первого символа номера. // Сохраним первый символ. // Чтение второго символа номера. // Сохраним второй символ. |
Int sim_num_adr () {
Sim_end_num = 0; siml_num = getch(); M0D_SIM1 = siml_num; sim2_num = getch(); MOD_SIM2 = sim2_num; siml_num = siml_num - 0x30; sim2_num = sim2_num - 0x30; sim_end_num = siml_num*0x0A + sim2_num; return sim_end_num;
Int cmd()
MOD_NUM = sim_num_adr(); 11 Чтение из сети (файла).
If (MOD_NUM == MOD_ADDR) // Если наш адрес модуля. {
While (input!= ИРИ) input = getchO; // Ждем.
If (input == ИР") // Если символ завершения команды. {
I=0;
Input = getch();
// Принимаем данные.
While (input!= 0x0) {
Ir_cmd[i] = input/0x2; input = getch(); ++i;
}
}
Void ir_trns() {
І = 0; С = 3;
While (ir__cmd[c] != 0) // Наш байт окончания команды. {
// Импульс, b = ir_cmd[c]; ++с;
While | (b! | 0) | |
{ | |||
RA6 | = | 1 | |
RA6 | 1 | ||
RA6 | = | 1 | |
RA6 | = | 1 | |
RA6 | = | 1 | |
RA6 | = | 1 | |
RA6 | = | 1 | |
RA6 | = | 1 | |
RA6 | = | 1 | |
RA6 | = | 1 | |
RA6 | = | 1 |
RA6 = 1; RA6 = 1;
RA6 = 0; RA6 = 0; RA6 = 0; RA6 = 0; RA6 = 0; RA6 = 0; RA6 = 0; RA6 = 0; —Ь;
}
// Пауза.
B = ir_cmd[c]; ++с;
While (b!= 0) {
RA6 = 0; RA6 = 0; RA6 = 0; RA6 = 0; RA6 = 0; RA6 = 0; RA6 = 0; RA6 = 0; RA6 = 0; RA6 = 0; RA6 = 0; RA6 = 0; RA6 = 0;
RA6 = 0; RA6 = 0; RA6 = 0; RA6 = 0; RA6 = 0; RA6 = 0; RA6 = 0; RA6 = 0; —b;
}
Void main (void) // Начнем работать.
{
Init_comms (); // Инициализация модуля.
//Прочитаем и преобразуем номер модуля. MOD__ADDR = PORTB; // Номер модуля в старших битах. M0D_ADDR=M0D_ADDR»4; // Сдвинем на четыре бита.
// Ждем прихода команды и данных, start: while (input!= MODULNAMESIM) input = getchO; //Ждем обращения.
Cmd О; // Обработаем сетевую команду.
Ir_trns(); // Отправим ИК команду на излучатель,
Goto start;
}
Если ее запустить, набраться терпения и дождаться результата, он будет выглядеть, как показано на рис. 1.69 (отслеживание RA0 я убрал, оно было полезно только для определения времен).
Ra6 - | 11 | 111 | II | |||
20000.0 30000.0 | Т—і—і—і—і—і—і—і—і—і—і—і—| 40000.0 50000.0 600( | Ю.0 |
Рис. 1.69. Итоговый вид ИК-кода |
Именно такой сигнал мы могли бы увидеть на записывающем осциллографе, если бы воспроизвели ИК-команду с пульта устройства перед фотоприемником (без встроенной обработки сигнала). Картинка не была бы столь четкой, вероятно, но была бы очень похожа. Видно, что сигнал начинается с заголовка (pulse), затем следует короткая пауза (space), после нее - единичная посылка несущей со своей паузой и т. д. По этой картинке можно прочитать сигнал -10101001101.
Вернувшись к началу этого раздела, мы найдем данную команду.
Проверить работу модуля с этой командой можно несколькими способами (собрав модуль): прочитать команду с пульта и с модуля считывающим устройством и сравнить результаты или отправить команду на устройство, которое этой командой управляется. Работает устройство - работает и команда.
Однако прежде чем проверять работу собранного модуля, есть смысл проверить его работу в MPLAB с другими командами. У меня есть несколько считанных команд, проверкой работы которых я и хочу заняться, записав их для этого в новый input. txt файл. Попробую понять, что из этого получается.
Вот как выглядит команда PLAY одного из устройств фирмы Sharp (в представлении, приведенном к виду, который получился бы с помощью считывания в WinLIRC).
Исходный вид:
4000 dlOl 4000 dlOl 4000 с700 4000 с700
4000 с700 4000 с700 4000 dlOl 4000 с700
4000 с700 4000 с700 4000 dlOl 4000 с700
4000 с700 4000 dlOl 4000 с700 4000 6f2b
После первого преобразования:
0040 | Импульс |
Oldl | Пауза |
0040 | Импульс |
Oldl | Пауза |
0040 | Импульс и т. д. |
00с7 | |
0040 | |
00с7 | |
0040 | |
00с7 | |
0040 | |
00с7 | |
0040 | |
Oldl | |
0040 | |
00с7 | |
0040 | |
00с7 |
0040
00с7 0040 Oldl 0040 00с7 0040 00с7 0040 Oldl 0040 00с7 0040 2b6f
Времена-64 (0040), 465 (Oldl), 199 (00с7), 11119 (2b6f). Первый импульс - 256 мкс. Что предполагает коэффициент 4. Тогда времена - 256 мкс, 1860 мкс, 796 мкс, 44 476 мкс. И, наконец, в том виде, с которым мы начали работать (справа в шес - тнадцатеричном представлении):
Pulse 256, 12 Я разделил на 13,8 и перевел в НЕХ-формат
Space 1860 87
TOC o "1-3" h z pulse 256 12
Space 1860 87
Pulse 256 12
Space 796 ЗА
Pulse 256 12
Space 796 ЗА
Pulse 256 12
Space 796 ЗА
Pulse 256 12
Space 796 ЗА
Pulse 256 12
Space 1860 87
Pulse 256 12
Space 796 ЗА
Pulse 256 12
Space 796 ЗА
Pulse 256 12
Space 796 ЗА
pulse 256 | 12 |
Space I860 | 87 |
Pulse 256 | 12 |
Space 796 | ЗА |
Pulse 256 | 12 |
Space 796 | ЗА |
Pulse 256 | 12 |
Space I860 | 87 |
Pulse 256 | 12 |
Space 796 | ЗА |
Pulse 256 | 12 |
Space 44476 | C96 |
Осталось заменить данные в файле input. txt. Посмотрим, что получится.
Общий вид полученных импульсов изображен на рис. 1.70, а времена: первой паузы рис. 1.71, второй паузы и импульса рис. 1.72 и рис. 1.73. И период несущей частоты, рис. 1.74, похож на «настоящий».
Т 1 1 1 1 1 1 1 Г— 65000.0 700000 Рис. 1.70. Вид контрольного ИК-кода |
II! | III | 1 | 1 | 1 | |
182 | [®1 1— | M | 111 | Ill |
45000 0 50000.0 55000.0 |
65000.0 70000.0 |
Рис. 1.71. Вид первой паузы контрольного ИК-кода |
1 | 1II1 | Mill | III | 1 |
ІШи | ІЛШ | У |
I 1 1 1 1 1 1—I 1 1 1 1 1 1 1 1—I 1 1 1 1—I—I—і—I—I—Г
45000.0 500000 55000.0 . 60000.0 650000 лхю0.0 |
Рис. 1.72. Вид второй паузы контрольного ИК-кода
Рис. 1.73. Вцд импульса контрольного ИК-кода |
Рис. 1.74. Вид несущей контрольного ИК-кода |
Для сравнения посмотрите, как выглядят информационные импульсы в программе, из базы данных которой взяты ИК-коды Sharp (рис. 1.75).
Сравнивая его с видом импульсов, представленных как RA0 на рисунке вида контрольного код4, можно убедиться в их схожести. А времена (256 мкс против 259, 1860 мкс против 1828, 796 мкс против 825) не столь разительно отличаются. Несущая, правда, уехала к 38,5 кГц, но это может быть и не совсем точно измеренное значение (маркер несколько съехал с фронта импульса).
Рис. 1.75. Вид импульсов кода Sharp |
И что получилось?
Приступая к разработке, мы вполне разумно определились в том, что хотим от данного модуля. В процессе разработки, столкнувшись с трудностями, мы не менее разумно отказались от части первоначальных запросов. Написав первую версию кода программы, выглядевшую вполне «рассудительно и логично», мы отказались от нее. Предполагая хранить значения текущей длительности интервала в двух байтах, мы для ускорения работы программы приняли однобайтовый вариант хранения.
С точки зрения обучения это яркий пример того, как не надо делать. С практической точки зрения это пример того, как приходится иногда поступать, чтобы справиться с проблемой, обойдя ее хотя бы на время.
В нашем случае, пытаясь овладеть приемами работы с программой MPLAB и одновременно создав некоторую любительскую систему, мы в праве принять несколько решений:
• оставить все, как есть - мы не можем менять частоту несущей ИК-команды «на лету», но пока не убедились, что это нужно, можем не тревожиться; как выглядит программа, изящно или нет, после загрузки ее в контроллер едва ли будет кому-либо интересно; все что требуется, так это чтобы модуль исправно работал;
• можно собрать модуль, опробовать и, если работает, забыть о существовании проблем;
• и, наконец, третье решение - пересмотреть все, что сделано, и создать модернизированные версии тех же модулей; к концу работы появится больше опыта, чаще будет приходить в голову мысль о том, что если бы знать раньше, можно было бы сделать и лучше!
Лично я склоняюсь к тому, что, когда появятся эти мысли, тогда и займемся модернизацией. А пока рисуем схему и отправляемся в «Чип и Дип», собираем модуль.
Напомню формат, который мы «перелопатили», и действия, которые мы осуществляем, вопреки первоначальным замыслам.
Формат записи в файл ИК-команды представлен в табл. 1.9.
Таблица 1.9. Новый вариант формата ИК-кода
|
Импульс и пауза - это времена в мкс, деленные на 13,8 после прочитывания в программе WinLIRC в виде шестнад - цатеричных чисел. Затем, в программе, они будут еще раз разделены на 2.
В программе в настоящий момент не используется служебная информация, относящаяся к устройству, количеству повторов и коэффициенту. Позже, если решим модернизировать модуль, мы дополнительно сделаем несколько выходов, которые будут выбираться по записи «Устройство», добавим обработку количества повторов и, возможно, используем коэффициент 13 или 14 для деления времени, полученного при подготовке кода, самой программой. Если решим, что следует внести изменения в модуль.