Расширитель GPIO PCA9535 c QIIC

GPIO — это набор входов/выходов общего назначения, с помощью которых микроконтроллер может управлять внешними устройствами и принимать от них сигналы. В одном из первых уроков мы подключали к Ардуино светодиод, и делали это как раз при помощи GPIO.

Ардуино Уно и её более мелкие собратья имеют 20 штук GPIO контактов, которых хватает для большинства DIY проектов. У версии Mega и того больше — 54. А вот у популярной WiFi платы ESP8266, особенно у версии 01S — свободных контактов крайне мало — их вообще всего 4. Как же быть, если нам не хватает контактов для подключения нужной периферии?

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

А ещё для увеличения GPIO существуют специальные микросхемы. На этом уроке мы будет работать с модулем расширения GPIO от RobotClass как раз на одной из таких микросхем — PCA9535.

GPIO расширитель PCA9535 RobotClass ROC

Модуль умеет работать как с напряжением 3,3 Вольт, так и 5 Вольт. Так что его можно использовать в связке с разного рода платами Ардуино, а также с stm32, esp8266, esp32 и другими микроконтроллерами.

В качестве примера, увеличим число GPIO контактов у самой обычной платы Ардуино Уно. Попробуем применить модуль PCA9535 и для передачи сигнала (OUTPUT), и для чтения (INPUT).

Список необходимых компонентов

Все необходимые для урока компоненты можно приобрести у нас в магазине:

В корзину
В корзину
В корзину
В корзину
В корзину
В корзину
В корзину
В корзину
В корзину
В корзину
В корзину

Подключение

На плате имеется 16 GPIO контактов, обозначенные G0 .. G15. Два разъёма QIIC для подключения совместимых с QIIC модулей и микроконтроллеров (подробнее о QIIC). Группа из шести контактов: SDA и SCL, прерывание INT, Земля G, питание V и один не используемый.

Подключим модуль PCA9525 к Ардуино Уно по следующей схеме:

Arduino UnoGnd+5VA4A5
Расширитель GPIOGndVccSDASCL

Чтобы продемонстрировать работу модуля, к ножку с надписью G15 подсоединим светодиод. Разумеется, светодиод подключаем через резистор 200Ом, всё как в том самом уроке про светодиод и Ардуино.

Этой схемы нам вполне хватит для первого теста, можно переходить к программе.

Программа

Самый простой пример — мигание светодиода. Для работы программы нам понадобится библиотека RobotClass_PCA9535. Скачать её можно из github:

https://github.com/robotclass/RobotClass_PCA9535


#include <RobotClass_PCA9535.h>

RobotClass_PCA9535 pca;

void setup() {
    pca.begin(0x27);
    pca.pinMode(15, OUTPUT);
}

void loop() {
    pca.digitalWrite(15, HIGH);
    delay(3000);
    pca.digitalWrite(15, LOW);
    delay(3000);    
}

В самом начале программы, говоря языком объектно-ориентированного программирования — мы создаём экземпляр класса RobotClass_PCA9535 с именем pca.

Затем, в функции setup мы вызываем метод pca.begin с числом 0x27 в качестве единственного аргумента. 0x27 — это адрес устройства в шестнадцатеричной системе счисления(подробности в последней главе «Адресация»).

Как видим, в программе используется знакомая нам функция digitalWrite. Единственное отличие от варианта программы без расширителя — вызов функции нужно делать от имени переменной pca.

Загружаем программу, убеждаемся, что светодиод мигает. Следующий пример использует все 16 контактов расширителя!

Программа для управления 16 светодиодами

Усложним схему и алгоритм. Подключим 16 светодиодов к выводам модуля с номерами от G0 до G15. Пусть теперь светодиоды зажигаются по очереди с G0 до G7, и синхронно в обратном порядке с другого конца: с G15 до G8.

#include <RobotClass_PCA9535.h>

RobotClass_PCA9535 pca;
  
void setup() {
    pca.begin(0x27);

    for(byte i=0; i<16; i++){
        pca.pinMode(i, OUTPUT);
    }
}

void loop() {
    for(int i=0; i<8; i++){
        pca.digitalWrite(i, HIGH);
        pca.digitalWrite(15-i, HIGH);
        delay(30);
        if(i>0){
            pca.digitalWrite(i-1, LOW);
            pca.digitalWrite(15-i+1, LOW);
            delay(30);
        }
    }
    for(int i=7; i>=0; i--){
        pca.digitalWrite(i, HIGH);
        pca.digitalWrite(15-i, HIGH);
        delay(30);
        if(i<7){
            pca.digitalWrite(i+1, LOW);
            pca.digitalWrite(15-i-1, LOW);
            delay(30);
        }
    }
}

Загружаем и наблюдаем волны светодиодного света!

Далее, на примере с тактовыми кнопками научимся считывать сигналы с помощью расширителя.

Считывание сигнала

Работа с сигналами кнопок при помощи GPIO-расширителя также не отличается сложностью. Для чтения сигнала на контакте используем хорошо знакомую функцию digitalRead. Как и в предыдущем примере, для вызова digitalRead на расширителе используем переменную pca.

Светодиод подключим к G15 через резистор 200Ом, а кнопку к G14 по схеме с подтяжкой к земле (подробнее в уроке про кнопки). То есть когда кнопка отжата, контакт G14 будет соединён с землей через резистор 10кОм, когда нажата — напрямую с питанием.

Напишем простую программу, которая будет зажигать светодиод G15 в зависимости от нажатия кнопки на контакте G14 расширителя.

#include <RobotClass_PCA9535.h>

const byte ledPin = 15;
const byte btnPin = 14;

RobotClass_PCA9535 pca;

void setup() {
    pca.begin(0x27); // инициализация модуля с адресом 0x27
    pca.pinMode(ledPin, OUTPUT); // настройка контакта G15 на вывод
    pca.pinMode(btnPin, INPUT); // настройка контакта G14 на ввод
}
void loop() {
    if(pca.digitalRead(btnPin)==HIGH) // если кнопка нажата, то
        pca.digitalWrite(ledPin, HIGH); // зажигаем светодиод
    else // иначе
        pca.digitalWrite(ledPin, LOW); // гасим светодиод
}

Собственно, весь код нам хорошо знаком из уроков про кнопки и Ардуино. Загружаем программу на Ардуино и жмем кнопку — видим свет!

На этом урок можно закончить. Теперь у нас есть мощный инструмент, который может легко победить нехватку GPIO контактов а Ардуино.

Прерывания

Как было указано в самом начале, у модуля есть специальный контакт INT. При смене уровня на контактах расширителя, на INT возникнет импульс, который можно использовать для активации прерывания на микроконтроллере.

Зачем нужны прерывания мы говорили в уроке про прерывания.

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

Пишем программу.

#include <RobotClass_PCA9535.h>

RobotClass_PCA9535 pca;

// к этому контакту Arduino подключен встроенный светодиод
const byte ledPin = 13;

// к этому контакту расширителя подключена кнопка
const byte btnPin = 0;

// к этому контакту Arduino подключен контакт INT расширителя
const byte arduinoIntPin = 3;

// на 3-м контакте Arduino обслуживается прерывание №1
const byte arduinoInterrupt = 1;

// переменная - флаг, которая примет значение true
// при возникновении прерывания
volatile boolean awakenByInterrupt = false;

void setup(){
    // настраиваем режим контакта для прерывания Arduino
    pinMode(arduinoIntPin, INPUT);

    // инициализируем расширитель с адресом 0x27
    pca.begin(0x27);
    // настраиваем режим контакта для кнопки
    pca.pinMode(btnPin, INPUT);

    // настраиваем режим контакта для светодиода
    pinMode(ledPin, OUTPUT);
    // активируем прерывание Arduino под номером arduinoInterrupt
    // с вызовом функции intCallBack
    // в режиме детектирования спада импульса - FALLING
    attachInterrupt(arduinoInterrupt,intCallBack,FALLING);
}

// обработчик прерывания Arduino
void intCallBack(){
    awakenByInterrupt = true;
}

void handleInterrupt(){
    // отключаем прерывание на время пока выполняется обработчик
    detachInterrupt(arduinoInterrupt);

    // мигаем один раз светодиодом
    digitalWrite(ledPin,HIGH);
    delay(100);
    digitalWrite(ledPin,LOW);
    delay(100);

    // ждём в бесконечном цикле, пока кнопку не отпустят
    while( mcp.digitalRead(ledPin) == HIGH );
    // сбрасываем флаг нажатия
    awakenByInterrupt = false;

    // включаем прерывание обратно
    attachInterrupt(arduinoInterrupt,intCallBack,FALLING);
}

void loop(){
    // если прерывание сработало и флаг истина
    if(awakenByInterrupt){
        // делаем, что задумали
        handleInterrupt();
    }
}

Загружаем программу на Ардуино и проводим тест: нажимаем кнопку — в ответ, встроенный светодиод должен мигнуть один раз. Затем отпускаем кнопку — мигает ещё один раз.

Адресация

Этот параграф посвящен случаю, когда требуется подключить сразу несколько расширителей GPIO к одному контроллеру, или адрес 0x27 занят вообще другим устройством.

Почти все устройства с I2C интерфейсом имеют возможность смены адреса. Модуль PCA9535 позволяет менять свой адрес в диапазоне от 0x20 до 0x27. На практике это означает, что мы можем подключить к контроллеру аж восемь модулей с суммарным количеством GPIO = 8*16 = 128 штук!

Как менять адрес? На обратной стороне модуля есть три перемычки: A0, A1 и A2.

GPIO расширитель PCA9535 RobotClass ROC

По-умолчанию, когда все перемычки разомкнуты, адрес устройства равен 0x27 (в двоичном коде 00100111).

Чтобы установить другой адрес, следует замкнуть нужные перемычки. Ниже приведена таблица всех возможных комбинаций перемычек и соответствующие им адреса. 1 — перемычка замкнута, 0 — разомкнута.

Адрес микросхемыA0A1A2Адрес I2C
000 (0)1110x20
001 (1)1100x21
010 (2)1010x22
011 (3)1000x23
100 (4)0110x24
101 (5)0100x25
110 (6)0010x26
111 (7)0000x27


Изменено:

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

Ваш адрес email не будет опубликован. Обязательные поля помечены *

Этот сайт использует Akismet для борьбы со спамом. Узнайте, как обрабатываются ваши данные комментариев.