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:
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:
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.
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:
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:
... 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:
Data Sheet mikro-ohjaimille STM32F103x8 ja STM32F103xB;
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:
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:
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ä:
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):
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ä:
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):
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:
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:
Rekisterit on koottu samannimiseen käsikirjaosaan. Osoitteen muutos (Osoitteen siirtymä) CR1 – 0x00, oletusarvoisesti kaikki bitit tyhjennetään (Nollaa arvo 0x0000):
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).
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):
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:
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:
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":
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":
<…>
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":
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:
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:
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:
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:
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.
(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:
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:
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.
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.
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.
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ä:
Koska aiomme käyttää PWM:ää ja kytkeä LEDit, katsotaanpa liitäntää:
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
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:
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):
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ä).
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:
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:
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:
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:
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:
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:
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
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.
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:
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ä:
@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.
Jos ainakin joku pitää tätä artikkelia hyödyllisenä, en kirjoittanut sitä turhaan. Otan mielelläni vastaan kommentteja ja huomautuksia, yritän vastata kaikkeen.