PCF8574 содержит 8-битный порт ввода-вывода общего назначения, чтение или запись данных осуществляется любым микроконтроллером или другим устройством по шине I2C. Расширитель имеет низкий потребляемый ток и выходы с регистром-защелкой с высокими характеристиками по току для прямой передачи сигнала на светодиоды и т.п. Также в устройстве есть линия прерывания (INT), которая может быть подключена к логике прерывания микроконтроллера. Посылая сигнал прерывания по этой линии, дистанционный ввод - вывод сообщает микроконтроллеру о поступающих на его порты данных, без необходимости поддерживать связь через I2C-шину. Это значит, что PCF8574 может оставаться простым "подчиненным" устройством.

Структурная схема PCF8574

Структурная схема PCF8574

Далее разберемся о программных способах работы с данной микросхемой. Для правильной адресации всех устройств на шине I2C существуют slave-адреса, у PCF8574 они такие:

Есть две разновидности микросхемы PCF8574 — с буквой «А» и без буквы. Отличаются они только четырьмя старшими битами slave-адреса. Таким образом, если на одной шине будет присутствовать 8 микросхем PCF8574 и 8 микросхем PCF8574A, конфликта это не вызовет. Биты А2 - А0 задаются с помощью внешних выводов микросхем.

Запись в порт осуществляется по схеме, представленной на рисунке ниже. «Запись» в данном случае означает, что данные с шины I2C появятся на параллельном порте Р0 - Р7.

Запись данных в PCF8574

Обратите внимание: данные появляются на выходе порта спустя время t после возникновения сигнала АСК (а также записи в порт). Микроконтроллер должен успеть считать предыдущие данные на линиях Р0 - Р7 до появления следующего байта данных.

Запись данных в PCF8574

Чтение с порта происходит по схеме, показанной на рисунке ниже. Напоминаем, что микросхема по-прежнему остается в режиме slave-устройства, то есть сигнал SCL генерируется master-устройством.

Чтение данных из PCF8574

Чтение данных с порта происходит в момент появления сигнала АСК. В промежутках между сигналами АСК данные менять нет смысла, поскольку они будут потеряны. При приеме последнего байта отсылаем условие NACK, таким образом говоря slave-устройству, что отсылать данные уже не надо.

Чтение данных из PCF8574

Практический пример

Далее заставим микроконтроллер Attiny45 управлять LCD дисплеем и одновременно обрабатывать данные от 8-ми кнопочной клавиатуры. На схеме видно что к одной PCF8574 подключен LCD 16x2, к другой подключены 8 кнопок, в то же время расширители между собой и контроллером образуют шину I2C. Обязательно ставим подтягивающие резисторы на линии SCL и SDA. Контроллер Attiny45 работает на частоте 8МГц, делитель на 8 не включаем.

PCF8574 - расширитель портов ввода/вывода

Алгоритм программы такой. В этом примере я использовал библиотеку для работы с программной шиной I2C, ее преимущество в том, что протокол I2C можно организовать на любых выводах любого контроллера. А недостатком такого решения является постоянная загрузка памяти контроллера, а также любое прерывание может прервать обмен информацией по шине. Но можно использовать и аппаратный I2C. В бесконечном цикле постоянно опрашивается клавиатура, т.е. идет чтение IC3, если кнопки не нажаты высвечивается надпись "NO KEY". Если нажата одна из 8-ми кнопок высвечивается "KEY #1" и т.д. Обратите внимание что на расширителях установлены разные slave-адреса.

// PCF8574 - расширитель портов ввода/вывода, подключение к AVR 
#include <avr/io.h>
#include <util/delay.h>

#include "I2C.h"

#define RS	(1 << 0)
#define E	(1 << 2)

unsigned char lcd_bufer, ack, key;

// Функция записи в PCF8574
void PCF8574_write(unsigned char data)
{
  do
  {
  ack = 0;
  i2c_start();              //  Условие старт
  i2c_write(0x40);          //  Запись адреса в PCF8574
  ack |= i2c_ack();         //  Подтверждение от ведомого 
  i2c_write(data);          //  Запись данных в PCF8574
  ack |= i2c_ack();         //  Подтверждение от ведомого 
  i2c_stop();               //  Условие стоп
  }
  while(ack);
}

// Функция чтения из PCF8574
unsigned char PCF8574_read(void)
{
unsigned char data;
  do
  {
  ack = 0;
  i2c_start();              //  Условие старт
  i2c_write(0x43);          //  Запись адреса в PCF8574
  ack |= i2c_ack();         //  Подтверждение от ведомого 
  data = i2c_read();        //  Чтение данных из PCF8574
  i2c_nack();               //  Неподтверждение от ведомого 
  i2c_stop();               //  Условие стоп
  }
  while(ack);
  return data;
}

// Функция передачи команды в LCD	
void lcd_com(unsigned char command)
{	
lcd_bufer = command & 0xF0;
lcd_bufer &= ~RS;
    
lcd_bufer |= E;
PCF8574_write(lcd_bufer);
lcd_bufer &= ~E;
PCF8574_write(lcd_bufer);

_delay_us(100);
	
lcd_bufer = (command & 0x0F)<<4;
lcd_bufer &= ~RS;

lcd_bufer |= E;
PCF8574_write(lcd_bufer);
lcd_bufer &= ~E;
PCF8574_write(lcd_bufer);

//	if(value & 0b11111100)
//	_delay_us(100);
//	else _delay_ms(2);
} 

// Функция передачи данных в LCD
void lcd_data(unsigned char data)
{
lcd_bufer = (data & 0xF0);
lcd_bufer |= RS;

lcd_bufer |= E;
PCF8574_write(lcd_bufer);
lcd_bufer &= ~E;
PCF8574_write(lcd_bufer);

_delay_us(100);
	
lcd_bufer = (data & 0x0F)<<4;
lcd_bufer |= RS;

lcd_bufer |= E;
PCF8574_write(lcd_bufer);
lcd_bufer &= ~E;
PCF8574_write(lcd_bufer);

_delay_ms(2);
}

// Функция вывода строки на LCD
void lcd_string(char* data, char nBytes)
{
if (!data) return;
for(unsigned char i=0; i < nBytes; i++)
{
lcd_data(data[i]);
}
}

// Функция инициализации LCD
void lcd_init(void)
{
PCF8574_write(0b00000000);
_delay_ms(40);
                // 3 x 0x03h, 8bit
lcd_bufer = 0b00110000;
PCF8574_write(lcd_bufer);

lcd_bufer |= E;
PCF8574_write(lcd_bufer);
lcd_bufer &= ~E;
PCF8574_write(lcd_bufer);

_delay_ms(5); // delay > 4,1ms

lcd_bufer |= E;
PCF8574_write(lcd_bufer);
lcd_bufer &= ~E;
PCF8574_write(lcd_bufer);

_delay_us(100);

lcd_bufer |= E;
PCF8574_write(lcd_bufer);
lcd_bufer &= ~E;
PCF8574_write(lcd_bufer);

_delay_us(100);
                           // 4bit - 0x02h
lcd_bufer = 0b00100000;
PCF8574_write(lcd_bufer);

lcd_bufer |= E;
PCF8574_write(lcd_bufer);
lcd_bufer &= ~E;
PCF8574_write(lcd_bufer);

_delay_us(100);

lcd_com(0x28); // шина 4 бит, LCD - 2 строки
lcd_com(0x08); // полное выключение дисплея
lcd_com(0x01); // очистка дисплея
_delay_us(100);
lcd_com(0x06); // сдвиг курсора вправо
lcd_com(0x0C); // включение дисплея, курсор не видим
}

int main(void)
{
i2c_init(); // Инициализация шины I2C
lcd_init(); // Инициализация дисплея

while(1)
{
  key = PCF8574_read();
  
  if(key == 0b11111110)
  {
  lcd_com(0x80);
  lcd_string("KEY #1", 6);
  }
  else if(key == 0b11111101)
  {
  lcd_com(0x80);
  lcd_string("KEY #2", 6);
  }
  else if(key == 0b11111011)
  {
  lcd_com(0x80);
  lcd_string("KEY #3", 6);
  }
  else if(key == 0b11110111)
  {
  lcd_com(0x80);
  lcd_string("KEY #4", 6);
  }
  else
  {
  lcd_com(0x80);
  lcd_string("KEY NO", 6);
  }
  _delay_ms(10);
}
}


Архив для статьи "PCF8574 - расширитель портов ввода/вывода"
Описание: Проект AVRStudio4 и Proteus
Размер файла: 58.05 KB Количество загрузок: 2 353 Скачать

Комментарии  

0 #1 80N60 30.01.2015 03:54
Здравствуйте!
А не подскажете, lcd_gotoxy() - как в Вашем примере задается?
Спасибо!
Сообщить модератору
0 #2 AntonChip 30.01.2015 10:24
Цитирую 80N60:
Здравствуйте!
А не подскажете, lcd_gotoxy() - как в Вашем примере задается?
Спасибо!

Командой lcd_com(0x80);
в данном случае символ выводится в первое знакоместо первой строки
Сообщить модератору
0 #3 80N60 03.02.2015 21:37
А не подскажете как определить другие знакоместа?
Спасибо!
Сообщить модератору
0 #4 AntonChip 03.02.2015 22:31
Цитирую 80N60:
А не подскажете как определить другие знакоместа?
Спасибо!

Сообщить модератору
0 #5 Alexfire 22.02.2015 20:23
Здравствуйте. Возникла ошибка при компиляции в AS6.2 "undefined reference" в функциях записи и чтения. например такая: undefined reference to `i2c_start'. Подскажите, где копать?
Сообщить модератору
0 #6 Alexfire 22.02.2015 22:44
А ларчик просто открывался! Импорт из AVR Studio 4.....
Сообщить модератору
0 #7 Dmitriy_FireBear 04.02.2017 08:00
А ничего, что на фото принципиальной схемы ошиблись в наименовании ноги питания дисплея - на VSS подали + или это VDD?
Сообщить модератору
0 #8 AntonChip 04.02.2017 12:22
Цитирую Dmitriy_FireBear:
А ничего, что на фото принципиальной схемы ошиблись в наименовании ноги питания дисплея - на VSS подали + или это VDD?

Спасибо, исправлю ошибку, на дисплее должен быть вывод VCC вместо VSS
Сообщить модератору
0 #9 mikar 31.05.2020 05:12
Здравствуйте! Смотрим функцию

void i2c_start(void)
{
DDR_I2C |= _BV(SDA);
PORT_I2C |= _BV(SDA);
...
}
(файл I2C.c). Такая подтяжка к плюсу питания на шине вообще запрещена - на шине устройствами образуется монтажное И с подтяжкой на землю через открытые транзисторы. Это чревато повреждением или порта МК или устройств на шине. Поэтому применяется пассивная подтяжка к плюсу через резисторы. На что указывает предупреждение в Протеус об логических конфликтах на шине. Всегда надо просматривать все предупреждения.
Сообщить модератору
+1 #10 AntonChip 02.06.2020 20:11
Здравствуйте, mikar, вы правы, при управлении шиной стоит задействовать только регистр DDR
Сообщить модератору