Comp. A, Comp. B здесь задается значение с которым будет сравниваться наш Compare A Match и если эти две цифири будут равными, то таймер сгенерит прерывание.
И так мы разобрались со всеми окошками. Возникает резонный вопрос, а какое число нужно записать вComp. A? Давайте подумаем. Нам нужно чтобы по достижении 10 минут, если датчик движения не сработал, выключился свет. Но 10 минут для МК слишком большой срок. Он привык оперировать временными задержками до 1 секунды. Конечно можно и больше но зачем. Потом почему именно 10 минут, а вдруг кому-то захочется 20 или 30 минут сделать. Да все решается довольно просто. Надо сделать так чтобы таймер генерил прерывание раз в секунду. А уж цикл в обработчике прерывания можно растянуть хоть на год. Лишь бы разрядной сетки хватило))) Для таких больших временных интервалов рекомендую использовать часы реального времени к примеру DS1302. Работает по шине I2C и может выдавать не только время, но и дату. Значит останавливаемся на секунде.
Задача:
С каким числом нам нужно сравнить таймер чтобы на достижении его таймер потратил ровно 1 секунду.
Решаем.
У нас есть частота кварца равная 4 МГц. Значит если мы разделим 1 секунду на 4000000 Гц то получим 0,00000025. Это период времени на выполнение одной операции. Ну проще говоря таймер увеличит свое значение на 1 за период времени 0,00000025 секунды. Быстро да? Таймер считает от 0 до 65535, так как он имеет 16 разрядов. Давайте посчитаем за какой период времени он досчитает до конца. 0,00000025 х 65536 = 0,016384 секунды. Маловато. Че делать? Ну конечно же, у нас есть делитель. Давайте посмотрим на наши цифры. Первый делитель нам не нужен. Попробуем второй равный 8. Частота равна 500000 Гц. Делим 1 секунду на 500000 Гц и получаем 0,000002. А теперь посмотрим за какой период времени таймер досчитает до конца. 0,000002 х 65536 = 0,131072. Больше но все еще маловато. Давайте попробуем сразу 256. Частота 15,625 КГц. Разделим 1 секунду на 15625 Гц и получим 0,000064. А теперь посчитаем время за которое таймер досчитает до конца. Умножим 0,000064 на 65536 и получим 4,194304. О! Это больше секунды, то что надо. Теперь нам надо узнать до какого числа досчитает таймер за 1 секунду. Значит нам надо 1 секунду разделить на 0,000064 и получим число 15625. Опытный глаз сразу заметит что данной число совпадает с частотой предделителя. Да, да скорее всего поэтому программисты CodVisionAVR выводят частоты, а не делитель. Сразу видно число с которым надо сравнить для достижении периода времени в 1 секунду. Ну теперь нам надо лишь выбрать предделитель и занести число в ячейку сравнения таймера А.
Число 3D09 это аналог 15625. В шеснадцатиричную систему я перевожу с помощью виндовского калькулятора. Пусть меня позорят гордые профи программеры, но мне все равно! Лично мне так проще. И так все готово для старта. Сохраняем проект, генерим код, кто не знает как идет сюда. Давайте посмотрим что нам сгенерил генератор кода.
// Timer1 output compare A interrupt service routine
interrupt [TIM1_COMPA] void timer1_compa_isr(void)
{
// Place your code here
}
Этот кусок кода до основной функции и есть функция обработка прерывания. То есть когда сработает прерывание то МК будет выполнять код написанный между фигурными скобками данной функции. Вот и давайте заполним ее.
#include <mega8.h>
unsigned char Lamp = 0x00;
unsigned int time = 0x00;
interrupt [TIM1_COMPA] void timer1_compa_isr(void)
{
time++;
if(time == 0x0258)
{
PORTB.0 = 0;
Lamp = 0x00;
time = 0x00;
}
}
Что мы тут делаем. Для начала мы инициализируем две глобальные переменные Lamp и time. Первая как и в прошлый раз отвечает за состояние лампы, вкл/выкл, а вторая за период времени, то есть те минуты которые нам надо отсчитать. Что у нас происходит в обработчике прерывания. Первым делом мы увеличиваем значение переменной timer. Данная переменная отвечает за период времени в который нам не надо выключать свет. То есть отсчитывает наши заветные секунды. Почему сразу увеличивает? Да просто если программа начала работать с самого начала, то значение переменной равно нулю, а если время отработало то обработчик сбросил значение данной переменной в ноль (последняя строка). Далее мы проверяем совпал ли наш период с заданным. Число 0х0258 равен 600 секундам, то есть нашим 10 минутам. Его значение можно менять. Если не совпало, значит ничего не делаем, а если совпало, то выключаем лампу, сбрасываем флагLamp в 0, чтобы МК знал что лампа выключена и обнуляем переменную time. Вот и все. Теперь давайте посмотрим что же в это же время делает основная программа. Замете я подчеркнул в это же время. То чего мы и добивались.
while (1)
{
if(PINB.1 == 1)
{
if(Lamp == 0x00)
{
PORTB.0 = 1;
Lamp = 0x01;
time = 0x00;
}
else
{
PORTB.0 = 0;
Lamp = 0x00;
}
while(PINB.1);
}
if(PINB.2 == 1)
{
while(!PINB.2);
time = 0x00;
while(PINB.2);
}
};
Что происходит тут. Первое что мы делаем, так это проверяем нажата ли кнопка включения света. Если да то проверяем выключен ли свет в данный момент. Если да, то включаем свет, выставляем флаг включенного света и обнуляем таймер. Иначе выключаем свет выставляем флаг выключенного света. Далее ждем отпускания кнопки. Далее мы проверяем, не сработал ли у нас датчик. Проверяем мы его всегда, даже при выключенном свете, это не страшно ведь даже если датчик не сработает он выключит уже выключенный свет. Так что нам все равно. Если датчик все же отработал, а в этот момент свет был включен, а время еще не истекло, то мы сбрасываем таймер и ждем сброса контактов датчика. Вот и все.
Данная программа не совсем корректна, но для объяснения работы на пальцах то что нужно. В любом случае вы можете уже собрать у себя дома автоматизированный свет и показывать своим друзьям.
Если что не понятно, пишите либо на почту, либо в комментарии. Удачи
И так. Давайте продолжим наше знакомство с микроконтроллером Atmega 8. В этой статье я постараюсь наиболее подробно и понятно рассказать как работать с ЖК дисплеем. Дисплей будет у нас самый простой со знакогенератором в две строки по 16 символов. Управлять будем дисплеем при помощи библиотечных функций любезно предоставленными нам разработчиками программы CodeVisionAVR.
Работа программы в МК это конечно хорошо, но какая польза от такой работы если мы не можем увидеть ее в действии. Конечно можно присоединить к выходным ножкам МК разные устройства: моторы, реле, светодиоды и т. д. Но все же хочется глазами взглянуть на саму программу. Ну к примеру, как вы себе представляете ПК без монитора. Ну ни как. Вот и здесь также можно сделать подобие монитора. Почему подобие? Да потому что ЖК имеет только знакогенератор и все. Можно выводить только знаки и никакой графики. Да, можно и графический дисплей прикрутить, но это в следующей статье.
Нус начнем. Что такое ЖК дисплей со знакогенератором? Смотрим на картинку.
Вот так выглядит наш подопытный зверь. Что мы видим. Плата с припаянным на ней жидко-кристаллическим дисплеем и ряд контактных площадок для подключения питания, данных и команд. Управляет всем этим чудом контроллер типа HD44780. Этот контроллер для ЖК как IBM совместимость для ПК. Да, да. Если вы видите что ЖК работает на контроллере совместимом с HD44780, то можно смело говорить: "Я знаю команды этого МК", при этом даже не зная фирмы изготовителя. Я уверяю вас, команды все одинаковы. К примеру возьмем наш отечественный ЖК фирмы "МЭЛТ". На его борту установлен контроллер КБ1013ВГ6 фирмы "Ангстрем", а по сути дела это тот же HD44780. Просто "Ангстрем" сделали его аналог, а команды у него такие же как и у HD44780. Ну вроде по железу пока все. А нет! Есть у всех ЖК дисплеев одно проблема. Нумерация и расположение контактных площадок и их назначения могут сильно различаться. Но изучив один ЖК можно на остальные лишь искать цоколевку и все.
Давайте рассмотрим эти самый контактные площадки. Чаще всего их насчитывается 16 штук.
1. GND (VSS) | Общий ввод (0В) |
2. UCC (VDD) | Напряжение питания (+5В) |
3. V0 (VEE) | Управление контрастностью |
4. A0 (RS) | Выбор сигнал/команда |
5. R/W (RW) | Выбор чтение/запись |
6. E | Разрешение обращения к модулю (строб данных) |
7. DB0 (D0) | Шина данных/команд бит 0 |
8. DB1 (D1) | Шина данных/команд бит 1 |
9. DB2 (D2) | Шина данных/команд бит 2 |
10. DB3 (D3) | Шина данных/команд бит 3 |
11. DB4 (D4) | Шина данных/команд бит 4 |
12. DB5 (D5) | Шина данных/команд бит 5 |
13. DB6 (D6) | Шина данных/команд бит 6 |
14. DB7 (D7) | Шина данных/команд бит 7 |
15. +LED (A) | + питание подсветки |
16. -LED (K) | - питания подсветки |
Я думаю что описания в таблице достаточно информативно и расписывать каждое я не буду. Остановлюсь лишь на питании подсветки. В цепь ставьте обязательно токоограничивающий резюк где-то на 330-470 Ом. А то в мануале ничего не было написано и я с радостью спалил светодиоды. Когда просмотрел разводку на плате, то резисторов там не нашел :) Вот так бывает.
Теперь давайте посмотрим на схему подключения.
Здесь все просто. Слева три входа ЖК это схема питания и регулировки контраста. Далее три входа RS RW E. Ну с этим тоже все понятно, а вот почему линии данных/команд всего четыре. Тут все просто. Управляющий контроллер позволяет работать как по 8-ми битной шине, так и по 4-х битной. Ну вот так придумали. Так как мы будем работать с библиотеками CodeVisionAVR, то нам потребуются лишь 4-х битная схема. Схема подключения на картинке не спроста именно такая. Она заточена под работу с библиотеками. Если использовать версию программы старше 2.05.0, то со всеми ЖК, библиотеки будут работать толькопо данной схеме. Более младшие версии могут устанавливать любой расклад подключения, что благоприятно влияет на трассировку будущих плат.
Ну что, схема готова. Теперь давайте попробуем что-нибудь вывести на экран. Открываем программу CodeVisionAVR и создаем новый проект с чипом Atmega8 и частотой кварца 4 МГц. Далее переходим во вкладку LCD.
Во всплывающем менюLCD Port:выбираем порт В. Ведь мы к нему подключились. Далее выбираем количество знаков в строке, пункт Chars./Line:16 штук. Ведь ЖК у нас на 2 строки по 16 знаков. Ниже приведены как раз те линии соединения про которые я говорил. Если вы их сравните со схемой, то увидите что они совпадают. Их программа вывела лишь для справки и поменять их никак нельзя. Если конечно у вас версия программы 0.05.0 или выше, то там можно выбрать на свой вкус, но мы пока поработаем с версией 0.04.4a. Теперь сохраняем проект как это было описано в части номер 2.
Давайте рассмотрим что создала нам программа.
#include <mega8.h>
#asm
.equ __lcd_port=0x18 ;PORTB
#endasm
#include <lcd.h>
Первая строка подключает файл с данными о всех адресах портов и регистрах МК. Вторая строка разрешает писать программу на ассемблере. Третья прописывает порт В, к которому мы подключили МК. Четвертая закрывает работу с ассемблером. И в последней строке подключается файл для работы с ЖК. При помощи этого файла мы сможем работать с ЖК довольно просто используя библиотечные функции. Давайте теперь спустимся в самый низ программы.
lcd_init(16);
while (1)
{
};
Что мы видим. Над основным циклом программы появилась строка lcd_init(16).Вот это как раз и одна из функций библиотеки. Данная функция вызывается один раз и выполняет инициализацию ЖК. В скобках задается количество знаков в строке. Так как наш ЖК 2 на 16, то и в скобках записано число 16. Ну вроде и все, больше генератор нам ничего не нагенерил. Вернусь к функции lcd_init(16). Данная функция проводит инициализацию ЖК, а именно включает дисплей, очищает экран, выставляет курсор в верхний левый угол и выключает его. То есть дисплей готов получать данные. Видите как все просто. Можно конечно самому на ассемблере написать функции инициализации дисплея, но зачем это делать, если уже все сделали за нас. Теперь давайте выведем что-нибудь на экран. Я не буду отходить от традиций и выведу самую распространенную фразу "Hello World"
lcd_init(16);
lcd_gotoxy(3,0);
lcd_putsf("Hello World");
while (1)
{
};
Что здесь поменялось? После функции инициализации я дописал еще две. Первая lcd_gotoxy(3,0)означает, что я хочу начать писать в первую строку пропустив 4 знака слева. Если вы заметили то отсчет начинается с 0, как строк так и знаков. То есть я данной функцией устанавливаю координаты курсора. Вторая функция выводит на экран строку занесенную в скобки. Двойные кавычки обязательно. И вот что у нас получилось.
Вот так просто можно вывести на ЖК текст. Вроде бы и на этом можно ставить точку, но как же вывести значение какой-нибудь переменной? Ведь программа например должна выводить полученную температуру или значение напряжения или еще чего. А все просто.
lcd_gotoxy(3,0);
lcd_putsf("Hello World");
lcd_gotoxy(7,1);
sprintf(string, "%i", parametr);
lcd_puts(string);
while (1)
{
};
Как видите у нас добавилось три строчки после вывода строки. Давайте рассмотрим их. Ну с координатой все понятно, седьмой символ вторая строка. Далее идет функция sprintf(string, "%i", parametr);которая помещает в строковый массив string значение переменной parametrс форматированием "%i"десятичное целое со знаком. А функция lcd_puts(string);выводит эту строку на дисплей. Главное не забудьте перед главной функцией main(),вверху программы там где прописываются глобальные переменный, прописать массив char string[17];.Почему 17 ячеек? А потому что максимальное число символов, если мы не забыли, равно 16. А при выводе строки массив должен обязательно иметь последнюю ячейку с нулем. Если мы напишем в количество ячеек массива равное 16, то при выводе 16 символов, 16-й не будет выведен. И подключите заголовочный файл #include <stdio.h>для работы со строками. Ну а переменнуюint parametr = 100;задавайте где хотите. Это зависит от вашей программы.
При форматировании используйте символы:
i | Десятичное целое со знаком |
d | Десятичное целое со знаком |
u | Десятичное целое без знака |
e | Вещественное с плавающей точкой (формат вывода [-]x.xxxxx e[-]xx) |
E | Вещественное с плавающей точкой (формат вывода [-]x.xxxxx E[-]xx) |
f | Вещественное с плавающей точкой (формат вывода [-]xxx.xxxxx) |
x | Шестнадцатеричное целое без знака (вывод символов нижнего регистра) |
X | Шестнадцатеричное целое без знака (вывод символов верхнего регистра) |
c | Символ со знаком |
Да вот еще, зайдите в настройки компилятора Project->Configureи выберите вкладку C compiler.Во всплывающей вкладке (s)printf Features: нужно выбрать float, width, precision.Это надо для работы с числами с плавающей точкой.
Ну вот вроде и все. Если возникнут вопросы, то задавайте в коментах или пишите на почту.
–