
A Megpróbáltam elmagyarázni a hobbi elektronikának szentelt közönségnek, akik az Arduino nadrágokból nőttek ki, hogy miért és hogyan érdemes adatlapokat és egyéb dokumentációt olvasniuk a mikrovezérlőkről. A szöveg hosszúra sikerült, ezért megígértem, hogy gyakorlati példákat mutatok be egy külön cikkben. Nos, itt vagyunk...
Ma megmutatom, hogyan lehet az adatlapok segítségével megoldani néhány egyszerű, de sok projekt számára szükséges feladatot STM32 (Blue Pill) és STM8 mikrovezérlőkön. Minden bemutatóprojekt a kedvenc LED-jeimnek szól, amelyeket nagy mennyiségben fogunk működtetni, ezért érdekes perifériákat kell igénybe vennünk.
A szöveg ismét hatalmas lett, ezért a könnyebb áttekinthetőség érdekében készítek egy tartalomjegyzéket:
Kizárás: nem vagyok mérnök, nem igénylem a mély elektronikai ismereteket, ez a cikk olyanoknak szól, mint én. Valójában a célcsoportom a két évvel ezelőtti énem volt. Ha akkor valaki azt mondta volna, hogy nem ijesztőolvasni az ismeretlen chip adatlapját, nem töltöttem volna annyi időt a neten kutatva kóddarabok után és próbálkozva különféle trükkökkel.
A cikk középpontjában az adatlapok állnak, nem a projektek, ezért a kód nem biztos, hogy nagyon rendezett és gyakran trükkös. Maguk a projektek nagyon egyszerűek, bár alkalmasak az új chip első megismerésére.
Remélem, hogy a cikkem segít valakinek hasonló hobbiba merülni.
STM32
16 LED DM634 és SPI segítségével
Egy kis projekt Blue Pill (STM32F103C8T6) és a DM634 LED meghajtó felhasználásával. Az adatlapok segítségével megérthetjük a meghajtót, az STM IO portjait, és beállítjuk az SPI-t.
DM634
Tajvani chip 16 db 16-bites PWM kimenettel, láncokba köthető. Az alacsonyabb, 12-bites modell a hazai projekt keretében ismert. . Amikor a DM63x és a jól ismert TLC5940 között választottam, a DM-re álltam át több okból: 1) a TLC külföldön valószínűleg hamisítvány, ez viszont nem az; 2) a DM-nek van önálló PWM-je a saját frekvenciagenerátorával; 3) olcsón meg tudtam venni Moszkvában, nem kellett várnom a küldeményre Kínából. Nos, és persze érdekes volt saját kezűleg megtanulni irányítani a chipet, nem csak egy kész könyvtárat használni. A chipek leginkább SSOP24 házban kaphatók, elég egyszerű őket egy adapterre forrasztani.
Mivel a gyártó tajvani, a chiphez kínai angolsággal íródott, ami azt jelenti, hogy szórakoztató lesz. Először is nézzük meg a pinelrendezést (Pin Connection), hogy megértsük, melyik lábra mit kell csatlakoztatni, és a pinok leírását (Pin Description). 16 kimenet:

Áramforrások (nyitott drain)
Sink / Nyitott drain kimenet – drain; áramforrás; aktív állapotban a földre csatlakozik – a LED-ek a meghajtóhoz katódjukkal kapcsolódnak. Elektronikusan, nos, ez nem igazán 'nyitott drain' (open drain), de az adatlapokon ezt a jelölést gyakran használják a drain módban lévő kimenetekre.
![]()
Külső ellenállások az REXT és a GND között a kimeneti áram értékének beállítására
Az REXT pin és a föld között egy referenciaellenállás van, amely ellenőrzi a kimenetek belső ellenállását, lásd a grafikonot az adatlap 9. oldalán. A DM634-nél ezt az ellenállást programozással is vezérelhetjük, beállítva a globális fényerőt (global brightness); ebben a cikkben nem merülnék el a részletekbe, csak ide teszem a 2.2 – 3 kOhm ellenállást.
Ahhoz, hogy megértsük, hogyan kell vezérelni a chipet, nézzük meg az eszköz interfészének leírását:

Hát igen, itt a kínai angol minden dicsőségében. Ezt lefordítani nehéz, de érteni lehet, ha akarjuk, de van egy másik út – nézzük meg, hogyan van leírva a csatlakozás az adatlapban egy funkcionálisan hasonló TLC5940 esetében:

… Az adatok eszközbe történő bejuttatásához csak három pin szükséges. Az SCLK jel előrejelező élje tolja át az adatokat a SIN pinről a belső regiszterbe. Miután minden adat betöltődött, egy rövid magas XLAT jel rögzíti a sorozatban átadott adatokat a belső regiszterekben. A belső regiszterek – a XLAT jel szintje alapján működő késleltetők. Minden adat a legmagasabb bit előre történik.
Latch – késleltető / zár / rögzítő.
Emelkedő él – az impulzus előrejelező éle
MSB először – a legnagyobb (legbaloldalibb) bit előre.
az óra adatokat – adatokat sorosan (bitenként) továbbít.
A szó latch gyakran előfordul a chipdokumenációban, és különbözőképpen fordítják, ezért a megértés érdekében engedjék meg, hogy
kicsit tanítsakaz LED-vezérlő alapvetően eltoló regiszter. Az eltolás (shift) a névben – a bitek belső eltolása a készüléken belül: minden újonnan betett bit az egész láncot maga előtt tolja előre. Mivel az eltolás alatt senki sem akarja látni a LED-ek zűrzavaros villogását, a folyamat pufferegiszterekben zajlik, amelyeket munkaként egy zár elválaszt (latch) – ez egyfajta előszoba, ahol a bitek a megfelelő sorrendbe állnak. Amikor minden kész, a zár kinyílik, és a bitek munkába állnak, felváltva a korábbi adagot. A szó latch a mikroszkémák dokumentációjában szinte mindig egy ilyen zárat jelent, függetlenül attól, hogy milyen kombinációkban használják.
Tehát az adatok átvitele a DM634-ben így történik: beállítjuk a DAI bemenetet a távoli LED legnagyobb bit értékére, fel-le rántjuk a DCK-t; beállítjuk a DAI bemenetet a következő bit értékére, rántjuk a DCK-t; és így tovább, amíg az összes bit át nem kerül (clocked in), ezután meg rántjuk a LAT-ot. Ezt meg lehet csinálni manuálisan (bit-bang), de jobban járunk, ha egy kifejezetten erre tervezett SPI interfészt használunk, amely szerencsére két példányban elérhető az STM32-n.
Kék Tabletta STM32F103
Bevezetés: az STM32 vezérlők lényegesen bonyolultabbak, mint az Atmega328, amitől megijedhetünk. Ezenkívül energiatakarékossági okokból az induláskor szinte az összes periféria ki van kapcsolva, és az óra frekvencia 8 MHz a belső forrásból. Szerencsére az STM programozói írtak egy kódot, amely a chipet a "számított" 72 MHz-ig emeli, és minden általam ismert IDE szerzője ezt belefoglalja az inicializálási eljárásba, így nekünk nem kell órajelt adni (de ). De a perifériákat be kell kapcsolnunk.
Dokumentáció: a Blue Pillen egy népszerű STM32F103C8T6 chip található, ehhez két hasznos dokumentum tartozik:
- az STM32F103x8 és STM32F103xB mikrokontrollerek számára;
- az STM32F103 családhoz és azon túl.
Az adatlapban az alábbiak lehetnek érdekesek számunkra:
- Pinouts – a chipek kivezetései – arra az esetre, ha úgy döntünk, hogy magunk készítünk nyomtatott áramköröket;
- Memória Térképe – memória térkép egy adott chiphez. A Reference Manual-ban található az egész sorozatra vonatkozó térkép, benne említve vannak azok a regiszterek, amelyek nincsenek a miénken.
- Pin Definíciók Táblázat – a pin-ek alapvető és alternatív funkcióinak felsorolása; az "kék tablettához" az interneten található kényelmesebb ábrák a pin-ek listájával és funkcióikkal. Ezért azonnal keressük a Blue Pill pinout-ot, és tartsunk kéznél egy ilyen képet:

NB: az interneten található képen hiba volt, amit a megjegyzésekben észrevettek, ezért köszönet érte. A kép cserélve lett, de ez egy lecke – az adatlapokon kívüli információt jobb ellenőrizni.
Az adatlapot eltávolítjuk, megnyitjuk a Reference Manual-t, ezentúl csak azt használjuk.
Módszerek: kezeljük a szabványos bemenetet/kimenetet, állítsuk be az SPI-t, kapcsoljuk be a szükséges perifériákat.
Bemenet-kimenet
Atmega328-on a bemenet-kimenet rendkívül egyszerűen valósul meg, ami miatt a STM32 rengeteg opciója zavaró lehet. Jelenleg csak a kimenetek szükségesek, de ezekből is négy lehetőség áll rendelkezésre:

nyitott kollektor, push-pull, alternatív push-pull, alternatív nyitott kollektor
"Push-pull" (push-pull) – a megszokott kimenet az Arduinón, a pin vagy HIGH, vagy LOW értéket vehet fel. De a "nyitott kollektorral" problémák adódnak , bár valójában ez minden egyszerű:


A kimenet konfigurációja / amikor a port kimenetre van beállítva: / aktiválva a kimeneti puffert: / - nyitott kollektor mód: „0” a kimeneti regiszterben aktiválja az N-MOS-t, „1” a kimeneti regiszterben hagyja a portot Hi-Z (a P-MOS nem aktiválódik) / - push-pull mód: „0” a kimeneti regiszterben aktiválja az N-MOS-t, „1” a kimeneti regiszterben aktiválja a P-MOS-t.
A nyitott kollektor (open drain) és a push-pull (push-pull) közötti minden eltérés az, hogy az elsőnél a pin nem tud HIGH állapotot felvenni: amikor egyet írunk a kimeneti regiszterbe, az magas ellenállású állapotba kerül (high impedance, Hi-Z). Amikor nullát írunk, a pin mindkét módban azonos módon viselkedik, logikailag és elektromosan is.
Normál kimeneti módban a pin egyszerűen közvetíti a kimeneti regiszter tartalmát. Az "alternatív" módban a megfelelő periféria irányítja (lásd 9.1.4):
![]()
Ha a port bitje alternatív funkcióként van konfigurálva, a kimeneti regiszter kikapcsol, és a pin csatlakozik a periféria kimeneti jeléhez.
Minden pin alternatív funkcióját a Pin Definíciók A datashit is available in the downloaded image. The footnote in the datashit answers the question of what to do if a pin has multiple alternative functions.
![]()
If several peripheral blocks use the same pin, only one peripheral block should be used at a time to avoid conflicts between alternative functions, switching with a peripheral activation bit (in the corresponding RCC register).
Finally, pins in output mode also have a clock speed. This is another energy-saving feature; in our case, we simply set it to maximum and forget about it.
So, we are using SPI, which means two pins (for data and the clock signal) should be 'alternative push-pull function', and one (LAT) should be 'normal push-pull'. But before assigning them, let's understand SPI.
SPI
A brief introduction
SPI, or Serial Peripheral Interface, is a simple and quite effective interface for communication between microcontrollers and the outside world. Its principle of operation has already been described above, where it talks about the Chinese LED driver (refer to section 25 in the reference manual). SPI can operate in master and slave modes. There are four basic channels in SPI, but not all of them may be used:
- MOSI, Master Output / Slave Input: this pin sends data in master mode and receives data in slave mode;
- MISO, Master Input / Slave Output: conversely, it receives in master mode and sends in slave mode;
- SCK, Serial Clock: sets the data transmission frequency in master mode or receives the clock signal in slave mode. Essentially, it ticks off bits;
- SS, Slave Select: this channel lets the slave know that someone wants something from it. On STM32, it's called NSS, where N = negative, meaning the controller becomes a slave if this channel goes to ground. It works well with the Open Drain Output mode, but that's a different story.
Like everything else, SPI on STM32 is rich in functionality, which complicates its understanding somewhat. For example, it can operate not only in SPI mode but also in I2S mode, and in the documentation, their descriptions are mixed together, necessitating timely pruning of unnecessary details. In our case, the task is extremely simple: we only need to send data using MOSI and SCK. Let's go to section 25.3.4 (half-duplex communication), where we find 1 clock and 1 unidirectional data wire (1 clock signal and 1 unidirectional data stream):

Ebben a módban az alkalmazás SPI-t használ, vagy csak adás, vagy csak vétel módban. Az adás mód hasonlít a duplex módra: az adatok az adó lábán (MOSI mester módban vagy MISO szolga módban) kerülnek továbbításra, míg a vételi láb (MISO vagy MOSI a megfelelő mód szerint) használható általános bemeneti-kimeneti lábként. Ebben az esetben az alkalmazásnak elegendő figyelmen kívül hagynia az Rx puffer, mivel ha elolvasásra kerül, nem lesznek átadott adatok benne.
Kiváló, a MISO lábunk szabad, csatlakoztassuk hozzá a LAT jelet. Foglalkozzunk a Slave Select-tel, amelyet az STM32-ben programozással lehet vezérelni, ami rendkívül kényelmes. Olvassuk el a 25.3.1 SPI Általános leírás című szakasz megfelelő bekezdését:

Programozott vezérlés NSS (SSM = 1) / A szolga kiválasztásának információja az SPI_CR1 regiszter SSI bitjében található. A külső NSS láb szabadon marad az alkalmazás egyéb igényeihez.
Ideje írni a regiszterekbe. Úgy döntöttem, hogy az SPI2-t használom,keressük a műszaki táblázatban az alapcímet – a 3.3. Memória térkép részben:
![]()
Nos, kezdjük:
#define _SPI2_(mem_offset) (*(volatile uint32_t *)(0x40003800 + (mem_offset)))Nyissuk meg a 25.3.3 szakaszt, amelynek beszédes címe van: "SPI beállítása mester módban":

1. Állítsa be a soros interfész órajel frekvenciáját a BR[2:0] bitek segítségével az SPI_CR1 regiszterben.
A regiszterek az azonos nevű szakaszban találhatók a referencia útmutatóban. Az adresz eltolás (Address offset) a CR1-nél – 0x00, alapértelmezés szerint minden bit alaphelyzetbe állítva (Reset value 0x0000):

A BR bitek a kontroller órajelének osztóját állítják be, így meghatározva a SPI működési frekvenciáját. Az STM32-nk frekvenciája 72 MHz lesz, a LED-vezérlő, a műszaki táblázata szerint, 25 MHz-ig működik, így négyre kell osztani (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. Állítsa be a CPOL és CPHA biteket, hogy meghatározza az adatok továbbítása és a soros interfész órajelének viszonyát (lásd a 240. oldalon található ábrát)
Mivel itt a műszaki táblázatot olvassuk, nem pedig ábrákat nézegetünk, inkább tanulmányozzuk a CPOL és CPHA bitek szöveges leírását a 704. oldalon (SPI Általános leírás):

Fázis és órajelek polaritása
A CPOL és CPHA bitek segítségével az SPI_CR1 regiszterben programozottan négy időzítési kapcsolatot választhatunk. A CPOL bit (órajelek polaritása) szabályozza az órajel állapotát, amikor az adatok nincsenek továbbítva. Ez a bit az mester és szolga módokat szabályozza. Ha a CPOL bit alaphelyzetbe van állítva, a SCK láb alacsony szinten van pihenő állapotban. Ha a CPOL bit be van állítva, a SCK láb magas szinten van pihenő állapotban.
Ha a CPHA bit be van állítva (órajel fázis), a legnagyobb bitet rögzítő kapu a SCK jel második élén van (eső érkező, ha a CPOL alacsony, vagy emelkedő, ha a CPOL magas). Az adatok a órajelek második változásakor rögzülnek. Ha a CPHA bit alacsony, a legnagyobb bitet rögzítő kapu a SCK jel első élén található (eső érkező, ha a CPOL magas, vagy emelkedő, ha a CPOL alacsony). Az adatok az órajel első változásakor rögzülnek.
Ezeket az ismereteket átnézve arra a következtetésre jutunk, hogy mindkét bitnek nullának kell maradnia, mivel azt akarjuk, hogy a SCK jel alacsony maradjon, amikor nem használják, és az adatok az impulzus előre emelkedő élén kerüljenek átvitelre (lásd: Rising Edge a DM634 adattáblázatban).
Egyébként itt találkozunk először egy ST adattáblázatokban előforduló kifejezési sajátossággal: az "bit nullára állítása" úgy van megadva, to reset a bit, nem pedig to clear a bit, például az Atmega esetében.
3. Állítsa be a DFF bitet az adatblokk 8 bites vagy 16 bites formátumának megállapításához.
Kifejezetten a 16 bites DM634-et választottam, hogy ne kelljen a 12 bites PWM adatok átvitelével foglalkoznom, mint a DM633 esetében. A DFF bit értelmesen 1-re állítani:
#define DFF 0x0800
_SPI2_ (_SPI_CR1) |= DFF; // 16-bit mode4. Állítsa be az LSBFIRST bitet az SPI_CR1 regiszterben az adatblokk formátumának megállapításához.
Az LSBFIRST, ahogyan a neve is mutatja, a legkisebb bit előre történő átvitelére konfigurál. De a DM634 azt akarja, hogy az adatokat a legnagyobb bittől kezdje. Ezért azt alacsonyan hagyjuk.
5. Hardveres módban, ha az NSS pinről történő bemenetre van szükség, az NSS pinre magas jelet kell adagolni a byte-ok átvitelének teljes szekvenciája alatt. Programozási módban az NSS-t úgy állítja be, hogy az SPI_CR1 regiszterben az SSM és SSI biteket beállítja. Ha az NSS pinnek kimeneti módban kell működnie, akkor csak az SSOE bitet kell beállítani.
Beállítjuk az SSM és SSI biteket, hogy elfelejtsük az NSS hardveres módját:
#define SSI 0x0100
#define SSM 0x0200
_SPI2_ (_SPI_CR1) |= SSM | SSI; //enable software control of SS, SS high6. Be kell állítani az MSTR és SPE biteket (ezek csak akkor maradnak beállítva, ha az NSS pinre magas jelet adunk).
Ezekkel a bitekkel határozzuk meg a SPI-t mint mestert és kapcsoljuk be:
#define MSTR 0x0004
#define SPE 0x0040
_SPI2_ (_SPI_CR1) |= MSTR; //SPI master
//когда все готово, включаем SPI
_SPI2_ (_SPI_CR1) |= SPE;A SPI be van állítva, nézzünk azonnal írásmódú funkciókat a bájtok vezérlőnek való küldésére. Folytassuk az olvasást a 25.3.3 "SPI beállítása mester módban":

Az adatok átvitelének sorrendje
Az átvitel akkor kezdődik, amikor egy bájt beírásra kerül a Tx pufferbe.
Az adat bájt betöltődik a shift regiszterbe a párhuzamos módban (a belső buszról) az első bit átvitele alatt, majd átkonvertálásra kerül a soros MOSI módban, az LSBFIRST bit CPI_CR1 regiszter beállításától függően az első vagy az utolsó bit kerül továbbításra. A TXE jelző adatátvitel után áll be. Tx pufferből a shift regiszterbe., és megszakítást is generál, ha a TXEIE bit be van állítva a CPI_CR1 regiszterben.
Kiemeltem néhány szót a fordításban, hogy felhívjam a figyelmet egy sajátosságra az SPI megvalósításában az STM vezérlőkben. Az Atmegán a TXE jelző (Tx üres, Tx üres és készen áll az adatok fogadására) csak akkor áll be, ha az egész byte elküldésre került. Külsőleg.. Itt azonban ez a jelző akkor áll be, amikor a byte bekerül a belső shift regiszterbe. Mivel az összes bit egyszerre (párhuzamosan) kerül be, az adatok pedig sorban kerülnek továbbításra, a TXE beállítása előtt történik meg a byte teljes elküldése. Ez fontos, mert a LED-vezérlőnk esetében a LAT pin megfeszítésére van szükség az összes adattal, tehát csak a TXE jelző nem lesz elegendő. Ez pedig azt jelenti, hogy szükségünk van még egy jelzőre. Nézzük meg a 25.3.7 – „Állapotjelzők” részt:
<…>

A BUSY jelző.

A BSY jelző hardveresen áll be és áll vissza (a rá való írás semmire nem hat). A BSY jelző az SPI kommunikációs réteg állapotát jelzi.
A következő esetekben áll vissza:
amikor az adatátvitel befejeződik (kivéve a mester üzemmódot, ha az átvitel folyamatos)
amikor az SPI le van állítva
amikor mester üzemmódbeli hiba lép fel (MODF=1)
Ha az átviteli mód nem folyamatos, a BSY jelző visszakerül minden adatátvitel között.
Rendben, ez hasznos lesz. Megnézzük, hol található a Tx puffer. Ehhez olvassuk el a „SPI adatregisztert”:
A 15:0 bitek DR[15:0] adatregiszter.

Az átvitt vagy küldendő adatok.
Az adatregiszter két pufferre van osztva – az egyik a írásra (küldés puffer) és a másik az olvasásra (fogadó puffer). Az adatregiszterbe írás a Tx pufferbe ír, míg az adatregiszterből való olvasás visszaadja a Rx pufferben tárolt értéket.
Na és a státuszregiszter, ahol megtalálható a TXE és BSY jelző:
Írjuk:

mivel 16 alkalommal kell átkonvertálni 2 byte-ot a LED-vezérlő kimenetei számának megfelelően, így valahogy így:
#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
}void sendLEDdata() { LAT_low(); uint8_t k = 16; do { k--; dm_shift16(leds[k]); } while (k);while (_SPI2_(_SPI_SR) & BSY); // befejezi az átvitelLAT_pulse(); }
De még nem tudjuk feszíteni a LAT pin-t, úgyhogy térjünk vissza az I/O-ra.Pinokat rendelünk.
Pinok kijelölése
Az STM32F1 regiszterei, amelyek a lábak állapotáért felelnek, meglehetősen különösek. Nyilvánvaló, hogy több van belőlük, mint az Atmegánál, de más STM chipekhez képest is eltérnek. 9.1 szakasz Általános GPIO leírás:

Minden általános célú bemeneti/kimeneti port (GPIO) két 32 bites konfigurációs regiszterrel (GPIOx_CRL és GPIOx_CRH), két 32 bites adatregiszterrel (GPIOx_IDR és GPIOx_ODR), egy 32 bites beállító/resetáló regiszterrel (GPIOx_BSRR), egy 16 bites resetelő regiszterrel (GPIOx_BRR) és egy 32 bites zároló regiszterrel (GPIOx_LCKR) rendelkezik.
Némi szokatlanság és elég kényelmetlen is a két első regiszter, mert a 16 láb eltérő mintázatban oszlik meg közöttük, „négy bitenként”. Tehát a nullától a hetedik láb a CRL-ben van, míg a többi a CRH-ban. Eközben a többi regiszter sikeresen elhelyezi az összes láb bitjeit – gyakran fele részben „fenntartották”.
A tisztaság kedvéért kezdjük a lista végéről.
A zároló regiszterre nincs szükségünk.
A beállító és resetelő regiszterek meglehetősen szórakoztatóak azzal, hogy részben megismétlik egymást: mindent csak a BSRR-be írhatunk, ahol a felső 16 bit a lábat nullára állítja, míg az alsó 1-re, vagy használhatjuk a BRR-t is, amelynek alsó 16 bitjei csak a lábat resetelik. Nekem a második változat tetszik. Ezek a regiszterek fontosak, mert atomikus hozzáférést biztosítanak a lábakhoz:

![]()
Atomikus beállítás vagy reset
Nincs szükség a megszakítások letiltására a GPIOx_ODR bit szintű programozásakor: egy vagy több bitet egy atomikus írási APB2 művelettel lehet módosítani. Ez azáltal érhető el, hogy „1”-et írunk a beállító/resetelő regiszterbe (GPIOx_BSRR vagy, csak resetelésre, a GPIOx_BRR-be) a megváltoztatni kívánt bithez. A többi bit változatlan marad.
Az adatregiszterek elnevezései önmagukért beszélnek – IDR = Bemeneti Irányító regiszter, a bemeneti regiszter; ODR = Kimeneti Irányító regiszter, a kimeneti regiszter. A jelenlegi projektben nincs szükségünk rájuk.
Nos, végül az vezérlő regiszterek. Mivel a második SPI lábai érdekelnek minket, nevezetesen PB13, PB14 és PB15, egyből a CRH-ra nézünk:

És látjuk, hogy valamit írni kell a 20. és 31. bitre.
Már korábban megértettük, hogy mit szeretnénk a lábakkal, ezért itt megkerülöm a képernyőképet, csak annyit mondok, hogy a MODE határozza meg az irányt (bemenet, ha mindkét bit 0-ra van állítva) és a láb sebességét (50 MHz-re van szükségünk, azaz mindkét láb "1"), míg a CNF a módot határozza meg: normál "távolítsd el" – 00, "alternatív" – 10. Alapértelmezés szerint, ahogyan fent láttuk, minden láb harmadik alatti bitje (CNF0) aktív. lebegő bemenet.
Mivel tervezek még valamit csinálni ezzel a chippel, egyszerűség kedvéért definiáltam az összes lehetséges MODE és CNF értéket mind a alsó, mind a felső vezérlőregiszterekre.
Nos, valahogy így
#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 0x20000000A lábaink a B porton találhatók (alap cím – 0x40010C00), a kód:
#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;És ennek megfelelően írhatunk definíciókat a LAT-hoz, ami a BRR és BSRR regisztereket aktiválja:
/*** LAT pulse – high, then low */
#define LAT_pulse() _PORTB_(_BSRR) = (1<<14); _PORTB_(_BRR) = (1<<14)
#define LAT_low() _PORTB_(_BRR) = (1<<14)(A LAT_low csak úgy megmaradt, mint régen, hadd maradjon ez is.)
Most már minden szuper, csak éppen nem működik. Mert ez STM32, itt takarékoskodnak az energiával, ezért be kell kapcsolni a szükséges periféria órajelét.
Kapcsoljuk be az órajelet
Az órajelet az órák, más néven Clock vezérlik. És már észlelhettük az RCC rövidítést. Keressük meg a dokumentációban: ez a Reset and Clock Control (Reset és órajel vezérlés).
Ahogy fent említettem, szerencsére a legnehezebb feladatot az STM emberei végezték helyettünk, ezért nagy köszönet nekik (még egyszer megadom a linket a , hogy érthető legyen, mennyire összetett ez). Csak a periféria órajelének aktiválásáért felelős regiszterekre van szükségünk (Periféria Órajel Engedélyező Regiszterek). Először is keressük meg az RCC alap címét, ezt az "Memória Térkép" elején találjuk:
![]()
#define _RCC_(mem_offset) (*(volatile uint32_t *)(0x40021000 + (mem_offset)))Ezután vagy rákattintunk a linkre, ahol próbálunk valamit találni a táblázatban, vagy, sokkal jobb, átfutunk az aktiváló regiszterek leírásain, amelyek a engedélyező regiszterekhez: ahol megtaláljuk az RCC_APB1ENR és RCC_APB2ENR-t:


És ezekben ezután a bitjei, amelyek aktiválják az SPI2, IOPB (I/O Port B) és alternatív funkciók (AFIO) órajeleit.
#define _APB2ENR 0x18
#define _APB1ENR 0x1C
#define IOPBEN 0x0008
#define SPI2EN 0x4000
#define AFIOEN 0x0001
//включаем тактирование порта B и альт. функций
_RCC_(_APB2ENR) |= IOPBEN | AFIOEN;
//включаем тактирование SPI2
_RCC_(_APB1ENR) |= SPI2EN;A végső kódot itt találjuk .
Ha van lehetőség és kedv, a DM634-et így csatlakoztathatjuk: DAI a PB15-höz, DCK a PB13-hoz, LAT a PB14-hez. A meghajtót 5 voltos tápellátással tápláljuk, ne felejtsük el összehozni a földeket.

STM8 PWM
PWM az STM8-on
Amikor először terveztem ezt a cikket, úgy döntöttem, hogy példaként megpróbálom elsajátítani egy ismeretlen chip egy funkcióját csupán a datasheet alapján, hogy ne legyek cipész, akinek nincsenek cipői. Az STM8 tökéletesen megfelelt ennek a szerepnek: egyrészt volt néhány kínai PCB-m STM8S103-mal, másrészt nem túl népszerű, így a neten való megoldáskeresés kísértése abba ütközik, hogy nem találok megoldást.
A chiphez tartozik és , az első a lábkiosztás és a regiszterek címei, a második – minden más. Az STM8-at C nyelven programozzák egy ronda IDE-ben, .
Órajel és bemenet-kimenet
Alapértelmezés szerint az STM8 2 MHz frekvencián működik, ezt azonnal módosítani kell.

A HSI (nagysebességű belső) órajel
A HSI órajelet egy belső 16 MHz-es RC-generátor biztosítja programozható osztóval (1-től 8-ig). Ezt az órajel osztó regiszterében (CLK_CKDIVR) állítják be.
Megjegyzés: induláskor a fő óraforrásként az HSI RC-generátor van kiválasztva 8-as osztóval.
Megkeressük a regiszter címét a datasheetben, a leírást a refman-ban, és látjuk, hogy a regisztert ki kell tisztítani:
#define CLK_CKDIVR *(volatile uint8_t *)0x0050C6
CLK_CKDIVR &= ~(0x18);Mivel PWM-et fogunk indítani és LED-eket csatlakoztatni, megnézzük a lábkiosztást:

A chip kicsi, sok funkció azonos lábakra van felfűzve. Ami a négyzetes zárójelekben van - az az 'alternatív funkció', ezt 'option bytes'-sal (option bytes) lehet váltogatni - valami hasonló az ATMega fúzióihoz. Az értékeket programozottan lehet módosítani, de nem érdemes, mivel az új funkciók csak újraindítás után aktiválódnak. Egyszerűbb használni a ST Visual Programmer-t (amely a Visual Develop-vel együtt tölthető le), ami tudja módosítani ezeket a byte-okat. A lábkiosztásban látható, hogy az első timer CH1 és CH2 kijelzői négyzetes zárójelekbe vannak rejtve; a STVP-ben az AFR1 és AFR0 biteket kell beállítani, ahol a második a második timer CH1 kimenetet a PD4-ről a PC5-re is áthelyezi.
Így a LED-ek vezérlését 6 láb végzi: PC6, PC7 és PC3 az első timerhez, PC5, PD3 és PA3 a másodikhoz.
Az STM8 bemeneti-kimeneti lábainak beállítása egyszerűbb és logikusabb, mint az STM32 esetében:
- az Atmega-ból ismert DDR (Data Direction Register): 1 = kimenet;
- az első vezérlő regiszter CR1 a kimenetnél szabályozza a 'push-pull' (1) vagy nyitott kollektor (0) módot; mivel az LED-eket katódon csatlakoztatom a chiphez, itt nullák maradnak;
- a második vezérlő regiszter CR2 a kimenetnél az órajel sebességét szabályozza: 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 beállítása
Kezdjük a kifejezések tisztázásával:
- PWM frekvencia – az az ütem, ahogy a timer ketyeg;
- Automatikus újratöltés, AR – az a betöltött érték, ameddig a timer számol (impulzus időtartam);
- Frissítési esemény, UEV – esemény, ami akkor következik be, amikor a timer elérte az AR-t;
- PWM kitöltési ciklus – a PWM kitöltési hányadosa, gyakran "skvázs" néven emlegetik;
- Fogás/Összehasonlítási érték – az a várt érték, amelyet a timer elérve valamit tesz (PWM esetén – invertálja a kimeneti jelet);
- Előzetes betöltési érték – a nyers érték, amely előre betöltésre kerül. Összehasonlítási érték nem változhat, amíg a timer ketyeg, különben a PWM ciklus megszakad. Ezért az új értékeket egy puffertárolóba helyezik, és azt onnan veszik ki, amikor a timer eléri a visszaállítás végét;
- Élszinkronizált és Középre szinkronizált módok – élszinkronizálás és középre szinkronizálás, ugyanaz, mint az atmega-nél Gyors PWM és Fázishelyes PWM.
- OCiREF, Kimeneti Összehasonlítási Referenciális Jelkép – a kimeneti referenciajel, az, ami PWM módban megjelenik a megfelelő lábon.
Amint az a lábkiosztásból látható, a PWM funkcióval két timer rendelkezik – az első és a második. Mindkettő 16 bites, az elsőnek sok további funkciója van (többek között képes felfelé és lefelé is számolni). Számunkra fontos, hogy mindkettő ugyanúgy működjön, ezért elhatároztam, hogy a nyilvánvalóan egyszerűbb másodikkal kezdem, hogy véletlenül se használjak olyan funkciót, ami benne nincs. Az egyetlen probléma, hogy a PWM funkció leírása a reference kézikönyvben az első timer fejezetében szerepel (17.5.7 PWM Mód), így folyamatosan oda-vissza kell ugrálnom a dokumentumban.
A PWM az STM8-on fontos előnyökkel rendelkezik a PWM Atmegához képest:

Él szinkronizálással
Alulról felfelé számolás
Az alulról felfelé számolás aktív, ha a DIR bit a TIM_CR1 regiszterben nullázva van
támadásra
A példa az első PWM módot használja. A PWM OCiREF referenciajel magasan tartva marad, amíg TIM1_CNT < TIM1_CCRi. Ellenkező esetben alacsony szintet vesz fel. Amennyiben az összehasonlító érték a TIM1_CCRi regiszterben nagyobb, mint a betöltendő érték (TIM1_ARR regiszter), az OCiREF jel 1-en marad. Ha az összehasonlító érték 0, az OCiREF nullán marad.…
STM8 timer az frissítési esemény először ellenőrzi az összehasonlító értéket,és csak ezután adja ki a referenciajelet. Az Atmegánál a timer először ketyeg, majd összehasonlít, így amikor a összehasonlító érték == 0 A végén egy tű kapunk, amely ellen valahogy meg kell küzdenünk (például programozott logikával megfordítva).
Tehát mit szeretnénk csinálni: 8-bites PWM (AR == 255), alulról felfele számítva, határra igazítva. Mivel a LED-ek a chiphez katódokkal csatlakoznak, a PWM-nek 0-t (LED világít) kell adnia az összehasonlító értéket, és 1-et utána.
Már olvastunk néhány dolgot a PWM módról, ezért a szükséges második timer regisztert keressük a reference manual-ban ezen kifejezéssel (18.6.8 – TIMx_CCMR1):

110: Első PWM mód – alulról felfele számítva, az első csatorna aktív, amíg TIMx_CNT < TIMx_CCR1. Ellenkező esetben az első csatorna nem aktív. [tovább a dokumentumban hibás másolás a 1-es timerből]
111: Második PWM mód – alulról felfele számítva, az első csatorna nem aktív, amíg TIMx_CNT < TIMx_CCR1. Ellenkező esetben az első csatorna aktív.
Mivel a LED-ek a mikrokontrollerhez katódokkal csatlakoznak, számunkra a második mód megfelelő (az első is, de ezt még nem tudjuk).

Bít 3 OC1PE: Aktiválja az 1-es kimeneti előtöltést
0: Az előtöltő regiszter a TIMx_CCR1-en ki van kapcsolva. A TIMx_CCR1-be bármikor írható. Az új érték azonnal érvénybe lép.
1: Az előtöltő regiszter a TIMx_CCR1-en be van kapcsolva. Az olvasási / írási műveletek az előtöltő regiszterre vonatkoznak. A TIMx_CCR1 előtöltött értéke minden frissítési esemény során betöltődik az árnyék regiszterbe.
*Megjegyzés: A PWM üzemmód megfelelő működéséhez az előtöltő regisztereket aktiválni kell. Ez nem szükséges az egységes jel módban (amikor a TIMx_CR1 regiszter OPM bite be van állítva).
Rendben, aktiváljuk mindazt, ami szükséges a második timer három csatornájához:
#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);Az AR két nyolcbites regiszterből áll, itt minden egyszerű:
#define TIM2_ARRH *(volatile uint8_t *)0x00530F
#define TIM2_ARRL *(volatile uint8_t *)0x005310
TIM2_ARRH = 0;
TIM2_ARRL = 255;A második timer csak alulról felfele tud számolni, határra igazítva, semmit nem kell változtatni. Állítsuk be a frekvencia osztót például 256-ra. A második timer osztója a TIM2_PSCR regiszterben van beállítva, és kettő hatványát képviseli:
#define TIM2_PSCR *(volatile uint8_t *)0x00530E
TIM2_PSCR = 8;Most már aktiválni kell a kimeneteket és magát a második timer-t. Az első feladat a Capture / Compare regisztereken keresztül oldható meg. Capture/Compare Engedélyez: kettő van belőlük, három csatorna nem aszimmetrikusan van elhelyezve rajtuk. Itt azt is megtudhatjuk, hogy a jel polaritása változtatható, tehát alapvetően a PWM Mód 1-et is használhattuk volna. Írjuk:
#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;És végül indítsuk el a timer-t a TIMx_CR1 regiszterben:

#define TIM2_CR1 *(volatile uint8_t *)0x005300
TIM2_CR1 |= 1;Írjunk egy egyszerű AnalogWrite() analógot, amely átadja a timer-nek a tényleges értékeket a összehasonlításhoz. A regiszterek várhatóan neveken vannak: Capture/Compare regiszterek, kettő van minden csatornára: a fiatalabb 8 bit a TIM2_CCRxL-ben, a régebbi pedig a TIM2_CCRxH-ban. Mivel 8 bites PWM-et állítottunk be, elegendő csak a fiatalabb biteket írni:
#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;
}A figyelmes olvasó észre fogja venni, hogy egy kissé hibás PWM-t kaptunk, amely nem képes 100%-os kitöltést adni (a maximális érték 255-nél a jel egy ciklusra inverzálódik). Ez a LED-ek számára nem számít, de a figyelmes olvasó már magától kitalálja, hogyan lehet ezt javítani.
A második timert PWM-mel működik, áttérünk az elsőre.
Az első timer pontosan ugyanazokkal a bitekkel rendelkezik ugyanazokban a regiszterekben (azok a bitek, amelyek a második timerben „fenntartottak” maradtak, az elsőben aktívan használva vannak különböző fejlett dolgokhoz). Tehát elegendő, ha megtaláljuk ezeknek a regisztereknek a címeit a datasheet-ben, és átmásoljuk a kódot. Nos, és meg kell változtatni a frekvenciaosztó értékét, mivel az első timer nem egy kettő hatványát szeretné, hanem pontos 16 bites értéket két regiszterben. Előosztó magas és Alacsony. Mindent megcsinálunk, és... az első timer nem működik. Mi a baj?
A problémát csak az 1-es timer vezérlő regisztereiről szóló rész teljes átnézésével lehet megoldani, ahol keresnünk kell azt, ami nincs a második timerben. Meg fogjuk találni 17.7.30 Törési regiszter (TIM1_BKR), ahol van egy ilyen bit:
![]()
A fő kimenet engedélyezése
#define TIM1_BKR *(volatile uint8_t *)0x00526D
TIM1_BKR = (1<<7);Most már biztosan minden rendben van, a kód .

STM8 Multiplex
Multiplexelés STM8-on
A harmadik mini-projekt célja, hogy a második timerhez nyolc RGB LED-et csatlakoztassunk PWM módban, és különböző színeket mutassanak. Az alapja a LED multiplexelés koncepciója, amely az, hogy ha nagyon-nagyon gyorsan kapcsolgatjuk a LED-eket, úgy fog tűnni, hogy folyamatosan világítanak (a látás tartóssága, a vizuális észlelés inercia). Régebben csináltam .
A működés algoritmusa a következőképpen néz ki:
- a első RGB LED anódját csatlakoztattuk;
- meggyújtottuk, a megfelelő jeleket adva a katódoknak;
- megvártuk a PWM ciklus végét;
- a második RGB LED anódját csatlakoztattuk;
- meggyújtottuk…
Nos és így tovább. Természetesen a szép működéshez szükség van arra, hogy az anód csatlakoztatása és a LED „meggyújtása” egyszerre történjen. Vagy majdnem. Mindenesetre írni kell egy kódot, amely a második timer három csatornáján értékeket ad, a UEV elérésével megváltoztatja őket, és egy időben változtatja az aktuális RGB LED-et.
Mivel az LED váltás automatikusan történik, szükség van egy "videomemóriára", ahonnan a megszakítási kezelő kapja az adatokat. Ez egy egyszerű tömb:
uint8_t colors[8][3];Ahhoz, hogy megváltoztassuk egy adott LED színét, elegendő, ha a szükséges értékeket írjuk be ebbe a tömbbe. Az aktív LED számát pedig egy változó határozza meg
uint8_t cnt;Demultiplexelés
A helyes multiplexeléshez, meglepő módon, demultiplexor CD74HC238 szükséges. A demultiplexor egy chip, amely hardveresen valósítja meg az operátort <<. Három bemeneti pin (0, 1 és 2 láb) segítségével egy hárombitű számot, X-et táplálunk be, és cserébe az aktivált kimenet száma (1<<X). A chip többi bemenete a teljes konstrukció skálázására szolgál. Erre a chipre nemcsak a mikrokontroller által elfoglalt pinok számának csökkentése miatt van szükség, hanem a biztonság érdekében is – hogy véletlenül ne kapcsoljunk be több LED-et, mint amennyit lehet, és ne égessük el a mikrokontrollert. A chip ára elenyésző, mindig érdemes tartani az otthoni készletben.
A CD74HC238 felelős azért, hogy feszültséget biztosítson a szükséges LED anódjához. Teljes multiplexelés esetén P-MOSFET-en keresztül biztosítaná a feszültséget az oszlopra, de ebben a bemutatóban közvetlenül is csatlakoztatható, mivel 20 mA-t húz, a abszolút maximális értékek dokumentáció szerint. A szükségünk van a pinelrendezésre és erre a segédletre:

H = magas feszültség, L = alacsony feszültség, X – mindegy
Csatlakoztassuk az E2-t és E1-et a földhöz, az E3-at, A0-t, A1-et és A3-at a PD5, PC3, PC4 és PC5 STM8 lábakhoz. Mivel a fenti táblázat tartalmaz alacsony és magas szintet is, ezeket a pinokat push-pull kimenetként állítjuk be.
PWM
A második időzítőn a PWM beállítása ugyanúgy történik, mint az előző történetben, két eltéréssel:
Először is, engedélyezni kell a megszakítást az Update Event (UEV), ami meghív egy funkciót, amely váltja az aktív LED-et. Ezt a bit módosításával lehet elérni a Update Interrupt Enable regiszterben, amelynek beszédes neve van.

A megszakítási engedélyező regiszter
#define TIM2_IER *(volatile uint8_t *)0x005303
//enable interrupt
TIM2_IER = 1;A második eltérés a multiplexeléshez kapcsolódó jelenség, a ghosting – a LED-ek zavaró fényjelensége. A mi esetünkben ez azért fordulhat elő, mert a timert, amely megszakítást hívott meg UEV-nél, továbbra is ketyeg, és a megszakításkezelő nem tudja kapcsolni a LED-et, mielőtt a timer már elkezdi kiírni az adatokat a kimenetekre. Ezzel szemben a logika megfordítása szükséges (0 = maximális fényerő, 255 = semmi nem világít), és el kell kerülni a szélsőséges kitöltési értékeket. Azaz, el kell érjük, hogy az UEV után a LED-ek teljesen kialudjanak egy PWM ciklusra.
Megváltoztatjuk a polaritást:
//set polarity
TIM2_CCER1 |= (CC1P | CC2P);
TIM2_CCER2 |= CC3P;Kerüljük az r, g és b 255-re állítását, és ne felejtsük el őket megfordítani használat közben.
Megszakítások
A megszakítás lényege, hogy bizonyos körülmények között a chip abbahagyja a fő program végrehajtását, és egy külső funkciót hív meg. Megszakítások léphetnek fel külső vagy belső hatások, beleértve a timert, miatt.
Amikor először létrehoztuk a projektet az ST Visual Develop-ban, akkor a main.c mellett kaptunk egy rejtélyes fájlt is stm8_interrupt_vector.c, amely automatikusan be lett kapcsolva a projektbe. Ebben a fájlban minden megszakításhoz egy funkció van hozzárendelve. NonHandledInterrupt. Nekünk a saját funkciónkat kell hozzárendelni a szükséges megszakításhoz.
A datasheet-ben található egy megszakítási vektor táblázat, ahol megtaláljuk a szükséges:

13 TIM2 frissítés/túlcsordulás
14 TIM2 rögzítés/összehasonlítás
A LED-et UEV-nél meg kell változtatnunk, így a 13-as számú megszakítás szükséges.
Ennek megfelelően, először is a stm8_interrupt_vector.c fájlban meg kell változtatnunk a 13-as számú (IRQ13) megszakításért felelős funkció nevét a sajátunkra:
{0x82, TIM2_Overflow}, /* irq13 */Másodszor, létre kell hoznunk egy main.h tartalmú fájlt:
#ifndef __MAIN_H
#define __MAIN_H
@far @interrupt void TIM2_Overflow (void);
#endifÉs végül, a funkciónkat a következőképpen kell megadni: main.c:
@far @interrupt void TIM2_Overflow (void)
{
PD_ODR &= ~(1<<5); // kikapcsoljuk a demultiplexort
PC_ODR = (cnt<<3); // új értéket írunk be a demultiplexorba
PD_ODR |= (1<<5); // bekapcsoljuk a demultiplexort
TIM2_SR1 = 0; // alaphelyzetbe állítjuk a Frissítési Megszakítási Jelzést
cnt++;
cnt &= 7; // mozgatjuk a LED számlálót
TIM2_CCR1L = ~colors[cnt][0]; // a bufferbe átadjuk a megfordított értékeket
TIM2_CCR2L = ~colors[cnt][1]; // a következő PWM ciklushoz
TIM2_CCR3L = ~colors[cnt][2]; //
return;
}Még meg kell engednünk a megszakításokat. Ezt az assembly paranccsal tehetjük meg rim – ezt a parancsot a :
//enable interrupts
_asm("rim");Másik assembly parancs – sim – kikapcsolja a megszakításokat. Ezokat ki kell kapcsolni új értékek „videomemóriába” való íráskor, hogy a nem megfelelő időben bekövetkező megszakítás ne rontsa el a tömböt.
Az összes kód – .

Ha ez a cikk bárkinek hasznos lesz, akkor nem írtam hiába. Örömmel várom a hozzászólásokat és észrevételeket, megpróbálok mindenre válaszolni.
Forrás: habr.com
