Viidates Arduino Uno, Mega2560, Leonardo jms tahvlitele:
- Kuidas SPI töötab?
- Kui kiire on SPI?
- Kuidas ühendada ülema ja orja vahel?
- Kuidas teha SPI-alamat?
Pange tähele: see on mõeldud võrdlusküsimusena.
Viidates Arduino Uno, Mega2560, Leonardo jms tahvlitele:
Pange tähele: see on mõeldud võrdlusküsimusena.
Serial Peripheral Interface Bus (SPI) liidest kasutatakse suhtlemiseks mitme seadme vahel lühikese vahemaa tagant ja suurel kiirusel.
Tavaliselt on olemas üks "põhiseade", mis käivitab side ja varustab kella, mis kontrollib andmeedastuskiirust. Orje võib olla üks või mitu. Rohkem kui ühe orja puhul on igaühel oma "alluvvaliku" signaal, mida kirjeldatakse hiljem.
Täispuhutavas SPI-süsteemis on teil neli signaaliliinid:
Kui mitu orjad on ühendatud MISO signaaliga, mida nad eeldatavasti kolmeseisundiks hoiavad (hoides kõrgel impedantsil), et MISO liin, kuni nad on valitud Slave Selecti poolt kinnitades. Tavaliselt on orjavalimine (SS) selle kinnitamiseks madal. See tähendab, et see on aktiivne madal. Kui konkreetne ori on valitud, peaks see konfigureerima väljundina MISO-liini, et see saaks masterile andmeid saata.
See pilt näitab, kuidas andmeid ühe baidi saatmisel vahetatakse:
Pange tähele, et kolm signaali väljastatakse põhiseadmest (MOSI, SCK, SS) ja üks on sisend (MISO).
Sündmuste jada on järgmine:
SS
läheb selle kinnitamiseks ja orja aktiveerimiseks madalaks SCK
näitab, millal tuleks andmeridadest proovivõtteid teha SCK
-st (kasutades vaikefaasi faasi) SCK
taga servas (kasutades vaikefaasi faasi), muutes MISO
/ MOSI
vajadusel SS
tõuseb selle kinnitamiseks kõrgeks Pange tähele, et:
Kuna andmeid saadetakse ja võetakse vastu samal kellapulsil, pole orjal võimalik masterile kohe vastata. SPI-protokollid eeldavad tavaliselt, et kapten küsib andmeid ühe edastuse kohta ja saab vastuse järgmisele.
Kasutades Arduinos SPI-teeki, näeb ühe ülekande tegemine koodis välja selline:
bait väljuv = 0xAB; bait sissetulev = SPI.transfer (väljaminev);
Ainult saatmise näide (sissetulevate andmete ignoreerimine):
#include <SPI.h>void setup (void) {digitalWrite (SS, HIGH); // tagada, et SS püsib kõrgel SPI.begin (); } // seadistuse lõppvältida silmus (tühine) {bait c; // lubage Slave Select digitalWrite (SS, LOW); SS on tihvt 10 // saadab testistringi (const char * p = "Fab"; c = * p; p ++) SPI.transfer (c); // keelata Slave Select digitalWrite (SS, HIGH); viivitus (100); } // tsükli lõpp
Eespool nimetatud koodi (mis saadab ainult) võidakse kasutada väljundi jadavahetuse juhtimiseks Registreeri. Need on ainult väljundiseadmed, seega ei pea me sissetulevate andmete pärast muretsema. Nende puhul võib SS-nööpnõela nimi olla "pood" või "riiv".
Selle näiteks on seerianumbrite register 74HC595 ja mitmed LED-ribad, et vaid paari mainida. Näiteks see 64-piksline LED-ekraan, mida juhib kiip MAX7219:
Sel juhul näete, et tahvli valmistaja on kasutanud veidi erinevad signaalinimed:
Enamik tahvleid järgib sarnast mustrit. Mõnikord on DIN lihtsalt DI (Data In).
Siin on veel üks näide, seekord seitsme segmendiga LED-ekraaniplaat (põhineb ka kiibil MAX7219):
See kasutab täpselt samu signaalinimesid nagu teine tahvel. Mõlemal juhul näete, et plaadil on selle jaoks vaja ainult viit juhtmest, kolm SPI jaoks, lisaks toide ja maandus.
on neli viisi, kuidas saate proovida SPI-kella.
SPI-protokoll võimaldab varieerida kellaimpulsside polaarsust. CPOL on kella polaarsus ja CPHA on kella faas.
Neid illustreerib see graafika:
Faaside ja polaarsuse õigeks muutmiseks peate oma seadme andmelehele viitama. Tavaliselt on olemas diagramm, mis näitab, kuidas kella proovida. Näiteks kiibi 74HC595 andmelehelt:
Nagu näete, on kell tavaliselt madal (CPOL = 0) ja selle näidis võetakse esiservast (CPHA = 0), seega on see SPI režiim 0.
Kella saab muuta polaarsus ja faas sellises koodis (valige muidugi ainult üks):
SPI.setDataMode (SPI_MODE0); SPI.setDataMode (SPI_MODE1); SPI.setDataMode (SPI_MODE2); SPI.setDataMode (SPI_MODE3);
See meetod on Arduino IDE versioonides 1.6.0 ja vanem. Viimaste versioonide korral muudate kellarežiimi kõnes SPI.beginTransaction
järgmiselt:
SPI.beginTransaction (SPISettings (2000000, MSBFIRST, SPI_MODE0)); // 2 MHz kell, kõigepealt MSB, režiim 0
Vaikimisi on kõigepealt kõige olulisem bitt, kuid võite riistvarale öelda töötlege kõigepealt kõige vähem olulist bitti järgmiselt:
SPI.setBitOrder (LSBFIRST); // vähim oluline bitt firstSPI.setBitOrder (MSBFIRST); // kõige olulisem bitt kõigepealt
Jällegi on see aegunud Arduino IDE versioonides 1.6.0 ja edasi. Viimaste versioonide puhul muudate SPI.beginTransaction
kõnes bitijärjestust järgmiselt:
SPI.beginTransaction (SPISettings (1000000, LSBFIRST, SPI_MODE2)); // 1 MHz kell, kõigepealt LSB, režiim 2
SPI vaikeseade on kasutada süsteemi taktsagedust jagatuna neljaga, see on on üks SPI kella impulss iga 250 ns järel, eeldades 16 MHz protsessori kella. Kellajaoturit saate muuta, kasutades setClockDivider
järgmist:
SPI.setClockDivider (divider);
Kus jaotaja on üks järgmistest:
Kiireim kiirus on "jaga 2-ga" või üks SPI kella impulss iga 125 ns järel, eeldades 16 MHz protsessori kella. Seetõttu kuluks ühe baidi edastamiseks 8 * 125 ns või 1 µs.
See meetod on Arduino IDE versioonides 1.6.0 ja vanem. Viimaste versioonide puhul muudate ülekandekiirust kõnes SPI.beginTransaction
järgmiselt:
SPI.beginTransaction (SPISettings (4000000, MSBFIRST, SPI_MODE0)); // 4 MHz kell, kõigepealt MSB, režiim 0
Kuid empiiriline testimine näitab, et baitide vahel peab olema kaks kellaimpulssi, seega on maksimaalne baitide taktsagedus Igaüks 1,125 µs (kellajaoturiga 2).
Kokkuvõtvalt võib iga baidi saata maksimaalselt ühe 1,125 µs kohta (16 MHz kellaga), andes teoreetilise maksimaalse edastuskiiruse 1 / 1,125 µs ehk 888 888 baiti sekundis (välja arvatud üldkulud, nagu SS-i madal seadmine jne).
Ühendamine digitaalsete tihvtide 10–13 kaudu:
Ühendamine ICSP päise kaudu:
Ühendamine digitaalsete tihvtide 50 kuni 52 kaudu:
Võite kasutada ka ICSP päist, sarnaselt ülaltoodud Unole.
Leonardo ja Micro ei paljasta SPI-tihvte digitaalsete tihvtide jaoks, erinevalt Uno ja Megast. Teie ainus võimalus on kasutada ICSP päise nööpnõelu, nagu Uno ülaltoodud on illustreeritud.
Meister saab suhelda mitme orjaga (kuid ainult ühe korraga). Ta teeb seda, kinnitades SS-i ühe orja jaoks ja kinnitades selle kõigi teiste jaoks. SS-i kinnitatud ori (tavaliselt tähendab see LOW) konfigureerib oma MISO-tihvti väljundiks, nii et ori ja ainult see ori saaksid masterile vastata. Teised orjad ignoreerivad sissetulevaid kella impulsse, kui SS-i ei kinnitata. Seega vajate iga orja jaoks ühte täiendavat signaali:
Selles graafikus näete, et MISO, MOSI, SCK on jagatud mõlema orja vahel, kuid igal orjal on oma SS (alluvvaliku) signaal.
SPI spetsifikatsioon ei määra protokolle kui selliseid, nii et see on üleval üksikute isand / paariline paaride vahel, et kokku leppida, mida andmed tähendavad. Ehkki saate baidid samaaegselt saata ja vastu võtta, ei saa vastuvõetud bait olla otsene vastus saadetud baidile (kuna neid komplekteeritakse samaaegselt).
Seega oleks loogilisem, kui üks ots saadaks päringu (nt. 4 võib tähendada "loetlege kettakataloogi") ja seejärel tehke ülekanded (võib-olla lihtsalt nullide väljapoole saatmine), kuni see saab täieliku vastuse. Vastus võib lõppeda uue rea või 0x00 tähemärgiga.
Lugege oma alamseadme andmelehelt, milliseid protokollijadasid see eeldab.
Varasemas näites on Arduino näidatud põhiseadmena, mis saadab andmeid orjaseadmesse. See näide näitab, kuidas Arduino saab olla ori.
Ühendage kaks Arduino Unot koos järgmiste üksteisega ühendatud tihvtidega:
13 (SCK)
+ 5v (vajadusel)
Arduino Megal on tihvtid 50 (MISO ), 51 (MOSI), 52 (SCK) ja 53 (SS).
Igal juhul on MOSI ühes otsas ühendatud MOSI-ga, te ei vahetage neid ümber (see tähendab, et teil pole MOSI <- > MISO). Tarkvara konfigureerib väljundina MOSI (põhiots) ühe ja sisendina teise (alamserv).
#include <SPI.h>void setup (void) {digitalWrite (SS, HIGH); // veenduge, et SS püsib praegu kõrgel // Pange SCK, MOSI, SS tihvtid väljundrežiimi // seadke ka SCK, MOSI madalasse olekusse ja SS kõrgesse olekusse. // Seejärel pange SPI riistvara Master režiimi ja lülitage SPI sisse SPI.begin (); // Aeglustage masterit natuke aeglasemalt SPI.setClockDivider (SPI_CLOCK_DIV8);} // seadistuse lõppvälja silmus (void) {char c; // lubage Slave Select digitalWrite (SS, LOW); // SS on tihvt 10 // saatke test string stringile (const char * p = "Tere, maailm! \ N"; c = * p; p ++) SPI.transfer (c); // keelata Slave Select digitalWrite (SS, HIGH); viivitus (1000); // 1 sekundi viivitus} // tsükli lõpp
#include <SPI.h>char buf [100]; lenduva baidi pos; lenduva boolprotsessi_it; tühise seadistuse (tühine) {Serial.begin (115200); // silumine // lülitage SPI sisse orjarežiimis SPCR | = bit (SPE); // peame saatma sisse master, * slave out * pinMode (MISO, OUTPUT); // valmistu katkestuseks pos = 0; // puhver tühi protsess_it = vale; // nüüd lülita sisse katkestused SPI.attachInterrupt ();} // seadistuse lõpp // SPI katkestus rutiinISIS (SPI_STC_vect) {bait c = SPDR; // haara bait SPI andmeregistrist // lisa puhvrisse if room (pos < sizeof buf) {buf [pos ++] = c; // näide: uus rida tähendab puhvri töötlemise aega, kui (c == '\ n') process_it = true; } // ruumi lõpp} // katkestuse rutiini lõpp SPI_STC_vect // main loop - oodake, millal lipp seatakse katkestuse rutinevoid loop (void) {if (process_it) {
buf [pos] = 0; Serial.println (buf); pos = 0; process_it = vale; } // lipu komplekti lõpp} // tsükli lõpp
Ori on täielikult katkestuspõhine, seega saab ta teha muid asju. Sissetulevad SPI andmed kogutakse puhvrisse ja märgistus seatakse, kui saabub "märkimisväärne bait" (antud juhul uus rida). See käsib orjal alustada ja alustada andmete töötlemist.
Kui järgite ülaltoodud koodi, mis saadab andmeid SPI-isandalt orjale, näitab allpool toodud näide andmete saatmist orjale, laskes seda teha midagi koos sellega ja tagastage vastus.
Meister sarnaneb ülaltoodud näitega. Kuid oluline punkt on see, et peame lisama väikese viivituse (umbes 20 mikrosekundit). Vastasel juhul pole orjal võimalust sissetulevatele andmetele reageerida ja nendega midagi teha.
Näide näitab "käsu" saatmist. Sel juhul "a" (lisage midagi) või "s" (lahutage midagi). Selle eesmärk on näidata, et ori teeb andmetega tegelikult midagi.
Pärast tehingu algatamiseks orjavaliku (SS) kinnitamist saadab juht käsu, millele järgneb suvaline arv baite ja seejärel tõstab SS-i tehingu lõpetamiseks.
Väga oluline punkt on see, et ori ei saa sissetulevale baidile samal hetkel vastata. Vastus peab olema järgmine bait. Seda seetõttu, et saadetavaid ja vastuvõetavaid bitte saadetakse samaaegselt. Neli numbri juurde lisamiseks vajame viit ülekannet, näiteks:
transferAndWait ('a'); // add commandtransferAndWait (10); a = transferAndWait (17); b = transferAndWait (33); c = transferAndWait (42); d = transferAndWait (0);
Kõigepealt taotleme toimingut numbril 10. Kuid me ei saa vastust enne järgmist ülekannet (see 17-le). Kuid vastuseks 10 seatakse "a". Lõpuks saadame lõpuks "näiva" numbri 0, et saada vastus numbrile 42.
#include <SPI.h> void setup (void) {Serial.begin (115200); Serial.println (); digitalWrite (SS, HIGH); // tagada, et SS püsib praegu kõrge SPI.begin (); // Aeglustage masterit natuke SPI.setClockDivider (SPI_CLOCK_DIV8); } // seadistamise baitide edastamise lõppAndWait (const byte what) {byte a = SPI.transfer (what); viivitusmikrosekundid (20); tagastama a; } // transferAndWait void loop (void) {bait a, b, c, d; // lubage Slave Select digitalWrite (SS, LOW); transferAndWait ('a'); // lisa käsk transferAndWait (10); a = transferAndWait (17); b = transferAndWait (33); c = transferAndWait (42); d = transferAndWait (0); // keelata Slave Select digitalWrite (SS, HIGH); Serial.println ("Tulemuste lisamine:"); Serial.println (a, DEC); Serial.println (b, DEC); Serial.println (c, DEC); Serial.println (d, DEC); // lubage Slave Select digitalWrite (SS, LOW); transferAndWait ('s'); // lahuta käsk transferAndWait (10); a = transferAndWait (17); b = transferAndWait (33); c = transferAndWait (42); d = transferAndWait (0); // keelata Slave Select digitalWrite (SS, HIGH); Serial.println ("Tulemuste lahutamine:"); Serial.println (a, DEC); Serial.println (b, DEC); Serial.println (c, DEC); Serial.println (d, DEC); viivitus (1000); // 1 sekund viivitus} // tsükli lõpp
Orja kood teeb põhimõtteliselt peaaegu kõike katkestusrutiinis (seda kutsutakse sissetulevate SPI-andmete saabumisel). See võtab sissetuleva baidi ja liidab või lahutab vastavalt meenutatud "käsubaidile". Pange tähele, et vastus kogutakse järgmisel korral tsükli kaudu. Sellepärast peab kapten saatma lõpliku vastuse saamiseks ühe lõpliku "näiva" ülekande.
Minu näites kasutan peatsüklit, et lihtsalt tuvastada, kui SS tõuseb, ja kustutada salvestatud käsk. Nii loetakse SS-i järgmise tehingu korral uuesti madalaks, loetakse esimene bait käsubaidiks.
Usaldusväärsemalt tehakse seda katkestusega. See tähendab, et ühendaksite füüsiliselt SS-i ühe katkestussisendiga (nt Uno-s, ühendage tihvt 10 (SS) tihvtiga 2 (katkestussisend) või kasutage tihvtil 10 vahetamise katkestust.
Siis saaks katkestust kasutada märkamiseks, kui SS tõmmatakse madalale või kõrgele.
// mida teha sissetuleva datavolatile baidi käsuga = 0; void setup (void) {// peame saatma master-i, * slave välja * pinMode (MISO, OUTPUT); // lülitage SPI sisse slave-režiimis SPCR | = _BV (SPE); // SPCR-i katkestuste sisselülitamine | = _BV (SPIE);} // seadistuse lõpp // SPI-i katkestamine rutiinISIS (SPI_STC_vect) {bait c = SPDR; switch (käsk) {// käsku pole? siis on see käsk 0: command = c; SPDR = 0; break; // lisada sissetulevale baidile, tagastada tulemuse juhtum 'a': SPDR = c + 15; // lisada 15 break; // lahutada sissetulevast baidist , tagastage tulemuse juhtum s: SPDR = c - 8; // lahutage 8 katkestust;} // et d of switch} // teenuse katkestamise rutiini lõpp (ISR) SPI_STC_vectvoid loop (void) {// kui SPI pole aktiivne, tühjendage praegune käsk if (digitalRead (SS) == KÕRGE) käsk = 0; } // tsükli lõpp
Tulemuste lisamine: 25324857Tulemuste lahutamine: 292534
Tulemuste lisamine: 25324857Tulemuste lahutamine: 292534
See näitab ülaltoodud koodi saatmise ja vastuvõtmise vahelist aega:
IDE versioon 1.6.0 on muutnud SPI toimimist määral . Enne SPI kasutamist peate ikkagi tegema SPI.begin ()
. See loob SPI riistvara. Kuid nüüd, kui hakkate suhtlema orjaga, tehke ka SPI.beginTransaction ()
, et seadistada SPI (selle orja jaoks) õigesti:
Kui olete orjaga suhtlemiseks valmis, helistate SPI.endTransaction ()
. Näiteks:
SPI.beginTransaction (SPISettings (2000000, MSBFIRST, SPI_MODE0)); digitalWrite (SS, LOW); // kinnita orjavalimisbaiti foo = SPI.transfer (42); // tee transferdigitalWrite (SS, HIGH); // de-assert Slave SelectSPI.endTransaction (); // tehing üle
Lisan ühe esialgse küsimuse: millal / miks te SPI-d kasutaksite? Vajadus mitme peamise konfiguratsiooni järele või väga suur arv orje kallutaks skaala I2C poole.
See on suurepärane küsimus. Minu vastused on järgmised.
Mõlemal meetodil on oma koht. I 2 C võimaldab teil ühendada paljusid seadmeid ühe siiniga (kaks juhtmest, pluss maandus), nii et see oleks eelistatud valik, kui peaksite küsima märkimisväärset arvu seadmeid, võib-olla üsna harva. SPI kiirus võib olla asjakohasem olukordades, kus peate kiiresti väljastama (nt LED-riba) või sisestama kiiresti (nt ADC-muundur).
Minu SPI-teemaline leht - sisaldab ka üksikasju bitipööratud SPI kohta ja USART-i abil Atmega328 kiibil teise riistvara SPI hankimiseks.
Vaadake minu postitust https://www.fpaynter.com/2020/03/arduino-spi-data-exchange-between-two-arduinos-in-a-master-slave-configuration/ kui algaja võtab probleemi vastu