Tag Archives: i2c scanner

ESP8266 и връзката му с околния свят, I2C накратко

Здравейте,

Ще се опитам накратко да обясня някои основни положения за връзката между микроконтролера с околния свят, с останалите периферни устройства.

Входове / изходи с логическо ниво

Някои от 17-те GPIO порта се използват служебно или имат специално предназначение (връзка с паметта, интерфейс за програмиране и т.н.). Ако на определени портове бъде подадено ниско или високо ниво, контролерът може да не се стартира, така че трябва да се чете и внимава.

НадписПортВходИзходКоментар
D0GPIO16няма прекъсване Няма ШИМ и I2CИма високо ниво при стартиране. Използва се за събуждане от Deep sleep. Не поддържа I2C.
D1 GPIO5Да Да SCL (I2C)
D2 GPIO4Да Да SDA (I2C)
D3 GPIO0към +vcc Да Свързан е към FLASH бутона. Контролерът не стартира ако е свързан към маса.
D4 GPIO2 към +vcc Да Има високо ниво при стартиране. Вграденият светодиод е свързан към него. Контролерът не стартира ако е свързан към маса.
D5 GPIO14 Да Да SPI (SCLK)
D6 GPIO12 Да Да SPI (MISO)
D7 GPIO13 Да Да SPI (MOSI)
D8 GPIO15към маса Да SPI (CS). Контролерът не стартира ако се свърже към +vcc.
RX GPIO3ДаRX изводИма високо ниво при стартиране.
TX GPIO1TX изводДаИма високо ниво при стартиране. Контролерът не стартира ако е свързан към маса.
A0ADC0Аналогов входНе

GPIO6 до GPIO11 са свърани вътрешно към флаш паметта. Не се препоръчва да се ползват.

И така, как да конфигурираме портовете?

Това вече ни е познато от първия пример за мигане на вградения светодиод.

pinMode(port, OUTPUT) - задаваме port да бъде изход
pinMode(port, INPUT) - задаваме port да бъде вход

Протоколът I2C (IIC)

ESP8266 има 17 GPIO – изводи за обща употреба. Всеки от тях може да бъде програмиран като вход или изход, да се ползва I2C, SPI, ШИМ и т.н. Най-често конкретни изводи се ползват за конкретна цел, и ще ги опиша по-долу.

Използването им като обикновени изходи вече го споменах, макар и без подробности.

За онагледяване ще използвам чертеж от статията на electronics hub.

https://www.electronicshub.org/wp-content/uploads/2021/02/NodeMCU-Pinout-Image.jpg

За текущия момент на нас ще ни бъдат интересни GPIO4 и GPIO5, които се използват за I2C протокола.

Когато свързвате някаква периферия, на конекторите е обозначено SCL, SDA, VCC(VIn), GND.

Ако не е указано друго, обикновено:

SCL се свързва към GPIO5 или D1
SDA се свързва към GPIO4 или D2

За удобство и да не бъркам кое къде съм свързал, си измислих една система. За свързване на SCL ползвам цветове, в чието име присъства буквата L – yellow, blue, violet, black. SDA е с цвят, в чието име няма такава буква.

Phillips semiconductors, сега NXP през 1982 година реализират I2C шината с идеята да я използват в техните устройства. Целта е била да се съкрати дължината на използваните медни проводници, както и да се опрости комуникацията между различните интегрални схеми. 2004 година патентът изтича и става общодостъпна.

Връзката се осъществява чрез сериен протокол по 4 проводника. На илюстрацията не са изобразени захранващите проводници.

SDA е съкращение от Serial Data Line, а SCL е съкращение от Serial Clock Line.

Може да има едно или няколко master устройства, както и едно или няколко slave устройства. Всяко от тях притежава собствен 7 битов адрес, чрез който стават обръщенията. От възможните 128 са достъпни 112 адреса. Останалите са резервирани.

Защо ги разправям тия работи? За да може контролерът да комуникира с устройствата трябва да знае адреса им. Докато някои са строго зададени, други може да се различават от общоприетите.

Понякога някой производител на клонирана джаджа решава да ползва собствен вместо оригиналния, от който са копирали.

Понякога се налага да се включат паралелно няколко еднакви I2C устройства, а в такъв случай трябва да се променят адресите им.

Не помня откъде го взех това, но ето пример за pcf8574.

https://ojd.unhidefairness.pw/img/d3ebfbf2c7a41b865033d5e32d88265f.jpg

На живо моята платка изглежда така:

За по-евтинко и по-компактно не се използват джъмпери, а с поялник трябва да се свържат нужните мостчета.

И така. Сдобили сме се с някаква периферия и искаме да я пуснем да работи. В моя случай исках да пусна SSD1306, но с настройките по подразбиране не се получи.

За целта компилираме и качваме i2c_scanner, отваряме терминала и гледаме на кой адрес отговаря джажата.

// --------------------------------------
// i2c_scanner
//
// Version 1
// This program (or code that looks like it)
// can be found in many places.
// For example on the Arduino.cc forum.
// The original author is not know.
// Version 2, Juni 2012, Using Arduino 1.0.1
// Adapted to be as simple as possible by Arduino.cc user Krodal
// Version 3, Feb 26 2013
// V3 by louarnold
// Version 4, March 3, 2013, Using Arduino 1.0.3
// by Arduino.cc user Krodal.
// Changes by louarnold removed.
// Scanning addresses changed from 0…127 to 1…119,
// according to the i2c scanner by Nick Gammon
// https://www.gammon.com.au/forum/?id=10896
// Version 5, March 28, 2013
// As version 4, but address scans now to 127.
// A sensor seems to use address 120.
// Version 6, November 27, 2015.
// Added waiting for the Leonardo serial communication.
//
//
// This sketch tests the standard 7-bit addresses
// Devices with higher bit address might not be seen properly.
//
include
void setup()
{
Wire.begin();
Serial.begin(115200);
while (!Serial); // Leonardo: wait for serial monitor
Serial.println("\nI2C Scanner");
}
void loop()
{
byte error, address;
int nDevices;
Serial.println("Scanning…");
nDevices = 0;
for(address = 1; address < 127; address++ )
{
// The i2c_scanner uses the return value of
// the Write.endTransmisstion to see if
// a device did acknowledge to the address.
Wire.beginTransmission(address);
error = Wire.endTransmission();
if (error == 0) { Serial.print("I2C device found at address 0x"); if (address<16) Serial.print("0"); Serial.print(address,HEX); Serial.println(" !"); nDevices++; } else if (error==4) { Serial.print("Unknown error at address 0x"); if (address<16) Serial.print("0"); Serial.println(address,HEX); }
}
if (nDevices == 0)
Serial.println("No I2C devices found\n");
else
Serial.println("done\n");
delay(5000); // wait 5 seconds for next scan
}

Бинго! Адресът, който ни трябва е 0x3C. Променяме го в примера по-горе, като в моя случай трябва да се промени и стойността на OLED_RESET да стане -1.

Компилираме, качваме кода и …

Свързване на повече от едно устройства на I2C

Много често ще ни се налага да свързваме повече от едно устройство по I2C шината. Например проста метеорологична станция. Какво да правим?

На илюстрацията по-горе се вижда как всичко е свързано паралелно. Съгласно нея свързваме всичките изводи SDA заедно, всичките изводи SCL заедно, масата на маса (0 В), захранването на захранване. Ако не ползваме бредборд ще се наложи да запоим изводите. Иначе е лесно:

Изходи с широчинно импулсна модулация (ШИМ)

Най-общо казано, широчинно импулсната модулация може да се използва за регулиране на аналогови устройства посредством цифров сигнал. Регулиране на яркостта на осветлението, скоростта на въртене на електродвигатели са два примера, често използвани в ежедневието.

Коефициентът на запълване може да се регулира с 10 битова стойност – от 0 до 1023. По подразбиране максималната стойност е 255. Ако искаме да е до 1023 го задаваме предварително с

cont MAX_VALUE = 1023;
analogWriteRange(MAX_VALUE);

И пак за пример ще ползваме вградения светодиод.

void setup() {
pinMode(LED_BUILTIN, OUTPUT);
analogWriteRange(1023);
delay(2e3);
}
void loop() {
for (int i=0; i<1023; i=i+10) {
analogWrite(LED_BUILTIN, i);
delay(50);
}
for (int i=1023; i>-1; i=i-10) {
analogWrite(LED_BUILTIN, i);
delay(50);
}
}

Аналогов вход

Аналоговият вход на ESP8266 приема напрежения от 0 до 1 В. На NodeMCU има сложен резисторен делител и позволеното напрежение е от 0 до 3,3 В. Стойността се чете по следния начин:

analogRead(analogInPin);

За повече подробности около аналоговия вход ще има следваща статия.

Успех!