Komunikace s čidlem přes I2C bez použití knihovny ... by LMS
Napsal: 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.

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.


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).

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.

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.

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

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:
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..
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.
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.

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.


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).

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.

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();
}

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

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
}
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
}