Olvassa el a 2. adatlapot: SPI az STM32-n; PWM, időzítők és megszakítások az STM8-on

Olvassa el a 2. adatlapot: SPI az STM32-n; PWM, időzítők és megszakítások az STM8-on

В az első rész Megpróbáltam elmesélni a hobbi elektronikai mérnököknek, akik Arduino nadrágból nőttek fel, hogyan és miért olvassák el a mikrokontrollerek adatlapjait és egyéb dokumentációit. A szöveg nagynak bizonyult, ezért megígértem, hogy gyakorlati példákat mutatok be egy külön cikkben. Nos, tejgombának nevezte magát...

Ma megmutatom, hogyan lehet adatlapokkal megoldani az egészen egyszerű, de sok projekthez, feladathoz szükséges feladatot STM32 (Blue Pill) és STM8 vezérlőkön. Minden demóprojektet a kedvenc LED-jeimnek szentelünk, nagy mennyiségben fogjuk megvilágítani, amihez mindenféle érdekes perifériát kell használnunk.

A szöveg ismét hatalmasra sikerült, így a kényelem kedvéért elkészítem a tartalmat:

STM32 Blue Pill: 16 LED DM634 meghajtóval
STM8: Hat PWM érintkező beállítása
STM8: 8 RGB LED három érintkezőn, megszakítások

Jogi nyilatkozat: Nem vagyok mérnök, nem állítom, hogy mély ismeretekkel rendelkezem az elektronikában, a cikk az olyan amatőröknek szól, mint én. Tulajdonképpen két éve még magamat tekintettem célközönségnek. Ha valaki akkor azt mondta volna, hogy egy ismeretlen chipen lévő adatlapokat nem ijesztő olvasni, nem töltöttem volna sok időt azzal, hogy néhány kódrészletet keressek az interneten, és ollóval és ragasztószalaggal mankókat találjak ki.

Ennek a cikknek a középpontjában az adatlapok állnak, nem a projektek, ezért előfordulhat, hogy a kód nem túl ügyes, és gyakran szűkös. Maguk a projektek nagyon egyszerűek, bár alkalmasak az új chip első megismerésére.

Remélem, hogy cikkem segíteni fog valakinek a hobbiba való elmélyülés hasonló szakaszában.

STM32

16 LED DM634-gyel és SPI-vel

Egy kis projekt Blue Pill (STM32F103C8T6) és DM634 LED meghajtó használatával. Adatlapok segítségével kitaláljuk az illesztőprogramot, az STM IO portokat és konfiguráljuk az SPI-t.

DM634

Tajvani chip 16 db 16 bites PWM kimenettel, láncba köthető. Az alsó kategóriás 12 bites modell egy hazai projektből ismert Lightpack. Egy időben, amikor a DM63x és a jól ismert TLC5940 között választottam, több okból is a DM-et választottam: 1) Az Aliexpress TLC-je határozottan hamis, de ez nem az; 2) A DM önálló PWM-mel rendelkezik saját frekvenciagenerátorral; 3) olcsón megvásárolható Moszkvában, ahelyett, hogy várna egy csomagot Alitól. És természetesen érdekes volt megtanulni, hogyan kell saját kezűleg irányítani a chipet, nem pedig egy kész könyvtárat. A chipek ma már főként az SSOP24 csomagban jelennek meg, könnyen adapterre forraszthatók.

Mivel a gyártó tajvani, adatlap a chip kínai angolul van írva, ami azt jelenti, hogy szórakoztató lesz. Először a tűt nézzük (Pin csatlakozás), hogy megértsük, melyik lábhoz mit kell csatlakoztatni, és a csapok leírása (PIN leírás). 16 tű:

Olvassa el a 2. adatlapot: SPI az STM32-n; PWM, időzítők és megszakítások az STM8-on
Egyenáramú nyelőforrások (nyitott lefolyó)

Mosogató / Nyitott leeresztő kimenet – lefolyó; beáramló áram forrása; a kimenet aktív állapotban a földre van kötve - a LED-ek katódokkal csatlakoznak a meghajtóhoz. Elektromosan ez természetesen nem „nyitott lefolyó” (nyitott csatorna), de az adatlapokon gyakran megtalálható ez a jelölés a leeresztő módban lévő tűkre.

Olvassa el a 2. adatlapot: SPI az STM32-n; PWM, időzítők és megszakítások az STM8-on
Külső ellenállások a REXT és a GND között a kimeneti áram értékének beállításához

A REXT érintkező és a test közé referencia ellenállás van beépítve, amely a kimenetek belső ellenállását szabályozza, lásd az adatlap 9. oldalán található grafikont. A DM634-ben ez az ellenállás szoftverrel is vezérelhető, beállítva a teljes fényerőt (globális fényerő); Ebben a cikkben nem megyek bele a részletekbe, csak egy 2.2 - 3 kOhm-os ellenállást teszek ide.

A chip vezérlésének megértéséhez nézzük meg az eszköz interfészének leírását:

Olvassa el a 2. adatlapot: SPI az STM32-n; PWM, időzítők és megszakítások az STM8-on

Igen, itt van, a kínai angol a maga teljes pompájában. Ennek lefordítása problémás, ha akarod, meg tudod érteni, de van más mód is - nézd meg, hogyan van leírva az adatlapon a csatlakoztatás a funkcionálisan hasonló TLC5940-hez:

Olvassa el a 2. adatlapot: SPI az STM32-n; PWM, időzítők és megszakítások az STM8-on
... Csak három tűre van szükség ahhoz, hogy adatokat vigyünk be a készülékbe. Az SCLK jel felfutó éle áthelyezi az adatokat a SIN lábról a belső regiszterbe. Az összes adat betöltése után egy rövid, magas XLAT jel reteszeli a szekvenciálisan átvitt adatokat a belső regiszterekbe. A belső regiszterek az XLAT jelszint által kiváltott kapuk. Minden adat először a legjelentősebb bitet továbbítja.

Retesz – retesz/retesz/zár.
Emelkedő él – az impulzus elülső éle
MSB először – legjelentősebb (bal szélső) bit előre.
adatok órajeléhez – szekvenciális adatátvitel (bitenként).

Szó retesz gyakran megtalálható a chipek dokumentációjában, és többféleképpen van lefordítva, ezért a megértés kedvéért megengedem magamnak

kis oktatási programA LED-meghajtó lényegében egy műszakregiszter. "Shift" (váltás) a névben - az adatok bitenkénti mozgása az eszközön belül: minden egyes betolt új bit a teljes láncot előre tolja maga előtt. Mivel a LED-ek kaotikus villogását a műszak alatt senki sem akarja megfigyelni, a folyamat a munkaregiszterektől csillapítóval elválasztott pufferregiszterekben zajlik (retesz) egyfajta váróterem, ahol a bitek a kívánt sorrendben vannak elrendezve. Amikor minden készen áll, a redőny kinyílik, és a bitek munkába állnak, lecserélve az előző tételt. Szó retesz a mikroáramkörök dokumentációjában szinte mindig ilyen csillapítót jelent, függetlenül attól, hogy milyen kombinációkban használják.

Tehát az adatátvitel a DM634-re a következőképpen történik: állítsa a DAI bemenetet a távoli LED legjelentősebb bitjének értékére, húzza fel és le a DCK-t; állítsa be a DAI bemenetet a következő bit értékére, húzza meg a DCK-t; és így tovább, amíg az összes bitet el nem küldték (beütött), ami után LAT-t húzunk. Ezt manuálisan is meg lehet tenni (bit-bumm).

Kék tabletta STM32F103

Bevezető: Az STM32 vezérlők sokkal összetettebbek, mint az Atmega328, mint amennyire ijesztőnek tűnhetnek. Ráadásul energiatakarékossági okokból indításkor szinte minden perifériát kikapcsolnak, az órajel pedig 8 MHz a belső forrásból. Szerencsére az STM programozók olyan kódot írtak, ami felhozza a chipet a „számított” 72 MHz-re, és az általam ismert összes IDE szerzői beépítették az inicializálási eljárásba, így nem kell órajelet készíteni (de megteheted, ha nagyon akarod). De be kell kapcsolnia a perifériákat.

Dokumentáció: A Blue Pill a népszerű STM32F103C8T6 chippel van felszerelve, két hasznos dokumentum van hozzá:

Az adatlapon a következőkre lehetünk kíváncsiak:

  • Pinouts – chip pinouts – arra az esetre, ha úgy döntünk, hogy magunk készítjük el a deszkákat;
  • Memóriatérkép – memóriatérkép egy adott chiphez. A Reference Manual tartalmaz egy térképet a teljes vonalra, és említi azokat a regisztereket, amelyek a miénkben nem találhatók.
  • Pin Definitions táblázat – a tűk fő és alternatív funkcióinak felsorolása; a „kék pirulához” kényelmesebb képeket találhat az interneten a tűk listájával és azok funkcióival. Ezért azonnal google-ban keressük a Blue Pill pinout-ot, és kéznél tartjuk ezt a képet:

Olvassa el a 2. adatlapot: SPI az STM32-n; PWM, időzítők és megszakítások az STM8-on
MEGJEGYZÉS: a képen az internetről származó hiba volt, amit a megjegyzésekben jeleztek, ezt köszönöm. A kép ki lett cserélve, de ez egy lecke - jobb, ha az információkat nem adatlapokból ellenőrizzük.

Eltávolítjuk az adatlapot, megnyitjuk a Reference Manual-t, és mostantól csak azt használjuk.
Eljárás: standard bemenettel/kimenettel foglalkozunk, SPI-t konfigurálunk, bekapcsoljuk a szükséges perifériákat.

Bemenet kimenet

Az Atmega328-on az I/O rendkívül egyszerűen van megvalósítva, ezért az STM32 opciók bősége zavaró lehet. Most már csak következtetésekre van szükségünk, de ezeknek is van négy lehetőségük:

Olvassa el a 2. adatlapot: SPI az STM32-n; PWM, időzítők és megszakítások az STM8-on
nyitott lefolyó, push-pull, alternatív push-pull, alternatív nyitott lefolyó

"Húz tol" (ellenütemű) az Arduino szokásos kimenete, a tű HIGH vagy LOW értéket vehet fel. De a „nyitott lefolyóval” vannak nehézségek, bár valójában itt minden egyszerű:

Olvassa el a 2. adatlapot: SPI az STM32-n; PWM, időzítők és megszakítások az STM8-on
Olvassa el a 2. adatlapot: SPI az STM32-n; PWM, időzítők és megszakítások az STM8-on
Kimeneti konfiguráció / amikor a port hozzá van rendelve a kimenethez: / kimeneti puffer engedélyezve: / – nyitott leürítési mód: a kimeneti regiszterben a „0” engedélyezi az N-MOS-t, a kimeneti regiszterben az „1” Hi-Z módban hagyja el a portot ( A P-MOS nincs aktiválva ) / – push-pull mód: „0” a kimeneti regiszterben aktiválja az N-MOS-t, „1” a kimeneti regiszterben a P-MOS-t.

Minden különbség a nyitott lefolyó között (nyitott csatorna) a „push-pull” szóból (ellenütemű) az, hogy az első érintkezőben nem tudja elfogadni a HIGH állapotot: amikor egyet írunk a kimeneti regiszterbe, nagy ellenállású módba lép (nagyimpedanciás, Szia-Z). Nulla írásakor a tű mindkét módban ugyanúgy viselkedik, logikailag és elektromosan is.

Normál kimeneti módban a láb egyszerűen sugározza a kimeneti regiszter tartalmát. Az "alternatívában" a megfelelő perifériák vezérlik (lásd 9.1.4):

Olvassa el a 2. adatlapot: SPI az STM32-n; PWM, időzítők és megszakítások az STM8-on
Ha egy portbit alternatív funkciójú érintkezőként van konfigurálva, a tűregiszter le van tiltva, és a láb a perifériás érintkezőhöz csatlakozik.

Az egyes tűk alternatív funkcióit a Pin definíciók Az adatlap a letöltött képen található. Arra a kérdésre, hogy mi a teendő, ha egy tűnek több alternatív funkciója van, az adatlapon található lábjegyzet ad választ:

Olvassa el a 2. adatlapot: SPI az STM32-n; PWM, időzítők és megszakítások az STM8-on
Ha több periféria használja ugyanazt a tűt, az alternatív funkciók közötti ütközések elkerülése érdekében egyszerre csak egy perifériát szabad használni, a perifériaóra engedélyezési bitjével váltva (a megfelelő RCC regiszterben).

Végül a kimeneti módban lévő érintkezőknek órajele is van. Ez egy másik energiatakarékos funkció, esetünkben csak maximumra állítjuk és elfelejtjük.

Tehát: SPI-t használunk, ami azt jelenti, hogy két tű (adatokkal és órajellel) legyen „alternatív push-pull funkció”, egy másik (LAT) pedig „regular push-pull”. De mielőtt hozzárendelnénk őket, foglalkozzunk az SPI-vel.

SPI

Egy újabb kis oktatási program

Az SPI vagy Serial Peripheral Interface (soros periféria interfész) egy egyszerű és nagyon hatékony interfész az MK más MK-kkal és általában a külvilággal való összekapcsolására. Működési elvét fentebb már leírtuk, ahol a kínai LED meghajtóról (a referencia kézikönyvben lásd a 25. fejezetet). Az SPI master („master”) és slave („szolga”) módban működhet. Az SPI-nek négy alapvető csatornája van, amelyek közül nem mindegyik használható:

  • MOSI, Master Output / Slave Input: ez a láb mester módban továbbítja az adatokat, és szolga módban fogad adatokat;
  • MISO, Master Input / Slave Output: éppen ellenkezőleg, a masterben vesz, és a slave-ben ad;
  • SCK, Serial Clock: beállítja az adatátvitel frekvenciáját a masterben vagy órajelet vesz a slave-ben. Lényegében ütemek ütése;
  • SS, Slave Select: ennek a csatornának a segítségével a rabszolga tudja, hogy akarnak tőle valamit. Az STM32-n NSS-nek hívják, ahol N = negatív, azaz. a vezérlő slave lesz, ha földelés van ebben a csatornában. Jól kombinálható az Open Drain Output móddal, de ez egy másik történet.

Mint minden más, az STM32 SPI is gazdag funkcionalitásban, ami kissé megnehezíti a megértést. Például nem csak SPI-vel, hanem I2S interfésszel is tud működni, és a dokumentációban ezek leírása keveredik, a felesleget időben le kell vágni. A feladatunk rendkívül egyszerű: csak MOSI és SCK használatával kell adatokat küldenünk. Lépjünk a 25.3.4 szakaszra (félduplex kommunikáció, félduplex kommunikáció), ahol megtaláljuk 1 óra és 1 egyirányú adatvezeték (1 órajel és 1 egyirányú adatfolyam):

Olvassa el a 2. adatlapot: SPI az STM32-n; PWM, időzítők és megszakítások az STM8-on
Ebben a módban az alkalmazás csak adási vagy csak vételi módban használja az SPI-t. / A Csak átvitel mód hasonló a duplex módhoz: az adatátvitel az átviteli lábon történik (MOSI master módban vagy MISO szolga módban), és a vételi láb (MISO vagy MOSI) normál I/O lábként használható . Ebben az esetben az alkalmazásnak csak az Rx puffert kell figyelmen kívül hagynia (ha beolvassa, ott nem lesz átvitt adat).

Remek, a MISO pin szabad, csatlakoztassuk rá a LAT jelet. Nézzük a Slave Selectet, ami az STM32-n programozottan vezérelhető, ami rendkívül kényelmes. Az azonos nevű bekezdést a 25.3.1 SPI Általános leírás szakaszban olvashatjuk:

Olvassa el a 2. adatlapot: SPI az STM32-n; PWM, időzítők és megszakítások az STM8-on
Szoftvervezérlés NSS (SSM = 1) / Slave kiválasztási információ az SPI_CR1 regiszter SSI bitjében található. A külső NSS tű szabad marad más alkalmazási igényekhez.

Ideje írni a regisztereknek. Úgy döntöttem, hogy az SPI2-t használom, keressem meg az alapcímét az adatlapon - a 3.3 Memóriatérkép részben:

Olvassa el a 2. adatlapot: SPI az STM32-n; PWM, időzítők és megszakítások az STM8-on

Nos, kezdjük:

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

Nyissa meg a 25.3.3 szakaszt a magától értetődő „SPI konfigurálása mester módban” címmel:

Olvassa el a 2. adatlapot: SPI az STM32-n; PWM, időzítők és megszakítások az STM8-on

1. Állítsa be a soros órajel frekvenciáját a BR[2:0] bitekkel az SPI_CR1 regiszterben.

A regiszterek az azonos nevű kézikönyvben találhatók. Címeltolás (Címeltolás) CR1 – 0x00 esetén alapértelmezés szerint minden bit törlődik (Állítsa vissza az értéket 0x0000):

Olvassa el a 2. adatlapot: SPI az STM32-n; PWM, időzítők és megszakítások az STM8-on

A BR bitek állítják be a vezérlő órajelosztóját, így határozzák meg, hogy az SPI milyen frekvencián fog működni. STM32-es frekvenciánk 72 MHz lesz, a LED-es meghajtó adatlapja szerint akár 25 MHz-es frekvenciával üzemel, így néggyel 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/4

2. Állítsa be a CPOL és a CPHA biteket az adatátvitel és a soros óra időzítése közötti kapcsolat meghatározásához (lásd az ábrát a 240. oldalon).

Mivel itt egy adatlapot olvasunk, és nem vázlatokat nézünk, nézzük meg közelebbről a CPOL és CPHA bitek szöveges leírását a 704. oldalon (SPI általános leírás):

Olvassa el a 2. adatlapot: SPI az STM32-n; PWM, időzítők és megszakítások az STM8-on
Órafázis és polaritás
Az SPI_CR1 regiszter CPOL és CPHA bitjei segítségével négy időzítési kapcsolatot választhat ki programozottan. A CPOL (óra polaritás) bit szabályozza az órajel állapotát, amikor nincs adatátvitel. Ez a bit vezérli a master és slave módot. Ha a CPOL alaphelyzetbe van állítva, az SCK tűje nyugalmi módban alacsony. Ha a CPOL bit be van állítva, az SCK láb magasan van nyugalmi üzemmódban.
Amikor a CPHA (órafázis) bit be van állítva, a magas bitcsapda villogó az SCK jel második éle (esik, ha a CPOL tiszta, emelkedik, ha a CPOL be van állítva). Az adatokat az órajel második változása rögzíti. Ha a CPHA bit tiszta, a magas bitcsapda villogó az SCK jel felfutó éle (lefutó él, ha a CPOL be van állítva, felfutó él, ha a CPOL törölve van). Az adatok rögzítése az órajel első megváltozásakor történik.

Ezt a tudást felszívva arra a következtetésre jutunk, hogy mindkét bitnek nullának kell maradnia, mert Azt akarjuk, hogy az SCK jel alacsony maradjon, amikor nincs használatban, és az adatokat az impulzus felfutó élén továbbítjuk (lásd az ábrát). Rising Edge a DM634 adatlapon).

Egyébként itt találkoztunk először az ST adatlapok szókincsének sajátosságával: azokon a „reset the bit to zero” kifejezés szerepel. egy kicsit visszaállítaniés nem egy kicsit tisztázni, mint például az Atmega.

3. Állítsa be a DFF bitet annak meghatározásához, hogy az adatblokk 8 bites vagy 16 bites formátumú

Kifejezetten egy 16 bites DM634-et vettem, hogy ne vesződjek a 12 bites PWM adatok továbbításával, mint például a DM633. Érdemes a DFF-t egyre állítani:

#define DFF         0x0800

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

4. Konfigurálja az LSBFIRST bitet az SPI_CR1 regiszterben a blokkformátum meghatározásához

Az LSBFIRST, ahogy a neve is sugallja, először a legkisebb jelentőségű bittel konfigurálja az átvitelt. De a DM634 a legjelentősebb bittől kezdődően akar adatokat fogadni. Ezért visszaállítjuk.

5. Hardveres módban, ha az NSS lábról bemenetre van szükség, adjon magas jelet az NSS lábra a teljes bájtátviteli szekvencia alatt. NSS szoftver módban állítsa be az SSM és SSI biteket az SPI_CR1 regiszterben. Ha az NSS tűt kimenetként kívánja használni, csak az SSOE bitet kell beállítani.

Telepítse az SSM-et és az SSI-t, hogy elfelejtse az NSS hardvermódját:

#define SSI         0x0100
#define SSM         0x0200

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

6. Az MSTR és SPE biteket be kell állítani (csak akkor maradnak beállítva, ha az NSS jel magas)

Valójában ezekkel a bitekkel az SPI-nket mesternek jelöljük ki, és bekapcsoljuk:

#define MSTR        0x0004
#define SPE         0x0040

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

Az SPI be van állítva, azonnal írjunk függvényeket, amik bájtokat küldenek a drivernek. Olvassa tovább a 25.3.3 „SPI konfigurálása mester módban” részt:

Olvassa el a 2. adatlapot: SPI az STM32-n; PWM, időzítők és megszakítások az STM8-on
Adatátviteli megbízás
Az átvitel akkor kezdődik, amikor egy bájt beírásra kerül a Tx pufferbe.
Az adatbájt a shift regiszterbe töltődik be párhuzamos módban (a belső buszról) az első bit átvitele során, amely után az átvitelre kerül egymás utáni MOSI pin mód, első vagy utolsó bit előre a CPI_CR1 regiszter LSBFIRST bitjének beállításától függően. A TXE jelző az adatátvitel után kerül beállításra Tx pufferből a shift regiszterbe, és megszakítást is generál, ha a CPI_CR1 regiszterben a TXEIE bit be van állítva.

A fordításban kiemeltem néhány szót, hogy felhívjam a figyelmet az STM-vezérlők SPI-megvalósításának egyik jellemzőjére. Az Atmegán a TXE zászló (Tx üres, Tx üres és készen áll az adatok fogadására) csak a teljes bájt elküldése után van beállítva kifelé. És itt ez a jelző be van állítva, miután a bájt bekerült a belső shift regiszterbe. Mivel az összes bittel egyszerre (párhuzamosan) kerül oda, majd szekvenciálisan továbbítják az adatokat, a TXE a bájt teljes elküldése előtt kerül beállításra. Ez azért fontos, mert a LED-es meghajtónk esetében az elküldés után ki kell húznunk a LAT tűt minden adatok, azaz A TXE zászló önmagában nem lesz elég nekünk.

Ez azt jelenti, hogy szükségünk van egy másik zászlóra. Nézzük a 25.3.7-et – „Állapotjelzők”:

Olvassa el a 2. adatlapot: SPI az STM32-n; PWM, időzítők és megszakítások az STM8-on
<…>
Olvassa el a 2. adatlapot: SPI az STM32-n; PWM, időzítők és megszakítások az STM8-on
BUSY zászló
A BSY jelzőt hardver állítja be és törli (a ráírásnak nincs hatása). A BSY jelző az SPI kommunikációs réteg állapotát jelzi.
Visszaállítja:
amikor az átvitel befejeződött (kivéve master módban, ha az átvitel folyamatos)
ha az SPI le van tiltva
ha mester mód hiba lép fel (MODF=1)
Ha az átvitel nem folyamatos, a BSY jelző minden adatátvitel között törlődik

Oké, ez jól fog jönni. Nézzük meg, hol található a Tx puffer. Ehhez olvassa el az „SPI-adatregiszter” részt:

Olvassa el a 2. adatlapot: SPI az STM32-n; PWM, időzítők és megszakítások az STM8-on
Bitek 15:0 DR[15:0] Adatregiszter
Fogadott vagy továbbítandó adatok.
Az adatregiszter két pufferre van osztva - az egyik az írási (átviteli puffer) és a másik az olvasásra (vételi puffer). Az adatregiszterbe írás a Tx pufferbe ír, az adatregiszterből való olvasás pedig az Rx pufferben lévő értéket adja vissza.

Nos, és az állapotregiszter, ahol a TXE és a BSY jelzők találhatók:

Olvassa el a 2. adatlapot: SPI az STM32-n; PWM, időzítők és megszakítások az STM8-on

Mi írunk:

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

Nos, mivel 16-szor két bájtot kell továbbítanunk, a LED-meghajtó kimenetek száma szerint, valami ilyesmi:

void sendLEDdata()
{
    LAT_low();
    uint8_t k = 16;
    do
    {   k--;
        dm_shift16(leds[k]);
    } while (k);

    while (_SPI2_(_SPI_SR) & BSY); // finish transmission

    LAT_pulse();
}

De még nem tudjuk, hogyan húzzuk ki a LAT-csapot, ezért visszatérünk az I/O-hoz.

Csapok hozzárendelése

Az STM32F1-ben a tűk állapotáért felelős regiszterek meglehetősen szokatlanok. Egyértelmű, hogy több van belőlük, mint az Atmega, de ezek is különböznek a többi STM chiptől. 9.1. szakasz A GPIO általános leírása:

Olvassa el a 2. adatlapot: SPI az STM32-n; PWM, időzítők és megszakítások az STM8-on
Mindegyik általános célú I/O 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 set/reset regiszterrel (GPIOx_BSRR), egy 16 bites visszaállító regiszterrel (GPIOx_BRR) és egy 32 bites regiszterrel rendelkezik. bitblokkoló regiszter (GPIOx_LCKR).

Az első két regiszter szokatlan, és meglehetősen kényelmetlen is, mert a 16 port érintkező érintkezői „négy bit per testvér” formátumban vannak szétszórva rajtuk. Azok. a nullától hétig terjedő érintkezők a CRL-ben, a többi pedig a CRH-ban találhatók. Ugyanakkor a fennmaradó regiszterek sikeresen tartalmazzák a port összes lábának bitjeit - gyakran félig „fenntartva”.

Az egyszerűség kedvéért kezdjük a lista végéről.

Nincs szükségünk blokkoló regiszterre.

A set és a reset regiszterek annyiban elég viccesek, hogy részben duplikálják egymást: mindent csak BSRR-ben írhatsz, ahol a magasabb 16 bit nullára állítja a pint, az alacsonyabbak pedig 1-re, vagy úgy is használja a BRR-t, amelynek alsó 16 bitje csak a pint állítja vissza. A második lehetőség tetszik. Ezek a regiszterek fontosak, mert atomi hozzáférést biztosítanak a csapokhoz:

Olvassa el a 2. adatlapot: SPI az STM32-n; PWM, időzítők és megszakítások az STM8-on
Olvassa el a 2. adatlapot: SPI az STM32-n; PWM, időzítők és megszakítások az STM8-on
Atombeállítás vagy visszaállítás
A GPIOx_ODR bitszintű programozásakor nincs szükség a megszakítások letiltására: egy vagy több bit megváltoztatható egyetlen atomi írási művelettel, az APB2-vel. Ezt úgy érik el, hogy "1"-et írnak a módosítandó bit set/reset regiszterébe (GPIOx_BSRR vagy csak alaphelyzetbe állítás esetén GPIOx_BRR). A többi bit változatlan marad.

Az adatregisztereknek eléggé magától értetődő neveik vannak - IDR = Bemenet Irányregiszter, bemeneti regiszter; ODR = teljesítmény Irányregiszter, kimeneti regiszter. A jelenlegi projektben nem lesz rájuk szükségünk.

És végül a vezérlőregiszterek. Mivel minket érdekelnek a második SPI tűk, nevezetesen a PB13, PB14 és PB15, azonnal megnézzük a CRH-t:

Olvassa el a 2. adatlapot: SPI az STM32-n; PWM, időzítők és megszakítások az STM8-on

És látjuk, hogy valamit 20 és 31 közötti bitekkel kell írnunk.

Fentebb már kitaláltuk, hogy mit akarunk a lábaktól, így itt megteszem screenshot nélkül, csak annyit mondok, hogy a MODE megadja az irányt (bemenet, ha mindkét bit 0-ra van állítva) és a pin sebességet (50 MHz kell, pl. mindkét láb „1”), és a CNF beállítja a módot: normál „push-pull” – 00, „alternatív” – 10. Alapértelmezés szerint, mint fentebb látjuk, minden érintkezőnél van a harmadik bit alulról (CNF0), módba állítja őket lebegő bemenet.

Mivel valami mást tervezek ezzel a chippel, az egyszerűség kedvéért minden lehetséges MODE és CNF értéket definiáltam mind az alsó, mind a felső vezérlőregiszterhez.

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 0x20000000

Pinjeink a B porton találhatók (alapcím – 0x40010C00), kódja:

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

Ennek megfelelően definíciókat írhat a LAT-hoz, amelyeket a BRR és BSRR regiszterek megrángatnak:

/*** 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 csak tehetetlenségből, mindig is így volt, maradjon)

Most minden nagyszerű, de nem működik. Mivel ez az STM32, áramot takarítanak meg, ami azt jelenti, hogy engedélyezni kell a szükséges perifériák órajelét.

Kapcsolja be az órajelet

Az óra, más néven Clock felelős az órajelért. És máris észrevehettük az RCC rövidítést. Keressük a dokumentációban: ez Reset és Clock Control.

Ahogy fentebb is elhangzott, szerencsére az órajeles téma legnehezebb részét az STM-esek csinálták nekünk, amit nagyon köszönünk nekik (még egyszer adok egy linket Di Halt honlapja, hogy egyértelmű legyen, mennyire zavaró). Csak olyan regiszterekre van szükségünk, amelyek felelősek a perifériás órajel engedélyezéséért (Peripheral Clock Enable Registers). Először is keressük meg az RCC alapcímét, ez a „Memory Map” legelején található:

Olvassa el a 2. adatlapot: SPI az STM32-n; PWM, időzítők és megszakítások az STM8-on

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

Ezután vagy kattintson arra a linkre, ahol megpróbál valamit találni a táblában, vagy, ami még jobb, nézze át az engedélyező regiszterek leírását a vonatkozó részekből. engedélyezze a regisztereket. Hol találjuk az RCC_APB1ENR-t és az RCC_APB2ENR-t:

Olvassa el a 2. adatlapot: SPI az STM32-n; PWM, időzítők és megszakítások az STM8-on
Olvassa el a 2. adatlapot: SPI az STM32-n; PWM, időzítők és megszakítások az STM8-on

Ennek megfelelően olyan biteket tartalmaznak, amelyek magukban foglalják az SPI2, IOPB (B I/O port) és alternatív funkciók (AFIO) órajelét.

#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égleges kód megtalálható itt.

Ha van lehetősége és vágya tesztelni, csatlakoztassa a DM634-et így: DAI a PB15-höz, DCK a PB13-hoz, LAT a PB14-hez. A meghajtót 5 V-ról tápláljuk, ne felejtse el csatlakoztatni a földelést.

Olvassa el a 2. adatlapot: SPI az STM32-n; PWM, időzítők és megszakítások az STM8-on

STM8 PWM

PWM az STM8-on

Amikor még csak ezt a cikket terveztem, elhatároztam, hogy példaként megpróbálom elsajátítani egy ismeretlen chip néhány funkcióját pusztán egy adatlap segítségével, hogy ne kerüljek egy csizma nélküli cipészre. Az STM8 ideális volt erre a szerepre: egyrészt volt pár kínai táblám STM8S103-mal, másrészt nem túl népszerű, ezért az interneten való olvasás és megoldás keresése éppen ezek hiányában múlik.

A chipnek is van adatlap и referencia kézikönyv RM0016, az elsőben pinout és regisztrációs címek találhatók, a másodikban - minden más. Az STM8 C-ben van programozva egy szörnyű IDE-ben ST Visual Develop.

Órajel és I/O

Alapértelmezés szerint az STM8 2 MHz-es frekvencián működik, ezt azonnal ki kell javítani.

Olvassa el a 2. adatlapot: SPI az STM32-n; PWM, időzítők és megszakítások az STM8-on
HSI (nagy sebességű belső) óra
A HSI órajel egy belső 16 MHz-es RC oszcillátorból származik, programozható osztóval (1-től 8-ig). Az óraosztó regiszterben (CLK_CKDIVR) van beállítva.
Megjegyzés: az elején egy 8-as osztóval rendelkező HSI RC oszcillátor van kiválasztva az órajel vezető forrásaként.

Az adatlapon megtaláljuk a regiszter címét, refmanban a leírást és látjuk, hogy a nyilvántartást törölni kell:

#define CLK_CKDIVR *(volatile uint8_t *)0x0050C6

CLK_CKDIVR &= ~(0x18);

Mivel PWM-et fogunk futtatni és csatlakoztatni fogjuk a LED-eket, nézzük meg a kivezetést:

Olvassa el a 2. adatlapot: SPI az STM32-n; PWM, időzítők és megszakítások az STM8-on

A chip kicsi, sok funkció ugyanazokon a csapokon van felfüggesztve. Ami szögletes zárójelben van, az „alternatív funkcionalitás”, ezt az „opció bájtok” váltják (opció bájtok) – valami Atmega biztosíték. Értékeiket programozottan módosíthatja, de ez nem szükséges, mert Az új funkció csak újraindítás után aktiválódik. Könnyebb az ST Visual Programmer használata (a Visual Developpal letöltve), amely képes megváltoztatni ezeket a bájtokat. A kivezetés azt mutatja, hogy az első időzítő CH1 és CH2 érintkezője szögletes zárójelben van elrejtve; STVP-ben be kell állítani az AFR1 és AFR0 biteket, és a második a második időzítő CH1 kimenetét is átviszi a PD4-ről a PC5-re.

Így 6 érintkező fogja vezérelni a LED-eket: PC6, PC7 és PC3 az első időzítőhöz, PC5, PD3 és PA3 a másodikhoz.

Maguk az I/O lábak beállítása az STM8-on egyszerűbb és logikusabb, mint az STM32-n:

  • ismerős az Atmega DDR adatirány regiszterből (Adatok irányának nyilvántartása): 1 = kimenet;
  • az első CR1 vezérlőregiszter kimenetkor a push-pull módot (1) vagy a nyitott leeresztést (0) állítja be; mivel a LED-eket katódokkal kötöm a chiphez, itt hagyok nullákat;
  • a második CR2 vezérlőregiszter kimenetkor az órajelet állítja be: 1 = 10 MHz

#define PA_DDR     *(volatile uint8_t *)0x005002
#define PA_CR2     *(volatile uint8_t *)0x005004
#define PD_DDR     *(volatile uint8_t *)0x005011
#define PD_CR2     *(volatile uint8_t *)0x005013
#define PC_DDR     *(volatile uint8_t *)0x00500C
#define PC_CR2     *(volatile uint8_t *)0x00500E

PA_DDR = (1<<3); //output
PA_CR2 |= (1<<3); //fast
PD_DDR = (1<<3); //output
PD_CR2 |= (1<<3); //fast
PC_DDR = ((1<<3) | (1<<5) | (1<<6) | (1<<7)); //output
PC_CR2 |= ((1<<3) | (1<<5) | (1<<6) | (1<<7)); //fast

PWM beállítás

Először is határozzuk meg a kifejezéseket:

  • PWM frekvencia – az időzítő ketyegésének gyakorisága;
  • Automatikus újratöltés, AR – automatikusan betölthető érték, ameddig az időzítő számol (impulzus periódus);
  • Frissítési esemény, UEV – esemény, amely akkor következik be, amikor az időzítő AR-ig számol;
  • PWM üzemi ciklus – PWM munkaciklus, amelyet gyakran „felhasználási tényezőnek” neveznek;
  • Érték rögzítése/összehasonlítása – a rögzítés/összehasonlítás értéke, amelyhez az időzítő számított tesz valamit (PWM esetén megfordítja a kimeneti jelet);
  • Előtöltési érték – előre feltöltött érték. Hasonlítsa össze az értéket nem változhat, amíg az időzítő ketyeg, különben a PWM ciklus megszakad. Ezért az új átvitt értékeket egy pufferbe helyezik, és kihúzzák, amikor az időzítő eléri a visszaszámlálás végét, és visszaáll;
  • Élhez igazított и Középre igazított módok – igazítás a határ mentén és a középpontban, ugyanaz, mint az Atmelé Gyors PWM и Fáziskorrekt PWM.
  • OCiREF, Output Compare Reference Signal – a referencia kimeneti jel, valójában az, ami a megfelelő lábon PWM módban megjelenik.

Amint az már a kivezetésből kiderül, két időzítő rendelkezik PWM-képességgel – az első és a második. Mindkettő 16 bites, az elsőnek sok további funkciója van (különösen, hogy felfelé és lefelé is számolhat). Mindkettőnknek egyformán kell működnie, ezért úgy döntöttem, hogy a nyilvánvalóan szegényebb másodikkal kezdem, nehogy véletlenül olyat használjunk, ami nincs. Némi probléma az, hogy a referencia kézikönyvben az összes időzítő PWM funkcióinak leírása az első időzítőről szóló fejezetben található (17.5.7 PWM mód), így folyamatosan oda-vissza kell ugrálni a dokumentumban.

Az STM8 PWM-nek fontos előnye van az Atmegán lévő PWM-mel szemben:

Olvassa el a 2. adatlapot: SPI az STM32-n; PWM, időzítők és megszakítások az STM8-on
Határhoz igazított PWM
Fiókkonfiguráció alulról felfelé
Az alulról felfelé történő számlálás akkor aktív, ha a DIR bit a TIM_CR1 regiszterben törlődik
Példa
A példa az első PWM módot használja. Az OCiREF PWM referenciajelet magasan tartják, amíg a TIM1_CNT < TIM1_CCRI. Ellenkező esetben alacsony szintre van szükség. Ha a TIM1_CCRI regiszterben lévő összehasonlítási érték nagyobb, mint az automatikus betöltési érték (TIM1_ARR regiszter), az OCiREF jel 1-en marad. Ha az összehasonlító érték 0, az OCiREF értéke nulla....

STM8 időzítő közben frissítési esemény először ellenőrzi összehasonlítani az értéket, és csak ezután állít elő referenciajelet. Az Atmega időzítője először felcsavarodik, majd összehasonlítja, ami azt eredményezi compare value == 0 a kimenet egy tű, amit valahogyan kezelni kell (pl. programozottan megfordítva a logikát).

Tehát mit akarunk tenni: 8 bites PWM (AR == 255), alulról felfelé számolva, a szegély mentén történő igazítással. Mivel az izzók katódokkal csatlakoznak a chiphez, a PWM-nek 0-t kell adnia (LED bekapcsolva), amíg összehasonlítani az értéket és 1 után.

Néhányról már olvashattunk PWM mód, így megtaláljuk a második időzítő szükséges regiszterét, ha a kézikönyvben erre a kifejezésre keresünk (18.6.8 - TIMx_CCMR1):

Olvassa el a 2. adatlapot: SPI az STM32-n; PWM, időzítők és megszakítások az STM8-on
110: Első PWM mód – ha alulról felfelé számol, az első csatorna aktív, miközben TIMx_CNT < TIMx_CCR1. Ellenkező esetben az első csatorna inaktív. [továbbiakban a dokumentumban hibás másolás-beillesztés található az 1-es időzítőből] 111: Második PWM mód – alulról felfelé számolva az első csatorna inaktív, míg TIMx_CNT < TIMx_CCR1. Ellenkező esetben az első csatorna aktív.

Mivel a LED-ek katódokkal vannak összekötve az MK-val, a második mód megfelel nekünk (az első is, de ezt még nem tudjuk).

Olvassa el a 2. adatlapot: SPI az STM32-n; PWM, időzítők és megszakítások az STM8-on
3. bit OC1PE: Engedélyezze az 1. láb előtöltését
0: A TIMx_CCR1 előtöltési regisztere le van tiltva. A TIMx_CCR1-be bármikor írhat. Az új érték azonnal működik.
1: A TIMx_CCR1 előtöltési regisztere engedélyezett. Az olvasási/írási műveletek hozzáférnek az előtöltési regiszterhez. Az előre betöltött TIMx_CCR1 érték minden frissítési esemény során betöltődik az árnyékregiszterbe.
*Megjegyzés: A PWM mód megfelelő működéséhez engedélyezni kell az előtöltési regisztereket. Ez nem szükséges egyjeles módban (az OPM bit a TIMx_CR1 regiszterben van beállítva).

Oké, kapcsoljunk be mindent, ami a második időzítő három csatornájához szükséges:

#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 nyolc bites regiszterből áll, 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 időzítő csak alulról felfelé tud számolni, igazítás a szegély mentén, semmit nem kell változtatni. Állítsuk például a frekvenciaosztót 256-ra. A második időzítőnél az osztó a TIM2_PSCR regiszterben van beállítva, és kettő hatványa:

#define TIM2_PSCR  *(volatile uint8_t *)0x00530E

TIM2_PSCR = 8;

Már csak a következtetéseket és magát a második időzítőt kell bekapcsolni. Az első problémát a regiszterek oldják meg Rögzítés/Összehasonlítás engedélyezése: két, három csatorna van rajtuk aszimmetrikusan szétszórva. Itt azt is megtudhatjuk, hogy lehetőség van a jel polaritásának megváltoztatására, pl. elvileg lehetséges volt PWM mód 1. Ezt í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 elindítjuk az időzítőt a TIMx_CR1 regiszterben:

Olvassa el a 2. adatlapot: SPI az STM32-n; PWM, időzítők és megszakítások az STM8-on

#define TIM2_CR1   *(volatile uint8_t *)0x005300

TIM2_CR1 |= 1;

Írjunk egy egyszerű AnalogWrite() analógot, amely összehasonlítás céljából átviszi a tényleges értékeket az időzítőbe. A regiszterek neve kiszámítható Regiszterek rögzítése/összehasonlítása, minden csatornához kettő van: az alacsony rendű 8 bit a TIM2_CCRxL-ben és a magasabb rendűek a TIM2_CCRxH-ban. Mivel 8 bites PWM-et hoztunk létre, elegendő csak a legkevésbé jelentős 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ó észreveszi, hogy enyhén hibás PWM-ünk van, nem tud 100%-os kitöltést produkálni (maximum 255-ös értéknél a jel egy időzítőciklusra invertálódik). A LED-ek esetében ez nem számít, és a figyelmes olvasó már sejtheti, hogyan kell javítani.

A második időzítő PWM-je működik, menjünk tovább az elsőre.

Az első időzítőnek pontosan ugyanazok a bitjei vannak ugyanazokban a regiszterekben (csak arról van szó, hogy azokat a biteket, amelyek a második időzítőben „fenntartva” maradtak, az elsőben aktívan használják mindenféle speciális dologra). Ezért elég az adatlapon megkeresni ugyanazon regiszterek címét és bemásolni a kódot. Nos, változtasd meg a frekvenciaosztó értékét, mert... az első időzítő nem kettős hatványt, hanem pontos 16 bites értéket akar fogadni két regiszterben Előskálázó magas и Elő/Utó. Mindent megteszünk és... az első időzítő nem működik. Mi a helyzet?

A probléma csak úgy oldható meg, ha végignézzük az 1. időzítő vezérlőregisztereiről szóló teljes részt, ahol azt keressük, amelyik a második időzítőben nem rendelkezik. Lesz 17.7.30 Törési regiszter (TIM1_BKR), ahol ez a bit van:

Olvassa el a 2. adatlapot: SPI az STM32-n; PWM, időzítők és megszakítások az STM8-on
Főkimenet engedélyezése

#define TIM1_BKR   *(volatile uint8_t *)0x00526D

TIM1_BKR = (1<<7);

Most már ennyi, a kód ott.

Olvassa el a 2. adatlapot: SPI az STM32-n; PWM, időzítők és megszakítások az STM8-on

STM8 multiplex

Multiplexelés STM8-on

A harmadik mini-projekt nyolc RGB LED-et csatlakoztat a második időzítőhöz PWM módban, és különböző színeket mutat be. Ez a LED-multiplexelés koncepcióján alapul, ami azt jelenti, hogy ha nagyon-nagyon gyorsan be- és kikapcsolja a LED-eket, akkor számunkra úgy tűnik, hogy folyamatosan világítanak (a látás kitartása, vizuális észlelés tehetetlensége). Egyszer megtettem valami ilyesmi az Arduino-n.

A munka algoritmusa így néz ki:

  • csatlakoztatta az első RGB LED anódját;
  • meggyújtotta, elküldve a szükséges jeleket a katódoknak;
  • várt a PWM ciklus végéig;
  • csatlakoztatta a második RGB LED anódját;
  • meggyújtotta...

Hát stb. Természetesen a szép működéshez szükséges az anód csatlakoztatása és a LED egyidejű „gyújtása”. Nos, vagy majdnem. Mindenesetre olyan kódot kell írnunk, amely a második időzítő három csatornáján adja ki az értékeket, módosítsa azokat az UEV elérésekor, és ezzel egyidejűleg módosítsa az aktuálisan aktív RGB LED-et.

Mivel a LED váltás automatikus, létre kell hoznunk egy "videómemóriát", amelyből a megszakításkezelő adatokat kap. Ez egy egyszerű tömb:

uint8_t colors[8][3];

Egy adott LED színének megváltoztatásához elegendő a szükséges értékeket ebbe a tömbbe írni. És a változó lesz felelős az aktív LED számáért

uint8_t cnt;

Demux

A megfelelő multiplexeléshez furcsa módon egy CD74HC238 demultiplexerre van szükségünk. Demultiplexer - egy chip, amely megvalósítja az operátort a hardverben <<. Három bemeneti tűn keresztül (0, 1 és 2 bit) betáplálunk egy hárombites X számot, és válaszul aktiválja a kimeneti számot (1<<X). A chip fennmaradó bemeneteit a teljes terv méretezésére használják. Erre a chipre nem csak a mikrokontroller elfoglalt tűinek számának csökkentésére van szükségünk, hanem a biztonság érdekében is – hogy véletlenül se kapcsoljunk be a lehetségesnél több LED-et, és ne égessük el az MK-t. A chip egy fillérbe kerül, és mindig tartsa otthoni gyógyszeres szekrényében.

A CD74HC238-unk lesz felelős a kívánt LED anódjának feszültségellátásáért. Teljes értékű multiplexben P-MOSFET-en keresztül adna feszültséget az oszlopra, de ebben a demóban ez közvetlenül lehetséges, mert szerint 20 mA-t vesz fel Maximális abszolút értéke az adatlapon. Tól től adatlap CD74HC238 szükségünk van pinoutokra és erre a csalólapra:

Olvassa el a 2. adatlapot: SPI az STM32-n; PWM, időzítők és megszakítások az STM8-on
H = magas feszültségszint, L = alacsony feszültségszint, X – nem érdekel

Az E2-t és az E1-et a földre, az E3-at, az A0-t, az A1-et és az A3-at az STM5 PD3, PC4, PC5 és PC8 érintkezőire kötjük. Mivel a fenti táblázat alacsony és magas szinteket is tartalmaz, ezeket a csapokat push-pull csapokként konfiguráljuk.

PWM

A második időzítő PWM-je ugyanúgy van konfigurálva, mint az előző történetben, két különbséggel:

Először is engedélyeznünk kell a megszakítást Esemény frissítése (UEV), amely meghív egy funkciót, amely átkapcsolja az aktív LED-et. Ez a bit megváltoztatásával történik Frissítés megszakítás engedélyezése sokatmondó nevű regiszterben

Olvassa el a 2. adatlapot: SPI az STM32-n; PWM, időzítők és megszakítások az STM8-on
Megszakítás engedélyezése regiszter

#define TIM2_IER   *(volatile uint8_t *)0x005303

//enable interrupt
TIM2_IER = 1;

A második különbség a multiplexelés jelenségével kapcsolatos, mint pl szellemkép – diódák parazita fénye. Esetünkben ez abból adódhat, hogy az időzítő, miután megszakítást okozott az UEV-n, továbbra is ketyeg, és a megszakításkezelőnek nincs ideje átkapcsolni a LED-et, mielőtt az időzítő írni kezdene valamit a lábakra. Ennek leküzdéséhez meg kell fordítania a logikát (0 = maximális fényerő, 255 = semmi sem világít), és kerülni kell az extrém munkaciklus értékeket. Azok. győződjön meg arról, hogy az UEV után a LED-ek teljesen kialszanak egy PWM ciklusra.

Polaritás megváltoztatása:

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

Kerülje az r, g és b értékét 255-re, és ne felejtse el megfordítani őket használat közben.

Megszakítja

A megszakítás lényege, hogy bizonyos körülmények között a chip leállítja a főprogram végrehajtását, és meghív valamilyen külső függvényt. A megszakítások külső vagy belső hatások miatt következnek be, beleértve az időzítőt is.

Amikor először létrehoztunk egy projektet az ST Visual Developban, amellett main.c kaptunk egy ablakot egy rejtélyes aktával stm8_interrupt_vector.c, automatikusan bekerül a projektbe. Ebben a fájlban minden megszakításhoz egy funkció van hozzárendelve NonHandledInterrupt. A függvényünket a kívánt megszakításhoz kell kötnünk.

Az adatlapon van egy táblázat a megszakítási vektorokról, ahol megtaláljuk a szükségeseket:

Olvassa el a 2. adatlapot: SPI az STM32-n; PWM, időzítők és megszakítások az STM8-on
13 TIM2 frissítés/túlcsordulás
14 TIM2 rögzítés/összehasonlítás

Ki kell cserélnünk a LED-et az UEV-nél, ezért kell a 13-as megszakítás.

Ennek megfelelően először is az aktában stm8_interrupt_vector.c módosítsa a 13. számú megszakításért felelős funkció alapértelmezett nevét (IRQ13) a sajátjára:

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

Másodszor, létre kell hoznunk egy fájlt main.h a következő tartalommal:

#ifndef __MAIN_H
#define __MAIN_H

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

És végül írja be ezt a függvényt a sajátjába main.c:

@far @interrupt void TIM2_Overflow (void)
{
    PD_ODR &= ~(1<<5); // вырубаем демультиплексор
    PC_ODR = (cnt<<3); // записываем в демультиплексор новое значение
    PD_ODR |= (1<<5); // включаем демультиплексор

    TIM2_SR1 = 0; // сбрасываем флаг Update Interrupt Pending

    cnt++; 
    cnt &= 7; // двигаем счетчик LED

    TIM2_CCR1L = ~colors[cnt][0]; // передаем в буфер инвертированные значения
    TIM2_CCR2L = ~colors[cnt][1]; // для следующего цикла ШИМ
    TIM2_CCR3L = ~colors[cnt][2]; // 

    return;
}

Már csak a megszakítások engedélyezése van hátra. Ez az assembler paranccsal történik rim - benne kell keresned Programozási kézikönyv:

//enable interrupts
_asm("rim");

Egy másik assembler parancs az sim – kikapcsolja a megszakításokat. Ezeket ki kell kapcsolni, amíg új értékeket írnak a „videomemóriába”, hogy a rossz pillanatban okozott megszakítás ne rontsa el a tömböt.

Minden kód - a GitHubon.

Olvassa el a 2. adatlapot: SPI az STM32-n; PWM, időzítők és megszakítások az STM8-on

Ha legalább valaki hasznosnak találja ezt a cikket, akkor nem hiába írtam. Észrevételeket, észrevételeket szívesen fogadok, igyekszem mindenre válaszolni.

Forrás: will.com

Hozzászólás