PCF8574 содержит 8-битный порт ввода-вывода общего назначения, чтение или запись данных осуществляется любым микроконтроллером или другим устройством по шине I2C. Расширитель имеет низкий потребляемый ток и выходы с регистром-защелкой с высокими характеристиками по току для прямой передачи сигнала на светодиоды и т.п. Также в устройстве есть линия прерывания (INT), которая может быть подключена к логике прерывания микроконтроллера. Посылая сигнал прерывания по этой линии, дистанционный ввод - вывод сообщает микроконтроллеру о поступающих на его порты данных, без необходимости поддерживать связь через I2C-шину. Это значит, что PCF8574 может оставаться простым "подчиненным" устройством.
Структурная схема PCF8574
Далее разберемся о программных способах работы с данной микросхемой. Для правильной адресации всех устройств на шине I2C существуют slave-адреса, у PCF8574 они такие:
Есть две разновидности микросхемы PCF8574 — с буквой «А» и без буквы. Отличаются они только четырьмя старшими битами slave-адреса. Таким образом, если на одной шине будет присутствовать 8 микросхем PCF8574 и 8 микросхем PCF8574A, конфликта это не вызовет. Биты А2 - А0 задаются с помощью внешних выводов микросхем.
Запись в порт осуществляется по схеме, представленной на рисунке ниже. «Запись» в данном случае означает, что данные с шины I2C появятся на параллельном порте Р0 - Р7.
Запись данных в PCF8574
Обратите внимание: данные появляются на выходе порта спустя время t после возникновения сигнала АСК (а также записи в порт). Микроконтроллер должен успеть считать предыдущие данные на линиях Р0 - Р7 до появления следующего байта данных.
Чтение с порта происходит по схеме, показанной на рисунке ниже. Напоминаем, что микросхема по-прежнему остается в режиме slave-устройства, то есть сигнал SCL генерируется master-устройством.
Чтение данных из PCF8574
Чтение данных с порта происходит в момент появления сигнала АСК. В промежутках между сигналами АСК данные менять нет смысла, поскольку они будут потеряны. При приеме последнего байта отсылаем условие NACK, таким образом говоря slave-устройству, что отсылать данные уже не надо.
Практический пример
Далее заставим микроконтроллер Attiny45 управлять LCD дисплеем и одновременно обрабатывать данные от 8-ми кнопочной клавиатуры. На схеме видно что к одной PCF8574 подключен LCD 16x2, к другой подключены 8 кнопок, в то же время расширители между собой и контроллером образуют шину I2C. Обязательно ставим подтягивающие резисторы на линии SCL и SDA. Контроллер Attiny45 работает на частоте 8МГц, делитель на 8 не включаем.
Алгоритм программы такой. В этом примере я использовал библиотеку для работы с программной шиной 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 403 | Скачать |
Комментарии
А не подскажете, lcd_gotoxy() - как в Вашем примере задается?
Спасибо!
Командой lcd_com(0x80);
в данном случае символ выводится в первое знакоместо первой строки
Спасибо!
Спасибо, исправлю ошибку, на дисплее должен быть вывод VCC вместо VSS
void i2c_start(void)
{
DDR_I2C |= _BV(SDA);
PORT_I2C |= _BV(SDA);
...
}
(файл I2C.c). Такая подтяжка к плюсу питания на шине вообще запрещена - на шине устройствами образуется монтажное И с подтяжкой на землю через открытые транзисторы. Это чревато повреждением или порта МК или устройств на шине. Поэтому применяется пассивная подтяжка к плюсу через резисторы. На что указывает предупреждение в Протеус об логических конфликтах на шине. Всегда надо просматривать все предупреждения.