HD44780. Библиотека для avr-gcc

AVR
Первым (и пока единственным) навесным расширением моей отладочной платы стал LCD дисплей WH1602 (или как-то так) с контроллером HD44780. Такого рода дисплеи пользуются популярностью среди электронщиков-самодельщиков за их простоту и достаточную для большинства задач информативность.



О работе с HD44780 уже писано-переписано столько, что добавлять нечего. На этом же сайте, например, подробнейший мануал по подключению и системе команд этого контроллера. От себя только упомяну две особенности, которые попили мне крови, пока я разбирался. Первая — вход контрастности. От напряжения на этом входе зависит, собственно, контрастность дисплея. И я по наивности думал, что можно притянуть этот вход куда-нибудь наугад или вообще оставить в воздухе — хоть что-то видно будет, а там разберусь. Оказалось — нет! На моём дисплее уже при 2 вольтах и выше буквы становятся настолько неконтрастными, что прочитать ничего не получится, а при нуле экран — сплошные квадраты. Поэтому, чтобы что-то увидеть на экране, надо заранее озаботиться подходящим напряжением. Именно для его регулировки рядом с моим дисплеем висит подстроечный резистор.
Вторая особенность — это инициализация в четырёхбитном режиме. Чтобы контроллер нормально инициализировался в четырёхбитном режиме нужно команду инициализации передать хитро. Выглядит команда так: 001DNFxx, где D — битность (0 — 4 бита, 1 — 8 бит), N — число строк, F — размер символа. Я сначала пытался отдавать эту команду как и все другие:

  • Старшие полбайта
  • Строб
  • Младшие полбайта
  • Строб.


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

  • Старшие полбайта
  • Строб
  • Снова старшие полбайта
  • Строб
  • Младшие полбайта
  • Строб


С такой инициализацией дисплей работает без сучка и задоринки.

Библиотека


Вдоволь надёргавшись ножками я сел и написал простенькую библиотеку для работы с HD44780. Оних библиотек существует уже много, но свою писать, это, во-первых, упражнение, а во-вторых, пока написал — во всём разобрался. Писал в пятой AVR Studio, то есть писал под avr-gcc.

Вот краткое описание:
  • Ноги D4-D7 могут находиться на произвольных ногах какого-нибудь одного порта.
  • Ноги R/S, R/W, E могут находиться на произвольных ногах какого-нибудь одного порта. Можно использовать тот же порт, что и для D4-D7.
  • Функция lcdIsBusy(), проверяет занят ли дисплей, или готов к приёму новой команды.
  • Функция lcdInit() — инициализация дисплея.
  • Функции lcdSetCursor() и lcdSetDisplay() для настройки поведения курсора и дисплея соответственно.
  • Функции lcdClear(), lcdGotоXY() и lcdDisplayScroll() для очистки экрана, установки курсора и прокрутки экрана соответственно.
  • Функции lcdPuts(), lcdPutsf() и lcdPutse() для вывода на экран строк из RAM, Flash и EEPROM соответственно.
  • Функции lcdLoadCharacter(), lcdLoadCharacterf() и lcdLoadCharactere() для загрузки в знакогенератор дисплея пользовательского символа из RAM, Flash и EEPROM соответственно.


Хотел ещё чего-то наваять, но отвлёкся, сразу не сделал, а сейчас уже и не помню чего хотел. Так что выкладываю библиотеку как есть, пользуйтесь если понравится. Вся документация внутри самой библиотеки в виде комментариев. Рекомендую внимательно прочесть хотя бы lcd-library.h. Там же, в архиве, небольшой пример работы с пользовательскими символами и строками во Flash.
Архив с библиотекой прикреплён к статье. Ещё, на всякий случай прикрепил даташит на контроллер.

А вот ужасного качества видео, демонстрирующее работу моей платы с LCD дисплеем:

Файлы в топике: lcd-library.zip, HD44780.zip

Комментарии (82)

RSS свернуть / развернуть
Код оформлен очень аккуратно.
Но можно кое-где подсократить и оптимизировать.
Часто встречается конструкции:
(1<<LCD_RS) | (1<<LCD_RW) | (1<<LCD_E)
и
(1<<LCD_D4) | (1<<LCD_D5) | (1<<LCD_D6) | (1<<LCD_D7)

Удобно для таких целей заводить дополнительные определения:
#define LCD_CONTROL_MASK ((1<<LCD_RS) | (1<<LCD_RW) | (1<<LCD_E))
#define LCD_DATA_MASK ((1<<LCD_D4) | (1<<LCD_D5) | (1<<LCD_D6) | (1<<LCD_D7))
...
LCDCONTROLDDR |= LCD_CONTROL_MASK;
LCDDATADDR |= LCD_DATA_MASK;


Функцию void lcdPutsf(char *str) можно оптимизировать так:
void lcdPutsf(char *str)
{
   char c;
   while (c = pgm_read_byte(str++)){
      while (lcdIsBusy()) ;
      lcdRawSendByte(c, LCD_DATA);
   }
}

Будут чуть меньше и чуть быстрее.
+1
Кстати, у Вас не только оптимизация, но исправление ошибки: в исходной функции всегда отправляется нулевой символ. Аналогично нужно изменить и lcdPutse.
0
Спасибо за комментарии и исправления! У меня паскальное воспитание, поэтому я на си иногда делаю чуток громоздко ))
0
… всместо
Старшие полбайта
    Строб
    Снова старшие полбайта
    Строб
    Младшие полбайта
    Строб
… можно так:

    Старшие полбайта
    Строб
    Строб
    Младшие полбайта
    Строб
потому что старший нибл итак тарчит на выводах. Кстати вот подобная либа
0
Тоже, кстати, мысль. Спасибо!
0
Что-то ссылка «подобная либа» не работает :(
0
Видать сайт там тормозит. У меня сейчас главная открылась и всё, больше никуда. Попробуйте позже посмотреть.
0
… были какие-то проблемы с утра, сейчас вроде пашет
0
Немножко не пойму, почему вы думаете, что всегда выводит? У меня lcdrawsendbyte тоже выполняется только если текущий байт ненулевой. Или я что-то упускаю?
0
Всё правильно, это меня переклинило :)
0
Ааа :) А то я минут десять тупил в свой код, пытаясь понять в чём разница в условиях :)
0
На мой взгляд, лучше вместо
while (lcdIsBusy()) ;
писать так
while (lcdIsBusy()) {
}
или даже так
while (lcdIsBusy()) {
    /* do nothing */ 
}
Точку с запятой легко потерять.
0
Привычка, не люблю пустые скобки, а про точку с запятой, у меня ещё с паскаля в голове твёрдо сидит, что это не фигня, а пустой оператор :)
0
Я в таких случаях пишу обычно так:
while (lcdIsBusy()) {}; 

И места немного занимает, и пустые фигурные скобки видно.
0
тогда уж так:)

while ( lcdIsBusy() ) continue;
0
отвратительная версия. с пустыми фигурными и даже с просто точка с запятой — гораздо читабельней.
0
*пожимает плечами*

вообще-то классический приём
0
Хорошая робота, возьму себе на вооружения.
0
Напомню, что в AVR Toolchain (ранее WinAVR) также есть подобная библиотека.
0
  • avatar
  • John
  • 19 апреля 2012, 20:45
+1 за использование 4-битного режима. долго не мог понять что к чему
0
Вобще даташит на данный дисплейчик требует инициализировать его несколько иначе.
Обычно и без этого прекрасно работает, но так получилось, что мой самый первый дисплейчик без данной инициализации вобще не работал. Я долго мучил бублиотечку на асме от Дихалта, пытаясь понять в чем дело, пока не наткнулся в даташите на рекомендацию по инициализации — переписал код и сразу все заработало.

0
Спалил ненароком свой дисплей, и заодно весь порт B у контроллера, на днях распаяю другой, попробую. Вообще это у моей библиотеки слабое место, я её только на одном дисплее испытывал. В даташите, немножко раньше сказано так: «An internal reset circuit automatically initializes the HD44780U when the power is turned on. The
following instructions are executed during the initialization. The busy flag (BF) is kept in the busy state
until the initialization ends (BF = 1). The busy state lasts for 10 ms after VCC rises to 4.5 V.». Поэтому я посчитал достаточным подождать пока BF упадёт. А тот случай, о котором вы написали, если я правильно понял для случаев, когда с питанием не всё хорошо. Но при случае допишу lcdHardInit(). Спасибо :)
0
Здорово. До кучи покажу ещё пример библиотеки на Си:
Библиотека работы с HD44780 для AVR

А вот мой вариант для С++: LCD.cpp

Инициализацию делаю таким образом:
0
  • avatar
  • uni
  • 22 апреля 2012, 15:03
Дядя Женя, спасибо за Вашу работу. Проверил с помощью Вашей либы дисплей 93-го года 24х2 от какого-то «секретарского» телефона, сделан он на KS0066 + 2xKS0065, они еще в нормальных корпусах, а не в виде кляксы. Пока играюсь с ним, в дальнейшем планирую использовать его в БП. Код мне показался красивым, с т.з. начинающего, в общем я по нему еще и учусь.

Кто-нибудь может подсказать, как устроена подсветка в дисплеях с оной? Мне хочется свой переделать, чтобы был с подсветкой, если это малой кровью получится.
0
На здоровье! Рад, что пригождается ))

В WH1602, по крайней мере в тех, с которыми я сталкивался подсветка состоит из пары светодиодов и световода под экраном.
0
Подсветка — равномерно светящаяся поверхность. Есть несколько технологий получения такой:
1) EL-лампа. Представляет собой пленочку, светящуюся при подаче напряжения. Самая тонкая (пленка она и есть пленка, 200-500 микрон), но недолговечная и требует высокое напряжение (переменка 80-100В).
2) Линейка или матрица светодиодов, накрытая матовым светорасеивателем. Очень толстая (5-10мм), зато довольно равномерная и долговечная.
3) Прозрачная пластинка световод-рассеиватель, подсвечиваемая одним или несколькоими светодиодами с торца. По долговечности как предыдущая, толще EL, но тоньше второго варианта (где-то от полумиллиметра до 3-4мм, толщина определяется световодом-рассеивателем), равномерность напрямую зависит от качества светорассивателя.

Так что смотришь, сколько места между стекляхой и платой и прикидываешь, что из этого удастся достать/наколхозить и поставить. Еще проблема может возникнуть, если отражающая пленка под стекляхой слишком непрозрачна. Тогда придется либо выкинуть ее (но дисплей будет видно только с подсветкой), либо искать специальную пленочку — отражающую с одной стороны и прозрачную с другой.
0
Спасибо за информацию!
Расстояние там от стекла с пленкой до ПП 1,5 мм, торцы стеклянные (т.е. прозрачные), я пробовал в них светить — ничего не видно.
Эта пленка светоотражающая, но с торцов отражение не работает. Подсовывал еще туда чип светодиод — не прошибает. Видимо придется ее сдирать и делать подложку из остатков ноутбучного экрана, там как раз имеются несколько рассеивающих пленок и кусочек тонкого оргстекла.
Я просто думал, что содрав родную пленку я поврежу дисплей.
0
Смотря насколько аккуратно сдерешь и как она приделана. Сзади две пленки — поляризатор и зеркало. Содрать нужно только зеркало, не попортив поляризатор и стекло. Вообще, сколько я видел зеркал — они обычно частично прозрачные даже у рефлективных дисплеев, так что при достаточной мощности подсветки наверное все же можно светить сквозь зеркало.
Еще один вариант — фронтальная подсветка. Для нее, правда, нужен специальный световод.
Кстати, в ноутбучном модуле подсветки есть односторонние зеркала, отрезком которых вполне можно заменить родное зеркало. Единственное, что надо учитывать — там хитрые зеркала, они отражают свет в одной поляризации, а в перпендикулярной — пропускают. Поскольку свет после ЖК поляризованный — придется подобрать ориентацию зеркала за ним по максимуму отражения.
0
Купил на ебее пару дисплеев HD44780 1602A, по ДШ процедуры инициализации/выводв и т.д. те же самые (окромя ихнего алфавита). Но не могу ни одной библиотекой вывести на экран хоть что-то вразумительное, горят верхний ряд квадратиков и всё (при некоторых прошивках и нижний ряд =)). С этой либой та же песня… подскажите как запустить эту штуку? Юзаю 4 битный интерфейс, подстветка через переменный резистор 10К кручу-верчу.
0
  • avatar
  • cacco
  • 30 августа 2012, 15:40
подстветка через переменный резистор 10К кручу-верчу.
Через этот резистор не подсветка, а контрастность.
0
И совсем ничего не меняется, в процессе кручения-верчения? Хоть квадратики должны пропадать-появляться. Если вообще никаких изменений, то возможно не работает дисплей, либо это не вход контрастности, либо, возможно, данный дисплей требует на этот вход отрицательной напруги. В последнем не уверен, но, вроде, бывают и такие.
0
Похоже на то, что контрастность не подключена, или выставлена неверно. Надо повесить переменный резистор между землёй и питанием, и с его движка подать напругу на вход контрастности экрана. И потом переменником выставить такую контрастность, при которой всё видно.
0
Если только первый ряд квадратиков — значит инициализация не прошла.
Если два ряда — скорее всего неправильное напряжение контраста.
0
Кажется я напорол с объяснением: потенциометром 10К меняю контрастность, не подсветку (3 вывод дисплея). При кручении в одном положении — на экране сплошной фон, в крайнем другом — квадратики. Подсветку (15 вывод) подключил через 150 Ом резистор.
Прошивку откомпилировал ту что здесь в примере лежит, только в библиотеке lcd-library.h поменял порты МК. Смущает то что с обоими дисплеями одинаковая картина…
0
  • avatar
  • cacco
  • 30 августа 2012, 16:44
Хм… а плата, на которой запускаете, какой-нибудь отдельный светодиод или ещё что-нибудь для индикации? Если да, то попробуйте после lcdInit, да и вообще в произвольных местах помигать диодом, для того, чтобы выяснить, оно вообще выполняется или нет. В библиотеке все функции ждут флага готовности от дисплея, и если его нет (например в силу особенностей данного дисплея или вследствие ошибки подключения), то программа повиснет в цикле ожидания флага. В общем предлагаю выяснить, выполняется ли программа вообще или висит на while (lcdIsBusy()); Если выпоняется, но нет вывода, то это пожалуй недоработки в моей либе. Если висит, то, вероятнее всего, ошибка подключения. (RS/RW перепутаны или шина данных). В моём, например, ysytiytv дисплее распиновка неочевидная, я первый раз его распаял неправильно, пришлось даташит искать, чтобы разобраться. Хорошо не спалил :)
Если есть отладка, то можно и без диода посмотреть :)
0
Попробуйте ещё вариант, пердложенный uni. Там инициализация другая. Возможно, именно в вашем случае это имеет значение. Мой код работал на двух дисплеях, побывавших у меня в руках, и, судя по комментариям, ещё на нескольких. Довольно небольшая статистика пока :)
0
Спасибо за подсказку — припаял светодиод, понял что инициализация не проходит. Попытался весь проект смастерить в AvrStudio 5 — выдаёт ругань что F_CPU не задан, а также требование использовать функцию -std=gnu99 (так и не нашёл где в свойствах проекта поставить. В AvrStudio 4.19 всё отлично компилировалось).
Распиновка как раз на дисплее в 1 ряд самая что ни на есть стандартная.
0
  • avatar
  • cacco
  • 04 сентября 2012, 16:28
F_CPU в пятой студии можно задать через настройки Toolchain->AVR/GNU C Compiler->Symbols, там создаёте новый Symbol и пишете в него F_CPU=8000000UL. Или можно прямо в коде через #define F_CPU 8000000 задать. А вот что зачем -std=gnu99 — ума не приложу, никогда не сталкивался… Но включить это дело наверняка тоже можно где-то в настройках тулчейна.
0
Toolchain -> AVR/GNU C Compiler -> Miscellaneous -> Other flags, вписать в текстовое поле -std=gnu99 . Этот параметр нужен, как минимум, для подобного кода (определение переменной i в цикле for):
for (char i = 0; i <= 7; i++){
    while (lcdIsBusy()) ;
Параметр указывает компилятору использовать стандарт языка C99 с расширениями GNU. Если расширения не нужны, можно указать -std=c99 .
0
ЕМНИП, для этого есть специализированная галочка где-то в настройках. Примерно так и подписана.
0
  • avatar
  • Vga
  • 05 сентября 2012, 10:20
Фух… благодаря подсказкам с AvrStudio сладил, но всё равно поставил 4.19 — с ней как-то проще, чтоли. Но ЖКИ победить не могу… По совету Дяди Жени
Попробуйте ещё вариант, пердложенный uni. Там инициализация другая. Возможно, именно в вашем случае это имеет значение. Мой код работал на двух дисплеях, побывавших у меня в руках, и, судя по комментариям, ещё на нескольких. Довольно небольшая статистика пока :)
попробовал библиотеку Netimperia. Благодаря светодиоду вычислил что не проходит инициализация. Стал внутри программы инициализации ковыряться — понял что не проходит строчка «HD44780_SEND_CMD_HOME» — отправляет команду, которая устанавливает курсор в начало. Это что получается, ЖКИ не воспринимает команды нормально? Цепи стопицот раз прозвонил уже, RS сидит на PD0, E на PD1, RW — на GND..?
0
  • avatar
  • cacco
  • 18 сентября 2012, 17:17
А почему RW на GND? RW тоже должна в порт заходить. Она же определяет направление передачи данных. Если на RW ноль, то дисплей ждёт команду от контроллера, а не отдаёт ему свой статус. А значит, вернуть флаг занятости он не может. Поэтому и не работает. Вешайте RW на PD(сколько-нибудь) и пропишите в настройках либы, и всё заведётся.
0
Впрочем это я о своей либе :) Но в варианте от Netimperia флаг занятости тоже проверяется.
0
Дааа!!! Завелась!!! Спасибо за ответ, всё как по подсказке. Когда разбирался как подключать дисплей, много где писали что RW на землю надо вешать если не требуется запись в дисплей. Мне, собственно, и не требовалась… Ещё раз спасибо, uncleeugene!!!
0
  • avatar
  • cacco
  • 19 сентября 2012, 16:49
Пожалуйста! :) Только не запись в дисплей, а чтение из него. Запись в него, это отправка ему данных, без этой операции он совсем бесполезное устройство :)
0
Это пока не изобрели дисплеи-экстрасенсы. Программисты вон изначально умеют делать то что надо без указания что надо сделать.
0
Здравстивуйте. А как с помощью этого драйвера выводить кирилицу?
Использовал код для отображения всех символов по номеру через дефис. Кирилица присутствует и в официальном datasheet-e на WH1602B-YYH-CTK он присутствует. А при использовании ф-ии lcdPuts() для вывода — латинский вывод отлично, а в место кириллицы другие символы библиотеки.:(

#include <avr/io.h>
#include <avr/pgmspace.h>
#include "lcd-library.h"
int main(void)
{
char myStringRU[] = "Мое имя Дима";
lcdInit();
lcdClear();
lcdPuts(myStringRU);
    while(1)
    {
    }
}
0
У этих дисплеев своя кодировка. Естественно, что строку в CP1251 он отразит кракозябрами. Используй любой метод конвертирования — есть различные программы для перекодировки, функции для перекодировки из CP1251 прямо на МК и так далее.
0
Спасибо. Разобрался, сделал по автоматам совмесил символ запрашиваемый из lcd, с фактической кириллицей.
void lcdRawSendByte(char byte, char state)
/*
	Отравляет байт в LCD. Если state == 0, то передаётся как команда,
	если нет, то как данные.
*/
{
//Заменяем/перекодируем номер символа, для нормального отображения кирилицы
	if (byte >= 0xA0 && state != 0)
	{
		switch (byte)
		{
			case 0xC0: byte = 0x41; break;//А
			case 0xC1: byte = 0xA0; break;//Б
			case 0xC2: byte = 0x42; break;//В
			case 0xC3: byte = 0xA1; break;//Г
			case 0xC4: byte = 0xE0; break;//Д
			case 0xC5: byte = 0x45; break;//Е
			case 0xA8: byte = 0xA2; break;//Ё
			case 0xC6: byte = 0xA3; break;//Ж
			case 0xC7: byte = 0xA4; break;//З
			case 0xC8: byte = 0xA5; break;//И
			case 0xC9: byte = 0xA6; break;//Й
			case 0xCA: byte = 0x4B; break;//К
			case 0xCB: byte = 0xA7; break;//Л
			case 0xCC: byte = 0x4D; break;//М
			case 0xCD: byte = 0x48; break;//H
			case 0xCE: byte = 0x4F; break;//O
			case 0xCF: byte = 0xA8; break;//П
			case 0xD0: byte = 0x50; break;//P
			case 0xD1: byte = 0x43; break;//C
			case 0xD2: byte = 0x54; break;//T
			case 0xD3: byte = 0xA9; break;//У
			case 0xD4: byte = 0xAA; break;//Ф
			case 0xD5: byte = 0x58; break;//X
			case 0xD6: byte = 0xE1; break;//Ц
			case 0xD7: byte = 0xAB; break;//Ч
			case 0xD8: byte = 0xAC; break;//Ш
			case 0xD9: byte = 0xE2; break;//Щ
			case 0xDA: byte = 0xAD; break;//Ъ
			case 0xDB: byte = 0xAE; break;//Ы
			case 0xDC: byte = 0x62; break;//Ь
			case 0xDD: byte = 0xAF; break;//Э
			case 0xDE: byte = 0xB0; break;//Ю
			case 0xDF: byte = 0xB1; break;//Я
			case 0xE0: byte = 0x61; break;//а
			case 0xE1: byte = 0xB2; break;//б
			case 0xE2: byte = 0xB3; break;//в
			case 0xE3: byte = 0xB4; break;//г
			case 0xE4: byte = 0xE3; break;//д
			case 0xE5: byte = 0x65; break;//е
			case 0xB8: byte = 0xB5; break;//ё
			case 0xE6: byte = 0xB6; break;//ж
			case 0xE7: byte = 0xB7; break;//з
			case 0xE8: byte = 0xB8; break;//и
			case 0xE9: byte = 0xB9; break;//й
			case 0xEA: byte = 0xBA; break;//к
			case 0xEB: byte = 0xBB; break;//л
			case 0xEC: byte = 0xBC; break;//м
			case 0xED: byte = 0xBD; break;//н
			case 0xEE: byte = 0x6F; break;//о
			case 0xEF: byte = 0xBE; break;//п
			case 0xF0: byte = 0x70; break;//р
			case 0xF1: byte = 0x63; break;//с
			case 0xF2: byte = 0xBF; break;//т
			case 0xF3: byte = 0x79; break;//у
			case 0xF4: byte = 0xE4; break;//ф
			case 0xF5: byte = 0x78; break;//х
			case 0xF6: byte = 0xE5; break;//ц
			case 0xF7: byte = 0xC0; break;//ч
			case 0xF8: byte = 0xC1; break;//ш
			case 0xF9: byte = 0xE6; break;//щ
			case 0xFA: byte = 0xC2; break;//ъ
			case 0xFB: byte = 0xC3; break;//ы
			case 0xFC: byte = 0xC4; break;//ь
			case 0xFD: byte = 0xC5; break;//э
			case 0xFE: byte = 0xC6; break;//ю
			case 0xFF: byte = 0xC7; break;//я
		}
	}
	lcdSendNibble((byte>>4), state);					
	lcdSendNibble(byte,state);
}

А не подскажите, возможно ли сделать, чтобы прокручивалась одна строка, а на второй оставался текст без изменений. Или чтобы прокручивались обе строки в разные стороны. В этом драйвере если крутятся, то обе с одним шагом.
0
Аппаратно в экране реализуется только такая прокрутка. Грубо говоря, память дисплея вмещает две строки по 40 символов, а видно только 2 по 16, например. И прокрутка смещает видимое окно по памяти вправо-влево. Более сложные варианты придётся делать программно.
0
Какой пи… кхм, ужас. Даже тупое перекодирование по массиву будет куда эффективней. А я тут где-то встречал функцию, которая обходится массивом всего на 64 символа, а не 128, как при решении в лоб.
В общем, лучше погугли нормальный вариант.
0
сколько матерных слов на языке вертится… %)
0
Сделал перекодирование по массиву:):):)
А как вывести посчитанное значение в программе (с датчиков).
Например i — это посчитанное значение, и его надо вывести, а драйвер выводит строки. Значит его надо преобразовать либо в строку, либо перекодировать по массиву?
0
Например у вас i = 1916, так, с потолка. Делаем целочисленное деление на 1000:
1916 / 1000 = 1.
Прибавляем к результату волшебные 0x30, и получаем ASCII-код символа «1»:
1 + 0x30 = 0x31.
Потом берём остаток от первой операции:
1916 mod 1000 = 916.
Остаток делим нацело на 100:
916 / 100 = 9.
9 + 0x30 = 0x39.
и так далее:
916 mod 100 = 16,
16 / 10 = 1,
1 + 0x30 = 0x31,
16 mod 10 = 6,
6 + 0x30 = 0x36.

Естественно, полученные значения складываем в строку друг за дружкой, и полученный результат печатаем :)
С десятичными дробями пожалуй можно сначала умножить i на такое n, чтобы точка ушла за пределы точности, а потом в строке подставить её в n'ную с конца позицию.
0
Умножить на n, это в смысле повысить порядок, то есть умножить на 10, 100, 1000 и т.д.
0
neiver недавно писал статью на тему сравнения по скорости разных способов преобразования числа в строку. Почитай, там для всех способов приведен алгоритм и ЕМНИП код.
0
Спасибо за наводку по преобразование числа в строку. Взял на вооружение «Вычитание степеней 10»
0
Библиотека отличная!
uncleeugene, вы на гитхаб не думали ее добавить?
0
Вообще нельзя сказать, что я когда-нибудь думал о гитхабе :) В смысле контроля версий тут особых ухищрений не надо — проект маленький, да и версия пока одна всего :) Можно, конечно и выложить, но я пока не понимаю зачем. Считаете, будет польза?
0
Один из плюсов — можно будет просмотреть код без скачивания, форкнуть :)
0
Не могу добиться работы библиотеки на модельке в Proteus, зависает на вызове функции lcdIsBusy в первой же процедуре lcdInit.
Вы не пробовали в протеусе гонять?
Не могу понять в чем проблема…
0
В Proteus не пробовал. Но там наверняка есть какой-то простой (или непростой) дебаггер, можно поглядеть где затык. Как вариант, может ошибка в схеме? Если виртуальная нога RW дисплея не доходит до контроллера, например, как это было выше в комментах, то оно будет вечно висеть на этой процедуре. Попробуйте заменить весь код lcdIsBusy() на паузу в несколько мс. Гарантий не дам, но может завестись :)
0
Действительно, после замены кода на паузу в 5 мс все заработало.
Наверное, реализация LCD в Proteus неполная.
Спасибо.
0
Проверьте, подключен ли выход RW дисплея к контроллеру, и на правильную ли ногу. Не тянет ли его к земле где-нибудь, не пытается ли кто-то ещё в программе использовать ноги отданные под дисплей. Мало ли, в один бит ошибиться и не заметить — самое дело :) Хотя вроде у меня это учтено, я уж не помню :) Поглядите, дёргает ли дисплей ножками после отправки ему команды…
Или забейте :) Всё равно, в большинстве случаев ждать готовности от дисплея не обязательно, можно просто подождать с запасом.
0
Все проверил, ноги дрыгаются периодически, никто больше на этих шинах не сидит. Апноут есть, но в нем ничего про такие ограничения не говорится.
Еще один нюанс выяснил — нужно компилировать с оптимизацией -О2, если делать другую — не работает или валится с ошибками :-)
0
А вообще я слыхивал, что у Протеуса иногда нелепым образом эмуляция хромает. Сам не видел, по своему опыту сказать не могу, но на отзывы натыкался. Так что может и реализация… Там каких-нибудь описаний а ля appnotes по компонентам нету?
0
привязка к конкретной архитектуре — зло
0
Почему? :) В случае с этой либой, мне так кажется, тут не так много кода, чтобы нельзя было его при необходимости портировать самостоятельно. А в целом, лучше тот код, который работает хорошо на одной платформе, чем тот, который заявлен кроссплатформенным, но работает, опять же, только на одной платформе, потому что в остальных автор не особо шарит :) Это я намекаю на причину того, почему мой код привязан к одной платформе :)
0
Дяде Жене спасибо за проделанную работу:) Протестировал несколько библиотек, из 3-х только эта пошла.
Сперва попробовал другую. Там много интересных функций и хорошая документация, но почему-то не заработала инициализация. А разбираться времени маловато.
0
  • avatar
  • and
  • 06 февраля 2013, 18:13
сделаю-ка наброс.
в моём litenkjerne буферизованный вывод висит на прерываниях, 500Гц. Этого хватает, чтоб обновлять 16х2 15 раз в сек.
Такие дела.
А данная библиотека, пмсм, — дилетантство.
Есть такой термин «БАУНТИС» — это когда разработчик побырику пишет, чтоб как-то работало, а что потом — пофигу. Такие проекты обладают нулевой масштабируемостью.
-1
А зачем обновлять ЖКИ 15 раз в секунду? Кина на нем не поглядишь все равно :-)
0
Реально незачем обновлять эти дисплеи 15 раз в секунду — с их инерционностью это пустая трата времени. Но это конечно здорово, что ваша litenkjerne, чем бы она не была, так умеет :) А по поводу дилетантства — позвольте с вами не согласиться. Дилетантство — это кривая реализация, обусловленная недостатком знаний разраба. А у меня просто либа для облегчения работы с экраном, для тех случаев когда не надо ось и 15 раз в секунду, а надо просто вывести что-то на экран. Совершенно согласен, что масштабируемость тут нулевая, но и цели такой не ставилось. А то, что ставилось — всё работает :)
0
Реально незачем обновлять эти дисплеи 15 раз в секунду — с их инерционностью это пустая трата времени.
Не совсем так. Именно обновление с высокой скоростью плюс инерционность позволяют делать фокус — выводить 16 разных пользовательских символов одновременно.
0
Сдаётся мне, контрастность пострадает. Или не? :)
0
Говорят, вполне неплохо получается, как раз за счет инерционности. В один цикл выводятся первые 8 пользовательских символов, вместо остальных выводятся сплошные квадратики. В следующем цикле — наоборот.
0
Надо попробовать, своими глазами посмотреть :)
0
Я просто заранее привёл максимум, чтоб не было упрёков типа нафига буфер для LCD ведь вывод будет тормозить.

Тормозить будет как раз без буфера: просто посчитайте, сколько надо в сумме пауз (из пустых циклов) для вывода одного экрана. На глаз оно конешно не заметно, но злоупотребление пустыми циклами приводит в итоге к потере ценных тактов.
0
Здравствуйте. Извините за возможно глупый вопрос, но я на стадии ученика. Как правильно в avr studio 6 добавлять библиотеку. Читал и в книге Шпака и в нете так и не понял, что обозначает «добавить файл расширения „с“ библиотеки в файл проэкта» если конечно это правильно, такое находил в нете. Спасибо.
0
Я добавлял библиотеку так, два файла библиотеки копировал в корень папки проекта и в студии в тульчейне в либре прописывал путь к библиотеке, но при добавлении Ваш образец проекта выдает ошибки. Я только догадываюсь что не вступает в работу файл с расширением «с». Спасибо.
0
Лог ошибок где?
0
Error 2 variable 'Char10' must be const in order to be put into read-only section by means of '__attribute__((progmem))'
Когда пытаюсь записать Char10 как константу сыпятся другие ошибки. Подозреваю, что синтаксис не для avr studio.
0
после поставления к массиву «const» выпадает 11 ошибок типа Error 15 undefined reference to `lcdInit'
0
Только зарегистрированные и авторизованные пользователи могут оставлять комментарии.