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

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

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

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

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

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


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

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

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

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

Bitid BR mÀÀravad kontrolleri taktsageduse jaguri, mÀÀrates seelÀbi sageduse, millega SPI töötab. STM32 sagedus on meil 72 MHz, LED-draiver töötab vastavalt oma andmelehe andmetele kuni 25 MHz, seega tuleb jagada neljaga (BR[2:0] = 001).
#define _SPI_CR1 0x00
#define BR_0 0x0008
#define BR_1 0x0010
#define BR_2 0x0020
_SPI2_ (_SPI_CR1) |= BR_0;// pclk/42. MÀÀrake CPOL ja CPHA bitid, et mÀÀrata suhe andmete edastamise ja jĂ€rjestikuse liidese sĂŒnkroonimise vahel (vt skeemi lk 240)
Kuna me loeme nĂŒĂŒd andmelehte, mitte skeeme, uurime parem CPOL ja CPHA bitide tekstikirjeldust lk 704 (SPI ĂŒldkirjeldus):

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

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

<âŠ>

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

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

Kirjutame:
#define _SPI_DR 0x0C
#define _SPI_SR 0x08
#define BSY 0x0080
#define TXE 0x0002
void dm_shift16(uint16_t value)
{
_SPI2_(_SPI_DR) = value; //send 2 bytes
while (!(_SPI2_(_SPI_SR) & TXE)); //wait until they're sent
}Kuna peame edastama 16 korda kaks baiti, LED-draiveri vÀljundite arvu pÔhjal, siis nÀeb see enam-vÀhem vÀlja nii:
void sendLEDdata()
{
LAT_low();
uint8_t k = 16;
do
{ k--;
dm_shift16(leds[k]);
} while (k);
while (_SPI2_(_SPI_SR) & BSY); \/\/ lÔpetame edastuse
LAT_pulse();
}Aga me ei oska veel LAT pinni aktiveerida, seega naaseme I/O juurde.
MÀÀrame pinid
STM32F1-s on pinide olekud on ĂŒsna ebatavalised. Selge on, et neid on rohkem kui Atmega-s, kuid need erinevad ka teistest STM-kiipidest. Jagunemine 9.1 Ălevaade GPIO-st:

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

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

Ja nÀeme, et peame kirjutama midagi bittidesse 20. kuni 31. positsioonideni.
Oleme juba eelnevalt arutanud, mida me pin'ide osas soovime, seega ei kasuta ma siin ekranipilti, vaid ĂŒtlen lihtsalt, et MODE mÀÀrab suuna (sisend, kui mĂ”lemad bitid on 0) ja pin'ide kiirus (meil on vaja 50MHz, st mĂ”lemad pin'id '1' peavad olema), ning CNF mÀÀrab reĆŸiimi: tavaline 'tĂ”mba-tĂ”uka' â 00, 'alternatiivne' â 10. Nagu ĂŒlal nĂ€eme, on kĂ”igil pin'idel mÀÀratud kolmas bit (CNF0), see seab need reĆŸiimi. ujuv sisend.
Kuna plaanin selle kiibiga veel midagi teha, olen lihtsuse huvides defineerinud kĂ”ik vĂ”imalikud vÀÀrtused MODE ja CNF nii alumistele kui ka ĂŒlemistele kontrollregistritele.
Nii et kuidagi selliselt
#define CNF0_0 0x00000004
#define CNF0_1 0x00000008
#define CNF1_0 0x00000040
#define CNF1_1 0x00000080
#define CNF2_0 0x00000400
#define CNF2_1 0x00000800
#define CNF3_0 0x00004000
#define CNF3_1 0x00008000
#define CNF4_0 0x00040000
#define CNF4_1 0x00080000
#define CNF5_0 0x00400000
#define CNF5_1 0x00800000
#define CNF6_0 0x04000000
#define CNF6_1 0x08000000
#define CNF7_0 0x40000000
#define CNF7_1 0x80000000
#define CNF8_0 0x00000004
#define CNF8_1 0x00000008
#define CNF9_0 0x00000040
#define CNF9_1 0x00000080
#define CNF10_0 0x00000400
#define CNF10_1 0x00000800
#define CNF11_0 0x00004000
#define CNF11_1 0x00008000
#define CNF12_0 0x00040000
#define CNF12_1 0x00080000
#define CNF13_0 0x00400000
#define CNF13_1 0x00800000
#define CNF14_0 0x04000000
#define CNF14_1 0x08000000
#define CNF15_0 0x40000000
#define CNF15_1 0x80000000
#define MODE0_0 0x00000001
#define MODE0_1 0x00000002
#define MODE1_0 0x00000010
#define MODE1_1 0x00000020
#define MODE2_0 0x00000100
#define MODE2_1 0x00000200
#define MODE3_0 0x00001000
#define MODE3_1 0x00002000
#define MODE4_0 0x00010000
#define MODE4_1 0x00020000
#define MODE5_0 0x00100000
#define MODE5_1 0x00200000
#define MODE6_0 0x01000000
#define MODE6_1 0x02000000
#define MODE7_0 0x10000000
#define MODE7_1 0x20000000
#define MODE8_0 0x00000001
#define MODE8_1 0x00000002
#define MODE9_0 0x00000010
#define MODE9_1 0x00000020
#define MODE10_0 0x00000100
#define MODE10_1 0x00000200
#define MODE11_0 0x00001000
#define MODE11_1 0x00002000
#define MODE12_0 0x00010000
#define MODE12_1 0x00020000
#define MODE13_0 0x00100000
#define MODE13_1 0x00200000
#define MODE14_0 0x01000000
#define MODE14_1 0x02000000
#define MODE15_0 0x10000000
#define MODE15_1 0x20000000Meie pin'id asuvad port B' s (pÔhisult 0x40010C00), kood:
#define _PORTB_(mem_offset) (*(volatile uint32_t *)(0x40010C00 + (mem_offset)))
#define _BRR 0x14
#define _BSRR 0x10
#define _CRL 0x00
#define _CRH 0x04
//ĐžŃĐżĐŸĐ»ŃĐ·ŃĐ”ĐŒ ŃŃĐ°ĐœĐŽĐ°ŃŃĐœŃĐč SPI2: MOSI ĐœĐ° B15, CLK ĐœĐ° B13
//LAT ĐżŃŃŃŃ Đ±ŃĐŽĐ”Ń ĐœĐ° ĐœĐ”ĐžŃĐżĐŸĐ»ŃĐ·ŃĐ”ĐŒĐŸĐŒ MISO â B14
//ĐŸŃĐžŃĐ°Đ”ĐŒ ĐŽĐ”ŃĐŸĐ»ŃĐœŃĐč бОŃ, ĐŸĐœ ĐœĐ°ĐŒ ŃĐŸŃĐœĐŸ ĐœĐ” ĐœŃĐ¶Đ”Đœ
_PORTB_ (_CRH) &= ~(CNF15_0 | CNF14_0 | CNF13_0 | CNF12_0);
//алŃŃĐ”ŃĐœĐ°ŃĐžĐČĐœŃĐ” ŃŃĐœĐșŃОО ĐŽĐ»Ń MOSI Đž SCK
_PORTB_ (_CRH) |= CNF15_1 | CNF13_1;
//50 ĐĐŃ, MODE = 11
_PORTB_ (_CRH) |= MODE15_1 | MODE15_0 | MODE14_1 | MODE14_0 | MODE13_1 | MODE13_0;Ja seega vÔib kirjutada defineeringud LAT jaoks, mis liigub registrite BRR ja BSRR abil:
/*** LAT pulse â high, then low */
#define LAT_pulse() _PORTB_(_BSRR) = (1<<14); _PORTB_(_BRR) = (1<<14)
#define LAT_low() _PORTB_(_BRR) = (1<<14)(LAT_low lihtsalt harjumusest, on alati olnud, jÀtkuge selle sÀilitamist)
NĂŒĂŒd on kĂ”ik juba tore, aga ei tööta. Sest see on STM32, siin sÀÀstetakse elektrit, seega peab vajaliku perifeeria kĂ€ivitama.
KĂ€ivitame kellamise
Kellad vastutavad taktimise eest, need on Clock. Ja me oleme juba nĂ€inud lĂŒhendit RCC. Otsime seda dokumentatsioonist: see tĂ€histab Reset and Clock Control (TaaskĂ€ivitamise ja taktimise juhtimine).
Kuidas eelnevalt öeldud, on Ônneks kÔige keerulisema taktimise osa meie eest teinud STM inimestest, mille eest suur aitÀh (veel kord jagan linki , et oleks arusaadav, kui keeruline see on). Me vajame ainult registreid, mis vastutavad perifeeria taktimise (Peripheral Clock Enable Registers) aktiveerimise eest. Alustuseks leiame RCC pÔhiaadressi, see on 'MÀlu kaartide' alguses:
![]()
#define _RCC_(mem_offset) (*(volatile uint32_t *)(0x40021000 + (mem_offset)))Edasi on kas vaja klikkida lingile, kus proovida tabelist midagi leida, vÔi, palju parem, joosta lÀbi aktiveerivate registrite kirjeldused osadest, mis rÀÀgivad aktiveerimisregister. Kus me leiame RCC_APB1ENR ja RCC_APB2ENR:


Ja neis, vastavalt, bitid, mis aktiveerivad SPI2, IOPB (I/O Port B) ja alternatiivfunktsioonid (AFIO).
#define _APB2ENR 0x18
#define _APB1ENR 0x1C
#define IOPBEN 0x0008
#define SPI2EN 0x4000
#define AFIOEN 0x0001
//ĐČĐșĐ»ŃŃĐ°Đ”ĐŒ ŃаĐșŃĐžŃĐŸĐČĐ°ĐœĐžĐ” ĐżĐŸŃŃа B Đž алŃŃ. ŃŃĐœĐșŃĐžĐč
_RCC_(_APB2ENR) |= IOPBEN | AFIOEN;
//ĐČĐșĐ»ŃŃĐ°Đ”ĐŒ ŃаĐșŃĐžŃĐŸĐČĐ°ĐœĐžĐ” SPI2
_RCC_(_APB1ENR) |= SPI2EN;LÔpliku koodi leiate .
Kui on vĂ”imalust ja soovi testida, siis ĂŒhendame DM634 jĂ€rgmiselt: DAI PB15-le, DCK PB13-le, LAT PB14-le. Toitke draiverit 5 voltiga, Ă€rge unustage ĂŒhendada maapindu.

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

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

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

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

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

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

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

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

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

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

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

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