Для активизации четырехбитового режима надо программно сформировать сигналы управления согласно временным диаграммам на рис.1. По структуре они совпадают с диаграммой 8-ми разрядной шины за исключением удвоенного числа импульсов "Е". Линии связи проходят через старшие разряды шины данных DB4-DB7, младшие DB0-DB3 остаются не задействованными.
Рис.1
Достоинство режима - малое число проводников, упрощение топологии печатной платы, экономия линий портов МК. Недостаток - пониженная скорость передачи данных в ЖКИ, так как приходится информацию передавать двумя порциями (нибблами или тетрадами) по 4 бита в каждой. Однако, учитывая обязательные задержки времени в программе и физическую инерционность "жидких кристаллов", снижение скорости почти не чувствуется.
Принцип работы 4-х разрядной шины рассмотрим на примере тестовой программы для нашего LCD. На дисплей будут с секундными паузами выводиться цифры десятичного адреса знакоместа 0-255 и графические образы содержащихся в них символов.
Рис. 2
Как известно, каждый LCD имеет встроенный знакогенератор, представляющий собой область ПЗУ объемом более 8 Кб, которая прошивается на заводе-изготовителе. Традиционно первая половина ПЗУ с адресами 00-7Fh содержит начертания цифр, знаков препинания, а также заглавных и строчных букв латинского алфавита. Все как в IBM PC. Вторая половина "отдана на откуп" национальным алфавитам. В связи с этим HD44780 имеет модификации исполнения с тремя основными вариантами зашивки знакогенератора:
латиница и европейские языки (European standard font или Euro)
латиница и японские иероглифы (Japanese standard font или Japan)
латиница и кириллица (Custom font или Russian, рис.2)
Не все из ячеек знакогенератора заполнены. При обращении к "пустым" ячейкам на экране будет выведена произвольная информация, чаще всего состоящая из засвеченных точек. Первые 8 символов с адресами 0х00-0х07 отмечены "звездочкой". При желании они могут быть самостоятельно запрограммированы пользователем.
Какой знакогенератор имеется в конкретном LCD, должно быть указано в его условном обозначении или в технических параметрах, хотя на практике приходится верить честному слову продавца. Другой подход воочию увидеть на экране LCD все возможные начертания символов. Напишем, откомпилируем программу и запрограммируем контроллер, после чего в верхней строке экрана LCD будут с секундными паузами будут выводиться цифры десятичного адреса знакоместа 0-255 и графические образы содержащихся в них символов. Если графика и очередность появления символов соответствует рис.2, значит, LCD в порядке.
Далее собираем схему согласно рис.3, ее отличие только в том что шина данных подключена по 4-х проводной линии, т.е. DB4-DB7 подключены, а DB0-DB3 остаются не задействованными. Вывод R/W дисплея подключен на минус, т.к. дисплей у нас является приемником данных.
Код программы проверки знакогенератора LCD приведен ниже.
// Программа проверки знакогенератора LCD #include <avr/io.h> #include <util/delay.h> #define RS PC0 #define EN PC2 // Функция записи команды в ЖКИ void lcd_com(unsigned char p) { PORTC &= ~(1 << RS); // RS = 0 (запись команд) PORTC |= (1 << EN); // EN = 1 (начало записи команды в LCD) PORTD &= 0x0F; PORTD |= (p & 0xF0); // Выделяем старший нибл _delay_us(100); PORTC &= ~(1 << EN); // EN = 0 (конец записи команды в LCD) _delay_us(100); PORTC |= (1 << EN); // EN = 1 (начало записи команды в LCD) PORTD &= 0x0F; PORTD |= (p << 4); // Выделяем младший нибл _delay_us(100); PORTC &= ~(1 << EN); // EN = 0 (конец записи команды в LCD) _delay_us(100); } // Функция записи данных в ЖКИ void lcd_dat(unsigned char p) { PORTC |= (1 << RS)|(1 << EN); // RS = 1 (запись данных), EN - 1 (начало записи команды в LCD) PORTD &= 0x0F; PORTD |= (p & 0xF0); // Выделяем старший нибл _delay_us(100); PORTC &= ~(1 << EN); // EN = 0 (конец записи команды в LCD) _delay_us(100); PORTC |= (1 << EN); // EN = 1 (начало записи команды в LCD) PORTD &= 0x0F; PORTD |= (p << 4); // Выделяем младший нибл _delay_us(100); PORTC &= ~(1 << EN); // EN = 0 (конец записи команды в LCD) _delay_us(100); } // Функция инициализации ЖКИ void lcd_init(void) { DDRC |= (1 << PC2)|(1 << PC0); // PC1, PC0 - выходы PORTC = 0x00; DDRD = 0xFF; // порт D - выход PORTD = 0x00; _delay_ms(50); // Ожидание готовности ЖК-модуля // Конфигурирование четырехразрядного режима PORTD |= (1 << PD5); PORTD &= ~(1 << PD4); // Активизация четырехразрядного режима PORTC |= (1 << EN); PORTC &= ~(1 << EN); _delay_ms(5); lcd_com(0x28); // Шина 4 бит, LCD - 2 строки lcd_com(0x08); // Полное выключение дисплея lcd_com(0x01); // Очистка дисплея _delay_us(100); lcd_com(0x06); // Сдвиг курсора вправо _delay_ms(10); lcd_com(0x0C); // Включение дисплея, курсор не видим } // Основная программа int main (void) { unsigned char znak = 0; // определяем переменную lcd_init(); // Инициализация дисплея while (1) { lcd_com(0x80); // Вывод в верхнюю левую позицию 1 строки lcd_dat(znak/100 + '0'); // Выделяем сотни lcd_dat((znak/10)%10 + '0'); // Выделяем десятки lcd_dat(znak%10 + '0'); // Выделяем единицы lcd_dat('='); // Выводим знак равенства lcd_dat(znak); // Выводим содержимое знакогенератора _delay_ms(100); // Тут можно поменять задержку вывода символов znak++; // Следующий символ знакогенератора } }
Комментарии
При работе наблюдается задержка смены выводимого символа, большая чем задана в коде. Например, при
Код:
_delay_ms(100);
смена символа происходит за время около 1 секунды.При Код:
_delay_ms(1000);
- около 5 секунд. Если предположить, что выложенное в конце статьи видео демонстрирует работу кода, приведенного в статье, то задержка также не соответствует 100мс.Что может быть причиной???
Частота работы процессора в коде Код:
#define F_CPU 16000000UL
задана правильно.#warning "F_CPU not defined for ″" [- Wcpp] что это за ошибка и как ее исправить
#define F_CPU 16000000UL
где укажите частоту на которой работает Ваш микроконтроллер в герцах.В приведенном примере это 16000000 (16МГц).
Прописать строчку в самом начале
Код:
#define F_CPU 16000000UL
где укажите частоту на которой работает Ваш микроконтроллер в герцах.
В приведенном примере это 16000000 (16МГц).
Не исправляет ошибку
Ошибка типа:
Warning #warning "F_CPU not defined for " [-Wcpp]
обычно возникает когда не указана частота МК необходимая для библиотеки delay.h, с помощью которой формируются временные задержки функциями _delay_us() или _delay_ms(). Исправляется указанием частоты МК. Начало кода в этом случае должно выглядеть так: Код:
#define F_CPU 16000000UL
#include <avr/io.h>
#include <util/delay.h>
Что за контроллер используете?
Я прописывал частоту указывал и 4кгц и 10 кгц и 16 кгц хотя в приведенной схеме нет внешнего генератора, но при компелировании результат один.
Контролер атмега 8 как по схеме в корпусе dip 28
Вот так долно быть записано в коде:
Код:
#define F_CPU 8000000UL
И частота контроллера в МГц а не кГц.
Но даже если частоту указать неправильно, то это повлияет лишь на корректность времени задержки. Ошибки быть не должно.
При создании нового проекта, естественно, необходимо выбрать контроллер под который пишется код.
Зачем устанавливаем 1 в порт PORTD5,а порт PORTD4 сбрасываем в 0?
Спасибо.
Команда необходима для инициализации 4 битной шины данных
Вывод Е - сигнал синхроимпульса, он по сути должен переключаться
Задержки примерные, согласно описанию на контроллер
Спасибо.
Приветствую, читайте комментарий №17
...
Прошу прощения за занудство
Вроде разобрался но не понимаю почему не нужна эта операция(см ***).
// Функция записи команды в ЖКИ
void lcd_com(unsigned char p)
{
PORTC &= ~(1 << RS); // RS = 0 (запись команд)
PORTC|= (1 << EN); // EN = 1 (начало записи команды в LCD)
PORTD &= 0x0F;
/*обнуление старшего ниббла
PORTD =0b00001111;
если ,например р=0b01101010;
получаем запись в LCD
0b01101010 & (0xF0 =0b1111000)=0b01100000;-это только старший ниббл
PORTD |=0b01100000;-запись старшего ниббла команды в LCD
*/
PORTD |= (p & 0xF0); // Выделяем старший нибл и записываем его в LCD
_delay_us(100);
PORTD &= ~(1 << EN); // EN = 0 (конец записи команды в LCD)
_delay_us(100);
PORTD |= (1 << EN); // EN = 1 (начало записи команды в LCD)
PORTD &= 0x0F;
/*обнуляем старшй ниббл PORTD =0b00001111;
теперь ,как в предыдущем комментарии пологаем р=0b01101010;
производим операцию сдвига влево для р=0b10100000;
имеем 0b00001111 & 0b10100000=0b10100000; ВОТ ЭТУ ОПЕРАЦИЮ Я НЕ ВИЖУ В КОДЕ ***
младший ниббл передан для записи команды в LCD
*/
PORTD |= (p << 4); // Выделяем младший нибл
_delay_us(100);
PORTD &= ~(1 << EN); // EN = 0 (конец записи команды в LCD)
_delay_us(100);
}...