Подключение LCD1602 + PCF8574 к ATmega8

Обсуждаем контроллеры компании Atmel.
Ответить
Denis-1307
Родился
Сообщения: 12
Зарегистрирован: Сб сен 28, 2024 16:19:04

Подключение LCD1602 + PCF8574 к ATmega8

Сообщение Denis-1307 »

Доброго сем времени суток!
Решил разобраться с подключением ЖК дисплея (winstar 1602) с расширителем портов PCF8574 к микроконтроллеру ATmega8.
Собрал вот такую схему в протеусе. Один диспей подключен напрямую к ножкам контроллера (я на него выводил значение регистра статуса модуля TWI). Второй дисплей подключен через PCF8574.
СпойлерИзображение
Написал вот такой код в AtmelStudio
Спойлер

Код: Выделить всё

#define F_CPU 1000000UL

#include <avr/io.h>
#include <util/delay.h>
#include <stdlib.h>

//пользовательские определения
#define RS_1 PORTD |= (1<<2); //передача данных
#define RS_0 PORTD &= ~(1<<2); //передача команд
#define E_1 PORTD |= (1<<3); //установка высокого потенциала на разрешающем пине Е
#define E_0 PORTD &= ~(1<<3); //установка низкого потенциала на разрешающем пине Е

//переменные
unsigned char TWI_Status = 0;

//прототипы функций
void LCD_pin_setup(void); //функция настройки пинов подлючения ЖК дисплея
void LCD_commands(unsigned char command); //функция передачи команд
void LCD_data(unsigned char data); //функция передачи данных
void LCD_settings(void); //функция настройки ЖК дисплея
void set_cursor(unsigned char y, unsigned char x); //функция установки курсора в строке и на заданной позиции этой строки
void print_string(char words[]); //функция вывода строк на дисплей

//прототипы функций для ЖК дисплея с модулем TWI
void TWI_Settings(void); //функция настроек модуля TWI
void TWI_Start(void); //функция формирования условия "Старт" для модуля TWI
void TWI_Stop(void); //функция формирования условия "Стоп" для модуля TWI
void TWI_Write_byte(unsigned char data); //функция передачи байта по шине TWI
void LCD_TWI_command(unsigned char data); //функция передачи команд ЖК дисплею по интерфейсу TWI
void LCD_TWI_data(unsigned char data); //функция передачи данных ЖК дисплею по интерфейсу TWI
void LCD_TWI_settings(void); //функция настройки ЖК дисплея подключенного к микроконтроллеру по TWI (I2C) модулю

int main(void)
{
    LCD_pin_setup();
	LCD_settings();
	TWI_Settings();
	
	TWI_Start(); //отправка условия Старт
 	TWI_Write_byte(0b01001110); //отправка адреса микросхемы PCF + бит записи
	LCD_TWI_settings();
	TWI_Stop();
	
	TWI_Start(); //отправка условия Старт
	TWI_Write_byte(0b01001110); //отправка адреса микросхемы PCF + бит записи
	LCD_TWI_data('A');
	LCD_TWI_data('B');
	LCD_TWI_data('C');
	TWI_Stop();
		
	TWI_Status = TWSR; //значение регистра статуса модуля TWI
	
	set_cursor(1,0); //установка курсора в позицию
	print_string("TWI status - "); //печать строки
	char str[16];
	itoa(TWI_Status, str, 16); //преобразование значения переменной статуса TWI в символьный массив
	print_string(str); //печать значения переменной статуса TWI
	
	set_cursor(2,0); //установка курсора в позицию
	LCD_data('A');
	LCD_data('B');
	LCD_data('C');
    while (1) 
    {
    }
}

//============== функции для ЖК дисплея подключенного к микроконтроллеру через модуль TWI (I2C) 
void TWI_Settings(void) //функция настроек модуля TWI
{
	TWBR = 2;
	TWSR &= ~((1<<TWPS1) | (1<<TWPS0));
}

void TWI_Start(void) //функция формирования условия "Старт" для модуля TWI
{
	TWCR = (1<<TWINT) | (1<<TWSTA) | (1<<TWEN);
	while(!(TWCR & (1<<TWINT)))
	;
}

void TWI_Stop(void) //функция формирования условия "Стоп" для модуля TWI
{
	TWCR = (1<<TWINT) | (1<<TWSTO) | (1<<TWEN);
}

void TWI_Write_byte(unsigned char data) //функция записи байта по шине TWI
{
	TWDR = data;
	TWCR = (1<<TWINT) | (1<<TWEN);
	while(!(TWCR & (1<<TWINT)))
	;
}

void LCD_TWI_command(unsigned char data) //функция передачи команд ЖК дисплею по интерфейсу TWI
{
	unsigned char val;
	val = (data | (1<<3) | (1<<2)) & ~(1<<0); //установка 3-го бита, включение подсветки дисплея 
	TWI_Write_byte(val);
	_delay_us(50);
	val &= ~(1<<2);
	TWI_Write_byte(val);
	_delay_us(50);
	
	val = ((data << 4) | (1<<3) | (1<<2)) & ~(1<<0); //установка 3-го бита, включение подсветки дисплея 
	TWI_Write_byte(val);
	_delay_us(50);
	val &= ~(1<<2);
	TWI_Write_byte(val);
	_delay_us(50);
}

void LCD_TWI_data(unsigned char data) //функция передачи данных ЖК дисплею по интерфейсу TWI
{
	unsigned char val;
	val = data | (1<<3) | (1<<2) | (1<<0); //установка 3-го бита, включение подсветки дисплея 
	TWI_Write_byte(val);
	_delay_us(50);
	val &= ~(1<<2);
	TWI_Write_byte(val);
	_delay_us(50);
	
	val = (data << 4) | (1<<3) | (1<<2) | (1<<0); //установка 3-го бита, включение подсветки дисплея 
	TWI_Write_byte(val);
	_delay_us(50);
	val &= ~(1<<2);
	TWI_Write_byte(val);
	_delay_us(50);
}

void LCD_TWI_settings(void) //функция настройки ЖК дисплея подключенного к микроконтроллеру по TWI (I2C) модулю
{
	_delay_ms(15);
	LCD_TWI_command(0b00110000);
	_delay_ms(5);
	LCD_TWI_command(0b00110000);
	_delay_us(110);
	LCD_TWI_command(0b00110000);
	_delay_ms(1);
	LCD_TWI_command(0b00000010);
	_delay_ms(2);
	LCD_TWI_command(0b00101000); //четырех битный интерфейс, дисплей двух строчный, размер шрифта 5*8
	_delay_ms(1);
	LCD_TWI_command(0b00001101); //включение дисплея, выключение курсора, курсор моргает (младший бит - 1 моргает, 0 не мограет)
	_delay_ms(1);
	LCD_TWI_command(0b00000001); //очистка дисплея
	_delay_ms(2);
	LCD_TWI_command(0b00000110); //инкрементирование и сдвиг курсора в право
	_delay_ms(1);
}

//============== функции для ЖК дисплея подключенного напрямую к микроконтроллеру =================
void LCD_pin_setup(void) //функция настройки пинов подлючения ЖК дисплея
{
	DDRD |= (1<<7) | (1<<6) | (1<<5) | (1<<4) | (1<<3) | (1<<2);
	PORTD &= ~((1<<7) | (1<<6) | (1<<5) | (1<<4) | (1<<3) | (1<<2));
}

void LCD_commands(unsigned char command) //функция передачи команд
{
	PORTD = command;
	RS_0; //низкий потенциал на выводе RS
	E_1; //высокий потенциал на выводе Е
	_delay_us(50);
	E_0; //низкий потенциал на выводе Е
	_delay_us(50);
	PORTD = (command << 4);
	RS_0; //низкий потенциал на выводе RS
	E_1; //высокий потенциал на выводе Е
	_delay_us(50);
	E_0; //низкий потенциал на выводе Е
	_delay_us(50);
}

void LCD_data(unsigned char data) //функция передачи данных
{
	PORTD = data;
	RS_1; //высокий потенциал на выводе RS
	E_1; //высокий потенциал на выводе Е
	_delay_us(50);
	E_0; //низкий потенциал на выводе Е
	_delay_us(50);
	PORTD = (data << 4);
	RS_1; //высокий потенциал на выводе RS
	E_1; //высокий потенциал на выводе Е
	_delay_us(50);
	E_0; //низкий потенциал на выводе Е
	_delay_us(50);
}

void LCD_settings(void) //функция настройки ЖК дисплея
{
	_delay_ms(15);
	LCD_commands(0b00110000);
	_delay_ms(5);
	LCD_commands(0b00110000);
	_delay_us(110);
	LCD_commands(0b00110000);
	_delay_ms(1);
	LCD_commands(0b00000010);
	_delay_ms(2);
	LCD_commands(0b00101000); //четырех битный интерфейс, дисплей двух строчный, размер шрифта 5*8
	_delay_ms(1);
	LCD_commands(0b00001100); //включение дисплея, выключение курсора, курсор не моргает
	_delay_ms(1);
	LCD_commands(0b00000001); //очистка дисплея
	_delay_ms(2);
	LCD_commands(0b00000110); //инкрементирование и сдвиг курсора в право
	_delay_ms(1);
}

void set_cursor(unsigned char y, unsigned char x) //функция установки курсора в строке (параметр y = 1 или 2) и на заданной позиции этой строки (параметр х от 0 до 15)
{
	switch(y)
	{
		case 1: LCD_commands(x | 0b10000000);
		break;
		case 2: LCD_commands((x + 0x40) | 0b10000000);
		break;
	}
}

void print_string(char words[]) //функция вывода строк на дисплей
{
	unsigned char z;
	for (z = 0; words[z] != '\0'; z++)
	{
		LCD_data(words[z]);
	}
}
И при запуске симуляции в протеусе получаю вот такой результат.
СпойлерИзображение
На дисплее подключенном на прямую к микроконтроллеру все отображается как надо, а на дисплее подключенном по TWI пустота и курсор смещенный на четыре символа в право. Хотя отправлялись три одиночных символа.
Если отправлять одиночный символ, то один дисплей покажет то что было отправлено, а второй совсем другой символ.
СпойлерИзображение
Уважаемые коты, помогите разобраться, что я мог сделать не так. Сильно тапками не пинайте, в программировании не очень силен.
Заранее всем спасибо за оказанную помощь!

P.S.
В железе работает так же как и в протеусе.

Прилагаю архив проекта в AtmelStudio 7 и проекта в протеусе
LCD display.zip
(38.25 КБ) 130 скачиваний
LCD.zip
(15.12 КБ) 114 скачиваний
veso74
Поставщик валерьянки для Кота
Сообщения: 1903
Зарегистрирован: Сб май 05, 2012 20:24:52
Откуда: KN34PC, Болгария
Контактная информация:

Re: Подключение LCD1602 + PCF8574 к ATmega8

Сообщение veso74 »

строка 98:

Код: Выделить всё

val = (data | (1<<3) | (1<<2)) & ~(1<<0); //установка 3-го бита, включение подсветки дисплея 
в

Код: Выделить всё

val = ((data & 0b11110000) | (1<<3) | (1<<2)) & ~(1<<0); //установка 3-го бита, включение подсветки дисплея
Denis-1307
Родился
Сообщения: 12
Зарегистрирован: Сб сен 28, 2024 16:19:04

Re: Подключение LCD1602 + PCF8574 к ATmega8

Сообщение Denis-1307 »

[uquote="veso74",url="/forum/viewtopic.php?p=4719685#p4719685"]строка 98:

Код: Выделить всё

val = (data | (1<<3) | (1<<2)) & ~(1<<0); //установка 3-го бита, включение подсветки дисплея 
в

Код: Выделить всё

val = ((data & 0b11110000) | (1<<3) | (1<<2)) & ~(1<<0); //установка 3-го бита, включение подсветки дисплея
[/uquote]

Спасибо, заработало.
На реальном железе на дисплее все отображается нормально. Различные символы, знаки, все отображается так как надо.
Протеус нормально отображает лишь "A, a, 1 и !". При отправке других символов, знаков на дисплее полная ерунда.
Главное что в железе все работает как надо.

И еще вопрос.
А можно поподробнее объяснить, зачем с байтом передаваемой команды надо делать вот это: data & 0b11110000 ?

Update
Сделал вот так: data & 0b11110000 и при передаче байта данных, и в протеусе все начало отображаться нормально.
Теперь еще болле стало интересно для чего необходимо так делать?
veso74
Поставщик валерьянки для Кота
Сообщения: 1903
Зарегистрирован: Сб май 05, 2012 20:24:52
Откуда: KN34PC, Болгария
Контактная информация:

Re: Подключение LCD1602 + PCF8574 к ATmega8

Сообщение veso74 »

См. "битовая маска" в булевой алгебры. Значение бит 1 произошло от значения data, а этого не должно быть.
Обычно проще сбросить все биты на ноль (которые следуют за изменением) с побитово И (&),
а затем добавить новые значения с помощью побитово ИЛИ (|).
(частично пользуюсь переводчиком)
Аватара пользователя
Starichok51
Модератор
Сообщения: 19039
Зарегистрирован: Сб авг 14, 2010 15:05:51
Откуда: г. Озерск, Челябинская обл.

Re: Подключение LCD1602 + PCF8574 к ATmega8

Сообщение Starichok51 »

Denis-1307 писал(а):Теперь еще болле стало интересно для чего необходимо так делать?
потому что байт передается за 2 раза - по 4 старших бита.
поэтому нужно сначала сбросить 4 младших бита, а потом к байту добавить нужные биты управления.
а когда передаешь 4 младших бита, то сначала байт сдвигается влево 4 раза (чтобы младшие биты заняли позицию старших), и младшие биты после сдвигов освобождаются сами.
Мудрость приходит вместе с импотенцией...
Когда на русском форуме переходят на Вы, в реальной жизни начинают бить морду.
Denis-1307
Родился
Сообщения: 12
Зарегистрирован: Сб сен 28, 2024 16:19:04

Re: Подключение LCD1602 + PCF8574 к ATmega8

Сообщение Denis-1307 »

Спасибо, понял.
Ответить

Вернуться в «AVR»