Leser datablad 2: SPI på STM32; PWM, tidtakere og avbrudd på STM8
В første del Jeg prøvde å fortelle hobbyelektronikkingeniører som vokste opp fra Arduino-bukser hvordan og hvorfor de skulle lese datablad og annen dokumentasjon for mikrokontrollere. Teksten viste seg å være stor, så jeg lovet å vise praktiske eksempler i en egen artikkel. Vel, han kalte seg en melkesopp...
I dag vil jeg vise deg hvordan du bruker dataark for å løse ganske enkle, men nødvendige for mange prosjekter, oppgaver på STM32 (Blue Pill) og STM8-kontrollere. Alle demoprosjekter er dedikert til favoritt-LED-ene mine, vi vil tenne dem i store mengder, som vi må bruke alle slags interessante periferiutstyr for.
Teksten viste seg igjen å være enorm, så for enkelhets skyld lager jeg innholdet:
Ansvarsfraskrivelse: Jeg er ikke en ingeniør, jeg later ikke til å ha dyp kunnskap innen elektronikk, artikkelen er ment for amatører som meg. Faktisk så jeg meg selv for to år siden som målgruppen. Hvis noen da hadde fortalt meg at dataark for en ukjent brikke ikke var skummelt å lese, ville jeg ikke ha brukt mye tid på å lete etter noen kodebiter på Internett og finne opp krykker med saks og teip.
Fokuset i denne artikkelen er på dataark, ikke prosjekter, så koden er kanskje ikke veldig ryddig og ofte trang. Prosjektene i seg selv er veldig enkle, selv om de passer for et første bekjentskap med den nye brikken.
Jeg håper at artikkelen min vil hjelpe noen på et lignende stadium av fordypning i hobbyen.
STM32
16 lysdioder med DM634 og SPI
Et lite prosjekt som bruker Blue Pill (STM32F103C8T6) og DM634 LED-driver. Ved å bruke dataark vil vi finne ut driveren, STM IO-porter og konfigurere SPI.
DM634
Taiwanesisk brikke med 16 16-bits PWM-utganger, kan kobles i kjeder. Low-end 12-bits modellen er kjent fra et innenlandsk prosjekt Lightpack. På en gang, ved å velge mellom DM63x og den velkjente TLC5940, valgte jeg DM av flere grunner: 1) TLC på Aliexpress er definitivt falsk, men denne er ikke det; 2) DM har en autonom PWM med egen frekvensgenerator; 3) det kunne kjøpes billig i Moskva, i stedet for å vente på en pakke fra Ali. Og det var selvfølgelig interessant å lære å kontrollere brikken selv, i stedet for å bruke et ferdig bibliotek. Brikker presenteres nå hovedsakelig i SSOP24-pakken; de er enkle å lodde til en adapter.
Siden produsenten er taiwansk, datablad brikken er skrevet på kinesisk engelsk, noe som betyr at det blir moro. Først ser vi på pinouten (Pin tilkobling) for å forstå hvilket ben som skal kobles til, og en beskrivelse av pinnene (Pin Beskrivelse). 16 pinner:
DC synkekilder (åpent avløp)
Synke / Utgang for åpent avløp - avløp; kilde til innstrømmende strøm; utgangen er koblet til jord i aktiv tilstand - LED-ene er koblet til driveren med katoder. Elektrisk er dette selvfølgelig ikke et "åpent avløp" (åpent sluk), men i dataark finnes ofte denne betegnelsen for pinner i dreneringsmodus.
Eksterne motstander mellom REXT og GND for å angi utgangsstrømverdien
En referansemotstand er installert mellom REXT-pinnen og jord, som kontrollerer den interne motstanden til utgangene, se grafen på side 9 i dataarket. I DM634 kan denne motstanden også kontrolleres av programvare, og stille inn den generelle lysstyrken (global lysstyrke); Jeg vil ikke gå inn på detaljer i denne artikkelen, jeg legger bare en 2.2 - 3 kOhm motstand her.
For å forstå hvordan du kontrollerer brikken, la oss se på beskrivelsen av enhetsgrensesnittet:
Ja, her er det, kinesisk engelsk i all sin prakt. Å oversette dette er problematisk, du kan forstå det hvis du ønsker det, men det er en annen måte - se på hvordan tilkoblingen til den funksjonelt lignende TLC5940 er beskrevet i dataarket:
... Bare tre pinner kreves for å legge inn data i enheten. Den stigende flanken til SCLK-signalet forskyver dataene fra SIN-pinnen til det interne registeret. Etter at alle data er lastet inn, låser et kort høyt XLAT-signal de sekvensielt overførte dataene inn i de interne registrene. Interne registre er porter som utløses av XLAT-signalnivået. Alle data overføres den viktigste biten først.
Klinke – lås/lås/lås. Stigende kant – forkant av pulsen MSB først – mest betydningsfulle (lengst til venstre) bit fremover. å klokke data – overføre data sekvensielt (bit for bit).
Word klinke finnes ofte i dokumentasjonen for sjetonger og er oversatt på ulike måter, så for forståelsens skyld vil jeg tillate meg selv
et lite pedagogisk programLED-driveren er i hovedsak et skiftregister. "Skift" (skift) i navnet - bitvis bevegelse av data inne i enheten: hver ny bit som dyttes inn, skyver hele kjeden foran seg. Siden ingen ønsker å observere kaotisk blinking av lysdiodene under skiftet, foregår prosessen i bufferregistre atskilt fra arbeidsregistrene med en spjeld (klinke) er et slags venterom hvor bitene er ordnet i ønsket rekkefølge. Når alt er klart, åpnes lukkeren og bitene går på jobb, og erstatter forrige batch. Ord klinke i dokumentasjonen for mikrokretser innebærer nesten alltid en slik demper, uansett i hvilke kombinasjoner den brukes.
Så, dataoverføring til DM634 utføres slik: sett DAI-inngangen til verdien av den mest signifikante biten av den fjerne LED-en, trekk DCK opp og ned; sett DAI-inngangen til verdien av neste bit, trekk DCK; og så videre til alle biter er overført (klokket inn), hvoretter vi trekker LAT. Dette kan gjøres manuelt (bit-smell), men det er bedre å bruke et SPI-grensesnitt spesielt skreddersydd for dette, siden det presenteres på vår STM32 i to eksemplarer.
Blå pille STM32F103
Innledning: STM32-kontrollere er mye mer komplekse enn Atmega328 enn de kan virke skumle. Av hensyn til energisparing er dessuten nesten alle eksterne enheter slått av ved starten, og klokkefrekvensen er 8 MHz fra den interne kilden. Heldigvis skrev STM-programmerere kode som bringer brikken opp til de "beregnede" 72 MHz, og forfatterne av alle IDE-ene jeg kjenner inkluderte den i initialiseringsprosedyren, så vi trenger ikke å klokke (men du kan hvis du virkelig vil). Men du må slå på periferiutstyret.
Dokumentasjon: Blue Pill er utstyrt med den populære STM32F103C8T6-brikken, det er to nyttige dokumenter for den:
Datablad for mikrokontrollere STM32F103x8 og STM32F103xB;
Pinouts – chip pinouts – i tilfelle vi bestemmer oss for å lage brettene selv;
Minnekart – minnekart for en spesifikk brikke. Referansemanualen har et kart for hele linjen, og den nevner registre som vår ikke har.
Tabell med definisjoner av pinner – viser hovedfunksjonene og alternative funksjoner til pinner; for den "blå pillen" kan du finne mer praktiske bilder på Internett med en liste over pinner og deres funksjoner. Derfor googler vi umiddelbart Blue Pill pinout og holder dette bildet for hånden:
NB: det var en feil på bildet fra Internett, som ble notert i kommentarfeltet, takk for det. Bildet er erstattet, men dette er en leksjon - det er bedre å sjekke informasjon ikke fra datablad.
Vi fjerner dataarket, åpner referansehåndboken, og fra nå av bruker vi bare det.
Prosedyre: vi håndterer standard input/output, konfigurerer SPI, slår på nødvendig periferiutstyr.
Inngang Utgang
På Atmega328 er I/O implementert ekstremt enkelt, og derfor kan overfloden av STM32-alternativer være forvirrende. Nå trenger vi bare konklusjoner, men selv disse har fire alternativer:
åpent avløp, push-pull, alternativ push-pull, alternativ åpent avløp
"Pull-push" (Push-pull) er den vanlige utgangen fra Arduino, pinnen kan ta verdien enten HØY eller LAV. Men med "åpent avløp" er det vanskeligheter, selv om alt er enkelt her:
Utgangskonfigurasjon / når porten er tilordnet utgang: / utgangsbuffer aktivert: / – åpen drain-modus: "0" i utgangsregisteret aktiverer N-MOS, "1" i utgangsregisteret forlater porten i Hi-Z-modus ( P-MOS er ikke aktivert ) / – push-pull-modus: "0" i utgangsregisteret aktiverer N-MOS, "1" i utgangsregisteret aktiverer P-MOS.
Hele forskjellen mellom åpent avløp (åpent sluk) fra "push-pull" (Push-pull) er at i den første pinnen ikke kan akseptere HØY-tilstanden: når du skriver en til utgangsregisteret, går den inn i høymotstandsmodus (høy impedans, Hei-Z). Når du skriver null, oppfører pinnen seg likt i begge modusene, både logisk og elektrisk.
I normal utgangsmodus sender pinnen ganske enkelt innholdet i utgangsregisteret. I "alternativet" styres den av tilsvarende periferiutstyr (se 9.1.4):
Hvis en portbit er konfigurert som en alternativ funksjonspinne, deaktiveres pinneregisteret og pinnen kobles til den perifere pinnen.
Alternativ funksjonalitet for hver pinne er beskrevet i Pin Definisjoner Dataarket er på det nedlastede bildet. På spørsmålet om hva du skal gjøre hvis en pinne har flere alternative funksjoner, er svaret gitt av en fotnote i dataarket:
Hvis flere perifere enheter bruker samme pinne, for å unngå konflikt mellom alternative funksjoner, bør kun én perifer enhet brukes om gangen, vekslet ved å bruke den perifere klokkeaktiveringsbiten (i det aktuelle RCC-registeret).
Til slutt har pinner i utgangsmodus også en klokkehastighet. Dette er en annen energisparende funksjon; i vårt tilfelle setter vi den bare til maksimum og glemmer den.
Så: vi bruker SPI, som betyr at to pinner (med data og med et klokkesignal) skal være "alternativ push-pull-funksjon", og en annen (LAT) skal være "vanlig push-pull". Men før vi tildeler dem, la oss ta oss av SPI.
SPI
Nok et lite pedagogisk program
SPI eller Serial Peripheral Interface (serielt perifert grensesnitt) er et enkelt og veldig effektivt grensesnitt for å koble en MK med andre MKer og omverdenen generelt. Prinsippet for driften er allerede beskrevet ovenfor, hvor om den kinesiske LED-driveren (i referansehåndboken, se avsnitt 25). SPI kan operere i master (“master”) og slave (“slave”) modus. SPI har fire grunnleggende kanaler, hvorav ikke alle kan brukes:
MOSI, Master Output / Slave Input: denne pinnen overfører data i mastermodus, og mottar data i slavemodus;
MISO, Master Input / Slave Output: tvert imot, den mottar i masteren, og sender i slaven;
SCK, Serial Clock: stiller inn frekvensen for dataoverføring i masteren eller mottar et klokkesignal i slaven. I hovedsak treffer beats;
SS, Slave Select: ved hjelp av denne kanalen vet slaven at noe er ønsket fra ham. På STM32 heter det NSS, hvor N = negativ, dvs. kontrolleren blir en slave hvis det er jord i denne kanalen. Den kombinerer godt med Open Drain Output-modus, men det er en annen historie.
Som alt annet er SPI på STM32 rik på funksjonalitet, noe som gjør det noe vanskelig å forstå. For eksempel kan det fungere ikke bare med SPI, men også med et I2S-grensesnitt, og i dokumentasjonen er beskrivelsene deres blandet, det er nødvendig å kutte av overskuddet i tide. Vår oppgave er ekstremt enkel: vi trenger bare å sende data ved å bruke bare MOSI og SCK. Vi går til avsnitt 25.3.4 (halvduplekskommunikasjon, halvduplekskommunikasjon), hvor vi finner 1 klokke og 1 ensrettet dataledning (1 klokkesignal og 1 ensrettet datastrøm):
I denne modusen bruker applikasjonen SPI i enten kun overføring eller kun mottak. / Kun overføringsmodus ligner på dupleksmodus: data overføres på overføringspinnen (MOSI i mastermodus eller MISO i slavemodus), og mottakspinnen (henholdsvis MISO eller MOSI) kan brukes som en vanlig I/O-pinne . I dette tilfellet trenger applikasjonen bare å ignorere Rx-bufferen (hvis den leses, vil det ikke være overførte data der).
Flott, MISO-pinnen er ledig, la oss koble LAT-signalet til den. La oss se på Slave Select, som på STM32 kan styres programmatisk, noe som er ekstremt praktisk. Vi leser avsnittet med samme navn i avsnitt 25.3.1 SPI Generell beskrivelse:
Programvarekontroll NSS (SSM = 1) / Slavevalginformasjon er inneholdt i SSI-biten til SPI_CR1-registeret. Den eksterne NSS-pinnen forblir ledig for andre applikasjonsbehov.
Det er på tide å skrive til registrene. Jeg bestemte meg for å bruke SPI2, se etter basisadressen i dataarket - i avsnitt 3.3 Minnekart:
Åpne seksjon 25.3.3 med den selvforklarende tittelen "Konfigurere SPI i mastermodus":
1. Still inn den serielle klokkefrekvensen med bits BR[2:0] i SPI_CR1-registeret.
Registrene er samlet i referansehåndbokdelen med samme navn. adresseskift (Adresseforskyvning) for CR1 – 0x00, som standard slettes alle biter (Tilbakestill verdi 0x0000):
BR-bitene setter kontrollerens klokkedeler, og bestemmer dermed frekvensen som SPI-en vil operere med. Vår STM32-frekvens vil være 72 MHz, LED-driveren, ifølge databladet, opererer med en frekvens på opptil 25 MHz, så vi må dele på fire (BR[2:0] = 001).
2. Still inn CPOL- og CPHA-bitene for å definere forholdet mellom dataoverføring og seriell klokketiming (se diagram på side 240)
Siden vi leser et dataark her og ikke ser på skjemaer, la oss se nærmere på tekstbeskrivelsen av CPOL- og CPHA-bitene på side 704 (SPI Generell beskrivelse):
Klokkefase og polaritet
Ved å bruke CPOL- og CPHA-bitene til SPI_CR1-registeret kan du programmere velge fire tidsforhold. CPOL-biten (klokkepolaritet) kontrollerer tilstanden til klokkesignalet når ingen data blir overført. Denne biten styrer master- og slavemodusene. Hvis CPOL er tilbakestilt, er SCK-pinnen lav i hvilemodus. Hvis CPOL-biten er satt, er SCK-pinnen høy under hvilemodus.
Når CPHA (klokkefase)-biten er satt, er høybit-felle-stroben den andre kanten av SCK-signalet (faller hvis CPOL er klar, stiger hvis CPOL er satt). Dataene fanges opp av den andre endringen i klokkesignalet. Hvis CPHA-biten er klar, er høybit-felle-stroben den stigende flanken til SCK-signalet (fallende flanke hvis CPOL er satt, stigende flanke hvis CPOL er slettet). Data fanges opp ved første endring i klokkesignalet.
Etter å ha absorbert denne kunnskapen, kommer vi til den konklusjon at begge bitene må forbli null, fordi Vi vil at SCK-signalet skal forbli lavt når det ikke er i bruk, og at data skal overføres på den stigende kanten av pulsen (se fig. Rising Edge i DM634-dataarket).
Forresten, her møtte vi først et trekk ved ordforrådet i ST-dataark: i dem er uttrykket "tilbakestill biten til null" skrevet å tilbakestille littOg ikke å rydde litt, som for eksempel Atmega.
3. Still inn DFF-biten for å bestemme om datablokken er 8-biters eller 16-biters format
Jeg tok spesifikt en 16-bit DM634 for ikke å bry meg med å overføre 12-bit PWM-data, som DM633. Det er fornuftig å sette DFF til én:
4. Konfigurer LSBFIRST-biten i SPI_CR1-registeret for å bestemme blokkformatet
LSBFIRST, som navnet antyder, konfigurerer overføring med den minst signifikante biten først. Men DM634 ønsker å motta data fra den mest betydningsfulle biten. Derfor lar vi den tilbakestilles.
5. I maskinvaremodus, hvis inndata fra NSS-pinnen er nødvendig, påfør et høyt signal til NSS-pinnen under hele byteoverføringssekvensen. I NSS-programvaremodus setter du SSM- og SSI-bitene i SPI_CR1-registeret. Hvis NSS-pinnen skal brukes som en utgang, er det kun SSOE-biten som må settes.
Installer SSM og SSI for å glemme NSS-maskinvaremodusen:
#define SSI 0x0100
#define SSM 0x0200
_SPI2_ (_SPI_CR1) |= SSM | SSI; //enable software control of SS, SS high
6. MSTR- og SPE-bitene må stilles inn (de forblir satt bare hvis NSS-signalet er høyt)
Faktisk, med disse bitene utpeker vi vår SPI som en master og slår den på:
SPI er konfigurert, la oss umiddelbart skrive funksjoner som sender byte til driveren. Fortsett å lese 25.3.3 "Konfigurere SPI i mastermodus":
Dataoverføringsordre
Overføringen begynner når en byte skrives til Tx-bufferen.
Databyten lastes inn i skiftregisteret kl parallell modus (fra den interne bussen) under overføringen av den første biten, hvoretter den overføres til sekvensiell MOSI pin-modus, første eller siste bit fremover avhengig av innstillingen til LSBFIRST-biten i CPI_CR1-registeret. TXE-flagget settes etter dataoverføring fra Tx buffer til skiftregister, og genererer også et avbrudd hvis TXEIE-biten i CPI_CR1-registeret er satt.
Jeg fremhevet noen få ord i oversettelsen for å trekke oppmerksomhet til en funksjon ved SPI-implementeringen i STM-kontrollere. På Atmega TXE-flagget (Tx tom, Tx er tom og klar til å motta data) settes først etter at hele byten er sendt ytre. Og her settes dette flagget etter at byten er satt inn i det interne skiftregisteret. Siden det skyves dit med alle bitene samtidig (parallelt), og deretter dataene overføres sekvensielt, settes TXE før byten er fullstendig sendt. Dette er viktig fordi når det gjelder LED-driveren vår, må vi trekke LAT-pinnen etter sending Alle data, dvs. TXE-flagget alene vil ikke være nok for oss.
Det betyr at vi trenger et flagg til. La oss se på 25.3.7 - "Statusflagg":
<…>
OPPTATT flagg
BSY-flagget settes og slettes av maskinvare (skriving til det har ingen effekt). BSY-flagget indikerer tilstanden til SPI-kommunikasjonslaget.
Den tilbakestiller:
når overføringen er fullført (unntatt i mastermodus hvis overføringen er kontinuerlig)
når SPI er deaktivert
når en mastermodusfeil oppstår (MODF=1)
Hvis overføringen ikke er kontinuerlig, slettes BSY-flagget mellom hver dataoverføring
Ok, dette kommer godt med. La oss finne ut hvor Tx-bufferen er plassert. For å gjøre dette, les "SPI Data Register":
Bits 15:0 DR[15:0] Dataregister
Data mottatt eller data som skal overføres.
Dataregisteret er delt inn i to buffere - en for skriving (sendebuffer) og en for lesing (mottaksbuffer). Å skrive til dataregisteret skriver til Tx-bufferen, og lesing fra dataregisteret vil returnere verdien i Rx-bufferen.
Vel, og statusregisteret, der TXE- og BSY-flaggene finnes:
Vel, siden vi trenger å overføre 16 ganger to byte, i henhold til antall LED-driverutganger, noe sånt som dette:
void sendLEDdata()
{
LAT_low();
uint8_t k = 16;
do
{ k--;
dm_shift16(leds[k]);
} while (k);
while (_SPI2_(_SPI_SR) & BSY); // finish transmission
LAT_pulse();
}
Men vi vet ikke hvordan vi skal trekke LAT-pinnen ennå, så vi går tilbake til I/O.
Tilordning av pinner
I STM32F1 er registrene som er ansvarlige for tilstanden til pinnene ganske uvanlige. Det er tydelig at det er flere av dem enn Atmega, men de er også forskjellige fra andre STM-brikker. Seksjon 9.1 Generell beskrivelse av GPIO:
Hver av de generelle I/O-portene (GPIO) har to 32-bits konfigurasjonsregistre (GPIOx_CRL og GPIOx_CRH), to 32-biters dataregistre (GPIOx_IDR og GPIOx_ODR), et 32-bits set/reset-register (GPIOx_BSRR), et 16-bits tilbakestillingsregister (GPIOx_BRR) og et 32- bitblokkeringsregister (GPIOx_LCKR).
De to første registrene er uvanlige, og også ganske upraktiske, fordi de 16 portpinnene er spredt over dem i et "fire bits per bror"-format. De. pinnene null til syv er i CRL, og resten er i CRH. Samtidig inneholder de gjenværende registrene bitene til alle pinnene i porten - ofte forblir halvparten "reservert".
For enkelhets skyld, la oss starte fra slutten av listen.
Vi trenger ikke et blokkeringsregister.
Sett- og tilbakestillingsregistrene er ganske morsomme ved at de delvis dupliserer hverandre: du kan bare skrive alt i BSRR, der de høyere 16 bitene vil tilbakestille pinnen til null, og de lavere vil bli satt til 1, eller du kan også bruk BRR, de nederste 16 bitene tilbakestiller bare pinnen . Jeg liker det andre alternativet. Disse registrene er viktige fordi de gir atomtilgang til pinner:
Atomsett eller tilbakestilling
Det er ikke nødvendig å deaktivere avbrudd når du programmerer GPIOx_ODR på bitnivå: en eller flere biter kan endres med en enkelt atomskriveoperasjon APB2. Dette oppnås ved å skrive en "1" til set/reset-registeret (GPIOx_BSRR eller, kun for tilbakestilling, GPIOx_BRR) til biten som må endres. Andre biter vil forbli uendret.
Dataregistrene har ganske selvforklarende navn - IDR = Input Retning Register, inngangsregister; ODR = Produksjon Retningsregister, utgangsregister. Vi trenger dem ikke i det nåværende prosjektet.
Og til slutt, kontrollregistre. Siden vi er interessert i de andre SPI-pinnene, nemlig PB13, PB14 og PB15, ser vi umiddelbart på CRH:
Og vi ser at vi må skrive noe i biter fra 20 til 31.
Vi har allerede funnet ut ovenfor hva vi vil ha fra pinner, så her vil jeg klare meg uten et skjermbilde, jeg vil bare si at MODE spesifiserer retningen (inngang hvis begge bitene er satt til 0) og pinnehastigheten (vi trenger 50MHz, dvs. både pin til "1"), og CNF setter modusen: vanlig "push-pull" - 00, "alternativ" - 10. Som standard, som vi ser ovenfor, har alle pinner den tredje biten fra bunnen (CNF0), den setter dem i modus flytende inngang.
Siden jeg planlegger å gjøre noe annet med denne brikken, har jeg for enkelhets skyld definert alle mulige MODE- og CNF-verdier for både nedre og øvre kontrollregister.
(LAT_low bare ved treghet, det har alltid vært sånn, la det bli)
Nå er alt flott, men det fungerer ikke. Fordi dette er STM32, sparer de elektrisitet, noe som betyr at du må aktivere klokkefunksjonen av nødvendig periferiutstyr.
Slå på klokkefunksjonen
Klokken, også kjent som Clock, er ansvarlig for klokke. Og vi kunne allerede legge merke til forkortelsen RCC. Vi ser etter det i dokumentasjonen: dette er Tilbakestilling og klokkekontroll.
Som det ble sagt ovenfor, ble heldigvis den vanskeligste delen av klokkeemnet gjort for oss av folk fra STM, noe vi takker dem veldig for (noen en gang vil jeg gi en lenke til Di Halts hjemmeside, for å gjøre det klart hvor forvirrende det er). Vi trenger bare registre som er ansvarlige for å aktivere perifer klokke (Peripheral Clock Enable Registers). Først, la oss finne baseadressen til RCC, den er helt i begynnelsen av "Minnekartet":
Og klikk deretter enten på lenken der du prøver å finne noe i platen, eller, mye bedre, gå gjennom beskrivelsene av aktiveringsregistrene fra seksjonene om aktivere registre. Hvor finner vi RCC_APB1ENR og RCC_APB2ENR:
Og de inneholder følgelig biter som inkluderer klokking av SPI2, IOPB (I/O Port B) og alternative funksjoner (AFIO).
Hvis du har mulighet og lyst til å teste, så koble til DM634 slik: DAI til PB15, DCK til PB13, LAT til PB14. Vi driver driveren fra 5 volt, ikke glem å koble til begrunnelsen.
STM8 PWM
PWM på STM8
Da jeg bare planla denne artikkelen, bestemte jeg meg for, for eksempel, å prøve å mestre funksjonaliteten til en ukjent brikke med kun et dataark, slik at jeg ikke ender opp med en skomaker uten støvler. STM8 var ideell for denne rollen: For det første hadde jeg et par kinesiske brett med STM8S103, og for det andre er det ikke veldig populært, og derfor hviler fristelsen til å lese og finne en løsning på Internett på mangelen på nettopp disse løsningene.
Som standard opererer STM8 med en frekvens på 2 MHz, dette må korrigeres umiddelbart.
HSI (High Speed Intern) klokke
HSI-klokkesignalet er utledet fra en intern 16 MHz RC-oscillator med en programmerbar deler (1 til 8). Den settes i klokkedelerregisteret (CLK_CKDIVR).
Merk: ved starten velges en HSI RC-oscillator med en deler på 8 som ledende kilde for klokkesignalet.
Vi finner registeradressen i dataarket, beskrivelsen i refman og ser at registeret må ryddes:
Siden vi skal kjøre PWM og koble til LED-ene, la oss se på pinouten:
Brikken er liten, mange funksjoner er suspendert på de samme pinnene. Det som står i hakeparenteser er "alternativ funksjonalitet", det byttes av "opsjonsbytes" (alternativbytes) – noe sånt som Atmega-sikringer. Du kan endre verdiene deres programmatisk, men det er ikke nødvendig, fordi Den nye funksjonaliteten aktiveres først etter en omstart. Det er lettere å bruke ST Visual Programmer (lastet ned med Visual Develop), som kan endre disse bytene. Pinouten viser at CH1- og CH2-pinnene til den første timeren er skjult i hakeparenteser; det er nødvendig å sette AFR1- og AFR0-bitene i STVP, og den andre vil også overføre CH1-utgangen til den andre timeren fra PD4 til PC5.
Dermed vil 6 pinner styre LED-ene: PC6, PC7 og PC3 for den første timeren, PC5, PD3 og PA3 for den andre.
Å sette opp selve I/O-pinnene på STM8 er enklere og mer logisk enn på STM32:
kjent fra Atmega DDR dataretningsregister (Dataretningsregister): 1 = utgang;
det første kontrollregisteret CR1, når det sendes ut, setter push-pull-modus (1) eller åpent avløp (0); siden jeg kobler LED-ene til brikken med katoder, lar jeg nuller her;
det andre kontrollregisteret CR2, når det sendes ut, setter klokkehastigheten: 1 = 10 MHz
Last inn automatisk, AR – automatisk lastbar verdi som timeren vil telle til (pulsperiode);
Oppdater hendelse, UEV – en hendelse som oppstår når tidtakeren har telt til AR;
PWM Duty Cycle – PWM arbeidssyklus, ofte kalt "duty factor";
Ta opp/sammenlign verdi – verdi for fangst/sammenligning, som tidtakeren har telt til vil gjøre noe (i tilfelle av PWM, inverterer den utgangssignalet);
Forhåndsinnlastingsverdi – forhåndsinnlastet verdi. Sammenlign verdi kan ikke endres mens tidtakeren tikker, ellers vil PWM-syklusen bryte. Derfor blir nye overførte verdier plassert i en buffer og trukket ut når tidtakeren når slutten av nedtellingen og tilbakestilles;
Kantjustert и Senterjusterte moduser – justering langs grensen og i midten, det samme som Atmels Rask PWM и Fasekorrekt PWM.
OCiREF, utgangssammenligningsreferansesignal – referanseutgangssignal, faktisk, det som vises på den tilsvarende pinnen i PWM-modus.
Som det allerede er klart fra pinouten, har to timere PWM-funksjoner - den første og den andre. Begge er 16-bit, den første har mange tilleggsfunksjoner (spesielt kan den telle både opp og ned). Vi trenger begge å jobbe likt, så jeg bestemte meg for å begynne med den åpenbart dårligere andre, for ikke å bruke noe som ikke er der ved et uhell. Et problem er at beskrivelsen av PWM-funksjonaliteten til alle timere i referansehåndboken er i kapittelet om den første timeren (17.5.7 PWM-modus), så du må hoppe frem og tilbake gjennom dokumentet hele tiden.
PWM på STM8 har en viktig fordel fremfor PWM på Atmega:
Grensejustert PWM
Kontokonfigurasjon fra bunn til topp
Nedenfra og opp-telling er aktiv hvis DIR-biten i TIM_CR1-registeret slettes
Eksempel
Eksemplet bruker den første PWM-modusen. PWM-referansesignalet OCiREF holdes høyt så lenge TIM1_CNT < TIM1_CCRi. Ellers krever det et lavt nivå. Hvis sammenligningsverdien i TIM1_CCRi-registeret er større enn autoload-verdien (TIM1_ARR-register), holdes OCiREF-signalet på 1. Hvis sammenligningsverdien er 0, holdes OCiREF på null....
STM8-timer under oppdater hendelsen sjekker først sammenligne verdi, og produserer først da et referansesignal. Atmegas tidtaker skrus først opp og sammenligner deretter, noe som resulterer i compare value == 0 utgangen er en nål, som må håndteres på en eller annen måte (for eksempel ved å programmere invertering av logikken).
Så hva vi vil gjøre: 8-bit PWM (AR == 255), teller fra bunn til topp, justering langs kanten. Siden lyspærene er koblet til brikken med katoder, skal PWM gi ut 0 (LED på) til sammenligne verdi og 1 etter.
Vi har allerede lest om noen PWM-modus, så vi finner det nødvendige registeret til den andre timeren ved å søke i referansehåndboken etter denne frasen (18.6.8 - TIMx_CCMR1):
110: Første PWM-modus – når man teller fra bunn til topp, er den første kanalen aktiv mens TIMx_CNT < TIMx_CCR1. Ellers er den første kanalen inaktiv. [lenger i dokumentet er det en feilaktig copy-paste fra timer 1] 111: Andre PWM-modus – når man teller fra bunn til topp, er den første kanalen inaktiv mens TIMx_CNT < TIMx_CCR1. Ellers er den første kanalen aktiv.
Siden lysdiodene er koblet til MK med katoder, passer den andre modusen oss (den første også, men det vet vi ikke ennå).
Bit 3 OC1PE: Aktiver pin 1 forhåndsbelastning
0: Forhåndsinnlastingsregister på TIMx_CCR1 er deaktivert. Du kan skrive til TIMx_CCR1 når som helst. Den nye verdien virker umiddelbart.
1: Forhåndslastregister på TIMx_CCR1 er aktivert. Lese-/skriveoperasjoner får tilgang til forhåndsinnlastingsregisteret. Den forhåndsinnlastede verdien TIMx_CCR1 lastes inn i skyggeregisteret under hver oppdateringshendelse.
*Merk: For at PWM-modus skal fungere skikkelig, må forhåndsbelastningsregistrene være aktivert. Dette er ikke nødvendig i enkeltsignalmodus (OPM-biten er satt i TIMx_CR1-registeret).
Ok, la oss slå på alt vi trenger for de tre kanalene til den andre timeren:
Den andre timeren kan bare telle fra bunn til topp, justering langs grensen, ingenting må endres. La oss sette frekvensdeleren, for eksempel, til 256. For den andre timeren er deleren satt i TIM2_PSCR-registeret og er en potens på to:
Alt som gjenstår er å slå på konklusjonene og selve den andre timeren. Det første problemet løses av registre Ta opp/sammenlign aktiver: det er to, tre kanaler spredt over dem asymmetrisk. Her kan vi også lære at det er mulig å endre polariteten til signalet, d.v.s. i prinsippet var det mulig å bruke PWM Mode 1. Vi skriver:
La oss skrive en enkel analog av AnalogWrite(), som vil overføre de faktiske verdiene til tidtakeren for sammenligning. Registrene er navngitt forutsigbart Ta opp/sammenlign registre, er det to av dem for hver kanal: lavordens 8 bits i TIM2_CCRxL og høyordens i TIM2_CCRxH. Siden vi har laget en 8-bits PWM, er det nok å skrive bare de minst signifikante bitene:
Den oppmerksomme leseren vil legge merke til at vi har en litt defekt PWM, som ikke er i stand til å produsere 100% fylling (ved en maksimal verdi på 255, blir signalet invertert for en tidtakersyklus). For lysdioder spiller dette ingen rolle, og den oppmerksomme leseren kan allerede gjette hvordan man fikser det.
PWM på den andre timeren fungerer, la oss gå videre til den første.
Den første timeren har nøyaktig de samme bitene i de samme registrene (det er bare at de bitene som forble "reservert" i den andre timeren, brukes aktivt i den første til alle slags avanserte ting). Derfor er det nok å finne adressene til de samme registrene i dataarket og kopiere koden. Vel, endre verdien på frekvensdeleren, fordi... den første tidtakeren ønsker ikke å motta en potens på to, men en nøyaktig 16-bits verdi i to registre Prescaler High и Lav. Vi gjør alt og... den første timeren fungerer ikke. Hva er i veien?
Problemet kan kun løses ved å se gjennom hele avsnittet om kontrollregistrene til timer 1, hvor vi ser etter den andre timeren ikke har. Det vil være 17.7.30 Pauseregister (TIM1_BKR), hvor det er denne biten:
Det tredje miniprosjektet er å koble åtte RGB LED-er til den andre timeren i PWM-modus og få dem til å vise forskjellige farger. Den er basert på konseptet med LED-multipleksing, som er at hvis du slår av og på lysdioder veldig, veldig raskt, vil det se ut for oss som om de hele tiden er på (vedvarende syn, treghet i visuell persepsjon). Det gjorde jeg en gang noe slikt på Arduino.
Arbeidsalgoritmen ser slik ut:
koblet til anoden til den første RGB LED;
tente den og sendte de nødvendige signalene til katodene;
ventet til slutten av PWM-syklusen;
koblet til anoden til den andre RGB LED;
tente den...
Vel, osv. For vakker drift kreves det selvfølgelig at anoden er tilkoblet og LED-en "tennes" samtidig. Vel, eller nesten. I alle fall må vi skrive en kode som vil sende ut verdier i tre kanaler av den andre timeren, endre dem når UEV er nådd, og samtidig endre den aktive RGB-LED-en.
Siden LED-bytte er automatisk, må vi lage et "videominne" som avbruddsbehandleren vil motta data fra. Dette er en enkel matrise:
uint8_t colors[8][3];
For å endre fargen på en bestemt LED, vil det være nok å skrive de nødvendige verdiene inn i denne matrisen. Og variabelen vil være ansvarlig for nummeret på den aktive LED-en
uint8_t cnt;
Demux
For riktig multipleksing trenger vi merkelig nok en CD74HC238 demultiplekser. Demultiplexer - en brikke som implementerer operatøren i maskinvare <<. Gjennom tre inngangspinner (bit 0, 1 og 2) mater vi den med et tre-bits nummer X, og som svar aktiverer den utgangsnummer (1<<X). De resterende inngangene til brikken brukes til å skalere hele designet. Vi trenger denne brikken ikke bare for å redusere antall okkuperte pinner til mikrokontrolleren, men også for sikkerhet - for ikke å ved et uhell slå på flere lysdioder enn mulig og ikke brenne MK. Brikken koster en krone og bør alltid oppbevares i ditt hjemmemedisinskap.
Vår CD74HC238 vil være ansvarlig for å levere spenning til anoden til ønsket LED. I en fullverdig multipleks vil den levere spenning til kolonnen gjennom en P-MOSFET, men i denne demoen er det mulig direkte, fordi den trekker 20 mA, iht absolutte maksimale karakterer i dataarket. Fra datablad CD74HC238 vi trenger pinouts og dette juksearket:
H = høyt spenningsnivå, L = lavt spenningsnivå, X – ikke bryr seg
Vi kobler E2 og E1 til jord, E3, A0, A1 og A3 til pinnene PD5, PC3, PC4 og PC5 på STM8. Siden tabellen ovenfor inneholder både lave og høye nivåer, konfigurerer vi disse pinnene som push-pull pinner.
PWM
PWM på den andre timeren er konfigurert på samme måte som i forrige historie, med to forskjeller:
Først må vi aktivere avbruddet Oppdater hendelse (UEV) som vil kalle en funksjon som slår av den aktive LED-en. Dette gjøres ved å bytte bit Aktiver oppdateringsavbrudd i et register med et sigende navn
Den andre forskjellen er knyttet til fenomenet multipleksing, som f.eks ghosting – parasittisk glød av dioder. I vårt tilfelle kan det vises på grunn av det faktum at timeren, etter å ha forårsaket et avbrudd på UEV, fortsetter å tikke, og avbruddsbehandleren har ikke tid til å bytte lysdioden før timeren begynner å skrive noe til pinnene. For å bekjempe dette, må du invertere logikken (0 = maksimal lysstyrke, 255 = ingenting lyser) og unngå ekstreme driftssyklusverdier. De. sørg for at LED-ene slukker helt etter UEV i én PWM-syklus.
Unngå å sette r, g og b til 255 og husk å snu dem når du bruker dem.
Avbryter
Essensen av et avbrudd er at under visse omstendigheter slutter brikken å kjøre hovedprogrammet og kaller en ekstern funksjon. Avbrudd oppstår på grunn av ytre eller indre påvirkninger, inkludert timeren.
Da vi først laget et prosjekt i ST Visual Develop, i tillegg til main.c vi mottok et vindu med en mystisk fil stm8_interrupt_vector.c, automatisk inkludert i prosjektet. I denne filen er en funksjon tildelt hvert avbrudd NonHandledInterrupt. Vi må binde funksjonen vår til ønsket avbrudd.
Dataarket har en tabell med avbruddsvektorer, der vi finner de vi trenger:
Og til slutt, skriv denne funksjonen i din main.c:
@far @interrupt void TIM2_Overflow (void)
{
PD_ODR &= ~(1<<5); // вырубаем демультиплексор
PC_ODR = (cnt<<3); // записываем в демультиплексор новое значение
PD_ODR |= (1<<5); // включаем демультиплексор
TIM2_SR1 = 0; // сбрасываем флаг Update Interrupt Pending
cnt++;
cnt &= 7; // двигаем счетчик LED
TIM2_CCR1L = ~colors[cnt][0]; // передаем в буфер инвертированные значения
TIM2_CCR2L = ~colors[cnt][1]; // для следующего цикла ШИМ
TIM2_CCR3L = ~colors[cnt][2]; //
return;
}
Alt som gjenstår er å aktivere avbrudd. Dette gjøres ved å bruke assembler-kommandoen rim - du må lete etter det Programmeringshåndbok:
//enable interrupts
_asm("rim");
En annen assembler-kommando er sim – slår av avbrudd. De må slås av mens nye verdier skrives til "videominnet", slik at et avbrudd forårsaket i feil øyeblikk ikke ødelegger matrisen.
Hvis i det minste noen finner denne artikkelen nyttig, så skrev jeg den ikke forgjeves. Jeg vil gjerne motta kommentarer og kommentarer, jeg vil prøve å svare på alt.