Программирование дисплея на контроллере ST7920

  • Tutorial
Здравствуйте, я хочу рассказать о программировании дисплея на контроллере ST7920 с использованием ATtiny2313 контроллера.



Характеристики дисплея


Приблизительная цена: 15$
Размер дисплее вместе с платой: 93.0 (Длина) × 70.0(Ширина) × 13.50(Высота) мм
Размер видимой области: 70x38 мм.

Дисплей имеет 2 режима работы:
  • Графический
  • Текстовый

В текстовом режиме дисплей имеет 4 строки и 16 знакомест на строку
В графическом режиме разрешение: 128x64 пикселя.

3 режима подключения:
  1. Подключение по 8 битной шине
  2. Подключение по 4 битной шине
  3. Подключения по SPI (3 битной шине)


Имеются 2 режима работы:
  • Нормальный: потребление 450 мкА, 5 В
  • Спящий режим: потребление 30 мкА, 5 В


В данном посте я расскажу о:
  • Работе в текстовом режиме
  • Подключения и программирование по 8 битной шине
  • Подключения и программирование по SPI

Для того что бы подключить дисплей к контроллеру нам понадобится:
  1. Дисплей на контроллере ST7920
  2. 2 подстрочных резистора на 10 кОм.
  3. Для 8 битного режима резистор на 4.7 кОм (или больше)
  4. Контроллер ATtiny2313
  5. Источник питание на 5В.


Схема подключения


Подключение по 8 битной шине данных


Распиновка контактов:
GND — Земля
VCC — +5В
V0 — Настройка контрастности
RS — Определяет режим передачи данных (1 — это данные, 0 — это команда)
RW — Запись или чтения (1 — чтения, 0 — запись)
E — Строб
D0-D7 — Шина данных
PSB — Определяет какой протокол передачи данных будет использоваться ( 1 — 8/4 бит шина, 0 — SPI)
BLA — Анод подсветки (+)
BLK — Катод подсветки (-)

На схеме DB0-DB7 и PB0-PB7 не замкнуты, это 8 битная шина данных.
Реальное соединения таково:
DB0 — PB0
DB1 — PB1
DB2 — PB2
DB3 — PB3
DB4 — PB4
DB5 — PB5
DB6 — PB6
DB7 — PB7

Подключение по SPI


Распиновка контактов:
GND — Земля
VCC — +5В
V0 — Настройка контрастности
RS — (CS) Начало/окончание передачи данных (1 — начало, 0 — окончание)
RW — (SID) Шина данных
E — (SCLK) Строб
PSB — Определяет какой протокол передачи данных будет использоваться ( 1 — 8/4 бит шина, 0 — SPI)
BLA — Анод подсветки (+)
BLK — Катод подсветки (-)

Подстроечные резисторы

RP1 — Регулятор контрастности
RP2 — Регулятор яркости

Описание протоколов программирования дисплея


8 битный режим

И так, с начало я расскажу о том как в общих чертах происходит работа с дисплеем.
Для того что бы работать с дисплеем нам нужно отправлять команды и данные на дисплей.
К командам относится: Включения/выключение дисплея, отображение курсора, перемещение курсора и т.д. К данным относятся например символы которые вы хотите видеть на дисплее.

Давайте рассмотрим пример того как производится инициализация для 8 битного режима.
Давайте рассмотрим пример того как производится инициализация:
  • Задержка в 50 мкс.
  • Отправляем команду установки 8 битного режима.
  • Задержка 120 мкс.
  • Отправляем команду включения дисплея ( в ней же указывается, включить ли курсор, и мигать ли курсором)
  • Задержка в 50 мкс.
  • Повторно отправляем функцию установки 8 битного режима
  • Задержка 120 мкс.
  • Отправляем команду отчистить экран
  • Задержка 20 мкс.
  • Устанавливаем ENTRY MODE (эта команда говорит о том в какую сторону сдвигать курсор после написания символа, нам соответственно нужно вправо)


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

Рассмотрим как отправить одну команду на дисплей в 8 битном режиме:
  • Устанавливаем низкий уровень E
  • Устанавливаем низкий уровень RS
  • Устанавливаем низкий уровень RW
  • Задержка 1 мкс.
  • Устанавливаем высокий уровень E
  • Отправляем в порт данных байт команды
  • Задержка 1 мкс.
  • Устанавливаем низкий уровень E
  • Задержка 50 мкс.


Для отправки одного байта данных выполняется абсолютно то же самое, только в начале устанавливается высокий
уровень RS.
RS = 0 Команда
RS = 1 Данные

Вот как отправляется один байт данных:
  • Устанавливаем низкий уровень E
  • Устанавливаем высокий уровень RS
  • Устанавливаем низкий уровень RW
  • Задержка 1 мкс.
  • Устанавливаем высокий уровень E
  • Отправляем в порт данных байт команды
  • Задержка 1 мкс.
  • Устанавливаем низкий уровень E
  • Задержка 50 мкс.


Давайте рассмотрим код отправки команды
Для начала установим константы что бы было удобнее:

.equ PCom = PORTD ; Управляющий порт к которому подключены RS, RW, E
.equ PW = PORTB   ; Порт данных 
.equ RS = 2       ; Контакт порта PCom к которому подключен RS
.equ E = 0          ; Контакт порта PCom к которому подключен E
.equ RW = 1       ; Контакт порта PCom к которому подключен RW
.def Data = R18   ; Регистр используется для записи данных в порт


Функция отправки команды:
;Перед вызовом функции в регистр Data нужно установить необходимую команду
LCD12864_CommandOut:                ;Вывод команды на дисплей.
       cbi  PCom,  E                ; Устанавливаем  низкий уровень E
       cbi  PCom,  RS               ; Устанавливаем низкий  уровень RS
       cbi  PCom,  RW		  ; Устанавливаем низкий уровень RW
       LCD8_MACRO_DELAY 1, 1	  ; Задержка 1 мкс.
       sbi  PCom,  E                ; Устанавливаем высокий  уровень E
       out  PW,    Data             ; Отправляем в порт данных байт команды 
       LCD8_MACRO_DELAY 1, 1	  ; Задержка 1 мкс.
       cbi  PCom,  E                ;  Устанавливаем низкий уровень E
       LCD8_MACRO_DELAY 1, 50	  ; Задержка 50 мкс.
       Ret


Функция отправки данных:
;Перед вызовом функции в регистр Data нужно установить необходимую команду
LCD12864_DataOut:                ;Вывод данных на дисплей.
       sbi  PCom,  E                ; Устанавливаем  высокий уровень E
       cbi  PCom,  RS               ; Устанавливаем низкий  уровень RS
       cbi  PCom,  RW		  ; Устанавливаем низкий уровень RW
       LCD8_MACRO_DELAY 1, 1	  ; Задержка 1 мкс.
       sbi  PCom,  E                ; Устанавливаем высокий  уровень E
       out  PW,    Data             ; Отправляем в порт данных байт команды 
       LCD8_MACRO_DELAY 1, 1	  ; Задержка 1 мкс.
       cbi  PCom,  E                ;  Устанавливаем низкий уровень E
       LCD8_MACRO_DELAY 1, 50	  ; Задержка 50 мкс.
       Ret


В коде был использован макрос LCD8_MACRO_DELAY, вот его код

; Пример макроса функции задержки в микросекундах
.MACRO LCD8_MACRO_DELAY ; 1 параметр количество задержек, 2 параметр, количество микросекунд в задержки
       ldi  Temp,  @0               
       ldi  Temp1, @1
     rcall  LCD12864_Delay
.ENDM

;Пример функции задержки для контроллера на 4 МГц.
;Функция имеет 2 параметра:
;R16 – Количество микросекунд
;R17 – Количество циклов по R16 микросекунд.
LCD12864_Delay:
	push  R16     ;Сохраняем младшую задержку в ОЗУ.
ES0:
	dec  R16       ;- задержка.
	cpi  R16,  0   ;Закончилась?
    brne  ES0      ;Нет - еще раз.
	
	pop  R16      ;Да? Восстановить задержку.
	dec  R17      ;Отнять от "количества задержек" разряда.
	cpi  R17, 0   ;Количество задержек = 0?
    brne  LCD12864_Delay               
    ret


Теперь рассмотрим команды инициализации дисплея в текстовом, 8 битном режиме:
Команда FUNCTION SET: 0 0 1 DL 0 RE 0 0
DL:
  • Если установлено 1 то устанавливаем 8 бит передачу данных
  • Если установлено 0 то устанавливается 4 бита передача данных


RE:
  • Если установлено 1 то устанавливается набор расширенных команд
  • Если установлено 0 то устанавливается набор базовых команд


Следующая команда это DISPLAY STATUS: 0 0 0 0 1 D C B
D:
  • Если установлено 1 то дисплей включен
  • Если установлено 0 то дисплей выключен

С:
  • Если установлено 1 то курсор включен
  • Если установлено 0 то курсор выключен

B:
  • Если установлено 1 то курсор будет мигать
  • Если установлено 0 то курсор не будет мигать


Следующая команда простая CLEAR – отчистка экрана: 0 0 0 0 0 0 0 1


И последняя команда это ENTRY MODE SET – установка направления движения курсора: 0 0 0 0 0 1 I/D S
  • Если I/D = 1 то курсор сдвигается вправо
  • Если I/D = 0 то курсор сдвигается влево


На основе этих четырех команд можно написать функцию инициализации дисплея:
LCD12864_Init:                      ;Инициализация дисплея.
	 LCD8_MACRO_DELAY 1, 50	; Задержка в 50 мкс.
	 Ldi Data, 0b00110000
	 rcall LCD12864_CommandOut  ; Отправляем команду установки 8 битного режима.
	 LCD8_MACRO_DELAY 1, 120    ; Задержка в 120 мкс.
	 Ldi Data, 0b00001111
	 rcall LCD12864_CommandOut ; Отправляем команду включения дисплея, включить курсор, мигать курсором
         LCD8_MACRO_DELAY 1, 50    ; Задержка в 50 мкс.
	 Ldi Data, 0b00110000
	 rcall LCD12864_CommandOut  ; Отправляем команду установки 8 битного режима.
	 LCD8_MACRO_DELAY 1, 120    ; Задержка в 120 мкс.
	 Ldi Data, 0b00000001
	 rcall LCD12864_CommandOut  ; Отправляем команду отчистить экран
         LCD8_MACRO_DELAY 1, 20      ; Задержка в 20 мкс.
	 Ldi Data, 0b00000110
	 rcall LCD12864_CommandOut   ; установка направления движения курсора вправо
	 LCD8_MACRO_DELAY 1, 50      ; Задержка в 50 мкс.
    ret


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

Режим SPI

Теперь о функции приема передачи команды/данных по SPI.
В этом режиме участвуют 2 линии:
  • SID это контакт передачи данных, на дисплее он же RW
  • SCLK – это линия строб, на дисплее он же E
  • CS – это начала/окончания передачи данных, на дисплее он же RS


В SPI режиме передача одной команды или 1 байта данных происходит при передачи 24 бит
Протокол передачи данных таков:
  • Устанавливаем высокий уровень CS
  • Передаем 4 единицы подряд
  • Передаем 1 бит RW – чтения или запись
  • Передаем 1 бит RS – Команда или данные
  • Передаем 0
  • Передаем 4 бита старшей половины байта данных
  • Передаем 4 нуля
  • Передаем 4 бита младшей половины байта данных
  • Передаем 4 нуля подряд
  • Устанавливаем низкий уровень CS


На этом передача одного байта завершена.

После каждого переданного бита делается строб, то есть:
  • Задержка 1 мкс.
  • Устанавливаем высокий уровень SCLK
  • Задержка 1 мкс.
  • Устанавливаем низкий уровень SCLK
  • Задержка 1 мкс.


Рассмотрим функцию передачи команды/данных в режиме SPI, но сперва объявим константы:
.equ PCom = PORTD ; Управляющий порт к которому подключены SID и SCLK 
.equ SID = 1 ; RW Шина данных
.equ SCLK = 0; E Строб
.equ CS = 2; RS Начало/конец передачи данных
.def Data = R18   ; Регистр используется для записи данных в порт

А теперь сама функция:
/*************************************
Функции отправки команды и данных по последовательному порту
	LCD12864_CommandOut - Отправляет команду
	LCD12864_DataOut    - Отправляет данные

	Команда или данные должны находится в регистре Data
**************************************/
LCD12864_CommandOut:
	ldi r20, 0
	rjmp command
LCD12864_DataOut:
	ldi r20, 1	 
command:	      
	LCD8_MACRO_DELAY 1, 1
	sbi PCom, CS ; Начинаем передачу данных
	sbi PCom, SID ; Устанавливаем SID В 1
    ; Шлем 4 единицы
	rcall strob ; 1
	rcall strob ; 1
	rcall strob ; 1
	rcall strob ; 1
	rcall strob ; 1
    ; Устанавливаем rw на запись
	cbi PCom, SID  ; rw = 0
	rcall strob

    ; Выберем, команда или данные и отправим ее.
	cbi PCom, SID  ; rs = 0
    cpi r20, 0
	breq command1
	sbi PCom, SID  ; rs = 1
command1:
	rcall strob

    ; Отправляем 0
	cbi PCom, SID  ; 0  
	rcall strob

    ; Началась отправка байта
	ldi r20, 8    ; Счетчик бит
for_send_data:
	cpi r20, 0  ; Смотрим не закончились ли биты?
	breq  stop_send_data ; Если закончились то переходим к отправки последних нулей
	cpi r20, 4    ; Смотрим, если было отправлено 4 бита то выполняем отправку 4 нулей.
	brne  no_strob ; Иначе переходим к отправки следующего бита

    ; Отправка 4 нуля
	cbi PCom, SID
	rcall strob
	rcall strob
	rcall strob
	rcall strob

    ;Отправка следующего бита
no_strob:
	dec r20 ; Уменьшаем счетчик бит
	rol		Data ; Сдвигаем регистр с данными на 1 влево
	brcs  send_bit_1 ; Если сдвинутый регистр был 1, то флаг C был поднят, а значит переходим на отправку бита 1
    ; Если флаг С не был поднят, отправляем 0
	cbi PCom, SID ; Данные 0 бит
	rcall strob
	rjmp for_send_data
    ;Отправляем бит 1
send_bit_1:
	sbi PCom, SID ; Данные 1 бит
	rcall strob
	rjmp for_send_data
stop_send_data:

    ; Отправка байта закончилась, отправляем 4 нуля
	cbi PCom, SID 
	rcall strob
	rcall strob
	rcall strob
	rcall strob
	cbi PCom, SID 
	cbi PCom, CS ; Передача данных закончена
	ret


Функция строб:
strob:
	LCD8_MACRO_DELAY 1, 1 ; Задержка в 1 мкс
	sbi PCom, SCLK	                   ; Устанавливаем высокий уровень SCLK
	LCD8_MACRO_DELAY 1, 1 ; Задержка в 1 мкс
	cbi PCom, SCLK                     ; Устанавливаем низкий уровень SCLK
	LCD8_MACRO_DELAY 1, 1 ; Задержка в 1 мкс
	ret
.endif
;******************************************************


Текстовый режим


Теперь после того как вы научились инициализировать дисплей вы можете выводить любые символы на экран, например вывести букву A:
     ldi  Data,  'A'
     rcall  LCD12864_DataOut

И на дисплее вы увидите букву A.

И так, теперь о том как устроенно адресное пространство в текстовом режиме:
Экран делится на 8 столбцов и 4 строки, в каждый столбец вы можете записать по 2 обычных символа или 1 иероглиф.
Адресное пространство находится от 0 до 31.
0 1 2 3 4 5 6 7
16 17 18 19 20 21 22 23
8 9 10 11 12 13 14 15
24 25 26 27 28 29 30 31

Как видите первая строчка это адреса от 0 до 7
Вторая же строчка от 16 до 23
Третья строчка от 8 до 15
То есть если вы напишете 16 букв подряд с адреса 0, то они будут в первой строчке,
но если вы напишите 17 символов, то последний символ будет не на второй строчке, а на третей!

Есть специальная команда установки адреса курсора: 1 AC6 AC5 AC4 AC3 AC2 AC1 AC0
С помощью этой команды можно поставить курсор в нужное место вписав за место AC0-AC6 адрес от 0 до 31.


Графический режим


И напоследок, для тех кто хочет использовать графический режим, есть такая статья: LCD 12864 на контроллере ST7920. Параллельный режим (8 бит)

Библиотека для работы с ST7920


Ссылка на файл библиотеки
Ads
AdBlock has stolen the banner, but banners are not teeth — they will be back

More

Comments 18

    0
    Было бы неплохо написать в начале статьи параметры дисплея: физические размеры, сколько строк и знакомест на строку в текстовом режиме, сколько точек по горизонтали и вертикали в графическом, ну и какова цена дисплея.
      0
      Спасибо за подсказку.
      0
      Цена может быть разной, на сколько я заметил от 15 до 20 долларов…
        0
        Подключения по SPI (3 или 2 битной шине)

        Все-же «2- или 3-проводной». Потому что 2 или 3 бита за один такт по SPI не передаются, зато в ней могут использоваться 2 (SCK+DATA) или 3 (SCK+IN+OUT) провода.
          0
          Возможно, я не правильно описал, я это исправлю.
          Дело в том, что 1 контакт используется для передачи данных, второй строб, а третий используется для разрешения и запрещения дисплею принимать данные (CS), именно по этому, я его просто подсоединяю к нулю. Так как я не вижу нужды запрещать дисплею принимать данные.
            0
            Обычно сигнал CS одновременно является сигналом начала приема(сброс приемного буфера), в идеальном случае если каждый раз строго передавать строго заданное количество данных — индикатор будет принимать и правильно интерпретировать данные даже без передергивания CS, но если вдруг появится какая-то рассинхронизация в один строб то индикатор просто будет неверно интерпретировать поступающие данные, до тех пор пока не передернешь CS.
              0
              Я добавил в схему и в код CS. Просто я рассчитывал на то что в коде не будет лишних стробов.
                0
                В коде их может и не быть, это может быть внешняя наводка. Разряд статики вблизи, например.
        • UFO just landed and posted this here
            0
            Так сказано в даташите.
              0
              Я извиняюсь, не мА, а мкА, просто опечатка. Спасибо что заметили.
              0
              Есть один маааленький вопрос — чем обусловлены такие низкие номиналы потенциометров? Неужели на контраст идет такой огромный ток? Может там 330кОм нужен резистор?

              Почему на подсветку нельзя было использовать не потенциометрическое включение а привычное для светодиодов реостатное включение переменного резистора.
              Ведь из-за этих потенциометров как раз и имеем 30мА потребления в спящем режиме.
                0
                На самом деле, номиналы переменных резисторов обусловлены практикой, да, я тоже думал, что для регулировки подсветки и контрастности нужны большие сопротивления, но на практике при подключении 4 кОм переменного резистора, подсветка даже не включилась, так же как и контрастность.
                Может быть мне попалась подделка… Потому что все что я описал я реализовал на практике, и пробовал разные резисторы.
                  0
                  Ну, подсветка понятно почему не включилась — поскольку это просто один мощный распределенный светодиод, его достаточно было включить реостатом. От 4К включенном потенциометром подсветка включилась бы в одном из крайних положений и очень резко — это как раз понятно. А вот контраст… это уже интересней. Обычно он требует определенного напряжения, и тут тоже потенциометр вроде бы ни к чему — надо обеспечить напряжение на выводе и все. Обычно для индикаторов вполне обходятся резисторами в десятки килоом, может что-то не так было с потенциометром? И вроде бы, хватает одного резистора в "-" для работы контраста, только его сложнее подобрать.
                    0
                    Думаю, подключу и контраст и подсветку реостатоми по 10 кОм
                      0
                      Подсветку нужно подключать намного меньшим резистором, там же просто светодиод по сути (и то, возможно минимальный резистор даже встроен). Поставьте подстроечный на сотню Ом, и посмотрите. А на контраст можно килоомы, причём как уже правильно написали — можно не подключать к плюсу питания.
                        0
                        С подсветкой отлично работает на 10 кОм, сейчас проверил, в одну сторону до конца гасит, в другую ярко подсвечивает, так что все ок.
                  0
                  И еще раз извиняюсь, не 30 мА а 30 мкА.

                Only users with full accounts can post comments. Log in, please.