Умный дом

Программа модуля излучения ИК-кодов на языке С

Файл заголовка

#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) {

RAl

=

0

RAl

=

0

RAl

=

0

RAl

=

0

RAl

=

0

RAl

=

0

RAl

=

0

RAl

=

0

RAl

=

0

RAl

=

0

RAl

=

0

RAl

=

0

RAl

=

0

RAl

=

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. Предполагаемый формат ИК-кода

Устройство

Команда

Повторов

Коэффициент

Частота

? байт

? байт

Байт

Байт

2 байта

Импульс

Пауза и т. д.

EOF

2 байт

2 байта

Байт 0

При этом в отличие от предыдущих модулей, модуль излу­чения должен получать по сети не только команду, но и дан­ные, следующие за ней. Если мы определим префикс модуля с помощью латинской буквы «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

Старший

Байт

Паузы единицы

Младший

Байт

Импульса нуля (586 мкс - среднее)

02

Старший

Байт

Импульса нуля

Младший

Байт

Паузы нуля (586 мкс - среднее)

02

Старший

Байт

Паузы нуля

ЕЗ

Младший

Байт

Импульса единицы (1251 мкс - среднее)

04

Старший

Байт

Импульса единицы

Младший

Байт

Паузы единицы (586 мкс - среднее)

02

Старший

Байт

Паузы единицы

Младший

Байт

Импульса нуля (586 мкс - среднее)

02

Старший

Байт

Импульса нуля

Младший

Байт

Паузы нуля (586 мкс - среднее)

02

Старший

Байт

Паузы нуля

Младший

Байт

Импульса нуля (586^мкс - среднее)

02

Старший

Байт

Импульса нуля

Младший

Байт

Паузы нуля (586 мкс - среднее)

02

Старший

Байт

Паузы нуля

ЕЗ

Младший

Байт

Импульса единицы (1251 мкс - среднее)

04

Старший

Байт

Импульса единицы

Младший

Байт

Паузы единицы (586 мкс - среднее)

02

Старший

Байт

Паузы единицы

ЕЗ

Младший

Байт

Импульса единицы (1251 мкс - среднее)

04

Старший

Байт

Импульса единицы

Младший

Байт

Паузы единицы (586 мкс - среднее)

02

Старший

Байт

Паузы единицы

Младший

Байт

Импульса нуля (586 мкс - среднее)

02

Старший

Байт

Импульса нуля

Младший

Байт

Паузы нуля (586 мкс - среднее)

02

Старший

Байт

Паузы нуля

ЕЗ

Младший

Байт

Импульса единицы (1251 мкс - среднее)

04

Старший

Байт

Импульса единицы

Младший

Байт

Паузы единицы (586 мкс - среднее)

02

Старший

Байт

Паузы единицы

Младший

Байт

Импульса нуля (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 Fil­ter 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. Новый вариант формата ИК-кода

Устройство

Повторов

Коэффициент

Импульс

Пауза и т. д.

EOF

Байт

Байт

Байт

Байт

Байт

Байт 0

Импульс и пауза - это времена в мкс, деленные на 13,8 после прочитывания в программе WinLIRC в виде шестнад - цатеричных чисел. Затем, в программе, они будут еще раз разделены на 2.

В программе в настоящий момент не используется служеб­ная информация, относящаяся к устройству, количеству по­второв и коэффициенту. Позже, если решим модернизиро­вать модуль, мы дополнительно сделаем несколько выходов, которые будут выбираться по записи «Устройство», добавим обработку количества повторов и, возможно, используем ко­эффициент 13 или 14 для деления времени, полученного при подготовке кода, самой программой. Если решим, что следу­ет внести изменения в модуль.

Умный дом

Схемы для экспериментов с радиоканалом

Если вам захочется провести эксперименты с радиоканалом вместо проводной связи модулей, то: • лучше было бы воспользоваться готовыми радиомоду­лями, но дорого; • не забывайте, что ваши эксперименты могут мешать вашим …

Немного О программировании на С++

Поскольку при программировании микроконтроллера я ис­пользовал язык С, мне показалось уместным добавить хотя бы несколько слов о языке. Но я не сделаю это лучше, чем С. Липпман. Когда мне понадобилось …

Цоколевка контроллера PIC16F628A

—- RA1/AN1 ]—RAO/ANO ]—- RA7/OSC1 /CLKIN ]—RA6/SDC2/CLKOUT — VDD RB7A10SI/PGD — RB6A1 ОБОДІ CKI/PGC j—RB5 RB4/PGM PDIPSOIC О KJ 1 18 2 17 3 16 4 15 5 14 6 …

Как с нами связаться:

Украина:
г.Александрия
тел. +38 05235 7 41 13 Завод
тел./факс +38 05235  77193 Бухгалтерия
+38 067 561 22 71 — гл. менеджер (продажи всего оборудования)
+38 067 2650755 - продажа всего оборудования
+38 050 457 13 30 — Рашид - продажи всего оборудования
e-mail: msd@inbox.ru
msd@msd.com.ua
Скайп: msd-alexandriya

Схема проезда к производственному офису:
Схема проезда к МСД

Представительство МСД в Киеве: 044 228 67 86
Дистрибьютор в Турции
и странам Закавказья
линий по производству ПСВ,
термоблоков и легких бетонов
ооо "Компания Интер Кор" Тбилиси
+995 32 230 87 83
Теймураз Микадзе
+90 536 322 1424 Турция
info@intercor.co
+995(570) 10 87 83

Оперативная связь

Укажите свой телефон или адрес эл. почты — наш менеджер перезвонит Вам в удобное для Вас время.