Вдруг и неожиданно потребовалось видеть определенные данные,
которые храняться и меняются во время работы микроконтроллера ATmega8.
Как это осуществить? Я знаю три пути: 1. Запустить программу
в отладчике; 2. МК будет записывать значения переменных в память, затем
программатором их считывать; 3. Выводить значения на экран -
LCD.
Способ № 1: Самый
замечательный и удобный, но теория зачастую отличается от практики - работающая
на отладчике/эмуляторе программа,
может давать сбой при запуске на МК.
Способ № 2: Ну это
совсем не хорошо. Как то коряво. ради одной переменной столько трудностей... -
это жуть.
Способ № 3: Как
видно из названия статьи, именно этот способ мне и пригленулся - да и на
будущее пригодится.
И не обращаясь к теории и томам информации, как надо было
сделать, я направился в магазин. И как озарение, лежит на витрине WH1602D!!! Такой красивый и
привлекательный модуль. На радостях я отсчитал 200 рублей и получил на руки
пакетик из "пузырчатой" пленки с надписью "WH1602D". Как
попал домой помню с трудом. :)
Уже дома, при помощи компьютера и интернет, я узнал, что
WH1602D - это LCD модуль, имеет 2 строки по 16 символов в каждой + наличие
русского алфавита. О великое счастье, на угад купил замечательную штуку.
Радость закончилась, когда я начал вникать, как это чудо
подключить к МК и заставить написать что-либо.
Первым делом, качаем даташит! /Stati/LCD/WH1602D-YGB-CP.pdf Читаем (на сколько позволяют
знания английского языка), и не фига не понятно.
Вторым делом, поиск по различным форумам - уже лучше - ясно,
что подключается эта радость по стандартному для подобных экрашек принципу. Как
подключить именно модель WH1602D не нашел :(
Выход один, найти описание подключения подобного экрана и изучить принцип.
Ищем... Ищем... И находим статью: серия
"Микроконтроллеры? Это же просто!", название: "Глава 4.
Сопряжение микроконтроллера с индикаторами различных типов" - к сожаления
я не знаю откуда эта статья. /Stati/LCD/manual1.pdf
Читаем... И еще разок... Вот, уже могу выделить особо важные
моменты...
Открываем даташит на наш модуль, сравниваем...
корректируем... Результат !!!
Теория получена. подключаем, пишем прогу, тест... далее
потом расскажу.
И так, результат (теория):
LCD модуль WH1602D иммет 14+2 контактов = 16.
+2 - это подсветка дисплея, на модуле обозначаются как
"А" и "К" - анод и катод соответственно - тут все ясно,
подоем питание (5 вольт) и
радуемся свечению панельки.
14 контактов:
3 из них служат для подключения питания:
1. Vdd - +
5 вольт. - Питание модуля.
2. Vss -
Земля ( - ). - Питание модуля.
3. V0 -
контрастность изображения. - Если закоротить на минус, то мы получим
максимальную контрастность (все точечки будут светиться на всю, и мы не чего не
увидим. Необходимо этот контакт подключить к минусу через переменный резистор номиналом
10 кОм. Мне понравился контраст при примерно 4 кОм.
Еще 3 контакта управляющие:
4. RS -
коммандный флаг - если подать 0, то мы посылаем команду. Если 1, значит
посылаются данные.
5. R/W -
Чтение/запись - 1 - читаем данные, 0 - записываем.
6. E -
импульс - что-бы модуль начал обработку данных с остальных контактов,
необходимо установить 1 на некоторое время.
Остальные 8 контактов служат носителями данных. называются
DB0 - DB7.
Смысл такой. Чтобы заставить модуль, что-то сделать
необходимо установить логические нули и/или еденички на пинах RS, R/W и DB0 -
DB7.
После этого подать импульс на пин E. Выждать время и можно
посылать новые команды.
Открываем даташит, распечатываем табличку:
"11.Instruction Table" и вешаем ее на стенку, чтоб перед глазами
была.
Все просто, устанавливаем нужные пины в нужное состояние в
соответствии с табличкой и подаем импульс. В таблице указаны странные сочетания
букв, вместо них надо поставить 0 или 1 – как нам надо. Рассмотрим команды и
эти знаки поближе:
Clear Display – Полностью очищает память и устанавливает курсор в
позицию 0, т.е. экран чистенький и вывод символов начнется сначала.
Return Home – Устанавливает курсор в позицию 0, не очищая памяти.
Экран не очищается (показывает, что и было), но вывод символов начнется
сначала.
Entry Mode Set – Авто передвижение курсора и экрана за ним. Тут
поподробнее, ибо важно! Команда использует два указателя: I/D и SH.
Если I/D = 0, то после вывода символа на экран
курсор останется на месте и следующий символ заменит его.
I/D = 1, после вывода символа курсор
автоматически переместится на следующую позицию.
SH – Памяти у
модуля намного больше, чем на один экран знаков. Т.е. можно всю память
загрузить стихом Пушкина, и лишь смещая позицию экрана относительно памяти
показывать две произвольных строки стиха. И так.
SH = 1 – экран будет смещаться за
курсором (внимательно! Если I/D = 1 и SH = 1,то
посылая знак на экран в позицию = 0, то мы его не увидим, т.к. Позиция курсора
после ввода символа стала =1, экран сместился на его положение и отображает
данные начиная с позиции 1, а знак мы записали в 0).
SH = 0 –
запрещаем смещать экран относительно памяти. В общих случаях я рекомендую
использовать настройку : I/D =
1; SH =
0 – удобно, что курсор смещается сам, а экран стоит на месте и когда надо мы его
сами переставим. Но это общие случаи и возможно кому-то режим авто смещения
экрана будет полезен.
Display ON/OFF Control – Управление включением/выключением экрана и курсора.
Используется три указателя: D,
C, B.
D – Вк/Вык экран, тут все
понятно, т.е. можно работать с памятью модуля, а на экране будет чисто. D = 0 – Экран выключен. D = 1 – Экран включен.
С - Вк/Вык отображение курсора. С = 0 – курсор не
отображается, С = 1 – на экране на месте курсора будет высвечиваться черточка
(удобно, когда пользователь что-то вводит).
B - Вк/Вык мерцания курсора. B = 0 – курсор отображается
постоянно. B = 1 –
курсор моргает с небольшой частотой как в консольках. Симпатично смотрится. :)
Cursor Or Display Shift – что-то
типа Entry Mode Set, если честно, то я так и не
понял, что это за команда. Если кто знает, то сообщите мне или напишите в
комментариях.
Function Set – Установка режимов работы модуля. 3 ключа DL, N, F
DL – модуль может работать с
шиной 8 бит или 4 бит, 8 бит – быстрее (используем все пины), 4 бита – экономия
на портах МК (используются пины DB4-DB7). Я работал только с 8
битами, так проще для понимания. И так, DL = 1 – это 8 бит, DL = 0 – это 4 бита.
N – количество рабочих строк
модуля. N=1 – это 2
строки. N=0 – это 1
строка.
F – модуль может отображать
символа размером 5х11 точек или 5х8 точек. F=0 – это 5х8, F=1
– 5х11.
Set CGRAM Address – Установить курсорчик в ячейку памяти знакогенератора.
Можно рисовать собственные символы в памяти модуля, как это делать я еще не
знаю (пока не потребовалось). Используя ключи AC0-AC5, указываем ячейку памяти, и курсорчик в нее установится. AC0-AC5 – в совокупности представляют собой
шестиразрядное двубитное число – это и есть адрес ячейки памяти.
Set DDRAM Address – Установить
курсор в ячейку видео памяти. Силой заставляем поставить курсор в указанную
ключами AC0-AC6 ячейку видео памяти.
Именно в этой ячейке отобразится знак, который будет послан следующим. AC0-AC6 – в совокупности представляют собой
семиразрядное двубитное число – это и есть адрес ячейки памяти.
Read Busy Flag and Address - Очень
интересная и полезная команда. Busy Flag (BF) – это флаг занятости
модуля. Когда модуль занят, по после посылки этой команды, мы увидим 0 на ножке
DB7.
Write Data to RAM – записать
данные в модуль. Самая часто-используемая команда. Установили курсор куда нам
надо (либо командой Set DDRAM Address, либо
авто-сдвигом, ключ I/D), и записываем номер знака.
Ключи D0-D7 – это восьмиразрядное
двубитное число, которое и несет в себе номер знака.
Read Data from RAM –
считать из памяти номер знака, по адресу, указанному ключами D0-D7. – Я еще не пользовался этой командой, так что не в курсе ее
работы. :(
Это все команды модуля. Их очень мало, но достаточно для
работы.
Ах да
!!! В памяти модуля хранится таблица символов. В даташите она называется «10.Character Generator ROM Pattern». Находим
нужный знак, по подписям в начеле столбцов и строк получаем его номер и
посылаем командой Write Data to RAM.
К
примеру, знак «G» :
столбец – 0100, строка – 0111 отсюда номер символа в памяти модуля = 01000111.
Все
понятно? Да! Те, кто уже пробует писать программу и подключать модуль уже
обломались. Так сразу модуль не заработает!!! Необходимо его сначала
инициализировать. Что означает это жуткое слово? – Мы должны сказать модулю:
«Включайся… Работай… Работай с 2 строками… Включи экран…» и т.д. Одним словом
настроить прибор для работы. В даташите есть два алгоритма инициализации
модуля, один для 8 бит, второй для 4 бит. Т.к. я работаю в 8 битном режиме, то
и разбирать буду именно его.
Но
для начала подключим модуль к микроконтроллеру. Я использовал МК ATmega8 (Другова нет). Необходимо
припаять 14 проводочков к модулю, можно и штекер какой-нибудь использовать,
лучше сразу в магазине купить.
Питание
и контраст подключаем к питанию всей схемы остальные пины куда попало :), в том
смысле, что к любым свободным портам микроконтроллера. Я использовал порт D для данных
(пины модуля DB0-DB7), эти пины у меня не
занимаются прочими функциями МК и очень удобно в дальнейшей работе – данные
записываются сразу в один порт без заморочек.
Под 3
управляющих пина я использовал порт С, его первые три пина.
И
так, получилось вот что:
PORTС
PС0 – E – импульс.
PC1 – RS
– команда или данные.
PC2 – R/W –
чтение или запись.
PORTD
PD0
– DB0 – данные байт 0
PD1
– DB1 – данные байт 1
PD2 – DB2
– данные байт 2
PD3 – DB3
– данные байт 3
PD4 – DB4
– данные байт 4
PD5 – DB5
– данные байт 5
PD6 – DB6
– данные байт 6
PD7 – DB7 – данные байт 7
Подключил
все как надо. Теперь дело за программой.
Для
удобства я все делал функциями, чтобы в будущем собрать все в файл для Include().
void Clear_ports(void) // Функция устанавливает 0 на всех пинах.
{
PORTD=0b00000000;
PORTC=0b0000000;
}
void Init_ports(void) // Указываем необходимые порты как выходы
{
DDRD=0b11111111;
DDRC=0b0000111;
Clear_ports();
// сразу очищаем все порты.
}
Для
настройки МК все готово, теперь необходимо инициализировать модуль.
Понадобится
вспомогательная функция, которая будет посылать импульс готовности модулю. Это
всего три строки, но каждый раз их писать нудно:
void Impuls(void) // импульс
готовности
{
PORTC
|= _BV(PC0); // на пин E устанавливаем
логическую единичку.
_delay_ms(1); // немного подождем
PORTC &= ~_BV(PC0); //Убираем сигнал
}
Задержка
в 1 мс даже великовата, в даташите это время намного меньше, но задержку в 1мс
незаметно, зато я уверен, что команда точно прошла.
Вот и
функция инициализации модуля, сделана точно по алгоритму из даташита. Одно
замечание в алгоритме даташита не указано, что после каждой команды (пункта)
необходимо посылать импульс, в программе импульсы хорошо видно.
void Init_LCD(void) // функция инициализации модуля.
{
_delay_ms(20); // после подачи питания на
модуль надо подождать.
PORTD=0b00110000; // Команда «РАБОТАТЬ !!!»
PORTC=0b0000000;
Impuls();
// Посылаем бит готовности (импульс Е)
_delay_ms(5);
// ждем, пока модуль врубится…
PORTD=0b00110000;
// Команда «Ты чё не понял??? РАБОТАТЬ !!!»
PORTC=0b0000000;
Impuls();
_delay_us(100);
// уже быстрее доходит…
PORTD=0b00110000;
// «Работать!!! А не то разобью об стену !!!»
PORTC=0b0000000;
Impuls();
//даже думать не пришлось, все как у людей – пока не
стукнешь…
PORTD=0b00111100; // 8 bit, 2 line, 5*11 (команда Function Set)
PORTC=0b0000000;
Impuls();
PORTD=0b00001100;
//display ON OFF
PORTC=0b0000000;
Impuls();
PORTD=0b00000001;//Clear
all
PORTC=0b0000000;
Impuls();
PORTD=0b00000110;
//Entry mode set
PORTC=0b0000000;
Impuls();
}
Ну
вот и запустили. В командах можно сразу выставлять нужные настройки. Все
команды кроме Function Set
можно использовать в работе программы. Команда Function Set вызывается один раз при
инициализации.
Ура-ура!
Инициализация есть!!! Теперь функция записи знака в память модуль для его
вывода на экран. Т.к. я использую режим авто сдвига курсора, то запись в память
осуществляется лишь одной командой Write
Data to RAM .
void Char_To_LCD ( char TXT
) // записать знак в память модуля
{
PORTD=TXT; //в порт записываем
номер знака в таблице
PORTC |= _BV(PC1); // устанавливаем ключ RS.
Impuls();// Посылаем бит готовности
(импульс Е)
}
Теперь
соберем все в кучу и напишем основную функцию программы (mail):
#include
#include
#include
void Clear_ports(void)
{
PORTD=0b00000000;
PORTC=0b0000000;
}
void Init_ports(void)
{
DDRD=0b11111111;
DDRC=0b0000111;
Clear_ports();
}
void Impuls(void)
{
PORTC
|= _BV(PC0);
_delay_ms(1);
PORTC
&= ~_BV(PC0);
}
void Init_LCD(void)
{
_delay_ms(20);
PORTD=0b00110000;
PORTC=0b0000000;
Impuls();
_delay_ms(5);
PORTD=0b00110000;
PORTC=0b0000000;
Impuls();
_delay_us(100);
PORTD=0b00110000;
PORTC=0b0000000;
Impuls();
PORTD=0b00111100;
// 8 bit, 2 line, 5*11
PORTC=0b0000000;
Impuls();
PORTD=0b00001100;
//display ON OFF
PORTC=0b0000000;
Impuls();
PORTD=0b00000001;//Clear
all
PORTC=0b0000000;
Impuls();
PORTD=0b00000110;
//Entry mode set
PORTC=0b0000000;
Impuls();
}
void Char_To_LCD ( char TXT )
{
PORTD=TXT;
//*
PORTC
|= _BV(PC1);
Impuls();
}
//
------- Основная программа ---------
void
main(void)
{
Init_ports(); // настраиваем МК
Init_LCD();
// инициализация модуля
Char_To_LCD(0b10101000); // П
Char_To_LCD(0b01110000);
// р
Char_To_LCD(0b10111000);
// и
Char_To_LCD(0b10110011);
// в
Char_To_LCD(0b01100101);
// е
Char_To_LCD(0b10111111);
// т
Char_To_LCD(0b00100001);
// !
while(1) { // Бесконечный цикл
}
}
Вот и
все! Если все сделано правильно, то на экране появится надпись «Привет!».
Ошибки,
которые совершал я при первых пробах:
- Функцию инициализации пока
не трогайте. Сначала запустите как есть и убедитесь, что «Привет!»
появился. Настройки так 100% рабочие – проверял.
- Контраст модуля. Пин V0 – если он упал на
(-), то все засвечено и знаков не видать. Если он не подключен, то экран
будет пустой, без каких-либо точек. Лучше поставьте переменный резистор и
покрутите.
- Временные задержки. Во
всей программе от посыла импульса и до инициализации используются задержки
по времени. В даташите указано их минимальное значение. Но написано «не
менее», отсюда смело ставим немного больше на всякий случай. Первые
запуски я проводил с задержками в 10 мс везде – буквы выводились по одной
и медленно, но 100% работало. Уже потом я их снижал для ускорения работы.
- Повнимательнее с
настройками авто смещения курсора и экрана относительно памяти.
Ну
вот и всё. Все остальное изучайте сами. Я долго бился, чтобы узнать, написанное
выше и поделился с вами. К примеру, таблицу символов и их номера можно прошить
в память МК, это сильно упростит жизнь – можно будет написать функцию, которая
будет посылать сразу строки – конечно по одному символу, но программист будет
писать строками. Короче, делайте что хотите!
Источник: http://groversuper.ucoz.ru/publ/2-1-0-2 |