STM32, seriell I2C-grensesnitt. STM32, seriell grensesnitt I2C Stm32 grensesnitt i2c beskrivelse fortsetter

Noen liker paier, noen liker ikke.

i2c-grensesnittet er vidt utbredt og brukt. I stm32f4 er det så mange som tre moduler som implementerer denne protokollen.
Naturligvis med full støtte hele denne greia.

Å jobbe med modulen er generelt det samme som i andre kontrollere: du gir den kommandoer, den utfører dem og rapporterer resultatet:
I> Vi gikk START.
S> Ok, jeg sendte den.
Me> Kult, send adressen nå. Slik: 0xXX.
S> Ok, jeg sendte den. Jeg ble fortalt at ACK. La oss gå videre.
I> Fremdeles i live, bra. Her er registernummeret: 0xYY, - la oss gå.
S> Sendt, mottatt ACK.
I> Send ham dataene, her er byten: 0xZZ.
S> Sendt, samtykker han til mer: ACK.
I> Faen ham, ikke ennå. De gikk STOPP.
S> Ok.

Og alt er omtrent i denne ånden.

I denne kontrolleren i2c-pinner er spredt over portene slik:
PB6: I2C1_SCL
PB7: I2C1_SDA

PB8: I2C1_SCL
PB9: I2C1_SDA

PB10: I2C2_SCL
PB11: I2C2_SDA

PA8: I2C3_SCL
PC9: I2C3_SDA
Generelt er det praktisk å se på pinouten til periferiutstyret på side 59.

Overraskende nok, for å jobbe med i2c trenger du alle registrene, heldigvis er det få av dem:
I2C_CR1- kommandoer til modulen for å sende kommandoer/tilstander og velge driftsmoduser;
I2C_CR2- sette opp DMA og indikere driftsfrekvensen til modulen (2-42 MHz);
I2C_OAR1- innstilling av enhetsadresse (for slave), adressestørrelse (7 eller 10 biter);
I2C_OAR2- angi enhetsadressen (hvis det er to adresser);
I2C_DR- dataregister;
I2C_SR1- modulstatusregister;
I2C_SR2- statusregister (slave, må leses hvis ADDR- eller STOPF-flaggene er satt i SR1);
I2C_CCR- angi grensesnitthastigheten;
I2C_TRISE- sette opp tidspunkter for kanter.

Imidlertid er halvparten av dem av typen "skriv det ned og glem det".

STM32F4-Discovery-kortet har allerede en I2C-enhet som du kan øve med: CS43L22, lyd-DAC. Den er koblet til pinnene PB6/PB9. Det viktigste er ikke å glemme å bruke et høyt nivå på pin PD4 (~RESET sitter der), ellers vil ikke DAC-en svare.

Oppsettsprosedyren er omtrent som følger:
1 . Tillat klokke av porter og selve modulen.
Vi trenger pinner PB6/PB9, så vi må sette bit 1 (GPIOBEN) i RCC_AHB1ENR-registeret for å aktivere porten.
Og sett bit 21 (I2C1EN) i RCC_APB1ENR-registeret for å aktivere I2C-modulen. For den andre og tredje modulen er bittallene henholdsvis 22 og 23.
2 . Deretter konfigureres pinnene: Oped Drain-utgang (GPIO->OTYPER), alternativ funksjonsmodus (GPIO->MODER), og alternativt funksjonsnummer (GPIO->AFR).
Hvis ønskelig, kan du konfigurere en pull-up (GPIO->PUPDR), hvis den ikke er på brettet (og en pull-up til strømforsyningen til begge linjene er nødvendig i enhver form). Nummeret for I2C er alltid det samme: 4. Det er fint at det er et eget nummer for hver type periferiutstyr.
3 . Den nåværende klokkefrekvensen til Fpclk1-periferien (uttrykt i MHz) er indikert i CR2-registeret. Slik jeg forstår det, er dette nødvendig for å beregne forskjellige protokolltiminger.
Det bør forresten være minst to for normalmodus og minst fire for hurtigmodus. Og hvis du trenger en full hastighet på 400 kHz, så må den også deles på 10 (10, 20, 30, 40 MHz).
Maksimal tillatt klokkefrekvens: 42 MHz.
4 . Grensesnitthastigheten konfigureres i CCR-registeret, og modusen velges (normal/rask).
Betydningen er: Tsck = CCR * 2 * Tpckl1, dvs. SCK-perioden er proporsjonal med CCR (for rask modus er alt litt mer vanskelig, men det er beskrevet i RM).
5 . Den maksimale stigekanttiden i TRISE-registeret justeres. For standardmodus er denne tiden 1 µs. I registeret må du skrive antall busssykluser som passer inn i denne tiden, pluss én:
hvis Tpclk1-syklusen varer 125 ns, så skriv (1000 ns / 125 ns) + 1 = 8 + 1 = 9.
6 . Generering av avbruddssignaler (feil, status og data) er valgfritt aktivert;
7 . Modulen slås på: PE-flagget i CR1-registeret er satt til 1.

Da fungerer modulen som den skal. Du trenger bare å implementere riktig rekkefølge av kommandoer og sjekke resultatene. For eksempel en registeroppføring:
1 . Først må du sende START ved å sette et flagg med samme navn i CR1-registeret. Hvis alt er ok, vil SB-flagget etter en tid settes i SR1-registeret.
Jeg vil gjerne merke meg ett poeng - hvis det ikke er noen pull-up på linjen (og de er på 0), kan det hende at dette flagget ikke venter i det hele tatt.
2 . Hvis flagget mottas, sender vi adressen. For en syv-bits adresse skriver vi den ganske enkelt i DR nøyaktig slik den vil være på linjen (7 adressebiter + retningsbit). For ti-bit, en mer kompleks algoritme.
Hvis enheten svarer på adressen med en ACK, vil ADDR-flagget vises i SR1-registeret. Hvis ikke, vil AF-flagget (Acknowledge failure) vises.
Hvis ADDR vises, må du lese SR2-registeret. Du trenger ikke å se på noe der, bare sekvensiell lesing av SR1 og SR2 tilbakestiller dette flagget. Og mens flagget er satt, holdes SCL lavt av masteren, noe som er nyttig hvis du trenger å be den eksterne enheten vente før du sender data.
Hvis alt er ok, vil modulen deretter bytte til modusen for å motta eller sende data, avhengig av den minst signifikante biten av den sendte adressen. For å skrive må det være null, for å lese må det være ett.
men vi ser på posten, så vi vil anta at det var en null der.
3 . Deretter sender vi adressen til registeret som interesserer oss. På samme måte skriver det ned i DR. Etter overføring settes TXE (overføringsbuffer er tom) og BTF (overføring fullført) flagg.
4 . Deretter kommer dataene som kan sendes mens enheten svarer med en ACK. Hvis svaret er NACK, vil disse flaggene ikke bli satt.
5 . Ved fullføring av overføringen (eller i tilfelle en uventet tilstand), sender vi STOP: flagget med samme navn settes i CR1-registeret.

Når du leser, er alt det samme. Endringer kun etter å ha skrevet registeradressen.
I stedet for å skrive data, sendes START på nytt (restart) og adressen sendes med den minst signifikante biten satt (lestegnet).
Modulen vil vente på data fra enheten. For å oppmuntre den til å sende de neste bytene, må du sette ACK-flagget i CR1 før du mottar (slik at modulen etter mottak vil sende samme ACK).
Når du blir lei av det, fjern flagget, enheten vil se NACK og bli stille. Deretter sender vi STOP på vanlig måte og gleder oss over de mottatte dataene.

Her er det samme i kodeform:
// Initialiser modulen void i2c_Init(void) ( uint32_t Klokke = 16000000UL; // Modulklokkefrekvens (system_stm32f4xx.c brukes ikke) uint32_t Speed ​​​​= 100000UL; // 100 kHz klokkeslett /-B klokke 100 kHz port /-B klokke |= RCC_AHB1ENR_GPIOBEN; // Sett opp pinner PB6, PB9 // Åpen avløp! GPIOB->OTYPER |= GPIO_OTYPER_OT_6 | GPIO_OTYPER_OT_9; // Opptrekket er eksternt, så det kan ikke konfigureres her! // om nødvendig, se GPIOB->PUPDR-registeret // Nummer på den alternative GPIOB-funksjonen ->AFR &= ~(0x0FUL<< (6 * 4)); // 6 очистим GPIOB->AFR |= (0x04UL<< (6 * 4)); // В 6 запишем 4 GPIOB->AFR &= ~(0x0FUL<< ((9 - 8) * 4)); // 9 очистим GPIOB->AFR |= (0x04UL<< ((9 - 8) * 4)); // В 9 запишем 4 // Режим: альтернативная функция GPIOB->MODER &= ~((0x03UL<< (6 * 2)) | (0x03UL << (9 * 2))); // 6, 9 очистим GPIOB->MODER |= ((0x02UL<< (6 * 2)) | (0x02UL << (9 * 2))); // В 6, 9 запишем 2 // Включить тактирование модуля I2C1 RCC->APB1ENR |= RCC_APB1ENR_I2C1EN; // På dette tidspunktet skal I2C være slått av // Tilbakestill alt (SWRST == 1, reset) I2C1->CR1 = I2C_CR1_SWRST; // PE == 0, dette er hovedsaken I2C1->CR1 = 0; // Vi antar at vi kjører fra RC (16 MHz) // Det er ingen forhåndsskalere i klokkesystemet (alle 1) // På en vennskapelig måte bør vi beregne alt dette fra // den faktiske klokkefrekvensen til modulen I2C1->CR2 = Klokke / 1000000UL; // 16 MHz // Juster frekvensen ( // Tclk = (1 / Fperiph); // Thigh = Tclk * CCR; // Tlow = Thigh; // Fi2c = 1 / CCR * 2; // CCR = Fperiph / ( Fi2c * 2); uint16_t Verdi = (uint16_t)(Klokke / (Speed ​​​​* 2)); // Minimumsverdi: 4 if(Verdi< 4) Value = 4; I2C1->CCR = Verdi; ) // Sett grensen for stigetid // I standardmodus er denne tiden 1000 ns // Vi legger ganske enkelt en til frekvensen uttrykt i MHz (se RM s. 604). I2C1->TRISE = (Klokke / 1000000UL) + 1; // Aktiver modul I2C1->CR1 |= (I2C_CR1_PE); // Nå kan du gjøre noe ) // Send en byte bool i2c_SendByte(uint8_t Address, uint8_t Register, uint8_t Data) ( if(!i2c_SendStart()) return false; // Chip address if(!i2c_SendAddress(Address)) return i2c_SendStop (); // Registrer adresse if(!i2c_SendData(Register)) return i2c_SendStop(); // Data if(!i2c_SendData(Data)) return i2c_SendStop(); // Stop! i2c_SendStop(); return true; ) // Motta byte bool i2c_ReceiveByte(uint8_t Address, uint8_t Register, uint8_t * Data) ( if(!i2c_SendStart()) return false; // Chip address if(!i2c_SendAddress(Address)) return i2c_SendStop(); // Register address if(! i2c_SendData(Register)) returner i2c_SendStop(); // Start på nytt if(!i2c_SendStart()) returner false; // Chipadresse (lest) if(!i2c_SendAddress(Adresse | 1)) returner i2c_SendStop(); // Motta en byte if(!i2c_ReceiveData(Data)) returner i2c_SendStop(); // Stop! i2c_SendStop(); return true; ) Bruk: ( uint8_t ID = 0; i2c_Init(); // Vi antar at PD4 er satt til et høyt nivå og DAC-en fungerer (dette må gjøres på en eller annen måte) // Send en byte til enheten med adresse 0x94, for å registrere 0x00 med verdien 0x00. i2c_SendByte(0x94, 0x00, 0x00); // Motta en byte fra enheten med adresse 0x94 fra register 0x01 (ID) inn i den variable bufferen i2c_ReceiveByte(0x94, 0x01, &ID); )
Selvfølgelig kan du ikke gjøre dette unntatt i et treningseksempel. Ventetiden på at handlingen skal fullføres er for lang for en så rask kontroller.

(Utviklerveiledning for HCS08-familiemikrokontrollere)

For å styre I2C-modulen brukes 6 spesialfunksjonsregistre:

  • IICC - første kontrollregister til I2C-modulen;
  • IICC2 - andre kontrollregister til I2C-modulen;
  • IICS - I2C-modulstatusregister;
  • IICF - I2C-modul baudrateregister;
  • IICA - I2C-moduladresseregister;
  • IICD er I2C-modulens dataregister.

QE-seriens MCU inneholder 2 I2C-moduler og følgelig to kontrollregistre av hver type. For eksempel er det første statusregisteret IIC1S og det andre statusregisteret er IIC2S.

11.2.8.1. IICC kontrollregister

For MK serie AC. AW, Dx, EL, GB, GT, JM, LC, QE. Q.G. SG, SH, SL
Registrere Modus D7 D6 D5 D4 D3 D2 D1 D0
IICC Lesning IICEN IICIE MST TX TXAK 0 0 0
Ta opp RSTA
Nullstille 0 0 0 0 0 0 0 0
Bits beskrivelse:
Bit navn Beskrivelse Symbol på C-språk
IICEN I2C-modulaktiveringsbit:
0 — I2C-kontrolleren er deaktivert;
1 - I2C-kontrolleren er aktivert.
bIICEN
IICIE Modulavbruddsaktiveringsbit fra I2C:
0 - I2C-forespørselsavbrudd er deaktivert;
1 - I2C-forespørselsavbrudd er aktivert.
bHCIE
MST I2C-kontroller driftsmodusvalgbit:
0 - I2C-kontrolleren fungerer i slavemodus;
1 - I2C-kontrolleren fungerer i Master-modus.
Når denne biten endres fra 0 til 1, genereres en starttilstand. Omvendt, når en bit endres fra 1 til 0, genereres en stoppbetingelse.
bMST
TX Overføringsretningsvalgbit på SDA-datalinjen:
0 — linjen fungerer for inndata;
1 - linje fungerer for utgang.
bTX
TXAK Kvitteringsbit i mottaksmodus:
0 - en bekreftelsesbit genereres etter å ha mottatt en byte;
1 - en bit med ubekreftet byte genereres.
Denne biten styrer genereringen av en bekreftelsesbit etter å ha mottatt en databyte, uavhengig av om det er en slave eller en master.
bTXAK
RSTA Hvis I2C-modulen opererer i mastermodus, vil skriving av 1 til denne biten føre til at Start - "Restart"-tilstanden blir generert på nytt. bRSTA

11.2.8.2. IICS Statusregister

For MK-serien AC, AW, Dx, EL, GB, GT, JM, LC, QE, QG, SG, SH, SL
Registrere Modus D7 D6 D5 D4 D3 D2 D1 D0
IICS Lesning TCF IAAS OPPTATT ARBL 0 SRW IICIF RXAK
Ta opp
Nullstille 0 0 0 0 0 0 0 0
Bits beskrivelse:
Bit navn Beskrivelse Symbol på C-språk
TCF Bytt ut fullføringsbit. Sett etter at utvekslingen av én byte er fullført:
0 — utveksling ikke fullført;
1 - utveksling fullført.
TCF-flagget slettes til 0 når IICD-dataregisteret leses (i mottaksmodus) eller når IICD-dataregisteret skrives (i overføringsmodus).
bTCF
IAAS Slaveadresseflagg. Still inn hvis enheten opererer i slavemodus og adressen som sendes i mastermeldingen er lik slaveadressen, som er lagret i IICA-adresseregisteret.
Flagget slettes ved skriving til IICC-registeret.
bIAAS
OPPTATT Opptatt linje flagg. Dette flagget settes hvis I2C-modulen har gjenkjent startbiten på linjen. Flagget slettes når modulen oppdager en stoppbit på linjen.
0 — I2C-bussen er gratis;
1 - I2C-bussen er opptatt.
bOPPTAK
ARBL Flagg for voldgiftstap:
0 - ingen brudd i driften av I2C-bussen;
1 – det er tap av arbitrage. I2C-modulen må vente en stund og deretter starte overføringen på nytt.
bARBL
SRW Slave overføringsretningsbit. Denne biten indikerer tilstanden til R/W-biten i adressefeltet:
0 - slave aksepterer. Lederen sender til slaven;
1 - slave sender. Lederen mottar fra slaven.
bSRW
IICIF Flagg for ubetjente avbruddsforespørsler for I2C-modulen. Sett til 1 hvis ett av flaggene er satt: TCF, IAAS eller ARBL.
0 - ingen ubetjente avbrudd;
1 - det er ubetjente avbrudd.
Flagget tilbakestilles ved å skrive 1 til det.
bIICIF
RXAK Master erkjenne bit:
0—slave bekreftet mottak av data;
1 — slaven bekreftet ikke datamottak.
Denne biten gjenspeiler tilstanden til ASK-feltet på tidsdiagrammet til utvekslingen.
bRXAK

11.2.8.3. IICA adresseregister

Registrere Modus D7 D6 D5 D4 D3 D2 D1 D0
IICA Lesning ADDR
Ta opp
Nullstille 0 0 0 0 0 0 0 0

Dette registeret lagrer 7-bits slaveadressen som utvikleren tildelte denne enheten ved utvikling av systemet. Denne adressen sammenlignes automatisk med adressekoden som slaven mottok i adressefeltet på I2C-bussen. Hvis adressene samsvarer, settes IAAS-biten i IICS-statusregisteret.

11.2.8.4. IICF Baud Rate Register

For MK-serien AC, AW, Dx, EL, GB, GT, JM, LC, QE, QG, SG, SH, SL
Registrere Modus D7 D6 D5 D4 D3 D2 D1 D0
IICF Lesning MULTI ICR
Ta opp
Nullstille 0 0 0 0 0 0 0 0
Bits beskrivelse:

Verdier av koeffisientene SCL_DIV og SDA_HV

ISR SCL_DIV SDA_HV ISR SCL_DIV SDA_HV
0x00 20 7 0x20 160 17
0x01 22 7 0x21 192 17
0x02 24 8 0x22 224 33
0x03 26 8 0x23 256 33
0x04 28 9 0x24 288 49
0x05 30 9 0x25 320 49
0x06 34 10 0x26 384 65
0x07 40 10 0x27 480 65
0x08 28 7 0x28 320 33
0x09 32 7 0x29 384 33
0x0A 36 9 0x2A 448 65
0x0B 40 9 0x2B 512 65
0x0C 44 11 0x2C 576 97
0x0D 48 11 0x2D 640 97
0x0E 56 13 0x2E 768 129
0x0F 68 13 0x2F 960 129
0x10 48 9 0x30 640 65
0x11 56 9 0x31 768 65
0x12 64 13 0x32 896 129
0x13 72 13 0x33 1024 129
0x14 80 17 0x34 1152 193
0x15 88 17 0x35 1280 193
0x16 104 21 0x36 1536 257
0x17 128 21 0x37 1920 257
0x18 80 9 0x38 1280 129
0x19 96 9 0x39 1536 129
0x1A 112 17 0x3A 1792 257
0x1B 128 17 0x3B 2048 257
0x1C 144 25 0x3C 2304 385
0x1D 160 25 0x3D 2560 385
0x1E 192 33 0x3E 3072 513
0x1F 240 33 0x3F 3840 513

Dette registeret lagrer to bitfelt som bestemmer hastigheten og tidsparametrene for I2C-utveksling. Frekvensen til synkroniseringssignaler bestemmes av formelen:

SDA_hold_time datainnstillingstiden på I2C-bussen er tidsintervallet mellom øyeblikket SCL-signalet settes til 0 og dataene på SDA-linjen endres. Tilordnet av parameteren SDA_HV (SDA_Hold_Value) fra tabellen for ICR-faktoren til baudhastighetsregisteret:

.

11.2.8.5. IICD dataregister

For MK-serien AC, AW, Dx, EL, GB, GT, JM, LC, QE, QG, SG, SH, SL
Registrere Modus D7 D6 D5 D4 D3 D2 D1 D0
IICD Lesning I2C DATA
Ta opp
Nullstille 0 0 0 0 0 0 0 0

Hvis I2C-modulen opererer i mastermodus, vil en skriveoperasjon til dette registeret initiere I2C-kommunikasjon (men bare hvis kommunikasjonsretningsbiten i IICC-kontrollregisteret er satt riktig, dvs. TX = 1). Den første byten etter starttilstanden, som programmet skriver til dataregisteret, tolkes av slavene som enhetsadressen. Derfor må programmet riktig danne innholdet i den første byten. Registerleseoperasjonen returnerer den siste byten mottatt via I2C. Registerleseoperasjonen initierer også starten på mottak av neste byte, men bare hvis kommunikasjonsretningsbiten i IICC-kontrollregisteret er riktig innstilt, dvs. ved TX = 0! Med TX = 1 vil en registerleseoperasjon ikke føre til at en ny byte mottas via I2C fra slaven.

Hvis I2C-modulen opererer i slavemodus, vil dataene som er skrevet til dette registeret bli overført til SDA-linjen til I2C-bussen når masterenheten utfører en mottakssyklus fra denne slaven. Registerleseoperasjonen returnerer den siste byten mottatt fra masteren.

11.2.8.6. Kontrollregister IICC2

For MK-serien AC, Dx, EL, JM, QE, SG, SH, SL
Registrere Modus D7 D6 D5 D4 D3 D2 D1 D0
IICC Lesning GCAEN ADEXT 0 0 0 AD10 AD9 AD8
Ta opp
Nullstille 0 0 0 0 0 0 0 0

Første trinn med STM32 og mikroC kompilator for ARM-arkitektur - Del 4 - I2C, pcf8574 og HD4478 basert LCD-tilkobling

Jeg vil gjerne vie den neste artikkelen til å arbeide med det vanlige i2c-grensesnittet, som ofte brukes i ulike mikrokretser koblet til en mikrokontroller.

I2C er en buss som opererer over to fysiske forbindelser (i tillegg til den felles ledningen). Det skrives ganske mye om det på Internett, det er gode artikler på Wikipedia. I tillegg er bussdriftsalgoritmen veldig tydelig beskrevet. Kort fortalt er bussen en to-leder synkron buss. Opptil 127 enheter kan være på bussen samtidig (enhetsadressen er 7-bit, vi kommer tilbake til dette senere). Nedenfor er et typisk diagram for tilkobling av enheter til i2c-bussen, med MK som hovedenhet.


For i2c bruker alle enheter (både master og slaver) åpen-drain-utganger. Enkelt sagt, de kan BARE tiltrekke dekket til bakken. Det høye bussnivået sikres av opptrekksmotstander. Verdien av disse motstandene velges vanligvis i området fra 4,7 til 10 kOhm. i2c er ganske følsom for de fysiske linjene som forbinder enheter, så hvis en tilkobling med stor kapasitans brukes (for eksempel en lang tynn eller skjermet kabel), kan påvirkningen av denne kapasitansen "sløre" signalkantene og forstyrre normal operasjon dekk. Jo mindre pull-up-motstanden er, jo mindre innflytelse har denne kapasitansen på egenskapene til signalkantene, men jo STØRRE BELASTNING på utgangstransistorene på i2c-grensesnittene. Verdien av disse motstandene er valgt for hver spesifikke implementering, men de bør ikke være mindre enn 2,2 kOhm, ellers kan du ganske enkelt brenne utgangstransistorene i enheter som fungerer med bussen.

Bussen består av to linjer: SDA (datalinje) og SCL (klokkesignal). Klokker buss Master-enheten, vanligvis vår MK. Når SCL er høy, leses informasjon fra databussen. SDA-tilstanden kan bare endres når klokkesignalet er lavt.. Når SCL er høy, endres signalet på SDA når det genereres signaler START (når SCL er høy endres signalet på SDA fra høy til lav) og STOPPE - når SCL-nivået er høyt, endres signalet på SDA fra lavt til høyt).

Separat skal det sies at i i2c er adressen spesifisert som et 7-bits nummer. 8 - minst signifikante bit indikerer retningen for dataoverføring 0 - betyr at slaven vil overføre data, 1 - motta.. Kort fortalt er algoritmen for å jobbe med i2c som følger:

  • Høyt nivå på SDA og SCL- bussen er gratis, du kan begynne å jobbe
  • Master heiser SCL til 1, og endrer tilstand S.D.A. fra 1 til 0 - tiltrekker den til bakken - det dannes et signal START
  • Masteren sender en 7-bits slaveadresse med en retningsbit (data på S.D.A. er utstilt når SCL trukket til bakken, og lest av slaven når den slippes). Hvis slaven ikke har tid til å "gripe" den forrige biten, tiltrekker den seg SCL til bakken, og gjør det klart for masteren at databussens tilstand ikke trenger å endres: "Jeg leser fortsatt den forrige." Etter at mesteren har løsnet dekket, sjekker han slapp slaven henne?.
  • Etter å ha sendt 8 biter av adressen, genererer masteren den 9. klokkesyklusen og frigjør databussen. Hvis slaven hørte adressen hans og tok imot den, så han vil trykke S.D.A. til bakken. Slik dannes signalet SPØRRE- akseptert, alt er i orden. Hvis slaven ikke forstår noe, eller han rett og slett ikke er der, vil det ikke være noen som trykker på dekket. mesteren vil vente på en timeout og forstå at han ikke ble forstått.
  • Etter å ha overført adressen, hvis vi har satt retningen fra herre til slave(8 biter av adressen er lik 1), så sender masteren dataene til slaven, og glemmer ikke å sjekke tilstedeværelsen av SPØRRE fra slaven og venter på at slaveenheten skal behandle den innkommende informasjonen.
  • Når masteren mottar data fra slaven, genererer masteren selv et signal SPØRRE etter å ha mottatt hver byte, og slaven kontrollerer sin tilstedeværelse. Mesteren sender kanskje ikke spesifikt SPØRRE før du sender kommandoen STOPPE, som vanligvis gjør det klart for slaven at det ikke er behov for å oppgi flere data.
  • Hvis det, etter å ha sendt data fra masteren (skrivemodus), er nødvendig å lese data fra slaven, så genererer masteren signalet igjen START , sender slaveadressen med et leseflagg. (hvis før kommandoen START ble ikke overført STOPPE så dannes et lag OMSTART). Dette brukes til å endre retningen på master-slave-kommunikasjon. For eksempel sender vi registeradressen til slaven, og leser deretter data fra den.)
  • Når arbeidet med slaven er fullført, genererer masteren et signal STOPPE- ved et høyt nivå av klokkesignalet danner det en databussovergang fra 0 til 1.
STM 32 har maskinvare-implementerte i2c buss-transceivere. Det kan være 2 eller 3 slike moduler i en MK. For å konfigurere dem brukes spesielle registre, beskrevet i referansen for MK som brukes.

I MicroC, før du bruker i2c (så vel som eventuelt periferiutstyr), må det initialiseres riktig. For å gjøre dette bruker vi følgende funksjon (initialisering som master):

I2Cn_Init_Advanced(usignert lang: I2C_ClockSpeed, const Module_Struct *modul);

  • n- nummeret på modulen som brukes, for eksempel I2C1 eller I2C2.
  • I2C_ClockSpeed- busshastighet, 100 000 (100 kbs, standardmodus) eller 400 000 (400 kbs, rask modus). Den andre er 4 ganger raskere, men ikke alle enheter støtter den
  • *modul- peker til en perifer modul, for eksempel &_GPIO_MODULE_I2C1_PB67, la oss ikke glemme det her Kodeassistent (ctrl-mellomrom ) hjelper mye.
La oss først sjekke om bussen er ledig, det er en funksjon for dette I2Cn_Is_Idle(); returnerer 1 hvis bussen er ledig, og 0 hvis det er bytte på den.

I2Cn_Start();
Hvor n- nummeret på den brukte i2c-modulen til mikrokontrolleren vår. Funksjonen vil returnere 0 hvis det er feil på bussen og 1 hvis alt er i orden.

For å overføre data til slaven bruker vi funksjonen:

I2Cn_Write(usignert char slave_address, unsigned char *buf, unsigned long count, unsigned long END_mode);

  • n- nummeret på modulen som brukes
  • slave_adresse- 7-bits slaveadresse.
  • *buff- en peker til våre data - en byte eller byte-array.
  • telle- antall overførte databyte.
  • END_modus- hva du skal gjøre etter overføring av data til slaven, END_MODE_STOPP - sende et signal STOPPE, eller END_MODE_RESTART Send igjen START, genererer et signal OMSTART og gjøre det klart for avdelingen at økten med ham ikke er over og data vil nå bli lest fra ham.
For å lese data fra slaven, bruk funksjonen:

I2Cn_Read(char slave_address, char *ptrdata, unsigned long count, unsigned long END_mode);

  • n- nummeret på modulen som brukes
  • slave_adresse- 7-bits slaveadresse.
  • *buff- en peker til en variabel eller matrise som vi mottar data i, skriv char eller short int
  • telle- antall databyte mottatt.
  • END_modus- hva du skal gjøre etter å ha mottatt data fra slaven - END_MODE_STOPP - sende et signal STOPPE, eller END_MODE_RESTART sende et signal OMSTART.
La oss prøve å koble noe til vår MK. Til å begynne med: den utbredte PCF8574(A) mikrokretsen, som er en utvidelse av inngangs-/utgangsporter som styres via i2c-bussen. Denne brikken inneholder bare ett internt register, som er dens fysiske I/O-port. Det vil si at hvis du sender en byte til henne, vil den umiddelbart bli utsatt for konklusjonene hennes. Hvis du teller en byte fra den (Send START adresse med leseflagg, signal RESTERT, lese dataene og til slutt generere et signal STOPPE) så vil den reflektere de logiske tilstandene på utgangene. La oss koble til mikrokretsen vår i samsvar med dataarket:


Mikrokretsadressen er dannet fra tilstanden til pinnene A0, A1, A2. For mikrokrets PCF8574 adressen vil være: 0100A0A1A2. (For eksempel har vi A0, A1, A2 på et høyt nivå, så adressen til mikrokretsen vår vil være 0b0100 111 = 0x27). Til PCF8574A - 0111A0A1A2, som med vårt koblingsskjema vil gi adressen 0b0111 111 = 0x3F. Hvis for eksempel A2 er koblet til jord, så er adressen for PCF8574A vil 0x3B. Totalt kan 16 mikrokretser monteres samtidig på én i2c-buss, 8 PCF8574A og PCF8574 hver.

La oss prøve å overføre noe, initialisere i2c-bussen og overføre noe til vår PCF8574.

#define PCF8574A_ADDR 0x3F //Adressen til vår PCF8574 void I2C_PCF8574_WriteReg(usignert char wData) ( I2C1_Start(); // Generer START-signalet I2C1_Write(PCF8574A_ADDR,&w overføring av /ENDa_ADDR,&w overføring av /ENDa_ADDR,&w ); STOPP signal) char PCF8574A_reg ; // variabelen som vi skriver i PCF8574 void main () ( I2C1_Init_Advanced(400000, &_GPIO_MODULE_I2C1_PB67); // Start I2C delay_ms(25); // Vent litt PCF8574A_reg.b0 = 0; /_reg.7 den første lysdioden PCF8574A_reg.b0 = 0; /_/81. = 1; // slå av den andre lysdioden mens (1) ( delay_ms(500); PCF8574A_reg.b0 = ~PCF8574A_reg.b0; PCF8574A_reg.b1 = ~PCF8574A_reg.b1; // inverter statusen til lysdiodene I2C_PCFrite85 (inverter statusen til lysdiodene I2C_PCFrite54_PCF85) ; // overføre data til vår PCF8574))
Vi kompilerer og kjører programmet vårt og ser at lysdiodene våre blinker vekselvis.
Jeg koblet LED-katoden til vår PCF8574 av en grunn. Saken er at når en logisk 0 tilføres utgangen, trekker mikrokretsen ærlig sin utgang til bakken, men når en logisk 1 blir brukt, kobler den den til + strøm gjennom en strømkilde på 100 μA. Det vil si at du ikke kan få en "ærlig" logisk 1 ved utgangen. Og du kan ikke tenne en LED med 100 µA. Dette ble gjort for å konfigurere PCF8574-utgangen til inngangen uten ekstra registre. Vi skriver ganske enkelt til utgangsregister 1 (sett i hovedsak pin-tilstandene til Vdd) og kan ganske enkelt kortslutte det til jord. Den nåværende kilden vil ikke tillate utgangstrinnet til vår I/O-utvider å "brenne ut". Hvis benet er trukket til bakken, så er jordpotensialet på det, og logisk 0 leses. Hvis benet trekkes til +, så leses logisk 1. På den ene siden er det enkelt, men på den andre. du bør alltid huske dette når du arbeider med disse mikrokretsene.


La oss prøve å lese tilstanden til pinnene til utvidelsesbrikken vår.

#define PCF8574A_ADDR 0x3F //Adressen til vår PCF8574 void I2C_PCF8574_WriteReg(usignert char wData) ( I2C1_Start(); // Generer START-signalet I2C1_Write(PCF8574A_ADDR, 1,ST og END OPP dataene fra /ENDA_ADDR, 1,ST og ENDO); STOPP signal) void I2C_PCF8574_ReadReg (usignert char rData) ( I2C1_Start(); // Generer START-signalet I2C1_Read(PCF8574A_ADDR, &rData, 1, END_MODE_STOP); // Les 1 byte med data og generer et PCFreg8-signal og generer et PCFreg85-signal. //variabel som vi skriver til PCF8574 char PCF8574A_out; // variabelen vi leser inn og PCF8574 char lad_state; // LED-en vår er på eller av void main () ( I2C1_Init_Advanced(400000, &_GPIO_MODULE_I2C1_PB67); // Start I2C delay_ms(25); // Vent litt PCF8574A_reg.b0 = 0; // tenn den første LEDA_reg5.7b11 LEDA_reg5. 1; / / slå av den andre LED-en PCF8574A_reg.b6 = 1; // Trekk pinnene 6 og 7 til strøm. PCF8574A_reg.b7 = 1; mens (1) ( delay_ms(100); I2C_PCF8574_WriteReg (PCF8574A_reg.b7_reg); til PCF8574 I2C_PCF8574_ReadReg (PCF8574 A_out ); // les fra PCF8574 if (~PCF8574A_out.b6) PCF8574A_reg.b0 = ~PCF8574A_reg.b0; // Hvis 1 og 6-knappen er trykket på 5-bit fra 0. slå LED-en vår på/av) hvis (~PCF8574A_out .b7) PCF8574A_reg.b1 = ~PCF8574A_reg.b1; // lignende for 2 knapper og 2 LED-er ) )
Nå ved å trykke på knappene slår vi LED-en vår på eller av. Mikrokretsen har en annen utgang INT. En puls genereres på den hver gang tilstanden til pinnene til vår I/O-utvider endres. Ved å koble den til den eksterne avbruddsinngangen til vår MK (jeg vil fortelle deg hvordan du konfigurerer eksterne avbrudd og hvordan du arbeider med dem i en av de følgende artiklene).

La oss bruke portutvideren vår til å koble til en tegnskjerm gjennom den. Det er veldig mange av disse, men nesten alle er bygget på grunnlag av en kontrollerbrikke HD44780 og hans kloner. For eksempel brukte jeg en LCD2004-skjerm.


Dataarket for den og HD44780-kontrolleren kan enkelt finnes på Internett. La oss koble skjermen vår til henholdsvis PCF8574 og hennes til STM32.

HD44780 bruker et grensesnitt med parallellport. Data overføres av 8 (per klokkesyklus) eller 4 (per 2 klokkesykluser) portpulser ved pinnen E. (leses av skjermkontrolleren på en synkende kant, overgang fra 1 til 0). Konklusjon R.S. indikerer om vi sender data til skjermen vår ( RS = 1) (tegnene den skal vise er faktisk ASCII-koder) eller kommandoen ( RS = 0). RW angir retningen for dataoverføring, skriving eller lesing. Vanligvis skriver vi data til skjermen, så ( RW=0). Resistor R6 kontrollerer displaykontrasten. Du kan ikke bare koble kontrastjusteringsinngangen til jord eller strøm, ellers vil du ikke se noe.. VT1 brukes til å slå skjermens bakgrunnsbelysning av og på i henhold til MK-kommandoer. MicroC har et bibliotek for å jobbe med slike skjermer via et parallelt grensesnitt, men vanligvis er det dyrt å bruke 8 ben på en skjerm, så jeg bruker nesten alltid PCF8574 for å jobbe med slike skjermer. (Hvis noen er interessert, vil jeg skrive en artikkel om arbeid med HD44780-baserte skjermer innebygd i MicroC via et parallelt grensesnitt.) Utvekslingsprotokollen er ikke spesielt komplisert (vi vil bruke 4 datalinjer og overføre informasjon i 2 klokkesykluser), det vises tydelig av følgende tidsdiagram:


Før data overføres til skjermen vår, må den initialiseres ved å sende servicekommandoer. (beskrevet i dataarket, her presenterer vi kun de mest brukte)

  • 0x28- kommunikasjon med indikatoren via 4 linjer
  • 0x0C- aktiver bildeutgang, deaktiver markørvisning
  • 0x0E- aktiver bildeutgang, aktiver markørvisning
  • 0x01- slett indikatoren
  • 0x08- deaktiver bildeutgang
  • 0x06- etter at symbolet vises, beveger markøren seg 1 kjent sted
Siden vi må jobbe med denne indikatoren ganske ofte, vil vi lage et plug-in-bibliotek "i2c_lcd.h" . For dette formålet i Prosjektleder Overskriftsfiler og velg Legg til ny fil . La oss lage vår header-fil.

#define PCF8574A_ADDR 0x3F //Adressen til vår PCF8574 #define DB4 b4 // Korrespondanse mellom PCF8574 pinnene og indikatoren #define DB5 b5 #define DB6 b6 #define DB7 b7 #define EN b3 #define RW b2 BL b0 // bakgrunnslyskontroll #define displenth 20 // antall tegn i vår displaylinje statisk usignert tegn BL_status; // variabel som lagrer tilstanden til bakgrunnsbelysningen (på/av) void lcd_I2C_Init(void); // Display og PCF8574 initialiseringsfunksjon void lcd_I2C_txt(char *pnt); // Viser en tekstlinje, parameteren er en peker til denne linjen void lcd_I2C_int(int pnt); // Viser verdien av en heltallsvariabel, parameteren er utgangsverdien void lcd_I2C_Goto(unsigned short row, unsigned short col); // flytter markøren til den angitte posisjonen, parametere rad - linje (fra 1 til 2 eller 4 avhengig av displayet) og col - (fra 1 til displenth)) void lcd_I2C_cls(); // Tømmer skjermen void lcd_I2C_backlight (usignert kort int-tilstand); // Aktiverer (når du sender 1 og deaktiverer - når du sender 0 skjermens bakgrunnsbelysning)
La oss nå beskrive funksjonene våre, igjen går vi til Prosjektleder høyreklikk på mappen Kilder og velg Legg til ny fil . Opprett en fil "i2c_lcd.c" .

#include "i2c_lcd.h" //inkluder vår overskriftsfil char lcd_reg; // midlertidig lagringsregister for data sendt til PCF8574 void i2c_pcf8574_writeReg (usignert char wdata) // funksjon for å sende data via i2c til PCF8574 -brikken (i2a_addr, & i2c1_write (PCF8574a_addr, &); com) //funksjon for å sende en kommando til skjermen vår ( lcd_reg = 0; //skriv 0 til det midlertidige registeret lcd_reg.BL = BL_status.b0; //sett bakgrunnsbelysningspinnen i samsvar med verdien til variabelen som lagrer bakgrunnsbelysningen state lcd_reg.DB4 = com.b4; // sett de 4 mest signifikante bitene av kommandoen vår til indikatordatabussen lcd_reg.DB5 = com.b5; lcd_reg.DB6 = com.b6; lcd_reg.DB7 = com.b7; lcd_reg .EN = 1; //sett strobeutgangen til 1 I2C_PCF8574_WriteReg ( lcd_reg); //skriv til PCF8574-registeret, sender faktisk data til indikatoren delay_us (300); //vent på timeout lcd_reg.EN = 0; // tilbakestill strobe-pulsen til 0, indikatoren leser dataene I2C_PCF8574_WriteReg (lcd_reg); delay_us (300) ; lcd_reg = 0; lcd_reg.BL = BL_status.b0; lcd_reg.DB4 = com.b0; //minst signifikant for the 4; biter lcd_reg.DB5 = com.b1; lcd_reg.DB6 = com.b2; lcd_reg.DB7 = com.b3; lcd_reg.NO = 1; I2C_PCF8574_WriteReg(lcd_reg); delay_us(300); lcd_reg.NO = 0; I2C_PCF8574_WriteReg(lcd_reg); delay_us(300); ) void LCD_CHAR (usignert tegn com) //sende data (ASCII-tegnkode) til indikatoren ( lcd_reg = 0; lcd_reg.BL = BL_status.b0; lcd_reg.EN = 1; lcd_reg.RS = 1; //sende et tegn er forskjellig fra å sende kommandoer ved å sette RS-biten til 1 lcd_reg.DB4 = com.b4; //sett de 4 mest signifikante bitene ved inngangene lcd_reg.DB5 = com.b5; lcd_reg.DB6 = com.b6; lcd_reg.DB7 = com.b7; I2C_PCF8574_WriteReg (lcd_reg); delay_us (300); lcd_reg.EN = 0; // tilbakestill strobe-pulsen til 0, indikatoren leser dataene I2C_PCF8574_WriteReg (lcdus_reg; = 0 delay_usc0; .BL = BL_status.b0; lcd_reg.EN = 1; lcd_reg.RS = 1; lcd_reg.DB4 = com.b0; //sett de 4 minst signifikante bitene ved inngangene lcd_reg.DB5 = com.b1; lcd_reg.DB6 = com.b2; lcd_reg.DB7 = com.b3; I2C_PCF8574_WriteReg ( lcd_reg); delay_us (300); lcd_reg.EN = 0; I2C_PCF8574_WriteReg (lcd_reg); delay_us (300); vanced(400000, &_GPIO_MODULE_I2C1_PB67 ); //initialiser vår I2c-modul i M​K delay_ms(200) ;lcd_Command(0x28); // Visning i 4 biter per klokkemodus delay_ms (5); lcd_Command(0x08); //Deaktiver datautgang til displayet delay_ms (5); lcd_Command(0x01); //Slett skjermen delay_ms (5); lcd_Command(0x06); //Aktiver automatisk markørskift etter visning av symbolet delay_ms (5); lcd_Command(0x0C); //Slå på visning av informasjon uten å vise cursor delay_ms (25); ) void lcd_I2C_txt(char *pnt) //Skriv ut en streng med tegn til skjermen (usignert kort int i; //temporary character array index variabel char tmp_str; //temporary array of characters, lengde 1 større enn lengden på skjermen linje, siden linjen må avsluttes сiv med et NULL ASCII-tegn 0x00 strncpy(tmp_str, pnt, displenth); //kopier ikke mer enn displenth tegn av den opprinnelige strengen til vår midlertidige streng for (i=0; i La oss nå koble det nyopprettede biblioteket til filen med vår hovedfunksjon:

#include "i2c_lcd.h" //inkluder vår header-fil usignert int i; //temporary variabel counter void main() ( lcd_I2C_Init(); //initialiser skjermen lcd_I2C_backlight (1); //slå på bakgrunnsbelysningen lcd_I2C_txt ("Hei habrahabr"); //vis linjen mens (1) ( delay_ms( 1000); lcd_I2C_Goto (2,1); //gå til tegn 1 på linje 2 lcd_i2c_int (i); //vis verdien i++; //øk telleren ))

Hvis alt er satt sammen riktig, bør vi se tekst på indikatoren og en teller som øker hvert sekund. Generelt ikke noe komplisert :)

I den neste artikkelen vil vi fortsette å forstå i2c-protokollen og enhetene som fungerer med den. La oss vurdere å jobbe med EEPROM 24XX-minne og MPU6050 akselerometer/gyroskop.

I dag er det en ny gjest på operasjonsbordet vårt, dette er et Microchip-produkt, portutvideren MCP23008-E. Denne tingen er ment (som navnet tilsier) for å øke antall I/O-ben til mikrokontrolleren hvis de plutselig blir utilstrekkelige. Selvfølgelig, hvis vi trenger beina og utgangene, kan vi ta det og ikke bekymre oss for det. Hvis du trenger input ben, så finnes det en løsning basert på streng logikk. Trenger vi både innganger og utganger og også en kontrollert pull-up for inngangene, så er en portexpander kanskje den mest normale løsningen. Når det gjelder prisen på enheten, er den veldig beskjeden - omtrent en dollar. I denne artikkelen vil jeg prøve å beskrive i detalj hvordan du kontrollerer denne mikrokretsen ved hjelp av AVR-mikrokontrolleren.

Først litt om egenskapene:

  • 8 uavhengige portstifter
  • Grensesnitt for kommunikasjon med omverdenen - I2C (frekvens opptil 1,7 MHz)
  • Tilpassbar pull-up for innganger
  • Kan rykke i beinet når tilstanden til visse innganger endres
  • Tre innganger for å stille inn adressen til mikrokretsen (du kan henge 8 enheter på en buss)
  • Driftsspenning fra 1,8 til 5,5 volt
  • Lavt strømforbruk
  • Stort utvalg av kabinetter (PDIP/SOIC/SSOP/QFN)

Jeg foretrekker å bruke grensesnittet I2C, det tiltrekker meg med et lite antall ledninger :-) Men hvis du trenger veldig høy hastighet (opptil 10 MHz), må du bruke SPI-grensesnittet som er til stede i MCP23S08. Forskjellen mellom MCP23S08 og MCP23008, slik jeg forstår det, er bare i grensesnittet og i antall ben for å angi adressen til brikken. Hvem liker noe kortere? Pinouten til mikruhi er i dataarket, den er ikke interessant på noen måte og vil ikke bli vurdert her. La oss derfor umiddelbart gå videre til hvordan du begynner å jobbe med denne enheten. Alt arbeid kommer ned til å skrive og lese visse data fra registrene til mikrokretsen. Til min glede var det ikke mange registre i det hele tatt - bare elleve. Å skrive og lese data fra registre er veldig enkelt, post om det. Det er riktig at vi ikke snakker om registre, men prinsippet er det samme. Nå gjenstår det bare å finne ut hvilke registre man skal lese hva fra og hvilke registre man skal skrive hva man skal. Dataarket vil selvfølgelig hjelpe oss med dette. Fra dataarket lærer vi at mikrokretsen har følgende registre:

IODIR register
Angir retningen dataene flyter i. Hvis biten som tilsvarer en bestemt etappe er satt til én, så er etappen en inngang. Avslutt hvis den tilbakestilles til null. Kort fortalt er dette registeret analogt med DDRx i AVR (bare i AVR er 1 en utgang og 0 er en inngang).

IPOL register
Hvis en registerbit er satt, er inngangsinversjon aktivert for den tilsvarende etappen. Dette betyr at hvis du legger en stokk på benet. null da fra GPIO-registeret regnes som en og omvendt. Nytten av denne funksjonen er svært tvilsom.

GPINTEN register
Hver bit av dette registeret tilsvarer en spesifikk portpinne. Hvis biten er satt, kan den korresponderende portpinnen konfigurert som en inngang forårsake et avbrudd. Hvis biten tilbakestilles, vil det ikke være noen avbrudd, uansett hva du gjør med portbenet. Avbruddsbetingelsene er satt av følgende to registre.

DEFVAL register
Den nåværende verdien av pinnene som er konfigurert for inngang sammenlignes konstant med dette registeret. Hvis den nåværende verdien plutselig begynner å avvike fra det som er i dette registeret, oppstår det et avbrudd. Enkelt sagt, hvis biten er satt, vil avbruddet oppstå når nivået endres fra høyt til lavt på den tilsvarende etappen. Hvis biten tilbakestilles, skjer avbruddet på en stigende kant.

INTCON register
Hver bit av dette registeret tilsvarer en spesifikk portpinne. Hvis biten er klar, forårsaker enhver endring i det logiske nivået til det motsatte et avbrudd. Hvis biten er satt, påvirkes forekomsten av avbruddet av DEFVAL-registeret (ellers ignoreres det fullstendig).

IOCON register
Dette er innstillingsregisteret. Den består av fire biter:
SEQOP— bit kontroller adresse auto-økning. Hvis den er installert, er automatisk økning deaktivert, ellers er den aktivert. Hvis vi slår den av, kan vi lese verdien av det samme registeret veldig raskt på grunn av at vi ikke trenger å sende adressen hver gang. Hvis du raskt trenger å lese alle 11 registre etter tur, må autoøkning være aktivert. Hver gang en byte leses fra registeret, vil selve adressen øke med én og trenger ikke å overføres.
DISSLW– hvem vet hva denne biten er. Uansett hvordan jeg vrir på det, fungerer alt fortsatt. Jeg blir glad hvis noen forklarer.
HAEN— INT utgangsinnstillingsbit. Hvis den er satt, er pinnen konfigurert som en åpen drenering, hvis biten er tilbakestilt, bestemmer det aktive nivået på INT-benet INTPOL-biten
INTPOL— bestemmer det aktive nivået på INT-benet. Hvis det er satt, er det aktive nivået ett, ellers null.

Det er fortsatt litt HAEN men den brukes ikke i denne brikken (slår av/på maskinvareadresseringspinner i MCP23S08)

GPPU-register
Styrer input pull-up. Hvis biten er satt, vil en pull-up til strømforsyningen gjennom en 100 kOhm motstand vises på den tilsvarende pinnen.

INTF register
Avbryt flaggregister. Hvis biten er satt, betyr dette at den tilsvarende portbenet forårsaket et avbrudd. Selvfølgelig må avbrudd for de nødvendige benene være aktivert i GPINTEN-registeret

INTCAP register
Når et avbrudd oppstår, leses hele porten inn i dette registeret. Hvis det er flere avbrudd etter dette, vil ikke innholdet i dette registeret bli overskrevet av den nye verdien før vi leser den eller GPIO.

GPIO register
Når vi leser data fra registeret, leser vi de logiske nivåene på pinnene til porten. Når vi registrerer data, setter vi logiske nivåer. Når du skriver til dette registeret, skrives de samme dataene automatisk til OLAT-registeret

OLAT register
Ved å skrive data til dette registeret sender vi dataene til porten. Leser du data derfra, vil du lese det som ble skrevet ned, og ikke hva som faktisk står ved portinngangene. For å lese innganger bruker vi kun GPIO.

Beskrivelsen av registrene, etter min mening, er ganske normal og burde ikke forvirre noen. La oss nå, bare for moro skyld, skrive en liten demo som vil spørre 4 knapper koblet til portutvideren, og avhengig av tilstanden, tenne eller slå av 4 tilsvarende lysdioder koblet til den samme utvideren. Eksemplet vil være så enkelt som mulig, uten noen avbrudd. Vi leser ganske enkelt konstant statusen til knappene og viser denne tilstanden på LED-ene. Diagrammet av enheten vår vil se slik ut:

Avbruddspinnen trenger ikke å være koblet til, vi trenger den ikke her, men opptrekket for tilbakestillingsbenet er nødvendig! Jeg brukte mye tid før jeg skjønte at portutvideren min ble tilbakestilt med jevne mellomrom på grunn av manglende stramming. La oss nå komme til koden. Selvfølgelig skal vi skrive videre. Enkel kode:

Program rashiritel; const AdrR=%01000001; //Adressen til brikken med lesebiten const AdrW=%01000000; //Adressen til brikken med bitposten var r:byte; ///Prosedyren skriver data fra Dat-variabelen til registeret på adressen Adr Procedure WriteReg(Dat,Adr:byte); Begynn TWI_Start(); TWI_Skriv(AdrW); TWI_Skriv(Adr); TWI_Write(Dat); TWI_Stopp(); Slutt; ///Funksjonen returnerer registerverdien på adressen Adr Function ReadReg(Adr:byte):byte; var a:byte; Begynn TWI_Start(); TWI_Skriv(AdrW); TWI_Skriv(Adr); TWI_Start(); TWI_Skriv(AdrR); a:=TWI_Read(0); TWI_Stopp(); resultat:=a; Slutt; begynne TWI_INIT(200000); ///Initialiserer i2c WriteReg(%00001111,0x00); //De laveste 4 bitene er innganger og de resterende 4 bitene er utganger WriteReg(%00001111,0x06); //På pull-up for 4 innganger Mens TRUE start r:=ReadReg(0x09); //Les tilstanden til inngangene r:= IKKE r; //Det er nødvendig å invertere bitene, ellers lyser LED-ene når knappen slippes r:= r shl 4; //Skift 4 biter til venstre... WriteReg(r,0x0A); //Vis statusen til knappene slutt; slutt.

Publisert 26.10.2016

I forrige artikkel så vi på driften av STM32 med I 2 C-bussen som en master. Det vil si at han var leder og avhørte sensoren. La oss nå gjøre STM32 til en slave og svare på forespørsler, det vil si at den selv fungerer som en sensor. Vi vil allokere 255 byte minne for registre med adresser fra 0 til 0xFF, og la masteren skrive/lese dem. Og for å gjøre eksemplet ikke så enkelt, la oss gjøre vår STM32 til en analog-til-digital-omformer med et I 2 C-grensesnitt. ADC-en vil behandle 8 kanaler. Kontrolleren vil gi resultatene av transformasjonene til Master ved lesing fra registre. Siden resultatet av ADC-konverteringen er 12 biter, trenger vi 2 registre (2 byte) for hver ADC-kanal.

i2c_slave.h inneholder innstillinger:

I2CSLAVE_ADDR– adressen til enheten vår;

ADC_ADDR_START– startadressen til registrene som er ansvarlige for resultatene av ADC-konverteringer.

I fil i2c_slave.c vi er mest interessert i funksjoner get_i2c1_ram Og set_i2c1_ram. Funksjon get_i2c1_ram er ansvarlig for å lese data fra registre. Den returnerer data fra den angitte adressen, som er gitt til masteren. I vårt tilfelle leses dataene fra matrisen i2c1_ram, men hvis masteren ber om registeradresser fra området som er tildelt for ADC-resultater, sendes ADC-konverteringsdataene.

get_i2c1_ram:

Uint8_t get_i2c1_ram(uint8_t adr) ( //ADC data if ((ADC_ADDR_START<= adr) & (adr < ADC_ADDR_START + ADC_CHANNELS*2)) { return ADCBuffer; } else { // Other addresses return i2c1_ram; } }

Funksjon set_i2c1_ram– skriver data mottatt fra Master inn i registre med spesifisert adresse. I vårt tilfelle skrives dataene ganske enkelt til en matrise i2c1_ram. Men dette er valgfritt. Du kan for eksempel legge til en sjekk og, når et bestemt nummer kommer til en bestemt adresse, utføre noen handlinger. På denne måten kan du sende forskjellige kommandoer til mikrokontrolleren.

set_i2c1_ram:

Void set_i2c1_ram(uint8_t adr, uint8_t val) ( i2c1_ram = val; return; )

Initialisering er ganske enkel:

Int main(void) ( SetSysClockTo72(); ADC_DMA_init(); I2C1_Slave_init(); while(1) ( ) )

Først setter vi den maksimale driftsfrekvensen til kontrolleren. Maksimal hastighet kreves når eventuelle forsinkelser på I 2 C-bussen må unngås. Da starter vi ADC-driften ved hjelp av DMA. OM . OM . Og til slutt initialiserer vi I 2 C-bussen som Slave. Som du kan se, ingenting komplisert.

La oss nå koble vår STM32-modul til Raspberry Pi. La oss koble potensiometre til ADC-kanalene. Og vi vil lese ADC-indikatorer fra kontrolleren vår. Ikke glem at for at I 2 C-bussen skal fungere, må du installere opptrekksmotstander på hver linje av bussen.

I Raspberry-konsollen, la oss sjekke om enheten vår er synlig på I 2 C-bussen (om det):

I2cdetect -y 1

Som du kan se, enhetens adresse 0x27, selv om vi spesifiserte 0x4E. Når du har tid, tenk på hvorfor dette skjedde.

For å lese fra registrene til I 2 C-Slave-enheten, utfør kommandoen:

I2cget -y 1 0x27 0x00

Hvor:
0x27– enhetsadresse,
0x00– registeradresse (0x00…0xFF).

For å skrive til registrene til I 2 C-Slave-enheten, utfør kommandoen:

I2cset -y 1 0x27 0xA0 0xDD

De:
0x27– enhetsadresse,
0xA0– registeradresse
0xDD-8-biters data (0x00…0xFF)

Den forrige kommandoen skrev tallet 0xDD til registeret 0xA0(du kan skrive til de første 16 registrene, men det er ingen vits, men de er forbeholdt ADC). La oss nå lese:

I2cget -y 1 0x27 0xA0

For å forenkle prosessen med å lese ADC-kanaldata, skrev jeg et skript:

#!/usr/bin/env python import smbus import time bus = smbus.SMBus(1) adresse = 0x27 while (1): ADC = (); for i i området(0, 8): LBS = bus.read_byte_data(adresse, 0x00+i*2) MBS = bus.read_byte_data(adresse, 0x00+i*2+1) ADC[i] = MBS*256 + LBS skriv ut ADC time.sleep(0.2)

Den poller og viser resultatene av alle de 8 ADC-kanalene til konsollen.

På lignende måte kan du kombinere flere mikrokontrollere. En av dem skal være Master(), den andre Slave.

Jeg ønsker deg suksess!




Topp