Komunikace s čidlem přes I2C bez použití knihovny ... by LMS

Odpovědět
petan
Příspěvky: 358
Registrován: 23 črc 2017, 10:19
Reputation: 0
Kontaktovat uživatele:

Komunikace s čidlem přes I2C bez použití knihovny ... by LMS

Příspěvek od petan » 28 črc 2017, 22:46

Tento článek vychází z článku Komunikace s čidlem přes I2C bez použití knihovn, jehož jsem autorem. Měl by osvětlit princip komunikace po sběrnici I2C a ověření v praxi komunikací s čidlem intenzity osvětlení. Pokud byste k článku něco měli, můžete napsat sem, nebo přímo na stránku.

Pokud používáte Arduino máte k dispozici nepřeberné množství knihoven. Knihovna k displeji, knihovna k termistoru, knihovna k tlačítku, … Přitom se někdy jedná o poměrně jednoduché kusy kódu. Výhoda použití knihoven je jejich snadné používání – naimportuje se knihovna a pak např. pomocí jednoho řádku kódu čtete požadované parametry z čidla. Problém nastane, pokud máte třeba Arduino Micro, Nano, apod., které má menší paměť. Knihovna musí (měla by být) „blbuvzdorná“, může obsahovat funkce, které nepotřebujete apod. A onen „blbuvzdorný“ a nadbytečný kód samozřejmě zabírá část paměti.

Výhodou může potom také být, že přesně víte jak program funguje. Může se stát, že importujete několik knihoven – každá samostatně funguje, ale pokud je použijete zároveň, některá z nich nemusí fungovat. A nelze jednoduše zjistit proč.

V tomto článku bych chtěl pouze za pomoci knihovny Wire.h (knihovna pro komunikaci I2C) vyčíst data z čidla BH1750, což je čidlo intenzity osvětlení komunikující po sběrnici. Výstup z čidla je přímo intenzita osvětlení v Luxech.

Obrázek

Fyzická vrstva
Díky použití knihovny Wire.h není nutná přesná znalost sběrnice I2C. Jen v krátkosti vysvětlím, že se jedná o dvoudrátovou multi-masterovou sběrnici. Na vodičích SDA (data) a SCL (hodiny) je v klidovém stavu kladné napětí udržované pull-up rezistory. Oba vodiče jsou připojeny k procesoru, který hlídá napěťovou úroveň na vodiči a v případě potřeby (vysílání) je schopný vodič přes tranzistor vyzkratovat proti zemi a tím snížit jeho napětí na 0 V. V případě modulů pro Arduino jsou již potřebné rezistory integrovány přímo na modulech. To by mohl být problém při připojení více modulů ke sběrnici. Mohlo by docházet k přetěžování tranzistoru – obvod by nebyl schopný napěťovou úroveň snížit. Při použití více modulů je tedy dobré vypájet kromě jednoho ze všech modulů pull-up rezistory.

Obrázek

Obrázek

Zařízení na sběrnici jsou adresovaná. Takže master zahájí komunikaci vysláním adresy a potřebných dat. Když se v síti nachází zařízení s požadovanou adresou, tak zařízení odpoví. Adresa zařízení je udaná v datasheetu (někdy si lze zvolit mezi dvěma adresami). Na sběrnici může být až 127 zařízení, některé adresy jsou vyhrazeny pro speciální účely.



Hardware
K otestování čidla bude potřeba Arduino (je v podstatě jedno jaké) a čidlo BH1750 (doporučuji si projít datasheet), jehož cena je cca $1. Arduino s čidlem propojíme pomocí vodičů Vcc (napájení 3 až 5 V; deska obsahuje stabilizátor na 3,3 V), GND (napájení -; ke GND se vztahuje potenciál na datových vodičích), SDA a SCK. Datové vodiče se nekříží! Pull-up rezistory není třeba osazovat, modul je má již integrované – na fotce níže jsou to ty dva 10k (nápis 103).

Obrázek

Software
Ke komunikaci pomocí sběrnice je nutné importovat knihovnu Wire.h. Je to základní knihovna, takže by s ní neměl být problém. Knihovnu je nutné inicializovat. Dále si přidám globální proměnnou (je ji možné použít kdekoliv v programu), do které se bude ukládat intenzita osvětlení v Luxech. Vytvořím si definici pro adresu zařízení.

Adresa zařízení by měla být v datasheetu. U tohoto konkrétního čidla lze adresu změnit přivedením log. 1, nebo log. 0 na vstup ADDR.

Obrázek

Kód: Vybrat vše

//import knihovny pro komunikaci I2C
#include <Wire.h>

#define BH1750_ADDRESS 0x23 //adresa zařízení 23hex = 35dec (možné je použít 0x5C, nebo jiné podle datasheetu)

uint16_t lightLevel; //intenzita osvětlení v Luxech

void setup(void) {
 //Inicializace knihovny
 //Pokud se zadá s parametrem, je zařízení přidělena adresa
 Wire.begin();
}
Dále se vytvoří jedna funkce, jejíž návratová hodnota bude intenzita osvětlení. V té bude nutné začít komunikovat na zadané adrese. Čidlu se odešle konfigurace, počká se, než dojde ke změření intenzity osvětlení. Jak je vidět na obrázku níže, lze čidlo konfigurovat několika příkazy. Lze si vyžádat různé náměry (podle přesnosti se mění doba měření) a lze změnit i čas měření. Instrukce jsou ve dvojkové soustavě, pro přehlednost je dobré si je převést na hexadecimální tvar (lze k tomu použít kalkulačku ve Windows, přepnutou do programátorského režimu). Kód pro jedno přečtení informace v H-Res Mode2 by byl 0×21.

Obrázek

Po změření intenzity osvětlení je nutné si data stáhnout. Čidlo odpovídá dvěma bajty podle obrázku níže.

Obrázek

Je tedy nutné:
- Zahájit komunikaci na zvolené adrese (u tohoto čidla 0×23)
- Odeslat konfiguraci (zde 0×21 pro přečtení kóduv H-Res mode2)
- Ukončit komunikaci
- Počkat, dokud nebudou k dispozici data. Měření trvá cca 120 ms, max. 180 ms (čidlo odpovídá dvěma 8b bajty)
- Přečíst data a převést je na vhodný formát
- Vrátit načtenou intenzitu osvětlení


Ve funkci se definuje šestnáctibitová proměnná result. Ta má ve výchozím nastavení hodnotu 00000000 00000000 (pro přehlednost rozdělena mezerou). Prvním přečtením Wire.read(); se do proměnné uloží první, horní bajt do dolní poloviny. Řekněme, že se načtou data 11001101. Proměnná result bude mít tedy hodnotu 00000000 11001101. Poté následuje bitový posun o 8 pozic vlevo. Výsledek bude vypadat takto: 11001101 00000000. Horní bajt je v horní polovině. Je možné tedy přečíst dolní bajt. Bude přečteno třeba 11100011. Výsledek v proměnné result bude tedy 11001101 11100011. hodnota proměnné je 52707. Ta se nakonec ještě vydělí číslem 1,2 a výsledek bude 43922 (proměnná je typu integer, neukládá se desetinná část). Samotná funkce na čtení z čidla by tedy mohla vypadat takto:

Kód: Vybrat vše

uint16_t GetLightLevel() {
 uint16_t result; //interní proměnná pro měření 16b proměnná 
 
 Wire.beginTransmission(BH1750_ADDRESS); //zahájení kominikace na požadované adrese
 Wire.write(0x21); //odeslání příkazu k měření H-Res Mode2 
 Wire.endTransmission(); //ukončení komunikace 

 Wire.requestFrom(BH1750_ADDRESS, 2); //požadavek na dva bajty od zařízení s adresou BH1750_ADDRESS 

 uint32_t timeout = millis() + 180; //proměnná pro timeout - max. 180 ms 
 while (Wire.available() < 2) { 
  //dokud nejsou přijata data opakuj 
  if ((millis() - timeout) > 0) { 
   //pokud vypršel časový limit ukonči funkci a vrať číslo 0 
   return 0; 
  }
 }

 result = Wire.read(); //přečtení dat (horní bajt) 
 result <<= 8; //bitový posun o 8 bitů vlevo 
 result += Wire.read(); //přičtení dat (dolní bajt 

 result /= 1.2; //vydělení výsledku 1,2 

 return result; //navrácení výsledku 
}
Funkce je hotová. Lze ji tedy kdykoliv zavolat. Lze si samozřejmě měnit adresu a parametr načítání. Ale v praxi ani není potřeba ho často měnit. Tím se ušetří část kódu, který musí v knihovně být. Program pro načítání dat z čidla a jeho odesílání na sériový port by mohl vypadat takto..

Kód: Vybrat vše

//import knihovny pro komunikaci I2C
#include <Wire.h>

#define BH1750_ADDRESS 0x23 //adresa zařízení 23hex = 35dec (možné je použít 0x5C, nebo jiné podle datasheetu)

uint16_t lightLevel; //intenzita osvětlení v Luxech

void setup() {
 //Inicializace knihovny
 //Pokud se zadá s parametrem, je zařízení přidělena adresa
 Wire.begin();

 //komunikace po sériovém portu
 Serial.begin(9600);
}


void loop(){
 lightLevel = GetLightLevel(); //načtení hodnoty
 Serial.print(lightLevel); //vypsání na sériový port

 delay(5000); //zpoždění 5 sekund před dalším načtením
}


uint16_t GetLightLevel() {
 //načtení intenzity osvětlení z čidla BH1750 pouze s knihovnou Wire.h
 uint16_t result; //interní proměnná pro měření 16b proměnná 
 
 Wire.beginTransmission(BH1750_ADDRESS); //zahájení kominikace na požadované adrese
 Wire.write(0x21); //odeslání příkazu k měření H-Res Mode2 
 Wire.endTransmission(); //ukončení komunikace 

 Wire.requestFrom(BH1750_ADDRESS, 2); //požadavek na dva bajty od zařízení s adresou BH1750_ADDRESS 

 uint32_t timeout = millis() + 180; //proměnná pro timeout - max. 180 ms 
 while (Wire.available() < 2) { 
  //dokud nejsou přijata data opakuj 
  if ((millis() - timeout) > 0) { 
   //pokud vypršel časový limit ukonči funkci a vrať číslo 0 
   return 0; 
  }
 }

 result = Wire.read(); //přečtení dat (horní bajt) 
 result <<= 8; //bitový posun o 8 bitů vlevo 
 result += Wire.read(); //přičtení dat (dolní bajt 

 result /= 1.2; //vydělení výsledku 1,2 

 return result; //navrácení výsledku 
}
Komunikace s jinými čidly je v podstatě stejná. Podle datasheetu se zjistí jaké příkazy je nutné odesílat a v jakém formátu data chodí. Pokud jde o větší čísla, nebo čísla s desetinnými místy je nutné je převést. Doporučuji si projít článek o proměnných, abyste věděli jaké číslo lze do jaké proměnné vložit. Také bude dobré si přečíst článek o bitovém posunu.

Odpovědět

Kdo je online

Uživatelé prohlížející si toto fórum: Žádní registrovaní uživatelé a 7 hostů