Hvordan få tilgang Chips Over SPI på BeagleBone Black

BeagleBone Black er en Single Board Computer for $ 45 som driver en ARM CPU, 2 GB flash, 512 MB RAM og kommer med mange kontakter slik at du kan kommunisere med elektronikk. I motsetning til Arduino, kjører BeagleBone Svart full Linux-kjernen, slik at du kan snakke direkte til din elektronikk fra språket du ønsker og med komforten av alt som RAM. I min forrige artikkel så jeg på hvordan man skal snakke med GPIO pinnene på BeagleBone Svart. Denne gangen jeg stepping den opp for å snakke med vedvarende lagring i form av en EEPROM over Serial Peripheral Interface Bus (SPI) på BeagleBone svart.

SPI gjør at data å bevege seg i begge retninger fra en bussmaster (kontroller) til forskjellige chips som er festet til bussen. Fordi det er mange brikker på bussen, må du ha noen måte å stoppe chips snakker samtidig, eller reagere på kommandoer du har sendt til en annen brikke på bussen. Så hver brikke får sin egen Chip Select linje som forteller at det er den aktive chip og du ønsker å snakke med den (og kanskje høre fra det).

Den normale grensesnittet kan ha en ledning for å sende data fra master til bussen, til en wire få data tilbake fra bussen, en klokke ledning til å kontrollere når en ny bit blir sendt og mottatt, og en chip velger ledning (for hver brikke på bussen). Ledningen til å sende fra master til chip (slave) kalles Mosi (Master Out, Slave I) og converse for kommunikasjon fra brikken (Slave) er miso (Master In). Du sender hver byte en eneste bit av gangen. For å sende en bit du setter Mosi linje til dagens bit du vil sende, og bruke klokke linje for å fortelle chip for å hente dagens bit. Deretter sende det neste bit på samme måte, og så videre. Hver gang du sender en bit, du kjører en klokke og chip kan også sende deg en bit tilbake på Miso linje.

Testing av EEPROM med Arduino

En billig EEPROM som har en SPI grensesnitt muliggjør testing SPI tilgang uten fare for å skade dyrere hardware. Jeg velger en 512KBit EEPROM som jeg fikk for et par dollar som den første SPI enheten. Hvis du er bekymret for hastigheten på å sende litt rundt på ting på en gang, kan det EEPROM kjøre på opp til 20Mhz klokkehastighet. Hastigheten på å sende data en bit av gangen kanskje ikke den tregeste delen av systemet, for eksempel EEPROM sjetonger ta tid å skrive data til permanent lagring.

Jeg bestemte meg for å få tilgang til EEPROM fra Arduino først for to grunner: Arduino styret er billigere enn en BeagleBone svart, og jeg kunne også teste at hvordan jeg trodde EEPROM jobbet fra å lese dataarket var hvordan det fungerte når tilkoblet og slått på. Å vite at jeg kan lese og skrive til EEPROM løpet SPI fra en Arduino var nyttig når jeg trenger å feilsøke problemer med tilgang til EEPROM fra BeagleBone Svart senere.

Mens det er 8 pinnene på EEPROM, halvparten av dem er forbundet med strømskinner (3 til 3.3v tilførselen og en til jord). Jeg har vist alle strømledningene i rødt, og de lyseblå ledninger er jordledninger (nedenfor). Klokken ledningen er vist i oransje, chip velger i blått, og Mosi og Miso i svart og hvitt hhv.


Arduino program for å lese og skrive til EEPROM er vist nedenfor . Som du sender data over ledningen én bit om gangen, må du vite hvilken rekkefølge bitene skal sendes inn: det er, bør du sende den mest signifikante bit (MSB) først eller sist? Dataarket for EEPROM fastsetter at den ønsker MSB først så oppsettet () funksjonen sørger for at er rekkefølgen. Brikken velg fest (10) er også satt for produksjonen, slik at vi kan snakke med EEPROM.

Hoved loop () av ​​programmet er ganske enkelt, bare leser det en byte fra adresse 10, viser hva det lese til brukeren, og deretter skriver gjeldende sløyfe iterasjon teller til byte på adressen 10. For å lese en byte fra EEPROM READ instruksjon er sendt, etterfulgt av to byte adresse som du ønsker å lese fra. Man kan lure på om det dristige overføring (0) linjen som følger lese og adresse, virker det som vi skal lese byte på det stadiet, ikke skrive en null. At overføring (0) samtalen blir brukt til å sende én byte, som vil klokke de SPI 8 ganger og har således en byte av data for oss fra brikken. Det spiller ingen rolle hva dataene vi sendte over SPI etter lese- og adresse er sendt, er det viktigste å klokke SPI slik at brikken kan sende tilbake data vi ønsker. Lesingen vil stoppe når vi slipper chip velge linje, og så må vi sende en ny instruks til EEPROM.

For å skrive til EEPROM du sende skrive aktivere (INSTWREN) instruksjon, deretter en WRITE instruksjon, adressen for å skrive data til, og deretter byte til å skrive. Etter det skriver jeg deaktivere IC igjen for fullstendighet (INSTWRDI). Du har å løsne brikken velger etter sending INSTWREN for at instruks om å bli handlet på av EEPROM. Hvis du forlot chip velger LOW etter å skrive INSTWREN og flyttet rett på å utstede skrive kommandoen, ville EEPROM sannsynlig ignorere dine skrive

 #include <. SPI.h > konst byte instread = B0000011; konst byte INSTWRITE = B0000010; konst byte INSTWREN = B0000110; konst byte INSTWRDI = B0000100; konst int chipSelectPin = 10; byte loopcount = 1; void setup () {Serial.begin (9600); //Start SPI bibliotek: SPI.begin (); SPI.setBitOrder (MSBFIRST); pinMode (chipSelectPin, utgang);} void loop () {byte data = 0; digitalWrite (chipSelectPin, LOW); //Les byte på adresse 10 SPI.transfer (instread); SPI.transfer (0); SPI.transfer (10); data =  SPI.transfer (0) 
; //≪ - klokke SPI 8 bits digitalWrite (chipSelectPin, høy); //Vise brukeren hva vi har Serial.print ("lese data:"); Serial.print (data, BIN); Serial.print ("\\ n"); //Set skrive aktivere skrive en byte med gjeldende sløyfe //og deaktivere skrive igjen digitalWrite (chipSelectPin, LOW); SPI.transfer (INSTWREN); digitalWrite (chipSelectPin, HIGH); digitalWrite (chipSelectPin, LOW); SPI.transfer (INSTWRITE); SPI.transfer (0); SPI.transfer (10); SPI.transfer ( loopcount
); digitalWrite (chipSelectPin, HIGH); digitalWrite (chipSelectPin, LOW); SPI.transfer (INSTWRDI); digitalWrite (chipSelectPin, HIGH); loopcount ++;
forsinkelse (5000);}

Jeg bør også nevne at EEPROM kan gi og få mer data for hver READ og WRITE instruksjonen du sender. For eksempel, jeg kunne ha klokket de SPI 8 ganger igjen for å lese byte på adressen 11 uten å utstede en annen LES instruksjon. Det finnes andre muligheter til EEPROM også, men i forbindelse med artikkelen EEPROM er bare brukt for å sjekke at man kan lese og skrive data over SPI.

På BeagleBone Black and Linux

Det er to SPI på BeagleBone Black: SPI0 og SPI1. De fire pinner for SPI0 vises midten av veien nedover P9 spissen og er forbundet i den nedenfor bildet. Orange er igjen på klokken og blå for chip velge. Legg merke til at det er d0 og d1 stedet for Mosi og Miso. Det er fordi du kan velge noe som er inngang eller utgang fra BeagleBone perspektiv under installering av programvare. Se theSystem Reference Manual (SRM) for detaljer om pinouts av den store P8 og P9 overskrifter på BeagleBone Svart. Det er noen tabeller på side 84, men som dokumentet kan endres, kan det være raskere å finne ved å se etter "Expansion Header P9 Pinout" tabellen i listen over bord av SRM. Multipleksing for P9 header kan også sees i denne tabellen.


Noen brikker som kan nås over SPI på BeagleBone vil ha drivere Linux kernel enhets. For eksempel kan en sanntidsklokke på SPI brukes til å gi systemet med /dev /RTC. Du kan også direkte få på SPI fra programmene dine ved hjelp av Linux-kjernen spidev enhetsdriver i Linux-kjernen. Det vil gi deg enheter som /dev/spidev1.0 som du kan bruke normal filtilgang som åpne (2), leser (2), og skrive (2) for å komme og sette data på SPI. Linux-kjernen vil automatisk håndtere holder chip velger linje for deg, og vil klokke dine data ut i den hastigheten du har angitt for spidev.

For EEPROM er det en liten komplikasjon. Når du gjør en write (2) til filen brikken velger er satt, blir data skrevet, og deretter chip velger er usatt. Men, til tilbakekalling fra ovenstående at lese en byte fra EEPROM du må holde chip velger, skriver LES instruksjon og adresse, deretter mens du fremdeles holder brikken velge
du leser fra SPI å få bytes fra EEPROM. Så snart du slipper chip velg linje, tar EEPROM at så slutten av ønsket leseoperasjon og slutter å gjøre det. Så du trenger en måte å skrive til SPI og deretter lese fra SPI mens du holder chip velge hele tiden fra BeagleBone Svart.

Dette fører til bruk av SPI_IOC_MESSAGE grensesnitt til ioctl (2). Som ioctl kan du både sende og motta samtidig, eller utføre en sekvens av sending og mottak og nominere SPI timing og chip deselection politikk mellom hver operasjon i sekvens av kommandoer. Den spidev_fdx.c eksemplet bruker ioctl å først sende og deretter motta data over SPI. Selv spidev_fdx er en halv dupleks eksempel det gir innsikt i hvordan du bruker dette ioctl grensesnitt. Du kan holde chip velg fest mellom operasjonene som bruker cs_change medlem av spi_ioc_transfer struktur.

BeagleBone Svart har to SPI at du kan få tilgang til, som hver har flere chip velger pins knyttet til den. De SPI1 aksjer noen pins med HDMI-grensesnitt, slik at du blir nødt til å deaktivere HDMI for å få på begge SPI. Fordi pinnene på overskriftene kan brukes til forskjellige ting, må du fortelle Linux-kjernen hva du ønsker å bruke disse pinnene for. Dette gjøres ved hjelp av enheten treet overlegg. En fordel med dette er at kjernen kan administrere pins og stoppe to ting fra å bruke de samme pinnene samtidig.

Du forteller neset Manager for å laste et overlegg ved å skrive navnet på legget til /sys /devices/bone_capemgr.*/slots fil. Kapp leder ser i /lib /firmware for en matchende dtbo fil som beskriver hva pins overlegg bruker og hvilken modus disse pinnene må være i. Du kan opprette dtbo filer ved kompilering dts kildefiler som beskriver denne informasjonen i en lesbar tekst-format . Du kan også instruere cape manager for å laste noen overlegg på støvel opp, så når du har en konfigurasjon du er fornøyd med så du ikke trenger å spille med disse overleggsfiler lenger.

elinux Nettstedet gir filer å aktivere SPI0 og SPI1 med D1 som utgang eller inngang for BeagleBone Svart. Som skissert på elinux fremgangsmåten for å muliggjøre disse overlappinger er som følger. Jeg endret også manuelt eieren av spidev filer slik at jeg ikke trengte å være root for å snakke EEPROM. Jeg fant ut at på Cloud9 GNOME Bilde 2013.05.27 automatisk cape lasting ved oppstart for BB-SPI0 hjelp av uEnv.txt optargs fungerte ikke for meg. Etter en programvareoppdatering, automatisk lasting fungerte

 root @ beaglebone: ~ # DTC -O DTB -o BB-SPI0-01-00A0.dtbo -b 0 - @ BB-SPI0-01-00A0.. dtsroot @ beaglebone: ~ # cp BB-SPI0-01-00A0.dtbo /lib /firmware /root @ beaglebone: ~ # echo BB-SPI0-01 > /sys/devices/bone_capemgr.*/slotsroot@beaglebone:~# ls -IH /dev /spi * CRW ------- 1 root root 153, 0 16 oktober 04:12 /dev/spidev1.0root@beaglebone : ~ # chown ben /dev /spi * 

De nedenfor kommandoer fortelle udev å alltid gjøre spidev filer som eies av min brukeren i stedet for rot. Den udevadm er et veldig nyttig kommando som det vil vise deg ikke bare hva ting du kan matche mot i regler i udev fil for å velge enheten, men også kan kjøres på nytt for å teste at reglene vil matche og utføre den forventede oppgaven.

 # udevadm test /sys/devices/ocp.2/48030000.spi/spi_master/spi1/spi1.0/spidev/spidev1.0...SUBSYSTEM=spidev...# cat /etc /udev /regler .d /99-spidev.rulesSUBSYSTEM == "spidev", EIER = "ben" 
Snakke med EEPROM løpet SPI

Så, nå som en /dev/spidev1.0 filen finnes, går videre til snakker med EEPROM løpet SPI fra BeagleBone Svart. Kjernen av en C ++ programmet er vist nedenfor som gjør det samme som det ovenfor Arduino gjør. Hver iterasjon en byte leses fra en fast adresse og vist, og deretter den aktuelle iterasjonen nummeret er skrevet til den samme faste adresse på EEPROM.

 int main (int argc, røye ** argv) {EEPROMTest obj ( argv [1]); address_t adr (305); for (uint8_t loopcount = 0;; loopcount ++) {int d = (int) obj.read (adr); cerr < < "HOVED: lese data:" < < hex < < d < < endl; cerr < < "HOVED: skrive data:" < < hex < < (int) loopcount < < endl; obj.write (adr, loopcount); søvn (1); } Return 0;} 

EEPROMTest klassen har noen metoder, lese- () og write () er den mest interessante og en konstruktør. Den EEPROMTest :: write () metoden er vist nedenfor. På en lignende måte til Arduino programmet første skriveklargjørings er satt, så WRITE-instruksjon, adresse, og byte blir sendt. Dette etterfølges av invalid skrive igjen. Den tostr () -metoden konverterer instruksjon til en streng for bruk med spi_write () -funksjonen vist i slutten av kodeblokken. Den spi_write () er en enkel wrapper av write () som sender en std :: string til en fil descriptor.

 void write (address_t adr, uint8_t b) {spi_write (m_fd, tostr (INSTWREN), sann ); {Stringstream ss; ss < < INSTWRITE < < sadr < < tømme; ss.write ((const char *) & b, sizeof (uint8_t)); spi_write (m_fd, ss.str (), true); } Spi_write (m_fd, tostr (INSTWRDI), true);} std :: string tostr (instruction_t v) {std :: stringstream ss; ss < < v; returnere ss.str ();} static void spi_write (int fd, std :: string v) {write (fd, v.data (), v.size ());} 

Hver samtale å skrive () vil ta tak i chip velger, sette data på bussen, og slipp chip velger. Dette er greit for å sende byte til å skrive og enkle instruksjoner som "sender bare" til EEPROM. Men for å lese fra EEPROM du må sende LES instruksjon og adresse og hold chip velg mens du leser bytes tilbake. Den EEPROMTest :: lese () metoden bruker spi_transfer () -funksjonen til å sende og motta data på SPI samtidig. Legg merke til at 4 byte blir sendt, 8 bit LES, den 16 bit-adressen og deretter 8 bit verdien av "d" som kan være en hvilken som helst verdi egentlig som EEPROM vil ignorere det. Sende de 8 biter av "d" lar SPI bussen å sende tilbake de 8 biter som finnes i adresse vi er interessert i. Så dataene som returneres fra spi_transfer () vil ha verdien av EEPROM på adressen adr i sitt utvalg på data [3]

 uint8_t lese (address_t adr) {uint8_t d = 77.; stringstream ss; ss < < Instread < < adr; . int prefixlength = ss.str () length (); ss.write ((const char *) &Co. d, sizeof (uint8_t)); std :: string data = spi_transfer (m_fd, ss.str ()); data = data.substr (prefixlength); returnere data [0];} 

spi_transfer () tar en streng for å sende og vil returnere en streng av samme lengde som er det som er blitt sendt tilbake til BeagleBone Svart løpet SPI mens overføringen ble forekommende. Så for EEPROM de første 24 bits av dataene vi får tilbake er ubrukelig. Men etter at vi sender 24 bits (les instruksjon og 16 bit adresse) neste byte sendt tilbake er byte på EEPROM på denne adressen. Ved å gi overføring buffer (tx_buf) og mottaksbufferen (rx_buf) av samme størrelse, den eneste ioctl () vil utføre både lese og skrive på SPI samtidig.

 statisk std :: string spi_transfer (int fd, std :: string v) {struct spi_ioc_transfer xfer [2]; int status; memset (xfer, 0, sizeof xfer); std :: string ret; ret.resize (v.size ()); xfer [0] .tx_buf = (unsigned long) v.data (); xfer [0] .rx_buf = (unsigned long) ret.data (); xfer [0] .len = v.size (); xfer [0] .delay_usecs = 200; status = ioctl (fd, SPI_IOC_MESSAGE (1), xfer); if (status < 0) {perror ("SPI_IOC_MESSAGE"); komme tilbake ""; } Retur ret;} 

Programmet utgang ser ut som følgende. I dette tilfellet hadde jeg kjørt 5 iterasjoner forrige gang jeg henrettet spibbb som er grunnen til den første lese- fikk en 5. De øverste linjene bruker en litt modifisert dumpstat () fra spidev_fdx.c eksempel for å vise SPI innstillinger for den valgte spidev Enhetsfil

 $ ./spibbb /dev/spidev1.0 åpnet filen /dev/spidev1.0before påføre innstillinger. SPI modus 0, 8 bit (MSB) per ord, 500000 Hz maxafter gjelder innstillinger: SPI modus 0, 8 bits (MSB) per ord, 500000 Hz maxMAIN: lese data: 5MAIN: skrive data: 0MAIN: lese data: 0MAIN: skrive data: 1MAIN: lese data: 1MAIN: skrive data: 2 

EEPROMTest konstruktør show gjeldende innstillinger SPI for enheten og deretter endrer ting for å sikre den mest signifikante bit blir sendt først, klokkehastighet på SPI er akseptabelt, og SPI modus er noe som EEPROM IC vil bruke.

 dumpstat ("før påføring innstillinger", m_fd); uint8_t lsb = 0; ioctl (m_fd, SPI_IOC_WR_LSB_FIRST, & lsb); uint32_t hastighet = 500000; ioctl (m_fd, SPI_IOC_WR_MAX_SPEED_HZ, & hastighet); uint32_t mode = SPI_MODE_0; ioctl (m_fd , SPI_IOC_WR_MODE, & mode), dumpstat ("etter påføring settings", m_fd); 

I det ovenstående instruksjoner og adresser er skrevet til std :: iostreams uten hensyn til de nødvendige 8 og 16 bits størrelser at EEPROM er forventer. Dette håndteres ved overbelastning utgangs operatørene for instruction_t og address_t slik at data skrives til output stream som chip forventer.

 enum instruction_t {instread = 0b00000011, INSTWRITE = 0b00000010, INSTWREN = 0b00000110, INSTWRDI = 0b00000100 , INSTRDSR = 0b00000101}; klasse address_t {public: uint16_t m_v; address_t (int v = 0): m_v (v) {}}; std :: ostream & Operatøren < < (std :: ostream & os, konst instruction_t & v) {os.write ((const char *) & v, sizeof (uint8_t)); returnere os;} std :: ostream & Operatøren < < (std :: ostream & os, konst address_t & v) {os.write ((const char *) & v.m_v, sizeof (uint16_t)); returnere os;} 

Neste gang håper jeg å bruke BeagleBone Svart å snakke over SPI til en trippel akse akselerometer og en 7-segment serie skjerm som selv kjører en ATMega328 på sin baseboard. Akselerometeret har noen avbryter pinner som jeg trenger for å overvåke og reagere på fra Beagle

For tidligere artikler i denne serien kan du se:.

Komme i gang med den BeagleBone Black: En 1GHz ARM Linux-maskin for $ 45

BeagleBone Svart Del 2: Linux ytelsestester Anmeldelser