WinAVR: работа с ЖКИ

Рейтинг пользователей: / 16
ХудшийЛучший 
Статьи - Программирование

Продолжая опровергать миф о том, что WinAVR сложен и неудобен, в этой статье я расскажу о том, как просто и легко организуется вывод информации на символьные ЖКИ.

Как обычно, начнем с традиционного скачивания архива с парой файлов. Это свободно распространяемая библиотечка-модуль (информация об авторе в комментариях сохранена), в которой я исправил несколько серьезных ошибок. Правда, эти ошибки проявляются в довольно экзотических случаях, но я на это попал, поэтому вам предлагаю уже свободную от (этих) проблем версию. Собственно говоря, это проявление плюса свободного ПО, к которому относится и WinAVR: возможность исправления багов оперативно и самостоятельно, в отличие от закрытого кода библиотек CVAVR.

Файл lcd.c должен быть включен в список компилируемых файлов. Любители makefile должны вручную вписать его в строку с перечнем файлов исходных текстов проекта, пользователи AVR Studio добавляют этот файл при помощи меню, ну а любителям Eclipse повезло больше всех - им достаточно просто скопировать оба файла в папку своего проекта.

Файл lcd.h, как могут догадаться читавшие предыдущую статью, служит для «настройки» модуля: в нем выбираются необходимые порты ввода-вывода, конкретизируются аппаратные особенности выбранного ЖКИ и делается ряд некоторых других настроек. Этот файл обязан быть в папке проекта и, кроме этого, должен подключаться директивой #include во все файлы, где будет требоваться работа с ЖКИ. Рассмотрим его содержимое подробно.

До 61-й строки находится информация, котрая никакой принципиальной роли не играет. Я не рекомендую вносить в нее какие-то правки, но подробно на ней останавливаться не намерен.

А вот с 61-й строки начинаются действительно важные вещи. Первый макрос в этой строке служит для выбора типа управляющего контроллера ЖКИ. Модуль поддерживает два типа контроллеров: популярный HD44780 и чуть более редкий KS0073. К сожалению, контроллер второго типа в моих руках никогда не был, и поэтому я могу лишь доверять разработчику модуля, а вот ЖКИ с HD44780 я тестировал лично, и могу гарантировать полную работоспособность всех функций.

Даолее я перечислю все важные макросы из файла lcd.h.

LCD_CONTROLLER_KS0073


Если в ЖКИ использован контроллер KS0073, задать значение макроса 1.

LCD_LINES


Число строк ЖКИ, может принимать значение 1,2,3 или 4.

LCD_DISP_LENGTH


Число позиций в строке ЖКИ. Должно соответствовать реальности.

LCD_LINE_LENGTH


Количество байтов встроенного ОЗУ контроллера на 1 строку. Обычно изменять не требуется, хотя для надежности стоит свериться с документацией на ваш ЖКИ - если там будет указано иное значение, нужно ввести его.

LCD_START_LINE1


Адрес начала первой строки во встроенном ОЗУ контроллера. Для всех известных мне ЖКИ всегда равно 0.

LCD_START_LINE2


Адрес начала второй строки. Обычно связано со значением LCD_LINE_LENGTH, однако, могут быть исключения, поэтому для страховки рекомендую свериться с документацией на ваш ЖКИ и при необходимости изменить это значение.

LCD_START_LINE3


Адрес начала третьей строки. Обычно равно LCD_START_LINE1 + LCD_DISP_LENGTH. Обратите внимание, что треться строка начинается раньше второй, т. е. является как бы продолжением первой. Такая вот экзотика - сверьтесь с документацией, чтобы убедиться, что в вашем ЖКИ именно так и есть.

LCD_START_LINE4


Адрес начала четвертой строки. Обычно равно LCD_START_LINE2 + LCD_DISP_LENGTH. Снова экзотика: четвертая строка есть продолжение второй.

LCD_WRAP_LINES


Как видите по предыдущим значениям, размещение символов в ОЗУ не совсем логично увязано с реальными строками на дисплее. Функции модуля lcd.c могут автоматически осуществлять перенос текста на следующую строку, если он выходит за пределы текущей - не по ячейкам ОЗУ, а по позициям дисплея. Если вам нужен такой автоперенос, установите значение этого макроса в 1, если вы самостоятельно будете контролировать вывод, оставьте нулевое значение.

LCD_IO_MODE


Некоторые МК имеют возможность работы с внешним адресным пространством, например, для расширения ОЗУ. ЖКИ может быть включен в это адресное пространство, и тогда значение этого макроса вы должны установить в 0. В этом случае программная работа с ЖКИ будет вестись, как будто это какие-то ячейки в ОЗУ микроконтроллера. Значение по умолчанию подразумевает более традиционный способ работы через обычные порты ввода-вывода.

LCD_PORT PORTD


Порт микроконтроллера, к которому подключены сигналы ЖКИ.

LCD_DATA0_PORT, LCD_DATA1_PORT, LCD_DATA2_PORT, LCD_DATA3_PORT


ЖКИ для рассматриваемого модуля всегда подключается по 4-проводной схеме обмена данными. Четыре рассматриваемых макроса позволяют задать 4 разных порта МК для каждой из этих линий - просто укажите наименование портов, если нужно. А если у вас все 4 линии подключены к одному порту - оставьте все, как есть, тогда будет использовано значение LCD_PORT

LCD_DATA0_PIN, LCD_DATA1_PIN, LCD_DATA2_PIN, LCD_DATA3_PIN


4 линии данных ЖКИ могут подключаться к портам МК в произвольном порядке. При помощи этих макросов вы должны указать правильную распиновку.

LCD_RS_PORT, LCD_RS_PIN


Линия RS для ЖКИ может подключаться так же к произволной линии любого порта МК. Пара этих макросов служит для этого.

LCD_RW_PORT, LCD_RW_PIN


Линия RW для ЖКИ может подключаться так же к произволной линии любого порта МК. Пара этих макросов служит для этого.

LCD_E_PORT, LCD_E_PIN


Линия E для ЖКИ может подключаться так же к произволной линии любого порта МК. Пара этих макросов служит для этого.

Все макросы в оставшейся части файла изменять не нужно, во всяком случае до тех пор, пока вы не созреете до глубинных переделок в коде модуля.

Как видите, гибкость в настройках весьма впечатляющая. Благодаря возможности разбросать сигналы управления ЖКИ по любым ножкам микроконтроллера, вы можете добиться идеальной трассировки печатной платы. Однако, такая гибкость оборачивается объемом кода и скоростью его работы, правда, все эти прибавки незначительные и в большинстве случаев ими можно пренебречь. Для достижения минимального объема кода и максимального быстродействия вы будете должны обязательно расположить все сигналы управления на одном порту МК, причем сигналы линий данных должны быть расположены последовательно от младшего бита к старшему.

Закончив возню с распределением сигналов, можно приступать к рассмотрению имеющихся в модуле функций. Их прототипы приведены в конце файла lcd.h.

void lcd_init(uint8_t dispAttr)


Инициализация ЖКИ. Эта функция должна быть вызвана до всех остальных обращений к ЖКИ. В качестве параметра функция принимает одну из следующих констант:

LCD_DISP_OFF - дисплей выключен
LCD_DISP_ON - дисплей включен, курсор невидим
LCD_DISP_ON_CURSOR - дисплей включен, немигающий курсор видим
LCD_DISP_ON_CURSOR_BLINK - дисплей включен, виден мигающий курсор.

void lcd_clrscr(void)


Очистка дисплея. Все позиции дисплея заполняются пробелами, курсор устанавливается в начало первой строки.

void lcd_home(void)


Установка позиции курсора в первую позицию первой строки ЖКИ.

void lcd_gotoxy(uint8_t x, uint8_t y)


Установка курсора в указанную позицию. Значения x и y должны быть в допустимых пределах (см. LCD_LINES и LCD_DISP_LENGTH). Нумерация строк и позиций начинается с нуля.

void lcd_putc(char c)


Вывод одного символа в позицию курсора. После вывода позиция курсора автоматически смещается в следующую позицию по строке, а если задано LCD_WRAP_LINES == 1, то при достижении конца текущей строки курсор сместится в начало следующей.

void lcd_puts(const char *s)


Вывод строки на ЖКИ, начиная с текущей позиции курсора. При выводе может осуществляться автоматический перенос текста на следующую строку, если задано LCD_WRAP_LINES == 1. Кроме этого, встреченный символ '\n' автоматически переводит курсор в начало следующей строки ЖКИ. После вывода курсор оказывается в следующей за последним символом строки позиции.

void lcd_puts_p(const char *progmem_s)


То же самое, что и lcd_puts, но для строки, хранящейся во flash микроконтроллера, т. е. в PROGMEM.

void lcd_command(uint8_t cmd)


Низкоуровневая функция отправки управляющей команды в контроллер ЖКИ.

void lcd_data(uint8_t data)


Низкоуровневая функция отправки данных в контроллер ЖКИ.

lcd_puts_P(s)


Макрос для удобного вывода строк, которые надо сохранить в PROGMEM. Идеология работы с данными во flash, принятая в WinAVR, требует, чтобы для сохранения строки «HELLO» во flash и последующего вывода ее на ЖКИ, мы выполнили следующее:

PROGMEM char str[] = "HELLO";
lcd_puts_p(str);

Если строка, выводимая на ЖКИ, повторно не будет использована в программе, можно воспользоваться макросом PSTR и сократить запись до lcd_puts_p(PSTR("HELLO")). А воспользовавшись макросом lcd_puts_P можно упростить запись еще больше: lcd_puts_P("HELLO");

В принципе, это все функции и макросы. Немного, но достаточно для всех нужд. Если вы располагаете МК с достаточно большим объемом ОЗУ и FLASH, можно совместить стандартный вывод при помощи семейства функций printf с рассматриваемым модулем. Для этого нужно сделать следующее.

Создаем новый исходный файл с названием, например, lcd_io.c (намек на com_io.c), в котором подключаем модуль стандартного ввода-вывода stdio.h и модуль поддержки ЖКИ lcd.h:

 #include  <avr/io.h>
#include "lcd.h"

Теперь нам необходимо определить функцию потокового вывода одного символа так, чтобы символ выводился на наш дисплей:

 static int lcd_putchar(char c, FILE *stream){
 lcd_putc(c);
 return 0;
}

Ничего сложного, как видите. Ничего сложного не будет и в описании переменной для потока, связанного с дисплеем:

 FILE lcd_out = FDEV_SETUP_STREAM(lcd_putchar, NULL, _FDEV_SETUP_WRITE); 

Как видите, мы определили для потока ЖКИ переменную lcd_out типа FILE и сразу присвоили ей значение (при помощи макроса FDEV_SETUP_STREAM), указав ранее сделанную функцию вывода символа на дисплей. Чтобы эта переменная была видна в других файлах нашего проекта, нужно создать еще заголовочный файл lcd_io.h такого содержимого:

 #ifndef __LCD_IO_H__
#define __LCD_IO_H__
#include "lcd.h"
#include "stdio.h"
extern FILE lcd_out;
#endif 

После этого в любом файле нашей программы мы можем проинклюдить lcd_io.h и использовать функцию fprintf для вывода на дисплей, например, так:

fprintf(lcd_out, "Value = %d", var); 

Однако, fprintf требует постоянно указывать, в какой именно поток осуществляется вывод, что может быть лишним для многих проектов. Можно назначить созданный нами поток дисплейного вывода в качестве потока стандартного вывода и пользоваться функцией printf. Такое назначение заключается в единственном операторе stdout = &lcd_out; который мы должны ввести в функции main одним из первых.

Но я предложу вам еще более удобный путь: воспользоваться модулем avr_helper.h, который был в архиве из предыдущей статьи, и его макросом INIT для автоматической инициализации дисплея и связанного с ним потока. Для этого в файле lcd_io.h мы добавляем строку #include , затем в файле lcd_io.c подключаем файл lcd_io.h директивой #include "lcd_io.h", а после этого в самом конце файла lcd_io.c дописываем макрос автоматической инициализации:

 INIT(7){
 stdout = &lcd_out;
 lcd_init(LCD_DISP_ON);
 lcd_clrscr();
}

Все операторы в этом макросе будут выполнены автоматически ДО НАЧАЛА функции main, поэтому вы можете, не переживая на счет ЖКИ, сразу, как принято, поприветствовать мир:

#include <avr/io.h>
#include "lcd_io.h"
int main(void){
 printf("HELLO, WORLD\nI like WinAVR!");
 return 0;
}

Важное примечание: все функции работы с ЖКИ из рассматриваемых модулей являются блокирующими, т. е. пока обмен с ЖКИ не будет полностью завершен, ни одна из функций не возвратит управление. Обмен с ЖКИ в том числе подразумевает получение от него определенных данных, поэтому ваша программа «зависнет» на функции lcd_init, если ЖКИ подключен к МК неправильно или вообще не подключен! Имейте это ввиду.

Файлы для загрузки:
ФайлОписаниеРазмер файла
Скачать файл (lcd.zip)lcd.zipАрхив с модулем поддержки ЖКИ8 Kb
Скачать файл (lcd_demo.zip)lcd_demo.zipПример проекта, с протеусом в том числе40 Kb
 Обсудить на форуме (29 комментариев).

Комментарии  

 
0 #7 ARV 15.06.2014 16:06
Цитирую eis:
Prostite no kak vyvesti kirillicu?pomogite pojaluista

да у вас, батенька, с кириллицей действительно проблемы :lol:

чтобы выводить кириллицу на ЖКИ, необходимо, во-первых, чтобы сам ЖКИ содержал кириллические символы в знакогенераторе , а во-вторых, чтобы кодировка этого знакогенератора совпадала с кодировкой хост-системы, на которой работает компилятор.
чаще всего первое соблюдается, а второе - нет (производители ЖКИ не сильно озабочены тем, чтобы встроенный знакогенератор ЖКИ имел ввообще какую-то общепринятую кодировку).
в этом случае помогают всякие вспомогательные утилиты, например, я такую делал: http://arv.radioliga.com/content/view/183/44/
Цитировать
 
 
0 #6 eis 15.06.2014 14:15
Prostite no kak vyvesti kirillicu?pomogite pojaluista
Цитировать
 
 
0 #5 druidcat 27.09.2012 07:06
Проверил работу на своей новой отладочной плате. Супер. Спасибо большое.
Цитировать
 
 
0 #4 druidcat 31.08.2012 20:54
Спасибо большое за очень понятную и наглядную статью. Воспользовался твоей библиотекой, все работает замечательно. Спасибо!!!
Цитировать
 
 
0 #3 ARV 07.05.2012 21:01
в статье была опечатка: в предпоследней врезке кода нужно было написать вот так:Код:stdout = &lcd_out;
ошибку исправил.
Цитировать
 
 
0 #2 wixa 07.05.2012 20:51
А у вас получилось запустить основываясь на эту статью? Можно ваши файлы посмотреть? а то что-то не получается вывести "HELLO, WORLD\nI like WinAVR!" с помощью Atmega 88... (не могу скомпилировать...) Наверное что то не так понял...

Но Автору Спасибо :) Написано доступно
Цитировать
 
 
0 #1 kot-69 25.03.2012 22:55
Спасибо, Роман. отличное продолжение. еще тогда я собрал все статьи Справочного руководства для себя в один файл для удобства. с удовольствием дополню его последними статьями. еще раз спасибо за отличный "ликбез".
Цитировать
 

Добавить комментарий

Правила комментирования

Запрещается вводить оскорбительные тексты, использовать нецензурные выражения, публиковать экстремистские призывы, давать ссылки на сайты, не имеющие отношения к теме обсуждения. Все сообщения, нарушающие данные требования, будут удалены без уведомлений, а их авторы - заблокированы.
Незарегистрированные посетители не могут вводить ссылки, BB-коды и т.п., зарегистрированные - могут. Подробности о возможностях, доступных зарегистрированным посетителям, см. в разделе Помощь.


Защитный код
Обновить



Темы форума
Комментарии
Статистика
Просмотров:
mod_vvisit_counterСегодня630
mod_vvisit_counterВчера3318
mod_vvisit_counterНа этой неделе38804
mod_vvisit_counterНа прошлой неделе40507
mod_vvisit_counterВ этом месяце167615
mod_vvisit_counterЗа все время11529836

Ваш IP: 54.81.47.168
 , 
27 Май. 2017