Lue datasheets 2: SPI on STM32; PWM, ajastimet ja keskeytykset STM8:ssa

Lue datasheets 2: SPI on STM32; PWM, ajastimet ja keskeytykset STM8:ssa

В ensimmäinen osa Yritin kertoa Arduino-housuista kasvaneille harrastuselektroniikkainsinööreille, kuinka ja miksi heidän pitäisi lukea mikrokontrollerien datalomakkeita ja muuta dokumentaatiota. Teksti osoittautui suureksi, joten lupasin näyttää käytännön esimerkkejä erillisessä artikkelissa. Hän kutsui itseään maitosieneksi...

Tänään näytän sinulle, kuinka voit käyttää tietolehtiä ratkaistaksesi melko yksinkertaisia, mutta välttämättömiä projekteja, tehtäviä STM32 (Blue Pill) ja STM8-ohjaimilla. Kaikki demoprojektit on omistettu suosikkiledeilleni, sytytämme niitä suuria määriä, joihin joudumme käyttämään kaikenlaisia ​​mielenkiintoisia oheislaitteita.

Tekstistä tuli jälleen valtava, joten teen mukavuuden vuoksi sisällön:

STM32 Blue Pill: 16 LEDiä DM634-ohjaimella
STM8: Kuusi PWM-nastaa asetetaan
STM8: 8 RGB-LEDiä kolmessa nastassa, keskeytykset

Vastuuvapauslauseke: En ole insinööri, en väitä omaavani syvää tietoa elektroniikasta, artikkeli on tarkoitettu kaltaisilleni amatööreille. Itse asiassa pidin itseäni kaksi vuotta sitten kohdeyleisönä. Jos joku olisi silloin kertonut minulle, että tuntemattomalla sirulla olevat tietosivut eivät olleet pelottavia lukea, en olisi käyttänyt paljon aikaa etsiessäni joitain koodinpätkiä Internetistä ja keksien kainalosauvoja saksilla ja teipillä.

Tämän artikkelin painopiste on tietolomakkeissa, ei projekteissa, joten koodi ei välttämättä ole kovin siisti ja usein ahdas. Itse projektit ovat hyvin yksinkertaisia, vaikkakin sopivat ensitutustumiseen uuteen siruun.

Toivon, että artikkelini auttaa jotakuta, joka on samankaltaisessa harrastuksen uppoutumisvaiheessa.

STM32

16 LEDiä DM634:llä ja SPI:llä

Pieni projekti, jossa käytetään Blue Pill (STM32F103C8T6) ja DM634 LED-ohjainta. Tietosivujen avulla selvitämme ohjaimen, STM IO -portit ja konfiguroimme SPI:n.

DM634

Taiwanilainen siru 16 16-bittisellä PWM-ulostulolla, voidaan kytkeä ketjuihin. Halpa 12-bittinen malli tunnetaan kotimaisesta projektista Lightpack. Aikoinaan, kun valitsin DM63x:n ja tunnetun TLC5940:n välillä, valitsin DM:n useista syistä: 1) Aliexpressin TLC on ehdottomasti väärennös, mutta tämä ei ole; 2) DM:llä on autonominen PWM omalla taajuusgeneraattorillaan; 3) sen voi ostaa edullisesti Moskovassa sen sijaan, että odottaisi pakettia Alilta. Ja tietysti oli mielenkiintoista oppia ohjaamaan sirua itse valmiin kirjaston käyttämisen sijaan. Sirut ovat nyt pääasiassa SSOP24-paketissa, ne on helppo juottaa sovittimeen.

Koska valmistaja on taiwanilainen, Tiedotteen siru on kirjoitettu kiinaksi englanniksi, mikä tarkoittaa, että siitä tulee hauskaa. Ensin katsomme pinoutia (Pin-yhteys) ymmärtääksesi, mihin jalkaan mihin liittää, ja kuvauksen tapeista (Nastan kuvaus). 16 nastaa:

Lue datasheets 2: SPI on STM32; PWM, ajastimet ja keskeytykset STM8:ssa
Tasavirtaaltaan lähteet (avoin viemäri)

Pesuallas / Avoin tyhjennyslähtö - viemäri; sisäänvirtaavan virran lähde; lähtö, aktiivisessa tilassa, kytketty maahan - LEDit on kytketty ohjaimeen katodeilla. Sähköisesti tämä ei tietenkään ole "avoin viemäri" (avoin viemäri), mutta teknisissä taulukoissa tämä nimitys nastojen tyhjennystilassa löytyy usein.

Lue datasheets 2: SPI on STM32; PWM, ajastimet ja keskeytykset STM8:ssa
Ulkoiset vastukset REXT:n ja GND:n välillä lähtövirran arvon asettamiseksi

REXT-nastan ja maan väliin on asennettu referenssivastus, joka ohjaa lähtöjen sisäistä resistanssia, katso datalehden sivulla 9 oleva kaavio. DM634:ssä tätä vastusta voidaan ohjata myös ohjelmistolla asettamalla kokonaiskirkkaus (globaali kirkkaus); En mene yksityiskohtiin tässä artikkelissa, laitan tähän vain 2.2 - 3 kOhm vastuksen.

Ymmärtääksesi kuinka ohjata sirua, katsotaanpa laitteen käyttöliittymän kuvausta:

Lue datasheets 2: SPI on STM32; PWM, ajastimet ja keskeytykset STM8:ssa

Joo, tässä se on, kiinalainen englanti kaikessa loistossaan. Tämän kääntäminen on ongelmallista, ymmärrät sen halutessasi, mutta on toinenkin tapa - katso kuinka yhteys toiminnallisesti samanlaiseen TLC5940:een on kuvattu lomakkeessa:

Lue datasheets 2: SPI on STM32; PWM, ajastimet ja keskeytykset STM8:ssa
... Vain kolme nastaa tarvitaan tietojen syöttämiseen laitteeseen. SCLK-signaalin nouseva reuna siirtää tiedot SIN-nastasta sisäiseen rekisteriin. Kun kaikki tiedot on ladattu, lyhyt korkea XLAT-signaali lukitsee peräkkäin siirretyt tiedot sisäisiin rekistereihin. Sisäiset rekisterit ovat portteja, jotka laukaisee XLAT-signaalitaso. Kaikki data lähetetään merkitsevin bitti ensin.

Salpa – salpa/salpa/lukko.
Nouseva reuna – pulssin etureuna
MSB ensin – tärkein (vasemmanpuoleinen) bitti eteenpäin.
kellon dataan – lähettää tietoja peräkkäin (bitti bitiltä).

Sana salpa löytyy usein sirujen dokumentaatiosta ja se on käännetty eri tavoin, joten ymmärryksen vuoksi sallin itselleni

pieni koulutusohjelmaLED-ohjain on pohjimmiltaan siirtorekisteri. "Vaihto" (siirtää) nimessä - bittikohtainen tiedonsiirto laitteen sisällä: jokainen sisään työnnetty uusi bitti työntää koko ketjua eteensä. Koska kukaan ei halua havaita LEDien kaoottista vilkkumista työvuoron aikana, prosessi tapahtuu puskurirekistereissä, jotka on erotettu työrekistereistä vaimentimella (salpa) on eräänlainen odotushuone, jossa bitit on järjestetty haluttuun järjestykseen. Kun kaikki on valmis, suljin aukeaa ja terät lähtevät töihin korvaamalla edellisen erän. Sana salpa mikropiirien dokumentaatiossa tarkoittaa melkein aina tällaista vaimentinta riippumatta siitä, missä yhdistelmissä sitä käytetään.

Joten tiedonsiirto DM634:ään tapahtuu näin: aseta DAI-tuloksi kauko-LED:n merkittävimmän bitin arvo, vedä DCK ylös ja alas; aseta DAI-tulo seuraavan bitin arvoon, vedä DCK; ja niin edelleen, kunnes kaikki bitit on lähetetty (kellotettu sisään), jonka jälkeen vedämme LAT. Tämä voidaan tehdä manuaalisesti (bit- bang), mutta on parempi käyttää erityisesti tähän räätälöityä SPI-liitäntää, koska se on esitetty STM32:ssamme kahtena kappaleena.

Sininen pilleri STM32F103

Johdanto: STM32-ohjaimet ovat paljon monimutkaisempia kuin Atmega328 kuin ne saattavat tuntua pelottavilta. Lisäksi energiansäästösyistä lähes kaikki oheislaitteet sammutetaan käynnistyksessä ja kellotaajuus on 8 MHz sisäisestä lähteestä. Onneksi STM-ohjelmoijat kirjoittivat koodin, joka nostaa sirun "laskettuun" 72 MHz:iin, ja kaikkien tuntemieni IDE:iden tekijät sisällyttivät sen alustusmenettelyyn, joten meidän ei tarvitse kelloa (mutta voit jos todella haluat). Mutta sinun on kytkettävä oheislaitteet päälle.

Dokumentaatio: Blue Pill on varustettu suositulla STM32F103C8T6-sirulla, sille on kaksi hyödyllistä asiakirjaa:

Tietolomakkeessa saatamme olla kiinnostuneita:

  • Pinouts – chip pinouts – jos päätämme tehdä laudat itse;
  • Memory Map – muistikartta tietylle sirulle. Viitekäsikirjassa on kartta koko linjalle, ja siinä mainitaan rekisterit, joita meillä ei ole.
  • Pin Definitions -taulukko – listaa nastojen pää- ja vaihtoehtoiset toiminnot; "sinistä pilleriä" varten löydät Internetistä kätevämpiä kuvia, joissa on luettelo pinneistä ja niiden toiminnoista. Siksi googletamme välittömästi Blue Pill pinoutin ja pidämme tämän kuvan käden ulottuvilla:

Lue datasheets 2: SPI on STM32; PWM, ajastimet ja keskeytykset STM8:ssa
HUOM: kuvassa oli virhe Internetistä, joka mainittiin kommenteissa, kiitos siitä. Kuva on korvattu, mutta tämä on oppitunti - on parempi tarkistaa tiedot ei tiedoista.

Poistamme tietolomakkeen, avaamme Reference Manual -oppaan ja tästä lähtien käytämme vain sitä.
Toimenpide: käsittelemme standardituloa/lähtöä, konfiguroimme SPI:n, kytkemme päälle tarvittavat oheislaitteet.

Input Output

Atmega328:ssa I/O on toteutettu erittäin yksinkertaisesti, minkä vuoksi STM32-vaihtoehtojen runsaus voi olla hämmentävää. Nyt tarvitsemme vain johtopäätöksiä, mutta näilläkin on neljä vaihtoehtoa:

Lue datasheets 2: SPI on STM32; PWM, ajastimet ja keskeytykset STM8:ssa
avoin viemäri, push-pull, vaihtoehtoinen työntö-veto, vaihtoehtoinen avoin viemäri

"Vedä Työnnä" (työnnä vedä) on tavallinen Arduinon lähtö, nasta voi saada arvon joko HIGH tai LOW. Mutta "avoimella viemärillä" niitä on vaikeuksia, vaikka itse asiassa kaikki on yksinkertaista täällä:

Lue datasheets 2: SPI on STM32; PWM, ajastimet ja keskeytykset STM8:ssa
Lue datasheets 2: SPI on STM32; PWM, ajastimet ja keskeytykset STM8:ssa
Lähtökonfiguraatio / kun portti on määritetty lähtöön: / lähtöpuskuri käytössä: / – avoin tyhjennystila: "0" lähtörekisterissä ottaa käyttöön N-MOS:n, "1" lähtörekisterissä jättää portin Hi-Z-tilaan ( P-MOS ei ole aktivoitu ) / – push-pull-tila: “0” lähtörekisterissä aktivoi N-MOS:n, “1” lähtörekisterissä aktivoi P-MOS:n.

Kaikki ero avoimen viemärin välillä (avoin viemäri) sanasta "push-pull" (työnnä vedä) on se, että ensimmäisessä nastassa ei voi hyväksyä HIGH-tilaa: kun kirjoitat sellaisen lähtörekisteriin, se menee korkean resistanssin tilaan (korkean impedanssin, Hei-Z). Nollaa kirjoitettaessa nasta toimii samalla tavalla molemmissa tiloissa, sekä loogisesti että sähköisesti.

Normaalissa lähtötilassa nasta yksinkertaisesti lähettää lähtörekisterin sisällön. "Vaihtoehtona" sitä ohjaavat vastaavat oheislaitteet (katso 9.1.4):

Lue datasheets 2: SPI on STM32; PWM, ajastimet ja keskeytykset STM8:ssa
Jos porttibitti on määritetty vaihtoehtoisen toiminnon nastaksi, nastarekisteri poistetaan käytöstä ja nasta on kytketty oheisnastan kanssa.

Jokaisen nastan vaihtoehtoiset toiminnot kuvataan kohdassa Pin-määritelmät Tietolomake on ladatussa kuvassa. Kysymykseen, mitä tehdä, jos nastalla on useita vaihtoehtoisia toimintoja, vastaus annetaan tietolomakkeessa olevalla alaviitteellä:

Lue datasheets 2: SPI on STM32; PWM, ajastimet ja keskeytykset STM8:ssa
Jos useat oheislaitteet käyttävät samaa nastaa, vaihtoehtoisten toimintojen välisten ristiriitojen välttämiseksi tulisi käyttää vain yhtä oheislaitetta kerrallaan, vaihdettaessa oheislaitteen kellon aktivointibitillä (asianmukaisessa RCC-rekisterissä).

Lopuksi lähtötilassa olevilla nastoilla on myös kellonopeus. Tämä on toinen energiansäästöominaisuus; meidän tapauksessamme asetamme sen vain maksimiin ja unohdamme sen.

Joten: käytämme SPI:tä, mikä tarkoittaa, että kahden nastan (datalla ja kellosignaalilla) tulisi olla "vaihtoehtoinen push-pull-toiminto" ja toisen (LAT) pitäisi olla "tavallinen push-pull". Mutta ennen kuin annat ne, käsitellään SPI:tä.

SPI

Toinen pieni koulutusohjelma

SPI tai Serial Peripheral Interface (serial peripheral interface) on yksinkertainen ja erittäin tehokas käyttöliittymä MK:n yhdistämiseen muihin MK:ihin ja ulkomaailmaan yleensä. Sen toimintaperiaate on jo kuvattu edellä, missä puhutaan kiinalaisesta LED-ohjaimesta (viitekäsikirjassa, katso kohta 25). SPI voi toimia isäntä ("master") ja slave ("orja") -tilassa. SPI:llä on neljä peruskanavaa, joista kaikkia ei välttämättä käytetä:

  • MOSI, Master Output / Slave Input: tämä nasta lähettää tietoja master-tilassa ja vastaanottaa tietoja orjatilassa;
  • MISO, Master Input / Slave Output: päinvastoin, se vastaanottaa isännässä ja lähettää orjassa;
  • SCK, Serial Clock: asettaa tiedonsiirron taajuuden isännässä tai vastaanottaa kellosignaalin orjalaitteessa. Pohjimmiltaan lyömällä lyöntejä;
  • SS, Slave Select: tämän kanavan avulla orja tietää, että häneltä halutaan jotain. STM32:ssa sitä kutsutaan NSS:ksi, missä N = negatiivinen, ts. säätimestä tulee orja, jos tässä kanavassa on maadoitus. Se yhdistyy hyvin Open Drain Output -tilan kanssa, mutta se on toinen tarina.

Kuten kaikki muukin, STM32:n SPI on runsaasti toimintoja, mikä tekee siitä jonkin verran vaikea ymmärtää. Esimerkiksi se voi toimia paitsi SPI:n, myös I2S-rajapinnan kanssa, ja dokumentaatiossa niiden kuvaukset ovat sekoittuneet, on tarpeen leikata ylimääräinen ajoissa. Tehtävämme on äärimmäisen yksinkertainen: meidän tarvitsee vain lähettää tietoja vain MOSI:n ja SCK:n avulla. Siirrymme kohtaan 25.3.4 (half-duplex-viestintä, half-duplex-viestintä), josta löydämme 1 kello ja 1 yksisuuntainen datajohto (1 kellosignaali ja 1 yksisuuntainen tietovirta):

Lue datasheets 2: SPI on STM32; PWM, ajastimet ja keskeytykset STM8:ssa
Tässä tilassa sovellus käyttää SPI:tä joko vain lähetys- tai vastaanottotilassa. / Vain lähetys -tila on samanlainen kuin duplex-tila: tiedot lähetetään lähetysnastalla (MOSI master-tilassa tai MISO orjatilassa), ja vastaanottonastaa (MISO tai MOSI vastaavasti) voidaan käyttää tavallisena I/O-nastana. . Tässä tapauksessa sovelluksen tarvitsee vain jättää huomiotta Rx-puskuri (jos se luetaan, sinne ei siirretä dataa).

Hienoa, MISO-nasta on vapaa, liitetään LAT-signaali siihen. Katsotaanpa Slave Selectiä, jota STM32:ssa voidaan ohjata ohjelmallisesti, mikä on erittäin kätevää. Luemme samannimisen kappaleen osiosta 25.3.1 SPI:n yleiskuvaus:

Lue datasheets 2: SPI on STM32; PWM, ajastimet ja keskeytykset STM8:ssa
Ohjelmistoohjaus NSS (SSM = 1) / Slave-valintatiedot sisältyvät SPI_CR1-rekisterin SSI-bittiin. Ulkoinen NSS-nasta on vapaa muita sovellustarpeita varten.

On aika kirjoittaa rekisteriin. Päätin käyttää SPI2:ta ja etsiä sen perusosoitetta tietolomakkeesta - osiosta 3.3 Muistikartta:

Lue datasheets 2: SPI on STM32; PWM, ajastimet ja keskeytykset STM8:ssa

No, aloitetaan:

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

Avaa osio 25.3.3 itsestään selvällä otsikolla "SPI:n määrittäminen isäntätilassa":

Lue datasheets 2: SPI on STM32; PWM, ajastimet ja keskeytykset STM8:ssa

1. Aseta sarjakellotaajuus bitteillä BR[2:0] SPI_CR1-rekisterissä.

Rekisterit on koottu samannimiseen käsikirjaosaan. Osoitteen muutos (Osoitteen siirtymä) CR1 – 0x00, oletusarvoisesti kaikki bitit tyhjennetään (Nollaa arvo 0x0000):

Lue datasheets 2: SPI on STM32; PWM, ajastimet ja keskeytykset STM8:ssa

BR-bitit asettavat ohjaimen kellonjakajan, mikä määrittää taajuuden, jolla SPI toimii. STM32-taajuutemme tulee olemaan 72 MHz, LED-ajuri toimii teknisten tietojensa mukaan jopa 25 MHz taajuudella, joten joudumme jakamaan neljällä (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. Aseta CPOL- ja CPHA-bitit määrittämään tiedonsiirron ja sarjakellon ajoituksen välinen suhde (katso kaavio sivulla 240).

Koska luemme tässä taulukkoa emmekä katso kaavioita, katsotaanpa tarkemmin CPOL- ja CPHA-bittien tekstikuvausta sivulla 704 (SPI:n yleinen kuvaus):

Lue datasheets 2: SPI on STM32; PWM, ajastimet ja keskeytykset STM8:ssa
Kellon vaihe ja napaisuus
Käyttämällä SPI_CR1-rekisterin CPOL- ja CPHA-bittejä voit valita ohjelmallisesti neljä ajoitussuhdetta. CPOL (clock polarity) -bitti ohjaa kellosignaalin tilaa, kun dataa ei lähetetä. Tämä bitti ohjaa isäntä- ja orjatilaa. Jos CPOL nollataan, SCK-nasta on alhainen lepotilassa. Jos CPOL-bitti on asetettu, SCK-nasta on korkea lepotilan aikana.
Kun CPHA (kellovaihe) -bitti on asetettu, high bit trap strobo on SCK-signaalin toinen reuna (laskee, jos CPOL on tyhjä, nousee, jos CPOL on asetettu). Data vangitaan kellosignaalin toisella muutoksella. Jos CPHA-bitti on tyhjä, korkean bittiloukun välähdys on SCK-signaalin nouseva reuna (laskeva reuna, jos CPOL on asetettu, nouseva reuna, jos CPOL on tyhjennetty). Tiedot kerätään ensimmäisellä kellosignaalin muutoksella.

Otettuamme tämän tiedon, tulemme siihen johtopäätökseen, että molempien bittien on pysyttävä nollina, koska Haluamme, että SCK-signaali pysyy alhaisena, kun sitä ei käytetä, ja dataa siirretään pulssin nousevalla reunalla (katso kuva. Nouseva reuna DM634-tietolomakkeessa).

Muuten, täällä kohtasimme ensimmäisen kerran sanaston ominaisuuden ST-tietolomakkeissa: niissä on kirjoitettu lause "reset the bit to zero". nollata hiemanEikä siivota vähän, kuten esimerkiksi Atmega.

3. Aseta DFF-bitti määrittääksesi, onko datalohko 8- vai 16-bittinen

Otin nimenomaan 16-bittisen DM634:n, jotta en vaivaudu lähettämään 12-bittistä PWM-dataa, kuten DM633. On järkevää asettaa DFF yhdeksi:

#define DFF         0x0800

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

4. Määritä SPI_CR1-rekisterin LSBFIRST-bitti lohkomuodon määrittämiseksi

LSBFIRST, kuten sen nimi kertoo, määrittää lähetyksen vähiten merkitsevällä bitillä ensin. Mutta DM634 haluaa vastaanottaa dataa merkittävimmästä bitistä alkaen. Siksi jätämme sen nollatuksi.

5. Jos laitteistotilassa tarvitaan tulo NSS-nastasta, anna korkea signaali NSS-nastalle koko tavunsiirtojakson ajan. Aseta NSS-ohjelmistotilassa SSM- ja SSI-bitit SPI_CR1-rekisteriin. Jos NSS-nastaa käytetään lähtönä, vain SSOE-bitti on asetettava.

Asenna SSM ja SSI unohtaaksesi NSS-laitteistotilan:

#define SSI         0x0100
#define SSM         0x0200

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

6. MSTR- ja SPE-bitit on asetettava (ne pysyvät asetettuina vain, jos NSS-signaali on korkea)

Itse asiassa näillä biteillä määritämme SPI:n isännäksi ja kytkemme sen päälle:

#define MSTR        0x0004
#define SPE         0x0040

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

SPI on konfiguroitu, kirjoitetaan heti funktioita, jotka lähettävät tavuja ajuriin. Jatka lukemista 25.3.3 "SPI:n määrittäminen master-tilassa":

Lue datasheets 2: SPI on STM32; PWM, ajastimet ja keskeytykset STM8:ssa
Tiedonsiirtotilaus
Lähetys alkaa, kun tavu kirjoitetaan Tx-puskuriin.
Datatavu ladataan siirtorekisteriin klo rinnakkain -tilassa (sisäiseltä väylältä) ensimmäisen bitin lähetyksen aikana, jonka jälkeen se lähetetään peräkkäinen MOSI pin mode, ensimmäinen tai viimeinen bitti eteenpäin riippuen LSBFIRST-bitin asetuksesta CPI_CR1-rekisterissä. TXE-lippu asetetaan tiedonsiirron jälkeen Tx-puskurista siirtorekisteriinja luo myös keskeytyksen, jos TXEIE-bitti CPI_CR1-rekisterissä on asetettu.

Korostin käännöksessä muutaman sanan kiinnittääkseni huomion yhteen SPI-toteutuksen ominaisuuteen STM-ohjaimissa. Atmegassa TXE-lippu (Tx tyhjä, Tx on tyhjä ja valmis vastaanottamaan tietoja) asetetaan vasta, kun koko tavu on lähetetty ulos. Ja tässä tämä lippu asetetaan sen jälkeen, kun tavu on lisätty sisäiseen siirtorekisteriin. Koska se työnnetään sinne kaikkien bittien kanssa samanaikaisesti (rinnakkain) ja sitten data siirretään peräkkäin, TXE asetetaan ennen kuin tavu on lähetetty kokonaan. Tämä on tärkeää, koska LED-ohjaimen tapauksessa meidän on vedettävä LAT-nasta lähettämisen jälkeen Kaikki dataa, ts. Pelkkä TXE-lippu ei riitä meille.

Tämä tarkoittaa, että tarvitsemme toisen lipun. Katsotaanpa kohtaa 25.3.7 - "Tilaliput":

Lue datasheets 2: SPI on STM32; PWM, ajastimet ja keskeytykset STM8:ssa
<…>
Lue datasheets 2: SPI on STM32; PWM, ajastimet ja keskeytykset STM8:ssa
VARATTU lippu
BSY-lippu asetetaan ja tyhjennetään laitteistolla (siihen kirjoittamisella ei ole vaikutusta). BSY-lippu ilmaisee SPI-viestintäkerroksen tilan.
Se nollaa:
kun siirto on valmis (paitsi master-tilassa, jos siirto on jatkuvaa)
kun SPI on poistettu käytöstä
kun päätilan virhe tapahtuu (MODF=1)
Jos siirto ei ole jatkuvaa, BSY-lippu tyhjennetään jokaisen tiedonsiirron välillä

Okei, tämä tulee tarpeeseen. Selvitetään missä Tx-puskuri sijaitsee. Voit tehdä tämän lukemalla "SPI-tietorekisteri":

Lue datasheets 2: SPI on STM32; PWM, ajastimet ja keskeytykset STM8:ssa
Bittiä 15:0 DR[15:0] Tietorekisteri
Vastaanotettu tai siirrettävä data.
Tietorekisteri on jaettu kahteen puskuriin - yksi kirjoittamista varten (lähetyspuskuri) ja toinen lukemista varten (vastaanottopuskuri). Tietorekisteriin kirjoittaminen kirjoittaa Tx-puskuriin ja datarekisteristä lukeminen palauttaa Rx-puskurissa olevan arvon.

No, ja tilarekisteri, josta löytyvät TXE- ja BSY-liput:

Lue datasheets 2: SPI on STM32; PWM, ajastimet ja keskeytykset STM8:ssa

Me kirjoitamme:

#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, koska meidän on lähetettävä 16 kertaa kaksi tavua LED-ohjainlähtöjen lukumäärän mukaan, jotain tällaista:

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

Mutta emme vielä tiedä, miten LAT-nasta vedetään, joten palaamme I/O:han.

Pinssien määrittäminen

STM32F1:ssä nastojen tilasta vastaavat rekisterit ovat melko epätavallisia. On selvää, että niitä on enemmän kuin Atmega, mutta ne eroavat myös muista STM-siruista. Osio 9.1 GPIO:n yleinen kuvaus:

Lue datasheets 2: SPI on STM32; PWM, ajastimet ja keskeytykset STM8:ssa
Jokainen yleiskäyttöinen I/O-portti (GPIO) siinä on kaksi 32-bittistä konfigurointirekisteriä (GPIOx_CRL ja GPIOx_CRH), kaksi 32-bittistä datarekisteriä (GPIOx_IDR ja GPIOx_ODR), 32-bittinen asetus/nollausrekisteri (GPIOx_BSRR), 16-bittinen palautusrekisteri (GPIOx_BRR) ja 32-bittinen. bitin estorekisteri (GPIOx_LCKR).

Kaksi ensimmäistä rekisteriä ovat epätavallisia ja myös melko hankalia, koska 16 portin nastaa on hajallaan niiden poikki "neljä bittiä veljeä kohti" -muodossa. Nuo. nastat nollasta seitsemään ovat CRL:ssä ja loput ovat CRH:ssa. Samanaikaisesti jäljellä olevat rekisterit sisältävät onnistuneesti portin kaikkien nastojen bitit - usein jäljellä puoliksi "varattuja".

Aloitetaan yksinkertaisuuden vuoksi luettelon lopusta.

Emme tarvitse estorekisteriä.

Asetus- ja nollausrekisterit ovat varsin hauskoja siinä mielessä, että ne osittain kopioivat toisiaan: kaiken voi kirjoittaa vain BSRR:ään, jossa korkeammat 16 bittiä nollaavat pinin ja alemmat 1, tai voit myös käytä BRR:tä, jonka alemmat 16 bittiä nollaa vain nastan . Pidän toisesta vaihtoehdosta. Nämä rekisterit ovat tärkeitä, koska ne tarjoavat atomin pääsyn nastoihin:

Lue datasheets 2: SPI on STM32; PWM, ajastimet ja keskeytykset STM8:ssa
Lue datasheets 2: SPI on STM32; PWM, ajastimet ja keskeytykset STM8:ssa
Atomic Set tai Reset
Keskeytyksiä ei tarvitse poistaa käytöstä ohjelmoitaessa GPIOx_ODR:ää bittitasolla: yksi tai useampi bitti voidaan vaihtaa yhdellä atomikirjoitusoperaatiolla APB2. Tämä saavutetaan kirjoittamalla "1" muutettavan bitin asetus-/palautusrekisteriin (GPIOx_BSRR tai vain nollauksen yhteydessä GPIOx_BRR). Muut bitit pysyvät ennallaan.

Tietorekistereillä on varsin itsestään selvät nimet - IDR = panos Suuntarekisteri, syöttörekisteri; ODR = ulostulo Suuntarekisteri, lähtörekisteri. Emme tarvitse niitä nykyisessä projektissa.

Ja lopuksi ohjausrekisterit. Koska olemme kiinnostuneita toisista SPI-nastoista, nimittäin PB13, PB14 ja PB15, katsomme heti CRH:ta:

Lue datasheets 2: SPI on STM32; PWM, ajastimet ja keskeytykset STM8:ssa

Ja näemme, että meidän on kirjoitettava jotain bitteinä 20 - 31.

Yllä olemme jo selvittäneet, mitä haluamme nastoista, joten tässä teen ilman kuvakaappausta, sanon vain, että MODE määrittää suunnan (syöte, jos molemmat bitit on asetettu 0: ksi) ja pin nopeuden (tarvitsemme 50MHz, ts. molemmat nastat arvoon "1"), ja CNF asettaa tilan: tavallinen "push-pull" - 00, "alternative" - ​​10. Oletusarvoisesti, kuten yllä näemme, kaikissa nastoissa on kolmas bitti alhaalta (CNF0), se asettaa ne tilaan kelluva syöttö.

Koska aion tehdä jotain muuta tällä sirulla, olen yksinkertaisuuden vuoksi määritellyt kaikki mahdolliset MODE- ja CNF-arvot sekä alemmalle että ylemmälle ohjausrekisterille.

Jotenkin näin

#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

Pinnamme sijaitsevat portissa B (perusosoite – 0x40010C00), koodi:

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

Ja vastaavasti voit kirjoittaa määritelmiä LAT:lle, jota BRR- ja BSRR-rekisterit nykivät:

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

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

(LAT_matala vain hitaudesta, se on aina ollut niin, anna sen jäädä)

Nyt kaikki on hienoa, mutta se ei toimi. Koska tämä on STM32, ne säästävät sähköä, mikä tarkoittaa, että sinun on otettava käyttöön tarvittavien oheislaitteiden kellotus.

Ota kellot käyttöön

Kello, joka tunnetaan myös nimellä Clock, on vastuussa kellosta. Ja voimme jo huomata lyhenteen RCC. Etsimme sitä dokumentaatiosta: tämä on Reset and Clock Control.

Kuten ylempänä todettiin, onneksi vaikeimman osan kellotusaiheesta tekivät meille STM:n ihmiset, mistä kiitämme paljon (jälleen kerran annan linkin Di Haltin verkkosivuilla, jotta kävisi selväksi, kuinka sekava se on). Tarvitsemme vain rekistereitä, jotka ovat vastuussa oheislaitteiden kellon mahdollistamisesta (Peripheral Clock Enable Registers). Etsitään ensin RCC:n perusosoite, se on aivan "Memory Map":n alussa:

Lue datasheets 2: SPI on STM32; PWM, ajastimet ja keskeytykset STM8:ssa

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

Ja sitten joko napsauta linkkiä, josta yrität löytää jotain levystä, tai, paljon parempi, käy läpi aktivointirekisterien kuvaukset osioista ottaa rekisterit käyttöön. Mistä löydämme RCC_APB1ENR ja RCC_APB2ENR:

Lue datasheets 2: SPI on STM32; PWM, ajastimet ja keskeytykset STM8:ssa
Lue datasheets 2: SPI on STM32; PWM, ajastimet ja keskeytykset STM8:ssa

Ja vastaavasti ne sisältävät bittejä, jotka sisältävät SPI2:n, IOPB:n (I/O-portti B) ja vaihtoehtoisten toimintojen (AFIO) kellotuksen.

#define _APB2ENR 0x18
#define _APB1ENR 0x1C

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

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

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

Lopullinen koodi löytyy täällä.

Jos sinulla on mahdollisuus ja halu testata, yhdistä DM634 näin: DAI PB15:een, DCK PB13:een, LAT PB14:ään. Annamme ohjaimen virran 5 voltista, älä unohda kytkeä maadoitukset.

Lue datasheets 2: SPI on STM32; PWM, ajastimet ja keskeytykset STM8:ssa

STM8 PWM

PWM STM8:ssa

Kun olin juuri suunnittelemassa tätä artikkelia, päätin esimerkkinä yrittää hallita tuntemattoman sirun toimintoja pelkän tietolomakkeen avulla, jotta en joutuisi suutarin kanssa ilman saappaita. STM8 oli ihanteellinen tähän rooliin: ensinnäkin minulla oli pari kiinalaista levyä STM8S103:lla, ja toiseksi se ei ole kovin suosittu, ja siksi houkutus lukea ja löytää ratkaisu Internetistä perustuu juuri näiden ratkaisujen puutteeseen.

Sirulla on myös Tiedotteen и viiteopas RM0016, ensimmäisessä on pinout- ja rekisteriosoitteet, toisessa - kaikki muu. STM8 on ohjelmoitu C-kielellä kauheassa IDE:ssä ST Visual Develop.

Kello ja I/O

Oletusarvoisesti STM8 toimii 2 MHz taajuudella, tämä on korjattava välittömästi.

Lue datasheets 2: SPI on STM32; PWM, ajastimet ja keskeytykset STM8:ssa
HSI (High Speed ​​​​Internal) -kello
HSI-kellosignaali johdetaan sisäisestä 16 MHz:n RC-oskillaattorista, jossa on ohjelmoitava jakaja (1 - 8). Se asetetaan kellonjakajarekisteriin (CLK_CKDIVR).
Huomautus: alussa HSI RC-oskillaattori, jonka jakaja on 8, valitaan kellosignaalin johtavaksi lähteeksi.

Löydämme rekisterin osoitteen tietolomakkeesta, kuvauksen refmanista ja näemme, että rekisteri on tyhjennettävä:

#define CLK_CKDIVR *(volatile uint8_t *)0x0050C6

CLK_CKDIVR &= ~(0x18);

Koska aiomme käyttää PWM:ää ja kytkeä LEDit, katsotaanpa liitäntää:

Lue datasheets 2: SPI on STM32; PWM, ajastimet ja keskeytykset STM8:ssa

Siru on pieni, monet toiminnot on ripustettu samoihin nastoihin. Hakasulkeissa oleva on "vaihtoehtoinen toiminnallisuus", se vaihdetaan "optiotavuilla" (vaihtoehto tavua) – jotain Atmega-sulakkeiden kaltaista. Voit muuttaa niiden arvoja ohjelmallisesti, mutta se ei ole välttämätöntä, koska Uusi toiminto aktivoituu vasta uudelleenkäynnistyksen jälkeen. On helpompi käyttää ST Visual Programmeria (ladattuna Visual Developin kanssa), joka voi muuttaa näitä tavuja. Pinout näyttää, että ensimmäisen ajastimen CH1- ja CH2-nastat on piilotettu hakasulkeisiin; STVP:ssä on tarpeen asettaa AFR1- ja AFR0-bitit, ja toinen siirtää myös toisen ajastimen CH1-lähdön PD4:stä PC5:een.

Siten 6 nastaa ohjaa LEDejä: PC6, PC7 ja PC3 ensimmäistä ajastinta varten, PC5, PD3 ja PA3 toista.

Itse I/O-nastojen asettaminen STM8:ssa on yksinkertaisempaa ja loogisempaa kuin STM32:ssa:

  • tuttu Atmega DDR -tietosuuntarekisteristä (Tietojen suuntarekisteri): 1 = lähtö;
  • ensimmäinen ohjausrekisteri CR1 asettaa ulostulona push-pull-tilan (1) tai avoimen nielun (0); koska yhdistän LEDit siruun katodeilla, jätän nollia tähän;
  • toinen ohjausrekisteri CR2 asettaa ulostulona kellonopeuden: 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-asetus

Ensin määritellään termit:

  • PWM-taajuus – taajuus, jolla ajastin tikittää;
  • Automaattinen uudelleenlataus, AR – automaattisesti ladattava arvo, johon asti ajastin laskee (pulssijakso);
  • Päivitystapahtuma, UEV – tapahtuma, joka tapahtuu, kun ajastin on laskenut AR:hen;
  • PWM-käyttöjakso – PWM-käyttösuhde, jota usein kutsutaan "käyttötekijäksi";
  • Kaappaa/vertaa arvoa – sieppauksen/vertailun arvo, johon ajastin on laskenut tekee jotain (PWM:n tapauksessa se invertoi lähtösignaalin);
  • Esilatausarvo – esiladattu arvo. Vertaa arvoa ei voi muuttua, kun ajastin tikittää, muuten PWM-sykli katkeaa. Siksi uudet lähetetyt arvot sijoitetaan puskuriin ja vedetään ulos, kun ajastin saavuttaa lähtölaskennan lopun ja nollataan;
  • Reunatasoitettu и Keskikohdistetut tilat – kohdistus reunaa pitkin ja keskellä, sama kuin Atmelin Nopea PWM и Vaiheen oikea PWM.
  • OCiREF, lähtövertailuviitesignaali – referenssilähtösignaali, itse asiassa se, mikä näkyy vastaavassa nastassa PWM-tilassa.

Kuten pinoutista jo käy ilmi, kahdella ajastimella on PWM-ominaisuudet - ensimmäisessä ja toisessa. Molemmat ovat 16-bittisiä, ensimmäisessä on paljon lisäominaisuuksia (erityisesti se voi laskea sekä ylös- että alaspäin). Meidän on molempien toimittava tasapuolisesti, joten päätin aloittaa selvästi köyhemmästä toisesta, jotta en vahingossa käytä jotain, mitä siellä ei ole. Ongelmana on, että kaikkien ajastimien PWM-toimintojen kuvaus viiteoppaassa on ensimmäistä ajastinta käsittelevässä luvussa (17.5.7 PWM-tila), joten joudut koko ajan hyppäämään edestakaisin läpi asiakirjan.

STM8:n PWM:llä on tärkeä etu Atmegan PWM:ään verrattuna:

Lue datasheets 2: SPI on STM32; PWM, ajastimet ja keskeytykset STM8:ssa
Rajoitettu PWM
Tilin määritykset alhaalta ylös
Alhaalta ylös -laskenta on aktiivinen, jos TIM_CR1-rekisterin DIR-bitti tyhjennetään
Esimerkki
Esimerkki käyttää ensimmäistä PWM-tilaa. PWM-referenssisignaali OCiREF pidetään korkeana niin kauan kuin TIM1_CNT < TIM1_CCRI. Muuten se vie matalan tason. Jos vertailuarvo TIM1_CCRI-rekisterissä on suurempi kuin automaattilatausarvo (TIM1_ARR-rekisteri), OCiREF-signaali pidetään arvossa 1. Jos vertailuarvo on 0, OCiREF pidetään nollassa....

STM8 ajastin aikana päivitystapahtuma tarkistaa ensin vertailla arvoa, ja tuottaa vasta sitten referenssisignaalin. Atmegan ajastin ensin kiertyy ja sitten vertaa, mikä johtaa compare value == 0 ulostulo on neula, jota on käsiteltävä jotenkin (esimerkiksi kääntämällä logiikka ohjelmallisesti käänteiseksi).

Joten mitä haluamme tehdä: 8-bittinen PWM (AR == 255), laskettuna alhaalta ylöspäin, kohdistus reunaa pitkin. Koska hehkulamput on kytketty siruun katodeilla, PWM:n pitäisi tuottaa 0 (LED päällä), kunnes vertailla arvoa ja 1 jälkeen.

Olemme jo lukeneet joistakin PWM-tila, joten löydämme tarvittavan rekisterin toisesta ajastimesta etsimällä viiteoppaasta tätä lausetta (18.6.8 - TIMx_CCMR1):

Lue datasheets 2: SPI on STM32; PWM, ajastimet ja keskeytykset STM8:ssa
110: Ensimmäinen PWM-tila – alhaalta ylös laskettaessa ensimmäinen kanava on aktiivinen, kun TIMx_CNT < TIMx_CCR1. Muussa tapauksessa ensimmäinen kanava ei ole aktiivinen. [asiakirjassa on virheellinen kopioi-liitä ajastimesta 1] 111: Toinen PWM-tila – alhaalta ylös laskettaessa ensimmäinen kanava on passiivinen, kun TIMx_CNT < TIMx_CCR1. Muussa tapauksessa ensimmäinen kanava on aktiivinen.

Koska LEDit on kytketty MK:hen katodeilla, sopii meille toinen tila (myös ensimmäinen, mutta sitä emme vielä tiedä).

Lue datasheets 2: SPI on STM32; PWM, ajastimet ja keskeytykset STM8:ssa
Bitti 3 OC1PE: Ota käyttöön nastan 1 esilataus
0: Esilatausrekisteri TIMx_CCR1:ssä on poistettu käytöstä. Voit kirjoittaa TIMx_CCR1:een milloin tahansa. Uusi arvo toimii heti.
1: Esilatausrekisteri TIMx_CCR1:ssä on käytössä. Luku/kirjoitustoiminnot käyttävät esilatausrekisteriä. Esiladattu arvo TIMx_CCR1 ladataan varjorekisteriin jokaisen päivitystapahtuman aikana.
*Huomaa: Jotta PWM-tila toimisi oikein, esilatausrekisterit on otettava käyttöön. Tämä ei ole tarpeen yksisignaalitilassa (OPM-bitti asetetaan TIMx_CR1-rekisteriin).

Okei, laitetaan päälle kaikki, mitä tarvitsemme toisen ajastimen kolmelle kanavalle:

#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 koostuu kahdesta kahdeksanbittisestä rekisteristä, kaikki on yksinkertaista:

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

TIM2_ARRH = 0;
TIM2_ARRL = 255;

Toinen ajastin voi laskea vain alhaalta ylös, kohdistus reunaa pitkin, mitään ei tarvitse muuttaa. Asetetaan esimerkiksi taajuusjakajaksi 256. Toiselle ajastimelle jakaja asetetaan TIM2_PSCR-rekisteriin ja se on kahden potenssi:

#define TIM2_PSCR  *(volatile uint8_t *)0x00530E

TIM2_PSCR = 8;

Jäljelle jää vain johtopäätösten ja itse toisen ajastimen kytkeminen päälle. Ensimmäinen ongelma ratkaistaan ​​rekistereillä Kaappaa/Vertaa Enable: niiden poikki on kaksi, kolme kanavaa hajallaan epäsymmetrisesti. Tästä voimme myös oppia, että signaalin napaisuutta on mahdollista muuttaa, ts. periaatteessa oli mahdollista käyttää PWM-moodia 1. Kirjoitamme:

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

Ja lopuksi käynnistämme ajastimen TIMx_CR1-rekisterissä:

Lue datasheets 2: SPI on STM32; PWM, ajastimet ja keskeytykset STM8:ssa

#define TIM2_CR1   *(volatile uint8_t *)0x005300

TIM2_CR1 |= 1;

Kirjoitetaan yksinkertainen analogi AnalogWrite(), joka siirtää todelliset arvot ajastimeen vertailua varten. Rekisterit on nimetty ennakoivasti Kaappaa/vertaa rekistereitä, niitä on kaksi kullekin kanavalle: matalan asteen 8 bittiä TIM2_CCRxL:ssä ja korkean kertaluvun bittiä TIM2_CCRxH:ssa. Koska olemme luoneet 8-bittisen PWM:n, riittää, että kirjoitat vain vähiten merkitsevät bitit:

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

Huomaavainen lukija huomaa, että meillä on hieman viallinen PWM, joka ei pysty tuottamaan 100 % täyttöä (maksimiarvolla 255 signaali käännetään yhdeksi ajastinjaksoksi). LEDeille tällä ei ole väliä, ja tarkkaavainen lukija voi jo arvata, kuinka se korjataan.

Toisen ajastimen PWM toimii, siirrytään ensimmäiseen.

Ensimmäisessä ajastimessa on täsmälleen samat bitit samoissa rekistereissä (se on vain niin, että niitä bittejä, jotka jäivät "varatuiksi" toisessa ajastimessa, käytetään aktiivisesti ensimmäisessä ajastimessa kaikenlaisiin edistyneisiin asioihin). Siksi riittää, että etsit samojen rekisterien osoitteet tietolomakkeesta ja kopioit koodin. Muuta taajuudenjakajan arvoa, koska... ensimmäinen ajastin ei halua vastaanottaa kahden potenssia, vaan tarkan 16-bittisen arvon kahdessa rekisterissä Esiskaalaaja korkea и Matala. Teemme kaiken ja... ensimmäinen ajastin ei toimi. Mikä hätänä?

Ongelma voidaan ratkaista vain katsomalla läpi koko ajastimen 1 ohjausrekistereitä käsittelevä osio, josta etsimme sitä, jota toisella ajastimella ei ole. Siellä on 17.7.30 Katkorekisteri (TIM1_BKR), missä on tämä osa:

Lue datasheets 2: SPI on STM32; PWM, ajastimet ja keskeytykset STM8:ssa
Ota päälähtö käyttöön

#define TIM1_BKR   *(volatile uint8_t *)0x00526D

TIM1_BKR = (1<<7);

Se on nyt varma, koodi samassa paikassa.

Lue datasheets 2: SPI on STM32; PWM, ajastimet ja keskeytykset STM8:ssa

STM8 Multiplex

Multipleksointi STM8:lla

Kolmas miniprojekti on yhdistää kahdeksan RGB-LEDiä toiseen ajastimeen PWM-tilassa ja saada ne näyttämään eri värejä. Se perustuu LED-multipleksoinnin konseptiin, joka on, että jos kytket LEDit päälle ja pois päältä hyvin, hyvin nopeasti, meistä näyttää siltä, ​​​​että ne ovat jatkuvasti päällä (näön pysyvyys, visuaalisen havainnon inertia). Tein kerran jotain tällaista Arduinossa.

Työalgoritmi näyttää tältä:

  • kytketty ensimmäisen RGB-LEDin anodi;
  • sytytti sen lähettämällä tarvittavat signaalit katodille;
  • odotti PWM-jakson loppuun;
  • kytketty toisen RGB-LEDin anodi;
  • sytytti sen...

No jne. Tietysti kauniiseen toimintaan tarvitaan, että anodi on kytketty ja LED "sytytetään" samanaikaisesti. No, tai melkein. Joka tapauksessa meidän on kirjoitettava koodi, joka tulostaa arvot toisen ajastimen kolmelle kanavalle, muuttaa niitä, kun UEV saavutetaan, ja samalla muuttaa tällä hetkellä aktiivista RGB-LEDiä.

Koska LED-vaihto on automaattinen, meidän on luotava "videomuisti", josta keskeytyskäsittelijä vastaanottaa tietoja. Tämä on yksinkertainen taulukko:

uint8_t colors[8][3];

Tietyn LEDin värin muuttamiseksi riittää, että kirjoitat tarvittavat arvot tähän ryhmään. Ja muuttuja on vastuussa aktiivisen LED-valon numerosta

uint8_t cnt;

Demux

Oikeaa multipleksointia varten tarvitsemme, kummallista kyllä, CD74HC238-demultiplekserin. Demultiplekseri - siru, joka toteuttaa operaattorin laitteistossa <<. Kolmen tulonastan (bitit 0, 1 ja 2) kautta syötämme sille kolmibittisen luvun X, ja vastauksena se aktivoi lähtönumeron (1<<X). Sirun jäljellä olevia tuloja käytetään koko suunnittelun skaalaamiseen. Tarvitsemme tätä sirua paitsi vähentääksemme mikro-ohjaimen varattujen nastojen määrää, vaan myös turvallisuuden vuoksi - jotta emme syty vahingossa päälle enempää LED-valoja kuin mahdollista ja emme polta MK:ta. Siru maksaa penniäkään ja se tulee aina säilyttää kotisi lääkekaappissa.

Meidän CD74HC238 on vastuussa jännitteen syöttämisestä halutun LEDin anodille. Täydellisessä multipleksissä se syöttäisi jännitettä kolonniin P-MOSFETin kautta, mutta tässä demossa se on mahdollista suoraan, koska se kuluttaa 20 mA, mukaan absoluuttiset maksimiarvosanat tietolomakkeessa. From tekninen lomake CD74HC238 tarvitsemme pinouts ja tämän huijausarkin:

Lue datasheets 2: SPI on STM32; PWM, ajastimet ja keskeytykset STM8:ssa
H = korkea jännitetaso, L = matala jännitetaso, X – älä välitä

Yhdistämme E2 ja E1 maahan, E3, A0, A1 ja A3 STM5:n nastoihin PD3, PC4, PC5 ja PC8. Koska yllä oleva taulukko sisältää sekä matalat että korkeat tasot, määritämme nämä tapit push-pull tappeiksi.

PWM

Toisen ajastimen PWM on määritetty samalla tavalla kuin edellisessä tarinassa, kahdella erolla:

Ensinnäkin meidän on otettava keskeytys käyttöön Päivitä tapahtuma (UEV), joka kutsuu toiminnon, joka vaihtaa aktiivisen LEDin. Tämä tehdään vaihtamalla bittiä Päivitä keskeytys käytössä rekisterissä, jolla on puhuva nimi

Lue datasheets 2: SPI on STM32; PWM, ajastimet ja keskeytykset STM8:ssa
Keskeytysrekisteri

#define TIM2_IER   *(volatile uint8_t *)0x005303

//enable interrupt
TIM2_IER = 1;

Toinen ero liittyy multipleksointiilmiöön, kuten esim haamukuvia – diodien loishohto. Meidän tapauksessamme se voi ilmetä johtuen siitä, että UEV:hen keskeytyksen aiheuttanut ajastin jatkaa tikitystä, eikä keskeytyskäsittelijä ehdi vaihtaa LED-valoa ennen kuin ajastin alkaa kirjoittaa jotain nastoihin. Tämän torjumiseksi sinun on käännettävä logiikka (0 = suurin kirkkaus, 255 = mikään ei pala) ja vältettävä äärimmäisiä käyttösuhdearvoja. Nuo. varmista, että UEV:n jälkeen LED-valot sammuvat kokonaan yhden PWM-jakson ajaksi.

Napaisuuden muuttaminen:

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

Vältä r:n, g:n ja b:n asettamista arvoon 255 ja muista kääntää ne ylösalaisin, kun käytät niitä.

Keskeyttää

Keskeytyksen olemus on, että tietyissä olosuhteissa siru lopettaa pääohjelman suorittamisen ja kutsuu jotain ulkoista toimintoa. Keskeytykset johtuvat ulkoisista tai sisäisistä vaikutuksista, mukaan lukien ajastin.

Kun loimme ensimmäisen projektin ST Visual Developissa, lisäksi main.c saimme ikkunan, jossa oli mystinen tiedosto stm8_interrupt_vector.c, sisällytetään automaattisesti projektiin. Tässä tiedostossa jokaiselle keskeytykselle on määritetty toiminto NonHandledInterrupt. Meidän täytyy sitoa funktiomme haluttuun keskeytykseen.

Tietolomakkeessa on taulukko keskeytysvektoreista, josta löydämme tarvitsemamme:

Lue datasheets 2: SPI on STM32; PWM, ajastimet ja keskeytykset STM8:ssa
13 TIM2-päivitys/ylivuoto
14 TIM2-kaappaus/vertaa

Meidän on vaihdettava LED UEV:ssä, joten tarvitsemme keskeytyksen #13.

Näin ollen ensinnäkin tiedostossa stm8_interrupt_vector.c muuta keskeytyksestä nro 13 (IRQ13) vastaavan toiminnon oletusnimi omaksesi:

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

Toiseksi meidän on luotava tiedosto main.h seuraavalla sisällöllä:

#ifndef __MAIN_H
#define __MAIN_H

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

Ja lopuksi kirjoita tämä funktio omaan 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;
}

Jäljelle jää vain ottaa keskeytykset käyttöön. Tämä tehdään assembler-komennolla rim - sinun täytyy etsiä se sieltä Ohjelmointikäsikirja:

//enable interrupts
_asm("rim");

Toinen assembler-komento on sim – kytkee keskeytykset pois päältä. Ne on kytkettävä pois päältä, kun uusia arvoja kirjoitetaan "videomuistiin", jotta väärällä hetkellä syntynyt keskeytys ei pilaa taulukkoa.

Kaikki koodi - GitHubissa.

Lue datasheets 2: SPI on STM32; PWM, ajastimet ja keskeytykset STM8:ssa

Jos ainakin joku pitää tätä artikkelia hyödyllisenä, en kirjoittanut sitä turhaan. Otan mielelläni vastaan ​​kommentteja ja huomautuksia, yritän vastata kaikkeen.

Lähde: will.com

Lisää kommentti