Branje podatkovnih listov 2: SPI na STM32; PWM, časovniki in prekinitve na STM8

Branje podatkovnih listov 2: SPI na STM32; PWM, časovniki in prekinitve na STM8

В 1. del Hobi inženirjem elektronike, ki so zrasli iz Arduino hlač, sem poskušal povedati, kako in zakaj naj berejo podatkovne liste in drugo dokumentacijo za mikrokrmilnike. Besedilo se je izkazalo za veliko, zato sem obljubil, da bom praktične primere prikazal v ločenem članku. No, sam sebe je imenoval mlečna goba ...

Danes vam bom pokazal, kako uporabiti podatkovne liste za reševanje precej preprostih, a potrebnih za številne projekte nalog na krmilnikih STM32 (Blue Pill) in STM8. Vsi demo projekti so posvečeni mojim najljubšim LED diodam, prižgali jih bomo v velikih količinah, za kar bomo morali uporabiti vse vrste zanimivih perifernih naprav.

Besedilo se je spet izkazalo za ogromno, zato zaradi udobja izdelujem vsebino:

STM32 Blue Pill: 16 LED z gonilnikom DM634
STM8: Nastavitev šestih pinov PWM
STM8: 8 RGB LED na treh pinih, prekinitve

Izjava o omejitvi odgovornosti: nisem inženir, ne pretvarjam se, da imam globoko znanje o elektroniki, članek je namenjen amaterjem, kot sem jaz. Pravzaprav sem se pred dvema letoma štel za ciljno publiko. Če bi mi takrat kdo rekel, da podatkovnih listov na neznanem čipu ni strašljivo brati, ne bi porabil veliko časa za iskanje kode na internetu in izumljanje bergel s škarjami in lepilnim trakom.

Ta članek se osredotoča na podatkovne liste, ne na projekte, zato koda morda ni zelo urejena in je pogosto utesnjena. Sami projekti so zelo preprosti, čeprav primerni za prvo seznanitev z novim čipom.

Upam, da bo moj članek pomagal komu na podobni stopnji potopitve v hobi.

STM32

16 LED z DM634 in SPI

Majhen projekt z uporabo Blue Pill (STM32F103C8T6) in gonilnika LED DM634. Z uporabo podatkovnih listov bomo ugotovili gonilnik, STM IO vrata in konfigurirali SPI.

DM634

Tajvanski čip s 16 16-bitnimi PWM izhodi, ki se lahko povezujejo v verige. Nizkocenovni 12-bitni model je znan iz domačega projekta Lightpack. Nekoč sem pri izbiri med DM63x in znanim TLC5940 izbral DM iz več razlogov: 1) TLC na Aliexpressu je zagotovo ponaredek, ta pa ni; 2) DM ima avtonomni PWM z lastnim frekvenčnim generatorjem; 3) v Moskvi ga je bilo mogoče kupiti poceni, namesto da bi čakali na pošiljko od Alija. In seveda se je bilo zanimivo naučiti, kako sami upravljati čip, namesto da bi uporabljali že pripravljeno knjižnico. Čipi so zdaj večinoma predstavljeni v paketu SSOP24, enostavno jih je spajkati na adapter.

Ker je proizvajalec tajvanski, podatkovni list čip je napisan v kitajski angleščini, kar pomeni, da bo zabavno. Najprej pogledamo pinout (Pin povezava), da razumete, na katero nogo kaj povezati, in opis zatičev (Opis pin). 16 nožic:

Branje podatkovnih listov 2: SPI na STM32; PWM, časovniki in prekinitve na STM8
Viri ponora enosmernega toka (odprt odtok)

Potopite / Izhod z odprtim odtokom – odtok; vir dotočnega toka; izhod je v aktivnem stanju povezan z maso - LED diode so s katodami povezane z gonilnikom. Električno gledano to seveda ni "odprt odtok" (odprt odtok), vendar v podatkovnih listih pogosto najdemo to oznako za zatiče v odvodnem načinu.

Branje podatkovnih listov 2: SPI na STM32; PWM, časovniki in prekinitve na STM8
Zunanji upori med REXT in GND za nastavitev vrednosti izhodnega toka

Referenčni upor je nameščen med zatičem REXT in maso, ki nadzoruje notranji upor izhodov, glejte graf na strani 9 podatkovnega lista. Pri modelu DM634 lahko ta upor nadzoruje tudi programska oprema, ki nastavi splošno svetlost (globalna svetlost); V tem članku se ne bom spuščal v podrobnosti, tukaj bom samo postavil upor 2.2 - 3 kOhm.

Da bi razumeli, kako upravljati čip, si oglejmo opis vmesnika naprave:

Branje podatkovnih listov 2: SPI na STM32; PWM, časovniki in prekinitve na STM8

Ja, tukaj je, kitajska angleščina v vsem svojem sijaju. Prevajanje tega je problematično, lahko ga razumete, če želite, vendar obstaja še en način - poglejte, kako je povezava s funkcionalno podobnim TLC5940 opisana v podatkovnem listu:

Branje podatkovnih listov 2: SPI na STM32; PWM, časovniki in prekinitve na STM8
... Za vnos podatkov v napravo so potrebni samo trije pini. Naraščajoči rob signala SCLK premakne podatke iz zatiča SIN v notranji register. Ko so vsi podatki naloženi, kratek visok signal XLAT zaklene zaporedno prenesene podatke v notranje registre. Notranji registri so vrata, ki jih sproži raven signala XLAT. Vsi podatki se najprej prenesejo z najpomembnejšim bitjem.

Zapah – zapah/zapah/ključavnica.
Dvižni rob – sprednji rob impulza
Najprej MSB – najpomembnejši (skrajno levi) bit naprej.
za podatke o uri – pošiljanje podatkov zaporedno (bit za bitom).

Beseda zapah se pogosto nahaja v dokumentaciji za čipe in se prevaja na različne načine, zato si bom zaradi razumevanja dovolil

majhen izobraževalni programGonilnik LED je v bistvu premikalni register. "Shift" (premik) v imenu - bitno premikanje podatkov znotraj naprave: vsak nov bit, potisnjen noter, potisne celotno verigo naprej pred seboj. Ker nihče ne želi opazovati kaotičnega utripanja LED diod med premikom, poteka proces v vmesnih registrih, ki so ločeni od delovnih registrov z dušilcem (zapah) je nekakšna čakalnica, kjer se deli razporedijo v želenem zaporedju. Ko je vse pripravljeno, se zaklop odpre in nastavki začnejo delovati ter nadomestijo prejšnjo serijo. Beseda zapah v dokumentaciji za mikrovezja skoraj vedno pomeni tak dušilec, ne glede na to, v kakšnih kombinacijah se uporablja.

Torej se prenos podatkov na DM634 izvaja takole: nastavite vhod DAI na vrednost najpomembnejšega bita oddaljene LED, povlecite DCK gor in dol; nastavite vhod DAI na vrednost naslednjega bita, povlecite DCK; in tako naprej, dokler niso preneseni vsi biti (vstavljen), nakar potegnemo LAT. To je mogoče storiti ročno (bit-bang), vendar je bolje uporabiti za to posebej prilagojen vmesnik SPI, saj je na našem STM32 predstavljen v dveh izvodih.

Modra tabletka STM32F103

Uvod: krmilniki STM32 so veliko bolj zapleteni kot Atmega328, kot se morda zdijo strašljivi. Poleg tega so zaradi varčevanja z energijo skoraj vse zunanje naprave na začetku izklopljene, frekvenca ure pa je 8 MHz iz notranjega vira. Na srečo so programerji STM napisali kodo, ki čip dvigne na "izračunanih" 72 MHz, in avtorji vseh IDE-jev, ki jih poznam, so jo vključili v postopek inicializacije, tako da nam ni treba meriti ure (ampak lahko, če res želiš). Vendar boste morali vklopiti periferijo.

Dokumentacija: Blue Pill je opremljen s priljubljenim čipom STM32F103C8T6, zanj obstajata dva uporabna dokumenta:

V podatkovnem listu nas lahko zanima:

  • Pinouts – chip pinouts – v primeru, da se odločimo za izdelavo plošč sami;
  • Memory Map – pomnilniški zemljevid za določen čip. Referenčni priročnik ima zemljevid za celotno linijo in omenja registre, ki jih naš nima.
  • Tabela z definicijami zatičev – seznam glavnih in alternativnih funkcij zatičev; za “modro tabletko” lahko na internetu najdete bolj priročne slike s seznamom žebljičkov in njihovih funkcij. Zato takoj poiščemo pinout Blue Pill in hranimo to sliko pri roki:

Branje podatkovnih listov 2: SPI na STM32; PWM, časovniki in prekinitve na STM8
Opomba: na sliki iz interneta je prišlo do napake, ki je bila zabeležena v komentarjih, hvala za to. Slika je bila zamenjana, vendar je to lekcija - bolje je preveriti informacije ne iz podatkovnih listov.

Odstranimo podatkovni list, odpremo Referenčni priročnik in od zdaj naprej uporabljamo samo njega.
Postopek: obravnavamo standardni vhod/izhod, konfiguriramo SPI, vklopimo potrebno periferijo.

Vhod Izhod

Na Atmega328 je I/O implementiran izjemno preprosto, zato je obilica možnosti STM32 lahko zmedena. Zdaj potrebujemo samo zaključke, a tudi ti imajo štiri možnosti:

Branje podatkovnih listov 2: SPI na STM32; PWM, časovniki in prekinitve na STM8
odprt odtok, push-pull, alternativni push-pull, alternativni odprti odtok

"Vleci-potisni" (potisni-potegni) je običajen izhod iz Arduina, lahko zatič sprejme vrednost HIGH ali LOW. Toda z "odprtim odtokom" obstajajo težave, čeprav je v resnici tukaj vse preprosto:

Branje podatkovnih listov 2: SPI na STM32; PWM, časovniki in prekinitve na STM8
Branje podatkovnih listov 2: SPI na STM32; PWM, časovniki in prekinitve na STM8
Konfiguracija izhoda / ko so vrata dodeljena izhodu: / izhodni medpomnilnik omogočen: / – način odprtega odtoka: »0« v izhodnem registru omogoči N-MOS, »1« v izhodnem registru pusti vrata v načinu Hi-Z ( P-MOS ni aktiviran ) / – način push-pull: “0” v izhodnem registru aktivira N-MOS, “1” v izhodnem registru aktivira P-MOS.

Vse razlike med odprtim odtokom (odprt odtok) iz "push-pull" (potisni-potegni) je v tem, da prvi pin ne more sprejeti stanja HIGH: pri zapisovanju enega v izhodni register gre v način visokega upora (visoka impedanca, Živjo Z). Pri pisanju ničle se zatič v obeh načinih obnaša enako, tako logično kot električno.

V običajnem izhodnem načinu pin preprosto oddaja vsebino izhodnega registra. V "alternativi" ga nadzirajo ustrezne zunanje naprave (glejte 9.1.4):

Branje podatkovnih listov 2: SPI na STM32; PWM, časovniki in prekinitve na STM8
Če je bit vrat konfiguriran kot nadomestni funkcijski zatič, je pin register onemogočen in zatič je priključen na periferni zatič.

Alternativna funkcionalnost vsakega zatiča je opisana v Definicije pinov Podatkovni list je na preneseni sliki. Na vprašanje, kaj storiti, če ima pin več alternativnih funkcij, je odgovor podana s sprotno opombo v podatkovnem listu:

Branje podatkovnih listov 2: SPI na STM32; PWM, časovniki in prekinitve na STM8
Če več zunanjih naprav uporablja isti zatič, je treba v izogib konfliktu med alternativnimi funkcijami hkrati uporabiti samo eno periferno enoto, preklapljano z bitom za omogočanje periferne ure (v ustreznem registru RCC).

Končno imajo zatiči v izhodnem načinu tudi takt. To je še ena funkcija za varčevanje z energijo; v našem primeru jo samo nastavimo na največ in pozabimo.

Torej: uporabljamo SPI, kar pomeni, da morata biti dva pina (s podatki in s taktnim signalom) "alternativna push-pull funkcija", drugi (LAT) pa "običajni push-pull". Toda preden jih dodelimo, se ukvarjajmo s SPI.

SPI

Še en majhen izobraževalni program

SPI ali Serial Peripheral Interface (serijski periferni vmesnik) je preprost in zelo učinkovit vmesnik za povezavo MK z drugimi MK in zunanjim svetom nasploh. Načelo njegovega delovanja je bilo že opisano zgoraj, kjer o kitajskem gonilniku LED (v referenčnem priročniku glejte razdelek 25). SPI lahko deluje v glavnem ("master") in podrejenem ("slave") načinu. SPI ima štiri osnovne kanale, od katerih ni mogoče uporabiti vseh:

  • MOSI, glavni izhod / podrejeni vhod: ta pin prenaša podatke v glavnem načinu in sprejema podatke v podrejenem načinu;
  • MISO, glavni vhod / podrejeni izhod: nasprotno, sprejema v glavnem in oddaja v podrejenem;
  • SCK, serijska ura: nastavi frekvenco prenosa podatkov v nadrejeni ali sprejme signal ure v podrejeni. V bistvu udarni ritmi;
  • SS, Slave Select: s pomočjo tega kanala suženj ve, da se od njega nekaj želi. Na STM32 se imenuje NSS, kjer je N = negativno, tj. krmilnik postane podrejeni, če je v tem kanalu ozemljitev. Dobro se kombinira z načinom Open Drain Output, vendar je to že druga zgodba.

Kot vse ostalo je tudi SPI na STM32 bogat s funkcionalnostjo, zaradi česar je nekoliko težko razumljiv. Na primer, lahko deluje ne samo s SPI, ampak tudi z vmesnikom I2S, v dokumentaciji pa so njihovi opisi mešani, presežek je treba pravočasno odrezati. Naša naloga je izjemno preprosta: podatke moramo samo poslati samo z uporabo MOSI in SCK. Pojdimo na razdelek 25.3.4 (poldupleksna komunikacija, poldupleksna komunikacija), kjer najdemo 1 ura in 1 enosmerna podatkovna žica (1 signal ure in 1 enosmerni tok podatkov):

Branje podatkovnih listov 2: SPI na STM32; PWM, časovniki in prekinitve na STM8
V tem načinu aplikacija uporablja SPI v načinu samo za prenos ali samo za sprejem. / Način samo za prenos je podoben dupleksnemu načinu: podatki se prenašajo na oddajnem zatiču (MOSI v glavnem načinu ali MISO v podrejenem načinu), sprejemni zatič (MISO ali MOSI) pa se lahko uporablja kot običajni V/I zatič . V tem primeru mora aplikacija samo prezreti medpomnilnik Rx (če je prebran, tam ne bo prenesenih podatkov).

Super, MISO pin je prost, povežimo LAT signal nanj. Poglejmo Slave Select, ki ga je na STM32 mogoče upravljati programsko, kar je izjemno priročno. Preberemo odstavek z istim imenom v razdelku 25.3.1 Splošni opis SPI:

Branje podatkovnih listov 2: SPI na STM32; PWM, časovniki in prekinitve na STM8
NSS (SSM = 1) / Informacije o izbiri podrejenega programa so vsebovane v bitu SSI registra SPI_CR1. Zunanji zatič NSS ostane prost za potrebe drugih aplikacij.

Čas je za vpis v registre. Odločil sem se za uporabo SPI2, poiskal njegov osnovni naslov v podatkovnem listu - v razdelku 3.3 Zemljevid pomnilnika:

Branje podatkovnih listov 2: SPI na STM32; PWM, časovniki in prekinitve na STM8

No, začnimo:

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

Odprite razdelek 25.3.3 s samoumevnim naslovom »Konfiguriranje SPI v glavnem načinu«:

Branje podatkovnih listov 2: SPI na STM32; PWM, časovniki in prekinitve na STM8

1. Nastavite frekvenco serijske ure z biti BR[2:0] v registru SPI_CR1.

Registri so zbrani v istoimenskem delu referenčnega priročnika. Premik naslova (Odmik naslova) za CR1 – 0x00, privzeto so vsi biti izbrisani (Ponastavi vrednost 0x0000):

Branje podatkovnih listov 2: SPI na STM32; PWM, časovniki in prekinitve na STM8

Biti BR nastavijo delilnik ure krmilnika in tako določijo frekvenco, pri kateri bo deloval SPI. Naša frekvenca STM32 bo 72 MHz, gonilnik LED v skladu s podatkovnim listom deluje s frekvenco do 25 MHz, zato moramo deliti s štiri (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. Nastavite bita CPOL in CPHA, da določite razmerje med prenosom podatkov in serijskim časom ure (glejte diagram na strani 240).

Ker tukaj beremo podatkovni list in ne gledamo shem, si poglejmo podrobneje besedilni opis bitov CPOL in CPHA na strani 704 (Splošni opis SPI):

Branje podatkovnih listov 2: SPI na STM32; PWM, časovniki in prekinitve na STM8
Faza in polarnost ure
Z uporabo bitov CPOL in CPHA registra SPI_CR1 lahko programsko izberete štiri časovna razmerja. Bit CPOL (polarnost ure) nadzoruje stanje signala ure, ko se podatki ne prenašajo. Ta bit nadzira glavni in podrejeni način. Če je CPOL ponastavljen, je pin SCK nizek v načinu mirovanja. Če je bit CPOL nastavljen, je pin SCK visok med načinom mirovanja.
Ko je bit CPHA (faza ure) nastavljen, je strob s prestrezanjem visokih bitov drugi rob signala SCK (pada, če je CPOL prazen, narašča, če je CPOL nastavljen). Podatki so zajeti z drugo spremembo signala ure. Če je bit CPHA prazen, je strob visokega bitnega trapa naraščajoči rob signala SCK (padajoči rob, če je CPOL nastavljen, naraščajoči rob, če je CPOL počiščen). Podatki se zajamejo ob prvi spremembi signala ure.

Ko absorbiramo to znanje, pridemo do zaključka, da morata oba bita ostati ničle, ker Želimo, da signal SCK ostane nizek, ko ni v uporabi, in da se podatki prenašajo na naraščajočem robu impulza (glej sliko XNUMX). Rising Edge v podatkovnem listu DM634).

Mimogrede, tukaj smo prvič naleteli na značilnost besedišča v podatkovnih listih ST: v njih je zapisan izraz "ponastavi bit na nič". da se malo ponastaviIn ne da malo razčistim, kot je na primer Atmega.

3. Nastavite bit DFF, da določite, ali je podatkovni blok 8-bitni ali 16-bitni format

Posebej sem vzel 16-bitni DM634, da se ne bi obremenjeval s prenosom 12-bitnih podatkov PWM, kot je DM633. DFF je smiselno nastaviti na eno:

#define DFF         0x0800

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

4. Konfigurirajte bit LSBFIRST v registru SPI_CR1, da določite format bloka

LSBFIRST, kot že ime pove, konfigurira prenos z najmanj pomembnim bitom najprej. Toda DM634 želi prejemati podatke od najpomembnejšega bita. Zato ga pustimo ponastaviti.

5. Če je v načinu strojne opreme potreben vhod iz nožice NSS, uporabite visok signal na nožici NSS med celotnim zaporedjem prenosa bajtov. V programskem načinu NSS nastavite bite SSM in SSI v registru SPI_CR1. Če naj se zatič NSS uporablja kot izhod, je treba nastaviti samo bit SSOE.

Namestite SSM in SSI, da pozabite na strojni način NSS:

#define SSI         0x0100
#define SSM         0x0200

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

6. Biti MSTR in SPE morajo biti nastavljeni (ostanejo nastavljeni le, če je signal NSS visok)

Pravzaprav s temi bitmi določimo naš SPI za glavnega in ga vklopimo:

#define MSTR        0x0004
#define SPE         0x0040

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

SPI je konfiguriran, takoj napišimo funkcije, ki gonilniku pošiljajo bajte. Nadaljujte z branjem 25.3.3 “Konfiguriranje SPI v glavnem načinu”:

Branje podatkovnih listov 2: SPI na STM32; PWM, časovniki in prekinitve na STM8
Nalog za prenos podatkov
Prenos se začne, ko se bajt zapiše v medpomnilnik Tx.
Podatkovni bajt se naloži v premični register pri vzporedno načinu (iz notranjega vodila) med prenosom prvega bita, po katerem se prenaša v zaporedno MOSI pin mode, prvi ali zadnji bit naprej, odvisno od nastavitve bita LSBFIRST v registru CPI_CR1. Zastavica TXE se nastavi po prenosu podatkov iz medpomnilnika Tx v premični register, in tudi generira prekinitev, če je nastavljen bit TXEIE v registru CPI_CR1.

V prevodu sem izpostavil nekaj besed, da bi opozoril na eno značilnost izvedbe SPI v krmilnikih STM. Na Atmega zastavica TXE (Tx Prazen, Tx je prazen in pripravljen za sprejem podatkov) se nastavi šele, ko je poslan celoten bajt navzven. In tukaj je ta zastavica nastavljena po tem, ko je bil bajt vstavljen v notranji premični register. Ker se tja potisne z vsemi biti hkrati (vzporedno), nato pa se podatki prenašajo zaporedno, se TXE nastavi, preden je bajt v celoti poslan. To je pomembno, ker v primeru našega gonilnika LED moramo po pošiljanju potegniti zatič LAT Vse podatkov, tj. Samo zastavica TXE nam ne bo zadostovala.

To pomeni, da potrebujemo še eno zastavo. Poglejmo 25.3.7 - »Statusne zastavice«:

Branje podatkovnih listov 2: SPI na STM32; PWM, časovniki in prekinitve na STM8
<…>
Branje podatkovnih listov 2: SPI na STM32; PWM, časovniki in prekinitve na STM8
Zastavica ZASEDEN
Zastavo BSY nastavi in ​​počisti strojna oprema (zapisovanje vanjo nima učinka). Zastavica BSY označuje stanje komunikacijske plasti SPI.
Ponastavi se:
ko je prenos končan (razen v glavnem načinu, če je prenos neprekinjen)
ko je SPI onemogočen
ko pride do napake glavnega načina (MODF=1)
Če prenos ni neprekinjen, se zastavica BSY počisti med vsakim prenosom podatkov

V redu, to bo prišlo prav. Ugotovimo, kje se nahaja medpomnilnik Tx. Če želite to narediti, preberite »Register podatkov SPI«:

Branje podatkovnih listov 2: SPI na STM32; PWM, časovniki in prekinitve na STM8
Bitov 15:0 DR[15:0] Register podatkov
Prejeti podatki ali podatki za prenos.
Podatkovni register je razdeljen na dva medpomnilnika - enega za pisanje (oddajni medpomnilnik) in enega za branje (sprejemni medpomnilnik). Pisanje v podatkovni register zapisuje v medpomnilnik Tx, branje iz podatkovnega registra pa bo vrnilo vrednost, ki jo vsebuje medpomnilnik Rx.

No, in statusni register, kjer sta zastavici TXE in BSY:

Branje podatkovnih listov 2: SPI na STM32; PWM, časovniki in prekinitve na STM8

Pišemo:

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

No, ker moramo prenesti 16 krat dva bajta, glede na število izhodov gonilnika LED, nekaj takega:

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

Vendar še ne vemo, kako potegniti zatič LAT, zato se bomo vrnili k V/I.

Dodeljevanje žebljičkov

V STM32F1 so registri, odgovorni za stanje zatičev, precej nenavadni. Jasno je, da jih je več kot Atmega, vendar so tudi drugačni od drugih čipov STM. Razdelek 9.1 Splošni opis GPIO:

Branje podatkovnih listov 2: SPI na STM32; PWM, časovniki in prekinitve na STM8
Vsaka V/I vrata za splošen namen (GPIO) ima dva 32-bitna konfiguracijska registra (GPIOx_CRL in GPIOx_CRH), dva 32-bitna podatkovna registra (GPIOx_IDR in GPIOx_ODR), 32-bitni register za nastavitev/ponastavitev (GPIOx_BSRR), 16-bitni register za ponastavitev (GPIOx_BRR) in 32-bitni register za ponastavitev register za blokiranje bitov (GPIOx_LCKR).

Prva dva registra sta nenavadna in tudi precej neprijetna, ker je 16 priključnih pinov razpršenih po njih v formatu "štirje biti na brata". Tisti. zatiči od nič do sedem so v CRL, ostali pa v CRH. Hkrati preostali registri uspešno vsebujejo bite vseh pinov vrat - pogosto ostanejo napol "rezervirani".

Za poenostavitev začnimo od konca seznama.

Ne potrebujemo registra za blokiranje.

Registra za nastavitev in ponastavitev sta precej smešna, saj se delno podvajata: vse lahko zapišete samo v BSRR, kjer bo višjih 16 bitov ponastavilo pin na nič, nižjih pa na 1, lahko pa tudi uporabite BRR, katerega spodnjih 16 bitov samo ponastavi pin. Všeč mi je druga možnost. Ti registri so pomembni, ker zagotavljajo atomski dostop do zatičev:

Branje podatkovnih listov 2: SPI na STM32; PWM, časovniki in prekinitve na STM8
Branje podatkovnih listov 2: SPI na STM32; PWM, časovniki in prekinitve na STM8
Atomska nastavitev ali ponastavitev
Pri programiranju GPIOx_ODR na bitni ravni ni treba onemogočiti prekinitev: enega ali več bitov je mogoče spremeniti z eno atomsko operacijo pisanja APB2. To dosežete tako, da v register za nastavitev/ponastavitev (GPIOx_BSRR ali samo za ponastavitev GPIOx_BRR) zapišete "1" bita, ki ga je treba spremeniti. Drugi deli bodo ostali nespremenjeni.

Podatkovni registri imajo precej samoumevna imena - IDR = vhod Register smeri, vhodni register; ODR = izhod Register smeri, izhodni register. V trenutnem projektu jih ne bomo potrebovali.

In končno, kontrolni registri. Ker nas zanimajo drugi zatiči SPI, in sicer PB13, PB14 in PB15, takoj pogledamo CRH:

Branje podatkovnih listov 2: SPI na STM32; PWM, časovniki in prekinitve na STM8

In vidimo, da bomo morali nekaj napisati v bitih od 20 do 31.

Zgoraj smo že ugotovili, kaj želimo od zatičev, zato bom tukaj brez posnetka zaslona, ​​rekel bom samo, da NAČIN določa smer (vnos, če sta oba bita nastavljena na 0) in hitrost zatiča (potrebujemo 50MHz, tj. oba zatiča na "1"), CNF pa nastavi način: običajni "push-pull" - 00, "alternativni" - 10. Privzeto, kot vidimo zgoraj, imajo vsi zatiči tretji bit od spodaj (CNF0), jih nastavi na način plavajoči vnos.

Ker nameravam s tem čipom narediti nekaj drugega, sem zaradi enostavnosti definiral vse možne vrednosti MODE in CNF za spodnji in zgornji kontrolni register.

Nekako tako

#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

Naši zatiči se nahajajo na vratih B (osnovni naslov – 0x40010C00), koda:

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

In v skladu s tem lahko napišete definicije za LAT, ki jih bodo trznili registri BRR in BSRR:

/*** 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 samo po inerciji, vedno je bilo tako, naj ostane)

Zdaj je vse super, a ne gre. Ker je to STM32, varčujejo z elektriko, kar pomeni, da morate omogočiti taktiranje zahtevanih zunanjih naprav.

Vklopite merjenje ure

Ura, znana tudi kot ura, je odgovorna za merjenje časa. In že smo lahko opazili kratico RCC. Iščemo ga v dokumentaciji: to je Reset and Clock Control.

Kot je bilo že rečeno zgoraj, so na srečo najtežji del clocking teme za nas opravili ljudje iz STM, za kar se jim najlepše zahvaljujemo (še enkrat dam povezavo do Di Haltova spletna stran, da bo jasno, kako zmedeno je). Potrebujemo le registre, ki so odgovorni za omogočanje perifernega takta (Peripheral Clock Enable Registers). Najprej poiščimo osnovni naslov RCC, je na samem začetku »pomnilniškega zemljevida«:

Branje podatkovnih listov 2: SPI na STM32; PWM, časovniki in prekinitve na STM8

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

Nato bodisi kliknite povezavo, kjer poskušate najti nekaj v plošči, ali, veliko bolje, pojdite skozi opise omogočenih registrov iz razdelkov o omogočiti registre. Kje bomo našli RCC_APB1ENR in RCC_APB2ENR:

Branje podatkovnih listov 2: SPI na STM32; PWM, časovniki in prekinitve na STM8
Branje podatkovnih listov 2: SPI na STM32; PWM, časovniki in prekinitve na STM8

In zato vsebujejo bite, ki vključujejo taktiranje SPI2, IOPB (V/I vrata B) in alternativne funkcije (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;

Končno kodo lahko najdete tukaj.

Če imate možnost in željo testirati, potem povežite DM634 takole: DAI na PB15, DCK na PB13, LAT na PB14. Voznik napajamo iz 5 voltov, ne pozabite priključiti ozemljitve.

Branje podatkovnih listov 2: SPI na STM32; PWM, časovniki in prekinitve na STM8

STM8 PWM

PWM na STM8

Ko sem ravno načrtoval ta članek, sem se odločil, da na primer poskusim obvladati nekatere funkcionalnosti neznanega čipa samo s podatkovnim listom, da ne bi končal pri čevljarju brez škornjev. STM8 je bil idealen za to vlogo: prvič, imel sem nekaj kitajskih plošč s STM8S103, in drugič, ni zelo priljubljen, zato skušnjava po branju in iskanju rešitve na internetu temelji na pomanjkanju teh rešitev.

Čip ima tudi podatkovni list и referenčni priročnik RM0016, v prvem so pinout in registrski naslovi, v drugem - vse ostalo. STM8 je programiran v C v groznem IDE ST Visual Develop.

Ura in I/O

STM8 privzeto deluje na frekvenci 2 MHz, to je treba takoj popraviti.

Branje podatkovnih listov 2: SPI na STM32; PWM, časovniki in prekinitve na STM8
HSI (Hitrohitrostna notranja ura).
Urni signal HSI izhaja iz notranjega 16 MHz RC oscilatorja s programabilnim delilnikom (1 do 8). Nastavljen je v registru delilnika ure (CLK_CKDIVR).
Opomba: na začetku je kot vodilni vir taktnega signala izbran HSI RC oscilator z delilnikom 8.

Najdemo naslov registra v podatkovnem listu, opis v refmanu in vidimo, da je treba register počistiti:

#define CLK_CKDIVR *(volatile uint8_t *)0x0050C6

CLK_CKDIVR &= ~(0x18);

Ker bomo zagnali PWM in povezali LED diode, poglejmo pinout:

Branje podatkovnih listov 2: SPI na STM32; PWM, časovniki in prekinitve na STM8

Čip je majhen, številne funkcije so obešene na iste zatiče. Kar je v oglatih oklepajih, je "alternativna funkcionalnost", preklopi se z "bajti možnosti" (opcijski bajti) – nekaj podobnega Atmega varovalkam. Njihove vrednosti lahko spremenite programsko, ni pa nujno, ker Nova funkcionalnost se aktivira šele po ponovnem zagonu. Lažje je uporabiti ST Visual Programmer (preneseno z Visual Develop), ki lahko spremeni te bajte. Pinout kaže, da sta zatiča CH1 in CH2 prvega časovnika skrita v oglatih oklepajih; v STVP je treba nastaviti bita AFR1 in AFR0, drugi pa bo tudi prenesel izhod CH1 drugega časovnika iz PD4 v PC5.

Tako bo 6 pinov krmililo LED diode: PC6, PC7 in PC3 za prvi časovnik, PC5, PD3 in PA3 za drugega.

Nastavitev samih I/O pinov na STM8 je preprostejša in bolj logična kot na STM32:

  • poznan iz registra smeri podatkov Atmega DDR (Register usmerjanja podatkov): 1 = izhod;
  • prvi krmilni register CR1, ko je izhod, nastavi potisni in vlečni način (1) ali odprti odtok (0); ker LED diode povezujem na čip s katodami, tukaj pustim ničle;
  • drugi kontrolni register CR2, ko je izhod, nastavi takt: 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

nastavitev PWM

Najprej opredelimo izraze:

  • Frekvenca PWM – frekvenca, s katero tiktaka časovnik;
  • Samodejno ponovno nalaganje, AR – samodejno naložena vrednost, do katere bo merilnik časa štel (perioda impulza);
  • Posodobitev dogodka, UEV – dogodek, ki se zgodi, ko je merilnik časa odštel do AR;
  • Delovni cikel PWM – delovni cikel PWM, pogosto imenovan »faktor obratovanja«;
  • Zajemi/primerjaj vrednost – vrednost za zajem/primerjavo, do katere je merilnik časa štel bo nekaj naredil (v primeru PWM obrne izhodni signal);
  • Vrednost prednalaganja – prednaložena vrednost. Primerjaj vrednost ne more spremeniti, medtem ko časovnik tiktaka, sicer se bo cikel PWM prekinil. Zato se nove prenesene vrednosti postavijo v medpomnilnik in izvlečejo, ko časovnik doseže konec odštevanja in se ponastavi;
  • Poravnano po robovih и Sredinsko poravnani načini – poravnava vzdolž obrobe in na sredini, enako kot pri Atmelu Hiter PWM и Fazno pravilen PWM.
  • OCiREF, referenčni signal za primerjavo izhoda – referenčni izhodni signal, pravzaprav tisto, kar se pojavi na ustreznem pinu v načinu PWM.

Kot je že razvidno iz pinout-a, imata dva časovnika PWM zmogljivosti - prvi in ​​​​drugi. Oba sta 16-bitna, prvi ima veliko dodatnih funkcij (predvsem zna šteti navzgor in navzdol). Oba morava delovati enako, zato sem se odločil, da začnem z očitno slabšim drugim, da slučajno ne uporabim česa, česar ni. Težava je v tem, da je opis funkcionalnosti PWM vseh časovnikov v referenčnem priročniku v poglavju o prvem časovniku (17.5.7 Način PWM), tako da morate ves čas skakati naprej in nazaj po dokumentu.

PWM na STM8 ima pomembno prednost pred PWM na Atmega:

Branje podatkovnih listov 2: SPI na STM32; PWM, časovniki in prekinitve na STM8
Boundary Aligned PWM
Konfiguracija računa od spodaj navzgor
Štetje od spodaj navzgor je aktivno, če je bit DIR v registru TIM_CR1 počiščen
Primer
Primer uporablja prvi način PWM. Referenčni signal PWM OCiREF je visok, dokler je TIM1_CNT < TIM1_CCRi. V nasprotnem primeru je potrebna nizka raven. Če je primerjalna vrednost v registru TIM1_CCRi večja od vrednosti samodejnega nalaganja (register TIM1_ARR), se signal OCiREF zadrži pri 1. Če je primerjalna vrednost 0, se OCiREF ohrani na nič....

Časovnik STM8 med dogodek posodobitve najprej preveri primerjajte vrednost, in šele nato proizvede referenčni signal. Atmegin časovnik se najprej pokvari in nato primerja, kar povzroči compare value == 0 rezultat je igla, s katero se je treba nekako spoprijeti (na primer s programskim obratom logike).

Torej, kaj želimo narediti: 8-bitni PWM (AR == 255), štetje od spodaj navzgor, poravnava vzdolž roba. Ker so žarnice povezane s čipom s katodami, mora PWM oddajati 0 (LED sveti), dokler primerjajte vrednost in 1 po.

O nekaterih smo že brali Način PWM, zato najdemo zahtevani register drugega časovnika z iskanjem v referenčnem priročniku za ta stavek (18.6.8 - TIMx_CCMR1):

Branje podatkovnih listov 2: SPI na STM32; PWM, časovniki in prekinitve na STM8
110: Prvi način PWM – pri štetju od spodaj navzgor je prvi kanal aktiven, medtem ko je TIMx_CNT < TIMx_CCR1. V nasprotnem primeru je prvi kanal neaktiven. [nadalje v dokumentu je napačna kopija-prilepi iz časovnika 1] 111: Drugi način PWM – pri štetju od spodaj navzgor je prvi kanal neaktiven, medtem ko je TIMx_CNT < TIMx_CCR1. V nasprotnem primeru je aktiven prvi kanal.

Ker so LED diode povezane s katodami na MK, nam ustreza drugi način (tudi prvi, a tega še ne vemo).

Branje podatkovnih listov 2: SPI na STM32; PWM, časovniki in prekinitve na STM8
Bit 3 OC1PE: Omogoči prednapetost nožice 1
0: prednaloženi register na TIMx_CCR1 je onemogočen. TIMx_CCR1 lahko pišete kadar koli. Nova vrednost deluje takoj.
1: Register prednalaganja na TIMx_CCR1 je omogočen. Operacije branja/pisanja dostopajo do registra prednalaganja. Vnaprej naložena vrednost TIMx_CCR1 se naloži v senčni register med vsakim dogodkom posodobitve.
*Opomba: Za pravilno delovanje načina PWM morajo biti omogočeni prednaloženi registri. V enosignalnem načinu to ni potrebno (bit OPM je nastavljen v registru TIMx_CR1).

V redu, vklopimo vse, kar potrebujemo za tri kanale drugega časovnika:

#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 je sestavljen iz dveh osembitnih registrov, vse je preprosto:

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

TIM2_ARRH = 0;
TIM2_ARRL = 255;

Drugi časovnik lahko šteje samo od spodaj navzgor, poravnava vzdolž meje, ničesar ni treba spreminjati. Nastavimo frekvenčni delilnik, na primer, na 256. Za drugi časovnik je delilnik nastavljen v registru TIM2_PSCR in je potenca dvojke:

#define TIM2_PSCR  *(volatile uint8_t *)0x00530E

TIM2_PSCR = 8;

Vse kar ostane je, da vklopite zaključke in sam drugi časovnik. Prvo težavo rešujejo registri Zajemi/primerjaj Omogoči: na njih so asimetrično razpršeni dva, trije kanali. Tu lahko tudi izvemo, da je mogoče spremeniti polarnost signala, t.j. načeloma je bilo mogoče uporabiti način PWM 1. Zapišemo:

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

In končno zaženemo časovnik v registru TIMx_CR1:

Branje podatkovnih listov 2: SPI na STM32; PWM, časovniki in prekinitve na STM8

#define TIM2_CR1   *(volatile uint8_t *)0x005300

TIM2_CR1 |= 1;

Napišimo preprost analog AnalogWrite(), ki bo dejanske vrednosti prenesel na časovnik za primerjavo. Registri so poimenovani predvidljivo Zajemi/primerjaj registre, sta za vsak kanal dva: 8 bitov nižjega reda v TIM2_CCRxL in višjih bitov v TIM2_CCRxH. Ker smo ustvarili 8-bitni PWM, je dovolj, da zapišemo samo najmanj pomembne bite:

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

Pozoren bralec bo opazil, da imamo rahlo pokvarjen PWM, ki ne more proizvesti 100% polnitve (pri največji vrednosti 255 se signal obrne za en cikel časovnika). Za LED to ni pomembno in pozoren bralec lahko že ugiba, kako to popraviti.

PWM na drugem časovniku deluje, pojdimo na prvo.

Prvi časovnik ima popolnoma enake bite v istih registrih (samo tisti biti, ki so ostali "rezervirani" v drugem časovniku, se v prvem aktivno uporabljajo za vse mogoče napredne stvari). Zato je dovolj, da poiščete naslove istih registrov v podatkovnem listu in kopirate kodo. No, spremenite vrednost frekvenčnega delilnika, ker ... prvi časovnik ne želi prejeti potence dvojke, temveč natančno 16-bitno vrednost v dveh registrih Prescaler High и nizka. Naredimo vse in ... prvi časovnik ne deluje. Kaj je narobe?

Težavo lahko rešimo le s pregledovanjem celotnega razdelka o krmilnih registrih časovnika 1, kjer iščemo tistega, ki ga drugi časovnik nima. Bo 17.7.30 Register prelomov (TIM1_BKR), kjer je ta bit:

Branje podatkovnih listov 2: SPI na STM32; PWM, časovniki in prekinitve na STM8
Omogoči glavni izhod

#define TIM1_BKR   *(volatile uint8_t *)0x00526D

TIM1_BKR = (1<<7);

To je zdaj gotovo vse, koda tam.

Branje podatkovnih listov 2: SPI na STM32; PWM, časovniki in prekinitve na STM8

STM8 Multiplex

Multipleksiranje na STM8

Tretji mini projekt je povezati osem RGB LED z drugim časovnikom v načinu PWM in omogočiti, da prikazujejo različne barve. Temelji na konceptu LED multipleksiranja, to je, da če LED diode prižgete in izklopite zelo, zelo hitro, se nam bo zdelo, da so nenehno prižgane (vztrajnost vida, inercija vizualne percepcije). Nekoč sem nekaj takega na Arduinu.

Delovni algoritem izgleda takole:

  • priključili anodo prve RGB LED;
  • prižgal in poslal potrebne signale katodam;
  • počakal do konca cikla PWM;
  • priključite anodo druge RGB LED;
  • prižgal...

No itd. Seveda je za lepo delovanje potrebno, da je anoda priključena in LED dioda hkrati "prižgana". No ali skoraj. V vsakem primeru moramo napisati kodo, ki bo izpisala vrednosti v treh kanalih drugega časovnika, jih spremenila, ko je dosežen UEV, in hkrati spremenila trenutno aktivno RGB LED.

Ker je preklapljanje LED samodejno, moramo ustvariti "video pomnilnik", iz katerega bo upravljalnik prekinitev sprejemal podatke. To je preprost niz:

uint8_t colors[8][3];

Če želite spremeniti barvo določene LED, bo dovolj, da v to matriko zapišete zahtevane vrednosti. In spremenljivka bo odgovorna za število aktivnih LED

uint8_t cnt;

Demux

Za pravilno multipleksiranje potrebujemo, nenavadno, demultiplekser CD74HC238. Demultiplekser - čip, ki izvaja operaterja v strojni opremi <<. Preko treh vhodnih pinov (biti 0, 1 in 2) mu dovajamo tribitno število X, v odgovor pa aktivira izhodno število (1<<X). Preostali vhodi čipa se uporabljajo za skaliranje celotne zasnove. Ta čip potrebujemo ne le za zmanjšanje števila zasedenih pinov mikrokontrolerja, ampak tudi za varnost - da ne bi pomotoma vklopili več LED diod, kot je mogoče, in ne bi zažgali MK. Čip stane peni in ga morate vedno imeti v domači omarici z zdravili.

Naš CD74HC238 bo odgovoren za napajanje anode želene LED. V polnem multipleksu bi napajal napetost stolpcu prek P-MOSFET-a, v tej predstavitvi pa je to mogoče neposredno, ker glede na to porabi 20 mA absolutne maksimalne ocene v podatkovnem listu. Od Podatkovni list CD74HC238 potrebujemo pinouts in to goljufijo:

Branje podatkovnih listov 2: SPI na STM32; PWM, časovniki in prekinitve na STM8
H = visoka napetost, L = nizka napetost, X – vseeno

E2 in E1 priključimo na maso, E3, A0, A1 in A3 na nožice PD5, PC3, PC4 in PC5 STM8. Ker zgornja tabela vsebuje nizke in visoke ravni, te zatiče konfiguriramo kot potisne in vlečne zatiče.

PWM

PWM na drugem časovniku je konfiguriran na enak način kot v prejšnji zgodbi, z dvema razlikama:

Najprej moramo omogočiti vklop prekinitve Posodobi dogodek (UEV), ki bo poklical funkcijo, ki preklopi aktivno LED. To se naredi s spremembo nastavka Omogoči prekinitev posodobitve v register z zgovornim imenom

Branje podatkovnih listov 2: SPI na STM32; PWM, časovniki in prekinitve na STM8
Register omogoči prekinitev

#define TIM2_IER   *(volatile uint8_t *)0x005303

//enable interrupt
TIM2_IER = 1;

Druga razlika je povezana s pojavom multipleksiranja, kot je npr ghosting – parazitski sij diod. V našem primeru se lahko pojavi zaradi dejstva, da časovnik, ki je povzročil prekinitev na UEV, še naprej tiktaka in upravljavec prekinitev nima časa za preklop LED, preden časovnik začne nekaj pisati na nožice. Za boj proti temu boste morali obrniti logiko (0 = največja svetlost, 255 = nič ne sveti) in se izogibati ekstremnim vrednostim delovnega cikla. Tisti. zagotovite, da po UEV LED diode popolnoma ugasnejo za en cikel PWM.

Spreminjanje polarnosti:

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

Izogibajte se nastavljanju r, g in b na 255 in ne pozabite jih obrniti, ko jih uporabljate.

Prekinitve

Bistvo prekinitve je, da v določenih okoliščinah čip preneha izvajati glavni program in pokliče neko zunanjo funkcijo. Do prekinitev pride zaradi zunanjih ali notranjih vplivov, vključno s časovnikom.

Ko smo prvič ustvarili projekt v ST Visual Develop, poleg tega main.c prejeli smo okno s skrivnostno datoteko stm8_interrupt_vector.c, samodejno vključen v projekt. V tej datoteki je vsaki prekinitvi dodeljena funkcija NonHandledInterrupt. Našo funkcijo moramo vezati na želeno prekinitev.

Podatkovni list ima tabelo vektorjev prekinitev, kjer najdemo tiste, ki jih potrebujemo:

Branje podatkovnih listov 2: SPI na STM32; PWM, časovniki in prekinitve na STM8
13 Posodobitev/prelivanje TIM2
14 Zajem/primerjava TIM2

Zamenjati moramo LED na UEV, zato potrebujemo prekinitev #13.

V skladu s tem, prvič, v datoteki stm8_interrupt_vector.c spremenite privzeto ime funkcije, odgovorne za prekinitev št. 13 (IRQ13), na svoje:

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

Drugič, ustvariti bomo morali datoteko main.h z naslednjo vsebino:

#ifndef __MAIN_H
#define __MAIN_H

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

In končno, zapišite to funkcijo v svoj 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;
}

Vse kar ostane je omogočiti prekinitve. To se naredi z ukazom asemblerja rim – poiskati ga boste morali v Priročnik za programiranje:

//enable interrupts
_asm("rim");

Drug ukaz asemblerja je sim – izklopi prekinitve. Med zapisovanjem novih vrednosti v "video pomnilnik" jih je treba izklopiti, tako da prekinitev, ki je nastala v napačnem trenutku, ne pokvari niza.

Vsa koda - na GitHubu.

Branje podatkovnih listov 2: SPI na STM32; PWM, časovniki in prekinitve na STM8

Če se vsaj komu zdi ta članek koristen, potem ga nisem napisal zaman. Vesela bom komentarjev in pripomb, na vse bom poskušala odgovoriti.

Vir: www.habr.com

Dodaj komentar