Lees datablaaie 2: SPI op STM32; PWM, timers en onderbrekings op STM8

Lees datablaaie 2: SPI op STM32; PWM, timers en onderbrekings op STM8

В die eerste deel Ek het probeer om stokperdjie-elektronika-ingenieurs wat uit Arduino-broeke gegroei het, te vertel hoe en hoekom hulle datablaaie en ander dokumentasie vir mikrobeheerders moet lees. Die teks blyk groot te wees, so ek het belowe om praktiese voorbeelde in 'n aparte artikel te wys. Wel, hy het homself 'n laaier genoem ...

Vandag sal ek jou wys hoe om datablaaie te gebruik om redelik eenvoudige, maar noodsaaklike take vir baie projekte op STM32 (Blue Pill) en STM8-beheerders op te los. Alle demonstrasieprojekte is opgedra aan my gunsteling LED's, ons sal hulle in groot hoeveelhede verlig, waarvoor ons allerhande interessante randapparatuur sal moet gebruik.

Die teks het weer groot geblyk te wees, so gerieflikheidshalwe maak ek die inhoud:

STM32 Blou Pil: 16 LED's met DM634 drywer
STM8: Stel ses PWM-penne op
STM8: 8 RGB LED's op drie penne, onderbreek

Vrywaring: Ek is nie 'n ingenieur nie, ek gee nie voor dat ek diep kennis in elektronika het nie, die artikel is bedoel vir amateurs soos ek. Trouens, ek het myself twee jaar gelede as die teikengehoor beskou. As iemand toe vir my gesê het dat dit nie skrikwekkend is om datablaaie vir 'n onbekende skyfie te lees nie, sou ek nie baie tyd spandeer het om 'n paar stukkies kode op die internet te soek en krukke met 'n skêr en kleefpleister uit te vind nie.

Die fokus van hierdie artikel is datablaaie, nie konsepte nie, so die kode is dalk nie te gepoleer nie en dikwels 'n kruk. Die projekte self is baie eenvoudig, hoewel hulle geskik is vir die eerste kennismaking met 'n nuwe skyfie.

Ek hoop dat my artikel iemand in 'n soortgelyke stadium van 'n stokperdjie sal help.

STM32

16 LED's met DM634 en SPI

Klein projek met Blue Pill (STM32F103C8T6) en DM634 LED-bestuurder. Met behulp van datablaaie sal ons die bestuurder, STM IO-poorte hanteer en SPI konfigureer.

DM634

Taiwanese skyfie met 16 x 16-bis PWM-uitsette, kan vasgeketting word. Die jonger 12-bis-model is bekend van 'n huishoudelike projek ligte pak. Op 'n tyd, deur te kies tussen DM63x en die bekende TLC5940, het ek om verskeie redes op DM besluit: 1) TLC op Aliexpress is beslis vals, maar hierdie een is nie; 2) DM het 'n outonome PWM met sy eie frekwensiegenerator; 3) dit kan goedkoop in Moskou gekoop word, en nie wag vir 'n pakkie van Ali nie. En natuurlik was dit interessant om te leer hoe om die skyfie self te beheer, en nie 'n klaargemaakte biblioteek te gebruik nie. Skyfies word nou hoofsaaklik in die SSOP24-pakket aangebied, dit is maklik om op die adapter te soldeer.

Aangesien die vervaardiger Taiwanees is, datablad na die skyfie is in Chinees Engels geskryf, wat beteken dit sal pret wees. Kyk eers na die pinoutPenverbinding) om te verstaan ​​watter been om wat te verbind, en 'n beskrywing van die penne (Speldbeskrywing). 16 penne:

Lees datablaaie 2: SPI op STM32; PWM, timers en onderbrekings op STM8
Sink DC bronne (oop drein)

Wasbak / Oopdrein-uitset - voorraad; bron van inkomende stroom; 'n uitset gekoppel aan grond in 'n aktiewe toestand - LED's word deur katodes aan die drywer gekoppel. Elektries is dit natuurlik geen "oop drein" (oop drein), maar in datablaaie word so 'n benaming vir uitsette in dreineermodus dikwels gevind.

Lees datablaaie 2: SPI op STM32; PWM, timers en onderbrekings op STM8
Eksterne weerstande tussen REXT en GND om die uitsetstroomwaarde in te stel

'n Verwysingsweerstand word tussen die REXT-pen en grond geïnstalleer, wat die interne weerstand van die uitsette beheer, sien die grafiek op bladsy 9 van die datablad. In die DM634 kan hierdie weerstand ook deur sagteware beheer word deur die algehele helderheid (globale helderheid); Ek gaan nie in hierdie artikel in op besonderhede nie, ek sal net 'n 2.2 - 3 kOhm-weerstand hier plaas.

Om te verstaan ​​hoe om die skyfie te beheer, kom ons kyk na die beskrywing van die toestelkoppelvlak:

Lees datablaaie 2: SPI op STM32; PWM, timers en onderbrekings op STM8

Ja, hier is dit, Chinese Engels in al sy glorie. Dit is problematies om dit te vertaal, jy kan dit verstaan ​​as jy wil, maar daar is 'n ander manier - om te kyk hoe die verbinding in die datablad na die funksioneel naby TLC5940 beskryf word:

Lees datablaaie 2: SPI op STM32; PWM, timers en onderbrekings op STM8
… Slegs drie penne word benodig om data in die toestel in te voer. Die stygende rand van die SCLK-sein skuif die data van die SIN-pen na die interne register. Nadat alle data gelaai is, grendel 'n kort hoë XLAT-sein die serie-oorgedra data in interne registers. Interne registers is hekke wat deur die XLAT seinvlak geaktiveer word. Alle data word MSB eerste versend.

Grendel - grendel / grendel / grendel.
Stygende rand is die voorpunt van die pols
MSB eerste – die mees betekenisvolle (mees links) bietjie vorentoe.
om data te klok – versend data opeenvolgend (bietjie vir bietjie).

Woord grendel word dikwels in die dokumentasie vir skyfies gevind en word op 'n verskeidenheid maniere vertaal, so vir begrip sal ek myself toelaat

klein opvoedkundige programDie LED-bestuurder is in wese 'n skuifregister. "Shift" (skuif) in die naam - bietjie-vir-bietjie beweging van data binne die toestel: elke nuwe bietjie wat binne gedruk word, stoot die hele ketting vorentoe voor dit. Aangesien niemand die chaotiese flikkering van LED's tydens die skof wil waarneem nie, vind die proses plaas in bufferregisters wat van die werkers geskei is deur 'n sluiter (grendel) is 'n soort kleedkamer waar die stukkies in die verlangde volgorde in lyn is. Wanneer alles gereed is, gaan die sluiter oop en die stukkies gaan aan die werk en vervang die vorige bondel. Woord grendel in die dokumentasie vir mikrobane impliseer byna altyd so 'n demper, in watter kombinasies dit ook al gebruik word.

Dus, data-oordrag na DM634 word soos volg uitgevoer: stel die DAI-invoer in op die waarde van die hoë bietjie van die verre LED, trek DCK op en af; stel die DAI-invoer na die waarde van die volgende bietjie, trek DCK; en so aan totdat alle stukkies oorgedra is (ingeklok het), waarna ons LAT trek. Dit kan met die hand gedoen wordbietjie bang), maar dit is beter om die spesiaal skerpgemaakte SPI-koppelvlak hiervoor te gebruik, aangesien dit in twee kopieë op ons STM32 aangebied word.

Blou Tablet STM32F103

Inleidend: STM32-beheerders is baie meer ingewikkeld as Atmega328 as wat hulle skrikwekkend kan wees. Terselfdertyd, vir redes van energiebesparing, is byna alle randapparatuur aan die begin gedeaktiveer, en die klokfrekwensie is 8 MHz vanaf 'n interne bron. Gelukkig het die STM-programmeerders 'n kode geskryf wat die skyfie na die "berekende" 72 MHz bring, en die skrywers van al die IDE's wat ek ken het dit by die inisialiseringsprosedure ingesluit, so ons hoef nie te klok nie (maar jy kan as jy regtig wil). Maar jy moet die randapparatuur aanskakel.

Dokumentasie: Die gewilde STM32F103C8T6-skyfie is op Blue Pill geïnstalleer, daar is twee nuttige dokumente daarvoor:

In die datablad stel ons dalk belang in:

  • Pinouts - chip pinouts - ingeval ons besluit om self borde te maak;
  • Memory Map - 'n geheue kaart vir 'n spesifieke chip. Die naslaanhandleiding het 'n kaart vir die hele lyn, dit noem registers wat nie op ons s'n is nie.
  • Pendefinisiestabel - 'n lys van die hoof- en alternatiewe penfunksies; vir die "blou pil" op die internet kan jy geriefliker prente vind met 'n lys penne en hul funksies. Daarom google ons dadelik Blue Pill pinout en hou hierdie prentjie byderhand:

Lees datablaaie 2: SPI op STM32; PWM, timers en onderbrekings op STM8
NB: daar was 'n fout in die prentjie vanaf die internet, opgemerk in die kommentaar, waarvoor dankie. Die prentjie is vervang, maar dit is 'n les - dit is beter om inligting na te gaan, nie uit datablaaie nie.

Ons verwyder die datablad, maak die Verwysingshandleiding oop, van nou af gebruik ons ​​net dit.
Prosedure: hanteer standaard insette / uitvoer, stel SPI in, skakel die nodige randapparatuur aan.

Invoer Uitset

Op die Atmega328 is I/O uiters eenvoudig, en daarom kan die oorvloed STM32-opsies verwarrend wees. Nou het ons net gevolgtrekkings nodig, maar selfs daar is vier opsies:

Lees datablaaie 2: SPI op STM32; PWM, timers en onderbrekings op STM8
Oop dreineer-uitset, druk-trek-uitset, druk-trek alternatiewe, oop-dreineer alternatiewe

"Trek druk" (stoot trek) - die gewone uitset van die Arduino, die pen kan óf HOOG óf LAAG wees. Maar met die "oop drein" ontstaan probleme, hoewel alles hier eintlik eenvoudig is:

Lees datablaaie 2: SPI op STM32; PWM, timers en onderbrekings op STM8
Lees datablaaie 2: SPI op STM32; PWM, timers en onderbrekings op STM8
Uitsetkonfigurasie / wanneer poort aan uitset toegewys is: / uitsetbuffer geaktiveer: / – oop dreinmodus: "0" in die uitsetregister aktiveer N-MOS, "1" in die uitsetregister verlaat die poort in Hi-Z-modus (P -MOS is nie geaktiveer nie ) / - druk-trek-modus: "0" in die uitsetregister aktiveer N-MOS, "1" in die uitsetregister aktiveer P-MOS.

Alle oop drein verskil (oop drein) van "druk-trek" (stoot trek) is dat dit in die eerste pen nie die HOOG-toestand kan neem nie: wanneer 'n eenheid na die uitsetregister geskryf word, gaan dit in hoë weerstandmodus (hoë impedansie, Hi-Z). Wanneer nul geskryf word, gedra die pen in beide modusse dieselfde, beide logies en elektries.

In normale uitsetmodus vertaal die pen eenvoudig die inhoud van die uitsetregister. In "alternatief" word dit beheer deur die ooreenstemmende randapparaat (sien 9.1.4):

Lees datablaaie 2: SPI op STM32; PWM, timers en onderbrekings op STM8
As die poortbis gekonfigureer is as 'n alternatiewe funksie-uitset, word die uitsetregister gedeaktiveer en die pen is gekoppel aan die uitsetsein van die randapparaat.

Die alternatiewe funksionaliteit van elke pen word beskryf in Spelddefinisies Die datablad is op die afgelaaide prent. Op die vraag wat om te doen as die pen verskeie alternatiewe funksies het, word die antwoord gegee deur 'n voetnoot in die datablad:

Lees datablaaie 2: SPI op STM32; PWM, timers en onderbrekings op STM8
As veelvuldige randapparatuur dieselfde pen gebruik, om konflik tussen alternatiewe funksies te vermy, moet slegs een randapparaat op 'n slag gebruik word, met behulp van die Periferal Clock Enable-bis (in die ooreenstemmende RCC-register).

Laastens het penne in uitsetmodus ook 'n klokspoed. Dit is nog 'n energiebesparende kenmerk, in ons geval stel ons dit net op die maksimum en vergeet dit.

Dus: ons gebruik SPI, wat beteken dat twee penne (met data en met 'n kloksein) "alternatiewe druk-trek-funksie" moet wees, en nog een (LAT) moet "normale druk-trek" wees. Maar voordat ons hulle toewys, kom ons gaan met SPI.

SPI

Nog 'n klein hack

SPI of Serial Peripheral Interface (seriële perifere koppelvlak) is 'n eenvoudige en baie effektiewe koppelvlak om MK met ander MK's en die buitewêreld in die algemeen te kommunikeer. Die beginsel van sy werking is reeds hierbo beskryf, waar oor die Chinese LED-bestuurder (sien afdeling 25 in die verwysingshandleiding). SPI kan in meester ("meester") en slaaf ("slaaf") modus werk. SPI het vier basiese kanale, waarvan nie almal betrokke mag wees nie:

  • MOSI, Meester Uitset / Slaaf Invoer: hierdie pen stuur data in Meester af, en ontvang data in slaaf af;
  • MISO, Meester Invoer / Slawe Uitset: inteendeel, in die meester wat dit ontvang, in die slaaf wat dit gee;
  • SCK, Serial Clock: stel die frekwensie van data-oordrag in die meester of ontvang 'n kloksein in die slaaf. In wese, klop die slae;
  • SS, Slave Select: met hierdie kanaal weet die slaaf dat hulle iets van hom wil hê. Op STM32 word dit NSS genoem, waar N = negatief, d.w.s. die beheerder word 'n slaaf as hierdie kanaal 'n grond het. Dit kombineer goed met die Open Drain Output-modus, maar dit is 'n ander storie.

Soos alles anders, is SPI op STM32 ryk aan funksionaliteit, wat dit ietwat moeilik maak om te verstaan. Byvoorbeeld, dit kan nie net met SPI werk nie, maar ook met 'n I2S-koppelvlak, en hul beskrywings word in die dokumentasie gemeng, u moet die oorskot betyds afsny. Ons taak is uiters eenvoudig: jy hoef net data te gee deur slegs MOSI en SCK te gebruik. Ons gaan na afdeling 25.3.4 (halfdupleks kommunikasie, halfdupleks kommunikasie), waar ons vind 1 horlosie en 1 eenrigting datadraad (1 horlosie en 1 eenrigting datastroom):

Lees datablaaie 2: SPI op STM32; PWM, timers en onderbrekings op STM8
In hierdie modus gebruik die toepassing SPI in óf slegs versend óf slegs ontvang modus. / Die versend-alleen-modus is soortgelyk aan dupleksmodus: data word oorgedra op die versendingpen (MOSI in meestermodus of MISO in slaafmodus), terwyl die ontvangpen (MISO of MOSI onderskeidelik) as 'n normale I/O gebruik kan word speld. In hierdie geval is dit genoeg dat die toepassing die Rx-buffer ignoreer (as dit gelees word, sal daar geen data oorgedra word nie).

Groot, die MISO-pen is gratis, kom ons koppel die LAT-sein daaraan. Kom ons hanteer Slave Select, wat programmaties op STM32 beheer kan word, wat uiters gerieflik is. Ons lees die gelyknamige paragraaf in afdeling 25.3.1 SPI Algemene Beskrywing:

Lees datablaaie 2: SPI op STM32; PWM, timers en onderbrekings op STM8
NSS sagteware beheer (SSM = 1) / Slaaf seleksie inligting is vervat in die SSI bis van die SPI_CR1 register. Die eksterne NSS-pen word vry gelaat vir ander toepassingsbehoeftes.

Dit is tyd om aan die registers te skryf. Ek het besluit om SPI2 te gebruik, ons soek sy basisadres in die datablad - in afdeling 3.3 Memory Map (Memory Map):

Lees datablaaie 2: SPI op STM32; PWM, timers en onderbrekings op STM8

Wel, kom ons begin:

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

Ons maak afdeling 25.3.3 oop met die veelseggende titel "Configuring SPI in master mode":

Lees datablaaie 2: SPI op STM32; PWM, timers en onderbrekings op STM8

1. Stel die seriële koppelvlakklok met die BR[2:0] bisse in die SPI_CR1 register.

Die registers word in die verwysingshandleiding-afdeling met dieselfde naam versamel. Adres verskuiwing (adres verreken) CR1 het 0x00, by verstek word alle bisse skoongemaak (Stel waarde terug 0x0000):

Lees datablaaie 2: SPI op STM32; PWM, timers en onderbrekings op STM8

Die BR bisse stel die kontroleerder klokverdeler, en bepaal dus die frekwensie waarteen die SPI sal werk. Die STM32-frekwensie sal 72 MHz wees, die LED-drywer, volgens sy datablad, werk teen 'n frekwensie van tot 25 MHz, so ons moet deur vier deel (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. Stel die CPOL en CPHA bisse om die verhouding tussen data-oordrag en seriële koppelvlakklok te definieer (sien diagram op bladsy 240)

Aangesien ons 'n datablad hier lees en nie na skematiese kyk nie, kom ons kyk noukeuriger na die tekstuele beskrywing van die CPOL en CPHA stukkies op bladsy 704 (SPI Algemene Beskrywing):

Lees datablaaie 2: SPI op STM32; PWM, timers en onderbrekings op STM8
Klokfase en polariteit
Deur die CPOL- en CPHA-bisse van die SPI_CR1-register te gebruik, kan jy vier opsies vir tydsberekeningsverhoudings programmaties kies. Die CPOL (Klokpolariteit)-bis ​​beheer die toestand van die kloksein wanneer geen data versend word nie. Hierdie bietjie beheer meester- en slaafmodusse. As CPOL teruggestel word, is die SCK-pen laag in rus. As die CPOL-bis ingestel is, is die SCK-pen hoog wanneer dit ledig is.
As die CPHA (Klokfase)-bis ​​ingestel is, is die MSB-vangstrobe die tweede rand van die SCK-sein (val as CPOL skoongemaak word, of stygende rand as CPOL gestel is). Die data word vasgemaak met die tweede klokverandering. As die CPHA-bis skoongemaak word, is die hoë-bis-vangerstrobe die stygende rand van die SCK-sein (val as CPOL gestel is, of stygende flank as CPOL duidelik is). Data word vasgemaak met die eerste klokverandering.

Nadat ons hierdie kennis geproe het, kom ons tot die gevolgtrekking dat beide stukkies nul moet bly, want ons wil hê dat die SCK-sein laag moet bly wanneer dit nie gebruik word nie, en dat data op die stygende rand van die pols gestuur word (sien fig. stygende rand in datablad DM634).

Terloops, hier het ons die eerste keer 'n kenmerk van die woordeskat in die ST-datablaaie teëgekom: daarin word die frase "terugstel die bietjie na nul" geskryf om 'n bietjie terug te stelen nie bietjie skoon te maak, soos byvoorbeeld Atmega.

3. Stel die DFF-bis om die 8-bis of 16-bis datablokformaat te bepaal

Ek het spesifiek die 16-bis DM634 geneem om nie te pla met die oordrag van 12-bis PWM data, soos die DM633 nie. DFF maak sin om in eenheid te sit:

#define DFF         0x0800

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

4. Konfigureer die LSBFIRST-bis in die SPI_CR1-register om die blokformaat te definieer

LSBFIRST, soos sy naam aandui, stel die transmissie op met die minste beduidende bietjie eerste. Maar die DM634 wil eers data MSB ontvang. Daarom laat ons dit herstel.

5. In hardeware-modus, as invoer vanaf die NSS-pen vereis word, ry die NSS-pen hoog tydens die hele greepoordragvolgorde. In NSS program af, stel die SSM en SSI bisse in die SPI_CR1 register. As die NSS-pen uitgevoer moet word, hoef slegs die SSOE-bis ingestel te word.

Installeer SSM en SSI om van NSS-hardewaremodus te vergeet:

#define SSI         0x0100
#define SSM         0x0200

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

6. Die MSTR en SPE bisse moet gestel word (hulle bly net gestel as die NSS hoog is)

Eintlik, met hierdie stukkies ken ons ons SPI as 'n meester toe en skakel dit aan:

#define MSTR        0x0004
#define SPE         0x0040

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

SPI is gekonfigureer, kom ons skryf onmiddellik funksies wat grepe na die bestuurder stuur. Lees verder 25.3.3 "Konfigureer SPI in Meestermodus":

Lees datablaaie 2: SPI op STM32; PWM, timers en onderbrekings op STM8
Data oordrag bestelling
Die oordrag begin wanneer 'n greep na die Tx-buffer geskryf word.
Die datagreep word in die skuifregister gelaai by parallel modus (vanaf die interne bus) tydens die oordrag van die eerste bis, waarna dit in gestuur word konsekwent MOSI-penmodus, eerste of laaste bietjie vorentoe afhangende van die instelling van die LSBFIRST-bis in die CPI_CR1-register. TXE-vlag word gestel na data-oordrag van Tx buffer na skuifregister, en 'n onderbreking word gegenereer as die TXEIE-bis in die CPI_CR1-register gestel is.

Ek het 'n paar woorde in die vertaling uitgelig om die aandag te vestig op een kenmerk van die implementering van SPI in STM-beheerders. Op die Atmega, die TXE vlag (Tx Leeg, Tx is leeg en gereed om data te ontvang) word eers gestel nadat die hele greep gestuur is uiterlik. En hier word hierdie vlag gestel nadat die greep in die interne skuifregister ingedruk is. Aangesien dit met al die bisse gelyktydig (in parallel) daarheen gestoot word en dan die data opeenvolgend versend word, word TXE gestel voordat die greep heeltemal gestuur word. Dit is belangrik omdat in die geval van ons LED-bestuurder, moet ons die LAT-pen trek nadat ons gestuur is alle data, d.w.s. net die TXE-vlag sal nie vir ons genoeg wees nie.

Wat beteken ons het nog 'n vlag nodig. Kom ons kyk na 25.3.7 - "Statusvlae":

Lees datablaaie 2: SPI op STM32; PWM, timers en onderbrekings op STM8
<…>
Lees datablaaie 2: SPI op STM32; PWM, timers en onderbrekings op STM8
BESIGE vlag
Die BSY-vlag word deur hardeware gestel en skoongemaak (om daarna te skryf het geen effek nie). Die BSY-vlag dui die toestand van die SPI-kommunikasielaag aan.
Dit herstel:
wanneer die oordrag voltooi is (behalwe in meestermodus as die oordrag deurlopend is)
wanneer SPI gedeaktiveer is
wanneer 'n meestermodusfout voorkom (MODF=1)
As die oordrag nie deurlopend is nie, word die BSY-vlag tussen elke data-oordrag skoongemaak.

Goed, dit sal handig te pas kom. Vind uit waar die Tx-buffer geleë is. Om dit te doen, lees die "SPI Data Register":

Lees datablaaie 2: SPI op STM32; PWM, timers en onderbrekings op STM8
Bits 15:0 DR[15:0] Dataregister
Data ontvang of data wat oorgedra moet word.
Die dataregister word in twee buffers verdeel, een vir skryf (stuurbuffer) en een vir lees (ontvangbuffer). 'n Skryf na die dataregister skryf na die Tx-buffer, en 'n lees vanaf die dataregister sal die waarde terugstuur wat in die Rx-buffer vervat is.

Wel, die statusregister, waar daar TXE- en BSY-vlae is:

Lees datablaaie 2: SPI op STM32; PWM, timers en onderbrekings op STM8

Ons skryf:

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

Wel, aangesien ons 16 keer twee grepe moet oordra, volgens die aantal uitsette van die LED-bestuurder, iets soos hierdie:

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();
}

Maar ons weet nog nie hoe om die LAT-pen te trek nie, so kom ons gaan terug na I/O.

Ken penne toe

In STM32F1 is die registers wat verantwoordelik is vir die toestand van die penne nogal ongewoon. Dit is duidelik dat daar meer van hulle as Atmega is, maar hulle verskil ook van ander STM-skyfies. Afdeling 9.1 GPIO Algemene beskrywing:

Lees datablaaie 2: SPI op STM32; PWM, timers en onderbrekings op STM8
Elk van die algemene I/O-poorte (GPIO) het twee 32-bis konfigurasieregisters (GPIOx_CRL en GPIOx_CRH), twee 32-bis dataregisters (GPIOx_IDR en GPIOx_ODR), 'n 32-bis stel/terugstel register (GPIOx_BSRR), 'n 16-bis terugstel register (GPIOx_BRR) en 'n 32- bietjie blokkeer register (GPIOx_LCKR).

Ongewoon, en ook taamlik ongerieflik, is die eerste twee registers hier, want die 16 penne van die poort is oor hulle gestrooi in 'n "vier stukkies per broer"-formaat. Dié. penne XNUMX tot XNUMX is in CRL, en die res is in CRH. Terselfdertyd pas die oorblywende registers suksesvol die stukkies van al die poortpennetjies - dikwels bly die helfte "gereserveer".

Kom ons begin vir eenvoud aan die einde van die lys.

Ons het nie 'n blokkeerregister nodig nie.

Die stel- en terugstelregisters is nogal snaaks omdat hulle mekaar gedeeltelik dupliseer: jy kan alles net in BSRR skryf, waar die boonste 16 bisse die pen na nul sal terugstel, en die onderste op 1 gestel sal wees, of jy kan ook gebruik BRR, waarvan die onderste 16 bisse net die pen terugstel. Ek hou van die tweede opsie. Hierdie registers is belangrik omdat hulle atoomtoegang tot die penne bied:

Lees datablaaie 2: SPI op STM32; PWM, timers en onderbrekings op STM8
Lees datablaaie 2: SPI op STM32; PWM, timers en onderbrekings op STM8
Atoom installeer of herstel
Jy hoef nie onderbrekings te deaktiveer wanneer jy GPIOx_ODR op die bisvlak programmeer nie: jy kan een of meer bisse verander met 'n enkele APB2 atoomskryfbewerking. Dit word bereik deur 'n "1" te skryf na die stel/terugstel register (GPIOx_BSRR of, slegs vir terugstelling, GPIOx_BRR) van die bis wat verander moet word. Ander stukkies sal onveranderd bly.

Dataregisters het nogal sprekende name - IDR = insette Rigtingregister, invoerregister; ODR= Uitgawe Rigtingregister, uitsetregister. In die huidige projek het ons hulle nie nodig nie.

En uiteindelik registreer die beheer. Aangesien ons belangstel in die penne van die tweede SPI, naamlik PB13, PB14 en PB15, kyk ons ​​dadelik na CRH:

Lees datablaaie 2: SPI op STM32; PWM, timers en onderbrekings op STM8

En ons sien dat dit nodig sal wees om iets in stukkies van die 20ste tot die 31ste te skryf.

Ons het reeds uit die penne hierbo uitgepluis wat ons wil hê, so hier sal ek sonder 'n kiekie doen, net sê dat MODE die rigting stel (invoer as beide bisse op 0 gestel is) en die spoed van die pen (ons benodig 50MHz, d.w.s. beide pen na "1"), en CNF stel die modus: normale "druk-druk" - 00, "alternatief" - 10. By verstek, soos ons hierbo kan sien, het alle penne die derde bietjie van onder ( CNF0), stel dit hulle in modus drywende insette.

Aangesien ek van plan is om iets anders met hierdie skyfie te doen, vir eenvoud, het ek oor die algemeen alle moontlike MODE- en CNF-waardes vir beide die onderste en boonste beheerregisters gedefinieer.

Op een of ander manier soos hierdie

#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

Ons penne is op poort B (basisadres - 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;

En dienooreenkomstig kan jy definisies vir LAT skryf, wat die BRR- en BSRR-registers sal verdraai:

/*** 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 net deur traagheid, op een of ander manier was dit altyd, laat dit vir jouself bly)

Nou is alles wonderlik, dit werk net nie. Omdat dit STM32 is, bespaar hulle elektrisiteit hier, wat beteken dat jy die klok van die nodige randapparatuur moet aanskakel.

Skakel klok aan

Die horlosie is verantwoordelik vir klok, hulle is ook Klok. En ons kon reeds die afkorting RCC opmerk. Ons soek dit in die dokumentasie: dit is Herstel en Klokbeheer (Bestuur van herstel en klok).

Soos hierbo genoem, het die mense van STM gelukkig die moeilikste deel van die klokonderwerp vir ons gedoen, waarvoor baie dankie aan hulle (ek sal weer 'n skakel gee na Di Halt se webwerfom dit duidelik te maak hoe verward dit is). Ons benodig slegs registers wat verantwoordelik is vir die aktivering van perifere klok (Periferal Clock Enable Registers). Kom ons soek eers die basisadres van die RCC, dit is heel aan die begin van die "Geheuekaart":

Lees datablaaie 2: SPI op STM32; PWM, timers en onderbrekings op STM8

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

En klik dan óf op die skakel waar om iets in die tabel te probeer vind, óf, veel beter, gaan oor die beskrywings van die insluitende registers uit die afdelings oor aktiveer registers. Waar vind ons RCC_APB1ENR en RCC_APB2ENR:

Lees datablaaie 2: SPI op STM32; PWM, timers en onderbrekings op STM8
Lees datablaaie 2: SPI op STM32; PWM, timers en onderbrekings op STM8

En in hulle, onderskeidelik, stukkies wat die klok van SPI2, IOPB (I / O Port B) en alternatiewe funksies (AFIO) insluit.

#define _APB2ENR 0x18
#define _APB1ENR 0x1C

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

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

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

Die finale kode kan gevind word hier.

As daar 'n geleentheid en begeerte is om te toets, dan verbind ons DM634 so: DAI na PB15, DCK na PB13, LAT na PB14. Ons voed die bestuurder van 5 volt, moenie vergeet om die gronde te kombineer nie.

Lees datablaaie 2: SPI op STM32; PWM, timers en onderbrekings op STM8

STM8 PWM

PWM op STM8

Toe ek net hierdie artikel beplan het, het ek byvoorbeeld besluit om die een of ander funksionaliteit van 'n onbekende skyfie met behulp van slegs 'n datablad te probeer bemeester, sodat 'n skoenmaker nie sonder stewels sou uitkom nie. STM8 was perfek vir hierdie rol: eerstens het ek 'n paar Chinese borde met STM8S103 gehad, en tweedens is dit nie baie gewild nie, en daarom berus die versoeking om te lees en 'n oplossing op die internet te vind op die afwesigheid van dieselfde oplossings.

Die chip het ook datablad и verwysingshandleiding RM0016, in die eerste pinout en register adresse, in die tweede - alles anders. Programmering van STM8 in C in 'n lelike IDE ST Visuele Ontwikkel.

Horlosie en I/O

By verstek werk STM8 teen 'n frekwensie van 2 MHz, dit moet onmiddellik reggestel word.

Lees datablaaie 2: SPI op STM32; PWM, timers en onderbrekings op STM8
HSI-klok (hoë intern)
Die HSI-klok is afgelei van 'n interne 16 MHz RC-ossillator met 'n programmeerbare verdeler (1 tot 8). Dit word in die klokverdelerregister (CLK_CKDIVR) ingestel.
Let wel: HSI RC-ossillator met 'n verdeler van 8 word gekies as die hoofklokbron by opstart.

Ons vind die adres van die register in die datablad, die beskrywing in refman en sien dat die register skoongemaak moet word:

#define CLK_CKDIVR *(volatile uint8_t *)0x0050C6

CLK_CKDIVR &= ~(0x18);

Aangesien ons PWM gaan laat loop en LED's gaan koppel, kom ons kyk na die pinout:

Lees datablaaie 2: SPI op STM32; PWM, timers en onderbrekings op STM8

Die skyfie is klein, baie funksies is op dieselfde penne opgeskort. Wat tussen vierkantige hakies is, is "alternatiewe funksionaliteit", dit word omgeskakel deur "opsiegrepe" (opsie grepe) - iets soos Atmega versmelt. U kan hul waardes programmaties verander, maar dit is nie nodig nie, want. Die nuwe funksionaliteit word eers geaktiveer na 'n herlaai. Dit is makliker om ST Visual Programmer (afgelaai met Visual Develop) te gebruik, wat hierdie grepe kan verander. Die pinout wys dat die uitsette CH1 en CH2 van die eerste timer tussen vierkantige hakies versteek is; dit is nodig om die AFR1 en AFR0 bisse in STVP te stel, en die tweede een sal ook die uitset CH1 van die tweede timer van PD4 na PC5 oordra.

Dus, 6 penne sal die LED's beheer: PC6, PC7 en PC3 vir die eerste timer, PC5, PD3 en PA3 vir die tweede.

Die opstel van die I/O-penne self op die STM8 is eenvoudiger en meer logies as op die STM32:

  • Atmega-bekende data rigting register DDR (Data Rigting Register): 1 = uitset;
  • die eerste beheerregister CR1 stel die druk-trek-modus (1) of oop drein (0) wanneer dit uitgegee word; aangesien ek die LED's met katodes aan die skyfie koppel, laat ek nulle hier;
  • die tweede beheerregister CR2 stel die klokspoed in wanneer dit uitgestuur word: 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 instelling

Kom ons definieer eers terme:

  • PWM Frekwensie – frekwensie waarmee die timer tik;
  • Outo-herlaai, AR - outo-gelaaide waarde, tot wat die timer sal tel (pulsperiode);
  • Dateer gebeurtenis, UEV – 'n gebeurtenis wat plaasvind wanneer die timer tot AR getel het;
  • PWM-dienssiklus - PWM dienssiklus, dikwels genoem "dienssiklus";
  • Vang/Vergelyk waarde – waarde om vas te vang/vergelyk, tel tot waar die timer iets sal doen (in die geval van PWM, keer dit die uitsetsein om);
  • vooraflaai waarde - voorafgelaaide waarde. waarde vergelyk kan nie verander terwyl die timer tik nie, anders sal die PWM-siklus breek. Daarom word nuwe oorgedra waardes in die buffer geplaas en uitgetrek wanneer die timer die einde van die aftelling bereik en teruggestel word;
  • Randbelyn и Middelgerigte modusse – belyning op die grens en in die middel, dieselfde as atmelovskie Vinnige PWM и Fase-korrekte PWM.
  • OCiREF, Uitset Vergelyk Verwysing Sein - die verwysingsuitsetsein, in werklikheid, wat op die ooreenstemmende pen in PWM-modus verskyn.

Soos reeds duidelik uit die pinout is, het twee timers PWM-vermoëns - die eerste en tweede. Albei is 16-bis, die eerste een het baie bykomende kenmerke (dit kan veral op en af ​​tel). Ons moet albei op dieselfde manier werk, so ek het besluit om met die ooglopend swakker tweede een te begin, om nie per ongeluk iets te gebruik wat nie daarin is nie. Een of ander probleem is dat die beskrywing van die PWM-funksionaliteit van alle timers in die verwysingshandleiding in die hoofstuk oor die eerste timer (17.5.7 PWM-modus) is, so jy moet heeltyd heen en weer deur die dokument spring.

PWM op STM8 het 'n belangrike voordeel bo Atmega PWM:

Lees datablaaie 2: SPI op STM32; PWM, timers en onderbrekings op STM8
PWM met randbelyning
Rekeningkonfigurasie van onder na bo
Optelling is aktief as die DIR-bis in die TIM_CR1-register duidelik is
Voorbeeld
Die voorbeeld gebruik die eerste PWM-modus. Die PWM verwysingsein OCiREF word hoog gehou solank TIM1_CNT < TIM1_CCRi. Andersins neem dit 'n lae vlak. As die waarde om te vergelyk in die TIM1_CCRi-register groter is as die outolaaiwaarde (TIM1_ARR-register), word die OCiREF-sein op 1 gehou. As die vergelykingswaarde 0 is, word OCiREF op nul gehou....

STM8 timer tydens gebeurtenis opdateer tjeks eers waarde vergelyk, en produseer dan eers 'n verwysingsein. In Atmega bewe die timer eers, en vergelyk dan, as gevolg waarvan, wanneer compare value == 0 die uitset is 'n naald wat op een of ander manier hanteer moet word (byvoorbeeld deur die logika programmaties om te keer).

So wat ons wil doen: 8-bis PWM (AR == 255), tel van onder na bo, belyning langs die grens. Aangesien die gloeilampe met katodes aan die skyfie gekoppel is, behoort die PWM 0 uit te voer (LED aan) tot waarde vergelyk en 1 daarna.

Ons het al van sommige gelees PWM-modus, dus vind ons die gewenste register van die tweede timer deur te soek in die verwysingshandleiding vir hierdie frase (18.6.8 - TIMx_CCMR1):

Lees datablaaie 2: SPI op STM32; PWM, timers en onderbrekings op STM8
110: Eerste PWM-modus - wanneer van onder na bo getel word, is die eerste kanaal aktief solank TIMx_CNT < TIMx_CCR1. Andersins is die eerste kanaal onaktief. [verder in die dokument, foutiewe kopieer-plak vanaf timer 1] 111: Tweede PWM-modus - wanneer van onder na bo getel word, is die eerste kanaal onaktief totdat TIMx_CNT < TIMx_CCR1. Andersins is die eerste kanaal aktief.

Aangesien die LED's met katodes aan die MK gekoppel is, pas die tweede modus ons (die eerste een ook, maar ons weet dit nog nie).

Lees datablaaie 2: SPI op STM32; PWM, timers en onderbrekings op STM8
Bit 3 OC1PE: Aktiveer vooraflaai-uitset 1
0: Voorlaai register op TIMx_CCR1 gedeaktiveer. Jy kan enige tyd na TIMx_CCR1 skryf. Die nuwe waarde werk dadelik.
1: Voorlaai register op TIMx_CCR1 geaktiveer. Lees/skryf-bewerkings kry toegang tot die vooraflaairegister. Die voorafgelaaide waarde van TIMx_CCR1 word tydens elke opdateringsgebeurtenis in die skaduregister gelaai.
*Let wel: Voorlaairegisters moet geaktiveer wees vir PWM-modus om behoorlik te werk. Dit is opsioneel in enkelseinmodus (die OPM-bis word in die TIMx_CR1-register gestel).

Goed, skakel alles aan wat jy nodig het vir die drie kanale van die tweede 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 bestaan ​​uit twee agt-bis registers, alles is eenvoudig hier:

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

TIM2_ARRH = 0;
TIM2_ARRL = 255;

Die tweede timer kan net van onder na bo tel, belyning op die grens, niks hoef verander te word nie. Stel die frekwensiedeler, byvoorbeeld, op 256. Vir die tweede timer word die deler in die TIM2_PSCR-register gestel en is 'n krag van twee:

#define TIM2_PSCR  *(volatile uint8_t *)0x00530E

TIM2_PSCR = 8;

Dit bly om die gevolgtrekkings en die tweede timer self aan te skakel. Die eerste taak word deur registers opgelos Vang/Vergelyk Aktiveer: daar is twee van hulle, drie kanale is asimmetries oor hulle gestrooi. Hier kan ons ook leer dat dit moontlik is om die polariteit van die sein te verander, m.a.w. in beginsel kan PWM-modus 1 ook gebruik word. Ons skryf:

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

En uiteindelik begin ons die timer in die TIMx_CR1-register:

Lees datablaaie 2: SPI op STM32; PWM, timers en onderbrekings op STM8

#define TIM2_CR1   *(volatile uint8_t *)0x005300

TIM2_CR1 |= 1;

Kom ons skryf 'n eenvoudige analoog van AnalogWrite (), wat die werklike waardes sal deurgee aan die timer vir vergelyking. Die registers word voorspelbaar benoem Vang/Vergelyk registers, daar is twee van hulle vir elke kanaal: die lae 8 bisse in TIM2_CCRxL en die hoë bisse in TIM2_CCRxH. Aangesien ons 8-bis PWM begin het, is dit genoeg om slegs die lae bisse te skryf:

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

Die aandagtige leser sal opmerk dat ons 'n effens defekte PWM het, nie in staat is om 100% vulling uit te gee nie (teen 'n maksimum waarde van 255 word die sein met een timersiklus omgekeer). Vir LED's speel dit nie 'n rol nie, en die aandagtige leser raai reeds hoe om dit reg te stel.

PWM op die tweede timer werk, gaan na die eerste.

Die eerste timer het presies dieselfde stukkies in dieselfde registers (dis net dat daardie stukkies wat in die tweede timer "gereserveer" gebly het, aktief gebruik word vir allerhande gevorderde dinge in die eerste). Daarom is dit genoeg om die adresse van dieselfde registers in die datablad te vind en die kode te kopieer. Wel, verander die waarde van die frekwensieverdeler, want. die eerste timer wil nie 'n krag van twee kry nie, maar 'n presiese 16-bis waarde in twee registers Prescaler High и Laagte. Ons doen alles en ... die eerste timer werk nie. Wats fout?

Die enigste manier om die probleem op te los is deur na die hele afdeling oor die beheerregisters van timer 1 te kyk, waar ons een soek wat die tweede timer nie het nie. daar sal wees 17.7.30 Pouseregister (TIM1_BKR), waar daar 'n bietjie soos hierdie is:

Lees datablaaie 2: SPI op STM32; PWM, timers en onderbrekings op STM8
Aktiveer hoofuitvoer

#define TIM1_BKR   *(volatile uint8_t *)0x00526D

TIM1_BKR = (1<<7);

Dit is al vir nou, die kode daar.

Lees datablaaie 2: SPI op STM32; PWM, timers en onderbrekings op STM8

STM8 Multipleks

Multipleksing op STM8

Die derde mini-projek is om agt RGB-LED's aan die tweede timer in PWM-modus te koppel en hulle verskillende kleure te laat vertoon. Dit is gebaseer op die konsep van LED-multipleksing, wat bestaan ​​uit die feit dat as jy die LED's baie, baie vinnig aan- en afskakel, dit vir ons sal lyk asof hulle voortdurend aan is (volharding van visie, traagheid van visuele persepsie). Ek het eenkeer gedoen so iets op arduino.

Die algoritme van werk lyk soos volg:

  • verbind die anode van die eerste RGB LED;
  • het dit aangesteek en die nodige seine aan die katodes gegee;
  • gewag vir die einde van die PWM-siklus;
  • verbind die anode van die tweede RGB LED;
  • het dit aangesteek...

Wel, ens. Natuurlik, vir pragtige werk, is dit nodig dat die anodeverbinding en die "ontsteking" van die LED gelyktydig plaasvind. Wel, amper. In elk geval, ons moet 'n kode skryf wat waardes sal uitvoer in drie kanale van die tweede timer, dit sal verander wanneer UEV bereik word en terselfdertyd die tans aktiewe RGB LED verander.

Aangesien die LED-omskakeling outomaties is, moet jy "videogeheue" skep van waar die onderbrekingshanteerder data sal ontvang. Dit is 'n eenvoudige skikking:

uint8_t colors[8][3];

Om die kleur van 'n spesifieke LED te verander, sal dit genoeg wees om die nodige waardes in hierdie skikking te skryf. En die veranderlike sal verantwoordelik wees vir die nommer van die aktiewe LED

uint8_t cnt;

Demux

Vir behoorlike multipleksing benodig ons, vreemd genoeg, die CD74HC238 demultiplekser. Demultiplexer - 'n skyfie wat die operateur in hardeware implementeer <<. Deur drie invoerpennetjies (bis 0, 1 en 2) voer ons hom 'n drie-bis getal X, en in reaksie aktiveer hy die uitsetnommer (1<<X). Die oorblywende insette van die skyfie word gebruik om die hele ontwerp te skaal. Ons benodig hierdie skyfie nie net om die aantal besette penne van die mikrobeheerder te verminder nie, maar ook vir veiligheid - om nie per ongeluk meer LED's as moontlik aan te skakel en nie die MK te verbrand nie. Die skyfie kos 'n sent, dit moet altyd in die noodhulptassie by die huis gehou word.

CD74HC238 sal verantwoordelik wees vir die verskaffing van spanning aan die anode van die verlangde LED. In 'n volwaardige multipleks sal dit spanning aan die kolom verskaf deur die P-MOSFET, maar in hierdie demo kan jy dit direk doen, want. dit trek 20mA, volgens Absolute Maksimum Graderings in die datablad. Van datablad CD74HC238 ons benodig 'n pinout en hierdie cheat sheet:

Lees datablaaie 2: SPI op STM32; PWM, timers en onderbrekings op STM8
H = hoë spanning vlak, L = lae spanning vlak, X - gee nie om nie

Ons koppel E2 en E1 aan grond, E3, A0, A1 en A3 aan penne PD5, PC3, PC4 en PC5 van STM8. Aangesien die tabel hierbo beide lae en hoë vlakke bevat, stel ons hierdie penne as druk-trekpennetjies op.

PWM

PWM op die tweede timer is op dieselfde manier gekonfigureer as in die vorige storie, met twee verskille:

Eerstens moet ons die onderbreking aktiveer Dateer gebeurtenis op (UEV) wat 'n funksie sal oproep om die aktiewe LED te skakel. Dit word gedoen deur die bietjie te verander Opdateer Onderbreking Aktiveer in 'n register met 'n spreeknaam

Lees datablaaie 2: SPI op STM32; PWM, timers en onderbrekings op STM8
Onderbreek aktiveer register

#define TIM2_IER   *(volatile uint8_t *)0x005303

//enable interrupt
TIM2_IER = 1;

Die tweede verskil hou verband met so 'n verskynsel van multipleksing as Ghosting - parasitiese gloed van diodes. In ons geval kan dit voorkom as gevolg van die feit dat die timer, wat 'n onderbreking op die UEV veroorsaak het, aanhou tik, en die onderbrekingshanteerder het nie tyd om die LED te skakel voordat die timer iets na die uitsette begin skryf nie. Om dit te bekamp, ​​sal jy die logika moet omkeer (0 = maksimum helderheid, 255 = niks is aan nie) en nie uiterste dienssikluswaardes toelaat nie. Dié. verseker dat na UEV die LED's heeltemal geblus is vir een PWM-siklus.

Verander die polariteit:

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

Vermy om r, g en b op 255 te stel en onthou om hulle om te keer wanneer jy gebruik.

Onderbreek

Die essensie van die onderbreking is dat die skyfie onder sekere omstandighede ophou om die hoofprogram uit te voer en een of ander eksterne funksie oproep. Onderbrekings vind plaas as gevolg van eksterne of interne invloede, insluitend vanaf die timer.

Toe ons die eerste keer 'n projek in ST Visual Develop geskep het, afgesien van main.c ons het 'n venster met 'n geheimsinnige lêer gekry stm8_interrupt_vector.coutomaties by die projek ingesluit. In hierdie lêer is 'n funksie aan elke onderbreking geheg NonHandledInterrupt. Ons moet ons funksie aan die verlangde onderbreking bind.

Die datablad het 'n tabel van onderbrekingsvektore, waar ons die een vind wat ons benodig:

Lees datablaaie 2: SPI op STM32; PWM, timers en onderbrekings op STM8
13 TIM2-opdatering/oorloop
14 TIM2 Vasvang/Vergelyk

Ons moet die LED by UEV verander, so onderbreking #13 is nodig.

Gevolglik, eerstens, in die lêer stm8_interrupt_vector.c verander die naam van die funksie verantwoordelik vir onderbreking nommer 13 (IRQ13) by verstek na ons eie:

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

Tweedens sal ons 'n lêer moet skep main.h inhoud soos hierdie:

#ifndef __MAIN_H
#define __MAIN_H

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

En laastens, skryf hierdie funksie in jou 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;
}

Dit bly om onderbrekings moontlik te maak. Dit word gedoen met 'n samesteller-opdrag. rim - jy sal dit moet soek Programmeringshandleiding:

//enable interrupts
_asm("rim");

Nog 'n samesteller-instruksie - sim - Skakel onderbrekings af. Hulle moet gedeaktiveer word terwyl nuwe waardes na die "videogeheue" geskryf word sodat 'n onderbreking wat op 'n ongelukkige oomblik veroorsaak word, nie die skikking bederf nie.

Alle kode - op Github.

Lees datablaaie 2: SPI op STM32; PWM, timers en onderbrekings op STM8

As ten minste iemand wat hierdie artikel nuttig is, dan het ek dit nie verniet geskryf nie. Ek sal bly wees vir kommentaar en opmerkings, ek sal probeer om alles te beantwoord.

Bron: will.com

Voeg 'n opmerking