Læs datablade 2: SPI på STM32; PWM, timere og interrupts på STM8

Læs datablade 2: SPI på STM32; PWM, timere og interrupts på STM8

В den første del Jeg forsøgte at fortælle hobbyelektronikingeniører, der voksede op fra Arduino-bukser, hvordan og hvorfor de skulle læse datablade og anden dokumentation til mikrocontrollere. Teksten viste sig at være stor, så jeg lovede at vise praktiske eksempler i en separat artikel. Nå, han kaldte sig selv en mælkesvamp...

I dag vil jeg vise dig, hvordan du bruger datablade til at løse ganske enkle, men nødvendige for mange projekter, opgaver på STM32 (Blue Pill) og STM8 controllere. Alle demoprojekter er dedikeret til mine yndlings-LED'er, vi vil tænde dem i store mængder, som vi bliver nødt til at bruge alle mulige interessante perifere enheder til.

Teksten viste sig igen at være enorm, så for nemheds skyld laver jeg indholdet:

STM32 Blue Pill: 16 LED'er med DM634 driver
STM8: Opsætning af seks PWM-ben
STM8: 8 RGB LED'er på tre ben, afbryder

Ansvarsfraskrivelse: Jeg er ikke ingeniør, jeg foregiver ikke at have dyb viden inden for elektronik, artiklen er beregnet til amatører som mig. Faktisk betragtede jeg mig selv for to år siden som målgruppen. Hvis nogen havde fortalt mig dengang, at dataark på en ukendt chip ikke var skræmmende at læse, ville jeg ikke have brugt meget tid på at lede efter nogle stykker kode på internettet og opfinde krykker med saks og klæbebånd.

Fokus i denne artikel er på dataark, ikke projekter, så koden er måske ikke særlig pæn og ofte trang. Selve projekterne er meget enkle, selvom de er velegnede til et første bekendtskab med den nye chip.

Jeg håber, at min artikel vil hjælpe nogen på et lignende stadium af fordybelse i hobbyen.

STM32

16 LED'er med DM634 og SPI

Et lille projekt med Blue Pill (STM32F103C8T6) og DM634 LED-driver. Ved hjælp af dataark vil vi finde ud af driveren, STM IO-porte og konfigurere SPI.

DM634

Taiwanesisk chip med 16 16-bit PWM-udgange, kan forbindes i kæder. Low-end 12-bit modellen er kendt fra et hjemligt projekt Lightpack. På et tidspunkt, da jeg valgte mellem DM63x og den velkendte TLC5940, valgte jeg DM af flere grunde: 1) TLC på Aliexpress er bestemt falsk, men denne er ikke; 2) DM har en autonom PWM med sin egen frekvensgenerator; 3) det kunne købes billigt i Moskva i stedet for at vente på en pakke fra Ali. Og det var selvfølgelig interessant at lære at styre chippen selv i stedet for at bruge et færdigt bibliotek. Chips er nu hovedsageligt præsenteret i SSOP24-pakken; de er nemme at lodde til en adapter.

Da producenten er taiwansk, datablad chippen er skrevet på kinesisk engelsk, hvilket betyder, at det bliver sjovt. Først ser vi på pinouten (Pin-forbindelse) for at forstå, hvilket ben der skal tilsluttes hvad til, og en beskrivelse af stifterne (Pin Beskrivelse). 16 ben:

Læs datablade 2: SPI på STM32; PWM, timere og interrupts på STM8
DC Sink-kilder (åbent afløb)

Håndvask / Udgang med åbent afløb – afløb; kilde til indstrømmende strøm; udgangen er forbundet til jord i aktiv tilstand - LED'erne er forbundet til driveren med katoder. Elektrisk er dette selvfølgelig ikke et "åbent afløb" (åbent afløb), men i datablade findes denne betegnelse for stifter i dræntilstand ofte.

Læs datablade 2: SPI på STM32; PWM, timere og interrupts på STM8
Eksterne modstande mellem REXT og GND for at indstille udgangsstrømværdien

En referencemodstand er installeret mellem REXT-stiften og jord, som styrer den interne modstand af udgangene, se grafen på side 9 i databladet. I DM634 kan denne modstand også styres af software, der indstiller den overordnede lysstyrke (global lysstyrke); Jeg vil ikke gå i detaljer i denne artikel, jeg vil bare sætte en 2.2 - 3 kOhm modstand her.

For at forstå, hvordan man styrer chippen, lad os se på beskrivelsen af ​​enhedsgrænsefladen:

Læs datablade 2: SPI på STM32; PWM, timere og interrupts på STM8

Ja, her er det, kinesisk engelsk i al sin herlighed. At oversætte dette er problematisk, du kan forstå det, hvis du ønsker det, men der er en anden måde - se hvordan forbindelsen til den funktionelt lignende TLC5940 er beskrevet i databladet:

Læs datablade 2: SPI på STM32; PWM, timere og interrupts på STM8
... Kun tre stifter er nødvendige for at indtaste data i enheden. Den stigende kant af SCLK-signalet flytter dataene fra SIN-stiften til det interne register. Efter at alle data er blevet indlæst, låser et kort højt XLAT-signal de sekventielt overførte data ind i de interne registre. Interne registre er porte, der udløses af XLAT-signalniveauet. Alle data overføres den mest signifikante bit først.

latch – lås/lås/lås.
Stigende kant – forkant af pulsen
MSB først – mest betydelige (længst til venstre) bit frem.
at klokke data – overføre data sekventielt (bit for bit).

Word låsen findes ofte i dokumentationen til chips og er oversat på forskellige måder, så for forståelsens skyld vil jeg tillade mig

et lille uddannelsesprogramLED-driveren er i det væsentlige et skifteregister. "Skift" (skifte) i navnet - bitvis bevægelse af data inde i enheden: hver ny bit, der skubbes ind, skubber hele kæden frem foran den. Da ingen ønsker at observere kaotisk blinkning af LED'erne under skiftet, foregår processen i bufferregistre adskilt fra arbejdsregistrene med et dæmper (låsen) er en slags venteværelse, hvor bitsene er arrangeret i den ønskede rækkefølge. Når alt er klar, åbnes lukkeren, og bitsene går på arbejde og erstatter den forrige batch. Ord låsen i dokumentationen for mikrokredsløb indebærer næsten altid et sådant dæmper, uanset i hvilke kombinationer det bruges.

Så dataoverførsel til DM634 udføres således: Indstil DAI-inputtet til værdien af ​​den mest signifikante bit af den fjerne LED, træk DCK op og ned; sæt DAI input til værdien af ​​den næste bit, træk DCK; og så videre indtil alle bits er blevet transmitteret (klokket ind), hvorefter vi trækker LAT. Dette kan gøres manuelt (bit-bang), men det er bedre at bruge en SPI-grænseflade, der er specielt skræddersyet til dette, da den præsenteres på vores STM32 i to eksemplarer.

Blå pille STM32F103

Indledende: STM32-controllere er meget mere komplekse end Atmega328, end de kan virke skræmmende. Af energibesparende årsager er næsten alt periferiudstyr desuden slukket ved starten, og klokfrekvensen er 8 MHz fra den interne kilde. Heldigvis skrev STM-programmører kode, der bringer chippen op på de "beregnede" 72 MHz, og forfatterne af alle de IDE'er, jeg kender, inkluderede den i initialiseringsproceduren, så vi behøver ikke at klokke (men du kan, hvis du virkelig vil). Men du bliver nødt til at tænde for periferiudstyret.

Dokumentation: Blue Pill er udstyret med den populære STM32F103C8T6 chip, der er to nyttige dokumenter til det:

I databladet kan vi være interesserede i:

  • Pinouts – chip pinouts – hvis vi beslutter os for at lave brædderne selv;
  • Memory Map – hukommelseskort til en specifik chip. Referencemanualen har et kort for hele linjen, og den nævner registre, som vores ikke har.
  • Tabel med pindefinitioner – en liste over de vigtigste og alternative funktioner af pins; for den "blå pille" kan du finde mere praktiske billeder på internettet med en liste over stifter og deres funktioner. Derfor googler vi straks Blue Pill pinout og holder dette billede ved hånden:

Læs datablade 2: SPI på STM32; PWM, timere og interrupts på STM8
NB: der var en fejl på billedet fra internettet, som blev noteret i kommentarerne, tak for det. Billedet er blevet erstattet, men dette er en lektie - det er bedre at kontrollere oplysninger ikke fra datablade.

Vi fjerner dataarket, åbner referencemanualen, og fra nu af bruger vi kun det.
Procedure: vi håndterer standard input/output, konfigurerer SPI, tænder for de nødvendige perifere enheder.

Input Output

På Atmega328 er I/O implementeret ekstremt enkelt, hvorfor overfloden af ​​STM32 muligheder kan være forvirrende. Nu mangler vi kun konklusioner, men selv disse har fire muligheder:

Læs datablade 2: SPI på STM32; PWM, timere og interrupts på STM8
åbent afløb, push-pull, alternativ push-pull, alternativt åbent afløb

"Træk skub" (skub træk) er det sædvanlige output fra Arduino, kan stiften tage værdien enten HIGH eller LOW. Men med "åbent afløb" er der vanskeligheder, selvom alt faktisk er enkelt her:

Læs datablade 2: SPI på STM32; PWM, timere og interrupts på STM8
Læs datablade 2: SPI på STM32; PWM, timere og interrupts på STM8
Udgangskonfiguration / når porten er tildelt udgang: / udgangsbuffer aktiveret: / – åben drain-tilstand: "0" i udgangsregisteret aktiverer N-MOS, "1" i udgangsregisteret forlader porten i Hi-Z-tilstand ( P-MOS er ikke aktiveret ) / – push-pull mode: “0” i udgangsregisteret aktiverer N-MOS, “1” i udgangsregisteret aktiverer P-MOS.

Hele forskellen mellem åbent afløb (åbent afløb) fra "push-pull" (skub træk) er, at den første pin ikke kan acceptere HIGH-tilstanden: når du skriver en til outputregisteret, går den i høj modstandstilstand (høj impedans, Hej-Z). Når du skriver nul, opfører stiften sig ens i begge tilstande, både logisk og elektrisk.

I normal udgangstilstand udsender stiften blot indholdet af outputregisteret. I "alternativet" styres det af de tilsvarende perifere enheder (se 9.1.4):

Læs datablade 2: SPI på STM32; PWM, timere og interrupts på STM8
Hvis en portbit er konfigureret som en alternativ funktionsstift, deaktiveres stiftregisteret, og stiften forbindes til den perifere ben.

Alternativ funktionalitet af hver pin er beskrevet i Pin-definitioner Dataarket er på det downloadede billede. På spørgsmålet om, hvad man skal gøre, hvis en pin har flere alternative funktioner, er svaret givet ved en fodnote i dataarket:

Læs datablade 2: SPI på STM32; PWM, timere og interrupts på STM8
Hvis flere perifere enheder bruger den samme pin, bør der kun bruges én perifer enhed ad gangen for at undgå konflikter mellem alternative funktioner, der skiftes med den perifere clock-aktiveringsbit (i det relevante RCC-register).

Endelig har ben i udgangstilstand også en clockhastighed. Dette er endnu en energibesparende funktion; i vores tilfælde sætter vi den bare til maksimum og glemmer den.

Altså: vi bruger SPI, hvilket betyder, at to ben (med data og med et clock-signal) skal være "alternativ push-pull-funktion", og en anden (LAT) skal være "almindelig push-pull". Men før vi tildeler dem, lad os beskæftige os med SPI.

SPI

Endnu et lille uddannelsesprogram

SPI eller Serial Peripheral Interface (serial peripheral interface) er en enkel og meget effektiv grænseflade til at forbinde en MK med andre MK'ere og omverdenen generelt. Princippet for dets drift er allerede blevet beskrevet ovenfor, hvor det drejer sig om den kinesiske LED-driver (i referencemanualen, se afsnit 25). SPI kan fungere i master ("master") og slave ("slave") tilstand. SPI har fire grundlæggende kanaler, hvoraf ikke alle kan bruges:

  • MOSI, Master Output / Slave Input: denne pin transmitterer data i master mode og modtager data i slave mode;
  • MISO, Master Input / Slave Output: tværtimod modtager den i masteren og sender i slaven;
  • SCK, seriel ur: indstiller frekvensen for datatransmission i masteren eller modtager et ursignal i slaven. I det væsentlige at ramme beats;
  • SS, Slave Select: ved hjælp af denne kanal ved slaven, at der ønskes noget fra ham. På STM32 hedder det NSS, hvor N = negativ, dvs. controlleren bliver slave, hvis der er jord i denne kanal. Det kombinerer godt med Open Drain Output-tilstanden, men det er en anden historie.

Som alt andet er SPI på STM32 rig på funktionalitet, hvilket gør det noget svært at forstå. For eksempel kan det fungere ikke kun med SPI, men også med en I2S-grænseflade, og i dokumentationen er deres beskrivelser blandet, det er nødvendigt at afskære overskuddet rettidigt. Vores opgave er ekstremt enkel: Vi skal blot sende data ved hjælp af kun MOSI og SCK. Vi går til afsnit 25.3.4 (halv-dupleks kommunikation, halv-dupleks kommunikation), hvor vi finder 1 ur og 1 ensrettet dataledning (1 kloksignal og 1 ensrettet datastrøm):

Læs datablade 2: SPI på STM32; PWM, timere og interrupts på STM8
I denne tilstand bruger applikationen SPI i enten kun transmission eller kun modtagelse. / Kun transmissionstilstand ligner duplekstilstand: data transmitteres på sendepinden (MOSI i mastertilstand eller MISO i slavetilstand), og modtagepinden (henholdsvis MISO eller MOSI) kan bruges som en almindelig I/O pin . I dette tilfælde behøver applikationen kun at ignorere Rx-bufferen (hvis den læses, vil der ikke være overførte data der).

Fantastisk, MISO-pinden er gratis, lad os forbinde LAT-signalet til den. Lad os se på Slave Select, som på STM32 kan styres programmatisk, hvilket er yderst praktisk. Vi læser afsnittet af samme navn i afsnit 25.3.1 SPI Generel beskrivelse:

Læs datablade 2: SPI på STM32; PWM, timere og interrupts på STM8
Softwarekontrol NSS (SSM = 1) / Slavevalgsinformation er indeholdt i SSI-bitten i SPI_CR1-registret. Den eksterne NSS-pin forbliver fri til andre applikationsbehov.

Det er tid til at skrive til registrene. Jeg besluttede at bruge SPI2, se efter dens basisadresse i dataarket - i afsnit 3.3 Memory Map:

Læs datablade 2: SPI på STM32; PWM, timere og interrupts på STM8

Nå, lad os begynde:

#define _SPI2_(mem_offset) (*(volatile uint32_t *)(0x40003800 + (mem_offset)))

Åbn afsnit 25.3.3 med den selvforklarende titel "Konfiguration af SPI i mastertilstand":

Læs datablade 2: SPI på STM32; PWM, timere og interrupts på STM8

1. Indstil den serielle clock-frekvens med bits BR[2:0] i SPI_CR1-registret.

Registrene er samlet i referencemanualafsnittet af samme navn. adresseskift (Adresseforskydning) for CR1 – 0x00 ryddes som standard alle bits (Nulstil værdi 0x0000):

Læs datablade 2: SPI på STM32; PWM, timere og interrupts på STM8

BR-bittene indstiller controllerens clockdeler og bestemmer således frekvensen, hvormed SPI'en vil fungere. Vores STM32-frekvens vil være 72 MHz, LED-driveren fungerer ifølge databladet med en frekvens på op til 25 MHz, så vi skal dividere med fire (BR[2:0] = 001).

#define _SPI_CR1 0x00

#define BR_0        0x0008
#define BR_1        0x0010
#define BR_2        0x0020

_SPI2_ (_SPI_CR1) |= BR_0;// pclk/4

2. Indstil CPOL- og CPHA-bits til at definere forholdet mellem dataoverførsel og seriel clock-timing (se diagram på side 240)

Da vi læser et dataark her og ikke ser på skemaer, lad os se nærmere på tekstbeskrivelsen af ​​CPOL- og CPHA-bittene på side 704 (SPI General Description):

Læs datablade 2: SPI på STM32; PWM, timere og interrupts på STM8
Urfase og polaritet
Ved at bruge CPOL- og CPHA-bits i SPI_CR1-registret kan du programmæssigt vælge fire timing-relationer. CPOL (clock polarity) bit styrer tilstanden af ​​clock signalet, når der ikke transmitteres data. Denne bit styrer master- og slavetilstandene. Hvis CPOL nulstilles, er SCK-pinden lav i hviletilstand. Hvis CPOL-bitten er indstillet, er SCK-pinden høj under hviletilstand.
Når CPHA (clock phase) bit er indstillet, er den høje bit trap strobe den anden kant af SCK signalet (faldende hvis CPOL er klar, stigende hvis CPOL er sat). Dataene fanges af den anden ændring i ursignalet. Hvis CPHA-bitten er klar, er den høje bit-fælde-strobe den stigende flanke af SCK-signalet (faldende flanke, hvis CPOL er indstillet, stigende flanke, hvis CPOL er slettet). Data fanges ved den første ændring i ursignalet.

Efter at have absorberet denne viden, kommer vi til den konklusion, at begge bit skal forblive nul, fordi Vi ønsker, at SCK-signalet forbliver lavt, når det ikke er i brug, og at data skal transmitteres på den stigende kant af pulsen (se fig. Rising Edge i DM634-dataarket).

Forresten, her stødte vi først på en funktion af ordforrådet i ST-dataark: i dem er sætningen "nulstil bit til nul" skrevet for at nulstille lidtOg ikke at rydde lidt, som for eksempel Atmega.

3. Indstil DFF-bitten for at bestemme, om datablokken er 8-bit eller 16-bit format

Jeg tog specifikt en 16-bit DM634 for ikke at genere at sende 12-bit PWM-data, som DM633. Det giver mening at sætte DFF til én:

#define DFF         0x0800

_SPI2_ (_SPI_CR1) |= DFF; // 16-bit mode

4. Konfigurer LSBFIRST-bitten i SPI_CR1-registret for at bestemme blokformatet

LSBFIRST konfigurerer, som navnet antyder, transmission med den mindst signifikante bit først. Men DM634 ønsker at modtage data fra den mest betydningsfulde bit. Derfor lader vi den nulstille.

5. Hvis input fra NSS-pinden er påkrævet i hardwaretilstand, skal du tilføre et højt signal til NSS-pinden under hele byteoverførselssekvensen. I NSS-softwaretilstand skal du indstille SSM- og SSI-bit i SPI_CR1-registret. Hvis NSS-pinden skal bruges som udgang, skal kun SSOE-bitten indstilles.

Installer SSM og SSI for at glemme NSS-hardwaretilstanden:

#define SSI         0x0100
#define SSM         0x0200

_SPI2_ (_SPI_CR1) |= SSM | SSI; //enable software control of SS, SS high

6. MSTR- og SPE-bits skal indstilles (de forbliver kun indstillet, hvis NSS-signalet er højt)

Faktisk, med disse bits udpeger vi vores SPI som en master og tænder den:

#define MSTR        0x0004
#define SPE         0x0040

_SPI2_ (_SPI_CR1) |= MSTR; //SPI master
//когда все готово, включаем SPI
_SPI2_ (_SPI_CR1) |= SPE;

SPI er konfigureret, lad os straks skrive funktioner, der sender bytes til driveren. Fortsæt med at læse 25.3.3 "Konfiguration af SPI i mastertilstand":

Læs datablade 2: SPI på STM32; PWM, timere og interrupts på STM8
Dataoverførselsordre
Transmission begynder, når en byte skrives til Tx-bufferen.
Databyten indlæses i skifteregisteret kl parallel mode (fra den interne bus) under transmissionen af ​​den første bit, hvorefter den transmitteres til sekventiel MOSI pin mode, første eller sidste bit fremad afhængigt af indstillingen af ​​LSBFIRST bit i CPI_CR1 registeret. TXE-flaget indstilles efter datatransmission fra Tx buffer til skifteregisterog genererer også et interrupt, hvis TXEIE-bitten i CPI_CR1-registret er indstillet.

Jeg fremhævede et par ord i oversættelsen for at henlede opmærksomheden på en funktion af SPI-implementeringen i STM-controllere. På Atmega TXE-flaget (Tx tom, Tx er tom og klar til at modtage data) indstilles først, efter at hele byten er blevet sendt udgående. Og her sættes dette flag efter at byten er blevet indsat i det interne skifteregister. Da det skubbes dertil med alle bits på samme tid (parallelt), og så overføres dataene sekventielt, sættes TXE før byten er fuldstændig sendt. Dette er vigtigt, fordi i tilfælde af vores LED-driver, skal vi trække LAT-stiften efter afsendelse Alle data, dvs. TXE-flaget alene vil ikke være nok for os.

Det betyder, at vi har brug for endnu et flag. Lad os se på 25.3.7 - "Statusflag":

Læs datablade 2: SPI på STM32; PWM, timere og interrupts på STM8
<…>
Læs datablade 2: SPI på STM32; PWM, timere og interrupts på STM8
OPTAGET flag
BSY-flaget indstilles og slettes af hardware (at skrive til det har ingen effekt). BSY-flaget angiver tilstanden af ​​SPI-kommunikationslaget.
Den nulstiller:
når overførslen er afsluttet (undtagen i mastertilstand, hvis overførslen er kontinuerlig)
når SPI er deaktiveret
når der opstår en mastertilstandsfejl (MODF=1)
Hvis overførslen ikke er kontinuerlig, slettes BSY-flaget mellem hver dataoverførsel

Okay, det vil være nyttigt. Lad os finde ud af, hvor Tx-bufferen er placeret. For at gøre dette, læs "SPI Data Register":

Læs datablade 2: SPI på STM32; PWM, timere og interrupts på STM8
Bits 15:0 DR[15:0] Dataregister
Data modtaget eller data, der skal transmitteres.
Dataregisteret er opdelt i to buffere - en til skrivning (sendebuffer) og en til læsning (modtagebuffer). Skrivning til dataregisteret skriver til Tx bufferen, og læsning fra dataregisteret vil returnere værdien indeholdt i Rx bufferen.

Nå, og statusregistret, hvor TXE- og BSY-flaget findes:

Læs datablade 2: SPI på STM32; PWM, timere og interrupts på STM8

Vi skriver:

#define _SPI_DR  0x0C
#define _SPI_SR  0x08

#define BSY         0x0080
#define TXE         0x0002

void dm_shift16(uint16_t value)
{
    _SPI2_(_SPI_DR) = value; //send 2 bytes
    while (!(_SPI2_(_SPI_SR) & TXE)); //wait until they're sent
}

Nå, da vi skal transmittere 16 gange to bytes, ifølge antallet af LED-driverudgange, noget 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 ved ikke, hvordan vi trækker LAT-stiften endnu, så vi går tilbage til I/O.

Tildeling af stifter

I STM32F1 er de registre, der er ansvarlige for stifternes tilstand, ret usædvanlige. Det er tydeligt, at der er flere af dem end Atmega, men de adskiller sig også fra andre STM-chips. Afsnit 9.1 Generel beskrivelse af GPIO:

Læs datablade 2: SPI på STM32; PWM, timere og interrupts på STM8
Hver af de generelle I/O-porte (GPIO) har to 32-bit konfigurationsregistre (GPIOx_CRL og GPIOx_CRH), to 32-bit dataregistre (GPIOx_IDR og GPIOx_ODR), et 32-bit sæt/nulstil register (GPIOx_BSRR), et 16-bit nulstillingsregister (GPIOx_BRR) og en 32-bit bitblokeringsregister (GPIOx_LCKR).

De første to registre er usædvanlige og også ret ubelejlige, fordi de 16 portstifter er spredt ud over dem i et "fire bit pr. bror"-format. De der. ben nul til syv er i CRL, og resten er i CRH. Samtidig indeholder de resterende registre med succes bits fra alle stifter i porten - ofte forbliver halvt "reserveret".

For nemheds skyld, lad os starte fra slutningen af ​​listen.

Vi har ikke brug for et blokeringsregister.

Sæt- og nulstillingsregistrene er ret sjove, idet de delvist duplikerer hinanden: du kan kun skrive alt i BSRR, hvor de højere 16 bit vil nulstille pinden til nul, og de nederste vil blive sat til 1, eller du kan også brug BRR, hvoraf de nederste 16 bit kun nulstiller stiften . Jeg kan godt lide den anden mulighed. Disse registre er vigtige, fordi de giver atomadgang til stifter:

Læs datablade 2: SPI på STM32; PWM, timere og interrupts på STM8
Læs datablade 2: SPI på STM32; PWM, timere og interrupts på STM8
Atomic Set eller Reset
Der er ingen grund til at deaktivere interrupts ved programmering af GPIOx_ODR på bitniveau: en eller flere bits kan ændres med en enkelt atomisk skriveoperation APB2. Dette opnås ved at skrive et "1" til sæt/nulstillingsregisteret (GPIOx_BSRR eller, kun for nulstilling, GPIOx_BRR) for den bit, der skal ændres. Andre bits forbliver uændrede.

Dataregistrene har ganske selvforklarende navne - IDR = Input Retning Register, input register; ODR = Produktion Retningsregister, udgangsregister. Vi får ikke brug for dem i det nuværende projekt.

Og endelig kontrolregistre. Da vi er interesserede i de andre SPI-stifter, nemlig PB13, PB14 og PB15, ser vi straks på CRH:

Læs datablade 2: SPI på STM32; PWM, timere og interrupts på STM8

Og vi ser, at vi bliver nødt til at skrive noget i stykker fra 20 til 31.

Vi har allerede fundet ud af ovenfor, hvad vi vil have fra pins, så her vil jeg undvære et skærmbillede, jeg vil bare sige, at MODE specificerer retningen (input, hvis begge bit er sat til 0) og pin-hastighed (vi skal bruge 50MHz, dvs. både pin til "1"), og CNF indstiller tilstanden: almindelig "push-pull" - 00, "alternativ" - 10. Som standard, som vi ser ovenfor, har alle pins den tredje bit fra bunden (CNF0), det sætter dem i tilstand flydende input.

Da jeg planlægger at gøre noget andet med denne chip, har jeg for nemheds skyld defineret alle mulige MODE- og CNF-værdier for både de nedre og øvre kontrolregistre.

Sådan på en eller anden måde

#define CNF0_0 0x00000004
#define CNF0_1 0x00000008
#define CNF1_0 0x00000040
#define CNF1_1 0x00000080
#define CNF2_0 0x00000400
#define CNF2_1 0x00000800
#define CNF3_0 0x00004000
#define CNF3_1 0x00008000
#define CNF4_0 0x00040000
#define CNF4_1 0x00080000
#define CNF5_0 0x00400000
#define CNF5_1 0x00800000
#define CNF6_0 0x04000000
#define CNF6_1 0x08000000
#define CNF7_0 0x40000000
#define CNF7_1 0x80000000
#define CNF8_0 0x00000004
#define CNF8_1 0x00000008
#define CNF9_0 0x00000040
#define CNF9_1 0x00000080
#define CNF10_0 0x00000400
#define CNF10_1 0x00000800
#define CNF11_0 0x00004000
#define CNF11_1 0x00008000
#define CNF12_0 0x00040000
#define CNF12_1 0x00080000
#define CNF13_0 0x00400000
#define CNF13_1 0x00800000
#define CNF14_0 0x04000000
#define CNF14_1 0x08000000
#define CNF15_0 0x40000000
#define CNF15_1 0x80000000

#define MODE0_0 0x00000001
#define MODE0_1 0x00000002
#define MODE1_0 0x00000010
#define MODE1_1 0x00000020
#define MODE2_0 0x00000100
#define MODE2_1 0x00000200
#define MODE3_0 0x00001000
#define MODE3_1 0x00002000
#define MODE4_0 0x00010000
#define MODE4_1 0x00020000
#define MODE5_0 0x00100000
#define MODE5_1 0x00200000
#define MODE6_0 0x01000000
#define MODE6_1 0x02000000
#define MODE7_0 0x10000000
#define MODE7_1 0x20000000
#define MODE8_0 0x00000001
#define MODE8_1 0x00000002
#define MODE9_0 0x00000010
#define MODE9_1 0x00000020
#define MODE10_0 0x00000100
#define MODE10_1 0x00000200
#define MODE11_0 0x00001000
#define MODE11_1 0x00002000
#define MODE12_0 0x00010000
#define MODE12_1 0x00020000
#define MODE13_0 0x00100000
#define MODE13_1 0x00200000
#define MODE14_0 0x01000000
#define MODE14_1 0x02000000
#define MODE15_0 0x10000000
#define MODE15_1 0x20000000

Vores stifter er placeret på port B (basisadresse – 0x40010C00), kode:

#define _PORTB_(mem_offset) (*(volatile uint32_t *)(0x40010C00 + (mem_offset)))

#define _BRR  0x14
#define _BSRR 0x10
#define _CRL  0x00
#define _CRH  0x04

//используем стандартный SPI2: MOSI на B15, CLK на B13
//LAT пусть будет на неиспользуемом MISO – B14

//очищаем дефолтный бит, он нам точно не нужен
_PORTB_ (_CRH) &= ~(CNF15_0 | CNF14_0 | CNF13_0 | CNF12_0);

//альтернативные функции для MOSI и SCK
_PORTB_ (_CRH) |= CNF15_1 | CNF13_1;

//50 МГц, MODE = 11
_PORTB_ (_CRH) |= MODE15_1 | MODE15_0 | MODE14_1 | MODE14_0 | MODE13_1 | MODE13_0;

Og derfor kan du skrive definitioner for LAT, som vil blive rykket af BRR- og BSRR-registrene:

/*** LAT pulse – high, then low */
#define LAT_pulse() _PORTB_(_BSRR) = (1<<14); _PORTB_(_BRR) = (1<<14)

#define LAT_low() _PORTB_(_BRR) = (1<<14)

(LAT_low bare ved inerti, det har altid været sådan, lad det blive)

Nu er alt fantastisk, men det virker ikke. Fordi dette er STM32, sparer de strøm, hvilket betyder, at du skal aktivere clocking af de nødvendige perifere enheder.

Slå clocking til

Uret, også kendt som Clock, er ansvarlig for clocking. Og vi kunne allerede nu bemærke forkortelsen RCC. Vi leder efter det i dokumentationen: dette er nulstilling og urkontrol.

Som det blev sagt ovenfor, blev den sværeste del af clocking-emnet heldigvis gjort for os af folk fra STM, hvilket vi takker dem meget for (igen vil jeg give et link til Di Halts hjemmeside, for at gøre det klart, hvor forvirrende det er). Vi har kun brug for registre, der er ansvarlige for at aktivere perifer clocking (Peripheral Clock Enable Registers). Lad os først finde basisadressen på RCC, den er helt i begyndelsen af ​​"Memory Map":

Læs datablade 2: SPI på STM32; PWM, timere og interrupts på STM8

#define _RCC_(mem_offset) (*(volatile uint32_t *)(0x40021000 + (mem_offset)))

Og så enten klik på linket, hvor du forsøger at finde noget i pladen, eller, meget bedre, gennemgå beskrivelserne af de aktiverende registre fra afsnittene om aktivere registre. Hvor vi finder RCC_APB1ENR og RCC_APB2ENR:

Læs datablade 2: SPI på STM32; PWM, timere og interrupts på STM8
Læs datablade 2: SPI på STM32; PWM, timere og interrupts på STM8

Og de indeholder derfor bits, der inkluderer clocking af SPI2, IOPB (I/O Port B) og alternative funktioner (AFIO).

#define _APB2ENR 0x18
#define _APB1ENR 0x1C

#define IOPBEN 0x0008
#define SPI2EN 0x4000
#define AFIOEN 0x0001

//включаем тактирование порта B и альт. функций
_RCC_(_APB2ENR) |= IOPBEN | AFIOEN;

//включаем  тактирование SPI2
_RCC_(_APB1ENR) |= SPI2EN;

Den endelige kode kan findes her.

Hvis du har mulighed og lyst til at teste, så tilslut DM634 sådan her: DAI til PB15, DCK til PB13, LAT til PB14. Vi driver driveren fra 5 volt, glem ikke at tilslutte grundene.

Læs datablade 2: SPI på STM32; PWM, timere og interrupts på STM8

STM8 PWM

PWM på STM8

Da jeg lige planlagde denne artikel, besluttede jeg for eksempel at prøve at mestre nogle funktioner i en ukendt chip ved kun at bruge et datablad, så jeg ikke ender med en skomager uden støvler. STM8 var ideel til denne rolle: For det første havde jeg et par kinesiske boards med STM8S103, og for det andet er det ikke særlig populært, og derfor hviler fristelsen til at læse og finde en løsning på internettet på manglen på netop disse løsninger.

Chippen har også datablad и referencemanual RM0016, i den første er der pinout og registeradresser, i den anden - alt andet. STM8 er programmeret i C i en frygtelig IDE ST Visuel Udvikling.

Ur og I/O

Som standard kører STM8 med en frekvens på 2 MHz, dette skal rettes med det samme.

Læs datablade 2: SPI på STM32; PWM, timere og interrupts på STM8
HSI (High Speed ​​​​Internt) ur
HSI-clock-signalet er afledt af en intern 16 MHz RC-oscillator med en programmerbar divider (1 til 8). Det indstilles i clock divider registeret (CLK_CKDIVR).
Bemærk: i starten vælges en HSI RC-oscillator med en divider på 8 som den førende kilde til ursignalet.

Vi finder registeradressen i databladet, beskrivelsen i refman og ser at registeret skal ryddes:

#define CLK_CKDIVR *(volatile uint8_t *)0x0050C6

CLK_CKDIVR &= ~(0x18);

Da vi skal køre PWM og forbinde LED'erne, lad os se på pinout'en:

Læs datablade 2: SPI på STM32; PWM, timere og interrupts på STM8

Chippen er lille, mange funktioner er suspenderet på de samme ben. Hvad der står i firkantede parenteser er "alternativ funktionalitet", det skiftes af "option bytes" (option bytes) – noget som Atmega-sikringer. Du kan ændre deres værdier programmatisk, men det er ikke nødvendigt, fordi Den nye funktionalitet aktiveres først efter en genstart. Det er nemmere at bruge ST Visual Programmer (downloadet med Visual Develop), som kan ændre disse bytes. Pinoutet viser, at CH1- og CH2-benene på den første timer er skjult i firkantede parenteser; det er nødvendigt at indstille AFR1 og AFR0 bits i STVP, og den anden vil også overføre CH1 output fra den anden timer fra PD4 til PC5.

Således vil 6 ben styre LED'erne: PC6, PC7 og PC3 for den første timer, PC5, PD3 og PA3 for den anden.

Opsætning af selve I/O-pindene på STM8 er enklere og mere logisk end på STM32:

  • kendt fra Atmega DDR data retningsregister (Dataretningsregister): 1 = output;
  • det første styreregister CR1, når det udlæses, indstiller push-pull-tilstanden (1) eller åbent afløb (0); da jeg forbinder LED'erne til chippen med katoder, efterlader jeg nuller her;
  • det andet kontrolregister CR2, når det udlæses, indstiller klokhastigheden: 1 = 10 MHz

#define PA_DDR     *(volatile uint8_t *)0x005002
#define PA_CR2     *(volatile uint8_t *)0x005004
#define PD_DDR     *(volatile uint8_t *)0x005011
#define PD_CR2     *(volatile uint8_t *)0x005013
#define PC_DDR     *(volatile uint8_t *)0x00500C
#define PC_CR2     *(volatile uint8_t *)0x00500E

PA_DDR = (1<<3); //output
PA_CR2 |= (1<<3); //fast
PD_DDR = (1<<3); //output
PD_CR2 |= (1<<3); //fast
PC_DDR = ((1<<3) | (1<<5) | (1<<6) | (1<<7)); //output
PC_CR2 |= ((1<<3) | (1<<5) | (1<<6) | (1<<7)); //fast

PWM indstilling

Lad os først definere begreberne:

  • PWM-frekvens – hvor ofte timeren tikker;
  • Automatisk genindlæsning, AR – autoloadable værdi, som timeren tæller til (pulsperiode);
  • Opdater begivenhed, UEV – en hændelse, der opstår, når timeren har talt til AR;
  • PWM Duty Cycle – PWM duty cycle, ofte kaldet "duty factor";
  • Fang/sammenlign værdi – værdi for optagelse/sammenligning, som timeren har talt til vil gøre noget (i tilfælde af PWM inverterer det udgangssignalet);
  • Preload værdi – forudindlæst værdi. Sammenlign værdi kan ikke ændres, mens timeren tikker, ellers vil PWM-cyklussen bryde. Derfor placeres nye transmitterede værdier i en buffer og trækkes ud, når timeren når slutningen af ​​sin nedtælling og nulstilles;
  • Kantjusteret и Centerjusterede tilstande – justering langs grænsen og i midten, det samme som Atmels Hurtig PWM и Fasekorrekt PWM.
  • OCiREF, Output Sammenlign referencesignal – Referenceudgangssignal, faktisk, hvad der vises på den tilsvarende pin i PWM-tilstand.

Som det allerede fremgår af pinout'en, har to timere PWM-funktioner - den første og den anden. Begge er 16-bit, den første har en masse ekstra funktioner (især kan den tælle både op og ned). Vi har brug for begge til at arbejde ens, så jeg besluttede at starte med den åbenbart dårligere anden, for ikke ved et uheld at bruge noget, der ikke er der. Et problem er, at beskrivelsen af ​​PWM-funktionaliteten af ​​alle timere i referencemanualen er i kapitlet om den første timer (17.5.7 PWM-tilstand), så du skal hele tiden hoppe frem og tilbage i dokumentet.

PWM på STM8 har en vigtig fordel i forhold til PWM på Atmega:

Læs datablade 2: SPI på STM32; PWM, timere og interrupts på STM8
Grænsejusteret PWM
Kontokonfiguration fra bund til top
Bottom-up-tælling er aktiv, hvis DIR-bitten i TIM_CR1-registret slettes
Eksempel
Eksemplet bruger den første PWM-tilstand. PWM-referencesignalet OCiREF holdes højt, så længe TIM1_CNT < TIM1_CCRi. Ellers tager det et lavt niveau. Hvis sammenligningsværdien i TIM1_CCRi-registret er større end autoload-værdien (TIM1_ARR-register), holdes OCiREF-signalet på 1. Hvis sammenligningsværdien er 0, holdes OCiREF på nul....

STM8 timer under opdatere begivenhed tjekker først sammenligne værdi, og frembringer først derefter et referencesignal. Atmegas timer skruer først op og sammenligner derefter, hvilket resulterer i compare value == 0 outputtet er en nål, som skal håndteres på en eller anden måde (f.eks. ved at programmere logikken).

Så hvad vi vil gøre: 8-bit PWM (AR == 255), tæller fra bund til top, justering langs kanten. Da elpærerne er forbundet til chippen med katoder, bør PWM udsende 0 (LED tændt) indtil sammenligne værdi og 1 efter.

Vi har allerede læst om nogle PWM-tilstand, så vi finder det nødvendige register for den anden timer ved at søge i referencemanualen efter denne sætning (18.6.8 - TIMx_CCMR1):

Læs datablade 2: SPI på STM32; PWM, timere og interrupts på STM8
110: Første PWM-tilstand – når der tælles fra bund til top, er den første kanal aktiv, mens TIMx_CNT < TIMx_CCR1. Ellers er den første kanal inaktiv. [længere i dokumentet er der en fejlagtig copy-paste fra timer 1] 111: Anden PWM-tilstand – når der tælles fra bund til top, er den første kanal inaktiv, mens TIMx_CNT < TIMx_CCR1. Ellers er den første kanal aktiv.

Da lysdioderne er forbundet til MK med katoder, passer den anden tilstand os (den første også, men vi ved det ikke endnu).

Læs datablade 2: SPI på STM32; PWM, timere og interrupts på STM8
Bit 3 OC1PE: Aktiver pin 1 preload
0: Preload register på TIMx_CCR1 er deaktiveret. Du kan til enhver tid skrive til TIMx_CCR1. Den nye værdi virker med det samme.
1: Preload register på TIMx_CCR1 er aktiveret. Læse-/skriveoperationer får adgang til preload-registret. Den forudindlæste værdi TIMx_CCR1 indlæses i skyggeregistret under hver opdateringshændelse.
*Bemærk: For at PWM-tilstand skal fungere korrekt, skal preload-registrene være aktiveret. Dette er ikke nødvendigt i enkeltsignaltilstand (OPM-bitten er indstillet i TIMx_CR1-registret).

Okay, lad os tænde alt, hvad vi har brug for til de tre kanaler i den anden timer:

#define TIM2_CCMR1 *(volatile uint8_t *)0x005307
#define TIM2_CCMR2 *(volatile uint8_t *)0x005308
#define TIM2_CCMR3 *(volatile uint8_t *)0x005309

#define PWM_MODE2   0x70 //PWM mode 2, 0b01110000
#define OCxPE       0x08 //preload enable

TIM2_CCMR1 = (PWM_MODE2 | OCxPE);
TIM2_CCMR2 = (PWM_MODE2 | OCxPE);
TIM2_CCMR3 = (PWM_MODE2 | OCxPE);

AR består af to otte-bit registre, alt er enkelt:

#define TIM2_ARRH  *(volatile uint8_t *)0x00530F
#define TIM2_ARRL  *(volatile uint8_t *)0x005310

TIM2_ARRH = 0;
TIM2_ARRL = 255;

Den anden timer kan kun tælle fra bund til top, justering langs grænsen, intet skal ændres. Lad os indstille frekvensdeleren, for eksempel til 256. For den anden timer er divideren indstillet i TIM2_PSCR-registret og er en potens af to:

#define TIM2_PSCR  *(volatile uint8_t *)0x00530E

TIM2_PSCR = 8;

Det eneste, der er tilbage, er at tænde for konklusionerne og selve den anden timer. Det første problem løses af registre Optag/sammenlign Aktiver: der er to, tre kanaler spredt over dem asymmetrisk. Her kan vi også lære, at det er muligt at ændre signalets polaritet, dvs. i princippet var det muligt at bruge PWM Mode 1. Vi skriver:

#define TIM2_CCER1 *(volatile uint8_t *)0x00530A
#define TIM2_CCER2 *(volatile uint8_t *)0x00530B

#define CC1E  (1<<0) // CCER1
#define CC2E  (1<<4) // CCER1
#define CC3E  (1<<0) // CCER2

TIM2_CCER1 = (CC1E | CC2E);
TIM2_CCER2 = CC3E;

Og til sidst starter vi timeren i TIMx_CR1-registret:

Læs datablade 2: SPI på STM32; PWM, timere og interrupts på STM8

#define TIM2_CR1   *(volatile uint8_t *)0x005300

TIM2_CR1 |= 1;

Lad os skrive en simpel analog af AnalogWrite(), som vil overføre de faktiske værdier til timeren til sammenligning. Registrene navngives forudsigeligt Hent/sammenlign registre, der er to af dem for hver kanal: lavordens 8 bits i TIM2_CCRxL og højordens i TIM2_CCRxH. Da vi har oprettet en 8-bit PWM, er det nok kun at skrive de mindst signifikante bits:

#define TIM2_CCR1L *(volatile uint8_t *)0x005312
#define TIM2_CCR2L *(volatile uint8_t *)0x005314
#define TIM2_CCR3L *(volatile uint8_t *)0x005316

void setRGBled(uint8_t r, uint8_t g, uint8_t b)
{
    TIM2_CCR1L = r;
    TIM2_CCR2L = g;
    TIM2_CCR3L = b;
}

Den opmærksomme læser vil bemærke, at vi har en lidt defekt PWM, der ikke er i stand til at producere 100% fyld (ved en maksimal værdi på 255, inverteres signalet i en timercyklus). For LED'er er dette ligegyldigt, og den opmærksomme læser kan allerede gætte, hvordan man løser det.

PWM på den anden timer virker, lad os gå videre til den første.

Den første timer har nøjagtig de samme bits i de samme registre (det er bare, at de bits, der forblev "reserverede" i den anden timer, bruges aktivt i den første til alle mulige avancerede ting). Derfor er det nok at finde adresserne på de samme registre i databladet og kopiere koden. Nå, skift værdien af ​​frekvensdeleren, fordi... den første timer ønsker ikke at modtage en potens på to, men en nøjagtig 16-bit værdi i to registre Prescaler High и Lav. Vi gør alt og... den første timer virker ikke. Hvad er der galt?

Problemet kan kun løses ved at kigge hele afsnittet om styreregistrene på timer 1 igennem, hvor vi leder efter den, som den anden timer ikke har. Der vil være 17.7.30 Pauseregister (TIM1_BKR), hvor der er denne bit:

Læs datablade 2: SPI på STM32; PWM, timere og interrupts på STM8
Aktiver hovedudgang

#define TIM1_BKR   *(volatile uint8_t *)0x00526D

TIM1_BKR = (1<<7);

Det er helt sikkert nu, koden der.

Læs datablade 2: SPI på STM32; PWM, timere og interrupts på STM8

STM8 Multiplex

Multiplexing på STM8

Det tredje miniprojekt er at forbinde otte RGB LED'er til den anden timer i PWM-tilstand og få dem til at vise forskellige farver. Det er baseret på konceptet med LED-multipleksing, som er, at hvis du tænder og slukker for LED'er meget, meget hurtigt, vil det virke for os, at de konstant er tændt (vedholdenhed i synetinerti af visuel perception). Det gjorde jeg engang sådan noget på Arduino.

Arbejdsalgoritmen ser sådan ud:

  • tilsluttet anoden af ​​den første RGB LED;
  • tændte den og sendte de nødvendige signaler til katoderne;
  • ventede til slutningen af ​​PWM-cyklussen;
  • tilsluttet anoden af ​​den anden RGB LED;
  • tændte den...

Nå, osv. For smuk drift kræves det selvfølgelig, at anoden er tilsluttet, og LED'en "tændes" på samme tid. Nå, eller næsten. Under alle omstændigheder skal vi skrive en kode, der udsender værdier i tre kanaler af den anden timer, ændre dem, når UEV nås, og samtidig ændre den aktuelt aktive RGB LED.

Da LED-skift er automatisk, skal vi oprette en "videohukommelse", hvorfra interrupt-handleren vil modtage data. Dette er et simpelt array:

uint8_t colors[8][3];

For at ændre farven på en specifik LED, vil det være nok at skrive de nødvendige værdier i dette array. Og variablen vil være ansvarlig for antallet af den aktive LED

uint8_t cnt;

Demux

For korrekt multipleksing har vi mærkeligt nok brug for en CD74HC238 demultiplexer. Demultiplexer - en chip, der implementerer operatøren i hardware <<. Gennem tre input-ben (bit 0, 1 og 2) fodrer vi det med et tre-bit nummer X, og som svar aktiverer det outputnummer (1<<X). De resterende input fra chippen bruges til at skalere hele designet. Vi har brug for denne chip ikke kun for at reducere antallet af besatte ben på mikrocontrolleren, men også for sikkerheden - for ikke ved et uheld at tænde flere lysdioder end muligt og ikke brænde MK. Chippen koster en krone og bør altid opbevares i dit medicinske skab.

Vores CD74HC238 vil være ansvarlig for at levere spænding til anoden på den ønskede LED. I en fuldgyldig multiplex ville den levere spænding til søjlen gennem en P-MOSFET, men i denne demo er det muligt direkte, pga. den trækker 20 mA, iflg absolut maksimale vurderinger i databladet. Fra Datablad CD74HC238 vi har brug for pinouts og dette snydeark:

Læs datablade 2: SPI på STM32; PWM, timere og interrupts på STM8
H = højspændingsniveau, L = lavspændingsniveau, X – ligeglad

Vi forbinder E2 og E1 til jord, E3, A0, A1 og A3 til benene PD5, PC3, PC4 og PC5 på STM8. Da tabellen ovenfor indeholder både lave og høje niveauer, konfigurerer vi disse stifter som push-pull stifter.

PWM

PWM på den anden timer er konfigureret på samme måde som i den forrige historie, med to forskelle:

Først skal vi aktivere afbrydelsen Opdater begivenhed (UEV), som vil kalde en funktion, der skifter den aktive LED. Dette gøres ved at ændre bit Opdater afbrydelse Aktiver i et register med sigende navn

Læs datablade 2: SPI på STM32; PWM, timere og interrupts på STM8
Afbryd aktiver register

#define TIM2_IER   *(volatile uint8_t *)0x005303

//enable interrupt
TIM2_IER = 1;

Den anden forskel er relateret til fænomenet multipleksing, som f.eks ghosting – parasitisk glød af dioder. I vores tilfælde kan det forekomme på grund af det faktum, at timeren, der har forårsaget en afbrydelse på UEV, fortsætter med at tikke, og afbrydelseshandleren har ikke tid til at skifte LED, før timeren begynder at skrive noget til stifterne. For at bekæmpe dette bliver du nødt til at invertere logikken (0 = maksimal lysstyrke, 255 = intet lyser) og undgå ekstreme driftscyklusværdier. De der. sørg for, at LED'erne efter UEV slukker helt i én PWM-cyklus.

Ændring af polaritet:

//set polarity 
    TIM2_CCER1 |= (CC1P | CC2P);
    TIM2_CCER2 |= CC3P;

Undgå at sætte r, g og b til 255 og husk at vende dem om, når du bruger dem.

Afbryder

Essensen af ​​en interrupt er, at chippen under visse omstændigheder holder op med at udføre hovedprogrammet og kalder en ekstern funktion. Afbrydelser opstår på grund af eksterne eller interne påvirkninger, herunder timeren.

Da vi første gang lavede et projekt i ST Visual Develop, udover main.c vi modtog et vindue med en mystisk fil stm8_interrupt_vector.c, automatisk inkluderet i projektet. I denne fil er der tildelt en funktion til hver afbrydelse NonHandledInterrupt. Vi skal binde vores funktion til den ønskede afbrydelse.

Dataarket har en tabel med interrupt-vektorer, hvor vi finder dem, vi skal bruge:

Læs datablade 2: SPI på STM32; PWM, timere og interrupts på STM8
13 TIM2 opdatering/overløb
14 TIM2 optag/sammenlign

Vi skal ændre LED'en ved UEV, så vi har brug for interrupt #13.

I overensstemmelse hermed for det første i sagen stm8_interrupt_vector.c ændre standardnavnet på den funktion, der er ansvarlig for interrupt nr. 13 (IRQ13) til dit eget:

{0x82, TIM2_Overflow}, /* irq13 */

For det andet bliver vi nødt til at oprette en fil main.h med følgende indhold:

#ifndef __MAIN_H
#define __MAIN_H

@far @interrupt void TIM2_Overflow (void);
#endif

Og skriv endelig denne funktion 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;
}

Det eneste, der er tilbage, er at aktivere afbrydelser. Dette gøres ved hjælp af assembler-kommandoen rim - du bliver nødt til at kigge efter det Programmeringsmanual:

//enable interrupts
_asm("rim");

En anden assembler-kommando er sim – slår afbrydelser fra. De skal slås fra, mens nye værdier skrives til "videohukommelsen", så en afbrydelse forårsaget på det forkerte tidspunkt ikke ødelægger arrayet.

Al kode - på GitHub.

Læs datablade 2: SPI på STM32; PWM, timere og interrupts på STM8

Hvis i det mindste nogen finder denne artikel nyttig, så skrev jeg den ikke forgæves. Jeg vil være glad for at modtage kommentarer og bemærkninger, jeg vil forsøge at besvare alt.

Kilde: www.habr.com

Tilføj en kommentar