Работа с графическим дисплеем WG12232A

Работа с графическим дисплеем WG12232A (на базе чипов SED1520) с использованием микроконтроллера AVR.


Автор: Погребняк Дмитрий.

Самара, 2013.


WG12232A-YGH-V#A
WG12232A-YGH-V#A


Недавно мне в руки попал графический дисплей WG12232A-YGH-V#A фирмы WINSTAR. И хотя спецификация нашлась довольно быстро, информация в ней оказалось довольно скудной и не всегда соответствующей истине. Поиск в интернете в основном приводил к вопросам без ответов, поэтому пришлось доходить опытным путём. Результатами своих изысканий я хочу с вами поделиться


Маркировка

Предупреждение. Здесь и далее речь идёт о практическом применении только той модели, которая есть у меня на руках – WG12232A-YGH-V#A. Остальное описание сделано на основе информации подчерпнутой из интернета и может отличаться от действительности.


Итак, вначале разберёмся с маркировкой дисплеев фирмы WINSTAR на нашем примере:


WG12232A-YGH-V

W - обозначает производителя, тайваньскую фирму WINSTAR

G - графический дисплей. H – знакосинтезирующий. В знакосинтезирующих дисплеях уже заложен некий знакогенератор (хотя, как правило, начертанием некоторых символов знакогенератора можно управлять) и вывод осуществляется посимвольно: данные кодируют, какой именно символ из набора знакогенератора будет отображаться на конкретном заданном месте. Графический дисплей (наш случай) позволяет изменять значения любой отображаемой точки, что даёт большие возможности по графическому выводу, но усложняет вывод текстовой информации.

12232 - размер дисплея 122х32. Правые две цифры – кодируют высоту, левые две или три - ширину. Для знакосинтезирующих дисплеев это размер в знакоместах (напр. WH1604 – 4 ряда по 16 символов), для графических дисплеев это размер в точках.

A - серийный код модели. На эту букву следует обратить внимание, от неё зависят количество выводов, габаритные размер дисплея, напряжение питания, использованные внутри чипы и, соответственно, поддерживаемые ими наборы команд. Например, для WG12232:

A (наш случай) – плата размером 84х44мм, внешний размер экрана 68,2х27,2мм, размер отображаемой области 53,64х15,64мм. Напряжение питания 5 Вольт. На двух чипах SED1520 (или эквивалентных), с 20ю выводами с шагом 2,54мм, с внутренним генератором частоты развёртки.

C - дисплей точно такого же размера, но на чипах SBN1661G_M18-D (или эквивалентных), в отличие от A задействует ещё два входа: внешний генератор 2 кГц и сигнал «разрешение записи».

G - плата размером 65,4х28,2мм. Внешний размер экрана 54,8х19,0. Видимая область 48,76х14,36мм. Напряжение питания 3,3 Вольта. На чипах SED1520 (или эквивалентных). С 18 выводами под шлейф (шаг 1 мм).

B - такой же, как G, но видимая область разбита на две горизонтальные части, высотой по 7,16 мм.

D - плата размером 59х29,3мм. Внешние габариты экрана 59х26мм. Видимая область 45,72х12мм, толщина всего 3,3 мм. Напряжение питания 3 Вольта. На чипах SED1520 (или эквивалентных). С 18 выводами с шагом 1,27мм.


Y - цвет подсветки

N - без подсветки;

B - электролюминесцентная, сине-зелёная;

D - электролюминесцентная, зелёная;

W - электролюминесцентная, белая;

F - флуоресцентная, белая;

Y - (наш случай) светодиодная, жёлто-зелёная;

A - светодиодная, янтарная (жёлтая);

R - светодиодная, красная;

O - светодиодная, оранжевая;

G - светодиодная, зелёная;


G - тип жидкокристаллического дисплея, режим работы и цвет

B - TN, позитивный, серый;

N - TN, негативный;

G - (наш случай) STN, позитивный, серый;

Y - STN, позитивный, жёлто-зелёный;

M - STN, негативный, синий;

F - FSTN, позитивный;

T - FSTN, негативный.


TN – twisted-nematic, обладают меньшим временем реакции (реагируют быстрее, время отклика порядка 100мс), но меньшей контрастностью (около 3:1).

STN – super-twisted-nematic, обладают большим временем реакции (реагируют дольше, время отклика порядка 250мс), но большей контрастностью (около 10:1). При этом возможны цветовые искажения при просмотре под большим углом.

FSTN – film-compensated super-twisted-nematic, добавлена плёнка, компенсирующая цветовые искажения при просмотре под разными углами.


Позитивный - ЖК-дисплей пропускает свет подсветки, при выводе единицы, указанная точка перестаёт пропускать свет. То есть, получаются тёмное изображение на светлом, светящемся фоне.

Негативный - ЖК-дисплей не пропускает подсветку, но начинает пропускать в указанной точке при выводе единицы. То есть, получаются светящиеся цветом подсветки символы на тёмном фоне.


H - тип поляризации, диапазон температур, угол обзора

A - отражающий, N.T, 6:00;

B - трансфлективный, N.T, 6:00;

C - пропускающий, N.T, 6:00;

D - отражающий, N.T, 12:00;

E - трансфлективный, N.T, 12:00;

F - пропускающий, N.T, 12:00;

G - отражающий, W.T, 6:00;

H - (наш случай) трансфлективный, W.T, 6:00;

I - пропускающий, W.T, 6:00;

J - отражающий, W.T, 12:00;

K - трансфлективный, W.T, 12:00;

L - пропускающий, W.T, 12:00;


отражающий - эти дисплеи используются без подсветки и видны в отражённом свете.

пропускающий - информация на таких дисплеях плохо видна в отражённом свете, только при использовании подсветки.

трансфлективный - эти дисплеи видны и в отражённом свете и в свете подсветки.


N.T. - обычный рабочий температурный диапазон, от 0 до 50°С.

W.T. - расширенный рабочий температурный диапазон, от -20 до 70°С.


12:00 - угол обзора «сверху»: информация на дисплее лучше видна, если дисплей находится ниже уровня глаз наблюдателя;

6:00 - угол обзора «снизу»: информация на дисплее лучше видна, если дисплей находится выше уровня глаз наблюдателя.

Следует учесть, что выводы дисплея WG12232A/C находятся сверху. Часто удобнее расположить дисплей выводами вниз. В этом случае он будет, наоборот, лучше смотреться снизу, или сверху, соответственно. При этом следует озаботиться написанием программного обеспечения должным образом, чтобы оно учитывало «перевёрнутость» дисплея. Правда, чипы SED1520 обладают режимом «обратной адресации», который облегчает адаптацию ПО для случаев, когда дисплей расположен вверх тормашками.


V - эта буква указывает на особенности дисплея. В частности буква V указывает на наличие вывода отрицательного напряжения, который можно использовать для питания ЖК-матрицы. Буква N указывает на отсутствие такого выхода, при этом контакты 19-20 могут использоваться для подачи напряжения на подсветку дисплея.


Устройство

Адресация видеопамяти
Адресация видеопамяти

Дисплей управляется двумя идентичными чипами, работающими независимо. Один управляет левыми 61й точкой дисплея, второй – правыми. Поэтому вывод графической информации пересекающей середину дисплея может представлять некую сложность.

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

Операции чтения (кроме режима чтение-изменение-запись) и записи автоматически увеличивают позицию слева направо, таким образом, следующая операция чтения и записи будет изменять/возвращать значения восьми пикселей в следующей горизонтальной позиции. Однако только до достижения 80й колонки, после чего адрес увеличиваться не будет.

У чипов SED1520 есть «обратный» режим работы, при этом колонки нумеруются справа налево, соответственно, операции записи/чтения увеличивая адрес, смещают позицию записи влево. Этот режим удобен в случае, если дисплей расположен вверх ногами (контактами вниз).

Чипы предназначены для вывода информации в 80 столбцов, в то время как на дисплее отображается только по 61 столбцу, правые 19 столбцов не отображаются. Поэтому в обратном режиме первый видимый столбец имеет номер 19.


При нормальной ориентации дисплея (контакты вверху) младший бит кодирует верхний (ближний к контактам) из восьми пикселей, старший – нижний. Включение обратного режима не изменяет назначение бит, таким образом, при перевёрнутом дисплее младший бит будет по-прежнему кодировать пиксели ближние к контактам, что следует учесть при разработке программного обеспечения.

Вертикально информация разбита на 4ре строки (называемые страницами) нумеруемые от 0 до 3х. Страницы нумеруются от верха при нормальной ориентации (от контактов) и не меняют нумерации в обратном режиме (т.е. нулевая страница остаётся ближней к контактам).


Подключение

Пример схемы подключения дисплея WG12232A к микроконтроллеру
Пример схемы подключения дисплея WG12232A к микроконтроллеру

Как упоминалось выше, описание относится к устройству WG12232A, но, т.к. во многом устройства A и C схожи, здесь будет приведена справочная информация и для модели WG12232C. Различия моделей отмечены ниже символами A* и C* соответственно. Также отличия 19-20 контактов касаются моделей с источником отрицательного напряжения (V в конце), и без оного (N), обозначены в таблице V* и N*, соответственно.

КонтактОбозначениеНазначение
1VssОбщий, минус
2VddПитание для логики, +5В
3VoПитание ЖК матрицы, переменное, от -5В до +5В
4A0Высокий уровень – данные, низкий – статус/инструкции
5CS1Выбор первого (левого) чипа
6CS2Выбор второго (правого) чипа
7NC/CLA* – не используется; C* – тактовый вход 2 кГц
8NC/EA* – не используется; C* – строб операции записи
9R/W̅A* – низкий уровень – запись, высокий – чтение; C* – строб операции чтения
10...17DB0...DB7Двунаправленная параллельная шина данных
18R̅E̅S̅Аппаратный сброс. Низкий уровень – сброс.
19Vee/AV* – выход с генератора отрицательного напряжения, -5В; N* - анод подсветки (+4,2 В)
20NC/KV* – не используется; N* – катод подсветки

1. Vss, общий

Сюда подключается минус, общий для питания логики и для управляющих сигналов.


2. Vdd, питание логики

+5 Вольт. Несмотря на то, что в спецификации на моё устройство написано что оно может, якобы, работать и от 2,7 Вольт, при подаче напряжения 3,3 Вольта дисплей не запустился. Поэтому, можно считать правдивой информацию из другого документа, где обозначено напряжение питания 4,5-5,5В. Этот же вход используется в качестве плюсового для питания ЖК.


3. Vo, питание ЖК

Питание для ЖК берётся между этим входом и напряжением Vdd, на входе 2. Регулировать его следует в диапазоне от -5 до +5 Вольт. Таким образом напряжение питания ЖК может изменяться от 10 до 0 Вольт. Инструкция рекомендует использовать переменный резистор 10-20 килоОм, один хвост которого подключен к +5В (Vdd), другой же подключен к источнику питания -5В, а переменный вывод подключен к этому входу.

В моделях с буковкой V на конце присутствует генератор отрицательного напряжения, которое выводится на 19й контакт. Для моделей с буковкой N, инструкция рекомендует использовать внешний источник, однако схема вполне хорошо работает, если второй вывод переменного резистора подключить не к отрицательному напряжению, а к «земле».


4. A0, данные/инструкции

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


5-6. CSn, выбор чипа

Для моделей WG12232A являются также стробирующими импульсами. Появление высокого уровня на одном из этих входов заставляет соответствующий чип читать данные с линий A0, R/W̅ и, для операции чтения, выставлять ответ на шине данных. Это означает, что сигналы на этих линиях должны быть выставлены заранее, до подачи импульса на CS.

При операциях чтения ответ будет удерживаться на линии до спада импульса на соответствующем входе CS. Таким образом, операция чтения представляет собой выставление высокого уровня на линии R/W̅, затем спустя минимум 20нс, высокого уровня на линии CS, и, выжидания некоторого промежутка времени (90нс), пока данные появятся на шине. После чтения данных, CS можно перевести в низкий уровень.

Операция записи происходит по спаду. Данные на шине могут быть выставлены после фронта по линии CS, но минимум за 80нс до спада, и должны удерживаться после спада в течение, как минимум, 10 нс.

Для операций записи и команд можно подавать одновременно импульсы на CS1 и CS2, в результате чего оба чипа будут одновременно выполнять одни и те же команды, и принимать одни и те же данные. Таким образом, можно ускорить инициализацию дисплея, его очистку или заполнение.


Судя по описанию, для моделей WG12232C стробирующими являются импульсы R/W̅, или E, и значения на линиях CS должны быть выставлены заранее.


7. CL

Для моделей WG12232A этот вход не используется, в моделях WG12232C (судя по описанию) на этот вход должен быть подан меандр с частотой 2000Гц.


8. E

Для моделей WG12232A этот вход не используется. В моделях WG12232C (судя по описанию) импульс на этом входе является стробирующим для операций чтения (контроллеры типа 80, для операций записи строб передаётся по входу R/W̅), либо стробирующим импульсом для всех операций (контроллеры типа 68). Рекомендую сажать его через подтягивающий резистор 100-500кОм на «землю», см. примечание ниже.


9. R/W̅, Выбор операции чтения

Для моделей WG12232A уровень сигнала на этом входе определяет проводимую операцию. Высокий уровень подразумевает операцию чтения, и разрешает контроллеру дисплея выставлять напряжение на линии данных. В зависимости от уровня сигнала на входе A0, после подачи одного из сигналов CS, на линии данных появятся либо информация о текущем состоянии чипа, либо данные из буфера, помещённые туда из видеопамяти предыдущей операцией чтения.

Низкий уровень запрещает контроллерам дисплея выставлять данные на линию и соответствует операциям команд или записи, в зависимости от уровня сигнала на входе A0.

Когда этот вход не подключен, контроллеры дисплея могут принимать шум на нём и хаотично выставлять уровни на шине данных. При работе с микроконтроллерами AVR, такое состояние может появиться, например, при программировании контроллера по последовательному интерфейсу. Если шина данных подключена на порт, использующий те же контакты, что и последовательный интерфейс, то появляющиеся на ней хаотичные сигналы могут отрицательно влиять на процесс программирования. Для избегания этого, рекомендую подключить подтягивающий резистор (например, 100-500 килоОм) между этим выводом и «землёй». Впрочем, рекомендую использовать подобные подтягивающие резисторы на всех управляющих линиях.


Для моделей WG12232C с контроллером типа 80 (судя по описаниям) импульс на этом входе является стробирующим для операций чтения. С контроллером типа 68 уровень на этом входе также выбирает операцию чтения и записи и должен быть выставлен заранее, в то время как строб передаётся по входу E.


10-17. Шина данных

В зависимости от сигнала R/W̅ данные по шине либо передаются к контроллерам дисплея, либо выставляются ими.

При работе с контроллерами AVR, для избегания появления токов утечки рекомендую:

1. Кроме операций записи, удерживать соответствующий порт сконфигурированным, как вход (DDRx = 0x00)

2. Перед конфигурированием порта на вывод (DDRx = 0xFF) Убедиться, что линия R/W̅ сконфигурирована на вывод и на ней выставлен низкий уровень.

3. После завершения операции записи, сконфигурировать порт обратно на вход (DDRx = 0x00)

18. R̅E̅S̅, Сброс

Низкий уровень сигнала на этом входе вызывает аппаратный сброс контроллеров дисплея. В случае, если этот вход не подключен, контроллеры дисплея могут улавливать шумы и хаотично переходить в режим сброса. Контроллеры дисплея имеют функцию программного сброса, поэтому управлять этой линией нет никакой необходимости. Её можно просто подключить через подтягивающий резистор (50-500 килоОм) к линии питания (Vdd).

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


19. Vee/A, Генератор -5В/Анод подсветки

У дисплеев WG12232x-xxx-V есть встроенный генератор отрицательного напряжения, и на этот контакт выводится полученное напряжение -5 Вольт. Оно может использоваться для питания ЖК матрицы (см. Vo).

Для моделей WG12232x-xxx-N с подсветкой, у которых такой генератор отсутствует, этот контакт ведёт на анод подсветки.

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


20. K, Катод подсветки

У WG12232x-xxx-V этот контакт не используется. У WG12232x-xxx-N с подсветкой, он ведёт на катод подсветки.


Подсветка

Для моделей с подсветкой, светодиодная подсветка работает от 4,2 Вольт и потребляет около 120 миллиАмпер (т.е. мощность составляет 0,5 Ватта). Подключить светодиодную подсветку можно к линии питания 5В, через резистор 6,7 Ом (резистор должен быть рассчитан на соответствующую мощность). Сойдёт и резистор 10 Ом, при этом дисплей будет светить в 2/3 яркости, ток составит около 80 миллиАмпер, а мощность около 0,33Вт.

Для подключения подсветки на плате дисплея используются два дополнительных контакта, находящиеся справа от дисплея (A – анод, K – катод). Либо, для моделей без встроенного генератора отрицательного напряжения, контакты 19 и 20.


Взаимодействие с дисплеем

Алгоритм взаимодействия

WG12232A на базе чипов SED1520

Последовательность действий следующая:

1. Установить на линии A0 высокий уровень для операции чтения/записи в видеопамять, либо низкий уровень для операции получения статуса/вывода команды.

2. Установить на линии R/W̅ высокий уровень для операций чтения (получение статуса и чтение видеопамяти), либо низкий уровень для операций записи (запись в видеопамять, вывод команды).

3. Выждать минимум tAW6 (20 нс) и вывести на линию CS1 или CS2, в зависимости от того, какому из двух чипов предназначается команда, высокий уровень (фронт стробирующего импульса).

4. Для операций записи/вывода команды: выставить на шине данных выводимый байт. Выставлять данные для записи можно и до фронта строба (п.3).

5. Для операций чтения/получения статуса: выждать минимум tACC6 (90 нс) после фронта строба (п.3) и прочитать логические уровни на линиях шины данных.

6. Выждать после фронта строба (п.3) минимум tEW (100 нс для операций чтения/получения статуса, 80 нс для операций записи/вывода команды), и, для операций записи/вывода команды, как минимум tDS6 (60 нс) после выставления уровней на шине данных (п.4), после чего установить на линии CS1/CS2 низкий уровень.

7. Следующую команду начинать не ранее tCYC6 (1 мс) после начала этой.


WG12232C на базе чипов SBN1661G

Последовательность для WG12232C не проверялась на практике.

Инструкция описывает 2 варианта последовательности действий, в зависимости от того какой тип контроллера используется.

Для контроллеров типа 68:

1. Выставить на линиях A0, R/W̅ уровни соответствующие выводимой команде.

2. Для операций записи выставить на шине данных выводимый байт.

3. Выставить уровни на линиях CS1/CS2 (в зависимости от того, какому чипу предназначается команда).

4. Выждать минимум 20 нс и выдать фронт строба на линию E.

5. Для операций чтения выждать минимум 90 нс и прочитать байт с шины данных.

6. Выждать 100 нс после фронта строба (п.4.) и выдать срез строба (низкий уровень) на линию E.

7. Время между командами 1 мс.

Для контролеров типа 80 (на линиях E и R/W̅ по умолчанию должен быть высокий уровень):

1. Выставить на линиях A0 уровни соответствующие выводимой команде (низкий – чтение статуса/вывод команды, высокий – чтение/запись видеопамяти).

2. Выставить уровни на линиях CS1/CS2 (в зависимости от того, какому чипу предназначается команда).

Для операций записи/вывода команды:

3. Выставить на шине данных выводимый байт.

4. Выждать 20 нс, на линии R/W̅ выставить низкий уровень (при этом на линии E должен оставаться высокий уровень).

5. Выждать 80 нс, вернуть на линии R/W̅ высокий уровень

Для операций чтения/получения статуса:

3. Выждать 20 нс, на линии E выставить низкий уровень (при этом на линии R/W̅ должен оставаться высокий уровень).

4. Выждать 90 нс, прочитать байт с шины данных

5. Выждать 10 нс, вернуть на линии E низкий уровень.

Время между операциями – 1 мс.


Тайминги

Хотя в спецификации на дисплей, который мне достался, написано что он работает и при 2,7 Вольтах, а спецификация на тайминги дана для случая напряжения питания 5В и 2,7В, на деле оказалось что дисплей не захотел запускаться при напряжении 3,3В. В любом случае, рекомендую ориентироваться на тайминги, данные в таблице для напряжения 2,7В. Рядом приводится расчёт для числа тактов микроконтроллеров, в зависимости от частоты их работы, для некоторых значений частот.



КодОперацияВремя, нсТакты (округлено вверх)
4MHz8MHz12MHz16MHz20MHz
При напряжении питания 5В
tCYC6длительность цикла, между срезами стробов двух соседних операций100048121620
tAW6между установкой сигналов на линиях R/W̅ и A0 до стробирующего импульса2011111
tAH6удержание уровня на линии A0 после среза строба1011111
tDS6между установкой записываемой информации на шине данных и срезом строба8011122
tDH6удержание информации на шине данных после среза строба1011111
tACC6появление данных на шине при операциях чтения, после фронта строба, макс.901 (1)1 (2)2 (2)2(2)2(3)
tOH6время до освобождения шины данных при операциях чтения, после среза строба, макс.6011112
tEW, чтениедлительность стробирующего импульса при операциях чтения10011222
tEW, записьдлительность стробирующего импульса при операциях записи8011122
При напряжении питания 2,7В
КодОперацияВремя, нсТакты (округлено вверх)
4MHz8MHz12MHz16MHz20MHz
tCYC6длительность цикла, между срезами стробов двух соседних операций2000816243240
tAW6между установкой сигналов на линиях R/W̅ и A0 до стробирующего импульса4011111
tAH6удержание уровня на линии A0 после среза строба2011111
tDS6между установкой записываемой информации на шине данных и срезом строба16012234
tDH6удержание информации на шине данных после среза строба2011111
tACC6появление данных на шине при операциях чтения, после фронта строба, макс.1801 (2)2 (2)3 (3)3(4)4(5)
tOH6время до освобождения шины данных при операциях чтения, после среза строба, макс.12011223
tEW, чтениедлительность стробирующего импульса при операциях чтения20012344
tEW, записьдлительность стробирующего импульса при операциях записи16012234

При этом время нарастания (tr) и спада (tf) стробирующего импульса не должно превышать 15нс.

Для всех операций, кроме действий, подразумевающих ответ устройства (tACC6, tOH6), в таблице указано минимальное время, которое необходимо выжидать между посылами управляющих сигналов. Для операции чтения (tACC6) указано время, которое необходимо выждать, прежде чем можно будет читать значение на шине данных. При этом максимальное время между этими сигналами не ограничено, и цикл может продолжаться сколь угодно долго.


При работе с микроконтроллерами AVR следует учитывать следующие особенности:

- Сама операция записи данных в порт занимает минимум такт, то есть следующая операция записи будет выполняться спустя минимум такт, а значит, нет необходимости добавлять дополнительную задержку, например, между установкой данных на линиях R/W̅, A0 и фронтом стробирующего импульса.

Появление сигнала на линии по команде OUT, и доступность ответа устройства по команде IN в микроконтроллерах AVR
Появление сигнала на линии по команде OUT, и доступность ответа устройства по команде IN в микроконтроллерах AVR

- Операция чтения из порта (PINx) выполняется по фронту импульса системного тактового генератора, в то время как значение сигнала на входе заносится в буфер по срезу. Как следствие, задержка между появлением сигнала на линии и его приёмом может составить от 0,5 до 1,5 системных тактов. Кроме того, следует учесть, что вывод данных в порт осуществляется по завершении такта, а чтение из порта возвращает состояние входа на момент начала такта. Таким образом, всегда между завершением команды записи в порт сигнала строба и началом команды чтения шины данных должен быть хотя бы один дополнительный такт. В таблице в скобках для времени tACC6 приведено число тактов, которые должны пройти между этими двумя операциями, с учётом изложенных особенностей.


Пример кода

Примеры кода даны на языке C.

Предполагается, что макросы DATA_PORT, A0_PORT, RW_PORT, CS1_PORT, CS2_PORT определяют соответствующие регистры PORTx ввода вывода для данных и соответствующих управляющих сигналов; DATA_DDR – регистр DDRx, определяющий направление порта данных; DATA_PIN – регистр PINx, определяющий уровни сигналов на порту данных; A0_PIN_NUM, RW_PIN_NUM, CS1_PIN_NUM, CS2_PIN_NUM – номер ввода на порту для соответствующего сигнала:

// Пример конфигурации: порт шины данных – B
// сигналы A0, RW, CS1, CS2 – на первых 4х выводах порта A
#define DATA_DDR DDRB
#define DATA_PIN PINB
#define DATA_PORT PORTB
#define A0_PORT PORTA
#define RW_PORT PORTA
#define CS1_PORT PORTA
#define CS2_PORT PORTA
#define A0_PIN_NUM 0
#define RW_PIN_NUM 1
#define CS1_PIN_NUM 2
#define CS2_PIN_NUM 3// Первые четыре вывода порта A конфигурируются на выход
// устанавливается низкий уровень
PORTA &= ~0b00001111; 
DDRA |= 0b00001111;

Предполагается что порт (или порты) соответствующие выводам A0, RW, CS1, CS2 настроены на вывод.

Предполагается, что код исполняется на частоте 16 Мегагерц, а тайминги дисплея соответствуют напряжению питания 5 Вольт.

// Получение данных из дисплея
// a0 - 0, для чтения статуса, 1 - для чтения графической информации
// chip - 1 - для первого чипа, 2 - для второго чипа
uint8_t display_in(uint8_t a0, uint8_t chip) {
  DATA_DDR = 0x00; // Настраиваем шину данных на приём
  DATA_PORT = 0x00; // Убеждаемся, что все pull-up резисторы на нём отключены
  if (a0) {
    A0_PORT |= 1 << A0_PIN_NUM; // Устанавливаем сигнал A0
  } else {
    A0_PORT &= ~(1 << A0_PIN_NUM); // Убираем сигнал A0
  }
  RW_PORT |= 1 << RW_PIN_NUM; // Устанавливаем сигнал RW,
  // разрешая контроллерам дисплея выставлять уровни на шине данных
  if (chip == 1) { // Если выбран первый чип, устанавливаем строб на нём
    CS1_PORT |= 1 << CS1_PIN_NUM;
  } else { // Иначе строб второго чипа
    CS2_PORT |= 1 << CS2_PIN_NUM;
  }
  asm volatile ("nop");
  asm volatile ("nop"); 
  asm volatile ("nop"); // Достаточно двух тактов. Третий - запасной
  uint8_t result = DATA_PIN; // Читаем ответ с шины данных
  CS1_PORT &= ~(1 << CS1_PIN_NUM); // Для простоты принудительно убираем строб
  CS2_PORT &= ~(1 << CS2_PIN_NUM); // на обоих чипах, опуская условный оператор
  RW_PORT &= ~(1 << RW_PIN_NUM); // Сбрасываем сигнал RW, запрещая дисплею выводить что-либо на шину данных.
  return result; // Возвращаем прочитанные данные
}

// Передача данных к дисплею
// a0 - 0, для записи команды, 1 - для записи графической информации
// chip - 1 - для первого чипа, 2 - для второго чипа, 3 - для обоих чипов вместе
void display_out(uint8_t a0, uint8_t data, uint8_t chip) {
  RW_PORT &= ~(1 << RW_PIN_NUM); // Сбрасываем сигнал RW
  if (a0) {
    A0_PORT |= 1 << A0_PIN_NUM; // Устанавливаем сигнал A0
  } else {
    A0_PORT &= ~(1 << A0_PIN_NUM); // Убираем сигнал A0
  }
  DATA_DDR = 0xFF; // Настраиваем шину данных на передачу
  DATA_PORT = data; // Выставляем данные на шине данных;
  // разрешая контроллерам дисплея выставлять уровни на шине данных
  if (chip & 1) { // Если выбран первый чип, устанавливаем строб на нём
    CS1_PORT |= 1 << CS1_PIN_NUM;
  }
  if (chip & 2) { // Если выбран второй чип, устанавливаем строб на нём
    CS2_PORT |= 1 << CS2_PIN_NUM;
  }
  asm volatile ("nop"); // Ждём два такта
  asm volatile ("nop");
  CS1_PORT &= ~(1 << CS1_PIN_NUM); // Для простоты принудительно убираем строб
  CS2_PORT &= ~(1 << CS2_PIN_NUM); // на обоих чипах, опуская условный оператор
  DATA_PORT = 0x00; // Снимаем сигналы с шины данных
  DATA_DDR = 0x00; // Переводим порт на приём, чтобы избегать токов утечки
}

// Получение состояния
// chip - 1 - для первого чипа, 2 - для второго чипа
inline uint8_t display_status(uint8_t chip) {
  return display_in(0, chip);
}

// Чтение графической информации
// chip - 1 - для первого чипа, 2 - для второго чипа
inline uint8_t display_read(uint8_t chip) {
  return display_in(1, chip);
}

// Комманда контроллерам дисплея
// chip - 1 - для первого чипа, 2 - для второго чипа, 3 - для обоих чипов вместе
inline void display_command(uint8_t command, uint8_t chip) {
  display_out(0, command, chip);
}

// Запись данных в графическую память дисплея
// chip - 1 - для первого чипа, 2 - для второго чипа, 3 - для обоих чипов вместе
inline void display_write(uint8_t command, uint8_t chip) {
display_out(1, command, chip);
}

Чтение состояния дисплея

При выполнении команды чтения состояния дисплея (A0 = 0, R/W̅ = 1) на шине данных сразу же появляется информация о текущем состоянии и настройках дисплея:

ВыводКодЗначение
DB0-DB3-Всегда низкий уровень (нули)
DB4ResetВысокий уровень означает что контроллер дисплея выполняет функцию сброса. Низкий уровень означает нормальный режим работы.
DB5ON/OFFВысокий уровень означает что контроллер отключен и не управляет своей частью дисплея, низкий – что включен.
DB6ADCВысокий уровень означает нормальное направление вывода, низкий – обратное (см. раздел «Устройство»)
DB7BusyВысокий уровень устанавливается, когда контроллер дисплея занят выполнением команды, низкий – когда готов к приёму новых команд.

Поскольку контроллеры дисплея выполняют команды очень быстро, и спустя время tCYC6 они готовы к выполнению следующей команды, в чтении флага Busy при выполнении обычных команд нет практического смысла. Однако, чтение статуса может использоваться для ожидания готовности дисплея после включения или программного сброса.


Пример:

// Цикл пока первый чип находится в состоянии busy, или reset
while (display_status(1) & 0b10010000);
// Цикл пока второй чип находится в состоянии busy, или reset
while (display_status(2) & 0b10010000);

Команды дисплея


Команды, которые передаются при логических уровнях A0 = 0, R/W̅ = 0.

ДвоичнаяHEXОписание
111000000xE0Включение режима «Чтение-Изменение-Запись»
111011100xEEОкончание работы в режиме «Чтение-Изменение-Запись»
111000100xE2Программный сброс
101011100xAEВыключение дисплея
101011110xAFВключение дисплея
101110xx0xB8+pageВыбор страницы (строки) от 0 до 3
110xxxxx0xC0+rollВыбор индекса верхней строки, от 0 до 31
0xxxxxxx0x00+colВыбор горизонтальной позиции от 0 до 79
101000000xA0Выбор прямого режима нумерации колонок – слева направо (см. раздел «Устройство»).
101000010xA1Выбор обратного режима нумерации колонок – справа налево (см. раздел «Устройство»).
101001000xA4Выключение режима static drive
101001010xA5Включение режима static drive
101010000xA8Выбор скважности драйвера LCD 16
101010010xA9Выбор скважности драйвера LCD 32

Режим «Чтение-Изменение-Запись»

Режим предназначен для изменения некоторого участка на дисплее. Режим включается выбором команды 0xE0. В этом режиме операция чтения не увеличивает номер текущей колонки, увеличение происходит только после вызова операции записи. Однако по-прежнему, чтение происходит через промежуточный буфер, который сразу после операции записи содержит мусор, поэтому для получения актуальных данных требуется одно холостое чтение. Пока этот режим активен, команды выбора колонки и страницы не действуют. Отключается режим передачей команды 0xEE. При включении режима текущая позиция запоминается в буфере, и после его отключения – восстанавливается.


Пример:

display_command(0xB8, 1); // Выбор страницы 0 на первом чипе
display_command(0x00, 1); // Выбор колонки 0 на первом чипе
// "Мигание курсора"
while(1) {
  display_command(0xE0, 1); // Включение режима «Чтение-Изменение-Запись»
  for (uint8_t x = 0; x < 8; x++) {
    display_read(1); // Холостое чтение
    uint8_t data = display_read(1); // Получение информации из очередной колонки
    display_write(~data, 1); // Запись инвертированного значния, после чего номер позиции увеличивается
  }
display_command(0xEE, 1); // Отключение режима «Чтение-Изменение-Запись», 
// после чего восстанавливается начальная позиция
  _delay_ms(1000); 
}

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


Выбор индекса верхней строки

Дисплей может «прокручивать» изображение вертикально. При этом строки, выходящие за верх дисплея, будут отображаться снизу. Команда 0xC0+roll, где roll в пределах от 0 до 31, выбирает индекс верхней отображающейся строки.


Static Drive

Включение режима работы со статичными LCD-дисплеями. Для данной модели должен оставаться отключенным. Команда для отключения 0xA4.


Скважность

Контроллер предназначен для работы с различными дисплеями. Для данного дисплея скважность должна быть установлена 32, командой 0xA9


Вывод данных на дисплей

Для вывода данных выходы конфигурируются следующим образом A0 = 1, R/W̅ = 0, на шину данных выставляются значения 8ми пикселей в выводимой колонке. Сразу после завершения команды информация на дисплее в выбранной позиции и строке (см. команды дисплея) обновляется, а номер текущей колонки автоматически увеличивается на один (если только он не достиг 80, после чего номер колонки уже не увеличивается)


Пример:

display_command(0xB8, 1); // Выбор страницы 0 на первом чипе
display_command(0x00, 1); // Выбор колонки 0 на первом чипе
display_write(0b00111100, 1); // Вывод графики
display_write(0b01010010, 1);
display_write(0b10100101, 1);
display_write(0b10100001, 1);
display_write(0b10100001, 1);
display_write(0b10100101, 1);
display_write(0b01010010, 1);
display_write(0b00111100, 1);

Получение данных из памяти дисплея

Чтение данных с дисплея представляет некоторую сложность. Дело в том что информация, появляющаяся на шине данных, отражает не содержимое видеопамяти в текущей позиции, а содержимое внутреннего буфера. После чего в буфер заносится содержимое текущей ячейки, а номер колонки (если он меньше 80 и если не выбран режим «Чтение-Изменение-Запись») увеличивается на 1. Таким образом, операция чтения всегда запаздывает на одну позицию.

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

Для чтения данных выходы конфигурируются следующим образом A0 = 1, R/W̅ = 1.


Несмотря на то, что информация, возвращаемая командой чтения, запаздывает на одну позицию, номер текущей позиции (если он меньше 80 и если не выбран режим «Чтение-Изменение-Запись») увеличивается при каждом вызове команды. Таким образом, если сразу за командой чтения следует команда записи, её вывод будет осуществлён в позиции с индексом на два больше чем та, чьё содержимое было возвращено командой чтения. При этом следующая команда чтения вернёт мусор. Операция записи также увеличит номер текущей колонки и очередная (вторая) команда чтения после записи вернёт уже содержимое ячейки, следующей за записываемой.

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


Пример:

display_command(0xB8 + 1, 2); // Выбор страницы 1 на втором чипе
display_command(0x00 + 5, 2); // Выбор колонки 5 на втором чипе
display_read(2); // Холостое чтение
uint8_t data = display_read(2); // Получение данных из колонки 5, позиции 1

Примеры кода

// Константы
//#define DISPLAY_UPSIDE_DOWN // режим перевёрнутого дисплея
#define CMD_ENABLE 0xAF
#define CMD_DISABLE 0xAE
#define CMD_START_LINE 0xC0
#define CMD_PAGE 0xB8
#define CMD_COLUMN 0x00
#define CMD_ADC_CW 0xA0
#define CMD_ADC_CCW 0xA1
#define CMD_DUTY_CYCLE_32 0xA9
#define CMD_STATIC_DRIVE_OFF 0xA4
#define CMD_STATIC_DRIVE_ON 0xA5
#define CMD_RMW_START 0xE0
#define CMD_RMW_END 0xEE
#define CMD_RESET 0xE2

// Очистка дисплея
void display_clear() {
  // Одновременно оба чипа
  for (uint8_t page = 0; page < 4; page++) {
    display_command(CMD_PAGE + page, 3);
    #ifdef DISPLAY_UPSIDE_DOWN
      display_command(CMD_COLUMN + 19, 3);
    #else
      display_command(CMD_COLUMN + 0, 3);
    #endif
    for (uint8_t x = 0; x < 61; x++) {
      display_write(0, 3);
    }
  }
}

// Сброс, инициализация и очистка дисплея
void display_init() {
  display_command(CMD_RESET, 3); // Сброс
  for (uint8_t i = 255; i != 0; i--) { // Ожидание завершения сброса
    // Цикл ограничен, чтобы случайно не впать в бесконечный цикл
    if (((display_status(1) & 0x90) == 0) && ((display_status(2) & 0x90) == 0))
      break;
    _delay_ms(1);
  }
  display_command(CMD_DUTY_CYCLE_32, 3); // широта импульса 1/32
  display_command(CMD_STATIC_DRIVE_OFF, 3); // отключение режима static_drive
  display_command(CMD_ENABLE, 3); // Включение
  display_command(CMD_START_LINE + 0, 3); // Прокрутка дисплея - 0
  #ifdef DISPLAY_UPSIDE_DOWN
  display_command(CMD_ADC_CCW, 3); // Режим CCW (для перевёрнутого дисплея)
  #else
  display_command(CMD_ADC_CW, 3); // Режим CW
  #endif
  display_clear();
}

// Устанавливает пиксель на заданной позиции, с учётом ориентации дисплея
// x - позиция от левого края экрана
// y - позиция от верхнего края экрана
// set - 1 - закрасить пиксель, 0 - стереть пиксель
void set_pixel(uint8_t x, uint8_t y, uint8_t set) {
  if ((y >= 32) || (x >= 122))
    return;
  uint8_t cx;
  uint8_t chip;
  uint8_t mask;
  #ifdef DISPLAY_UPSIDE_DOWN
    if (x >= 61) {
      chip = 1;
      cx = x + (19 - 61);
    } else {
      chip = 2;
      cx = x + 19;
    }
    display_command(CMD_PAGE + 3 - (y >> 3), chip);
    mask = 0x80 >> (y & 7);
  #else
    if (x >= 61) {
      chip = 2;
      cx = x - 61;
    } else {
      chip = 1;
      cx = x;
    }
    display_command(CMD_PAGE + (y >> 3), chip);
    mask = 0x01 << (y & 7);
  #endif
  display_command(CMD_COLUMN + cx, chip);
  display_read(chip);
  uint8_t data = display_read(chip);
  uint8_t newData = (set) ? (data | mask) : (data & ~mask);
  if (data != newData) {
    display_command(CMD_COLUMN + cx, chip);
    display_write(newData, chip);
  } 
}

Проект для AtmelStudio 6

Исходный код проекта доступен для скачивания здесь: zip-файл, 80 кБ.


Проект написан на языке C и реализован для микроконтроллеров ATmega32, но может быть легко адаптирован для других AVR микроконтроллеров с флеш-памятью 32 кБ и более. Схема подключения такая же как на рисунке выше.

Архив также включает скомпилированный код для микроконтроллера ATmega32, в форматах hex и elf.


Проект включает в себя модуль для работы с дисплеем WG12232A, вывод простейшей графики и модуль для вывода текстовой информации, включающий в себя также три шрифта.


Помещённый здесь код, включая шрифты, является свободным. То есть, допускается его свободное использование для любых целей, включая коммерческие, при условии указания ссылки на автора (Погребняк Дмитрий, http://aterlux.ru/).




5 ms; mod: Sat, 06 May 2017 20:41:51 GMT; gen: Tue, 24 Oct 2017 05:35:22 GMT