Läs datablad 2: SPI på STM32; PWM, timers och avbrott på STM8

Läs datablad 2: SPI på STM32; PWM, timers och avbrott på STM8

В den första delen Jag försökte berätta för hobbyelektronikingenjörer som växte upp från Arduino-byxor hur och varför de skulle läsa datablad och annan dokumentation för mikrokontroller. Texten visade sig vara stor, så jag lovade att visa praktiska exempel i en separat artikel. Han kallade sig själv för mjölksvamp...

Idag kommer jag att visa dig hur du använder datablad för att lösa ganska enkla, men nödvändiga för många projekt, uppgifter på STM32 (Blue Pill) och STM8-kontroller. Alla demoprojekt är dedikerade till mina favoritlysdioder, vi kommer att tända dem i stora mängder, för vilka vi måste använda alla möjliga intressanta kringutrustningar.

Texten visade sig återigen vara enorm, så för bekvämlighets skull gör jag innehållet:

STM32 Blue Pill: 16 lysdioder med DM634-drivrutin
STM8: Konfigurera sex PWM-stift
STM8: 8 RGB-lysdioder på tre stift, avbryter

Ansvarsfriskrivning: Jag är ingen ingenjör, jag låtsas inte ha djup kunskap inom elektronik, artikeln är avsedd för amatörer som jag. Faktum är att jag ansåg mig själv för två år sedan som målgrupp. Om någon hade sagt till mig då att datablad på ett okänt chip inte var läskigt att läsa, skulle jag inte ha ägnat mycket tid åt att leta efter några kodbitar på Internet och hitta på kryckor med sax och tejp.

Fokus i den här artikeln ligger på datablad, inte projekt, så koden kanske inte är särskilt snygg och ofta trång. Projekten i sig är väldigt enkla, även om de lämpar sig för en första bekantskap med det nya chippet.

Jag hoppas att min artikel kommer att hjälpa någon i ett liknande stadium av fördjupning i hobbyn.

STM32

16 lysdioder med DM634 och SPI

Ett litet projekt med Blue Pill (STM32F103C8T6) och DM634 LED-drivrutin. Med hjälp av datablad kommer vi att ta reda på drivrutinen, STM IO-portar och konfigurera SPI.

DM634

Taiwanesiskt chip med 16 16-bitars PWM-utgångar, kan kopplas i kedjor. Den låga 12-bitarsmodellen är känd från ett inhemskt projekt Ljuspack. Vid ett tillfälle, när jag valde mellan DM63x och den välkända TLC5940, valde jag DM av flera anledningar: 1) TLC på Aliexpress är definitivt falsk, men den här är det inte; 2) DM har en autonom PWM med egen frekvensgenerator; 3) det kunde köpas billigt i Moskva, snarare än att vänta på ett paket från Ali. Och det var förstås intressant att lära sig styra chippet själv, snarare än att använda ett färdigt bibliotek. Chips presenteras nu huvudsakligen i SSOP24-paketet, de är lätta att löda till en adapter.

Eftersom tillverkaren är taiwanesisk, datablad chippet är skrivet på kinesisk engelska, vilket betyder att det kommer att bli roligt. Först tittar vi på pinouten (Stiftanslutning) för att förstå vilket ben som ska kopplas till, och en beskrivning av stiften (Stiftbeskrivning). 16 stift:

Läs datablad 2: SPI på STM32; PWM, timers och avbrott på STM8
DC Sink-källor (Open Drain)

Sjunka / Öppet avloppsutgång - dränera; källa för inströmmande ström; utgången är ansluten till jord i aktivt tillstånd - lysdioderna är anslutna till föraren med katoder. Elektriskt är detta naturligtvis inte ett "öppet avlopp" (öppna avlopp), men i datablad finns ofta denna beteckning för stift i dräneringsläge.

Läs datablad 2: SPI på STM32; PWM, timers och avbrott på STM8
Externa motstånd mellan REXT och GND för att ställa in utgångsströmvärdet

Ett referensmotstånd är installerat mellan REXT-stiftet och jord, som styr det interna motståndet för utgångarna, se grafen på sidan 9 i databladet. I DM634 kan detta motstånd också styras av mjukvara, som ställer in den totala ljusstyrkan (global ljusstyrka); Jag kommer inte att gå in på detaljer i den här artikeln, jag lägger bara ett 2.2 - 3 kOhm motstånd här.

För att förstå hur man styr chippet, låt oss titta på beskrivningen av enhetens gränssnitt:

Läs datablad 2: SPI på STM32; PWM, timers och avbrott på STM8

Ja, här är den, kinesisk engelska i all ära. Att översätta detta är problematiskt, du kan förstå det om du vill, men det finns ett annat sätt - titta på hur anslutningen till den funktionellt liknande TLC5940 beskrivs i databladet:

Läs datablad 2: SPI på STM32; PWM, timers och avbrott på STM8
... Endast tre stift krävs för att mata in data i enheten. Den stigande flanken på SCLK-signalen skiftar data från SIN-stiftet till det interna registret. Efter att all data har laddats, låser en kort hög XLAT-signal de sekventiellt överförda data till de interna registren. Interna register är grindar som triggas av XLAT-signalnivån. All data överförs den mest signifikanta biten först.

Spärr – spärr/spärr/lås.
Stigande kant – framkant av pulsen
MSB först – mest betydande (längst till vänster) bit framåt.
för att klocka data – överföra data sekventiellt (bit för bit).

Ord spärr finns ofta i dokumentationen för chips och är översatt på olika sätt, så för förståelsens skull tillåter jag mig

litet utbildningsprogramLED-drivrutinen är i huvudsak ett skiftregister. "Skift" (skifta) i namnet - bitvis rörelse av data inuti enheten: varje ny bit som skjuts inuti skjuter hela kedjan framåt framför sig. Eftersom ingen vill observera kaotisk blinkning av lysdioderna under skiftet, sker processen i buffertregister separerade från arbetsregistren med en spjäll (spärr) är ett slags väntrum där bitarna är ordnade i önskad sekvens. När allt är klart öppnas slutaren och bitarna börjar arbeta och ersätter den tidigare batchen. Ord spärr i dokumentationen för mikrokretsar innebär nästan alltid en sådan dämpare, oavsett i vilka kombinationer den används.

Så, dataöverföring till DM634 utförs så här: ställ in DAI-ingången till värdet för den mest signifikanta biten av den bortre lysdioden, dra DCK upp och ner; ställ in DAI-ingången till värdet för nästa bit, dra DCK; och så vidare tills alla bitar har sänts (klockade in), varefter vi drar LAT. Detta kan göras manuellt (bit-pang), men det är bättre att använda ett SPI-gränssnitt speciellt anpassat för detta, eftersom det presenteras på vår STM32 i två exemplar.

Blått piller STM32F103

Inledning: STM32-kontroller är mycket mer komplexa än Atmega328 än de kan verka skrämmande. Dessutom, av energibesparingsskäl, stängs nästan all kringutrustning av vid starten, och klockfrekvensen är 8 MHz från den interna källan. Lyckligtvis skrev STM-programmerare kod som för chippet upp till de "beräknade" 72 MHz, och författarna till alla IDE:er jag känner inkluderade den i initialiseringsproceduren, så vi behöver inte klocka (men du kan om du verkligen vill). Men du måste slå på kringutrustningen.

Dokumentation: Blue Pill är utrustad med det populära STM32F103C8T6-chippet, det finns två användbara dokument för det:

I databladet kan vi vara intresserade av:

  • Pinouts – chip pinouts – ifall vi bestämmer oss för att göra brädorna själva;
  • Memory Map – minneskarta för ett specifikt chip. Referenshandboken har en karta för hela linjen, och den nämner register som vår inte har.
  • Tabell för pindefinitioner – listar stiftens huvudsakliga och alternativa funktioner; för det "blåa pillret" kan du hitta mer bekväma bilder på Internet med en lista över stift och deras funktioner. Därför googlar vi omedelbart Blue Pill pinout och håller denna bild till hands:

Läs datablad 2: SPI på STM32; PWM, timers och avbrott på STM8
OBS: det var ett fel i bilden från Internet som noterades i kommentarerna, tack för det. Bilden har ersatts, men det här är en läxa - det är bättre att kontrollera information inte från datablad.

Vi tar bort databladet, öppnar referensmanualen och från och med nu använder vi bara det.
Procedur: vi hanterar standard input/output, konfigurerar SPI, sätter på nödvändig kringutrustning.

Ingång Utgång

På Atmega328 implementeras I/O extremt enkelt, vilket är anledningen till att överflöd av STM32-alternativ kan vara förvirrande. Nu behöver vi bara slutsatser, men även dessa har fyra alternativ:

Läs datablad 2: SPI på STM32; PWM, timers och avbrott på STM8
öppet avlopp, push-pull, alternativ push-pull, alternativt öppet avlopp

"Dra tryck" (tryck dra) är den vanliga utgången från Arduino, stiftet kan ta värdet antingen HÖG eller LÅG. Men med "öppet avlopp" finns det svårigheter, även om allt är enkelt här:

Läs datablad 2: SPI på STM32; PWM, timers och avbrott på STM8
Läs datablad 2: SPI på STM32; PWM, timers och avbrott på STM8
Utgångskonfiguration / när porten är tilldelad utgång: / utgångsbuffert aktiverad: / – öppet dräneringsläge: "0" i utgångsregistret aktiverar N-MOS, "1" i utgångsregistret lämnar porten i Hi-Z-läge ( P-MOS är inte aktiverad ) / – push-pull-läge: "0" i utgångsregistret aktiverar N-MOS, "1" i utgångsregistret aktiverar P-MOS.

Hela skillnaden mellan öppet avlopp (öppna avlopp) från "push-pull" (tryck dra) är att i det första stiftet inte kan acceptera HÖG-tillståndet: när man skriver en till utgångsregistret går den in i högresistansläge (hög impedans, Hej-Z). När man skriver noll beter sig stiftet likadant i båda lägena, både logiskt och elektriskt.

I normalt utgångsläge sänder stiftet helt enkelt innehållet i utgångsregistret. I "alternativet" styrs det av motsvarande kringutrustning (se 9.1.4):

Läs datablad 2: SPI på STM32; PWM, timers och avbrott på STM8
Om en portbit är konfigurerad som ett alternativt funktionsstift inaktiveras stiftregistret och stiftet ansluts till det perifera stiftet.

Alternativ funktionalitet för varje stift beskrivs i Pin-definitioner Databladet finns på den nedladdade bilden. På frågan om vad man ska göra om en stift har flera alternativa funktioner, ges svaret av en fotnot i databladet:

Läs datablad 2: SPI på STM32; PWM, timers och avbrott på STM8
Om flera kringutrustning använder samma stift, för att undvika konflikter mellan alternativa funktioner, bör endast en kringutrustning användas åt gången, växlad med hjälp av den perifera klockaktiveringsbiten (i lämpligt RCC-register).

Slutligen har stift i utgångsläge också en klockhastighet. Detta är ytterligare en energisparfunktion; i vårt fall ställer vi bara in den på maximalt och glömmer det.

Så: vi använder SPI, vilket innebär att två stift (med data och med en klocksignal) ska vara "alternativ push-pull-funktion", och en annan (LAT) ska vara "vanlig push-pull". Men innan vi tilldelar dem, låt oss ta itu med SPI.

SPI

Ännu ett litet utbildningsprogram

SPI eller Serial Peripheral Interface (serial peripheral interface) är ett enkelt och mycket effektivt gränssnitt för att koppla ihop en MK med andra MK:er och omvärlden i allmänhet. Principen för dess funktion har redan beskrivits ovan, om den kinesiska LED-drivrutinen (i referensmanualen, se avsnitt 25). SPI kan arbeta i master (”master”) och slav (”slave”) läge. SPI har fyra grundläggande kanaler, av vilka inte alla kan användas:

  • MOSI, Master Output / Slave Input: detta stift sänder data i masterläge och tar emot data i slavläge;
  • MISO, Master Input / Slave Output: tvärtom, den tar emot i mastern och sänder i slaven;
  • SCK, seriell klocka: ställer in frekvensen för dataöverföring i mastern eller tar emot en klocksignal i slaven. I huvudsak slå beats;
  • SS, Slave Select: med hjälp av denna kanal vet slaven att något önskas från honom. På STM32 kallas det NSS, där N = negativ, d.v.s. regulatorn blir en slav om det finns jord i denna kanal. Det kombinerar bra med läget Open Drain Output, men det är en annan historia.

Som allt annat är SPI på STM32 rik på funktionalitet, vilket gör det något svårt att förstå. Till exempel kan det fungera inte bara med SPI, utan också med ett I2S-gränssnitt, och i dokumentationen är deras beskrivningar blandade, det är nödvändigt att skära av överskottet i tid. Vår uppgift är extremt enkel: vi behöver bara skicka data med endast MOSI och SCK. Vi går till avsnitt 25.3.4 (halvduplexkommunikation, halvduplexkommunikation), där vi hittar 1 klocka och 1 enkelriktad datakabel (1 klocksignal och 1 enkelriktad dataström):

Läs datablad 2: SPI på STM32; PWM, timers och avbrott på STM8
I det här läget använder applikationen SPI i antingen sändningsläge eller endast mottagningsläge. / Endast sändningsläge liknar duplexläge: data sänds på sändningsstiftet (MOSI i masterläge eller MISO i slavläge), och mottagningsstiftet (MISO respektive MOSI) kan användas som ett vanligt I/O-stift . I det här fallet behöver applikationen bara ignorera Rx-bufferten (om den läses kommer det inte att finnas några överförda data där).

Bra, MISO-stiftet är gratis, låt oss ansluta LAT-signalen till det. Låt oss titta på Slave Select, som på STM32 kan styras programmatiskt, vilket är extremt bekvämt. Vi läser stycket med samma namn i avsnitt 25.3.1 SPI Allmän beskrivning:

Läs datablad 2: SPI på STM32; PWM, timers och avbrott på STM8
Programvarustyrning NSS (SSM = 1) / Slavvalsinformation finns i SSI-biten i SPI_CR1-registret. Det externa NSS-stiftet förblir ledigt för andra applikationsbehov.

Det är dags att skriva till registren. Jag bestämde mig för att använda SPI2, leta efter dess basadress i databladet - i avsnitt 3.3 Minneskarta:

Läs datablad 2: SPI på STM32; PWM, timers och avbrott på STM8

Nåväl, låt oss börja:

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

Öppna avsnitt 25.3.3 med den självförklarande titeln "Konfigurera SPI i Master Mode":

Läs datablad 2: SPI på STM32; PWM, timers och avbrott på STM8

1. Ställ in den seriella klockfrekvensen med bitar BR[2:0] i SPI_CR1-registret.

Registren finns samlade i referensmanualen med samma namn. Adressförskjutning (Adressförskjutning) för CR1 – 0x00, som standard rensas alla bitar (Återställ värde 0x0000):

Läs datablad 2: SPI på STM32; PWM, timers och avbrott på STM8

BR-bitarna ställer in styrenhetens klockdelare, vilket bestämmer frekvensen vid vilken SPI:n kommer att arbeta. Vår STM32-frekvens kommer att vara 72 MHz, LED-drivrutinen, enligt dess datablad, arbetar med en frekvens på upp till 25 MHz, så vi måste dividera med fyra (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. Ställ in CPOL- och CPHA-bitarna för att definiera förhållandet mellan dataöverföring och seriell klocktid (se diagram på sidan 240)

Eftersom vi läser ett datablad här och inte tittar på scheman, låt oss ta en närmare titt på textbeskrivningen av CPOL- och CPHA-bitarna på sidan 704 (SPI General Description):

Läs datablad 2: SPI på STM32; PWM, timers och avbrott på STM8
Klockfas och polaritet
Med hjälp av CPOL- och CPHA-bitarna i SPI_CR1-registret kan du programmässigt välja fyra timingrelationer. CPOL-biten (klockpolaritet) styr klocksignalens tillstånd när ingen data sänds. Denna bit styr master- och slavlägena. Om CPOL återställs är SCK-stiftet lågt i viloläge. Om CPOL-biten är inställd är SCK-stiftet högt under viloläge.
När CPHA-biten (klockfas) är inställd är fällningsstroben för hög bit den andra kanten av SCK-signalen (faller om CPOL är fri, stiger om CPOL är inställd). Data fångas upp av den andra förändringen i klocksignalen. Om CPHA-biten är ren, är den höga bitfällningsstroben den stigande flanken av SCK-signalen (fallande flank om CPOL är inställd, stigande flank om CPOL rensas). Data fångas vid den första ändringen av klocksignalen.

Efter att ha absorberat denna kunskap kommer vi till slutsatsen att båda bitarna måste förbli nollor, eftersom Vi vill att SCK-signalen ska förbli låg när den inte används och att data ska överföras på pulsens stigande kant (se fig. Rising Edge i DM634-databladet).

Förresten, här mötte vi först en funktion av ordförrådet i ST-datablad: i dem är frasen "återställ biten till noll" skriven för att återställa liteOch inte att rensa lite, som till exempel Atmega.

3. Ställ in DFF-biten för att avgöra om datablocket är 8-bitars eller 16-bitars format

Jag tog specifikt en 16-bitars DM634 för att inte bry mig om att överföra 12-bitars PWM-data, som DM633. Det är vettigt att ställa in DFF till ett:

#define DFF         0x0800

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

4. Konfigurera LSBFIRST-biten i SPI_CR1-registret för att bestämma blockformatet

LSBFIRST, som namnet antyder, konfigurerar överföringen med den minst signifikanta biten först. Men DM634 vill ta emot data från den mest signifikanta biten. Därför lämnar vi den återställd.

5. I hårdvaruläge, om inmatning från NSS-stiftet krävs, applicera en hög signal på NSS-stiftet under hela byteöverföringssekvensen. I NSS-programvaruläge, ställ in SSM- och SSI-bitarna i SPI_CR1-registret. Om NSS-stiftet ska användas som utgång behöver bara SSOE-biten ställas in.

Installera SSM och SSI för att glömma NSS-hårdvaruläget:

#define SSI         0x0100
#define SSM         0x0200

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

6. MSTR- och SPE-bitarna måste ställas in (de förblir inställda endast om NSS-signalen är hög)

Med dessa bitar utser vi faktiskt vår SPI som en mästare och slår på den:

#define MSTR        0x0004
#define SPE         0x0040

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

SPI är konfigurerad, låt oss omedelbart skriva funktioner som skickar bytes till föraren. Fortsätt läsa 25.3.3 "Konfigurera SPI i masterläge":

Läs datablad 2: SPI på STM32; PWM, timers och avbrott på STM8
Order för dataöverföring
Överföringen börjar när en byte skrivs till Tx-bufferten.
Databyten laddas in i skiftregistret kl parallell läge (från den interna bussen) under överföringen av den första biten, varefter den sänds till sekventiell MOSI-stiftläge, första eller sista bit framåt beroende på inställningen av LSBFIRST-biten i CPI_CR1-registret. TXE-flaggan sätts efter dataöverföring från Tx-buffert till skiftregisteroch genererar också ett avbrott om TXEIE-biten i CPI_CR1-registret är satt.

Jag lyfte fram några ord i översättningen för att uppmärksamma en funktion av SPI-implementeringen i STM-styrenheter. På Atmega TXE-flaggan (Tx tom, Tx är tom och redo att ta emot data) ställs in först efter att hela byten har skickats ut. Och här sätts denna flagga efter att byten har infogats i det interna skiftregistret. Eftersom den skjuts dit med alla bitar samtidigt (parallellt), och sedan data överförs sekventiellt, ställs TXE in innan byten är helt skickad. Detta är viktigt eftersom i fallet med vår LED-drivrutin måste vi dra i LAT-stiftet efter sändning Alla data, dvs. Enbart TXE-flaggan kommer inte att räcka för oss.

Det betyder att vi behöver en flagga till. Låt oss titta på 25.3.7 - "Statusflaggor":

Läs datablad 2: SPI på STM32; PWM, timers och avbrott på STM8
<…>
Läs datablad 2: SPI på STM32; PWM, timers och avbrott på STM8
UPPTAGEN flagga
BSY-flaggan ställs in och rensas av hårdvara (att skriva till den har ingen effekt). BSY-flaggan indikerar tillståndet för SPI-kommunikationslagret.
Den återställer:
när överföringen är klar (förutom i masterläge om överföringen är kontinuerlig)
när SPI är inaktiverat
när ett masterlägesfel uppstår (MODF=1)
Om överföringen inte är kontinuerlig rensas BSY-flaggan mellan varje dataöverföring

Okej, det här kommer väl till pass. Låt oss ta reda på var Tx-bufferten finns. För att göra detta, läs "SPI Data Register":

Läs datablad 2: SPI på STM32; PWM, timers och avbrott på STM8
Bits 15:0 DR[15:0] Dataregister
Mottagna data eller data som ska överföras.
Dataregistret är uppdelat i två buffertar - en för skrivning (sändningsbuffert) och en för läsning (mottagningsbuffert). Att skriva till dataregistret skriver till Tx-bufferten, och läsning från dataregistret kommer att returnera värdet som finns i Rx-bufferten.

Tja, och statusregistret, där TXE- och BSY-flaggorna finns:

Läs datablad 2: SPI på STM32; PWM, timers och avbrott 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
}

Tja, eftersom vi behöver sända 16 gånger två byte, beroende på antalet LED-drivrutiner, ungefär så här:

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 inte hur man drar i LAT-stiftet än, så vi går tillbaka till I/O.

Tilldela stift

I STM32F1 är registren som ansvarar för stiftens tillstånd ganska ovanliga. Det är klart att det finns fler av dem än Atmega, men de skiljer sig också från andra STM-chips. Avsnitt 9.1 Allmän beskrivning av GPIO:

Läs datablad 2: SPI på STM32; PWM, timers och avbrott på STM8
Var och en av de allmänna I/O-portarna (GPIO) har två 32-bitars konfigurationsregister (GPIOx_CRL och GPIOx_CRH), två 32-bitars dataregister (GPIOx_IDR och GPIOx_ODR), ett 32-bitars set/återställningsregister (GPIOx_BSRR), ett 16-bitars återställningsregister (GPIOx_BRR) och en 32-bit bitblockeringsregister (GPIOx_LCKR).

De två första registren är ovanliga och också ganska obekväma, eftersom stiften med 16 portar är utspridda över dem i ett "fyra bitar per bror"-format. De där. stiften noll till sju är i CRL, och resten är i CRH. Samtidigt innehåller de återstående registren framgångsrikt bitarna från alla stift i porten - ofta förblir hälften "reserverade".

För enkelhetens skull, låt oss börja från slutet av listan.

Vi behöver inget blockeringsregister.

Set- och återställningsregistren är ganska roliga eftersom de delvis duplicerar varandra: du kan bara skriva allt i BSRR, där de högre 16 bitarna återställer stiftet till noll, och de lägre kommer att ställas in på 1, eller så kan du också använd BRR, vars nedre 16 bitar återställer bara stiftet . Jag gillar det andra alternativet. Dessa register är viktiga eftersom de ger atomär tillgång till stift:

Läs datablad 2: SPI på STM32; PWM, timers och avbrott på STM8
Läs datablad 2: SPI på STM32; PWM, timers och avbrott på STM8
Atominställning eller återställning
Det finns inget behov av att inaktivera avbrott vid programmering av GPIOx_ODR på bitnivå: en eller flera bitar kan ändras med en enda atomskrivoperation APB2. Detta uppnås genom att skriva en "1" till set/återställningsregistret (GPIOx_BSRR eller, endast för återställning, GPIOx_BRR) för den bit som behöver ändras. Övriga bitar kommer att förbli oförändrade.

Dataregistren har ganska självförklarande namn - IDR = Ingång Riktningsregister, ingångsregister; ODR = Produktion Riktningsregister, utgångsregister. Vi kommer inte att behöva dem i det nuvarande projektet.

Och slutligen, kontrollregister. Eftersom vi är intresserade av de andra SPI-stiften, nämligen PB13, PB14 och PB15, tittar vi omedelbart på CRH:

Läs datablad 2: SPI på STM32; PWM, timers och avbrott på STM8

Och vi ser att vi kommer att behöva skriva något i bitar från 20 till 31.

Vi har redan räknat ut ovan vad vi vill ha av stift, så här kommer jag att göra utan en skärmdump, jag säger bara att MODE specificerar riktningen (ingång om båda bitarna är inställda på 0) och stifthastigheten (vi behöver 50MHz, d.v.s. både pin till "1"), och CNF ställer in läget: vanlig "push-pull" - 00, "alternativ" - 10. Som standard, som vi ser ovan, har alla pins den tredje biten från botten (CNF0), det sätter dem i läge flytande ingång.

Eftersom jag planerar att göra något annat med detta chip, har jag för enkelhets skull definierat alla möjliga MODE- och CNF-värden för både de nedre och övre kontrollregistren.

På något sätt så här

#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

Våra stift finns på port B (basadress – 0x40010C00), kod:

#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;

Och följaktligen kan du skriva definitioner för LAT, som kommer att ryckas av BRR- och BSRR-registren:

/*** 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 bara av tröghet, det har alltid varit så, låt det stanna)

Nu är allt bra, men det fungerar inte. Eftersom det här är STM32 sparar de el, vilket innebär att du måste aktivera klockning av den kringutrustning som krävs.

Slå på klockning

Klockan, även känd som Clock, ansvarar för klockning. Och vi kunde redan lägga märke till förkortningen RCC. Vi letar efter det i dokumentationen: det här är Reset and Clock Control.

Som sagt ovan, lyckligtvis gjordes den svåraste delen av klockningsämnet för oss av personer från STM, vilket vi tackar dem så mycket för (jag ska återigen ge en länk till Di Halts hemsida, för att tydliggöra hur förvirrande det är). Vi behöver bara register som är ansvariga för att möjliggöra perifer klockning (Peripheral Clock Enable Registers). Låt oss först hitta RCC:s basadress, den är i början av "Memory Map":

Läs datablad 2: SPI på STM32; PWM, timers och avbrott på STM8

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

Och sedan antingen klicka på länken där du försöker hitta något i plattan, eller, mycket bättre, gå igenom beskrivningarna av de möjliggörande registren från avsnitten om aktivera register. Där vi hittar RCC_APB1ENR och RCC_APB2ENR:

Läs datablad 2: SPI på STM32; PWM, timers och avbrott på STM8
Läs datablad 2: SPI på STM32; PWM, timers och avbrott på STM8

Och de innehåller följaktligen bitar som inkluderar klockning av SPI2, IOPB (I/O Port B) och alternativa 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 slutliga koden kan hittas här.

Om du har möjlighet och lust att testa, anslut då DM634 så här: DAI till PB15, DCK till PB13, LAT till PB14. Vi driver föraren från 5 volt, glöm inte att ansluta grunderna.

Läs datablad 2: SPI på STM32; PWM, timers och avbrott på STM8

STM8 PWM

PWM på STM8

När jag bara planerade den här artikeln bestämde jag mig för att som exempel försöka bemästra vissa funktioner hos ett obekant chip med bara ett datablad, så att jag inte skulle få en skomakare utan stövlar. STM8 var perfekt för den här rollen: för det första hade jag ett par kinesiska brädor med STM8S103, och för det andra är det inte särskilt populärt, och därför vilar frestelsen att läsa och hitta en lösning på Internet på bristen på just dessa lösningar.

Chipet har också datablad и referensmanual RM0016, i den första finns pinout och registeradresser, i den andra - allt annat. STM8 är programmerad i C i en fruktansvärd IDE ST visuell utveckling.

Klockning och I/O

Som standard arbetar STM8 med en frekvens på 2 MHz, detta måste korrigeras omedelbart.

Läs datablad 2: SPI på STM32; PWM, timers och avbrott på STM8
HSI (High Speed ​​​​Intern) klocka
HSI-klocksignalen härleds från en intern 16 MHz RC-oscillator med en programmerbar delare (1 till 8). Den ställs in i klockdelarregistret (CLK_CKDIVR).
Notera: vid starten väljs en HSI RC-oscillator med en delare på 8 som den ledande källan för klocksignalen.

Vi hittar registeradressen i databladet, beskrivningen i refman och ser att registret behöver rensas:

#define CLK_CKDIVR *(volatile uint8_t *)0x0050C6

CLK_CKDIVR &= ~(0x18);

Eftersom vi ska köra PWM och ansluta lysdioderna, låt oss titta på pinouten:

Läs datablad 2: SPI på STM32; PWM, timers och avbrott på STM8

Chipet är litet, många funktioner är upphängda på samma stift. Det som står inom hakparenteser är "alternativ funktionalitet", det växlas av "alternativbytes" (alternativbytes) – något som Atmega säkringar. Du kan ändra deras värden programmatiskt, men det är inte nödvändigt, eftersom Den nya funktionen aktiveras först efter en omstart. Det är enklare att använda ST Visual Programmer (nedladdat med Visual Develop), som kan ändra dessa byte. Pinouten visar att CH1- och CH2-stiften på den första timern är dolda inom hakparenteser; det är nödvändigt att ställa in AFR1- och AFR0-bitarna i STVP, och den andra kommer också att överföra CH1-utgången från den andra timern från PD4 till PC5.

Således kommer 6 stift att styra lysdioderna: PC6, PC7 och PC3 för den första timern, PC5, PD3 och PA3 för den andra.

Att ställa in själva I/O-stiften på STM8 är enklare och mer logiskt än på STM32:

  • bekant från Atmega DDR datariktningsregister (Datariktningsregister): 1 = utgång;
  • det första styrregistret CR1, när det matas ut, ställer in push-pull-moden (1) eller öppen dränering (0); eftersom jag ansluter lysdioderna till chipet med katoder lämnar jag nollor här;
  • det andra styrregistret CR2, när det matas ut, ställer in klockhastigheten: 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-inställning

Låt oss först definiera termerna:

  • PWM-frekvens – frekvensen med vilken timern tickar;
  • Automatisk omladdning, AR – autoloadable värde upp till vilket timern kommer att räknas (pulsperiod);
  • Uppdatera händelse, UEV – en händelse som inträffar när timern har räknat till AR;
  • PWM Duty Cycle – PWM arbetscykel, ofta kallad "driftsfaktor";
  • Fånga/jämför värde – värde för fångst/jämförelse, som timern har räknat till kommer att göra något (i fallet med PWM inverterar den utsignalen);
  • Förladdningsvärde – förinläst värde. Jämför värde kan inte ändras medan timern tickar, annars bryts PWM-cykeln. Därför placeras nya överförda värden i en buffert och dras ut när timern når slutet av sin nedräkning och återställs;
  • Kantjusterad и Centerjusterade lägen – inriktning längs gränsen och i mitten, samma som Atmels Snabb PWM и Fas-korrekt PWM.
  • OCiREF, utgångsjämförelsereferenssignal – referensutgångssignal, faktiskt, vad som visas på motsvarande stift i PWM-läge.

Som redan framgår av pinouten har två timers PWM-funktioner – den första och den andra. Båda är 16-bitars, den första har många ytterligare funktioner (i synnerhet kan den räkna både upp och ner). Vi behöver båda arbeta lika, så jag bestämde mig för att börja med den uppenbart sämre tvåan, för att inte råka använda något som inte finns där. Ett problem är att beskrivningen av PWM-funktionaliteten för alla timers i referensmanualen finns i kapitlet om den första timern (17.5.7 PWM-läge), så du måste hoppa fram och tillbaka genom hela dokumentet.

PWM på STM8 har en viktig fördel jämfört med PWM på Atmega:

Läs datablad 2: SPI på STM32; PWM, timers och avbrott på STM8
Gränsjusterad PWM
Kontokonfiguration från botten till toppen
Bottom-up-räkning är aktiv om DIR-biten i TIM_CR1-registret rensas
Exempel
Exemplet använder det första PWM-läget. PWM-referenssignalen OCiREF hålls hög så länge som TIM1_CNT < TIM1_CCRi. Annars tar det en låg nivå. Om jämförelsevärdet i TIM1_CCRi-registret är större än autoload-värdet (TIM1_ARR-registret), hålls OCiREF-signalen vid 1. Om jämförelsevärdet är 0 hålls OCiREF på noll..

STM8 timer under uppdatera händelse kontrollerar först jämför värde, och endast då alstrar en referenssignal. Atmegas timer skruvar först upp och jämför sedan, vilket resulterar i compare value == 0 utgången är en nål, som måste hanteras på något sätt (till exempel genom att programmässigt invertera logiken).

Så vad vi vill göra: 8-bitars PWM (AR == 255), räknat från botten till toppen, justering längs gränsen. Eftersom glödlamporna är anslutna till chipet med katoder, bör PWM mata 0 (LED på) tills jämför värde och 1 efter.

Vi har redan läst om några PWM-läge, så vi hittar det nödvändiga registret för den andra timern genom att söka i referensmanualen efter denna fras (18.6.8 - TIMx_CCMR1):

Läs datablad 2: SPI på STM32; PWM, timers och avbrott på STM8
110: Första PWM-läget – när man räknar från botten till toppen är den första kanalen aktiv medan TIMx_CNT < TIMx_CCR1. Annars är den första kanalen inaktiv. [längre i dokumentet finns det en felaktig copy-paste från timer 1] 111: Andra PWM-läge – när man räknar från botten till toppen är den första kanalen inaktiv medan TIMx_CNT < TIMx_CCR1. Annars är den första kanalen aktiv.

Eftersom lysdioderna är anslutna till MK med katoder, passar det andra läget oss (det första också, men det vet vi inte än).

Läs datablad 2: SPI på STM32; PWM, timers och avbrott på STM8
Bit 3 OC1PE: Aktivera stift 1 förspänning
0: Förladdningsregistret på TIMx_CCR1 är inaktiverat. Du kan skriva till TIMx_CCR1 när som helst. Det nya värdet fungerar direkt.
1: Förladdningsregistret på TIMx_CCR1 är aktiverat. Läs-/skrivoperationer får åtkomst till förladdningsregistret. Det förladdade värdet TIMx_CCR1 laddas in i skuggregistret under varje uppdateringshändelse.
*Obs: För att PWM-läget ska fungera korrekt måste förladdningsregistren vara aktiverade. Detta är inte nödvändigt i enkelsignalläge (OPM-biten ställs in i TIMx_CR1-registret).

Okej, låt oss slå på allt vi behöver för den andra timerns tre kanaler:

#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 av två åttabitarsregister, allt är enkelt:

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

TIM2_ARRH = 0;
TIM2_ARRL = 255;

Den andra timern kan bara räkna från botten till toppen, justering längs gränsen, ingenting behöver ändras. Låt oss ställa in frekvensdelaren, till exempel, till 256. För den andra timern ställs delaren in i TIM2_PSCR-registret och är en potens av två:

#define TIM2_PSCR  *(volatile uint8_t *)0x00530E

TIM2_PSCR = 8;

Allt som återstår är att slå på slutsatserna och själva den andra timern. Det första problemet löses av register Fånga/jämföra aktivera: det finns två, tre kanaler utspridda över dem asymmetriskt. Här kan vi också lära oss att det är möjligt att ändra polariteten på signalen, d.v.s. i princip var det möjligt att använda 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;

Och slutligen startar vi timern i TIMx_CR1-registret:

Läs datablad 2: SPI på STM32; PWM, timers och avbrott på STM8

#define TIM2_CR1   *(volatile uint8_t *)0x005300

TIM2_CR1 |= 1;

Låt oss skriva en enkel analog av AnalogWrite(), som kommer att överföra de faktiska värdena till timern för jämförelse. Registren namnges förutsägbart Fånga/jämför register, det finns två av dem för varje kanal: 8-bitarna av låg ordning i TIM2_CCRxL och de av hög ordningen i TIM2_CCRxH. Eftersom vi har skapat en 8-bitars PWM räcker det att bara skriva de minst signifikanta bitarna:

#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 uppmärksamma läsaren kommer att märka att vi har en något defekt PWM som inte kan producera 100% fyllning (vid ett maximalt värde på 255 inverteras signalen under en timercykel). För lysdioder spelar detta ingen roll, och den uppmärksamma läsaren kan redan gissa hur man fixar det.

PWM på den andra timern fungerar, låt oss gå vidare till den första.

Den första timern har exakt samma bitar i samma register (det är bara att de bitar som förblev "reserverade" i den andra timern används aktivt i den första för alla möjliga avancerade saker). Därför räcker det att hitta adresserna till samma register i databladet och kopiera koden. Tja, ändra värdet på frekvensdelaren, eftersom... den första timern vill inte ta emot en effekt av två, utan ett exakt 16-bitars värde i två register Prescaler High и Låg. Vi gör allt och... första timern fungerar inte. Vad är problemet?

Problemet kan bara lösas genom att titta igenom hela avsnittet om styrregistren för timer 1, där vi letar efter den som den andra timern inte har. Det kommer vara 17.7.30 Pausregister (TIM1_BKR), där det finns denna bit:

Läs datablad 2: SPI på STM32; PWM, timers och avbrott på STM8
Aktivera huvudutgång

#define TIM1_BKR   *(volatile uint8_t *)0x00526D

TIM1_BKR = (1<<7);

Det är allt säkert nu, koden där.

Läs datablad 2: SPI på STM32; PWM, timers och avbrott på STM8

STM8 Multiplex

Multiplexing på STM8

Det tredje miniprojektet är att ansluta åtta RGB-lysdioder till den andra timern i PWM-läge och få dem att visa olika färger. Den är baserad på konceptet med LED-multiplexering, vilket går ut på att om du slår på och stänger av lysdioder mycket, mycket snabbt, kommer det att tyckas för oss att de ständigt är på (synhållfasthettröghet hos visuell perception). Det gjorde jag en gång något sånt här på Arduino.

Arbetsalgoritmen ser ut så här:

  • ansluten anoden för den första RGB-lysdioden;
  • tände den och skickade de nödvändiga signalerna till katoderna;
  • väntade till slutet av PWM-cykeln;
  • ansluten anoden för den andra RGB-lysdioden;
  • tände den...

Tja osv. Naturligtvis krävs det att anoden är ansluten och lysdioden "tänds" för en vacker funktion. Tja, eller nästan. I vilket fall som helst måste vi skriva en kod som kommer att mata ut värden i tre kanaler av den andra timern, ändra dem när UEV nås och samtidigt ändra den för närvarande aktiva RGB-lysdioden.

Eftersom LED-växling sker automatiskt måste vi skapa ett "videominne" från vilket avbrottshanteraren kommer att ta emot data. Detta är en enkel array:

uint8_t colors[8][3];

För att ändra färgen på en specifik lysdiod räcker det att skriva de nödvändiga värdena i denna matris. Och variabeln kommer att ansvara för numret på den aktiva lysdioden

uint8_t cnt;

Demux

För korrekt multiplexering behöver vi konstigt nog en CD74HC238 demultiplexer. Demultiplexer - ett chip som implementerar operatören i hårdvara <<. Genom tre ingångsstift (bitar 0, 1 och 2) matar vi den med ett trebitars nummer X, och som svar aktiverar det utgångsnummer (1<<X). De återstående ingångarna på chippet används för att skala hela designen. Vi behöver detta chip inte bara för att minska antalet upptagna stift på mikrokontrollern, utan också för säkerheten - för att inte av misstag slå på fler lysdioder än möjligt och inte bränna MK. Chipet kostar en slant och ska alltid förvaras i ditt hemmedicinskåp.

Vår CD74HC238 kommer att ansvara för att mata spänning till anoden på den önskade lysdioden. I en fullfjädrad multiplex skulle den leverera spänning till kolonnen genom en P-MOSFET, men i denna demo är det möjligt direkt, eftersom den drar 20 mA, enligt Absolut högsta betyg i databladet. Från Datablad CD74HC238 vi behöver pinouts och detta cheat sheet:

Läs datablad 2: SPI på STM32; PWM, timers och avbrott på STM8
H = hög spänningsnivå, L = låg spänningsnivå, X – bryr sig inte

Vi ansluter E2 och E1 till jord, E3, A0, A1 och A3 till stiften PD5, PC3, PC4 och PC5 på STM8. Eftersom tabellen ovan innehåller både låga och höga nivåer, konfigurerar vi dessa stift som push-pull stift.

PWM

PWM på den andra timern är konfigurerad på samma sätt som i föregående berättelse, med två skillnader:

Först måste vi aktivera avbrottet Uppdatera händelse (UEV) som kommer att anropa en funktion som växlar den aktiva lysdioden. Detta görs genom att byta bit Aktivera uppdateringsavbrott i ett register med talande namn

Läs datablad 2: SPI på STM32; PWM, timers och avbrott på STM8
Avbryt aktivera register

#define TIM2_IER   *(volatile uint8_t *)0x005303

//enable interrupt
TIM2_IER = 1;

Den andra skillnaden är relaterad till fenomenet multiplexering, som t.ex spökbilder – parasitisk glöd av dioder. I vårt fall kan det verka på grund av det faktum att timern, efter att ha orsakat ett avbrott på UEV, fortsätter att ticka, och avbrottshanteraren har inte tid att byta lysdiod innan timern börjar skriva något till stiften. För att bekämpa detta måste du invertera logiken (0 = maximal ljusstyrka, 255 = ingenting lyser) och undvika extrema arbetscykelvärden. De där. se till att efter UEV lysdioderna slocknar helt under en PWM-cykel.

Ändra polaritet:

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

Undvik att ställa in r, g och b till 255 och kom ihåg att invertera dem när du använder dem.

Avbryter

Kärnan i ett avbrott är att chippet under vissa omständigheter slutar att köra huvudprogrammet och anropar någon extern funktion. Avbrott uppstår på grund av yttre eller inre påverkan, inklusive timern.

När vi först skapade ett projekt i ST Visual Develop, utöver main.c vi fick ett fönster med en mystisk fil stm8_interrupt_vector.c, automatiskt inkluderad i projektet. I den här filen tilldelas en funktion till varje avbrott NonHandledInterrupt. Vi måste binda vår funktion till det önskade avbrottet.

Databladet har en tabell med avbrottsvektorer, där vi hittar de vi behöver:

Läs datablad 2: SPI på STM32; PWM, timers och avbrott på STM8
13 TIM2 uppdatering/spill
14 TIM2 fånga/jämföra

Vi måste byta lysdiod vid UEV, så vi behöver avbrott #13.

Följaktligen för det första i akten stm8_interrupt_vector.c ändra standardnamnet på funktionen som ansvarar för avbrott nr 13 (IRQ13) till ditt eget:

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

För det andra måste vi skapa en fil main.h med följande innehåll:

#ifndef __MAIN_H
#define __MAIN_H

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

Och slutligen, skriv denna 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;
}

Allt som återstår är att möjliggöra avbrott. Detta görs med hjälp av kommandot assembler rim - du måste leta efter det Programmeringshandbok:

//enable interrupts
_asm("rim");

Ett annat assembler-kommando är sim – stänger av avbrott. De måste stängas av medan nya värden skrivs till "videominnet", så att ett avbrott orsakat vid fel ögonblick inte förstör arrayen.

All kod - på GitHub.

Läs datablad 2: SPI på STM32; PWM, timers och avbrott på STM8

Om åtminstone någon tycker att den här artikeln är användbar, så skrev jag den inte förgäves. Jag tar gärna emot kommentarer och kommentarer, jag ska försöka svara på allt.

Källa: will.com

Lägg en kommentar