Простые устройства
Просто об устройствах

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

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

{ads2}Как обычно, начнем с традиционного скачивания архива с парой файлов. Это свободно распространяемая библиотечка-модуль (информация об авторе в комментариях сохранена), в которой я исправил несколько серьезных ошибок. Правда, эти ошибки проявляются в довольно экзотических случаях, но я на это попал, поэтому вам предлагаю уже свободную от (этих) проблем версию. Собственно говоря, это проявление плюса свободного ПО, к которому относится и 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 для ЖКИ может подключаться так же к произволной линии любого порта МК. Пара этих макросов служит для этого.

 

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

{ads1}

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

Закончив возню с распределением сигналов, можно приступать к рассмотрению имеющихся в модуле функций. Их прототипы приведены в конце файла 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 и последующего вывода ее на ЖКИ, мы выполнили следующее:

{code}PROGMEM char str[] = "HELLO";
lcd_puts_p(str);{/code}

Если строка, выводимая на ЖКИ, повторно не будет использована в программе, можно воспользоваться макросом 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:

{code} #include  <avr/io.h>
#include "lcd.h"{/code}

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

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

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

{code} FILE lcd_out = FDEV_SETUP_STREAM(lcd_putchar, NULL, _FDEV_SETUP_WRITE); {/code}

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

{code} #ifndef __LCD_IO_H__
#define __LCD_IO_H__
#include "lcd.h"
#include "stdio.h"
extern FILE lcd_out;
#endif {/code}

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

{code}fprintf(lcd_out, "Value = %d", var); {/code}

Однако, 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 дописываем макрос автоматической инициализации:

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

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

{code}#include <avr/io.h>
#include "lcd_io.h"

int main(void){
printf("HELLO, WORLD\nI like WinAVR!");
return 0;
}{/code}

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

{ads1}

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

Комментарии   

#1 kot-69 26.03.2012 03:55
Спасибо, Роман. отличное продолжение. еще тогда я собрал все статьи Справочного руководства для себя в один файл для удобства. с удовольствием дополню его последними статьями. еще раз спасибо за отличный "ликбез".
#2 wixa 08.05.2012 01:51
А у вас получилось запустить основываясь на эту статью? Можно ваши файлы посмотреть? а то что-то не получается вывести "HELLO, WORLD\nI like WinAVR!" с помощью Atmega 88... (не могу скомпилировать. ..) Наверное что то не так понял...

Но Автору Спасибо :) Написано доступно
#3 ARV 08.05.2012 02:01
в статье была опечатка: в предпоследней врезке кода нужно было написать вот так:Код:stdout = &lcd_out;
ошибку исправил.
#4 druidcat 01.09.2012 01:54
Спасибо большое за очень понятную и наглядную статью. Воспользовался твоей библиотекой, все работает замечательно. Спасибо!!!
#5 druidcat 27.09.2012 12:06
Проверил работу на своей новой отладочной плате. Супер. Спасибо большое.
#6 eis 15.06.2014 19:15
Prostite no kak vyvesti kirillicu?pomog ite pojaluista
#7 ARV 15.06.2014 21:06
Цитирую eis:
Prostite no kak vyvesti kirillicu?pomogite pojaluista

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

чтобы выводить кириллицу на ЖКИ, необходимо, во-первых, чтобы сам ЖКИ содержал кириллические символы в знакогенераторе , а во-вторых, чтобы кодировка этого знакогенератора совпадала с кодировкой хост-системы, на которой работает компилятор.
чаще всего первое соблюдается, а второе - нет (производители ЖКИ не сильно озабочены тем, чтобы встроенный знакогенератор ЖКИ имел ввообще какую-то общепринятую кодировку).
в этом случае помогают всякие вспомогательные утилиты, например, я такую делал: http://arv.radioliga.com/content/view/183/44/

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

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

Обсудить эту статью на форуме (29 ответов).

Copyright 2019 © simple-devices.ru.
При использовании материалов ссылка на simple-devices.ru обязательна.