
V Püüdsin selgitada hobi-elektroonikutele, kes on kasvanud välja Arduino pikkadest pükstest, miks ja kuidas nad peaksid lugema andmelehti ja muud dokumentatsiooni mikrokontrollerite kohta. Tekst sai pikk, seetõttu lubasin praktilisi näiteid näidata eraldi artiklis. Noh, mis seal ikka, lubasin end kindlasti kanda...
Täna näitan, kuidas andmelehtede abil lahendada üsna lihtsaid, kuid paljude projektide jaoks vajalikke ülesandeid STM32 (Blue Pill) ja STM8 kontrollerites. Kõik näidiste projektid on pühendatud minu lemmik LED-idele, mida me süütame suurtes kogustes, mille jaoks peame kasutama igasugust huvitavat periferiaga.
Tekst sai taas hiiglaslik, seetõttu teen mugavuse huvides sisu:
Tühistus: ma ei ole insener ega pretendeeri sügavale arusaamisele elektroonikast; artikkel on mõeldud nagu mina – entusiastide jaoks. Tegelikult käsitlesin sihtrühmana ennast kaht aastat tagasi. Kui keegi oleks mulle tol ajal öelnud, et tundmatute kiipide andmelahtede lugemine ei ole hirmus, ei oleks ma kulutanud rohkelt aega koodilõikude otsimisele internetis ning ajanud kokku koddusid, kääre ja plaastrit.
Selle artikli keskmes on andmelahed, mitte projektid, seega võib kood olla natuke lohakas ja sageli ajutine. Ise projektid on väga lihtsad, kuigi sobivad oma esimese tutvustuse jaoks uue kiibiga.
Loodan, et minu artikkel aitab kedagi samas etapis hobisse süvenemisega.
STM32
16 LEDi koos DM634 ja SPI-ga
Väike projekt, kus kasutatakse Blue Pill (STM32F103C8T6) ja LED-driva DM634. Andmelahted aitavad meil mõista draiverit, STM IO-pordid ja seadistada SPI.
DM634
Taiwani kiip, millel on 16 16-bitist PWM-väljundit, saab ühendada ahelatesse. Noorem 12-bitine mudel on tuntud kodumaisest projektist. . Ajal, kui valisin DM63x ja hästi tuntud TLC5940 vahel, jäin DM-i kasuks mitmel põhjusel: 1) TLC on Aliexpressis kindlasti vale, aga see ei ole; 2) DM-il on iseseisev PWM oma sagedusgeneraatorega; 3) seda sai Moskvas odavalt osta, mitte oodata pakki Alist. Ja loomulikult oli huvitav ise õppida, kuidas kiipi juhtida, mitte kasutada olemasolevat raamatukogu. Kiibid on praegu peamiselt SSOP24 korpuses, neid ei ole keeruline adapterile jootta.
Kuna tootja on taiwani firma, kiibiga on kirjutatud hiina inglise keeles, mis tähendab, et saab huvitav olema. Esmalt vaatame pin-out'i (Pin Connection), et mõista, millisele jalale mida külge ühendada, ja jala kirjeldust (Pin Description). 16 väljundit:

Töötavate pidevvoogude allikad (avatud drenaaž)
Sink / Ava-draina väljund – drenaaž; töötava konstantse voolu allikas; väljund, mis on aktiivses olekus ühendatud maapinnaga, – LED-id on ühendatud draiveriga katoodide kaudu. Elektriliselt ei ole see muidugi mingi 'avatud drenaaž' (open drain), kuid andmelehtedes kasutatakse selliseid tähistusi väljundite drenaažirežiimis sageli.
![]()
Välimodulaatorid REXT-i ja GND vahel väljundvoolu väärtuse seadmiseks
REXT-pinni ja maapinna vahel paigaldatakse viidatud takistus, mis kontrollib väljundite sisetakistust. Vt graafikut lehe 9 andmelehes. DM634-s saab seda takistust ka programmiliselt juhtida, määrates üldise heleduse (üldine heledus); ei hakka seda artiklis detailidesse laskuma, lihtsalt panen siia takistuse 2,2 – 3 kΩ.
Kuna mõistmine, kuidas kiipi juhtida, vaatame seadme liidese kirjeldust:

Ah, siin see on, hiina inglise keeles oma hiilguses. Selle tõlkimine on keeruline, kuid soovi korral on seda võimalik mõista. Siiski on teine tee – vaadata, kuidas on ühendust kirjeldatud andmelehes funktsionaalselt sarnase TLC5940 jaoks:

… Andmete sisestamiseks seadmesse on vajalikud ainult kolm pinni. SCLK signaali eesmine serv nihutab andmed SIN pinnilt sisemisse registrisse. Kui kõik andmed on laaditud, fikseerib lühike kõrge signaal XLAT järjestikku edastatud andmed sisemistes registrites. Sisemised registrid on tasemega signaali XLAT aktiveeritud lülitid. Kõik andmed edastatakse kõige olulisema bitiga edasi.
Latch – lukk/kinnihoidja/fiksaator.
Tõusva serva – impulsi eesmine serv
MSB ees – kõige vasakpoolsema (vanema) bitiga ette.
andmete kellamine – edastada andmeid järjestikku (bitihiti).
Sõna kinnihoidja esineb sageli kiipide dokumentatsioonis ja seda tõlgitakse erinevalt, seega lubage mul
teha väike selgitusLED-juhik on tegelikult nihke register. "Nihke" (nihke) nimetuses viitab bitihitiliste andmete liikumisele seadme sees: iga uus sisestatud bit surub kogu ahela ette. Kuna nihke ajal ei soovi keegi näha kaootilist LEDide vilkumist, toimub protsess vahemälu registrites, mis on tööregistritest eraldatud sulguri abil (kinnihoidja) – see on nagu esikene, kus bitid järjestatakse soovitud järjestusse. Kui kõik on valmis, avatakse sulgur ja bitid saadetakse tööle, asendades eelneva partii. Sõna kinnihoidja dokumentatsioonis kiipide osas tähendab peaaegu alati sellist sulgurit, sõltumata sellest, millistes kombinatsioonides seda kasutatakse.
Nii et andmete edastamine DM634 toimub järgmiselt: seadistame DAI sisendi kõige kõrgema biti väärtuseks, tõmbame DCK üles-alla; seadistame DAI sisendi järgmise biti väärtuseks, tõmbame DCK; ja nii edasi, kuni kõik bitid on edastatud (kella järgi), pärast mida tõmbame LATi. Seda saab teha käsitsi (bit-bang), aga parem on kasutada selleks spetsiaalselt loodud SPI liidest, mis on meie STM32s esindatud kahel eksemplaril.
Sinine tablett STM32F103
Sissejuhatus: STM32 kontrollerid on oluliselt keerukamad kui Atmega328, suurest hirmust hoolimata. Energiatõhususe kaalutlustel on alguses peaaegu kogu periferiat välja lülitatud ja kellafrekvenss on 8 MHz sisemisest allikast. Õnneks on STM-i programmeerijad kirjutanud koodi, mis viib kiibi "arvestatavale" 72 MHz, ja kõik mulle teadaolevad IDE looajad on selle algatamise protseduuri lisanud, seega ei pea me seda seadistama (aga ). Kuid periferiat tuleb sisse lülitada.
Dokumentatsioon: Blue Pillil on populaarne STM32F103C8T6 kiip, millele on olemas kaks kasulikku dokumenti:
- STM32F103x8 ja STM32F103xB mikrokontrollerite jaoks;
- kogu STM32F103 seeria jaoks ja mitte ainult.
Andmelehtedest võivad meid huvitada:
- Pinouts – mikrokiipide pin'ide konfiguratsioonid – juhuks, kui otsustame plaate ise teha;
- Mälu kaart – konkreetse mikrokiibi mälu kaart. Viidatud juhendis on kogu seeria kaart, kus on mainitud registreid, mida meie omal ei ole.
- Pin Definitions tabel – põhifunktsioonide ja alternatiivsete funktsioonide nimekiri pin'ide jaoks; "sinise tableti" kohta on Internetis saadaval mugavamaid pilte pin'ide nimekirjade ja nende funktsioonidega. Seega googeldame kohe Blue Pill pinout ja hoidke selline pilt käepärast:

NB: Interneti pildil oli viga, mida märkisid kommentaarides, mille eest aitäh. Pilt on vahetatud, kuid see on õppetund – teavet, mis ei tule andmelehtedest, on parem kontrollida.
Eemaldame andmelehe, avame Reference Manual'i, edaspidi kasutame ainult seda.
Tegevuste järjekord: selgitame standardse sisendi/väljundi üle, seadistame SPI, aktiveerime vajalikud perifeeriad.
Sisend-väljund
Atmega328-l on sisendi-väljundi rakendamine äärmiselt lihtne, mistõttu STM32 rohkete valikute tõttu võib see segadusse ajada. Praegu vajame ainult väljundeid, kuid isegi nende jaoks on neli varianti:

avatud kollektoriga väljund, „tõmba-tõuka” väljund, alternatiivne „tõmba-tõuka”, alternatiivne avatud kollektor
„Tõmba-tõuka” (push-pull) – tuttav väljund Arduinol, pin võib võtta väärtuse kas HIGH või LOW. Aga avatud kollektoriga tekivad , kuigi tegelikult on kõik lihtne:


Väljundkonfiguratsioon / kui port on määratud väljundiks: / väljundpuhver on lubatud: / – avatud kollektoriga režiim: „0” väljundregister aktiveerib N-MOS, „1” väljundregister jätab porta Hi-Z režiimi (P-MOS ei aktiveeru) / – „tõmba-tõuka” režiim: „0” väljundregister aktiveerib N-MOS, „1” väljundregister aktiveerib P-MOS.
Ainus erinevus avatud kollektoriga (open drain) ja „tõmba-tõuka” (push-pull) vahel on see, et esimesel pin ei saa võtta seisundit HIGH: kui kirjutada üks väljundregistrisse, läheb ta kõrge resistentsuse režiimi (high impedance, Hi-Z). Kui kirjutada null, käitub pin mõlemas režiimis sama, nii loogiliselt kui ka elektriliselt.
Tavarežiimis väljund pin lihtsalt edastab väljundregisteri sisu. „Alternatiivset” juhtib vastav perifeeria (vt 9.1.4):
![]()
Kui porta bitti on konfigureeritud kui alternatiivne funktsioon, siis väljundregister lülitatakse välja ja pisipinna ühendatakse perifeerse seadme väljundsignaaliga.
Iga pinni alternatiivne funktsionaalsus on kirjeldatud Pin Definition'id andmelehest ja on allalaaditud pildil olemas. Küsimusele, mida teha, kui pinil on mitmeid alternatiivseid funktsioone, annab vastuse märkuses andmelehes:
![]()
Kui mitu perifeerset plokki kasutavad sama pinda, tuleks vältida konfliktide tekkimist alternatiivsete funktsioonide vahel ja kasutada korraga ainult ühte perifeerset plokki, vahetades perifeersete seadmete aktiveerimise bitti abil (vastavas RCC registris).
Lõpuks on väljundrežiimis pinidel ka taktimine kiirus. See on veel üks energiasäästufunktsioon, meie juhul seadistame lihtsalt maksimumi ja unustame selle.
Nii et: kasutame SPI-d, seega peavad kaks pinda (andmed ja taktsignaal) olema "alternatiivne funktsioon tõmba-tõuka", ja veel üks (LAT) – "tavaline tõmba-tõuka". Aga enne, kui neid määrame, selgitame välja SPI.
SPI
Veel üks väike teadlikkuse tõstmine
SPI ehk Serial Peripheral Interface (järjestikuline perifeerne liides) – lihtne ja äärmiselt efektiivne liides MCU-de ja teiste MCU-de ning üldiselt välismaailmaga suhtlemiseks. Tema tööpõhimõte on juba eespool kirjeldatud, seal, kus räägitakse Hiina LED-draiverist (viide käsiraamatu jaos 25). SPI võib töötada meistrirežiimis ja ori-režiimis. SPI-l on neli põhikanalit, millest kõik ei pruugi olla kasutuses:
- MOSI, Master Output / Slave Input: see pin meistrirežiimis edastab, ori-režiimis aga võtab vastu andmeid;
- MISO, Master Input / Slave Output: vastupidi, meistrirežiimis võtab vastu, ori-režiimis edastab;
- SCK, Serial Clock: määrab andmete edastamise sageduse meistris või võtab vastu taktsignaali oris. Sisuliselt lööb see bitid;
- SS, Slave Select: selle kanali kaudu teab ori, et temalt tahetakse midagi. STM32-l nimetatakse seda NSS-iks, kus N = negative, st kontrollerist saab ori, kui sellel kanali pin on maapinnaga ühendatud. Koostöös Open Drain Output režiimiga töötab see hästi, kuid see on juba teine lugu.
Nagu kõik muud, on SPI STM32-l mitmekesine funktsionaalsus, mis muudab selle mõistmise veidi keeruliseks. Näiteks suudab see töötada mitte ainult SPI, vaid ka I2S-liidesega, kusjuures dokumentatsioonis on nende kirjeldused segamini, seega tuleb omal ajal üleliigne välja jätta. Meie ülesanne on äärmiselt lihtne: peame lihtsalt edastama andmeid, kasutades ainult MOSI ja SCK. Suundume jagu 25.3.4 (pooldupleksside), kus leiame 1 kellaperiood ja 1 ühesuunaline andmejuhe (1 clock and 1 unidirectional data wire):

Selles režiimis kasutab rakendus SPI-d kas ainult edastamise režiimis või ainult vastuvõturežiimis. / Ainult edastamise režiim sarnaneb dupleksrežiimile: andmed edastatakse edastamispinni kaudu (MOSI meistri režiimis või MISO ori režiimis), samas kui vastuvõtupin (MISO või MOSI vastavalt) saab kasutada tavalise sisend-väljundpinni moodi. Sellisel juhul piisab rakendusel Rx-puhvri ignoreerimisest (kui seda lugeda, ei tule seal edastatud andmeid).
Suurepärane, MISO pin on meil vaba, ühendame selle LAT-signaaliga. Saame hakkama Slave Select'iga, mida STM32-s saab programmiliselt juhtida, mis on erakordselt mugav. Loe nimeka vastava lõike 25.3.1 SPI Üldkirjeldus:

Programmiline juhtimine NSS (SSM = 1) / Teave slavi valimise kohta on bitis SSI registris SPI_CR1. Väline pin NSS jääb rakenduse teiste vajaduste jaoks vabaks.
On aeg registritesse kirjutada. Otsustasin kasutada SPI2, otsime andmelehes tema põhiaadressi – jaotises 3.3 Aameremap (Mälu kaart):
![]()
Nii alustame:
#define _SPI2_(mem_offset) (*(volatile uint32_t *)(0x40003800 + (mem_offset)))Avame jaotise 25.3.3 kõneka nimega „SPI seadistamine meistrirežiimis“:

1. Sea järjestikuse liidese taktsagedus bitidega BR[2:0] registris SPI_CR1.
Registrid on koondatud sama nime all viidatud manuaali jaotises. Aadressi nihke (Aadressi nihke) CR1 – 0x00, vaikimisi on kõik bitid lähtestatud (Lähtestamisväärtus 0x0000):

Bitid BR määravad kontrolleri taktsageduse jaguri, määrates seeläbi sageduse, millega SPI töötab. STM32 sagedus on meil 72 MHz, LED-draiver töötab vastavalt oma andmelehe andmetele kuni 25 MHz, seega tuleb jagada neljaga (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/42. Määrake CPOL ja CPHA bitid, et määrata suhe andmete edastamise ja järjestikuse liidese sünkroonimise vahel (vt skeemi lk 240)
Kuna me loeme nüüd andmelehte, mitte skeeme, uurime parem CPOL ja CPHA bitide tekstikirjeldust lk 704 (SPI üldkirjeldus):

Sünkroonimise faas ja polariteet
CPOL ja CPHA bitide abil SPI_CR1 registris saab nelja erinevat ajastussuhet programmeerida. Bit CPOL (sünkroonimise polariteet) kontrollib sünkroonimissignaali seisundit, kui andmeid ei edastata. See bit reguleerib master ja slave režiime. Kui CPOL on nullitud, on SCK pinni rahuolekus madal tase. Kui CPOL bit on seatud, on SCK pinni rahuolekus kõrge tase.
Kui bit CPHA (kella signaali faas) on seadistatud, on vana bitisignaal SCK (langus, kui CPOL on väljastatud, või tõus, kui CPOL on seadistatud). Andmed fikseeritakse teise kellasignaali muutumise järgi. Kui bit CPHA on väljastatud, on vana bitisignaal esimesel kellasignaali muutumisel (langus, kui CPOL on seadistatud, või tõus, kui CPOL on väljastatud). Andmed fikseeritakse esimese kellasignaali muutumise järgi.
Tuginedes nendele teadmistele, jõuame järeldusele, et mõlemad bitid peavad jääma nulliks, kuna SCK signaal peab olema madal, kui seda ei kasutata, ja andmed edastatakse impulsi esimesel tõususignaalil (vt Rising Edge DM634 andmelehelt).
Muide, siin kohtume esmakordselt ST andmelehtede leksika eripäraga: seal kirjutatakse fraas "bit nulli seadmine" to reset a bit, mitte to clear a bit, nagu näiteks Atmega puhul.
3. Seadke DFF bit 8-bitise või 16-bitise andmeploki formaadi määramiseks.
Võtsin spetsiaalselt 16-bitise DM634, et mitte muretseda 12-bitiste PWM andmete edastamise pärast, nagu DM633 puhul. DFF on mõistlik seada üheks:
#define DFF 0x0800
_SPI2_ (_SPI_CR1) |= DFF; // 16-bit mode4. Konfigureerige SPI_CR1 registris bit LSBFIRST, et määrata ploki formaat.
LSBFIRST, nagu nimigi ütleb, seadeb edastamise väikseima bittide ette. Kuid DM634 soovib andmeid alustada suurima bitiga. Seetõttu jätame selle nulli.
5. Riistvararežiimis, kui pin NSS-i sisend peab olema, rakendage pin NSS-le kõrge signaal kogu baitide edastamise seansi vältel. Programmeerimisrežiimis seadke registris SPI_CR1 bitid SSM ja SSI. Kui pin NSS peab töötama väljundina, tuleb seadistada ainult bit SSOE.
Seame SSM ja SSI, et unustada riistvararežiim NSS:
#define SSI 0x0100
#define SSM 0x0200
_SPI2_ (_SPI_CR1) |= SSM | SSI; //enable software control of SS, SS high6. Bitid MSTR ja SPE peavad olema seadistatud (nad jäävad seadistatud ainult siis, kui NSS-i toidetakse kõrge signaaliga).
Tegelikult määrame nende bittidega meie SPI meistriks ja aktiveerime selle:
#define MSTR 0x0004
#define SPE 0x0040
_SPI2_ (_SPI_CR1) |= MSTR; //SPI master
//когда все готово, включаем SPI
_SPI2_ (_SPI_CR1) |= SPE;SPI on seadistatud, kirjutame kohe funktsioonid, mis saadavad baidid draiverile. Jätkame lugemist 25.3.3 "SPI seadistamine meistrirežiimis":

Andmete edastamise järjekord
Edastus algab, kui Tx-puhvrisse kirjutatakse bait.
Andmebait laaditakse nihkeregisterisse paralleelses režiimis (sisemise bussi kaudu) esimesena bitina edastamise ajal, pärast mida edastatakse see. järjestikorras MOSI pin'i režiimis, esimese või viimase bitiga edasi sõltuvalt LSBFIRST bit'i seadistusest CPI_CR1 registris. TXE lipp seatakse andmete edastamise järel Tx puhvrist nihkeregisteri, samuti luuakse katkestus, kui TXEIE bit CPI_CR1 registris on seatud.
Olen tõlkes mõned sõnad esile tõstnud, et märkida ühte SPI teostuse eripära STM kontrollerites. Atmegel seatakse TXE lipp (Tx Tyhi, Tx on tühi ja valmis andmeid vastu võtma) alles pärast seda, kui kogu bait on välja saadetud väljas. Siin aga seatakse see lipp kohe pärast seda, kui bait on pandud sisemisse nihkeregisteri. Kuna see pistetakse sinna kõigi bittidega korraga (paralleelselt), edastatakse andmed seejärel järjestikku, siis TXE seatakse enne, kui bait on täielikult edastatud. See on oluline, kuna meie LED-juhi puhul peame pinni LAT tõmbama pärast andmete edastamist keskkondades ja projekti klastrites. Selline põhimõte on õige , st ainult TXE lipp ei piisa meile.
See tähendab, et meil on vaja veel mingit lippu. Vaadakem 25.3.7 – “Oleku lipud”:

<…>

BUSY lipp
BSY lipp on seadistatud ja lähtestatud riistvaraliselt (sellele kirjutamine ei mõjuta midagi). BSY lipp näitab SPI suhtlemiskihi olekut.
See lähtestatakse:
kui edastus on lõpetatud (välja arvatud juhtrežiim, kui edastus on pidev)
kui SPI on väljas
kui juhtrežiimi tõrge esineb (MODF=1)
Kui edastus ei ole pidev, siis BSY lipp lähtestatakse igas andmeedastuses.
Okei, see tuleb kasuks. Uurime, kus on Tx puhver. Selleks loeme "SPI andmeregister":

Bitid 15:0 DR[15:0] Andmeregister
Saadud andmed või edastatavad andmed.
Andmeregister on jagatud kaheks puhviks – üks kirjutamiseks (edastuspuhver) ja teine lugemiseks (vastuvõtupuhver). Andmeregistrisse kirjutamine kirjutab Tx puhvri, samas kui andmeregistrist lugemine tagastab Rx puhvris sisalduva väärtuse.
Ja siis olekuregister, kus on TXE ja BSY flags:

Kirjutame:
#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
}Kuna peame edastama 16 korda kaks baiti, LED-draiveri väljundite arvu põhjal, siis näeb see enam-vähem välja nii:
void sendLEDdata()
{
LAT_low();
uint8_t k = 16;
do
{ k--;
dm_shift16(leds[k]);
} while (k);
while (_SPI2_(_SPI_SR) & BSY); \/\/ lõpetame edastuse
LAT_pulse();
}Aga me ei oska veel LAT pinni aktiveerida, seega naaseme I/O juurde.
Määrame pinid
STM32F1-s on pinide olekud on üsna ebatavalised. Selge on, et neid on rohkem kui Atmega-s, kuid need erinevad ka teistest STM-kiipidest. Jagunemine 9.1 Ülevaade GPIO-st:

Iga üldkasutatav sisendi/väljundi port (GPIO) omab kahte 32-bitist konfigureerimisregister (GPIOx_CRL ja GPIOx_CRH), kahte 32-bitist andmeregistrit (GPIOx_IDR ja GPIOx_ODR), 32-bitist seadistamis-/nullimisregister (GPIOx_BSRR), 16-bitist nullimisregister (GPIOx_BRR) ja 32-bitist lukustamisregisterit (GPIOx_LCKR).
Need on ebatavalised ning üsna ebamugavad, siin on esimesed kaks registrit, kuna 16 porti on jaotatud nende vahel formaadis „neli bitti venna kohta“. St. bitid nullist seitsmenda porti kohta istuvad CRL-is, samas kui ülejäänud on CRH-is. Samal ajal mahutavad muud registrid edukalt kõikide portide bitid – tihti jäädes pooleldi „reserveeritud“.
Lihtsuse huvides alustame nimekirja lõpust.
Lukustamisregister ei ole meil vajalik.
Konfiguratsiooni ja lähtestamise registrid on mõnevõrra lustlikud, kuna nad kattuvad osaliselt. Kõike saab kirjutada ainult BSRR-sse, kus ülemised 16 bitti lähtestavad pinni nulliks, samas kui alumised seadistavad selle 1-ks. Teine variant on kasutada ka BRR-i, mille alumised 16 bitti lähtestavad pinni ainuüksi. Mulle meeldib teine variant. Need registrid on olulised, kuna nad pakuvad atomaarset juurdepääsu pinna.

![]()
Atomaarne seadistus või lähtestamine
GPIOx_ODR-i bititasemel programmeerimise ajal ei ole vaja katkestusi välja lülitada: saad muuta ühe või mitu bitti ühe atomaarse APB2 kirjutamise operatsiooniga. See saavutatakse, kirjutades väärtuse „1” seadistus/ lähtestamisregistrisse (GPIOx_BSRR või ainult lähtestamiseks GPIOx_BRR), bitti, mida tuleb muuta. Ülejäänud bitid jäävad muutumatuks.
Andmeregistrid omavad üsna räägivad nimesid – IDR = Sisend Suuna register, sisendregister; ODR = Väljund Suuna register, väljundregister. Me ei vaja neid praeguses projektis.
Ja lõpuks, juhtimisregistrid. Kuna meid huvitavad teise SPI pinid, nimelt PB13, PB14 ja PB15, vaatame kohe CRH-d:

Ja näeme, et peame kirjutama midagi bittidesse 20. kuni 31. positsioonideni.
Oleme juba eelnevalt arutanud, mida me pin'ide osas soovime, seega ei kasuta ma siin ekranipilti, vaid ütlen lihtsalt, et MODE määrab suuna (sisend, kui mõlemad bitid on 0) ja pin'ide kiirus (meil on vaja 50MHz, st mõlemad pin'id '1' peavad olema), ning CNF määrab režiimi: tavaline 'tõmba-tõuka' – 00, 'alternatiivne' – 10. Nagu ülal näeme, on kõigil pin'idel määratud kolmas bit (CNF0), see seab need režiimi. ujuv sisend.
Kuna plaanin selle kiibiga veel midagi teha, olen lihtsuse huvides defineerinud kõik võimalikud väärtused MODE ja CNF nii alumistele kui ka ülemistele kontrollregistritele.
Nii et kuidagi selliselt
#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 0x20000000Meie pin'id asuvad port B' s (põhisult 0x40010C00), kood:
#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 seega võib kirjutada defineeringud LAT jaoks, mis liigub registrite BRR ja BSRR abil:
/*** 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 lihtsalt harjumusest, on alati olnud, jätkuge selle säilitamist)
Nüüd on kõik juba tore, aga ei tööta. Sest see on STM32, siin säästetakse elektrit, seega peab vajaliku perifeeria käivitama.
Käivitame kellamise
Kellad vastutavad taktimise eest, need on Clock. Ja me oleme juba näinud lühendit RCC. Otsime seda dokumentatsioonist: see tähistab Reset and Clock Control (Taaskäivitamise ja taktimise juhtimine).
Kuidas eelnevalt öeldud, on õnneks kõige keerulisema taktimise osa meie eest teinud STM inimestest, mille eest suur aitäh (veel kord jagan linki , et oleks arusaadav, kui keeruline see on). Me vajame ainult registreid, mis vastutavad perifeeria taktimise (Peripheral Clock Enable Registers) aktiveerimise eest. Alustuseks leiame RCC põhiaadressi, see on 'Mälu kaartide' alguses:
![]()
#define _RCC_(mem_offset) (*(volatile uint32_t *)(0x40021000 + (mem_offset)))Edasi on kas vaja klikkida lingile, kus proovida tabelist midagi leida, või, palju parem, joosta läbi aktiveerivate registrite kirjeldused osadest, mis räägivad aktiveerimisregister. Kus me leiame RCC_APB1ENR ja RCC_APB2ENR:


Ja neis, vastavalt, bitid, mis aktiveerivad SPI2, IOPB (I/O Port B) ja alternatiivfunktsioonid (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;Lõpliku koodi leiate .
Kui on võimalust ja soovi testida, siis ühendame DM634 järgmiselt: DAI PB15-le, DCK PB13-le, LAT PB14-le. Toitke draiverit 5 voltiga, ärge unustage ühendada maapindu.

STM8 PWM
PWM STM8-l
Kui ma seda artiklit alles planeerisin, otsustasin proovida õppida mõnda minu jaoks tundmatute kiipide funktsiooni ainult andmelehe abil, et mitte olla kingsepp, kellel pole kingi. STM8 sobis selleks ideaalselt: esiteks, mul oli paar Hiina plaati STM8S103-ga, ja teiseks, see ei ole liiga populaarne, mistõttu kiusatus otsida lahendusi internetist on piiratud nende lahenduste puudumise tõttu.
Kiipide juurde kuulub ka ja , kus esimeses on joonised ja registrite aadressid, teises – kõik muu. STM8 programmeeritakse C-s pigem ebameeldivas IDE-s .
Kellastamine ja sisend-väljund
Vaikimisi töötab STM8 sagedusel 2 MHz, see tuleb kohe parandada.

Kell signaal HSI (sisekell, kiire)
Kell signaal HSI saadakse sisemise 16 MHz RC-generaatorist, millel on programmeeritav jagur (1 kuni 8). See on määratud kellasageduse jaguri registris (CLK_CKDIVR).
Märkus: alguses valitakse peamiseks kellasignaali allikaks HSI RC-generaator jaguriga 8.
Otsime andmelehelt registri aadressi, kirjelduse viidemanuaalist ja näeme, et register tuleb tühjendada:
#define CLK_CKDIVR *(volatile uint8_t *)0x0050C6
CLK_CKDIVR &= ~(0x18);Kuna plaanime käivitada PWM-i ja ühendada LED-id, vaatame pin-outi:

Kiip on väike, paljud funktsioonid on üles riputatud samadele jalgadele. See, mis on nurksulgudes – "alternatiivne funktsionaalsus", lülitub "valikute baitide" (option bytes) abil – midagi sarnast Atmega fusidega. Nende väärtusi saab muuta programmiliselt, kuid seda ei ole vaja, kuna uus funktsionaalsus aktiveeritakse alles pärast taaskäivitamist. Lihtsam on kasutada ST Visual Programmerit (mis tuleb koos Visual Develop'iga), mis oskab neid baite muuta. Pin-outi näeme, et esimese taimeri CH1 ja CH2 väljundid on peidetud nurksulgudesse; STVP-s tuleb määrata AFR1 ja AFR0 bitid, kusjuures teine viib CH1 väljundi teisest taimerist PD4-lt PC5-le.
Seega juhtivad LED-e 6 jala: PC6, PC7 ja PC3 esimese taimeri jaoks, PC5, PD3 ja PA3 teise jaoks.
STM8 sisse- ja väljundpindade seadistamine on lihtsam ja loogilisem kui STM32-l:
- tuttav Atmega andmeregister DDR (Data Direction Register): 1 = väljund;
- esimene kontrollregister CR1 väljundil määrab režiimi "tõmba-tõuka" (1) või avatud drenaaž (0); kuna ma ühendangi LED-id kiibi katoodide kaudu, jätan siia nullid;
- Teine kontrollregister CR2 määrab taktsükli kiirusel: 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)); //fastPWM seadistus
Käime alguses läbi mõisted:
- PWM sagedus – sagedus, millega ajastaja tiksub;
- Auto-reload, AR – automaatselt laaditav väärtus, kuni mille ajastaja loendab (impulsi periood);
- Update Event, UEV – sündmus, mis toimub, kui ajastaja jõuab AR väärtuseni;
- PWM täitmisaste – PWM täitmisaste, mida tavaliselt nimetatakse 'tõhususe' määramiseks;
- Capture/Compare Value – väärtus, mida tuleb haarata/võrrelda, ja ajastaja loendab selle juurde, et midagi juhtuks (PWM puhul – inversioon väljundsignaalile);
- Eelladimisväärtus – eelnevalt laaditud väärtus. Võrdlusväärtus ei saa muutuda, kuni ajastaja tiksub, vastasel juhul rikutakse PWM tsüklit. Seetõttu pannakse uued edastatavad väärtused puhvri ja need tõmmatakse sealt välja, kui ajastaja jõuab loenduse lõppu ja lähtestatakse;
- Serva-alline ja Kesk-alline režiimid – serva ja keskuse järgi joondamine, sama, mis atmelite Kiire PWM ja Faasiline korrektne PWM.
- OCiREF, väljundvõrdlussignaal – viidatud väljundsignaal, mis tegelikult esindab PWM režiimil vastaval pinnal.
Nagu juba selgesti näha, on kahe ajastiga – esimese ja teise – olemas PWM funktsionaalsus. Mõlemad on 16-bitised, esimesel on palju lisafunktsioone (sealhulgas suudab see lugeda nii üles kui ka alla). Me peame tagama, et mõlemad töötaksid ühtemoodi, seetõttu otsustasin alustada teadlikult vähem funktsionaalset teisest, et juhuslikult mitte kasutada midagi, mida selles ei ole. Teatud probleem seisneb selles, et PWM funktsionaalsuse kirjeldus kõikide ajastite jaoks viidatud käsiraamatus asub esimese ajasti jaotises (17.5.7 PWM Mode), mistõttu tuleb pidevalt dokumenti edasi-tagasi hüpata.
STM8 PWM-il on oluline eelis võrreldes Atmega PWM-iga:

Piiriga joondatud PWM
Ülespoole loendamise konfiguratsioon
Ülespoole loendamine on aktiivne, kui bit DIR TIM_CR1 registris on lähtestatud
Näide
Näide kasutab esimest PWM režiimi. PWM OCiREF viide signaal hoitakse kõrgel tasemel, kuni TIM1_CNT < TIM1_CCRi. Vastasel juhul on see madalal tasemel. Kui võrdlemise väärtus TIM1_CCRi registris on suurem kui automaatlaadimise väärtus (registreeritud TIM1_ARR), siis hoitakse OCiREF signaal 1-l. Kui võrdlemise väärtus on 0, hoitakse OCiREF nullis.…
STM8 ajasti uuendusürituse kontrollib esmalt võrdle väärtust, ja alles siis väljastab referentssignaali. Atmega timer esmalt loendab ja siis võrdleb, mille tõttu võrdle väärtus == 0 tulemusena saadakse nõel, millega tuleb mingil moel võidelda (näiteks, programmiga loogikat pöörates).
Nii et, mida me soovime teha: 8-bitine PWM (AR == 255), loeme alt ülespoole, piiride kohaselt. Kuna lambid on ühendatud kiibi katoodide kaudu, peab PWM väljastama 0 (LED süttib) kuni võrdle väärtust ja 1 pärast.
Oleme juba lugenud teatud PWM režiim, seega otsime teise taimeri vajalikku registrit viidates viidande juhendile selle fraasi kaudu (18.6.8 – TIMx_CCMR1):

110: Esimene PWM režiim – loendades alt üles, on esimene kanal aktiivne seni, kuni TIMx_CNT < TIMx_CCR1. Muul juhul ei ole esimene kanal aktiivne. [edasi dokumendis vale koopia taimerist 1]
111: Teine PWM režiim – loendades alt üles, ei ole esimene kanal aktiivne seni, kuni TIMx_CNT < TIMx_CCR1. Muul juhul on esimene kanal aktiivne.
Kuna LED-id on ühendatud MK katoodide kaudu, sobib meile teine režiim (esimene ka, kuid me pole seda veel teada).

Bitt 3 OC1PE: Lülita välja 1 eelkoormus
0: TIMx_CCR1 eelnev register on. TIMx_CCR1 can be written to at any time. The new value takes effect immediately.
1: TIMx_CCR1 eelnev register is on. Read/write operations access the preload register. The preloaded value of TIMx_CCR1 is loaded into the shadow register at each update event.
*Märkus: PWM režiimi õige toimimise tagamiseks peavad eelregistreerimine olema sisse lülitatud. See pole vajalik ühetasemelise signaali režiimis (TIMx_CR1 registris seatud OPM bit).
Okei, lülitame sisse kõik, mis on vajalik teise taimeri kolme kanali jaoks:
#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 koosneb kahest kaheksabitisest registrist, siin on kõik lihtne:
#define TIM2_ARRH *(volatile uint8_t *)0x00530F
#define TIM2_ARRL *(volatile uint8_t *)0x005310
TIM2_ARRH = 0;
TIM2_ARRL = 255;Teine taimer oskab lugeda ainult alt üles, piirmäärad, midagi ei pea muutma. Seame sageduse jaguri näiteks 256. Teise taimeri jagur on seatud TIM2_PSCR registrisse ja esindab kaht tõusu:
#define TIM2_PSCR *(volatile uint8_t *)0x00530E
TIM2_PSCR = 8;Alles jääb väljundite ja teise taimeri sisselülitamine. Esimese ülesande lahendamine toimub registrite abil. Capture/Compare Enable: neid on kaks, kolm kanalit, mis on ebaühtlaselt jaotatud. Siin saame ka teada, et signaali polaarset on võimalik muuta, st põhimõtteliselt võiks kasutada ka PWM Mode 1. Kirjutame:
#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;Noh, ja lõpuks käivitame taimeri TIMx_CR1 registris:

#define TIM2_CR1 *(volatile uint8_t *)0x005300
TIM2_CR1 |= 1;Kirjutame lihtsa analooge AnalogWrite(), mis edastab taimerile vastavad väärtused võrdlemiseks. Registrid on etteaimatavalt nimetatud Capture/Compare registrid, neid on igas kanalis kaks: madalamad 8 bitti TIM2_CCRxL ja kõrgemad TIM2_CCRxH. Kuna oleme seadnud 8-bitise PWM-i, piisab ainult madalamate bittide kirjutamisest:
#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;
}Karmim lugeja märgib, et meil on tekkinud veidi defektne PWM, mis ei suuda anda 100% täituvust (maksimaalse väärtuse 255 korral signaal invertitakse ühe taimeri tsükli jooksul). LED-de jaoks pole see probleem, aga tähelepanelik lugeja juba oletab, kuidas seda parandada.
PWM teisel taimeril töötab, liikume esimesse.
Esimene taimer kasutab täpselt samu registreid ja bitte nagu teine, lihtsalt need bitid, mis olid "broneeritud" teise taimeri puhul, on esimeses aktiivselt kasutusel erinevates arenenud funktsioonides. Seega piisab, kui leida nende registrite aadressid andmete lehelt ja kopeerida kood. Samuti tuleks muuta sageduse jaguri väärtust, kuna esimene taimer peab saama mitte 2 astme, vaid täpse 16-bitise väärtuse kahes registris. Jaguri kõrge väärtus ja Madal. Teeme kõik ja… esimene taimer ei tööta. Milles on probleem?
Probleemi lahendamiseks peab vaatama kogu jaotist, mis käsitleb taimeri 1 juhtimisregistreid, kus otsime puuduvat elementi teise taimeri puhul. Leiame 17.7.30 Lõhke register (TIM1_BKR), kus on selline bit:
![]()
Aktiveeri peamine väljund
#define TIM1_BKR *(volatile uint8_t *)0x00526D
TIM1_BKR = (1<<7);Nüüd on kõik, kood .

STM8 Multiplex
Multiplexing STM8-l
Kolmas mini projekt seisneb selles, et ühendada teise taimeriga PWM-režiimis kaheksa RGB LED-i ja sundida neid kuvama erinevaid värve. Põhialus on LED-multipleximise kontseptsioon, mis seisneb selles, et kui süüdata ja kustutada LED-e väga kiiresti, näib meile, et nad põlevad pidevalt (nägemise püsimine, visuaalse tajumise inerts). Kunagi tegin ma .
Töö algoritm näeb välja järgmiselt:
- ühendasime esimese RGB LEDi anoodi;
- süütasime selle, andes katoodidele vajalikud signaalid;
- ootasime PWM tsükli lõppu;
- ühendasime teise RGB LEDi anoodi;
- süütasime selle…
Ja nii edasi. Muidugi, et kõik kenasti toimiks, peab anoodi ühendamine ja LEDi «süütamine» toimuma samaaegselt. Ükskõik, kuidas, peame kirjutama koodi, mis kolmes kanalis teisel taimeril väärtusi annab, UEV saavutamisel neid muudab ja samal ajal muudab aktiivset RGB-LEDi.
Kuna LEDide vahetus toimub automaatselt, tuleb luua «videomälu», kust katkestuse töötleja andmeid saab. See on lihtne massiiv:
uint8_t colors[8][3];Konkreetse LEDi värvi muutmiseks piisab, kui kirjutada sellesse massiivi vajalikud väärtused. Ja aktiivse LEDi numbri eest vastutab muutujad
uint8_t cnt;Demuux
Õige multipleximise jaoks vajame, nagu mitte üllatav, demultiplexorit CD74HC238. Demultiplexor on chip, mis rakendab riistvaraliselt operaatorit <<. Kolme samaa pinniä (bittit 0, 1 ja 2) syötämme sille kolmitiivisen numeron X, ja se aktivoi vastauksena lähtönumeron (1<<X). Chipin muut tulot käytetään koko rakenteen skaalaamiseen. Tämä siru ei ole vain tarpeellinen mikro-ohjaimen käytettävien pinnejen määrän vähentämiseksi, vaan myös turvallisuuden vuoksi - jotta ei vahingossa kytketä päälle enemmän LED-valoja kuin on sallittua ja polteta mikro-ohjainta. Siru maksaa vain vähän, joten sen on hyvä olla aina kotilääkkaapissa.
CD74HC238 vastaa siitä, että se antaa jännitteen oikean LEDin anodille. Täysimittaisessa multiplexissa se antaisi jännitteen sarakkeeseen P-MOSFETin kautta, mutta tässä demossa voi myös liittää suoraan, koska se kuluttaa 20 mA, kuten absolute maximum ratings datasheetissä. Datasheetistä CD74HC238 tarvitsemme pinoutin ja tämän muistiinpanon: Liitä E2 ja E1 maahan, E3, A0, A1 ja A3 pinneihin PD5, PC3, PC4 ja PC5 STM8:ssä. Koska yllä oleva taulukko sisältää sekä matalat että korkeat tasot, määritellään nämä pinnit push-pull-ulostuloiksi.

PWM
PWM toisen ajastimen asetetaan samalla tavalla kuin aiemmassa tarinassa, kahdella poikkeuksella:
PWM
PWM teisel taimeril seadistatakse samamoodi nagu eelnevas loos, kahel eristusel:
Esiteks peame aktiveerima katkestuste signaali Uuenda sündmus (UEV), mis kutsub üles funktsiooni, mis vahetab aktiivset LEDi. Seda tehakse bitimuutmisega Katkestuse uuendamise sisselülitus registreerimisfailis, mille nimi ütleb kõik

Katkestuste lubamise register
#define TIM2_IER *(volatile uint8_t *)0x005303
//enable interrupt
TIM2_IER = 1;Teine erinevus on seotud sellise nähtusega nagu ghosting – LEDide soovimatu kumamine. Meie puhul võib see tekkida, kui ajastust, mis käivitab katkestuse UEV, edasiviib ja katkestuse käitleja ei jõua LEDi vahetada enne, kui ajastus hakkab juba midagi väljunditesse kirjutama. Selle vastu võitlemiseks peame rakendama loogika ümber (0 = maksimaalne heledus, 255 = ei põle) ja vältima lõppväärtusi. St. saavutama, et pärast UEV-d LEDid täielikult kustuksid ühe PWM tsükli jooksul.
Vahetame polaarsust:
//set polarity
TIM2_CCER1 |= (CC1P | CC2P);
TIM2_CCER2 |= CC3P;Vältige r, g ja b seadistamist 255-ks ning ärge unustage neid kasutada invertitud väärtustes.
Katkestused
Katkestuse olemus seisneb selles, et teatud tingimustel lõpetab kiip põhiprogrammi täitmise ja kutsub üles mingit välisest funktsiooni. Katkestused tekivad väliste või sisemiste mõjutuste tõttu, sealhulgas ajastusest.
Kui me esmakordselt lõime projekti ST Visual Developis, saime lisaks main.c salapärase failiga stm8_interrupt_vector.c, mis oli automaatselt projekti lisatud. Selles failis on iga katkestuse jaoks seotud funktsioon NonHandledInterrupt. Peame oma funktsiooni siduma vajaliku katkestusega.
Andmelehes on katkestuste vektorite tabel, kust leiame vajaliku:

13 TIM2 uuendus/ülemäärasus
14 TIM2 tabamine/võrdlemine
Peame LED-i muutma UEV korral, seega on vajalik katkestus nr 13.
Seega, kõigepealt, failis stm8_interrupt_vector.c muudame katkestuse nr 13 (IRQ13) vaike funktsiooni nime oma omaks:
{0x82, TIM2_Overflow}, /* irq13 */Teiseks, peame looma faili main.h sellise sisuga:
#ifndef __MAIN_H
#define __MAIN_H
@far @interrupt void TIM2_Overflow (void);
#endifJa lõpuks, registreerime selle funktsiooni oma main.c:
@far @interrupt void TIM2_Overflow (void)
{
PD_ODR &= ~(1<<5); // lülitab demultiplexori välja
PC_ODR = (cnt<<3); // kirjutame demultiplexorisse uue väärtuse
PD_ODR |= (1<<5); // lülitame demultiplexori sisse
TIM2_SR1 = 0; // nullime värskenduskatkestuse oote lipu
cnt++;
cnt &= 7; // liigutame LED-i loendurit
TIM2_CCR1L = ~colors[cnt][0]; // edastame puhverisse pööratud väärtused
TIM2_CCR2L = ~colors[cnt][1]; // järgmise PWM tsükli jaoks
TIM2_CCR3L = ~colors[cnt][2]; //
return;
}Tuleb sisse lülitada katkestused. Selleks on vaja kasutada assembleri käsku rim – tuleb seda otsida :
//enable interrupts
_asm("rim");Teine assembleri käsk – sim – keebab katkestused välja. Need tuleb välja lülitada, kui salvestatakse uusi väärtusi 'videomällu', et ootamatu katkestamine ei rikuks massiivi.
Kogu kood on .

Kui see artikkel kellelegi kasulikuks osutub, siis ei olnud mu kirjutamine asjata. Ootan kommentaare ja tähelepanekuid, püüan kõigile vastata.
Allikas: habr.com
