LCD 1602 является широко распространенным и популярным дисплеем среди радиолюбителей, кроме этого, аналогичные дисплеи встраиваются в различные устройства серийного производства. Дисплей построен на базе контроллера HD44780 и его аналогах, широкая известность связана с его давним появлением на рынке, во времена стремительного развития электроники. В этой статье я расскажу про сопряжение интерфейса I2C и LCD 1602, с помощью микросхемы расширителя портов PCF8574.
В продаже имеется два типа дисплея, с зеленым экраном и темными символами (заказать можно здесь), а также с синим экраном и светлыми символами (заказать можно тут), как по мне, то светлый зеленый экран смотрится лучше.
Описание и команды управления LCD 1602
Дисплей содержит две строки, в каждой умещается по 16 символов, напряжение питания может находиться в пределах 4,5-5,5 В, ток потребления составляет 1,2 мА без учета подсветки, которая может потреблять значительный ток. Дисплей имеет достаточно много выводов, назначение которых приведено в следующей таблице:
Номер вывода | Название | Описание |
1 | Vss | Вывод питания дисплея “-” |
2 | Vdd | Вывод питания дисплея “+” |
3 | VO | Вход регулировки контрастности дисплея |
4 | RS | Вход выбора типа инструкций, 1 – данные, 0 – команда |
5 | R/W | Вход направления передачи данных, 1 – запись данных в дисплей, 0 – чтение данных из дисплея |
6 | E | Вход тактирования |
7-14 | DB0-DB7 | Линии ввода/вывода данных |
15 | A | Вывод питания подсветки “+” |
16 | K | Вывод питания подсветки “-” |
Вход VO предназначен для регулировки контрастности экрана, которая зависит от величины напряжения на входе, обычно для этих целей устанавливается переменный резистор сопротивлением 10-20 кОм, подключенный к линии питания. С помощью входов RS, R/W выбирается тип инструкций и направление передачи данных. Вход тактирования E предназначен для “защелкивания” (фиксации) состояний входов и линий ввода/вывода, ввод инструкций в контроллер дисплея, а также считывание данных происходит по спаду сигнала (задний фронт). Линии DB7-DB0 представляют собой 8-битный интерфейс ввода/вывода данных, за один период тактового импульса передается 1 байт данных. Выводы A и K представляют собой анод и катод светодиода подсветки, на плате дисплея установлен токоограничивающий резистор.
Ниже в таблице представлены команды для управления LCD 1602:
№ | Описание инструкции | Код инструкции | Время выполнения | |||||||||
RS | R/W | DB7 | DB6 | DB5 | DB4 | DB3 | DB2 | DB1 | DB0 | |||
1 | Очистка дисплея с установкой курсора в начало первой строки | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 1,53 мс |
2 | Установка курсора в начало первой строки | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | – | 1,53 мс |
3 | Установка направления вывода символов, разрешение сдвига экрана | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | I/D | SH | 39 мкс |
4 | Управление режимом питания дисплея и отображением курсора | 0 | 0 | 0 | 0 | 0 | 0 | 1 | D | C | B | 39 мкс |
5 | Команда сдвига курсора и экрана | 0 | 0 | 0 | 0 | 0 | 1 | S/C | R/L | – | – | 39 мкс |
6 | Настройка интерфейса ввода/вывода данных, количества строк для вывода символов, размера шрифта | 0 | 0 | 0 | 0 | 1 | DL | N | F | – | – | 39 мкс |
7 | Запись адреса CGRAM памяти в адресный указатель | 0 | 0 | 0 | 1 | AC5 | AC4 | AC3 | AC2 | AC1 | AC0 | 39 мкс |
8 | Запись адреса DDRAM памяти в адресный указатель | 0 | 0 | 1 | AC6 | AC5 | AC4 | AC3 | AC2 | AC1 | AC0 | 39 мкс |
9 | Команда чтения флага занятости и адресного указателя | 0 | 1 | BF | AC6 | AC5 | AC4 | AC3 | AC2 | AC1 | AC0 | 0 мкс |
10 | Запись данных во внутреннюю память дисплея | 1 | 0 | D7 | D6 | D5 | D4 | D3 | D2 | D1 | D0 | 43 мкс |
11 | Чтение данных из внутренней памяти дисплея | 1 | 1 | D7 | D6 | D5 | D4 | D3 | D2 | D1 | D0 | 43 мкс |
Назначение битов представлено в следующей таблице:
Бит | Значение | Описание |
I/D | 0 | Вывод символов справа-налево, декремент адресного указателя DDRAM/CGRAM памяти |
1 | Вывод символов слева-направо, инкремент адресного указателя DDRAM/CGRAM памяти | |
SH | 0 | Запрет сдвига экрана при выводе символов |
1 | Разрешение сдвига экрана при выводе символов | |
D | 0 | Выключить экран дисплея, сегменты погашены, содержимое внутренней памяти сохраняется |
1 | Включить экран дисплея, нормальный режим работы | |
C | 0 | Отключить отображение курсора |
1 | Включить отображение курсора | |
B | 0 | Отключить функцию мигания курсора |
1 | Включить функцию мигания курсора | |
S/C | 0 | Выбрать курсор для сдвига |
1 | Выбрать экран (вместе с курсором) для сдвига | |
R/L | 0 | Сдвиг влево (только курсор или весь экран, зависит от бита S/C) |
1 | Сдвиг вправо (только курсор или весь экран, зависит от бита S/C) | |
DL | 0 | 4-битный интерфейс ввода/вывода данных |
1 | 8-битный интерфейс ввода/вывода данных | |
N | 0 | Использовать одну строку для вывода символов |
1 | Задействовать 2 строки для вывода символов | |
F | 0 | Размер шрифта 5×8 пикселей |
1 | Размер шрифта 5×11 пикселей | |
BF | 0 | Контроллер дисплея готов к обработке новой команды |
1 | Контроллер дисплея занят выполнением внутренних операций |
Дисплей можно подключить к микроконтроллеру используя все линии ввода/вывода, то есть через 8-битный интерфейс, но для этого потребуется значительное количество выводов микроконтроллера. Можно сократить количество выводов, если переключиться на 4-битный интерфейс (инструкция №6 из таблицы), в этом режиме задействованы линии ввода/вывода DB7-DB4, линии DB3-DB0 становятся неактивными. В этом случае 1 байт данных передается за два тактовых импульса, сначала старшие 4 бита, затем младшие.
Каждая инструкция выполняется определенное время, которое указано в вышеприведенной таблице, завершение внутренней операции можно узнать с помощью чтения флага занятости BF (инструкция №9). Но обычно флаг не опрашивают, а просто выдерживают соответствующую паузу после передачи инструкции. Вообще чтение данных из дисплея применяется редко, и не представляет особого интереса.
Внутренняя память LCD 1602 подразделяется на 3 вида: DDRAM, CGROM и CGRAM. Область DDRAM (Display Data RAM) памяти используется для хранения 8-битного кода ASCII символов, отображаемых на экране. Адреса регистров DDRAM памяти связаны с положением символов на экране, соответствие приведено в следующей таблице:
Адреса ячеек | ||||||||||||||||
1-я строка | 00 | 01 | 02 | 03 | 04 | 05 | 06 | 07 | 08 | 09 | 0A | 0B | 0C | 0D | 0E | 0F |
2-я строка | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 4A | 4B | 4C | 4D | 4E | 4F |
На каждой строке экрана умещается 16 символов, но это только видимая область, общий объем DDRAM памяти составляет 80 байт, то есть в каждую строку можно записать по 40 символов, и только 16 из них будут отображаться на экране, остальные символы останутся в невидимой области. С помощью команды сдвига экрана (инструкция №5) можно просмотреть все остальные символы, наподобие эффекта бегущей строки. Регистры с адресами 0x00-0x27 составляют первую строку, ячейки 0x40-0x67 вторую строку. Если задать только одну строку для вывода (бит N=0 в инструкции №6, при этом будет использована 1-я строка), то длина строки увеличиться, и будет вмещать 80 символов.
Память CGROM (Character Generator ROM) представляет собой знакогенератор и содержит данные для прорисовки ASCII символов. В памяти заложены спецзнаки, цифры, латинский алфавит, если не ошибаюсь, имеются китайские иероглифы, а также символы греческого алфавита, которые используются для обозначения физических величин. Каждый символ занимает 5 байт в памяти, что соответствует размеру шрифта 5×8 пикселей. На следующей картинке представлена таблица символов в соответствии с ASCII кодами:
Кириллица в LCD 1602 не заложена, что не удивительно, ведь это китайский дисплей, но существуют аналогичные отечественные дисплеи фирмы МЭЛТ с поддержкой русского алфавита.
Для вывода символа необходимо записать адрес регистра DDRAM памяти в адресный указатель (инструкция №8), тем самым выбрав положение символа на экране, затем записать в выбранный регистр код ASCII символа (инструкция №10), исходя из полученного кода, контроллер дисплея извлекает данные из CGROM памяти для прорисовки символа в заданном положении на экране. После вывода символа, адресный указатель автоматически инкрементируется или декрементируется, в зависимости от того, какое значение было задано биту направления I/D. Таким образом, символы можно выводить последовательно, при этом корректировка адреса DDRAM памяти не требуется. Например, если первая строка полностью заполнится символами, то произойдет переход на вторую строку, и наоборот.
Энергонезависимая память CGRAM (Character Generator RAM) предназначена для создания уникальных символов под нужды разработчика. Объем памяти небольшой, и позволяет хранить 8 произвольных символов. Для создания одного символа размером 5×8 пикселей, необходимо передать 8 байт данных в регистры памяти. На картинке ниже изображена структура CGRAM памяти и пример расположения символов:
Перед созданием символа необходимо записать адрес регистра CGRAM памяти в адресный указатель (инструкция №8), далее передать байты данных, которые составят изображение символа (инструкция №10), адресный указатель автоматически инкрементируется/декрементируется (в зависимости от бита I/D), как и в случае DDRAM памяти. В создании символа участвуют только 5 младших битов байта данных. Символам присваиваются коды 0x00-0x07 в соответствии с их расположением в памяти CGRAM, они отображены в ранее приведенной таблице символов.
Инициализация LCD 1602
После подачи питания необходимо провести инициализацию дисплея для корректного функционирования, в этой статье я не стану рассматривать 8-битный интерфейс передачи данных, так как все операции с LCD 1602 по I2C интерфейсу выполняются в 4-битном режиме. Порядок инструкций для инициализации представлен на следующей картинке:
После инициализации дисплей настроен на использование 2-х строк, экран очищен от мусора, курсор установлен в начало первой строки (адрес DDRAM памяти 0x00), отображение курсора отключено, выбран шрифт 5×8 пикселей, направление вывода символов слева-направо, сдвиг экрана при выводе символов запрещен.
Если разрешить сдвиг экрана при выводе символов (бит SH=1), то с каждым новым символом экран будет сдвигаться в выбранном направлении, то есть новые символы будут появляться в заданном положении на экране, а остальные сдвигаться.
Дополнительно на экране можно включить отображение курсора (бит C=1), который выглядит в виде линии подчеркивания, и может мигать в зависимости от настроек. Положение курсора на экране соответствует текущему адресу DDRAM памяти в адресном указателе. Курсор можно сдвигать по экрану (инструкция №5), при этом адресный указатель будет инкрементироваться/декрементироваться в зависимости от направления сдвига.
Подключение LCD 1602 через I2C интерфейс
Специально для дисплея, в продаже имеется модуль-переходник на основе микросхемы PCF8574AT, которая предназначена для расширения количества линий ввода/вывода. Микросхема подключается по I2C интерфейсу и имеет порт из 8 линий ввода/вывода, принцип функционирования простой, при записи байта данных в микросхему, линии порта принимают уровни, соответствующие значениям битов полученного байта. Операция чтения возвращает байт данных, биты которого указывают состояние линий порта. Таким образом, микросхема позволяет расширить количество линий ввода/вывода, используя два управляющих провода. Фото модуля представлено ниже:
Заказать модуль можно тут. На модуле установлен подстроечный резистор для регулировки контрастности экрана, также имеется джампер (перемычка), разрешающий управление подсветкой дисплея, о котором будет сказано позже.
На следующей картинке приведена схема подключения LCD 1602 и I2C модуля:
Адрес микросхемы PCF8574AT на шине I2C можно настраивать, старшие 4 бита адреса фиксированы, и равны 0111, младшие 3 бита зависят от состояния входов микросхемы A2-A0. На модуле данные входы подтянуты к высокому уровню, соответственно адрес микросхемы принимает значение 0111111.
Как видно из схемы, к микросхеме подключена только часть линий ввода/вывода дисплея DB7-DB4, это означает, что управление дисплеем возможно только через 4-битный интерфейс. Для ввода инструкции в дисплей требуется 2 тактовых импульса на линии E, то есть последовательность уровней 1010 (“защелкивание” данных происходит по спаду уровня), в итоге необходимо записать в микросхему 4 байта для одной инструкции. На картинке ниже приведен пример записи инструкции в дисплей по интерфейсу I2C:
Сначала передается старший полубайт инструкции с битом E=1, затем то же самое с битом E=0, при этом в дисплей передается первая половина инструкции. Далее таким же образом передается вторая половина (младший полубайт).
Для управления подсветкой дисплея, на плате модуля установлен транзистор, подключенный к линии P3 микросхемы. Таким образом, 3-й бит в байте данных управляет подсветкой, 0 – выключена, 1 – включена. При отсутствии джампера (о котором я писал выше) подсветка отключается, независимо от бита управления.
Я подключил дисплей с модулем к микроконтроллеру PIC16F628A, схема представлена ниже:
Кнопка SB1 управляет подсветкой дисплея, светодиод HL1 загорается в случае ошибки передачи данных. Неполный код программы приведен ниже (полную версию можно скачать в конце статьи):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 | ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; #include <P16F628A.INC> LIST p=16F628A __CONFIG H'3F10' ;Конфигурация микроконтроллера errorlevel -302 ;не выводить сообщения с ошибкой 302 в листинге Sec equ 0020h ;вспомогательные регистры счета Sec1 equ 0021h ; Sec2 equ 0022h ; scetbit equ 0024h ;вспомогательный регистр счета кол-ва бит perem equ 0025h ;вспомогательный регистр приема/передачи байта по интерфейсу tmp equ 0026h ;вспомогательный регистр счетчик ; lcd_tmp equ 0027h ;вспомогательный регистр передачи инструкций ; adr_i2c equ 0028h ;регистры подпрограммы передачи данных по интерфейсу tmp_i2c equ 0029h ; slave_adr equ 002Ah ; data_i2c equ 002Bh ; ; flag equ 007Fh ;регистр флагов #DEFINE sda PORTB,0 ;линия sda #DEFINE scl PORTB,1 ;линия scl #DEFINE sda_io TRISB,0 ;линия направления sda #DEFINE scl_io TRISB,1 ;линия направления scl #DEFINE knp_led PORTB,2 ;кнопка вкл/выкл подсветки #DEFINE led PORTB,3 ;светодиод ошибки передачи данных по интерфейсу ;flag,2 - флаг состояния подсветки (0 - выключена, 1 - включена) ;flag,3 - флаг передачи команда/данные (0 - команда, 1 - данные) ;flag,4 - флаг направления передачи (0 - чтение, 1 - запись) ;flag,5 - флаг окончания приема данных от ведомого ;flag,6 - флаг ошибки передачи по интерфейсу I2C (отсутствие подтверждения от ведомого) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; org 0000h ;начать выполнение программы с адреса 0000h goto Start ;переход на метку Start ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;Основная программа Start movlw b'00000000' ;установка значений выходных защелок порта B movwf PORTB ; ; movlw b'00000111' ;выключение компараторов movwf CMCON ; ; bsf STATUS,RP0 ;выбрать 1-й банк movlw b'11110111' ;настройка линий ввода\вывода порта B movwf TRISB ;RB3 - на выход, остальные на вход bcf STATUS,RP0 ;выбрать 0-й банк ; clrf flag ;сброс регистра флагов ; call lcd_led_on ;вызов подпрограммы включения подсветки ; call init_lcd ;вызов подпрограммы инициализации дисплея call err_prov ;проверка ошибки ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; movlw 0x02 ;установка адреса DDRAM памяти (1-я строка 3-я позиция) call lcd_adr ;вызоа подпрограммы передачи адреса DDRAM movlw 'R' ;Вывод надписи RadioLaba.ru call lcd_dat ; movlw 'a' ; call lcd_dat ; movlw 'd' ; call lcd_dat ; movlw 'i' ; call lcd_dat ; movlw 'o' ; call lcd_dat ; movlw 'L' ; call lcd_dat ; movlw 'a' ; call lcd_dat ; movlw 'b' ; call lcd_dat ; movlw 'a' ; call lcd_dat ; movlw '.' ; call lcd_dat ; movlw 'r' ; call lcd_dat ; movlw 'u' ; call lcd_dat ; ; call paus ;пауза 1 сек ; movlw 0x43 ;установка адреса DDRAM памяти (2-я строка 4-я позиция) call lcd_adr ;вызоа подпрограммы передачи адреса DDRAM ; movlw 'L' ;Вывод надписи LCD 1602A call lcd_dat ; movlw 'C' ; call lcd_dat ; movlw 'D' ; call lcd_dat ; movlw ' ' ; call lcd_dat ; movlw '1' ; call lcd_dat ; movlw '6' ; call lcd_dat ; movlw '0' ; call lcd_dat ; movlw '2' ; call lcd_dat ; movlw 'A' ; call lcd_dat ; ; call paus ;пауза 1 сек call paus ;пауза 1 сек ; movlw .16 ;сдвиг экрана влево на 16 позиций movwf tmp ; met_1 movlw b'00011000' ;команда сдвига экрана влево call lcd_comm ;вызов подпрограммы передачи команды decfsz tmp,F ; goto met_1 ; movlw 0x13 ;установка адреса DDRAM памяти (1-я строка 20-я позиция) call lcd_adr ;вызоа подпрограммы передачи адреса DDRAM movlw 'P' ;Вывод надписи PCF8574AT call lcd_dat ; movlw 'C' ; call lcd_dat ; movlw 'F' ; call lcd_dat ; movlw '8' ; call lcd_dat ; movlw '5' ; call lcd_dat ; movlw '7' ; call lcd_dat ; movlw '4' ; call lcd_dat ; movlw 'A' ; call lcd_dat ; movlw 'T' ; call lcd_dat ; ; call paus ;пауза 1 сек ; movlw 0x54 ;установка адреса DDRAM памяти (2-я строка 21-я позиция) call lcd_adr ;вызоа подпрограммы передачи адреса DDRAM ; movlw 'I' ;Вывод надписи I2C bus call lcd_dat ; movlw '2' ; call lcd_dat ; movlw 'C' ; call lcd_dat ; movlw ' ' ; call lcd_dat ; movlw 'b' ; call lcd_dat ; movlw 'u' ; call lcd_dat ; movlw 's' ; call lcd_dat ; ; call paus ;пауза 1 сек call paus ;пауза 1 сек ; sdvig movlw .16 ;сдвиг экрана вправо на 16 позиций с одновременным опросом кнопки подсветки movwf tmp ;с частотой 10 Гц sdvig_1 movlw b'00011100' ;команда сдвига экрана вправо call lcd_comm ;вызов подпрограммы передачи команды call knp_opros ;вызов подпрограммы опроса кнопки подсветки movlw .100 ; call paus_lcd ;пауза 100 мс decfsz tmp,F ; goto sdvig_1 ; movlw .20 ;пауза 2 сек с опросом кнопки подсветки с частотой 10 Гц movwf tmp ; sdvig_2 call knp_opros ;вызов подпрограммы опроса кнопки подсветки movlw .100 ; call paus_lcd ;пауза 100 мс decfsz tmp,F ; goto sdvig_2 ; movlw .16 ;сдвиг экрана влево на 16 позиций с одновременным опросом кнопки подсветки movwf tmp ;с частотой 10 Гц sdvig_3 movlw b'00011000' ;команда сдвига экрана влево call lcd_comm ;вызов подпрограммы передачи команды call knp_opros ;вызов подпрограммы опроса кнопки подсветки movlw .100 ; call paus_lcd ;пауза 100 мс decfsz tmp,F ; goto sdvig_3 ; movlw .20 ;пауза 2 сек с опросом кнопки подсветки с частотой 10 Гц movwf tmp ; sdvig_4 call knp_opros ;вызов подпрограммы опроса кнопки подсветки movlw .100 ; call paus_lcd ;пауза 100 мс decfsz tmp,F ; goto sdvig_4 ; goto sdvig ;переход на метку sdvig ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; knp_opros btfsc knp_led ;Подпрограмма опроса кнопки подсветки дисплея return ;кнопка не нажата: выход из подпрограммы call paus_knp ;кнопка нажата, ожидание отжатия btfss knp_led ; goto $-2 ; ; btfsc flag,2 ;опрос флага состояния подсветки goto knp_1 ; call lcd_led_on ;подсветка выключена: вызов подпрограммы включения подсветки return ;выход из подпрограммы ; knp_1 call lcd_led_off ;подсветка включена: вызов подпрограммы выключения подсветки return ;выход из подпрограммы ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; err_prov btfss flag,6 ;проверка ошибок передачи данных return ;нет ошибок: выход из подпрограммы err_1 bsf led ;ошибка: включить светодиод led goto err_1 ;переход на метку err_1: зацикливание программы ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;Подпрограмма включения/выключения подсветки дисплея lcd_led_on movlw b'00001000' ;Команда включения подсветки 3-й бит в 1 movwf adr_i2c ; bsf flag,2 ;установка флага включенного состояния подсветки goto lcd_led_1 ; lcd_led_off movlw b'00000000' ;Команда выключения подсветки 3-й бит в 0 movwf adr_i2c ; bcf flag,2 ;установка флага выключенного состояния подсветки lcd_led_1 movlw b'01111110' ; movwf slave_adr ;запись адреса микросхемы PCF8574AT в регистр slave_adr clrf tmp_i2c ; call write_i2c ;вызов подпрограммы записи по интерфейсу i2c return ;выход из подпрограммы ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;Подпрограмма инициализации дисплея LCD 1602 init_lcd movlw .15 ;пауза 15 мс call paus_lcd ; movlw b'00110000' ;Передача команды 0011 (старший полубайт) call lcd_comm_1 ;вызов подпрограммы передачи полубайта команды на дисплей btfsc flag,6 ;проверка ошибки return ; movlw .5 ;пауза 5 мс call paus_lcd ; movlw b'00110000' ;Передача команды 0011 (старший полубайт) call lcd_comm_1 ;вызов подпрограммы передачи полубайта команды на дисплей btfsc flag,6 ;проверка ошибки return ; movlw .1 ;пауза 1 мс call paus_lcd ; movlw b'00110000' ;Передача команды 0011 (старший полубайт) call lcd_comm_1 ;вызов подпрограммы передачи полубайта команды на дисплей btfsc flag,6 ;проверка ошибки return ; ;Передача команды 0010 (старший полубайт) movlw b'00100000' ;Установка 4-х битного режима call lcd_comm_1 ;вызов подпрограммы передачи полубайта команды на дисплей btfsc flag,6 ;проверка ошибки return ; ;Передача команды 00101000 movlw b'00101000' ; 4-х битный режим, использовать 2 строки дисплея, шрифт 5x8 call lcd_comm ;вызов подпрограммы передачи байта команды на дисплей btfsc flag,6 ;проверка ошибки return ; ;Передача команды 00001000 movlw b'00001000' ;выключить дисплей, выключить курсор, выключить мигание курсора call lcd_comm ;вызов подпрограммы передачи байта команды на дисплей btfsc flag,6 ;проверка ошибки return ; movlw b'00000001' ;Передача команды очистки дисплея 00000001 call lcd_comm ;вызов подпрограммы передачи байта команды на дисплей btfsc flag,6 ;проверка ошибки return ; ;Передача команды 00000110 movlw b'00000110' ;направление движения курсора- вправо (инкремент адреса), запретить сдвиг экрана call lcd_comm ;вызов подпрограммы передачи байта команды на дисплей btfsc flag,6 ;проверка ошибки return ; ;Передача команды 00001100 movlw b'00001100' ;включить дисплей, выключить курсор, выключить мигание курсора call lcd_comm ;вызов подпрограммы передачи байта команды на дисплей return ; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;Подпрограмма передачи полубайта команды на дисплей lcd_comm_1 movwf lcd_tmp ; movlw b'01111110' ; movwf slave_adr ;запись адреса микросхемы PCF8574AT в регистр slave_adr movlw b'11110000' ;Передача старшего полубайта, бит E=1 andwf lcd_tmp,W ; movwf adr_i2c ; movlw b'00000100' ; iorwf adr_i2c,F ; btfsc flag,2 ;опрос флага состояния подсветки bsf adr_i2c,3 ;флаг подсветки=1, включение подсветки movlw data_i2c ;установка первого регистра приема/передачи movwf FSR ; movlw b'11110000' ;Передача старшего полубайта, бит E=0 andwf lcd_tmp,W ; movwf INDF ; btfsc flag,2 ;опрос флага состояния подсветки bsf INDF,3 ;флаг подсветки=1, включение подсветки movlw .1 ;передача 1-го байта movwf tmp_i2c ; call write_i2c ;вызов подпрограммы записи по интерфейсу i2c return ;выход из подпрограммы ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;Подпрограмма передачи команды или данных на дисплей (1 байт) lcd_adr movwf lcd_tmp ;Команда записи адреса bsf lcd_tmp,7 ;установка 7-го бита, bcf flag,3 ;сброс флага, передача команды goto lcd_met_1 ; lcd_cgram movwf lcd_tmp ;Команда установки адреса CGRAM памяти bcf lcd_tmp,7 ;сброс 7-го бита, установка 6-го бита bsf lcd_tmp,6 ; bcf flag,3 ;сброс флага, передача команды goto lcd_met_1 ; lcd_dat movwf lcd_tmp ;Подпрограмма записи данных в дисплей bsf flag,3 ;установка флага, передача данных goto lcd_met_1 ; lcd_comm movwf lcd_tmp ;Подпрограмма передачи команды на дисплей bcf flag,3 ;сброс флага, передача команды lcd_met_1 movlw b'01111110' ; movwf slave_adr ;запись адреса микросхемы PCF8574AT в регистр slave_adr movlw b'11110000' ;Передача старшего полубайта, бит E=1 andwf lcd_tmp,W ;если передается команда бит RS=0 movwf adr_i2c ;если передаются данные бит RS=1 btfsc flag,3 ; movlw b'00000101' ; btfss flag,3 ; movlw b'00000100' ; iorwf adr_i2c,F ; btfsc flag,2 ;опрос флага состояния подсветки bsf adr_i2c,3 ;флаг подсветки=1, включение подсветки movlw data_i2c ;установка первого регистра приема/передачи movwf FSR ; movlw b'11110000' ;Передача старшего полубайта, бит E=0 andwf lcd_tmp,W ;если передается команда бит RS=0 movwf INDF ;если передаются данные бит RS=1 btfss flag,3 ; goto lcd_met_2 ; movlw b'00000001' ; iorwf INDF,F ; lcd_met_2 incf FSR,F ;инкремент регистра FSR swapf lcd_tmp,F ;меняем местами полубайты movlw b'11110000' ;Передача младшего полубайта, бит E=1 andwf lcd_tmp,W ;если передается команда бит RS=0 movwf INDF ;если передаются данные бит RS=1 btfsc flag,3 ; movlw b'00000101' ; btfss flag,3 ; movlw b'00000100' ; iorwf INDF,F ; incf FSR,F ;инкремент регистра FSR movlw b'11110000' ;Передача младшего полубайта, бит E=0 andwf lcd_tmp,W ;если передается команда бит RS=0 movwf INDF ;если передаются данные бит RS=1 btfss flag,3 ; goto lcd_met_3 ; movlw b'00000001' ; iorwf INDF,F ; lcd_met_3 btfss flag,2 ;опрос флага состояния подсветки goto lcd_met_4 ;флаг подсветки=0, подсветка выключена, биты не меняем movlw data_i2c ;установка первого регистра приема/передачи movwf FSR ; bsf INDF,3 ;флаг подсветки=1, включение подсветки incf FSR,F ; bsf INDF,3 ; incf FSR,F ; bsf INDF,3 ; lcd_met_4 movlw .3 ;передача 3-х байта movwf tmp_i2c ; call write_i2c ;вызов подпрограммы записи по интерфейсу i2c btfsc flag,3 ;проверка команда или данные return ;выход из подпрограммы передачи данных на дисплей swapf lcd_tmp,F ;меняем местами полубайты movlw .1 ;проверка крманды очистка дисплея xorwf lcd_tmp,W ; btfsc STATUS,Z ; goto lcd_met_5 ;команда соответствует очистке дисплея переход на lcd_met_5 movlw b'11111110' ;проверка крманды возврата курсора andwf lcd_tmp,W ; xorlw .2 ; btfss STATUS,Z ; return ;выход из подпрограммы передачи данных на дисплей lcd_met_5 movlw .2 ;команда соответствует возврату курсора call paus_lcd ;вызов подпрограммы паузы 2 мс return ;выход из подпрограммы передачи данных на дисплей ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; |
Микроконтроллер настроен на внутренний тактовый генератор с частотой 4 МГц. После настройки внутренних регистров, выполняется включение подсветки дисплея, после чего вызывается подпрограмма инициализации дисплея, порядок инструкций для инициализации я приводил выше. Далее в первой строке выводится надпись “Radiolaba.ru”, на второй строке “LCD 1602A”, затем выполняется сдвиг экрана влево на 16 позиций, то есть для отображения выбирается свободная область DDRAM памяти. Здесь в продолжение первой строки выводится надпись “PCF8574AT”, и для второй строки “I2C bus”. Далее выполняется круговой цикл, где экран поочередно сдвигается на 16 позиций вправо и влево. Таким образом, можно просмотреть все символы записанные в DDRAM память. Одновременно в цикле выполняется опрос кнопки подсветки SB1.
Для установки адреса DDRAM памяти (позиция на экране) вызывается подпрограмма lcd_adr, для записи данных в дисплей – подпрограмма lcd_dat. Подпрограмма lcd_comm используется для записи команды, для установки адреса CGRAM памяти – lcd_cgram. Включение и выключение подсветки – подпрограммы lcd_led_on и lcd_led_off.
Инструкции очистки дисплея и возврата курсора выполняются значительно дольше остальных, поэтому, после передачи этих инструкций в дисплей, в коде выполняется пауза на 2 мс.
У дисплея имеются некоторые особенности, при смене количества используемых строк, меняется контрастность экрана, то есть она не одинакова для однострочного и двухстрочного режима. Шрифт 5×10 в дисплее не заложен, хотя команда настройки размера имеется, судя по матрице, она физически не предназначена для такого шрифта.
Заказать комплектующие можно по ссылкам ниже:
Дисплей LCD 1602
Дисплей LCD 2004
Модуль I2C PCF8574
На следующем видеоролике представлен пример вывода символов на дисплей:
Виктор
8 Май 2018Непонятно, правда, зачем єто нужно?
Разве что когда нехватает выводов микроконтроллера… А не будет ли дешевле взять контроллерс большим количеством пинов?
Дмитрий
15 Июл 2018Эта статья заслуживает уважения. Очень подробно всё расписано. Особенно пример отправки одной команды по I2C. Спасибо автору за труд. Обожаю когда с виду сложные вещи объясняют на простым языком. Нет ничего сложного для понимания микроконтроллеров с такими статьями.
Victor_K
27 Окт 2018В коде есть пару ошибок, думаю не случайных. А так код , после 2х часов плясок с бубном, ожил. Вообще-то чтобы использовать I2C для связи с дисплеем нужна какая-то крайняя необходимость. Я в простых схемах использую регистр 74HC595 (когда нет необходимости читать дисплей). да и код более оптимальный.
admin
27 Окт 2018Приветствую! Что за ошибки вы нашли в коде? если не сложно укажите на них. У меня все работает без ошибок, что и продемонстрировано на видео.
timm
5 Фев 2024>Что за ошибки вы нашли в коде?
В таблице, команда 2, написано “Установка курсора в начало первой строки”. Но это инициализация, а не просто перевод курсора.
Павел
14 Ноя 2018Насчет нужды в применении I2C – как раз все очень понятно. Если свободных портов хватает, никто и не будет мудрить. А при нехватке – переходить на PIC16F873 (или похожее) вместо PIC16F628 уж совсем не хочется. Два порта вместо шести (или три вместо семи) – это существенно! Громила типа 16F873 имеет на 10 выводов больше. Тем более, если I2C требуется для связи с другими устройствами, выигрыш очевиден. Понятно, если размер устройства значения не имеет, можно и PIC16F877 применить, там портов много.
Victor_K
27 Окт 2018Не нашел в коде где устанавливаются биты PORTB,0 и PORT,1 из-за этого собственно и не было передачи данных пришлось добавить в этом куске кода:
sda_1 bsf sda ; ПРИШЛОСЬ ДОБАВИТЬ ИНАЧЕ ВЫВОД PORTB,0 НЕ ;УСТАНАВЛИВАЕТСЯ В 1
bsf STATUS,RP0 ;перенастройка линии sda на вход
bsf sda_io ;
bcf STATUS,RP0 ;
return ;возврат из подпрограммы
sda_0 bcf sda ;ТО-ЖЕ ДОБАВЛЕНО ДЛЯ УСТАНОВЛЕНИЯ PORTB,O В 0
bsf STATUS,RP0 ;
bcf sda_io ;
bcf STATUS,RP0 ;
return ;возврат из подпрограммы
scl_1 bsf scl ; ТО-ЖЕ ДЛЯ УСТАНОВКИ PORTB,1 В 1
bsf STATUS,RP0 ;перенастройка линии scl на вход
bsf scl_io ;
bcf STATUS,RP0 ;
return ;возврат из подпрограммы
scl_0 bcf scl ; ТО-ЖЕ ДЛЯ УСТАНОВКИ PORTB,1 В 0
bsf STATUS,RP0 ;
bcf scl_io ;
bcf STATUS,RP0 ;
return ;возврат из подпрограммы
Victor_K
27 Окт 2018так-же пришлось добавить в начале подтяжку вн.резисторов PORT,B(bit 7 RBPU: PORTB Pull-up Enable bit
1 = PORTB pull-ups are disabled
0 = PORTB pull-ups are enabled by individual port latch values):
BANKSEL TRISA
MOVLW B’01111111′
MOVWF OPTION_REG
BANKSEL PORTA
admin
27 Окт 2018Насчет подтягивающих резисторов, они уже имеются на плате переходнике I2C PCF8574AT. Поэтому я ничего не подтягивал, ни программно, ни физически.
Victor_K
27 Окт 2018ААА, я сначала в Proteuse тестил, потом на макетке собрал. В Proteuse пока подтяжку не включил вообще ничего не шло!
Victor_K
27 Окт 2018с этим понятно, но вот Ваш код:
bsf STATUS,RP0 ;
bcf sda_io ;
bcf STATUS,RP0 ;
собственно он и должен установить PORTB,0, а бит sda_io, я так понял относится к регистру TRISB, для изменения вход-выход. А где устанавливается бит sda?
admin
27 Окт 2018Просто вы не до конца изучили протокол I2C, нельзя на линиях устанавливать лог.1 ! Линии должны быть подтянуты к + питания, а ведущий и ведомый должны иметь выходы с открытым стоком или коллектором. Чтобы выдать лог.0 на кто то из них притягивает линию к 0, а чтобы выдать лог.1 соответственно отпускают линию, отсюда в коде и подпрограммы которые меняют направление линии.
Например линию SCL может контролировать и ведомый, он может ее удерживатьв 0, если занят. А если в этот момент ведущий установит линию в 1, то произойдет замыкание! Можете почитать про I2C у меня на сайте https://radiolaba.ru/microcotrollers/i2c-interfeys.html
Victor_K
27 Окт 2018тут Вы меня ставите в тупик… т.е. команды установки битов PORTB,0 и 1 не нужны? и передача данных . точнее выставление на шину 0 и 1 осуществляется за счет изменения направления регистра TRIS? потому что в этой части кода:
bsf STATUS,RP0 ;
bcf sda_io ;
bcf STATUS,RP0 ;
изменяется только направление бита 0 PORTB на вход-выход
admin
27 Окт 2018нужна только команда сброса соответствующего бита PORTB. Чтобы установить лог.0 на линии, нужно предварительно сбросить бит линии в 0 (например bcf sda (что равнозначно bcf PORTB,0)), и перенастроить ее на выход (bcf sda_io (что равнозначно bcf TRISB,0)), чтобы установить лог.1 нужно просто перенастроить линию на вход (bsf sda_io (что равнозначно bsf TRISB,0)), вот и все.
admin
27 Окт 2018Так это же специально сделано, чтобы можно было любой удобный вывод назначить под это дело.
Все это задается в начале программы:
#DEFINE sda PORTB,0 ;линия sda
#DEFINE scl PORTB,1 ;линия scl
#DEFINE sda_io TRISB,0 ;линия направления sda
#DEFINE scl_io TRISB,1 ;линия направления scl
То есть задал в начале программы нужные линии и все, чтобы как раз избежать ваших действий, по редактированию кода.
Victor_K
27 Окт 2018и после вывода второй части текста на дисплее оставалось часть первого сообщения, пришлось добавить между ними:
movlw b’00000001′ ;ОЧИСТКА
call lcd_comm
admin
27 Окт 2018Не совсем понял, каким образом оставалось часть первого сообщения, там просто происходит сдвиг экрана, то есть на экран выводится сначала одна область памяти дисплея, затем вторая область памяти. Память дисплея больше чем может уместиться символов на экране.
Victor_K
27 Окт 2018у меня сообщения выходят без сдвига. просто сначала одно потом через 0,5с второе, думаю связано с дисплеем
Инженер
27 Фев 2019“… ведь это китайский дисплей”
Японский.
“… имеются китайские иероглифы”
Это японские. Китайские встречаются в некоторых нестандартных вариантах кодовых страниц.
“… длина строки увеличиться”
Ну вот как так можно, а?!
Гумер
19 Июл 2019Добрый день.
Большой проект Color and Code версии 19. Определение элементов по цвету, коду, справочники, включая Arduino.
Есть встроенный калькулятор LCD1602 символов, генерация программной строки….
Может кому пригодится https://colorandcode.su
Сергей
13 Окт 2019R/W – определяет что будем делать, считывать(R/W=1) или записывать(R/W=0)
Исправьте ошибку, долго не мог найти, что не так
Spike
2 Дек 2019Спасибо, все работает, легко переделал под PIC16F877, были проблемы, но из-за того, что у меня не такие чуть-чуть модули, у меня в них микросхема PCF8574T, а по даташиту у неё другой адрес, после включения дисплей не инициализировался, горела подсветка, на кнопку не реагировал, после замены адреса микросхемы в программе на “01001110”, все заработало идеально как на видео.
jroyce8
6 Мар 2020Здравствуйте!
Хотел бы спросить: “Регистры с адресами 0x00-0x27 составляют первую строку, ячейки 0x40-0x67 вторую строку”, является ли “0x40-0x67” опечаткой? Разве не эти адреса 0х28-0х4F составляют вторую строку?
admin
6 Мар 2020Это не опечатка, все правильно
Денис
27 Апр 2020Привет. Уважаемый автор, ваш код я переделал под PIC16F648A и управляю часами на DS3231. Всё прекрасно работает, но я так и не понял из даташита, возможно ли во время работы устройства, программно переключать PCF8574 линии P4-P7 на вход, дабы считывать флаг BF и счётчик адреса. Ведь в вашем коде нет опроса этого флага. В даташите только при инициализации, разово можно установить часть пинов на вход, а часть на выход.
20mikel
10 Сен 2020Здравствуйте. Я пока учусь программированию, и хотел попросить программу под PCF8574T, PIC16F628А Надо менять адрес, но у меня не получается. Очень прошу выложить прошивку под PCF8574T, я попробую сравнить с прошивкой под PCF8574АT, тогда до меня быстрее дойдет. Спасибо .
Dmitrii
8 Май 2022Я не пойму ни как… А где строб в инициализации? Где он вообще? ауу…
Dmitrii
16 Май 2022И зачем в инициализации 4 битного режима нужна8 битная инструкция? Я сразу начал с инициализации 4бит и все нормально
timm
7 Фев 2024у меня так же, по-моему 8-битная инструкция и не выполняется
Александр
8 Янв 2023Спасибо, пока из Китая едет модуль, буду вникать в Ваш код. В Протеусе работает.
Виктор Парамонов
13 Фев 2023Огромная благодарность за предоставленный материал!
Именно отсюда вразумился как дисплей управляется по (4+3)-проводной шине. Тем более, что первый полученный дисплей с интерфейсом I2C имел неисправную интерфейсную плату.
Программировал на ассемблере для AVR.
Спасибо!!!
Виктор Парамонов. viktor-paramon@yandex.ru
Олег
30 Ноя 2023Спасибо! Всё хорошо работает на pic12f629, с заменой адреса на “01001110”
timm
7 Фев 2024Почему работа идёт в 4-битном режиме, хотя и дисплей и PCF8574 поддерживают 8 битный режим? Сборка LCD+PCF выглядит так, будто распаяны все 8 пинов данных.
timm
7 Фев 2024Похоже, 4 бит хотя бы из-за того что физически не хватает ног на PCF8574. Спасибо за статью, помогло запустить проект!