Увеличение числа входов-выходов через i2c

Иногда возникают ситуации, когда на Arduino уже не хватает пинов ввода-вывода для подключения всего, что душе угодно, или необходимо сосредоточить некоторое количество кнопок\светодиодов на удалении от контроллера. На помощь приходит микросхема PCF8674 - удаленный 8-битный расширитель портов ввода-вывода для шины i2c. Сегодня мы пристально ее рассмотрим, подключим и закодим. Поехали!

 

Шина i2c (произносится как "ай-ту-си") - это последовательный интерфейс для связи микросхем друг с другом. Для передачи данных используются две линии: SDA (данные) и SCL (тактирование). На шине всегда присутствует одно ведущее устройство, которое инициирует все акты передачи данных, и некоторое количество ведомых. Эта шина адресная, и каждое ведомое устройство имеет свой адрес. При инициации чтения или записи ведущим устройством каждое ведомое устройство слушает линию данных, и если узнает свой адрес, то начинает передачу, а остальные при этом замолкают.

Не будем сегодня подробно разбираться с принципом работы шины, остановимся на конкретной микросхеме - PCF8574. Распространены микросхемы от двух производителей: Texas Instruments и NXP. Различий между ними я не нашел (кроме цены) :D

Микросхема PCF8574 имеет:

  1. интерфейс i2c (SDA, SCL), по которой принимает данные от микроконтроллера или отправляет ему данные обратно;
  2. пины задания адреса на шине i2c (A0, A1, A2), которые мы соединяем или с землей или с питанием, обеспечивая индивидуальный адрес для микросхемы, что позволяет подключить к одному контроллеру 8 одинаковых микросхем;
  3. пины ввода-вывода (P0...P7), которые могут выполнять роль входов или выходов, как цифровые пины Arduino;
  4. линию для генерирования прерывания по смене состояния пинов ввода-вывода (INT), которая позволяет информировать контроллер об изменении состояния входов, не дожидаясь, пока контроллер опросит микросхему.

Пример схемы включения PCF8574 для управления разными подсистемами:

Несколько комментариев по поводу схемы:

  1. Лини SDA и SCL требуют подтяжки к питанию через резисторы 10 кОм;
  2. адрес устройства сконфигурирован как 0100000, поскольку пины A0, A1, A2 соединены с землей. О формировании адреса я расскажу чуть позже;
  3. P0, P2 и P3 настроены как выходы;
  4. P1, P4 и P5 настроены как входы;
  5. P6 и P7 не использутся и должны быть настроены как выходы (это требование спецификации).

Возьмем микросхему в корпусе DIP, поскольку для монтажа на беспаечную макетку использовать такой корпус удобнее всего. Посмотрим распиновку микросхемы. Выводов всего 16, они нумеруются с верхнего левого по кругу. Определить, где верх, помогает "ключ" - специальная выемка в корпусе.

Традиционно для таких микросхем питание и земля - это 16-я и 8-я ноги. Пины ввода-вывода по четыре слева и справа. Адресные пины - это 1, 2, 3. Пины 14 и 15 - это шина i2c, а 13-й пин - прерывание.

Давайте начнем сборку нашей суперсхемы с четырьмя входами и четырьмя выходами на макетке. Возьмем Arduino nano и расширитель портов PCF8574, который воткнем в макетку.

 

 Подключим питание с Arduino на макетку на длинные рельсы, чтобы было удобно выполнять подключение всего остального. Подсоединим 16-й пин микросхемы на питание, 8-й пин на землю:

Теперь подключим линии шины i2c. Для этого соединим пины 14 и 15 через рещисторы 10 кОм с питанием, чтобы обеспечить подтяжку, а после этого прицепим их напрямую к пинам A4 и A5 на Arduino:

 Теперь подсоединим четыре светодиода длинными ножками через резисторы к пинам 9, 10, 11, 12 - это будут наши выходы. Короткие ножки светодиодов соединим с землей. Пины 4, 5, 6, 7 соединим с питанием через резисторы 10 кОм, обеспечив подтяжку вверх, после чего подключим к ним кнопки одним концом - это будут наши входы. Кнопки другим концом посадим на землю:

 

 Наша суперсхема готова! Я спаял ее на маленькой макетке, чтобы выглядело аккуратно:

 

 

Время программировать!

Для работы с микросхемой можно использовать готовую библиотеку, заботливо написанную хорошим человеком. Очень удачная, на мой взгляд, вот эта библиотека. Благодаря этой библиотеке "расширенные" пины можно читать и писать точно также, как цифровые пины Arduino. Скачаем ее и установим.

Для продолжения работы нам все таки придется узнать, какой адрес все-таки имеет наша микросхема. Микросхема может маркирована как PCF8574, а может быть как PCF8574A, это влияет на адрес. Взглянем на таблицу:

 Пока ничего не понятно. Чтобы разобраться, сначала определимся, что у нас микросхема с буквой А, а потом рукой напишем на месте L нули, на месте H - единицы, а на месте A2, A1 и A0 напишем те значения, которые мы сами установили (все нули):

Получилось  двоичное число 0111000 или шестнадцатеричное 38. При написании кода принято двоичные числа начинать с префикса 0b, а шестнадцатеричные - с 0x. Соответственно, адрес нашей схемы будет записываться как 0b0111000 или 0x38.

Начнем с подключения библиотеки. Напишем инклуд для самой библиотеки и еще для библиотеки Wire, над которой она надстроена:

1
2
#include <Wire.h>    
#include "PCF8574.h"

Теперь создадим объект класса PCF8574, через который мы будем взаимодействовать с нашей микросхемой:

1
PCF8574 expander;

Если мы будем подключать несколько одинаковых микросхем, мы просто создадим несколько объектов с разными именами, так программа будет понимать, с какой именно мы собираемся общаться.

Внутри setup() инициализируем микросхему, задав ее адрес:

1
expander.begin(0x38);

Настроим "расширенные" пины 0, 1, 2, 3 как входы с подтяжкой:

1
2
3
4
	expander.pinMode(0,INPUT_PULLUP);
	expander.pinMode(1,INPUT_PULLUP);
	expander.pinMode(2,INPUT_PULLUP);
	expander.pinMode(3,INPUT_PULLUP);

Теперь настроим пины 4, 5, 6, 7 как выходы:

1
2
3
4
	expander.pinMode(4,OUTPUT);
	expander.pinMode(5,OUTPUT);
	expander.pinMode(6,OUTPUT);
	expander.pinMode(7,OUTPUT);

Функции pinMode() совпадают с обычными арудиновскими. Единственное, что они вызываются как методы объекта expander. То же самое и с функциями digitalRead() и digitalWrite() - будем их использовать как обычно.

Внутри loop() напишем поочередный опрос кнопок и зажигание светодиодов. Если кнопка не нажата (читается 1), то пишем в соответствующий светодиод LOW, в противном случае пишем HIGH.

1
2
3
4
5
6
	for (int i=0; i<4; i++){
		if (expander.digitalRead(i)>0)
			expander.digitalWrite(i+4, LOW);
		else
			expander.digitalWrite(i+4, HIGH);
	}

В итоге должен получиться вот такой код:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#include <Wire.h>
#include "PCF8574.h"
 
PCF8574 expander;
 
void setup(){
	expander.begin(0x38);
	expander.pinMode(0,INPUT_PULLUP);
	expander.pinMode(1,INPUT_PULLUP);
	expander.pinMode(2,INPUT_PULLUP);
	expander.pinMode(3,INPUT_PULLUP);
	expander.pinMode(4,OUTPUT);
	expander.pinMode(5,OUTPUT);
	expander.pinMode(6,OUTPUT);
	expander.pinMode(7,OUTPUT);
}
 
void loop(){
	for (int i=0; i<4; i++){
		if (expander.digitalRead(i)>0)
			expander.digitalWrite(i+4, LOW);
		else
			expander.digitalWrite(i+4, HIGH);
	}
}

 Вот так весело и просто у нас получился такой удаленный "пульт", который мы можем запрограммировать как хотим:

По нашему опыту, даже при удалении расширителя порта на три метра от Arduino, все прекрасно работает, при условии, что не забыли поставить подтягивающие резисторы на сигнальные линии шины i2c, и провод экранирован.

 

Метки: программирование, Arduino, код, i2c, Texas Instruments, PCF8574

Комментарии   

+1 #1 Илья Дубков 29.04.2017 17:51
Цитирую Владимир:
А почему может не работать на расстоянии 3х метров?

Это из-за помех и ослабления сигнала. С некоторого расстояния начиная, уже не работает.
Цитировать

Добавить комментарий


Защитный код
Обновить