Рассмотрим 3 основных варианта добавления портов на микроконтроллерах или одноплатных компьютерах:
Микросхема MCP23017
Микросхема MCP23017 добавляет 16 портов,которые можно настроить как на вход,так и на выход.
Микросхема использует популярную двухпроводную шину I2C. Имеет очень гибкие настройк.
Микросхемы MCP23017 можно нарасщивать до 8 штук на одной шине I2C ,например используя 3 провода (+питание) можно их разводить по квартире для уменьшения количества проводов к датчикам и исполнительным механизмам.Каждой микросхеме задается адрес через выводы А0,А1,А2 в двоичном виде , подключив их к минусу или плюсу.Цена одной микросхемы MCP23017 - 1.5$.
Существует так же вариант микросхемы MCP23S17 ,которая использует шину SPI .
Работа MCP23017 в Arduino
У плат Arduino имются отдельные выводы SCL и SDA ,но например плат,основанных на ATMEGA328P устройства подключаются к аналоговым входам А5 (SCL) и А4(SDA) .Шины SCL и SDA должны быть "подтянуты" через резисторы 4.7KoM к плюсу.
.Назначение выводов определяется скетчем.
Библиотека для работы MCP23017 с Arduino: MCP23x17.zip
Для некоторых случаев может быть выгоднее использовать MCP23017 без использования библиотек,смотрите пример ниже:
// Arduino code ,используется банк А на вход,а банк В - на вывод. // pins 15,16 и 17 к GND для I2C шины адреса 0x20 #include "Wire.h" // подключаем библиотеку I2C byte a=0; boolean registers[8]; // храним состояние регистров банка В void setup() { Serial.begin(9600); Wire.begin(); // wake up I2C bus // настраиваем банк B на вывод Wire.beginTransmission(0x20);// подключение к чипу Wire.write((byte)0x01); // выбираем банк B Wire.write((byte)0x00); // установка банка B на вывод,точнее устанавливаем на всех выводах значение 0 Wire.endTransmission(); // отключаемся. // } void loop() { // чтение банки A Wire.beginTransmission(0x20); // подключение к чипу Wire.write(0x12); // выбираем для работы банку A Wire.endTransmission(); // отключаемся. Wire.requestFrom(0x20, 1); // отправляем один байт a=Wire.read(); // чтение состояния портов банка А в переменную 'a' // выводим состояние портов банка А,если на них подан минус. if (!((a>>0)&1)) Serial.print(0); if (!((a>>0)&2)) Serial.print(1); if (!((a>>0)&4)) Serial.print(2); if (!((a>>0)&8)) Serial.print(3); if (!((a>>0)&16)) Serial.print(4); if (!((a>>0)&32)) Serial.print(5); if (!((a>>0)&64)) Serial.print(6); if (!((a>>0)&128)) Serial.print(7); // Serial.print(a); Serial.println("--"); // тест мигалка на выводе банки В порта номер 3 mcWrite(3,0); delay(500); mcWrite(3,1); delay(500); } // подпрограмма записи в банк B void mcWrite(int whichPin, int whichState) { byte d=0; byte l=1; registers[whichPin] = whichState; for(int i = 0; i <= 7; i++){ d=registers[i]*l+d; l=l*2; } //запись в банк B Wire.beginTransmission(0x20); // подключение к чипу Wire.write(0x13); // выбираем для работы банку B Wire.write(d); // запись байта Wire.endTransmission(); // отключаемся }
Работа MCP23017 с Raspberry PI
Кроме Raspberry PI микросхему MCP23O17 можно подключить к любому компютеру через переходник I2C-USB ,например через этот.Подробнее про это тут.
Микросхема регистр 74HC595
Регистр 74HC595 дает возможность добавить 8 портов на выход,но при этом занимает 3 цифровых выхода.
Микросхемы 74HC595 так же можно наращивать для увеличения выводов и при этом дополнительные выводы микропроцессора не занимаются.Цена одной микросхемы 74HC595 - 0.20$.
int SER_Pin = 9; //pin 14 (DS) на 75HC595 int RCLK_Pin = 8; //pin 12 (ST_CP) на 75HC595 int SRCLK_Pin = 7; //pin 11 (SH_CP) на 75HC595 //количество регистров #define number_of_74hc595s 2 //не трогать ! #define numOfRegisterPins number_of_74hc595s * 8 boolean registers[numOfRegisterPins]; // void setup(){ pinMode(SER_Pin, OUTPUT); pinMode(RCLK_Pin, OUTPUT); pinMode(SRCLK_Pin, OUTPUT); } void registerWrite(int whichPin, int whichState) { registers[whichPin] = whichState; digitalWrite(RCLK_Pin, LOW); for(int i = numOfRegisterPins - 1; i >= 0; i--){ digitalWrite(SRCLK_Pin, LOW); int val = registers[i]; digitalWrite(SER_Pin, val); digitalWrite(SRCLK_Pin, HIGH); } digitalWrite(RCLK_Pin, HIGH); }
Пример подпрограммы для использования 2 регистров,изменение статуса вызывается через registerWrite(номер_порта, LOW);
// #include <SPI.h> #define SS_HC595 45 // ST_CP(12) // SH_CP (11) на SCK (13 uno,52 mega) // DS(14) на MOSI (11 uno,51 mega) //SER (10) на минус или при каскаде-с предыдущего регистра //количество регистров 1 или 2 #define number_of_74hc595s 1 //не трогать ! #define numOfRegisterPins number_of_74hc595s * 8 boolean registers[numOfRegisterPins]; void setup() { // Serial.begin(9600); SPI.begin(); pinMode(SS_HC595, OUTPUT); // pinMode(13, OUTPUT); } void loop() { // digitalWrite(13, HIGH); registerWrite(4,1); delay(1000); // digitalWrite(13, LOW); registerWrite(4,0); delay(1000); } void registerWrite(byte whichPin, byte whichState) { // int d=0; //для 2 регистров byte d=0; byte l=1; registers[whichPin] = whichState; for(byte i = 0; i <= 7; i++){ d=registers[i]*l+d; l=l*2; } digitalWrite(SS_HC595, HIGH); SPI.transfer(d); //для 2 регистров: // SPI.transfer(highByte(d)); // SPI.transfer(lowByte(d)); digitalWrite(SS_HC595, LOW); }
Микросхема регистр 74HC165
Регистр 74HC165 дает возможность добавить 8 портов на вход,но при этом занимает 3 цифровых выхода.
Микросхемы 74HC165 так же можно наращивать для увеличения входов и при этом дополнительные выводы микропроцессора не занимаются.Цена одной микросхемы 74HC165 - 0.35$.
// #define latch_SH_in 44 // SH(1) #define data_SH_in 50 // QH (9) #define clock_SH_in_out 52// CLK(2) //CLK INH(15) на минус //SER (10) на минус или при каскаде-с предыдущего регистра void setup() { Serial.begin(9600); pinMode(latch_SH_in, OUTPUT); pinMode(data_SH_in, INPUT); pinMode(clock_SH_in_out, OUTPUT); } void loop() { uint8_t a = 0; //int,если 2 регистра. digitalWrite(latch_SH_in, HIGH); for (int i=7; i>=0; i--) { // i=15,если 2 регистра digitalWrite(clock_SH_in_out,LOW); delayMicroseconds(1); a |= (digitalRead(data_SH_in) ? (1<<i) : 0); digitalWrite(clock_SH_in_out,HIGH); delayMicroseconds(1); } digitalWrite(latch_SH_in, LOW); Serial.print("<"); Serial.print(a, BIN); Serial.print(">"); if (!((a>>0)&1)) Serial.print(0); if (!((a>>0)&2)) Serial.print(1); if (!((a>>0)&4)) Serial.print(2); if (!((a>>0)&8)) Serial.print(3); if (!((a>>0)&16)) Serial.print(4); if (!((a>>0)&32)) Serial.print(5); if (!((a>>0)&64)) Serial.print(6); if (!((a>>0)&128)) Serial.print(7); // выдаст номера портов,которые подключены на GND. Serial.println("--"); delay(1000); }
// #include <SPI.h> #define SS_HC165 8 // SH(1) // QH (9) на SCK (13 uno,52 mega) // CLK(2) на MISO (12 uno,50 mega) //CLK INH(15) на минус //SER (10) на минус или при каскаде-с предыдущего регистра void setup() { Serial.begin(9600); SPI.begin(); pinMode(SS_HC165, OUTPUT); } void loop() { digitalWrite(SS_HC165, HIGH); uint8_t a = SPI.transfer(0); digitalWrite(SS_HC165, LOW); Serial.print(a,BIN); Serial.print(">"); if (!((a>>0)&1)) Serial.print(0); if (!((a>>0)&2)) Serial.print(1); if (!((a>>0)&4)) Serial.print(2); if (!((a>>0)&8)) Serial.print(3); if (!((a>>0)&16)) Serial.print(4); if (!((a>>0)&32)) Serial.print(5); if (!((a>>0)&64)) Serial.print(6); if (!((a>>0)&128)) Serial.print(7); // выдаст номера портов,которые подключены на GND. Serial.println("--"); delay(1000); }
74HC165 и 74HC595 при использовании вместе могут использовать общий пин синхронизации.В итоге при подключении к м/к в сумме используют 5 выводов.
При использовании варианта с SPI на шине могут находится другие устройства,например Ethernet .В итоге используется дополнительно всего 1 вывод на регистр для выбора канала,а SCK,MISO,MOSI - общие.
Микросхема PCF8574
Микросхема PCF8574.Так же используется шина I2C,но выводов всего 8.
Примерная цена 1.15$.
#include <Wire.h> #define expander B0100000 // адрес на шине i2c void setup() { Wire.begin(); Serial.begin(9600); } void loop() { Serial.println("Writing B00000000."); expanderWrite(B00000000); Serial.print("Read: "); Serial.println(expanderRead(), BIN); delay(1000); Serial.println("Writing B11111111."); expanderWrite(B11111111); Serial.print("Read: "); Serial.println(expanderRead(), BIN); delay(1000); } void expanderWrite(byte _data ) { Wire.beginTransmission(expander); Wire.write(_data); Wire.endTransmission(); } byte expanderRead() { byte _data; Wire.requestFrom(expander, 1); if(Wire.available()) { _data = Wire.read(); } return _data; }
Данный скетч не тестировался !!
Примеры реализации расширения портов можно посмотреть тут .