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

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

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

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

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

avatud kollektor, âpush-pullâ, alternatiivne âpush-pullâ, alternatiivne avatud kollektor
âPush-pullâ (push-pull) â tavaline vĂ€ljund Arduino-lt, pin saab vĂ”tta vÀÀrtusi kas HIGH vĂ”i LOW. Kuid "avatud kollektoriga" tekivad , kuigi tegelikult on kĂ”ik lihtne:


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

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

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

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

Bitid BR seadistavad kontrolleri taktsageduse jagurit, mÀÀrates seega sageduse, millega SPI töötab. STM32 sagedus on meil 72 MHz, LED-kontroller, vastavalt selle andmelehele, töötab sagedusega kuni 25 MHz, seega peab jagama neljaga (BR[2:0] = 001).
#define _SPI_CR1 0x00
#define BR_0 0x0008
#define BR_1 0x0010
#define BR_2 0x0020
_SPI2_ (_SPI_CR1) |= BR_0;// pclk/42. Seadke bitid CPOL ja CPHA, et mÀÀrata andmete edastamise ja serial interface taktimise suhe (vt skeemi lehel 240).
Kuna me siin loeme andmelehte, mitte skeeme, uurime pigem CPOL ja CPHA bittide tekstikirjeldust lehelt 704 (SPI Ăldine kirjeldus):

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

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

<âŠ>

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

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

Kirjutame:
#define _SPI_DR 0x0C
#define _SPI_SR 0x08
#define BSY 0x0080
#define TXE 0x0002
void dm_shift16(uint16_t value)
{
_SPI2_(_SPI_DR) = value; //send 2 bytes
while (!(_SPI2_(_SPI_SR) & TXE)); //wait until they're sent
}Kuna me peame edastama 16 korda kaks bitti, LED-juhtija vÀljundite arvu jÀrgi, siis midagi sellist:
void sendLEDdata()
{
LAT_low();
uint8_t k = 16;
do
{ k--;
dm_shift16(leds[k]);
} while (k);
while (_SPI2_(_SPI_SR) & BSY); // lÔpetage edastus
LAT_pulse();
}Aga me ei oska LAT pinni tÔmmata, seega naaseme I/O juurde.
MÀÀrame pinid
STM32F1 registrid, mis vastutavad pinni oleku eest, on ĂŒsna eriskummalised. On selge, et neid on rohkem kui Atmegi omasid, kuid nad erinevad ka teiste STM kiipide omadest. Jaotis 9.1 Ăldine ĂŒlevaade GPIO:

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

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

Ja nÀeme, et peame kirjutama midagi bittidesse 20. kuni 31. koht.
Oleme juba varem saanud aru, mida me pin'ide osas tahame, seega seekord ma ekraanipilti ei lisa, lihtsalt ĂŒtlen, et MODE mÀÀrab suuna (sisend, kui mĂ”lemad bitid on 0) ja pin'ide kiirus (me vajame 50MHz, st mĂ”lemad pin'id peavad olema â1â), samas kui CNF mÀÀrab reĆŸiimi: tavaline âtĂ”mba-tĂ”ukamineâ â 00, âalternatiivneâ â 10. Nagu ĂŒlal nĂ€ha, on kĂ”igil pin'idel mÀÀratud kolmas allpool olev bit (CNF0), mis seab nad reĆŸiimi. ujuv sisend.
Kuna plaanin selle kiibiga veel midagi teha, olen mugavuse huvides defineerinud kĂ”ik vĂ”imalikud vÀÀrtused MODE ja CNF nii alumistele kui ka ĂŒlemistele kontrollregisteritele.
Nii see ongi.
#define CNF0_0 0x00000004
#define CNF0_1 0x00000008
#define CNF1_0 0x00000040
#define CNF1_1 0x00000080
#define CNF2_0 0x00000400
#define CNF2_1 0x00000800
#define CNF3_0 0x00004000
#define CNF3_1 0x00008000
#define CNF4_0 0x00040000
#define CNF4_1 0x00080000
#define CNF5_0 0x00400000
#define CNF5_1 0x00800000
#define CNF6_0 0x04000000
#define CNF6_1 0x08000000
#define CNF7_0 0x40000000
#define CNF7_1 0x80000000
#define CNF8_0 0x00000004
#define CNF8_1 0x00000008
#define CNF9_0 0x00000040
#define CNF9_1 0x00000080
#define CNF10_0 0x00000400
#define CNF10_1 0x00000800
#define CNF11_0 0x00004000
#define CNF11_1 0x00008000
#define CNF12_0 0x00040000
#define CNF12_1 0x00080000
#define CNF13_0 0x00400000
#define CNF13_1 0x00800000
#define CNF14_0 0x04000000
#define CNF14_1 0x08000000
#define CNF15_0 0x40000000
#define CNF15_1 0x80000000
#define MODE0_0 0x00000001
#define MODE0_1 0x00000002
#define MODE1_0 0x00000010
#define MODE1_1 0x00000020
#define MODE2_0 0x00000100
#define MODE2_1 0x00000200
#define MODE3_0 0x00001000
#define MODE3_1 0x00002000
#define MODE4_0 0x00010000
#define MODE4_1 0x00020000
#define MODE5_0 0x00100000
#define MODE5_1 0x00200000
#define MODE6_0 0x01000000
#define MODE6_1 0x02000000
#define MODE7_0 0x10000000
#define MODE7_1 0x20000000
#define MODE8_0 0x00000001
#define MODE8_1 0x00000002
#define MODE9_0 0x00000010
#define MODE9_1 0x00000020
#define MODE10_0 0x00000100
#define MODE10_1 0x00000200
#define MODE11_0 0x00001000
#define MODE11_1 0x00002000
#define MODE12_0 0x00010000
#define MODE12_1 0x00020000
#define MODE13_0 0x00100000
#define MODE13_1 0x00200000
#define MODE14_0 0x01000000
#define MODE14_1 0x02000000
#define MODE15_0 0x10000000
#define MODE15_1 0x20000000Meie pin'id asuvad B-porti (baasiaadress â 0x40010C00), kood:
#define _PORTB_(mem_offset) (*(volatile uint32_t *)(0x40010C00 + (mem_offset)))
#define _BRR 0x14
#define _BSRR 0x10
#define _CRL 0x00
#define _CRH 0x04
//ĐžŃĐżĐŸĐ»ŃĐ·ŃĐ”ĐŒ ŃŃĐ°ĐœĐŽĐ°ŃŃĐœŃĐč SPI2: MOSI ĐœĐ° B15, CLK ĐœĐ° B13
//LAT ĐżŃŃŃŃ Đ±ŃĐŽĐ”Ń ĐœĐ° ĐœĐ”ĐžŃĐżĐŸĐ»ŃĐ·ŃĐ”ĐŒĐŸĐŒ MISO â B14
//ĐŸŃĐžŃĐ°Đ”ĐŒ ĐŽĐ”ŃĐŸĐ»ŃĐœŃĐč бОŃ, ĐŸĐœ ĐœĐ°ĐŒ ŃĐŸŃĐœĐŸ ĐœĐ” ĐœŃĐ¶Đ”Đœ
_PORTB_ (_CRH) &= ~(CNF15_0 | CNF14_0 | CNF13_0 | CNF12_0);
//алŃŃĐ”ŃĐœĐ°ŃĐžĐČĐœŃĐ” ŃŃĐœĐșŃОО ĐŽĐ»Ń MOSI Đž SCK
_PORTB_ (_CRH) |= CNF15_1 | CNF13_1;
//50 ĐĐŃ, MODE = 11
_PORTB_ (_CRH) |= MODE15_1 | MODE15_0 | MODE14_1 | MODE14_0 | MODE13_1 | MODE13_0;Seega on vÔimalik kirjutada defineeringud LAT'ile, mis toimib BRR ja BSRR registritega:
/*** LAT pulse â high, then low */
#define LAT_pulse() _PORTB_(_BSRR) = (1<<14); _PORTB_(_BRR) = (1<<14)
#define LAT_low() _PORTB_(_BRR) = (1<<14)(LAT_low lihtsalt harjumusest, selline on alati olnud, las jÀÀb sinna)
NĂŒĂŒd on kĂ”ik juba suurepĂ€rane, aga siiski ei tööta. Sest see on STM32, siin ökonoomitakse elektrit, seega tuleb sisse lĂŒlitada vajaliku perifeeria kell.
LĂŒlitame sisse kellatöö
Kella eest vastutavad kellad, see on Clock. Ja me oleme juba mĂ€rganud akronĂŒĂŒmi RCC. Otsime selle dokumentatsioonist: see on Reset and Clock Control (TaaskĂ€ivitamise ja kella juhtimine).
Kuna ĂŒlal on öeldud, et Ă”nneks on STM inimestelt kĂ”ige keerulisema kellatöö teema meie eest lahendanud, mille eest neile suur aitĂ€h (veel kord annan lingi) , et oleks selge, kui keeruline see on). Me vajame vaid registreid, mis vastutavad perifeeria kellatöö sisse lĂŒlitamise eest (Peripheral Clock Enable Registers). Alustuseks leiame RCC pĂ”hialuse, see on "MĂ€lu kaartide" alguses:
![]()
#define _RCC_(mem_offset) (*(volatile uint32_t *)(0x40021000 + (mem_offset)))Ja edasi kas klĂ”psata lingile, kus proovida tabelist midagi leida, vĂ”i veel parem, vaadata sisse lĂŒlitavate registeride kirjeldusi osadesse, mis kĂ€sitlevad lĂŒlitusregistreid. Siit leiame RCC_APB1ENR ja RCC_APB2ENR:


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

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

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

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

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

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

Bitt 3 OC1PE: LĂŒlita sisse vĂ€ljundi 1 eelkoormus
0: Eelkoormuse register TIMx_CCR1 on vĂ€lja lĂŒlitatud. TIMx_CCR1-sse saab kirjutada igal ajal. Uus vÀÀrtus töötab kohe.
1: Eelkoormuse register TIMx_CCR1 on sisse lĂŒlitatud. Lugemis-/kirjutamisoperatsioonid pöörduvad eelkoormuse registri poole. Eelkoormatud vÀÀrtus TIMx_CCR1 laaditakse varjatud registrisse iga uuenduse sĂŒndmuse ajal.
*MĂ€rkus: PWM reĆŸiimi korrektseks tööks peavad eelkoormuse registrid olema sisse lĂŒlitatud. See ei ole vajalik ĂŒksiku signaali reĆŸiimis (registris TIMx_CR1 on OPM bitt seatud).
Selge, lĂŒlitame sisse kĂ”ik, mis on vajalik teise taimeri kolme kanali jaoks:
#define TIM2_CCMR1 *(volatile uint8_t *)0x005307
#define TIM2_CCMR2 *(volatile uint8_t *)0x005308
#define TIM2_CCMR3 *(volatile uint8_t *)0x005309
#define PWM_MODE2 0x70 //PWM mode 2, 0b01110000
#define OCxPE 0x08 //preload enable
TIM2_CCMR1 = (PWM_MODE2 | OCxPE);
TIM2_CCMR2 = (PWM_MODE2 | OCxPE);
TIM2_CCMR3 = (PWM_MODE2 | OCxPE);AR koosneb kahest kaheksabitisest registrist, siin on kÔik lihtne:
#define TIM2_ARRH *(volatile uint8_t *)0x00530F
#define TIM2_ARRL *(volatile uint8_t *)0x005310
TIM2_ARRH = 0;
TIM2_ARRL = 255;Teine taimer oskab lugeda ainult alt ĂŒles, serva jĂ€rgi joondamine, midagi muuta ei ole vaja. Seame sageduse jaguri nĂ€iteks 256. Teise taimeri jagur seatakse registris TIM2_PSCR ja see on kahe astme:
#define TIM2_PSCR *(volatile uint8_t *)0x00530E
TIM2_PSCR = 8;JÀÀnud on lĂŒlitada sisse vĂ€ljundid ja ise teine taimer. Esimene ĂŒlesanne lahendatakse registreid Capture/Compare Enable: neid on kaks, kolm kanalit on jaotatud asĂŒmmeetriliselt. Siin saame ka teada, et signaali polariteeti on vĂ”imalik muuta, st pĂ”himĂ”tteliselt oleks saanud kasutada ka PWM ReĆŸiim 1. Kirjutame:
#define TIM2_CCER1 *(volatile uint8_t *)0x00530A
#define TIM2_CCER2 *(volatile uint8_t *)0x00530B
#define CC1E (1<<0) // CCER1
#define CC2E (1<<4) // CCER1
#define CC3E (1<<0) // CCER2
TIM2_CCER1 = (CC1E | CC2E);
TIM2_CCER2 = CC3E;Noh ja lÔpuks kÀivitame taimeri registris TIMx_CR1:

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

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

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

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

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

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