
Uues Proovisin selgitada hobi-elektrikutele, kes on kasvanud välja Arduino lühikesest, miks ja kuidas nad peaksid lugema datalehti ja muud dokumentatsiooni mikrokontrollerite kohta. Tekst osutus pikaks, seega lubasin praktilisi näiteid näidata eraldi artiklis. Noh, lubasime endale…
Täna näitan, kuidas datalehtede abil lahendada üsna lihtsaid, kuid paljude projektide jaoks vajalikke ülesandeid STM32 (Blue Pill) ja STM8 kontrolleritel. Kõik demoprojektid on pühendatud minu lemmik LED-dele, mida me süütame suures koguses, selleks tuleb kasutada igasugust huvitavat perifeeriat.
Tekst sai jälle tohutu, seega mugavuse huvides teen sisukorra:
Vastutuse välistamine: ma ei ole insener, ei pretendeeri sügavale teadlikkusele elektroonikast, artikkel on mõeldud samasugustele harrastajatele nagu mina. Tegelikult vaatlesin sihtgrupina iseennast kaks aastat tagasi. Kui keegi oleks mulle sel ajal öelnud, et datalehti tundmatu kiibi kohta lugeda pole hirmus, ei oleks ma kulutanud tohutult aega mingite koodilõikude otsimisele internetis ja jalgrataste väljamõtlemisele kääride ja plaastritega.
Selle artikli keskmes on datalehed, mitte projektid, seega võib kood olla mitte eriti viimistletud ja sageli improviseeritud. Ise projektid on väga lihtsad, kuigi sobivad esmakordseks kohtumiseks uue kiibiga.
Loodan, et minu artikkel aitab kedagi sarnasel hobi tutvumise etapil.
STM32
16 LED-i DM634 ja SPI abil
Väike projekt, kus kasutatakse Blue Pill (STM32F103C8T6) ja LED-draiveri DM634. Uurime datalehti mööda draiverit, STM IO-pordid ja seadistame SPI.
DM634
Taiwani kiip, millel on 16 16-bitist PWM-väljundit, võib ühendada ahelatesse. Noorem 12-bitine mudel on tuntud kodumaise projekti kaudu. . Aeg hakkasin DM63x ja tuntud TLC5940 vahel otsustama, jäi DM peale mitmel põhjusel: 1) TLC AliExpress'is on kindlasti vale, aga see ei ole; 2) DM-l on iseseisev PWM oma sagedusgeneraatorega; 3) seda oli Moskvas odavalt võimalik osta, mitte oodata pakki AliExpress'ist. Ja muidugi, oli huvitav õppida ise juhtima kiipi, mitte kasutada valmis raamatukogu. Kiibid on praegu peamiselt esindatud SSOP24 korpuses, neid ei ole keeruline adapterile jootta.
Kuna tootja on Taiwanist, siis on kiibi dokumentatsioon kirjutatud hiina inglise keeles, mis tähendab, et see saab lõbus olema. Esiteks vaatame pinne (Pin Connection), et mõista, millisele jalale mida ühendada ja pinna kirjeldust (Pin Description). 16 väljundit:

Sisse voolav konstantne vool (avatud äravool)
Sink / Open-drain output – äravool; sisse voolav vool; väljund, aktiivses olekus ühendatud maaga – LED-id ühendatakse draiverile katoodidega. Elektriliselt ei ole see muidugi „avatud äravool” ( ), kuid andmelehtedes on selline tähistus väljundite äravoolurežiimis sageli.avatud äravoolVälimised resistorsid REXT ja GND vahel väljundvoolu määramiseks
![]()
REXT pinni ja maa vahel paigaldatakse viidatud resistor, mis kontrollib väljundite sisemist takistust, vt diagramm andmelehe lk 9. DM634 puhul saab seda takistust ka tarkvara abil juhtida, seades üldise heleduse (
global brightness); selles artiklis ma detailidesse süvenema ei hakka, lihtsalt panen siia 2.2 – 3 kΩ resistori.Et mõista, kuidas kiipi juhtida, vaatame seadme liidese kirjeldust:
Aha, siin on see hiina inglise keel kogu oma hiilguses. Selle tõlkimine on keeruline, aga soovikorral on võimalik aru saada, kuid on olemas teine tee – vaadata, kuidas kirjeldatakse ühendust andmelehes funktsionaalselt sarnase TLC5940-ga:

… Andmete sisestamiseks seadmesse on vajalikud ainult kolm pinni. SCLK signaali tõusva ääre peale edastatakse andmed pinni SIN kaudu sisemisse registrisse. Pärast kõigi andmete laadimist fikseerib lühike kõrge signaal XLAT järjestikku edastatud andmed sisemistes registrites. Sisemised registrid – aktiveeritud XLAT signaali taseme järgi lülitid. Kõik andmed edastatakse kõige kõrgema bitiga ettepoole.

Latch – lüliti/klamber/fikseerija.
Rising edge – impulsi tõusvas ääres MSB first
MSB first MSB first
MSB first – kõige vasaku bitiga edasi.
andmete vahetamiseks – edastama andmeid järjestikku (bitiviisiliselt).
Sõna lukk esineb sageli kiipide dokumentatsioonis ja tõlgitakse erinevalt, seetõttu luban endale
väikese teoreetilise ülevaateLED-draiver on sisuliselt nihkeregister. "Nihke" (sift) nimetuses tähistab bitiviisilist andmeedastust seadme sees: iga uus sisse sisestatud bit tõukab kogu ahela enda ette edasi. Kuna nihkeprotsessi käigus ei soovita keegi jälgida kaootilist LED-de vilkumist, toimub see protsess puhverregisterites, mis on tööregisteritest eraldatud sulguri (lukk) abil – see on omamoodi eelruum, kus bitid järjestusse seadistatakse. Kui kõik on valmis, avatakse sulgur ja bitid suunatakse tööle, asendades eelneva partii. Sõna lukk kiipide dokumentatsioonis viitab peaaegu alati sellisele sulgurile, olenemata sellest, millistes kombinatsioonides seda kasutatakse.
Nii et DM634 andmeedastamine toimub nii: seadistame DAI sisendi kauge LED-i kõige oluliseks bitiks, tõukame DCK üles-alla; seadistame DAI sisendi järgmise bitiga, tõukame DCK; ja nii edasi, kuni kõik bitid on edastatud (clocked in), seejärel tõukame LAT-i. Seda saab teha käsitsi (bit-bang), kuid parem on kasutada selleks spetsiaalselt kohandatud SPI liidest, mille on meie STM32-s esindatud kahe eksemplariga.
Sinine pill STM32F103
Sisend: STM32 kontrollerid on oluliselt keerukamad kui Atmega328, kui see võib hirmutada. Energiatõhususe kaalutlustel on enamik perifeeriast alguses välja lülitatud ja kellafrekvents on 8 MHz sisemisest allikast. Õnneks on STM programmeerijad kirjutanud koodi, mis viib kiibi "arvutatud" 72 MHz-ni, ja kõigi teadaolevate IDE autorid on selle initsiatsiooniprotseduuri lisanud, seega ei pea me kellastama (kuid ). Kuid perifeeriat tuleb sisse lülitada.
Dokumentatsioon: Blue Pillil on populaarne kiip STM32F103C8T6, sellele on kaks kasulikku dokumenti:
- STM32F103x8 ja STM32F103xB mikrokontrollerite jaoks;
- kogu STM32F103 seeria ja mitte ainult.
Datasheet'is võivad meid huvitada:
- Pinouts – kiipide pinout'id – juhuks, kui otsustame ise plaate teha;
- Mälu kaart – konkreetse kiibi mälu kaart. Viidatud juhendis on kogu tootesarja kaart, kus on mainitud registreid, mida meie omal ei ole.
- Pin Definitions tabel – peamiste ja alternatiivsete pinna funktsioonide loetelu; "sinise tableti" kohta leiab internetist mugavamaid pilti pinna ja nende funktsioonidega. Seetõttu otsime koheselt üles Blue Pill pinout ja hoidke selline pilt käepärast:

NB: internetipildil oli viga, millele viidati kommentaarides, tänu sellele. Pilt on vahetatud, kuid see on õppetund – teavet, mis ei pärine andmelehtedest, on parem kontrollida.
Kustutame andmelehe, avame Reference Manual, edaspidi kasutame ainult seda.
Tegevuste järjekord: arutame standardset sisend-/väljundit, seadistame SPI, lülitame sisse vajaliku periferi.
Sisend-väljund
Atmega328-l on sisend-väljund äärmiselt lihtne, mille tõttu STM32 jaoks rohkete valikute tõttu võib see segadusse ajada. Praegu vajame ainult väljundeid, aga isegi neid on neli varianti:

avatud kollektor, „push-pull“, alternatiivne „push-pull“, alternatiivne avatud kollektor
„Push-pull“ (push-pull) – tavaline väljund Arduino-lt, pin saab võtta väärtusi kas HIGH või LOW. Kuid "avatud kollektoriga" tekivad , kuigi tegelikult on kõik lihtne:


Väljundi konfiguratsioon / kui port määratakse väljundiks: / väljundibufri on sisse lülitatud: / – avatud kollektorirežiim: „0“ väljundiregistris aktiveerib N-MOS, „1“ väljundiregistris jätab port Hi-Z režiimi (P-MOS ei aktiveeru) / – „push-pull“ režiim: „0“ väljundiregistris aktiveerib N-MOS, „1“ väljundiregistris aktiveerib P-MOS.
Ainus erinevus avatud kollektoril (avatud äravool) ja „push-pull“ (push-pull) seisneb selles, et esimesel pin ei saa võtta HIGH positsiooni: kui kirjutate väljundiregistrisse üht, läheb ta kõrgvastupidavusse (high impedance, Hi-Z). Kui kirjutate nulli, käitub pin mõlemas režiimis sama, nii loogiliselt kui ka elektriliselt.
Tava väljundirežiimis pin lihtsalt edastab väljundiregistri sisu. „Alternatiivses“ juhib seda vastav periferi (vt 9.1.4):
![]()
Kui pordi bit on konfigureeritud alternatiivseks väljundiks, häälestatakse väljundregister välja ja pin ühineb periferi väljundsignaaliga.
Iga pinna alternatiivne funktsionaalsus on kirjeldatud Pin Definitions datasheet ja on allalaaditud pildil. Küsimusele, mida teha, kui pinnal on mitu alternatiivset funktsiooni, annab vastuse viide datasheet'is:
![]()
Kui mitu perifeerset plokki kasutavad sama pini, tuleks konfliktide vältimiseks samal ajal kasutada ainult ühte perifeerset plokki, vahetades läbi perifeeria aktiveerimise bite (vastavas RCC registris).
Lõppkokkuvõttes on väljundrežiimis pinidel ka taktsagedus. See on veel üks energiasäästufunktsioon, meie puhul paneme lihtsalt maksimumi ja unustame.
Nii et: kasutame SPI-d, mis tähendab, et kaks pini (andmete ja taktsignaali) peaksid olema "alternatiivne funktsioon tõukamiseks-tõmbamiseks", ja veel üks (LAT) – "tavaline tõukamiseks-tõmbamiseks". Aga enne nende määramist selgitame SPI-d.
SPI
Veel üks väike õpik
SPI ehk Serial Peripheral Interface (järjestikuline perifeerne liides) – lihtne ja tõhus liides MCU-de ühendamiseks teiste MCU-de ja üldiselt välismaailmaga. Selle tööpõhimõte on juba eespool kirjeldatud, seal, kus räägiti Hiina LED-draiverist (viidatud manuaalis vt jaotist 25). SPI võib töötada meistrirežiimis („isanda”) ja orjarežiimis („orja”). SPI-l on neli peamist kanali, mida kõiki ei pruugi kasutada:
- MOSI, Master Output / Slave Input: see pin meistrirežiimis edastab ja orjarežiimis võtab vastu andmeid;
- MISO, Master Input / Slave Output: vastupidiselt, meistrirežiimis võtab vastu, orjarežiimis edastab;
- SCK, Serial Clock: määrab andmete edastamise sageduse meistris või võtab vastu taktsignaali orjas. Essentsiaalselt tavab see bitte;
- SS, Slave Select: selle kanali kaudu saab orj teada, et temalt tahetakse midagi. STM32-l nimetatakse seda NSS-ks, kus N = negatiivne, st kontroller muutub orjaks, kui selles kanalis on maapind. Kombineerub hästi Open Drain Output režiimiga, kuid see on teine lugu.
Nagu kõik muu, on SPI STM32-l rikkaliku funktsionaalsusega, mis teeb selle mõistmise veidi keeruliseks. Näiteks oskab see töötada mitte ainult SPI, vaid ka I2S-liidesena, lisaks on nende kirjeldused dokumentatsioonis segamini ja tuleb õigel ajal üleliigne seadmest ära lõigata. Meie ülesanne on äärmiselt lihtne: peame lihtsalt andmeid edastama, kasutades ainult MOSI ja SCK. Liigume jaotisse 25.3.4 (poolduplekskommunikatsioon), kus leiame 1 taktsignaal ja 1 ühesuunaline andmejuhe (1 taktsignaal ja 1 ühesuunaline andmevoog):

Selles režiimis kasutab rakendus SPI-d kas ainult edastus- või ainult vastuvõturežiimis. / Ainult edastusrežiim sarnaneb dupleksrežiimiga: andmed edastatakse edastuspinni (MOSI meisterrežiimis või MISO orjarežiimis) kaudu, samas kui vastuvõtupin (MISO või MOSI vastavalt) võib kasutada tavalise sisend-/väljundpinni kui rakendus ignoreerib Rx-puhvrit (kui seda lugeda, ei ole seal edastatud andmeid).
Suurepärane, pin MISO on meil vaba, ühendame selle signaali LAT külge. Järgmine on Slave Select, mida STM32-s saab programmeerimise teel juhtida, mis on erakordselt mugav. Loeme sama nime kandvat lõiku jaotisest 25.3.1 SPI Üldine kirjeldus:

Programmiline juhtimine NSS (SSM = 1) / Teave orja valimise kohta on bitis SSI registris SPI_CR1. Väline pin NSS jääb rakenduse teiste vajaduste tarbeks vabaks.
On aeg registreid kirjutada. Olen otsustanud kasutada SPI2, otsime selle põhiaaluse aadressi - jaotisest 3.3 Mälukaart:
![]()
Nüüd alustame:
#define _SPI2_(mem_offset) (*(volatile uint32_t *)(0x40003800 + (mem_offset)))Avame jaotis 25.3.3 kõlavalt nimetatuna „SPI seadistamine meistrirežiimis“:

1. Seadke serial interface taktsagedus bitide BR[2:0] abil registris SPI_CR1.
Register on koondatud sama nime kandvasse viidatud manuaali jaotisse. Aadressi nihke (Aadressi nihke) CR1 - 0x00, vaikimisi on kõik bitid lähtestatud (Lähtestamisväärtus 0x0000):

Bitid BR seadistavad kontrolleri taktsageduse jagurit, määrates seega sageduse, millega SPI töötab. STM32 sagedus on meil 72 MHz, LED-kontroller, vastavalt selle andmelehele, töötab sagedusega kuni 25 MHz, seega peab jagama 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. Seadke bitid CPOL ja CPHA, et määrata andmete edastamise ja serial interface taktimise suhe (vt skeemi lehel 240).
Kuna me siin loeme andmelehte, mitte skeeme, uurime pigem CPOL ja CPHA bittide tekstikirjeldust lehelt 704 (SPI Üldine kirjeldus):

Taktsignaali faas ja polaarsus
Bittide CPOL ja CPHA abil registris SPI_CR1 saab programmiliselt valida neli erinevat ajastusrežiimi. Bitt CPOL (taktsignaali polaarsus) juhib taktsignaali seisundit, kui andmeid ei edastata. See bitt juhib meistri ja orja režiime. Kui CPOL on lähtestatud, asub pin SCK vaikimise režiimis madalal tasemel. Kui bitt CPOL on seatud, asub pin SCK vaikimise režiimis kõrgel tasemel.
Kui bit CPHA (kellafaas) on seadistatud, toimib SCK-signaali teine langus tõukemestikuna ülemise bitiga (langev, kui CPOL on madal, või tõusev, kui CPOL on kõrge). Andmed fikseeritakse teisel kella muutusel. Kui bit CPHA on madal, siis on tõukemestikuna ülemise bitiga SCK-signaali esmane langus (langev, kui CPOL on kõrge, või tõusev, kui CPOL on madal). Andmed fikseeritakse esimesel kella muutusel.
Saades neid teadmisi, jõuame järeldusele, et mõlemad bitid peavad jääma nulliks, et SCK-signaal jääks madalaks, kui seda ei kasutata, ja andmed edastatakse impulsi esimesel tõusul (vt Rising Edge dataleht DM634).
Muide, siin kohtame esmakordselt ST andmelehtedes leksika eripära: fraas «reset a bit» - kirjutatakse to reset a bit, mitte to clear a bit, nagu näiteks Atmega puhul.
3. Seadistage DFF-bit 8-bitise või 16-bitise andmeploki vormingu määramiseks
Võtsin teadlikult 16-bitise DM634, et mitte vaeva näha 12-bitiste PWM-andmete edastamisega nagu DM633 puhul. DFF-i on mõistlik seada ühte:
#define DFF 0x0800
_SPI2_ (_SPI_CR1) |= DFF; // 16-bit mode4. Konfigureerige LSBFIRST-bit registeris SPI_CR1 ploki vormingu määramiseks
LSBFIRST, nagu nimest nähtub, seadistab edastamise noorema bitiga ees. Kuid DM634 soovib andmeid saada alguses vanema bittide järgi. Seega jätame selle madalaks.
5. Riistvararežiimis, kui on vaja sisendit pin NSSI-l, tooge NSSI-pinnile kõrge signaal kogu baitide edastamise ajal. Tarkvaras seadke register SPI_CR1 bittide SSM ja SSI. Kui pin NSSI peab töötama väljundina, tuleb seada ainult bit SSOE.
Seame SSM ja SSI, et unustada riistvararežiim NSSI:
#define SSI 0x0100
#define SSM 0x0200
_SPI2_ (_SPI_CR1) |= SSM | SSI; //enable software control of SS, SS high6. Peavad olema seadistatud bitid MSTR ja SPE (need jäävad seadistatuks ainult siis, kui NSSI-le antakse kõrge signaal)
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 baite draiverile. Jätkame lugemist 25.3.3 «SPI seadistus meistrirežiimis»:

Andmete edastusjärjekord
Edastus algab, kui Tx-puhvrisse kirjutatakse bait.
Andmebait laaditakse nihkeregistrisse paralleelselt (sisemise bussiga) esimese bitiga edastamise ajal, pärast mida edastatakse see järgnevate bittidena. MOSI režiimis, esimese või viimase bitiga edasi, olenevalt LSBFIRST bitist CPI_CR1 registris. TXE lipp seadistatakse pärast andmete edastamist Tx puhvrist nihkeäregistrisse, samuti luuakse häire, kui TXEIE bit CPI_CR1 registris on seadistatud.
Olen tõlkes esile tõstnud mõned sõnad, et juhtida tähelepanu SPI rakenduse eripärale STM kontrollerites. Atmega puhul seadistatakse TXE lipp (Tx tühjaks, Tx on tühi ja valmis andmeid vastu võtma) alles pärast seda, kui kogu bait on saadetud välja. Siin seadistatakse see lipp pärast seda, kui bait on sisestatud sisemisse nihkeäregistrisse. Kuna see sisestatakse seal kõigi bittidega korraga (paralleelselt), edastatakse andmed seejärel järjestikku, siis TXE seadistatakse enne, kui bait on täielikult saadetud. See on oluline, kuna meie LED-juhtimise jaoks peame LAT pinni tõmbama pärast andmete edastamist, kõigi st ainult TXE lipp ei ole piisav.
See tähendab, et meil on vaja veel ühte lippu. Vaadake 25.3.7 – „Oleku lipud“:

<…>

BUSY lipp
BSY lipp seadistatakse ja lähtestatakse riistvara kaudu (sellele kirjutamine ei mõjuta midagi). BSY lipp näitab SPI kommunikatsioonikihtide olekut.
See on lähtestatud:
kui edastus on lõppenud (välja arvatud meistrirežiimis, kui edastus on pidev)
kui SPI on välja lülitatud
kui meistrirežiimi viga toimub (MODF=1)
Kui edastus ei ole pidev, lähtestatakse BSY lipp iga andmeedastuse vahel
Okei, see on kasulik. Uurime, kus asub Tx puhver. Selleks loeme „SPI andmeregister“:

Bitid 15:0 DR[15:0] Andmeregister
Saadud andmed või edastatavad andmed.
Andmeregister on jagatud kaheks puhvriks – üks kirjutamiseks (edastuspuhver) ja teine lugemiseks (vastuvõtupuhver). Andmeregistrisse kirjutamine kirjutab Tx puhvri, lugemine andmeregistrist tagastab väärtuse, mis on salvestatud Rx puhvrisse.
Ja ka oleku register, kus leidub TXE ja BSY lipud:

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 me peame edastama 16 korda kaks bitti, LED-juhtija väljundite arvu järgi, siis midagi sellist:
void sendLEDdata()
{
LAT_low();
uint8_t k = 16;
do
{ k--;
dm_shift16(leds[k]);
} while (k);
while (_SPI2_(_SPI_SR) & BSY); // lõpetage edastus
LAT_pulse();
}Aga me ei oska LAT pinni tõmmata, seega naaseme I/O juurde.
Määrame pinid
STM32F1 registrid, mis vastutavad pinni oleku eest, on üsna eriskummalised. On selge, et neid on rohkem kui Atmegi omasid, kuid nad erinevad ka teiste STM kiipide omadest. Jaotis 9.1 Üldine ülevaade GPIO:

Iga universaalse sisendi/väljundi port (GPIO) on varustatud kahe 32-bitise konfiguratsiooniregistriga (GPIOx_CRL ja GPIOx_CRH), kahe 32-bitise andmeregistriga (GPIOx_IDR ja GPIOx_ODR), 32-bitise seade-/lülitusregistriga (GPIOx_BSRR), 16-bitise lähtestamisregistriga (GPIOx_BRR) ja 32-bitise lukustusregistriga (GPIOx_LCKR).
Need on eriskummalised ja samuti üsna ebamugavad, siin on esimesed kaks registrit, kuna 16 pinni porti on neis laiali jaotatud «neli bitti vennana» formaadis. St. pinnid nullist seitsmendani asuvad CRL-is, ülejäänud aga CRH-is. Samas mahtuvad teised registrid kenasti kõigi porti pinni bittidega – sageli jäädes poolenisti «reserveeritud».
Lihtsuse huvides alustame nimekirja lõpust.
Lukustusregistrit meil ei ole vaja.
Seade- ja lülitusregistrid on üsna huvitavad, kuna nad osaliselt dubleerivad üksteist: kõik saab kirjutada ainult BSRR-isse, kus üle 16 biti lülitavad pinni nulli, ja madalamad 1-sse, või kasutada ka BRR-i, mille madalamad 16 bitti ainult lähtestavad pinni. Mulle meeldib teine variant. Need registrid on olulised, kuna tagavad atomaarse juurdepääsu pinidele:

![]()
Atomaarne seadmine või lähtestamine
Ei ole vaja katkestusi välja lülitada, kui programmeeritakse GPIOx_ODR bitti tasemel: saab muuta ühte või mitut bitti ühe atomaarse kirjutamise operatsiooniga APB2. See saavutatakse «1» kirjutamisega seadmis-/lülitusregistrisse (GPIOx_BSRR või, ainult lähtestamiseks, GPIOx_BRR) bitti, mida on vaja muuta. Ülejäänud bitid jäävad muutmatuks.
Andmeregistrid on päris jutukad nimede poolest – IDR = Sisend Suunaregister, sisendi registr; ODR = Väljund Suunaregister, väljundi registr. Praeguses projektis ei ole neid meil vaja.
Ja lõpuks, juhtimisregistrid. Kuna meid huvitavad teise SPI pinnid, nimelt PB13, PB14 ja PB15, vaatame kohe CRH-d:

Ja näeme, et peame kirjutama midagi bittidesse 20. kuni 31. koht.
Oleme juba varem saanud aru, mida me pin'ide osas tahame, seega seekord ma ekraanipilti ei lisa, lihtsalt ütlen, et MODE määrab suuna (sisend, kui mõlemad bitid on 0) ja pin'ide kiirus (me vajame 50MHz, st mõlemad pin'id peavad olema „1”), samas kui CNF määrab režiimi: tavaline „tõmba-tõukamine” – 00, „alternatiivne” – 10. Nagu ülal näha, on kõigil pin'idel määratud kolmas allpool olev bit (CNF0), mis seab nad režiimi. ujuv sisend.
Kuna plaanin selle kiibiga veel midagi teha, olen mugavuse huvides defineerinud kõik võimalikud väärtused MODE ja CNF nii alumistele kui ka ülemistele kontrollregisteritele.
Nii see ongi.
#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 B-porti (baasiaadress – 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;Seega on võimalik kirjutada defineeringud LAT'ile, mis toimib BRR ja BSRR registritega:
/*** 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, selline on alati olnud, las jääb sinna)
Nüüd on kõik juba suurepärane, aga siiski ei tööta. Sest see on STM32, siin ökonoomitakse elektrit, seega tuleb sisse lülitada vajaliku perifeeria kell.
Lülitame sisse kellatöö
Kella eest vastutavad kellad, see on Clock. Ja me oleme juba märganud akronüümi RCC. Otsime selle dokumentatsioonist: see on Reset and Clock Control (Taaskäivitamise ja kella juhtimine).
Kuna ülal on öeldud, et õnneks on STM inimestelt kõige keerulisema kellatöö teema meie eest lahendanud, mille eest neile suur aitäh (veel kord annan lingi) , et oleks selge, kui keeruline see on). Me vajame vaid registreid, mis vastutavad perifeeria kellatöö sisse lülitamise eest (Peripheral Clock Enable Registers). Alustuseks leiame RCC põhialuse, see on "Mälu kaartide" alguses:
![]()
#define _RCC_(mem_offset) (*(volatile uint32_t *)(0x40021000 + (mem_offset)))Ja edasi kas klõpsata lingile, kus proovida tabelist midagi leida, või veel parem, vaadata sisse lülitavate registeride kirjeldusi osadesse, mis käsitlevad lülitusregistreid. Siit leiame RCC_APB1ENR ja RCC_APB2ENR:


Ja neis on vastavalt bitid, mis aktiveerivad SPI2, IOPB (I/O Port B) ja alternatiivsed funktsioonid (AFIO) kellatöö.
#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 leiame .
Kui on võimalus ja soov testida, siis ühendame DM634 järgmiselt: DAI PB15-ga, DCK PB13-ga, LAT PB14-ga. Toitke draiverit 5 volti, ärge unustage ühendama maapindu.

STM8 PWM
PWM STM8-l
Kui ma seda artiklit alles plaanisin, otsustasin proovida mõnda mulle tundmatut kiipi lihtsalt andmete lehe abil, et mitte olla kingsepp, kellel pole kingi. STM8 sobis selleks ideaalselt: kõigepealt oli mul paar Hiina plaati STM8S103-ga, ja teiseks, ta ei ole liiga populaarne, seetõttu on kiusatus Googeldada ja leida lahendus internetis piiratud nende lahenduste puudumisega.
Kiibile on ka ja , esimeses on joonised ja registri aadressid, teises - kõik muu. STM8 programmeeritakse C-s kohatus IDE-nes .
Kellamine ja sisendi-väljundi
Vaikimisi töötab STM8 sagedusel 2 MHz, seda tuleb kohe muuta.

Kellasiini HSI (kõrge sisemine)
HSI kellasiin saadakse sisemisest 16 MHz RC-generaatorist, millel on programmeeritav jagaja (1 kuni 8). See määratakse kellasiini jagaja registris (CLK_CKDIVR).
Märkus: alguses valitakse peamiseks kellasiini allikaks HSI RC-generaator, mille jagaja on 8.
Otsime registri aadressi andmelehelt, kirjelduse refman-ist ja näeme, et register tuleb puhastada:
#define CLK_CKDIVR *(volatile uint8_t *)0x0050C6
CLK_CKDIVR &= ~(0x18);Kuna kavatseme käivitada PWM-i ja ühendada LED-id, vaatame jooniseid:

Kiip on väike, paljud funktsioonid on ühe ja sama pesaga. See, mis on ruudukestesse pandud, on „alternatiivne funktsioon”, mida vahetatakse „valikubaitide” (option bytes) kaudu - midagi sarnast Atmega fusiinidega. Nende väärtusi saab programmiliselt muuta, kuid seda ei ole vaja, kuna uus funktsioon aktiveeritakse alles pärast taaskäivitamist. Lihtsam on kasutada ST Visual Programmert (mis installitakse koos Visual Developiga), mis oskab neid bite muuta. Joonistel on näha, et esimese ajasturi väljundid CH1 ja CH2 on ruudukestesse peidetud; STVP-s tuleb seadistada AFR1 ja AFR0 bitid, teine ühtlasi viib CH1 väljundi teise ajasturi PD4-lt PC5-le.
Seega hakkavad LED-e juhtima 6 pesad: PC6, PC7 ja PC3 esimese ajasturi jaoks, PC5, PD3 ja PA3 teise jaoks.
STM8 sisendi-väljundi seadistamine on lihtsam ja loogilisem kui STM32-l:
- tuttav Atmega andmesuunaregister DDR (Data Direction Register): 1 = väljund;
- esimene kontrollregistr CR1 väljundis määrab režiimi „tõukamine-tõmbamine” (1) või avatud äravool (0); kuna ma ühendan LED-id kiibi katoodidega, jätan siia nullid;
- teine kontrollregistr CR2 väljundis annab määratud 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 seadistamine
Alustame terminitega:
- PWM sagedus – sagedus, millega taimer tiksub;
- Auto-reload, AR – automaatne laadimisväärtus, mille kuni taimer arvutab (impulsi periood);
- Update Event, UEV – sündmus, mis toimub, kui taimer jõuab AR väärtuseni;
- PWM täitmisosa – PWM täitmisaste, mida sageli nimetatakse "käikude suhteks";
- Capture/Compare Value – väärtus, mida kasutatakse kirjutamiseks/võrdlemiseks, mille kuni taimer teeb midagi (PWM korral – invertib väljundsignaali);
- Preload Value – eelnevalt laaditud väärtus. Võrdlusväärtus ei tohi muutuda, kui taimer tiksub, vastasel juhul rikutakse PWM tsüklit. Seetõttu tõstetakse uued edastatavad väärtused puhvri ja võetakse sealt välja, kui taimer jõuab arvestuse lõpuni ja lähtestatakse;
- Serva joondatud ja Keskelt joondatud režiimid – serva ja keskuse joondamine, sama mis Atmelile Kiire PWM ja Faasi õige PWM.
- OCiREF, väljundvõrdlus-signaal – viidatud väljundsignaal, tegelikult see, mis PWM režiimis antakse vastavale pinile.
Kuna on selge, et PWM võimeid on kahe taimeriga – esimene ja teine. Mõlemad on 16-bitised, esimene omab palju täiendavaid funktsioone (sealhulgas oskab lugeda üles ja alla). Meil on vaja, et mõlemad töötaksid ühtemoodi, seetõttu otsustasin alustada tahtlikult vaesemast teisest, et juhuslikult mitte kasutada midagi, mida seal ei ole. Teatud probleem on see, et kõikide taimerite PWM funktsionaalsuse kirjeldus on viidatud manuaalis esimeses taimeri osas (17.5.7 PWM režiim), seega tuleb kogu aeg dokumenti üles-alla hüpata.
STM8 PWM-l on Atmel PWM-i ees oluline eelis:

Serva joondamiseks mõeldud PWM
Alates allapoole lugemisseade
Allapoole lugemine on aktiivne, kui DIRE täpp TIM_CR1 registris on lähtestatud
Näide
Näide kasutab esimest PWM režiimi. PWM OCiREF viidatud signaal hoitakse kõrgel tasemel, kui TIM1_CNT < TIM1_CCRi. Muidu see võtab madala taseme. Kui võrdlusväärtus registris TIM1_CCRi on suurem kui automaatne laadimisväärtus (register TIM1_ARR), signaal OCiREF hoitakse 1 peal. Kui võrdlusväärtus on 0, siis OCiREF hoitakse 0-l.…
STM8 taimeri ajal uuendussündmus esiteks kontrollib võrdlusväärtus, ja alles siis väljastab viidatud signaali. Atmeli taimer kõigepealt genereerib signaali ja siis võrdleb, mille tulemusena juhul, kui võrdlusväärtus == 0 väljub nõel, millega tuleb kuidagi võidelda (näiteks, programmiliselt loogikat inverted).
Nii, mida me tahame teha: 8-bitine PWM (AR == 255), lugedes alt ülespoole, serva järgi joondamine. Kuna lambid on ühendatud kiibi katoodidega, peab PWM andma 0 (LED põleb) kuni võrdlusväärtus ja 1 pärast.
Oleme juba lugenud teatud PWM režiimist, seega leiame vajaliku teise taimeri registra, otsides selle fraasi reference manualist (18.6.8 – TIMx_CCMR1):

110: Esimene PWM režiim – lugedes alt üles, on esimene kanal aktiivne, seni kuni TIMx_CNT < TIMx_CCR1. Vastasel juhul ei ole esimene kanal aktiivne. [edasi dokumendis vale copy-paste taimerist 1]
111: Teine PWM režiim – lugedes alt üles, ei ole esimene kanal aktiivne, seni kuni TIMx_CNT < TIMx_CCR1. Vastasel juhul on esimene kanal aktiivne.
Kuna LED-id on ühendatud mikrokontrolöri katoodidega, sobib meile teine režiim (esimene ka, kuid me ei tea seda veel).

Bitt 3 OC1PE: Lülita sisse väljundi 1 eelkoormus
0: Eelkoormuse register TIMx_CCR1 on välja lülitatud. TIMx_CCR1-sse saab kirjutada igal ajal. Uus väärtus töötab kohe.
1: Eelkoormuse register TIMx_CCR1 on sisse lülitatud. Lugemis-/kirjutamisoperatsioonid pöörduvad eelkoormuse registri poole. Eelkoormatud väärtus TIMx_CCR1 laaditakse varjatud registrisse iga uuenduse sündmuse ajal.
*Märkus: PWM režiimi korrektseks tööks peavad eelkoormuse registrid olema sisse lülitatud. See ei ole vajalik üksiku signaali režiimis (registris TIMx_CR1 on OPM bitt seatud).
Selge, 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, serva järgi joondamine, midagi muuta ei ole vaja. Seame sageduse jaguri näiteks 256. Teise taimeri jagur seatakse registris TIM2_PSCR ja see on kahe astme:
#define TIM2_PSCR *(volatile uint8_t *)0x00530E
TIM2_PSCR = 8;Jäänud on lülitada sisse väljundid ja ise teine taimer. Esimene ülesanne lahendatakse registreid Capture/Compare Enable: neid on kaks, kolm kanalit on jaotatud asümmeetriliselt. Siin saame ka teada, et signaali polariteeti on võimalik muuta, st põhimõtteliselt oleks saanud kasutada ka PWM Režiim 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 registris TIMx_CR1:

#define TIM2_CR1 *(volatile uint8_t *)0x005300
TIM2_CR1 |= 1;Kirjutame lihtsa analooge AnalogWrite()-ist, mis edastab taimerile tegelikult võrdluse väärtused. Registrid nimetatakse eelnevalt nagu Capture/Compare registers, neist on kaks iga kanali kohta: nooremad 8 bitti TIM2_CCRxL ja vanemad TIM2_CCRxH. Kuna me kasutasime 8-bitist PWM-i, piisab ainult nooremate bitide 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;
}Hoolikas lugeja märkab, et meil on tekkinud pisut defektne PWM, mis ei suuda anda 100% täituvust (maksimaalse väärtuse 255 korral signaal invertitakse ühe ajastus tsükli jooksul). LED-ide jaoks ei oma see tähtsust, ja tähelepanelik lugeja juba mõistab, kuidas seda parandada.
PWM teisel ajastil töötab, liigume esimese juurde.
Esimene ajasti omab täpselt samu bitte sama registrite kohta (lihtsalt need bitid, mis jäävad "reserveeritud" teisele ajastile, on esimeses aktiivselt kasutusel erinevate edasijõudnute asjade jaoks). Seega piisab, kui leida need registrite aadressid andmelehelt ja kopeerida kood. Ja muuta ka sageduse jaguri väärtust, kuna esimene ajast peab saama mitte kahe astme väärtuse, vaid täpselt 16-bitise väärtuse kahele registrile. Prescaler High ja Madal. Teeme kõik nii ja... esimene ajasti ei tööta. Mis on viga?
Probleemi saab lahendada ainult vaatamata kogu ajasti 1 juhtimisregisteride osa, kus otsime seda, mida teisel ajastil pole. Leiame 17.7.30 Break register (TIM1_BKR), kus on selline bitte:
![]()
Lülita sisse peamine väljund
#define TIM1_BKR *(volatile uint8_t *)0x00526D
TIM1_BKR = (1<<7);Nüüd on kõik kindlasti korras, kood .

STM8 Multiplex
Múltipleximine STM8-l
Kolmas mini-projekt seisneb selles, et ühendada teise ajasti PWM-režiimis kaheksa RGB-LED-i ja sundida neid näitama erinevaid värve. Aluseks on LED-múltipleximise kontseptsioon, mille kohaselt, kui süüdata ja kustutada LED-e väga kiiresti, tundub meile, et nad põlevad pidevalt (visioni püsimine, visuaalse tajumise inertsi tõttu). Kunagi tegin .
Töö algoritm näeb välja selline:
- ühendame esimese RGB LED-i anod;
- süüdata see, andes vajalikud signaalid katoodidele;
- ootame PWM-i tsükli lõppemiseni;
- ühendame teise RGB LED-i anod;
- süüdata see...
Ja nii edasi. Loomulikult, et ilusalt töötada, peab anoodi ühendamine ja LED-i "süüdamine" toimuma samaaegselt. Noh, või peaaegu. Igatahes peame kirjutama koodi, mis annab teisel ajastil kolmele kanalile väärtused, ja UEV-i saavutamisel muudab neid ning samal ajal muudab aktiivset RGB-LED-i.
Kuna LED-i vahetus toimub automaatselt, tuleb luua „videomälu“, kust katkestuse töötleja andmeid saab. See on lihtne massiiv:
uint8_t colors[8][3];Konkreetse LED-i värvi muutmiseks piisab, kui salvestada sellesse massiivi vajalikud väärtused. Aktiivse LED-i numbrit haldab muutujat
uint8_t cnt;Demux
Õige multiplexerimise jaoks vajame, nagu poleks uskunud, demultiplexerit CD74HC238. Demultiplexer on kiip, mis rakendab operaatorit <<. Kaugemal kolmest sisende pinnit (bitid 0, 1 ja 2) edastame talle kolmikbitise arvu X, ja ta aktiveerib väljundi numbri (1<<X). Kiibi ülejäänud sisendid on vajalikud kogu konstruktsiooni skaleerimiseks. See kiip on vajalik mitte ainult mikrokontrolleri hõivatud pin-numbri vähendamiseks, vaid ka ohutuse tagamiseks - et mitte kogemata aktiveerida rohkem LED-e, kui lubatud, ja mitte mikrokontrollerit rikkuda. Kiip maksab vähe, seega tasub see koju apteeki alati hoida.
CD74HC238 vastutab selle eest, et anda pinget vajaliku LED-i anoodile. Täismultiplexis annaks ta pinget veergu läbi P-MOSFET-i, kuid selles demo-s võib otse, kuna see tarbib 20 mA, vastavalt absolute maximum ratings andmelehtedest. Andmelehe vajab meilt pinout'i ja seda meelespead:

H = kõrge pingetase, L = madal pingetase, X - pole vahet
Ühendame E2 ja E1 maapinnaga, E3, A0, A1 ja A3 STM8 pinna PD5, PC3, PC4 ja PC5 juurde. Kuna ülemine tabel sisaldab nii madalat kui ka kõrget taset, seadistame need pinnad kui push-pull väljundid.
PWM
Teises ajastija seadistamine toimub samamoodi nagu eelnevas loos, kahe erandiga:
Esiteks peame lubama katkestuse Update Event (UEV), mis kutsuks esile funktsiooni, mis vahetab aktiivset LED-i. Selleks muudetakse bitti Update Interrupt Enable registris, mille nimi on kõnekas

Katkestusregistreerimise registreerimine
#define TIM2_IER *(volatile uint8_t *)0x005303
//enable interrupt
TIM2_IER = 1;Teine erinevus on seotud sellise nähtusega nagu multiplexing, mida nimetatakse ghosting – diodide parasiitne säde. Meie puhul võib see ilmneda, kuna taimer, mis kutsub esile katkemise UEV-l, tiksub edasi ja katkestuse käsitleja ei jõua LED-i lülitada enne, kui taimer juba hakkab midagi väljunditesse kirjutama. Selle vastu võitlemiseks tuleb loogikat inversioonida (0 = maksimaalne heledus, 255 = mitte midagi ei põle) ja vältida signaali äärmuslikke väärtusi. See tähendab, et tuleb saavutada, et pärast UEV'd LED-id täielikult kustuksid ühe PWM-i takti jooksul.
Muudame polaarsust:
//set polarity
TIM2_CCER1 |= (CC1P | CC2P);
TIM2_CCER2 |= CC3P;Vältime r, g ja b seadmist väärtuseks 255 ning ei unusta neid inversioonida kasutamisel.
Katkestused
Katkestuse olemus on see, et teatud tingimustel lõpetab kiip põhiprogrammi täitmise ja kutsub esile mõne välishart. Katkestused tekivad välistest või sisemistest mõjudest, sh taimerist.
Kui me esmakordselt projekti ST Visual Develop'is lõime, siis peale main.c saime akna salapärase failiga stm8_interrupt_vector.c, mis oli automaatselt projekti lisatud. Selles failis on iga katkestuse jaoks seotud funktsioon NonHandledInterrupt. Me peame oma funktsiooni siduma vajaliku katkestusega.
Andmelehel on katkestuste vektorite tabel, kus me leiame vajalikud:

13 TIM2 uuendus/täiendamine
14 TIM2 haaramine/võrdlus
Peame LED-i muutma UEV ajal, seega on vajalik katkestus nr 13.
Seega, kõigepealt, failis stm8_interrupt_vector.c muudame katkestuse nr 13 (IRQ13) vaikefunktsiooni nime oma nimeks:
{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, peab selle funktsiooni oma main.c:
@far @interrupt void TIM2_Overflow(void)
{
PD_ODR &= ~(1<<5); // lülitame demultiplexer välja
PC_ODR = (cnt<<3); // kirjutame demultiplexerisse uue väärtuse
PD_ODR |= (1<<5); // lülitame demultiplexer sisse
TIM2_SR1 = 0; // lähtestame Update Interrupt Pending lipu
cnt++;
cnt &= 7; // liigutame LED-i loenduri
TIM2_CCR1L = ~colors[cnt][0]; // edastame puhvrisse inversioonitud väärtused
TIM2_CCR2L = ~colors[cnt][1]; // järgmiseks PWM-i tsükliks
TIM2_CCR3L = ~colors[cnt][2]; //
return;
}Jäänud on katkestuste aktiveerimine. Seda tehakse assembleri käsuga rim – seda tuleb otsida :
//enable interrupts
_asm("rim");Teine assembleri käsk – sim – lülitab katkestused välja. Need tuleb välja lülitada uute väärtuste 'videomälu' kirjutamise ajaks, et ebasoodsal hetkel esile kutsutud katkestus ei rikuks massiivi.
Kood on .

Kui see artikkel kellelegi kasulik on, siis ei ole ma seda asjata kirjutanud. Ootan meeleldi kommentaare ja märkusi, üritan kõigile vastata.
Allikas: habr.com
