Управление ЖК дисплеем 1602 при помощи сдвигового регистра 74HC595
Дата: 27 Октября 2015. Автор: Алексей
В библиотеке AXLIB есть набор функций для работы с дисплеем используя этот регистр как шлюз.
Помнится как-то я писал про Управление LCD 1602 по шине I2C. Микросхемка PCF8574 конечно интересная штука, да вот нашел я ей альтернативу. Знакомьтесь, сдвиговый регистр с защелкой 74HC595. Чем он хорош. Первое что меня порадовало так это цена. PCF8574 стоит в районе 90-120 рублей, а 74РС595 около 12 рублей. Почти на порядок дешевле. Многие могут возразить, мол данный регистр односторонний. Да, я согласен. но цель данной статьи именно в использовании в одну сторону. То есть управлять ЖК дисплеем кидая ему команды и данные, без чтения из него какой-либо информации. Проще говоря я заменил PCF8574 на 74HC595. Правда для управления регистром нужно выделить 4 вывода МК, но зато можно использовать любые пины и порты, в отличии от PCF8574 которая привязана жестко к I2C
И по русски)))
Q0...Q7 Параллельный выход данных. GND, VCC Питание микросхемы. Q7' Выход переноса для каскада регистров. OE Включение микросхемы. 0-включена, 1-выключена. MR Сброс данных в регистре. SH_CP Стробирование. ST_CP Защелка. DS Вход данных.А теперь давайте поговорим об алгоритме работы регистра. Для того чтобы включить и начать работать с микросхемой, нужно прижать к GND вывод ОЕ, ST_CP и SH_CP, а MR подтянуть к питанию. В этом случае микросхема готова получать данные в регистр. Для ввода данных, байт необходимо подавать старшим битом вперед на вход DS регистра. Как только на входе DS выставляется первый бит, нужно подтянуть к питанию вход SH_CP, а затем прижать его к GND. Далее выставить на DS следующий бит и снова дернуть SH_CP. И так 8 раз пока весь байт не запишется в регистр. Для того чтобы наш байт вылез наружу через параллельный порт, необходимо подтянуть к питанию вход ST_CP, а затем снова прижать его к GND. Для того чтобы стереть все данные в регистре нужно выполнить следующие действия. Прижать вывод MR к GND, тем самым стереть данные в регистре, а для того чтобы стереть данные на выходном порту, необходимо не отпуская MR подтянуть к плюсу вывод ST_CP, а потом оба вывода отпустить, MR к плюсу, а ST_CP к GND. Таким образом стираются все данные в регистре. Если дергать просто вывод MR, то стираться данные будут только в самом регистре, а на параллельном порту будут висеть старые данные. Вот как-то так. А теперь подключаем ЖК дисплей к регистру вот по такой схеме.
По крупнее
А теперь программа.
#define REGISTR_RESET 0 // Разряд сброса регистра #define REGISTR_CLK 1 // Разряд стробирования #define REGISTR_SHIFT 2 // Разряд защелки #define REGISTR_DATA 3 // Разряд данных #define REGISTR_PORT PORTD #define REGISTR_DDR DDRDЗдесь мы определились с ножками МК.
// Инициализация регистра void reg_74hc595_init(void) { REGISTR_DDR |= (1 << REGISTR_RESET) | (1 << REGISTR_CLK) | (1 << REGISTR_SHIFT) | (1 << REGISTR_DATA); // Настройка разрядов порта на вывод REGISTR_PORT &= ~(1 << REGISTR_CLK); REGISTR_PORT &= ~(1 << REGISTR_SHIFT); REGISTR_PORT |= (1 << REGISTR_RESET); }Данная функция настраивает ножки МК на выход, а затем подтягивает ножку сброса к 1, а защелку и строб к 0.
// Функция сброса регистра void reg_74hc595_reset(void) { REGISTR_PORT &= ~(1 << REGISTR_RESET); REGISTR_PORT |= (1 << REGISTR_SHIFT); REGISTR_PORT &= ~(1 << REGISTR_SHIFT); REGISTR_PORT |= (1 << REGISTR_RESET); }Данная функция стирает все данные в регистре. Алгоритм я уже описывал выше, а это его реализация в коде.
// Вункция вывода байта через регистр void reg_74hc595_byte(char data) { for(char i=0; i<8; i++) { if((data << i) & 0x80) // Выставляем по очереди, начиная со старшего, разряды { REGISTR_PORT |= (1 << REGISTR_DATA); // Если 1 } else { REGISTR_PORT &= ~(1 << REGISTR_DATA); // Если 0 } REGISTR_PORT |= (1 << REGISTR_CLK); // Стробирование REGISTR_PORT &= ~(1 << REGISTR_CLK); } REGISTR_PORT |= (1 << REGISTR_SHIFT); // Защелка REGISTR_PORT &= ~(1 << REGISTR_SHIFT); }А вот эта функция по интереснее. Здесь мы получаем требуемый байт для вывода через регистр, затем загоняем его в цикл с 8 итерациями. В данном цикле мы сначала сдвигаем данные влево на количество равное индексу цикла, затем логичеки умножаем на 0x80 и если получившееся значение не равно нулю, то выставляем на линию DS единицу. В противном случае ноль. После дергаем ножку стробирования. После того как закончится цикл и наш байт запишется в регистр, мы дергаем ножку защелки, тем самым выводим данные наружу. В принципе можно пользоваться данными функциями и для любых других целей, например зажигать светодиоды или управлять светодиодным индикатором. Но мы возвращаемся к нашей задаче.
Программа для вывода данных на ЖК.
#define LCD_RS 0 #define LCD_E 1 // Команды #define LCD_E_0(data) (data &= ~(1 << LCD_E)) // Е в ноль #define LCD_E_1(data) (data |= (1 << LCD_E)) // Е в единицу #define LCD_COM(data) (data &= ~(1 << LCD_RS)) // Запись команды #define LCD_DATA(data) (data |= (1 << LCD_RS)) // Запись данныхТут для удобства обзовем выводы строба и выбора команда/дата и пропишем четыре макроса для выставления нужных состояний этих выводов.
// Передача команды дисплею void lcd1602_com(char com) { LCD_COM(com); // Запись команды reg_74hc595_byte(LCD_E_1(com)); // Выставить Е reg_74hc595_byte(LCD_E_0(com)); // Сбросить Е _delay_ms(2); // После команды пауза }Данная функция передает команду дисплею.
// Инициализация дисплея void lcd1602_init(char lcd) { switch (lcd) { case 0: lcd = 0xC0; break; case 1: lcd = 0xD0; break; case 2: lcd = 0xE0; break; case 3: lcd = 0xF0; break; } _delay_ms(20); // После включения питания подождать 20 мс lcd1602_com(0x30); // Переход в 4-х битный режим _delay_us(40); lcd1602_com(0x30); // Переход в 4-х битный режим _delay_us(40); lcd1602_com(0x30); // Переход в 4-х битный режим _delay_us(40); lcd1602_com(0x20); // Переход в 4-х битный режим _delay_us(40); lcd1602_com(0x20); // Установка параметров lcd1602_com(0x80); // Установка параметров lcd1602_com(0x00); // Выключаем дисплей lcd1602_com(0x80); // Выключаем дисплей lcd1602_com(0x00); // Очищаем дисплей lcd1602_com(0x10); // Очищаем дисплей lcd1602_com(0x00); // Устанавливаем режим ввода данных lcd1602_com(0x60); // Устанавливаем режим ввода данных lcd1602_com(0x00); // Включаем дисплей с выбранным курсором lcd1602_com(lcd); // Включаем дисплей с выбранным курсором }Данная функция инициализирует дисплей. Собственно она ничего интересного не делает, а лишь передает команду, за командой для переводя дисплея в 4 битный режим. Так же учитывает все временные интервалы. В общем это полная копия алгоритма из документации на дисплей. Одно исключение составляет последняя команда. Она принимает значение переданной функции для определения как будет выглядеть курсор.
// Функция передачи байта дисплею void lcd1602_char_out(char data) { char temp = 0; temp = (data & 0xF0); // Передача старших 4 бит LCD_DATA(temp); // Передача данных reg_74hc595_byte(LCD_E_1(temp)); // Выставить Е reg_74hc595_byte(LCD_E_0(temp)); // Сбросить Е temp = (data << 4); // Передача младших 4 бит LCD_DATA(temp); // Передача данных reg_74hc595_byte(LCD_E_1(temp)); // Выставить Е reg_74hc595_byte(LCD_E_0(temp)); // Сбросить Е _delay_ms(2); // После командная пауза }Эта функция передает байт ЖК дисплею по 4-х битовому закону. Нюансы. Во первых здесь необходимо создать временную переменную, так как мы будем постоянно записывать бит строба с учетом того что для внесения изменений в регистре, необходимо переписывать весь байт. Следующим делом мы записываем во временную переменную передаваемый байт и логически умножаем его на 0xF0, тем самым избавляемся от младших 4-х бит. Затем пихаем его в регистр меняя лишь бит строба. Далее снова записываем во временную переменную передаваемый байт, но теперь уже не умножая,а двигая влево на 4 разряда. Это нужно чтобы мы передали младшие 4 разряда.
// Функция передачи строки void lcd1602_str_out(char *str) { while((*str) != '\0') { lcd1602_char_out(*str); str++; } }Данная функция всего навсего выводит строку на ЖК дисплей. А теперь что в основной функции main().
int main(void) { reg_74hc595_init(); lcd1602_init(0); lcd1602_str_out("WWW.AVRKI.RU"); while(1) { } }А вот и все))) Инициализируем регистр и ЖК дисплей, а затем выводим строку.
Архив с проектом.
В библиотеке AXLIB есть набор функций для работы с дисплеем используя этот регистр как шлюз.
Небольшое кино на данную тему.
JW Player goes here
Игорь 20.04.16 17:13
Наверное надо подключать по SPI, не будет никакой ногодрыг забирать ресурсы процессора, ну и, у SPI скорость может достигать половины тактовой процессора.
Алексей 20.04.16 18:09
А если нужен SPI. Универсальность достигается как раз не привязанностью к ногам. Плюс ногодрыг ресурсы не забирает. Это из темы, что легче, толкать тележку или тянуть за собой.
Игорь 21.04.16 16:04
Возможно Вы правы.Тут https://habrahabr.ru/post/274895/ несколько иной способ реализации и возникает вопрос возможна ли работа в 8 бит режиме.
Алексей 21.04.16 16:51
Можно, но нужно два регистра. Один для управления, а второй для шины данных.
Игорь 21.04.16 17:34
В main_init.h
// 74HC595 | LCD
// ------------------
// | Q2 | RS |
// ------------------
// | Q3 | E |
// ------------------
а работает Q0 Q1
Алексей 21.04.16 18:34
Игорь 22.04.16 10:09
У меня заработало как в статье A0 и A1.
Алексей 22.04.16 10:18
Странно. Но в любом случае скоро выходит генератор кода и там вообще не нужно будет заморачиваться с файлами и настройками.
Игорь 22.04.16 10:21
Если не использовать SPI то может быть использовать другую схему подключения например как эта kazus.ru/articles/470.html. Как думаете какие у нее недостатки. И возможно ли Вашу библиотеку доработать до этой схемы.
Алексей 22.04.16 10:45
Здесь важна аппаратная часть регистра. Если с защелкой такое прокатит, то можно. Но опять же, зачем вешать вывод Е напрямую от мк. Или это влияет на работу регистра? Если да, то это опять шило на мыло, так как и там и тут для управления нужно 3 вывода МК.
seramirun 18.02.19 22:33
А я подключал его к LPT порту иначе.Соединил вместе выводы 11 и 12,затем к ним же припаял подтягивающий резистор(многооборотный очень точный) на 5 килоом(включенный реостатом).Вывод сброса на 5 вольт(нет сброса).Данные в последовательном виде,как обычно, на вывод 14.Вывод 13 как и вывод 14 к соответствующим выводам LPT порта.К этим же выводам подключенны подтягивающие резисторы номиналом 4.7 килоом.Таким образом освободился ещё один вывод LPT порта для других целей за счёт объединения тактирования со сдвигом.