AVR библиотека для LCD с произвольным подключением выводов

23/07/2013 - 13:23 Павел Бобков

Введение

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

Особенности

- работает с компиляторами IAR AVR, CodeVision AVR, GNU GCC,
- поддерживает lcd контроллеры HD44780 и KS0066,
- поддерживает подключение lcd к произвольным выводом микроконтроллера,
- поддерживает 4-х и 8-ми разрядный интерфейс,
- имеет функции вывода строк из ОЗУ и флэш,
- имеет функции добавление пользовательских символов.

Состав библиотеки

compilers_4.h - файл для поддержки трех компиляторов
port_macros.h - макросы виртуальных портов
lcd_lib_2.h - заголовочный файл LCD библиотеки с прототипами функций и настройками
lcd_lib_2.c - файл реализации функций LCD библиотеки

Подключение к проекту

1. Переписываем все файлы библиотеки в папку проекта.
2. Подключаем lcd_lib_2.c к проекту внутри среды разработки.
3. Инклюдим заголовочный файл lcd_lib_2.h к сишному файлу, в котором будут использоваться lcd функции.
4. Настраиваем конфигурацию lcd библиотеки в заголовочном файле lcd_lib_2.h
5. Прописываем в код вызов функций lcd библиотеки.

Настройка конфигурации

   Настройка конфигурации в файле lcd_lib_2.h включает в себя следующие шаги.

   1. Настройка виртуального или реального порта, к которому подключается LCD

   Синтаксис объявления виртуального порта подробно описан в файле port_macros.h и в статье посвященной виртуальным портам. В заголовочном файле lcd_lib_2.h уже объявлен порт, в этих объявления нужно менять только буквы порта (A, B, C..), номера выводов (0, 1, 2, 3 ...), тип порта (_REAL, _VIRT), активный уровень (_HI, _NONE). Все остальное (имя порта и имена выводов) трогать не надо. 

Пример объявление виртуального порта для 8-ми битной шины и реального порта для 4-х битной шины.

   Рамками выделены те части кода, которые нужно настраивать под свой проект. 


   2. Глобальные настройки драйвера

LCD_CHECK_FL_BF - проверять флаг BF или использовать программную задержку. 0 - задержка, 1 - проверка флага.

LCD_BUS_4_8_BIT - используемая шина данных. 0 - 4 разрядная шина, 1 - 8-ми разрядная

   3. Настройка инициализации дисплея

Эти настройки определяют состояние дисплей после вызова функции LCD_Init().

LCD_ONE_TWO_LINE - количество отображаемых строк. 0 - 1 строка; 1 - 2 строки.

LCD_FONT58_FONT511 - тип шрифта. 0 - 5х8 точек; 1 - 5х11 точек.

LCD_DEC_INC_DDRAM - изменения адреса ОЗУ при выводе на дисплей. 0 - курсор движется влево, адрес уменьшается на 1 (текст получается задом наперед) ; 1 - курсор движется вправо, адрес увеличивается на 1.

LCD_SHIFT_RIGHT_LEFT - сдвиг всего дисплея. 0 - при чтении ОЗУ сдвиг не выполняется, 1 - при записи в ОЗУ сдвиг дисплея выполняется согласно установке LCD_DEC_INC_DDRAM (0 - сдвиг вправо, 1 - сдвиг влево)

LCD_DISPLAY_OFF_ON - включение / выключение дисплея. 0 - дисплей выключен, но данные в ОЗУ остаются; 1 - дисплей включен.

LCD_CURSOR_OFF_ON - отображение подчеркивающего курсора. 0 - курсор не отображается, 1 - курсор отображается.

LCD_CURSOR_BLINK_OFF_ON - отображение мигающего курсора. 0 - мигающий курсор не отображается; 1 - мигающий курсор отображается.

LCD_CURSOR_DISPLAY_SHIFT - команда сдвига вправо/влево курсора или дисплея без записи на дисплей. В библиотеке не используется и ни на что не влияет. Затесалась сюда случайно )

Пользовательские макросы и функции

LCD_Clear() - очистка дисплея.
LCD_ReturnHome() - возврат курсора в начальное положение.
LCD_Goto(x, y) - позиционирование курсора. x - номер знакоместа, y - номер строки.
void LCD_Init(void) - инициализация дисплея.
void LCD_WriteCom(uint8_t data) - запись команды 
void LCD_WriteData(char data) - вывод одного символа
void LCD_SendStr(char *str) - вывод строки из ОЗУ.
void LCD_SendStrFl(char __flash *str) - вывод строки из флэш памяти.
void LCD_SetUserChar(uint8_t __flash *sym, uint8_t adr) - загрузка пользовательского символа в ОЗУ дисплея. 

Пример использования:


char textCompiler[] = "xz ";
__flash uint8_t quarterNote[] = {4,4,4,4,4,4,28,28};

int main( void )
{
//инициализируем дисплей
LCD_Init();
//загружаем пользовательский символ в нулевую ячейку ОЗУ дисплея
LCD_SetUserChar(quarterNote, 0);
//устанавливаем курсор в 8-мое знакоместо
LCD_Goto(8,0);
//выводим строку на дисплей
LCD_SendStr(textCompiler);
while(1);
return 1;
}

Файлы

LCD_VP_iar.rar  -  тестовый проект для IAR AVR
LCD_VP_as6.rar - тестовый проект для AtmelStudio
LCD_VP_cv.rar - тестовый проект для CodeVision AVR
LCD_VP_winavr.rar - тестовый проект для WinAvr
LCD_VP_proteus.rar - тестовый проект для Proteus
lcd_lib_2.rar  - lcd библиотека с произвольным подключением выводов

Comments   

# foxit 2013-07-24 19:29
Спасибо
# Pashgan 2013-07-27 17:41
Пожалуйста.
# Constantin 2013-09-28 13:58
Спасибо большое за вашу работу, сайт, опыт. Заведите пэйпал, а то ни одной виртуальной валюты, которые вы принимаете у меня нет.
# Pashgan 2013-09-28 20:02
Пожалуйста.
# mmavka 2013-10-11 11:10
Спасибо вам за сайт и труды!
У меня вопрос по назначению реального порта!? Пины должны нумероваться с 0 по 7 и никак по другому?(Наприм ер у порта С в меге 88 пользоваться пинами можно с 0 по 5) Выход - виртуальный порт?
Еще допилил вашу библиотеку для работы с произвольным X Y дисплея. (20x4 мне нужен был) Куда изменения выложить?
# Pashgan 2013-10-11 11:34
Пожалуйста.

Нумерация с 0 по 7. Если выводы реального порта не используются, то добавляется атрибут _NONE.

Напиши мне через форму обратной связи, я тебе отвечу по почте и ты мне скинешь свой проект. Я его выложу на форум или присоединю к материалу.
# mmavka 2013-10-11 11:52
Если я делаю так:
#define LCD_PORT LCD_DATA, C, _REAL

#define LCD_DATA_0 C, 0, _NONE
#define LCD_DATA_1 C, 1, _NONE
#define LCD_DATA_2 C, 6, _NONE
#define LCD_DATA_3 C, 7, _NONE
#define LCD_DATA_4 C, 2, _HI
#define LCD_DATA_5 C, 3, _HI
#define LCD_DATA_6 C, 4, _HI
#define LCD_DATA_7 C, 5, _HI

Не работает. Если погонять в протосе, видно как 0x30 пишется в порт 0x30 а не 0x0C
# Pashgan 2013-10-11 12:06
Попробуй вот так:
Code:

#define LCD_DATA_0 C, 0, _NONE
#define LCD_DATA_1 C, 1, _NONE
#define LCD_DATA_2 C, 2, _HI
#define LCD_DATA_3 C, 3, _HI
#define LCD_DATA_4 C, 4, _HI
#define LCD_DATA_5 C, 5, _HI
#define LCD_DATA_6 C, 6, _NONE
#define LCD_DATA_7 C, 7, _NONE
# mmavka 2013-10-11 12:14
Quoting Pashgan:
Попробуй вот так:
Code:

#define LCD_DATA_0 C, 0, _NONE
#define LCD_DATA_1 C, 1, _NONE
#define LCD_DATA_2 C, 2, _HI
#define LCD_DATA_3 C, 3, _HI
#define LCD_DATA_4 C, 4, _HI
#define LCD_DATA_5 C, 5, _HI
#define LCD_DATA_6 C, 6, _NONE
#define LCD_DATA_7 C, 7, _NONE


А что это изменит?
Как я понимаю для корректной работы драйвера LCD требуется соответствие
Code:
#define LCD_DATA_4 C, 2, _HI
#define LCD_DATA_5 C, 3, _HI
#define LCD_DATA_6 C, 4, _HI
#define LCD_DATA_7 C, 5, _HI

Попробовал, ничего не изменилось.
З.Ы. Проект готов к отправке.
# Pashgan 2013-10-11 13:01
Тогда не знаю. Не видя схемы и твоего проекта, не могу сказать в чем дело.
# alexval2007 2013-10-29 15:57
mmavka поделитесь допиленной либой нужна для работы с 4х строчным дисплеем alexval2006@rambler.ru
# alexval2007 2013-10-29 15:51
Pashgan а возможно ли расширить функционал библиотеки для работы с 4х строчными дисплеями?
# Pashgan 2013-10-29 18:52
Попробуй заменить макрос LCD_Goto() на такой
Code:
#define LCD_Goto(x,y) LCD_WriteCom(((((y)&1)*0x40)+((((y)&3)>>1)*0x10)+(x))|128)
# Dim 2014-09-02 18:49
Это для дисплея 16 х 4

А вот так для дисплея 20 х 4 :
#define LCD_Goto(x,y) LCD_WriteCom((( ((y)&1)*0x40)+( (((y)&3)>>1)*0x 14)+(x))|128)
# Serge 2013-11-05 20:00
Спасибо Вам большое за библиотеку. Но что то не могу я заставить хоть как то её работать. Дисплей WH0802A-NGG-CT вообще не реагирует никак.
Компилятор WINAVR, МК- ATmega8A

Распиновка следующая

#define VIRT_PORT
#ifdef VIRT_PORT

//здесь я определил виртуальный порт

//шина данных LCD
#define LCD_PORT LCD_DATA, F, _VIRT

#define LCD_DATA_0 D, 0, _HI
#define LCD_DATA_1 D, 1, _HI
#define LCD_DATA_2 D, 2, _HI
#define LCD_DATA_3 C, 5, _HI
#define LCD_DATA_4 C, 4, _HI
#define LCD_DATA_5 C, 3, _HI
#define LCD_DATA_6 B, 0, _HI
#define LCD_DATA_7 B, 1, _HI

//управляющие выводы LCD
#define LCD_RS C, 0, _HI
#define LCD_RW C, 1, _HI
#define LCD_EN C, 2, _HI


//глобальные настройки драйвера
#define LCD_CHECK_FL_BF 1
#define LCD_BUS_4_8_BIT 1

//настройки инициализации дисплея
#define LCD_ONE_TWO_LIN E 1
#define LCD_FONT58_FONT 511 0
#define LCD_DEC_INC_DDR AM 1
#define LCD_SHIFT_RIGHT _LEFT 0
#define LCD_DISPLAY_OFF _ON 1
#define LCD_CURSOR_OFF_ ON 1
#define LCD_CURSOR_BLIN K_OFF_ON 1
#define LCD_CURSOR_DISP LAY_SHIFT 0

остальные
вывод 1 - земля
вывод 2 - +5
вывод 3 - +5

Вы не могли бы мне подсказать что я не так сделал ? Или я мог бы выслать проект с моими настройками на указанный Вами адрес.
Спасибо
# Pashgan 2013-11-05 20:18
Кинь вот сюда проект, я посмотрю.
http://chipenable.ru/index.php/forum/materialy-sajta-chipenable/3245-vetka-dlya-vremennogo-khraneniya-proektov.html?start=50
# Alyes 2013-11-19 18:29
Спасибо, все работает! Строки вывести на ЖК, все Ok! но как переменную какую, показать никак еще не соображу... подскажите...(т олько начинаю осваивать AtmelStudio...)
# Alyes 2013-11-19 19:05
Все, не утруждайтесь объяснять))) разобрался... BCD.* и эту либу)))Еще раз Спасибо!
# iWh1te 2013-11-26 20:24
Доброго всем. Спасибо за библиотеку, но что то меня ни как не хочет работать. Имеется плата ардуина с готовой платой дисплея для ардуины. Там пин RW подключен к земле. Вот ни как не могу понять, как правильно сконфигурироват ь пины :(

ЗЫ Так же имеется дисплей 20х4 и тоже не хочет работать. Ув mmavka, можете поделиться допилинной библиотекой для сего дисплея?
# Pashgan 2013-12-03 01:58
Можно сконфигурироват ь пины также как в моем примере, только вывод RW никуда не подключать. И еще в настройках нужно указать, что не используется проверка флага BF.
# Dim 2013-12-24 16:31
так а как все же выводить переменную?
# Pashgan 2013-12-25 14:56
С помощью библиотеки BCD. http://chipenable.ru/index.php/programming-avr/item/152-bcd.html
# Dim 2013-12-25 08:15
и постоянно этот варнинг присутствует при обращении к библиотеке
../lcd_lib_2.c:78: warning: 'data' may be used uninitialized in this function
# Pashgan 2013-12-25 14:56
В каком из проектов?
# Dim 2013-12-25 15:17
ну вообще беру все пихаю в avrstudio на чистовой проект - фактически пустой, только эта библиотека и команда вывода на дисплей строки или просто символа и почему-то всегда этот варнинг
# Pashgan 2013-12-25 15:27
Если тебя раздражает этот варнинг, то в файле lcd_lib2.c поправь функцию INLINE static void LCD_Wait(void)

было
Code:
uint8_t data, tmp;
LCD_DirPort(LCD_PORT, 0x00);


стало
Code:
uint8_t data, tmp;
data = 0;
LCD_DirPort(LCD_PORT, 0x00);


Компилятору не нравится, что эта переменная не инициализирован а. Но я сделал это специально и как самый последний жлоб сэкономил два байта памяти) Инициализация этой переменной выполняется дальше с помощью макроса LCD_ReadPort(LC D_PORT, data);
# Dim 2013-12-25 15:32
о, спасибо)
и, кстати, отдельное спасибо за хорошие библиотеки - как начинающему это очень помогает
# dim 2014-02-08 10:26
А как сделать мигающим число на дисплее?
Например, когда нужно изменить значение - для удобства его хорошо бы выделять, курсором выделяется 1 цифра, а если их несколько в числе, красивее чтоб все число мигало
# Vitaliy81 2014-04-20 12:35
подскажите как вывести значение ацп в строку или отдельно у меня не получается)
# Alyes 2014-04-20 13:09
Quote:
подскажите как вывести значение ацп в строку или отдельно у меня не получается)
АЦП нужно выводить как переменную. чтобы переменную вывести на LCD, в проект нужно вставить еще два файла библ. "bcd.h" и "bcd.c"

посмотри например "Учебный курс. 16 разрядный таймер/счетчик Т1." там вывод переменной на LCD, и файлы есть.

и пишешь например:
BCD_5IntLcd(АЦП переменная); //это на LCD
# Pashgan 2014-04-20 14:15
Все по-разному делают. Кто-то использует библиотечную функцию printf, я обычно использую самописные функции. Например вот эти - http://chipenable.ru/index.php/programming-avr/item/152-bcd.html
# Alyes 2014-04-21 09:47
Подскажите Павел, как можно реализовать progress bar?... так все отлично, но прогресс, никак не получается... Примерчик какой... зарание Благодарен!
# Pashgan 2014-04-21 10:59
Можно вот так сделать.
Code:void LCD_ProgressBar(uint8_t state, uint8_t size)
{
uint8_t i, bLine, wLine;

bLine = (size * state)/100;
wLine = size - bLine;

for(i = 0; i < bLine; i++){
LCD_WriteData(0xff);
}

for(i = 0; i < wLine; i++){
LCD_WriteData(' ');
}
}


size - это размер прогресс бара (количество элементов). state - состояние прогресс бара в процентах.

Code:
LCD_Goto(0,0);
//прогресс бар из 10-и элементов
//3 элемента будут черные
LCD_ProgressBar(30, 10);


Если убрать расчет процентов, а задавать количество черных элементов, код будет компактнее.
# Alyes 2014-04-21 13:44
Спасибо!!! смысл понял. буду пробовать.
# Михаил 2014-05-04 19:05
подскажите как float число показать на ЖК. Пишу в студии 6
# Pashgan 2014-05-08 09:51
Подключи вот эту библиотеку - http://elm-chan.org/fsw/strf/xprintf.html. Она позволяет выводить float.
# Azimut 2014-09-05 14:07
Подключил библиотеку Atmega1284p дисплей wh2004a в протеусе все работает как надо а в реале дисплей инициализируетс я но не выводит ни символы ни курсор даже, соединение верное уже раз 5 все перепроверил, не подскажете в чем может быть еще дело?
# Peter 2014-09-10 19:25
Выводы портов, недоступные для битовых операций (F,G Atmega128 и т.п. ), будут модифицироватьс я неатомарно. Аналогично неатомарно выводятся данные для 4-проводной схемы управления LCD. Как-то не комильфо. ОпасТно с точки зрения возможных прерываний во время доступа.
# Andrey789 2014-10-18 10:20
Pashgan, совместил ваш проект с энкодером (http://chipenable.ru/index.php/programming-avr/item/37-uchebnyy-kurs-podklyuchenie-enkodera-tipovaya-struktura-prostoy-programmy-generator-na-avr.html) и этой библиотекой. Частоту мерцания светика вывожу на дисплей. Dсе работает почти нормально. Но проблема в том, что когда крутишь ручку энкодера и значение меняется с большего на меньшее, на дисплее показываются неверные данные. Пример: кручу ручку энкодера, меняется значение переменной с 9 на 10, а если крутить в обратную сторону - значение переменной меняется с 10 на 90 (а не на 9) 80, 70 и т.д. То есть появляется лишний ноль на дисплее. Если энкодером докрутиться до трехзначного числа и потом крутить обратно до однозначного - получаю два лишних нуля. Как побороть данную проблему? Код тут http://pastebin.com/CNLSR4Fv. Камень - мега8.
# Andrey789 2014-10-20 19:57
И еще поменял пины для удобства разводки на Code:#define LCD_DATA_0 D, 0, _NONE
#define LCD_DATA_1 D, 1, _NONE
#define LCD_DATA_2 D, 2, _NONE
#define LCD_DATA_3 D, 3, _NONE
#define LCD_DATA_4 D, 4, _HI
#define LCD_DATA_5 D, 3, _HI
#define LCD_DATA_6 D, 2, _HI
#define LCD_DATA_7 D, 1, _HI

//управляющие выводы LCD
#define LCD_RS D, 7, _HI
#define LCD_RW D, 6, _HI
#define LCD_EN D, 5, _HI

дисплей показывает мерцаюцие квадратики. Печаль-беда :cry:
# Andrey789 2014-10-20 20:22
Последнюю проблему решил с помощью виртуального порта.
Спасибо за прекрасные библиотеки ;-)
# Mikhail_5000 2015-03-22 21:01
Объясните дураку. Из каких соображений для процедуры LCD_Wait используется модификатор INLINE. Скорость выполнения? да вроде нужно убить 40мкСек. куда торопиться? ни как не въеду.
# DiS 2015-11-01 12:36
Подскажите как выводить пользовательски й символ с помощью функции
void LCD_SetUserChar (uint8_t __flash *sym, uint8_t adr)
Как я понимаю , надо создать массив Code:sym [] ={0b11111,0b10001,0b10000,0b11110,0b10001,0b10001,0b11110,0b00000,};(это русская буква Б)
во флеш памяти с символами , в самой функции прописывать начало адреса символа в флеш памяти ? У меня еще сильный напряг с синтаксисом Си , так что если не сложно можно пример , хотя бы с тойже буквой Б ?
# dima1208 2016-02-23 19:00
Здравствуйте, Павел.
Огромное спасибо вам за материалы, примеры и библиотеки. Только начал изучать микроконтроллеры.
Подскажите, у меня дисплей 20х4, выше Dim писал что надо изменить дефайн на такой Code:#define LCD_Goto(x,y) LCD_WriteCom((( ((y)&1)*0x40)+( (((y)&3)>>1)*0x 14)+(x))|128)
После этого у меня не компилируется, ругается на F_CPU, return type of main is not int, отсутвующие скобки и неверный суффикс Х в LCD_Goto.
В синтаксисе еще плохо разбираюсь. Почему так?
Заранее спасибо!
# dima1208 2016-02-23 19:03
Блин разобрался, пробел лишний стоял))) Все равно спасибо!
# Minloud 2016-03-20 11:32
Код отказывается запускаться на ATmega48 в Proteus . Выводит ошибку invalide opcode 0x0088 at PC 0x0202. А на Atmega16 работает без проблем. В чем дело? Помогите разобраться. Спасибо
# Александр 1 2016-04-23 12:23
Удобная библиотека! Автор допиши пожалуйста в шапке, что обязательно подключать переменный резистор на вывод V0, пол дня потратил на поиск причины неработоспособн ости.
# dima1208 2016-05-16 17:34
Здравствуйте. Подскажите пожалуйста, экран 16х2, AC162Y, KS0066U. При включении первая строка горит квадратиками и все, ничего не меняется. Резистор ставил разный, контрастность меняется. Все пины перепроверил, все подключено нормально. Даже если контроллер отключаю - все равно квадраты горят(( В чем может быть дело? Заранее спасибо!
# Nikitos 2016-06-02 10:42
Доброго дня, нашел тут ошибочку в библиотеке, пофиксил. В функции LCD_SendStrFl() код такой:
Code:char data;
while (*str){
data = pgm_read_byte(str);
str++;
LCD_WriteData(data);
}

а нужно (можно) такой:
Code:char data;
while (data = pgm_read_byte(str)){
str++;
LCD_WriteData(data);
}

Иначе нулевой конец ищется в оперативке.
# ValeryTver 2016-06-13 17:10
у меня не получается сделать активным низкий уровень на сигнале строба EN.
в уже готовой железке в этой цепи стоит транзистор, делающий этот сигнал инверсивным, сам незнаю зачем, плата заводская, ставя в .h файле _LOW программа он все равно работает с высоким уровнем.
подскажите как поступить?
# Алексей 2017-03-18 19:44
Уважаемый Pashgan! Большое спасибо за библиотеку, с индикатором 2х16 вообще никаких проблем нет. Но вот в чем вопрос. Хотелось бы подключить индикатор 4х40. Т.е. он на двух контроллерах,со ответственно, имеются два сигнала Е - Е1 и Е2. Если есть какие-нибудь наработки по этой теме, благодарен буду выслушать.
# goodspeedmen 2017-11-05 20:43
что имеется ввиду /*функция вывда строки из ОЗУ*/ вывод переменной???
# goodspeedmen 2017-11-11 13:45
Я понимаю что вопрос чайника, но все же. Пытаюсь написать код на СИ для LCD - AVR, вроде казалось вопрос избитый но как оказалось хороший библиотеке рабочей нет, то распиновка не подходит, всякая фигня в виде ошибок или вообще не работает. Читая всякие сайты я не смог понять как происходит передача числа из переменной если значение более 9. В памяти lcd нет числа 124 или 234 значит либо это происходит программно или аппаратно в самом lcd. Но судя из того кода в библиотеках которые я смог найти и понять, идет просто отправка байта, значит это происходит аппаратно в lcd?

У вас недостаточно прав для комментирования.