Biblioteka žica za Arduino za rad sa I2C magistralom. I2C teorija, EEPROM iz Atmel Servo i biblioteka koračnih motora

Dobio sam paket iz Kine koji sadrži Atmel EEPROM čip. Koje želim da povežem sa Arduinom. Ali ja zapravo ne želim da koristim gotovu biblioteku, već da je sam shvatim. Stoga će se članak pokazati malo obimnim i dosadnim, pa ćemo ga podijeliti na tri dijela:

  • Teorija I2C interfejsa.
  • EEPROM, opisi mog mikrokola (AT24C256) i veze.
  • Pisanje biblioteke za rad sa memorijom.

Prvi dio, I2C i biblioteka “Wire”.

Protokol serijske komunikacije IIC(takođe se zove I2C- Inter-integrirana kola, inter-chip veza). Razvijen od strane Philips Semiconductors ranih 1980-ih kao jednostavnu 8-bitnu internu komunikacijsku magistralu za kontrolnu elektroniku. Budući da pravo korištenja košta, Atmel pharma ga je nazvala TWI, ali ovo ne mijenja značenje.

Kako radi?

Za prijenos podataka koriste se dvije dvosmjerne podatkovne linije. S.D.A.(Serial Data) sabirnica serijskih podataka i SCL(Serial Clock) sabirnica sata. Obje magistrale su povučene otpornicima na pozitivnu sabirnicu napajanja. Prijenos/prijem signala se vrši pritiskom linije na 0, na jedan instalira se pomoću pull-up otpornika.

Postoji barem jedan glavni uređaj na mreži ( Gospodaru), koji inicira prijenos podataka i generira signale sata i slave ( Slave), koji prenose podatke na zahtjev voditelja. Svaki slave uređaj ima jedinstvenu adresu, koju koristi master za pristup. Naravno, jasno je da je Master naš mikrokontroler, a rob naše pamćenje. Glavni uređaj počinje pritiskati sabirnicu SCL na nulu sa određenom čistoćom, i guma S.D.A. pritisnite ili otpustite za određeni broj kvačica, prolazeći jedan ili nulu. Prijenos podataka počinje sa START signalom, zatim se prenosi 8 bitova podataka i 9. bit Podređeni uređaj potvrđuje prijem bajtova pritiskom na sabirnicu S.D.A. na minus . Prijenos se završava signalom STOP .

Biblioteka "Žica".

Da bi se olakšala razmjena podataka sa uređajima preko I2C magistrale, za Arduino je napisana standardna biblioteka Žica koji već uključeno u IDE komplet. Ima sljedeće glavne funkcije:

Wire.begin(Adresa)pozvan jednom za inicijalizaciju i povezivanje na sabirnicu kao glavni ili slave uređaj. Ako Adresa nije navedena, povezujemo se kao Master uređaj.

Wire.beginTransmission(adresa) počinje prijenos na I2C slave uređaj sa datom adresom.

Wire.endTransmission() prestaje sa slanjem podataka slave-u. Funkcija vraća vrijednost bajta:

  • 0—uspjeh.
  • 1 - podaci su predugački za popunjavanje bafera za prijenos.
  • 2 — NACK primljen prilikom slanja adrese.
  • 3 - NACK primljen tokom prenosa podataka.
  • 4 - ostale greške.

Wire.write() zapisuje podatke sa slave-a kao odgovor na zahtjev od mastera, ili stavlja u red bajtova za prijenos od master-a do slave-a. Veličina pufera 32 bajta a (minus 2 bajta adresa, zapravo 30 bajtova), a funkcija prosljeđuje bafer Wire.endTransmission().

  • Wire.write(vrijednost)- vrijednost: vrijednost koja se prenosi, jedan bajt.
  • Wire.write(string)—string: niz koji se prenosi, niz bajtova.
  • Wire.write(podaci, dužina)- podaci: niz podataka za prijenos, bajtovi. dužina : broj bajtova za prijenos.

Wire.read()Čita bajt koji je prebačen sa podređenog na master ili koji je prenet sa glavnog na podređeni. Bajt povratne vrijednosti: primljen je sljedeći bajt.

Ovo su najosnovnije funkcije biblioteke, ostalo ćemo pogledati kako predstava bude napredovala))

Drugi dio, EEPROM.

EEPROM Programabilna memorija samo za čitanje koja se može izbrisati) - Programabilni ROM (EEPROM), koji se može električno izbrisati, jedan od tipova nepromjenjive memorije (kao što su PROM i EPROM). Ova vrsta memorije može se obrisati i napuniti podacima do milion puta.

Poslali su mi gotov EEPROM modul sa AT24C256 čipom iz Atmela kapaciteta 32 kbajta. Da bismo otkrili ovo čudo, moraćemo da proučimo datasheet koji je veoma dosadan i na engleskom. Tako da ću vam dati gotov rezultat svog mučenja.

karakteristike:

  • Niskonaponsko i standardno napajanje. VCC = 1.7V do 5.5V.
  • 400kHz (1,7V) i 1MHz (2,5V, 2,7V, 5,0V) kompatibilan sa frekvencijom takta.
  • Izdržljivost: 1.000.000 ciklusa pisanja.
  • Interno organizovano kao 32,768 stranica x 8 bita.

zaključci:

  • W.P.— zaštita od pisanja. Ako je pin spojen na GND, tada možete zapisivati ​​podatke u memoriju.
  • A0…A2— pinovi koji određuju adresu uređaja.
  • Vcc- hrana plus.
  • GND- hrana minus.
Adresa memorije:

Definirano sa tri kraka A0..A2. Ako je noga pritisnuta na Gnd tada je vrijednost bita 0, ako je na Vcc onda 1. Mikrokolo koristi osmobitnu adresu, posljednji bit je odgovoran za odabir operacije. Ako je vrijednost bita visoka, tada se inicijalizira operacija čitanja, ako je niska (nula), tada se pokreće operacija pisanja.

Odnosno, ako su sva tri pina pritisnuta na GND i želimo da upišemo u memoriju, adresa uređaja će izgledati kao 10100000 (u biblioteci „Wire“ koristi se 7-bitna adresa, sve pomeramo udesno za jedan bit 01010000 0x50).

Zapisivanje podataka u memoriju:

Za snimanje prvo se okrećemo pamćenju sa bitom Write u adresi. Zatim šaljemo dvije 8-bitne adrese (to jest, imamo adrese 0x8000), zatim bajt podataka i STOP signal. Nakon toga, EEPROM ulazi u interno sinhronizirani ciklus pisanja tWR (Vrijeme ciklusa pisanja 5 ms) u nepromjenjivu memoriju. Svi ulazni signali
onemogućeno tokom ovog ciklusa snimanja, i EEPROM neće odgovoriti, dok se snimanje ne završi.

Kopamo dalje i nalazimo u tablici podataka da je memorija čipa organizirana kao 512 pages by 64 bajtova To jest, možemo odmah upisati do 64 bajta informacija po komandi. Da bismo to učinili, prenosimo svih 64 bajta informacija i tek nakon toga šaljemo STOP signal.

Podaci o čitanju:

Čitanje podataka čini stvari zanimljivijima. Memorija podržava tri opcije čitanja:

  • Pročitajte trenutnu adresu;
  • Čitanje nasumične adrese;
  • Sekvencijalno čitanje;

Memorija pamti posljednju adresu upisivanja dok se napajanje ne isključi, tako da možemo pročitati posljednji bajt bez navođenja adrese.

Za čitanje nasumične adrese moramo početi od početka poslati naredbu za pisanje i proslijedite adresu koju želimo pročitati ( ne zaboravite da se adresa sastoji od dva 8-bitna dijela). Zatim pošaljite naredbu za čitanje i primite traženi bajt, završavajući sve naredbom STOP.

Sekvencijalno čitanje se može izvršiti ili sa trenutne adrese ili sa nasumične adrese i nastavit će se sve dok mikrokontroler ne pošalje STOP signal. Ako se adresa prekorači, memorija će se resetovati i adresiranje će početi od početka.

Pa, vrijeme je da pokušate nešto zapisati:
#include // Uključuje biblioteku #define EEPROM_ADDRESS 0x53 // Postavi riječ memorijske adrese adresa = 0; // Adresa na koju ćemo napisati bajt data_send = 170 ; // Data void setup() ( Wire.begin(); // Inicijaliziraj I2C Serial.begin(9600); Serial.print("Upiši bajt u EEPROM memoriju..."); Serial.println (data_send); Wire. beginTransmission(EEPROM_ADDRESS) // Pokreni prijenos Wire.write(address >> 8); Žica endTransmission() Završi prijenos i provjeri status prijenosa if (status == 0)Serial.println("Pročitaj bajt iz EEPROM memorije..."); Wire.beginTransmission(EEPROM_ADDRESS), prvo pošaljite adresu na kojoj se nalaze Wire.write(address >> 8) (status == 0)Serial ("U redu" // zaustaviti prijenos Wire.requestFrom(EEPROM_ADDRESS, (byte)1 // pošaljite naredbu za čitanje jednog bajta podataka = 0); (podaci = Wire.read(); //čitaj podatke) Serial.println(data, DEC); ) void loop() ( )

Wire.requestFrom(adresa, količina) - Koristi ga master za traženje bajtova od slave-a. Ovi bajtovi se mogu dobiti korištenjem metoda available() i read(). Veličina bafera je istih 32 bajta.

  • adresa : 7-bitna adresa uređaja od kojeg se traže bajtovi;
  • quantity : broj traženih bajtova;

Wire.available()- Vraća broj bajtova dostupnih za preuzimanje pomoću read() .

Pa, evo primjera upisivanja niza "Hello Word" u memoriju:

#include #define EEPROM_ADDRESS 0x53 riječ adresa = 0; char data_send = "Zdravo Word" ; void setup() ( Wire.begin(); // Serial.begin(9600); Serial.print("Upiši bajt u EEPROM memoriju..."); Serial.println (data_send); Wire.beginTransmission(EEPROM_ADDRESS); Wire.write(adresa) Wire.write(adresa & 0xFF status = Wire.endTransmission()); .println("Čitanje bajta iz EEPROM memorije..." Wire.beginTransmission(EEPROM_ADDRESS)(adresa & 0xFF Wire.endTransmission()); "); // zaustavi prijenos Wire.requestFrom(EEPROM_ADDRESS, (byte)10); for (int i=0); i<10 ;i++) { if (Wire.available()) { data = Wire.read(); } Serial.write (data); } } void loop() { }

Organizacija memorije:

Pošto list sa podacima to neodređeno kaže. Pokušao sam to shvatiti u praksi. List sa podacima kaže da je memorija čipa organizovana kao 512 pages by 64 bajtova Šta to znači? Ako želimo da upišemo više od 64 bajta odjednom, recimo na adresi 0x40 (adresa je početak druge stranice), kada adresa prelazi granice stranice Interni brojač čipa će resetovati adresu na početak stranice. A dodatni bajtovi će biti upisani na vrh stranice i obrisati podatke koji su tamo upisani.

Nema takvih ograničenja za čitanje samo kaže da kada dođete do kraja adresa, automatski ćete biti prebačeni na početak (adresa 0x00).

To je sve što mislim. Naravno, možete preuzeti gotovu biblioteku za EEPROM, a možete i napisati svoju. Glavna stvar je da razumijemo osnovni princip rada.

Arduino je standardni mikrokontroler koji je široko priznat od strane inženjera, majstora i nastavnika zbog svoje jednostavnosti, niske cijene i širokog spektra ploča za proširenje. Ploče za proširenje koje se povezuju na glavnu Arduino ploču omogućavaju vam pristup internetu, kontrolu robota i kućnu automatizaciju.

Jednostavne projekte bazirane na Arduinu nije teško implementirati. Ali kako ulazite u teritoriju koja nije obuhvaćena uvodnim tutorijalima i povećavate složenost svojih projekata, brzo ćete naići na problem nedostatka znanja – neprijatelja svih programera.

Ova knjiga je zamišljena kao nastavak bestselera “Programiranje Arduino: Početak rada sa skicama”. Iako ova knjiga ukratko sažima osnove programiranja Arduino, ona će čitatelja upoznati s naprednijim aspektima programiranja Arduino ploča.

Na različitim Arduino modelima, I2C sučelje je povezano na različite pinove. Na primjer, model Uno koristi pinove A4 i A5 - linije SDA i SCL, dok Leonardo model koristi pinove D2 i D3. (SDA i SCL linije su detaljnije opisane u sljedećem odjeljku.) Na oba modela, SDA i SCL linije se također izlaze u blok koji se nalazi pored AREF kontakta (slika 7.3).

U tabeli 7.1 navodi najčešće modele Arduino ploča i pinove koji odgovaraju I2C interfejsu.

Rice. 7.3. I2C pinovi na Arduino Uno ploči

Tabela 7.1. I2C pinovi u različitim Arduino modelima

Model Kontakti Bilješke
Uno A4 (SDA) i A5 (SCL) Kontakti su potpisani SCL i SDA i nalaze se pored AREF kontakta. Ove linije interfejsa se takođe izlaze na pinove A4 i A5
Leonardo D2 (SDA) i D3 (SCL) Kontakti su potpisani SCL i SDA i nalaze se pored AREF kontakta. Ove linije interfejsa se takođe izlaze na pinove D2 i D3
Mega2560 D20 (SDA) i D21 (SCL) -
Due D20 (SDA) i D21 (SCL) Due model ima drugi par I2C pinova označenih SDA1 i SCL1

I2C protokol

Za prijenos i primanje podataka preko I2C sučelja koriste se dvije linije (otuda i drugi naziv - dvožični interfejs, Two Wire Interface). Ove dvije linije se također nazivaju Serial Clock Line (SCL) i Serial Data Line (SDA). Na sl. Slika 7.4 prikazuje vremenski dijagram signala koji se prenosi preko I2C interfejsa.

Rice. 7.4. Vremenski dijagram signala koji se prenosi preko I2C interfejsa

Master generiše takt impuls na SCL liniji, a kada postoje podaci za prijenos, pošiljatelj (master ili slave) dovodi SDA liniju iz trećeg stanja (u digitalni izlazni mod) i šalje podatke u obliku logičkih nula i one u momentima pozitivnih taktnih impulsa signala. Kada se prijenos završi, izlaz takta se može zaustaviti i SDA linija se vraća u treće stanje.

Wire Library

Možete, naravno, sami generirati prethodno opisane impulse kontrolom bitova, odnosno uključivanjem i isključivanjem digitalnih izlaza u softveru. Ali da bi nam olakšao život, Arduino softver uključuje Wire biblioteku koja se brine o svim složenostima sinhronizacije i omogućava nam jednostavno slanje i primanje bajtova podataka.

Da biste koristili Wire biblioteku, prvo je morate uključiti u naredbu

#include

I2C inicijalizacija

U većini slučajeva, Arduino ploča djeluje kao glavni uređaj na bilo kojoj I2C magistrali. Da biste inicijalizirali Arduino kao master, morate izdati naredbu start u funkciji podešavanja, kao što je prikazano u nastavku:

Imajte na umu da, budući da se Arduino ponaša kao master u ovom slučaju, ne mora mu se dodijeliti adresa. Ako je ploča konfigurirana da radi u slave modu, morali bismo dodijeliti adresu u rasponu od 0 do 127, proslijeđujući je kao parametar za jedinstvenu identifikaciju ploče na I2C magistrali.

Slanje podataka od strane mastera

Da biste poslali podatke uređaju na I2C sabirnici, prvo morate izvršiti funkciju beginTransmission i proslijediti joj adresu odredišnog uređaja:

Wire.beginTransmission(4);

Slanje podataka uređajima na I2C sabirnici može se vršiti bajt po bajt ili cijeli niz znakova, kao što je prikazano u sljedeća dva primjera:

Wire.send(123); // prenosimo bajt sa vrijednošću 123

Wire.send("ABC"); // prosljeđivanje niza znakova "ABC"

Kada je prijenos završen, funkciju endTransmission treba pozvati:

Wire.endTransmission();

Prijem podataka od strane mastera

Da biste primili podatke od slave-a, prvo morate odrediti broj bajtova koje očekujete pozivanjem funkcije requestFrom:

Wire.requestFrom(4, 6); // traži 6 bajtova od uređaja s adresom 4

Prvi argument ovoj funkciji je adresa slave uređaja s kojeg master želi primiti podatke, a drugi argument je broj bajtova koje očekuje da primi. Podređeni uređaj može prenijeti manje bajtova, tako da se mora koristiti dostupna funkcija da bi se utvrdilo da li su podaci primljeni i koliko bajtova je stvarno primljeno. Sljedeći primjer (preuzet iz primjera paketa uključenog u Wire biblioteku) pokazuje kako glavni uređaj uzima sve primljene podatke i šalje ih na monitor serijskog porta:

#include

Wire.begin(); // spajanje na i2c sabirnicu (za master

// adresa uređaja nije navedena)

Serial.begin(9600); // inicijalizira monitor serijskog porta

Wire.requestFrom(8, 6); // traži 6 bajtova od slave #8

while (Wire.available()) ( // slave može slati manje

char c = Wire.read(); // prihvatiti bajt kao karakter

Biblioteka Wire automatski baferuje dolazne podatke.

Primjeri korištenja I2C

Svaki I2C uređaj mora imati prateći tehnički opis koji navodi poruke koje podržava. Takvi opisi su neophodni za konstruisanje poruka koje se šalju podređenim uređajima i za tumačenje njihovih odgovora. Međutim, za mnoge I2C uređaje koji se mogu spojiti na Arduino ploču, postoje specijalizirane biblioteke koje omotaju I2C poruke u jednostavne i praktične funkcije. Zapravo, ako morate raditi s nekim uređajem za koji ne postoji specijalizirana biblioteka, objavite vlastitu biblioteku za javnu upotrebu i zaradite si neke karma bodove.

Čak i ako kompletna biblioteka podrške za određeni uređaj nije dostupna, često možete pronaći korisne isječke koda na Internetu koji pokazuju kako se koristi uređaj.

VHF radio prijemnik TEA5767

Prvi primjer, koji pokazuje interakciju sa I2C uređajem, ne koristi biblioteku. Ovdje se razmjenjuju stvarne poruke između Arduina i TEA5767 modula. Ovaj modul se može kupiti online vrlo jeftino, lako se povezuje na Arduino ploču i koristi se kao VHF prijemnik koji kontrolira Arduino.

Najteža faza je povezivanje uređaja. Jastučići su vrlo mali i nalaze se vrlo blizu jedan drugom, tako da mnogi ljudi radije prave ili kupe adapter za spajanje na ploču.

Na sl. Slika 7.5 prikazuje dijagram povezivanja modula na Arduino.

Rice. 7.5. Povezivanje TEA5767 modula na Arduino Uno ploču preko I2C interfejsa

Podaci o modulu TEA5767 mogu se naći na www.sparkfun.com/datasheets/Wireless/General/TEA5767.pdf. Opis sadrži mnogo tehničkih informacija, ali ako pogledate dokument, primijetit ćete dio s detaljnim opisom poruka koje uređaj prepoznaje. U dokumentaciji se navodi da TEA5767 prihvata poruke duge 5 bajtova. Sljedeći je primjer koji radi u potpunosti koji vrši podešavanje frekvencije odmah nakon pokretanja. U praksi je obično potreban nešto drugačiji mehanizam konfiguracije, na primjer, zasnovan na gumbima i displeju s tekućim kristalima.

// sketch_07_01_I2C_TEA5767

#include

setFrequency(93.0); // MHz

void setFrequency(float frekvencija)

frekvencija bajtaH = frekvencijaB >> 8;

Wire.beginTransmission(0x60);

Wire.write(frequencyH);

Wire.write(frequencyL);

Wire.write(0xB0);

Wire.write(0x10);

Wire.write(0x00);

Wire.endTransmission();

Sav kod koji nas zanima u ovom primjeru nalazi se u funkciji setFrequency. Potreban je realan broj - frekvencija u megahercima. Odnosno, ako želite da napravite i isprobate ovaj projekat, saznajte frekvenciju lokalne radio stanice sa dobrim jakim signalom i ubacite njenu vrijednost u setFrequency poziv u funkciji podešavanja.

Da biste pretvorili stvarnu vrijednost frekvencije u dvobajtni prikaz koji se može poslati kao dio petobajtne poruke, mora se izvršiti određena aritmetika. Sljedeći fragment izvodi ove operacije:

unsigned int frekvencijaB = 4 * (frekvencija * 1000000 + 225000) / 32768;

frekvencija bajtaH = frekvencijaB >> 8;

bajt frekvencijaL = frekvencijaB & 0XFF;

Komanda >> pomiče bitove udesno, to jest, operacija >> 8 će pomaknuti visokih 8 bitova prema nižim 8 bitova za 8 binarnih cifara. & operator izvodi operaciju AND po bitu, koja će u ovom slučaju resetovati najznačajnijih 8 bitova i ostaviti samo one najmanje značajne. Za potpuniju raspravu o bitskim operacijama, pogledajte Poglavlje 9.

Ostatak koda u funkciji setFrequency inicira prijenos I2C poruke na slave s adresom 0x60, koja je dodijeljena TEA5767 prijemniku. Zatim se vrši serijski prijenos od 5 bajtova, počevši od frekvencijskog bajta 2.

Nakon čitanja dokumentacije, naučit ćete o mnogim drugim opcijama dostupnim putem različitih poruka, kao što su skeniranje raspona, isključivanje jednog ili dva audio izlazna kanala i odabir mono/stereo načina rada.

U dodatku ćemo se vratiti na ovaj primjer i kreirati Arduino biblioteku kako bismo olakšali rad s TEA5767 modulom.

Komunikacija između dvije Arduino ploče

Drugi primjer koristi dvije Arduino ploče, od kojih jedna djeluje kao I2C master, a druga kao slave. Glavni uređaj će slati poruke podređenom uređaju, koji će ih zauzvrat poslati na monitor serijskog porta tako da možete vizualno provjeriti da kolo radi.

Dijagram povezivanja ploče za ovaj primjer prikazan je na Sl. 7.6. Imajte na umu da TEA5767 modul ima ugrađene pull-up otpornike na I2C linijama. Međutim, u ovom slučaju, kada su dvije Arduinos ploče povezane jedna s drugom, nema takvih otpornika, tako da ćete morati uključiti vlastite otpore nominalne vrijednosti od 4,7 kOhm (slika 7.6).

Rice. 7.6. Povezivanje dvije Arduino ploče preko I2C interfejsa

U ploče se moraju učitati različite skice. Obje skice su uključene u primjere za biblioteku Wire. Program za Arduino master ploču se nalazi u meniju File->Example->Wire->master_writer (File-> Examples->Wire->master_writer), a za slave ploču - u meniju File-> Example-> Wire->slave_receiver (File ->Examples->Wire->slave_receiver).

Nakon programiranja obje ploče, ostavite slave povezan s računalom kako biste vidjeli izlaz sa te ploče na serijskom monitoru i osigurali napajanje Arduino master ploče.

Počnimo sa skicom na glavnoj ploči:

#include

Wire.begin(); // povezivanje na i2c sabirnicu (za glavni uređaj

// adresa nije navedena)

Wire.beginTransmission(4); // inicijalizira prijenos na uređaj #4

Wire.write("x je "); // šalje 5 bajtova

Wire.write(x); // šalje 1 bajt

Wire.endTransmission(); // zaustavljanje prijenosa

Ova skica generiše poruku kao što je x je 1, gde je 1 broj koji se povećava svake pola sekunde. Poruka se zatim šalje na slave adresu 4, kako je definirano u pozivu beginTransmission.

Zadatak slave skice je da primi poruku od glavnog i pošalje je na monitor serijskog porta:

#include

Wire.begin(4); // povezivanje na i2c sabirnicu sa adresom #4

Wire.onReceive(receiveEvent); // registrujemo rukovaoca događaja

Serial.begin(9600); // otvori monitor serijskog porta

// ova funkcija se poziva kad god sa strane mastera

// sljedeći podaci stižu, ova funkcija je registrirana kao rukovalac

// događaji, pogledajte setup()

void receiveEvent(int howMany) (

dok (1< Wire.available()) { // цикл по всем принятым байтам, кроме

// last

Serial.print(c); // ispis simbola

Serial.println(x); // ispisuje cijeli broj

Prva stvar koju treba primijetiti u ovoj skici je da se Wire.begin funkciji prosljeđuje parametar 4. Ovo specificira adresu slave uređaja na I2C magistrali, u ovom slučaju 4. Mora odgovarati adresi koju glavni uređaj koristi za slanje poruke.

SAVJET

Možete povezati mnogo Arduino slave ploča na jednu dvožičnu magistralu, pod uslovom da sve imaju različite I2C adrese.

Slave skica se razlikuje od glavne skice jer koristi prekide za primanje poruka od mastera. Podešavanje rukovaoca porukama vrši se funkcijom onReceive, koja se zove kao rutine prekida (Poglavlje 3). Poziv ove funkcije mora biti postavljen u funkciju podešavanja kako bi se osiguralo da se korisnički definirana funkcija receiveEvent poziva kada se primi bilo koja dolazna poruka.

Funkcija receiveEvent uzima jedan parametar - broj bajtova spremnih za čitanje. U ovom slučaju, ovaj broj se zanemaruje. Dok petlja čita sve dostupne znakove redom i šalje ih na monitor serijskog porta. Jednobajtni broj na kraju poruke se zatim čita i šalje na monitor porta. Korišćenje println umjesto write osigurava da se vrijednost bajta ispisuje kao broj, a ne kao znak sa odgovarajućim numeričkim kodom (slika 7.7).

Rice. 7.7. Izlaz poruka koje prima jedna Arduino ploča od druge preko I2C sučelja na monitor porta

Ploče sa LED indikatorima

Još jedan širok spektar I2C uređaja su različiti tipovi displeja. Najtipičniji predstavnici ovih uređaja su LED matrice i sedmosegmentni indikatori proizvođača Adafruit. Sadrže LED displeje montirane na štampanu ploču i kontrolne čipove koji podržavaju I2C interfejs. Ovo rješenje eliminira potrebu za korištenjem velikog broja I/O pinova na Arduino ploči za kontrolu LED displeja i omogućava vam da se snađete sa samo dva pina, SDA i SCL.

Biblioteke skrivaju sve I2C interakcije iza svoje fasade, omogućavajući upotrebu komandi visokog nivoa, kao što pokazuje sljedeći isječak, uzet iz primjera uključenog u biblioteku:

#include

#include "Adafruit_LEDBackpack.h"

#include "Adafruit_GFX.h"

Adafruit_8x8matrix matrica = Adafruit_8x8matrix();

matrix.begin(0x70);

matrix.drawLine(0, 0, 7, 7, LED_RED);

matrix.writeDisplay();

Sat realnog vremena DS1307

Još jedan uobičajeni I2C uređaj je DS1307 modul sata realnog vremena. Tu je i zgodna i robusna biblioteka za ovaj modul, što olakšava interakciju sa modulom bez potrebe da se bavite stvarnim I2C porukama. Biblioteka se zove RTClib i dostupna je na https://github.com/adafruit/RTClib.

Sljedeći isječci koda su također uzeti iz primjera koji se nalaze u biblioteci:

#include

#include "RTClib.h"

Serial.begin(9600);

if (!RTC.isrunning()) (

Serial.println("RTC NE radi!");

// upisujemo datum i vrijeme kada je skica sastavljena u modul

RTC.adjust(DateTime(__DATE__, __TIME__));

Datum i vrijeme sada = RTC.now();

Serial.print(sada.year(), DEC);

Serial.print("/");

Serial.print(sada.month(), DEC);

Serial.print("/");

Serial.print(sada.day(), DEC);

Serial.print(" (");

Serial.print(daysOfTheWeek);

Serial.print() ");

Serial.print(sada.hour(), DEC);

Serial.print(":");

Serial.print(sada.minute(), DEC);

Serial.print(":");

Serial.print(sada.second(), DEC);

Serial.println();

Ako ste zainteresovani da vidite kako I2C interakcije zapravo funkcionišu, samo pogledajte datoteke biblioteke. Na primjer, izvorni kod biblioteke RTClib pohranjen je u datotekama RTClib.h i RTClib.cpp. Ove datoteke se nalaze u folderu libraries/RTClib.

Na primjer, u datoteci RTClib.cpp možete pronaći definiciju funkcije now:

Datum i vrijeme RTC_DS1307::sada() (

Wire.beginTransmission(DS1307_ADDRESS);

Wire.endTransmission();

Wire.requestFrom(DS1307_ADRESA, 7);

uint8_t ss = bcd2bin(Wire.read() & 0x7F);

uint8_t mm = bcd2bin(Wire.read());

uint8_t hh = bcd2bin(Wire.read());

uint8_t d = bcd2bin(Wire.read());

uint8_t m = bcd2bin(Wire.read());

uint16_t y = bcd2bin(Wire.read()) + 2000;

return DateTime(y, m, d, hh, mm, ss);

Funkcija Wire.read vraća vrijednosti u binarno kodiranom decimalnom (BCD) formatu, tako da se konvertuju u bajtove pomoću funkcije biblioteke bcd2bin.

U BCD formatu, bajt je podijeljen na dva 4-bitna grickanja. Svaki grickalica predstavlja jednu cifru dvocifrenog decimalnog broja. Tako bi broj 37 u BCD formatu bio predstavljen kao 0011 0111. Prva četiri bita odgovaraju decimalnoj vrijednosti 3, a druga četiri bita decimalnoj vrijednosti 7.

Konačno

U ovom poglavlju naučili ste o I2C sučelju i kako ga koristiti za komunikaciju s Arduino pločama i perifernim uređajima i drugim Arduino pločama.

U narednom poglavlju ćemo istražiti drugu vrstu serijskog interfejsa koji se koristi za komunikaciju sa perifernim uređajima. To se zove 1-Wire. Ovo sučelje nije tako široko korišteno kao I2C, ali se koristi u popularnom senzoru temperature DS18B20.

Trebao sam napraviti sat baziran na mikrokolu koje ima I 2 C interfejs. RTC čip, tzv. "sat realnog vremena" PCF8583.

Unutar čipa se nalaze: sat, budilnik, tajmer, kalendar (kriva) i 240 bajtova RAM memorije, gdje možete snimiti bilo koju informaciju koju želite. RAM je vrlo korisna stvar, za razliku od fleš memorije, RAM nema ograničenja u broju ciklusa ponovnog pisanja, a neke podatke i postavke možete spremiti u njega koliko god često želite.

Ali postojao je jedan problem - zaista nisam želio pisati kod, pa sam odlučio da pronađem gotov kod na internetu. Kako se kasnije ispostavilo, bilo je “na vlastitu” da ga pronađete. Skinuo sam primjer rada sa I 2 C, ispravio ga i flešovao mikrokontroler. Nije uspelo. Počeo sam da petljam po kodu, tražeći razlog zašto ne radi... i bio sam užasnut!! U nekim slučajevima, snimanje je obavljeno na cijeli port odjednom, a ne na određene bitove. Dakle, ako na port priključite nešto drugo, na primjer, ekran, onda najvjerovatnije neće raditi. Takođe, čitanje podataka na magistrali je implementirano pogrešno (bez generisanja uslova završetka prijema, ili jednostavno bez NACK). Ali to je pola nevolje. Glavni problem je drugačiji. Često je autor koda postavljao logičku "1" na port, a kao što znamo, I 2 C sabirnica se kontrolira "povlačenjem" SDA i SCL pinova na zajedničku žicu. A logična "1" na sabirnici se zauzvrat formira povlačenjem napajanja na plus sa otpornicima od 4,7 kilo-oma. Dakle, ako je izlaz mikrokontrolera postavljen na logičku "1", a slave uređaj "povuče" ovaj izlaz na zajedničku žicu, tada će rezultirati kratki spoj "bang-bang". Ovo mi se zaista nije dopalo, i odlučio sam da ponovo izmislim svoj točak i napišem sopstvenu biblioteku, odnosno čak 2 biblioteke: jednu za rad sa I 2 C magistralom, a drugu direktno za rad sa PCF8583 u realnom vremenu sat. Da, usput, kod je napisan u .

Da biste I 2 C biblioteku povezali sa projektom, potrebno je da je uključite preko uključivanja, kao na slici, a takođe kopirate biblioteku u fasciklu projekta.

Nakon toga, potrebno je da otvorite datoteku “i2c.h” i navedete pinove mikrokontrolera, koji će se ponašati kao I 2 C sabirnica ). A podešavanje je obavljeno ovdje:

To je to, spojili smo I2C biblioteku, konfigurisali noge i biblioteka je spremna za upotrebu. Primjer upotrebe:

I2c_init(); // Inicijaliziranje I2C sabirnice i2c_start_cond(); //pokreni sabirnicu i2c_send_byte (0xA0); // adresa uređaja koji visi na sabirnici i2c_send_byte (0x10); // bajt podataka koji je upisan u uređaj i2c_send_byte (0x10); // drugi bajt podataka koji upisujemo na uređaj i2c_stop_cond(); // stop gume

Nakon stanja zaustavljanja, možemo provjeriti da li je sve u redu sa I 2 C magistralom. Da bismo to učinili, moramo pročitati varijablu “i2c_frame_error”. Ako je sve normalno, onda će biti 0. Ako jedan od pinova sabirnice nije "povučen" do napajanja, a logički "1" nije instaliran na sabirnici, tada biblioteka generiše grešku i upisuje broj 1 u varijablu “i2c_frame_error” potrebno je pročitati varijablu “i2c_frame_error” nakon stanja zaustavljanja. Na donjoj slici ću pokazati kako funkcionira kontrola grešaka:

Sada povežimo PCF8583 biblioteku sata realnog vremena. Da biste to uradili, morate uraditi iste korake. Kopirajmo datoteku "PCF8583.h" u fasciklu projekta i dodajmo je u uključi, kao na fotografiji:

Spreman. PCF8583 biblioteka sata realnog vremena je povezana. Ne zahtijeva nikakva podešavanja, tako da možete odmah početi čitati vrijeme i datum sa čipa. Imajte na umu da biblioteka PCF8583 radi koristeći I2C biblioteku, tako da ako želimo da radimo sa PCF8583, moramo da povežemo obe biblioteke!

Primjer korištenja biblioteke (vrijeme i datum pisanja i čitanja):

// Inicijaliziranje I2C sabirnice i2c_init(); // Pripremite vrijeme i datum za pisanje u PCF8583 čip PCF_hour=23; // 23 sata PCF_min=59; // 59 minuta PCF_day=31; // 31. dan PCF_month=12; // 12. mjesec - decembar PCF_year=0; // godina (0 - nije prestupna godina) PCF_weekday=6; // 6. dan u sedmici (nedjelja) // Upisuje vrijeme i datum na PCF8583 čip PCF_write_hh_mm_ss(); // Čitanje vremena i datuma sa PCF8583 čipa PCF_read_hh_mm_ss(); Primjer rada sa RAM-om (pisanje i čitanje): // Pripremite 5 bajtova za pisanje u PCF8583 čip PCF_data_ram_1=255; // bajt 1 PCF_data_ram_2=255; // bajt 2 PCF_data_ram_3=255; // bajt 3 PCF_data_ram_4=255; // bajt 4 PCF_data_ram_5=255; // bajt 5 // Upisuje 5 bajtova u PCF8583 čip PCF_write_ram(); // Čitanje 5 bajtova sa PCF8583 čipa PCF_read_ram();

Čitanje iz mikrokola je još lakše - samo pozovite funkcijuPCF_ čitaj_ hh_ mm_ ss() nakon čega će se vrijeme i datum pojaviti u varijablama odakle ih samo preuzimate. Za čitanje RAM-a koristimo funkcijuPCF_ čitaj_ RAM() nakon čega prikupljamo podatke u varijablamaPCF_ podaci_ RAM_ N

Evo liste varijabli, gdje i šta se pohranjuje:

// vrijeme i datum PCF_hour=0; // vrijeme, sati (od 0 do 23, zaštita od prelivanja pri pisanju i čitanju) PCF_min=0; // vrijeme, minute (od 0 do 59, zaštita od prelivanja pri pisanju i čitanju) PCF_sec=0; // vrijeme, sekunde (samo za čitanje, resetiranje na 00 prilikom pisanja) PCF_day=0; // dan (od 1 do 31, zaštita od prelivanja pri pisanju i čitanju) PCF_weekday=0 // dan u nedelji (0-ponedeljak; 6-nedelja, zaštita od prelivanja pri pisanju i čitanju) PCF_month=0; // mjesec (od 1 do 12, zaštita od prelivanja pri pisanju i čitanju) PCF_year=0; // godina (0-skok; 1,2,3-ne-skok, zaštita od prelivanja pri pisanju i čitanju) // RAM PCF_data_ram_1; // Podaci (PCF8583 RAM), bajt 1 PCF_data_ram_2; // Podaci (PCF8583 RAM), bajt 2 PCF_data_ram_3; // Podaci (PCF8583 RAM), bajt 3 PCF_data_ram_4; // Podaci (PCF8583 RAM), bajt 4 PCF_data_ram_5; // Podaci (PCF8583 RAM), bajt 5

Sada ću vam reći o zaštiti od prelijevanja. Recimo da smo zaboravili spojiti mikrokolo. Očitajmo podatke iz mikrokola i... bajt 11111111, odnosno broj 255, bit će očitan. ako mikrokolo nije spojeno. Da bih se zaštitio od takvih slučajeva, u biblioteci PCF8583 napravio sam zaštitu od prelivanja koja osigurava da vam sat ne pokazuje 62 sata 81 minut... Prisustvo prelivanja se može pratiti čitanjem varijable “PCF_overflow”. Ako je 0, onda nije bilo grešaka prekoračenja. Ako sadrži 1 ili više, onda postoje greške prelijevanja. Morate pročitati varijablu “PCF_overflow” nakon funkcije čitanja datuma i vremenaPCF_ čitaj_ hh_ mm_ ss()

Radi jasnoće, priložen je projekat AVR Studio 6 za ATmega32. Možete ponovo kompajlirati za bilo koji AVR. U projekat sam priključio i displej za vizuelnu kontrolu. Kada se uključi napajanje, mikrokontroler postavlja 23 sata i 59 minuta, 31. decembar, nedelja. A minut kasnije postaje 00 sati 00 minuta, 1. januar, ponedjeljak.

Sada ću vam reći zašto sam govorio o "krivom" kalendaru ovog mikrokola. Stvar je u tome što mikrokolo ne zna kako pohraniti tekuću kalendarsku godinu, već samo pohranjuje zastavu prijestupne godine. Ukratko:
0 – prijestupna godina
1 – nije prestupna godina
2 – nije prestupna godina
3 – nije prestupna godina

I tako dalje u ciklusu 0-1-2-3-0-1-2-3-0...

Općenito, da biste napravili normalan kalendar, morate implementirati softversko izračunavanje i spremanje godine, na primjer, u istu PCF8583 RAM memoriju, ali to nije zgodno. A glavna stvar je da ako je kolo bez napona, nažalost, niko neće prepisati memoriju...

Na kraju članka prilažem i kratak video izvještaj. Mogu da kazem da sam pocetnik u programiranju iako programiram vec 3 godine (malo po malo), ne sudite striktno o kodu, ako ima nekih dopuna ili komentara napišite, ispravićemo. Srećna izrada svima!

Spisak radioelemenata

Oznaka Tip Denominacija Količina BilješkaProdavnicaMoja beležnica
MK AVR 8-bit

ATmega32

1 U notes
Sat realnog vremena (RTC)

PCF8583

1 U notes
LCD ekranWH16021

Opis Wire biblioteke

Ova biblioteka vam omogućava interakciju sa I2C / TWI uređajima. Na Arduino pločama sa R3 rasporedom (pinout 1.0), SDA (linija podataka) i SCL (linija sata) nalaze se na pinovima blizu AREF pina. Arduino Due ima dva I2C/TWI interfejsa: SDA1 i SCL1 se nalaze u blizini AREF pina, a dodatne linije se nalaze na pinovima 20 i 21.

Tabela ispod pokazuje gdje se nalaze TWI pinovi na različitim Arduino pločama.

Od Arduino 1.0, ova biblioteka nasljeđuje Stream funkcije, što je čini kompatibilnom s drugim bibliotekama za čitanje/pisanje. Zbog toga su send() i receive() zamijenjeni read() i write() .

Bilješka

Postoje 7 i 8-bitne verzije I2C adresa. 7 bita identifikuje uređaj, a 8. bit određuje da li se uređaj upisuje ili čita. Wire biblioteka koristi 7-bitne adrese. Ako imate tablicu sa podacima ili primjer koda koji koristi 8-bitnu adresu, morate okrenuti najmanji bitni bit (tj. pomaknuti vrijednost za jedan bit udesno), što rezultira adresom od 0 do 127. Međutim, adrese od 0 do 7 se ne koriste, jer su rezervirane, tako da je prva adresa koja se može koristiti je 8. Imajte na umu da su pull-up otpornici potrebni kada povezujete SDA/SCL pinove. Pogledajte primjere za više detalja. MEGA 2560 ploča ima pull-up otpornike na pinovima 20 i 21.

Opis metoda

Wire.begin()

Opis

Inicijalizira Wire biblioteku i povezuje se na I2C sabirnicu kao master ili slave. Obično se treba pozvati samo jednom.

Sintaksa

Wire.begin(adresa)

Opcije

adresa: 7-bitna adresa slave uređaja (opciono); ako nije navedeno, ploča je povezana na sabirnicu kao master.

Povratna vrijednost

Primjer

Za primjere za podređeni uređaj, pogledajte primjere za metode onReceive() i onRequest(). Za primjere za glavni uređaj, pogledajte primjere za druge metode. .

Wire.requestFrom()

Opis

Koristi ga master za traženje bajtova od slave-a. Ovi bajtovi se mogu dobiti korištenjem metoda available() i read().

Ako je ovaj argument istinit, tada requestFrom() šalje STOP poruku nakon zahtjeva, oslobađajući I2C sabirnicu.

Ako je ovaj argument lažan, tada requestFrom() šalje poruku RESTART nakon zahtjeva. Sabirnica nije otpuštena, što sprečava da drugi glavni uređaj prelazi između poruka. Ovo omogućava jednom masteru da pošalje više zahtjeva dok kontrolira sabirnicu.

Sintaksa

Wire.requestFrom(adresa, količina)

Wire.requestFrom(adresa, količina, stajalište)

Opcije

  • adresa: 7-bitna adresa uređaja od kojeg se traže bajtovi;
  • količina: broj traženih bajtova;
  • stop: boolean. true šalje STOP poruku nakon zahtjeva. false šalje poruku RESTART nakon zahtjeva, održavajući vezu aktivnom.
Povratna vrijednost

bajt: broj bajtova vraćenih od podređenog.

Primjer

Wire.beginTransmission()

Opis

Započinje prijenos na I2C slave uređaj sa datom adresom. Nakon toga, redoslijed bajtova za prijenos stavlja se u red pomoću funkcije write(), a oni se prenose korištenjem endTransmission() poziva.

Sintaksa

Wire.beginTransmission(adresa)

Opcije

adresa: 7-bitna adresa uređaja na koji se podaci prenose.

Povratna vrijednost

Primjer

Wire.endTransmission()

Opis

Završava prijenos slave-u koji je pokrenut metodom beginTransmission() i prenosi bajtove koji su stavljeni u red pomoću metode write().

Radi kompatibilnosti sa određenim I2C uređajima, od Arduina 1.0.1, requestFrom() prihvata argument booleovog tipa podataka koji mijenja njegovo ponašanje.

Ako je ovaj argument istinit, tada requestFrom() šalje STOP poruku nakon prijenosa, oslobađajući I2C sabirnicu.

Ako je ovaj argument lažan, tada requestFrom() šalje poruku RESTART nakon prijenosa. Sabirnica nije otpuštena, što sprečava da drugi glavni uređaj prelazi između poruka. Ovo omogućava jednom masteru da pošalje višestruke prijenose dok nadgleda sabirnicu.

Po defaultu, ovaj argument je istinit.

Sintaksa

Wire.endTransmission()

Wire.end Prijenos (stop)

Opcije

stop: boolean. true šalje STOP poruku nakon prijenosa. false šalje poruku RESTART nakon prijenosa, održavajući vezu aktivnom.

Povratna vrijednost

bajt koji ukazuje na status prijenosa:

  • 0: uspjeh;
  • 1: Podaci su predugački za popunjavanje bafera za prijenos;
  • 2: NACK primljen prilikom slanja adrese;
  • 3: NACK primljen prilikom prenosa podataka;
  • 4: druge greške.
Primjer

Pogledajte primjer za metodu write().

Wire.write()

Opis

Zapisuje podatke sa slave-a kao odgovor na zahtjev od mastera ili stavlja u red bajtova za prijenos od master-a do slave-a (između poziva beginTransmission() i endTransmission().

Sintaksa

Wire.write(vrijednost)

Wire.write(string)

Wire.write(podaci, dužina)

Opcije

  • vrijednost: vrijednost za prijenos, jedan bajt.
  • string: niz koji se prenosi, niz bajtova.
  • podaci: niz podataka za prijenos, bajtovi.
  • dužina: broj bajtova za prijenos.
Povratna vrijednost

bajt: write() vraća broj upisanih bajtova, iako čitanje ovog broja nije neophodno.

Primjer #include bajt val = 0; void setup() ( Wire.begin(); // povezivanje na i2c sabirnicu ) void loop() ( Wire.beginTransmission(44); // prijenos na uređaj #44 (0x2c) // adresa uređaja je navedena u Wire-u tehnički opis // pošaljite vrijednost bajta Wire.endTransmission() // povećajte vrijednost if(val == 64) (max.); val = 0 // počinje od početka ) delay(500);

Wire.available()

Opis

Vraća broj bajtova dostupnih za preuzimanje pomoću read() . Ova metoda mora biti pozvana na master nakon poziva requestFrom() ili na slave unutar onReceive() rukovatelja.

Sintaksa

Wire.available()

Opcije

Povratna vrijednost

Broj bajtova dostupnih za čitanje.

Primjer

Pogledajte primjer za metodu read().

Wire.read()

Opis

Čita bajt koji je prebačen sa slave-a na master nakon što je pozvan requestFrom(), ili koji je prenet sa master-a na slave.

Sintaksa

Opcije

Povratna vrijednost

bajt: primljen je sljedeći bajt.

Primjer #include bajt val = 0; void setup() ( Wire.begin(); // povezivanje na i2c sabirnicu (master adresa je opcionalna) Serial.begin(9600); // konfiguriraj serijski port za izlaz ) void loop() ( Wire.requestFrom(2 , 6 // zatražiti 6 bajtova od slave #2 while(Wire.available()) // slave može poslati manje od traženog ( char c = Wire.read(); // prihvatiti bajt kao znak Serial.print (). c // ispis znaka ) delay(500);

Wire.setClock()

Opis

Mijenja frekvenciju sata za komunikaciju sa I2C magistralom. I2C slave nemaju minimalnu radnu frekvenciju takta, ali se obično koristi 100 kHz.

Sintaksa

Wire.setClock(frekvencija sata)

Opcije

clockFrequency: Vrijednost frekvencije (u hercima) signala sata. Prihvaćene vrijednosti su 100000 (standardni način rada) i 400000 (brzi način rada). Neki procesori takođe podržavaju 10000 (režim male brzine), 1000000 (brzi plus režim) i 3400000 (režim velike brzine). Da biste bili sigurni da je traženi režim podržan, pogledajte tehničku dokumentaciju za vaš specifični procesor.

Povratna vrijednost

Wire.onReceive()

Opis

Registrira funkciju koja će biti pozvana kada slave primi prijenos od mastera.

Sintaksa

Wire.onReceive(rukovalac)

Opcije

rukovalac: funkcija koju treba pozvati kada slave uređaj primi podatke; trebao bi uzeti jedan parametar int (broj bajtova pročitanih sa mastera) i ništa ne vratiti, tj.:

void myHandler(int numBytes)

Povratna vrijednost

Primjer

#include void setup() ( Wire.begin(8); // spojite se na i2c sabirnicu s adresom #8 Wire.onReceive(receiveEvent); // registrirajte obrađivač događaja Serial.begin(9600); // konfigurirajte serijski port za output ) void loop() ( delay(100); ) // funkcija koja će se izvršavati svaki put kada se podaci primaju od mastera // ova funkcija je registrirana kao rukovalac događaja, pogledajte setup() void receiveEvent(int howMany) ( dok (1< Wire.available()) // пройтись по всем до последнего { char c = Wire.read(); // принять байт как символ Serial.print(c); // напечатать символ } int x = Wire.read(); // принять байт как целое число Serial.println(x); // напечатать число }

Wire.onRequest()

Opis

Registrira funkciju koja će biti pozvana kada master zatraži podatke od slave-a.

Sintaksa

Wire.onRequest(rukovalac)

Opcije

handler: funkcija koja će biti pozvana, ne uzima nikakve parametre i ne vraća ništa, tj.:

void myHandler()

Povratna vrijednost

Primjer

Kod za Arduino ploču koja radi kao slave:

#include void setup() ( Wire.begin(8); // spajanje na i2c sabirnicu s adresom #8 Wire.onRequest(requestEvent); // registriranje rukovatelja događajem ) void loop() ( delay(100); ) // funkcija, koja će se izvršavati kad god master // zatraži podatke // ova funkcija je registrirana kao rukovalac događaja, pogledajte setup() void requestEvent() ( Wire.write("zdravo"); // odgovorite porukom )