Управление ЖК дисплеем 1602 при помощи сдвигового регистра 74HC595


• О проекте
• Обратная связь
• Полезные ссылки
• Полезные программы
• Друзья сайта


Последние комментарии

Аби: Подключение микроконтроллеров к шине RS-485
написано просто о...

Анатолий: Джойстик для денди на stm32
Автору 100500 рес...




           

Библиотека для AVR





AXLIB Генератор





Помощь сайту


				

Управление ЖК дисплеем 1602 при помощи сдвигового регистра 74HC595

	
	
	

Дата: 27 Октября 2015. Автор: Алексей

	
	
В библиотеке AXLIB есть набор функций для работы с дисплеем используя этот регистр как шлюз.

Помнится как-то я писал про Управление LCD 1602 по шине I2C. Микросхемка PCF8574 конечно интересная штука, да вот нашел я ей альтернативу. Знакомьтесь, сдвиговый регистр с защелкой 74HC595. Чем он хорош. Первое что меня порадовало так это цена. PCF8574 стоит в районе 90-120 рублей, а 74РС595 около 12 рублей. Почти на порядок дешевле. Многие могут возразить, мол данный регистр односторонний. Да, я согласен. но цель данной статьи именно в использовании в одну сторону. То есть управлять ЖК дисплеем кидая ему команды и данные, без чтения из него какой-либо информации. Проще говоря я заменил PCF8574 на 74HC595. Правда для управления регистром нужно выделить 4 вывода МК, но зато можно использовать любые пины и порты, в отличии от PCF8574 которая привязана жестко к I2C (правда я обычно использую программный i2c и мне все равно какой порт и пин.). Второй камень в огород PCF8574 это скорость. 74HC595 на много быстрее чем шина I2C. Ну что же, довольно лирики, переходим к делу. Регистр:

Регистр 74HC595

И по русски)))

Регистр 74HC595


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

У меня все работает. Фото 1 Фото 2

Игорь    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 порта для других целей за счёт объединения тактирования со сдвигом.




Чтобы вставить ссылку используйте форму вида[url]http://www.адрес.ru[/url][text]текст ссылки[/text]
Чтобы вставить код используйте форму вида[code]код[/code]

Имя:   





  







Рейтинг@Mail.ru Яндекс.Метрика