
В Es mēģināju stāstīt hobiju elektronikas inženieriem, kuri uzauguši no Arduino biksēm, kā un kāpēc viņiem vajadzētu lasīt datu lapas un citu mikrokontrolleru dokumentāciju. Teksts izrādījās liels, tāpēc praktiskus piemērus apsolīju parādīt atsevišķā rakstā. Nu viņš sevi sauca par piena sēni...
Šodien es jums parādīšu, kā izmantot datu lapas, lai atrisinātu diezgan vienkāršus, bet daudziem projektiem nepieciešamos uzdevumus STM32 (Blue Pill) un STM8 kontrolleros. Visi demo projekti ir veltīti manām mīļākajām gaismas diodēm, mēs tās apgaismosim lielos daudzumos, kam būs jāizmanto visādas interesantas perifērijas ierīces.
Teksts atkal izrādījās milzīgs, tāpēc ērtības labad veidoju saturu:
Atruna: Es neesmu inženieris, nepretendēju uz dziļām zināšanām elektronikā, raksts ir paredzēts tādiem amatieriem kā es. Patiesībā es sevi uzskatīju par mērķauditoriju pirms diviem gadiem. Ja kāds man toreiz būtu teicis, ka datu lapas nepazīstamā mikroshēmā nav biedējoši lasīt, es nebūtu veltījis daudz laika, meklējot dažus koda gabalus internetā un izgudrojot kruķus ar šķērēm un līmlenti.
Šajā rakstā galvenā uzmanība tiek pievērsta datu lapām, nevis projektiem, tāpēc kods var nebūt ļoti glīts un bieži vien ir ierobežots. Paši projekti ir ļoti vienkārši, lai gan piemēroti pirmajai iepazīšanai ar jauno mikroshēmu.
Es ceru, ka mans raksts palīdzēs kādam līdzīgā hobija iegremdēšanas posmā.
STM32
16 gaismas diodes ar DM634 un SPI
Neliels projekts, izmantojot Blue Pill (STM32F103C8T6) un DM634 LED draiveri. Izmantojot datu lapas, mēs noskaidrosim draiveri, STM IO portus un konfigurēsim SPI.
DM634
Taivānas mikroshēma ar 16 16 bitu PWM izejām, var savienot ķēdēs. Zemākās klases 12 bitu modelis ir zināms no vietējā projekta . Savulaik, izvēloties starp DM63x un labi zināmo TLC5940, es izvēlējos DM vairāku iemeslu dēļ: 1) Aliexpress TLC noteikti ir viltots, bet šis nav; 2) DM ir autonoms PWM ar savu frekvenču ģeneratoru; 3) to varēja lēti iegādāties Maskavā, nevis gaidīt sūtījumu no Ali. Un, protams, bija interesanti iemācīties pašam vadīt mikroshēmu, nevis izmantot gatavu bibliotēku. Tagad mikroshēmas galvenokārt tiek piedāvātas SSOP24 iepakojumā, tās ir viegli pielodēt ar adapteri.
Tā kā ražotājs ir taivānietis, mikroshēma ir rakstīta ķīniešu angļu valodā, kas nozīmē, ka tas būs jautri. Vispirms skatāmies uz pinout (Pin savienojums), lai saprastu, kurai kājiņai ar ko pieslēgt, un tapu aprakstu (Pin Apraksts). 16 tapas:

Līdzstrāvas izlietnes avoti (atvērta kanalizācija)
Izlietne / Atvērta kanalizācijas izvade – notekas; ieplūstošās strāvas avots; izeja ir savienota ar zemi aktīvā stāvoklī - gaismas diodes ir savienotas ar draiveri ar katodiem. Elektriski tā, protams, nav “atvērta kanalizācija” (atvērta kanalizācija), taču datu lapās šis apzīmējums tapām drenāžas režīmā bieži ir atrodams.
![]()
Ārējie rezistori starp REXT un GND, lai iestatītu izejas strāvas vērtību
Starp REXT tapu un zemējumu ir uzstādīts atsauces rezistors, kas kontrolē izeju iekšējo pretestību, skatiet diagrammu datu lapas 9. lappusē. DM634 šo pretestību var kontrolēt arī ar programmatūru, iestatot kopējo spilgtumu (globālais spilgtums); Šajā rakstā es neiedziļināšos, es vienkārši ievietošu šeit 2.2–3 kOhm rezistoru.
Lai saprastu, kā vadīt mikroshēmu, apskatīsim ierīces saskarnes aprakstu:

Jā, lūk, ķīniešu angļu valoda visā tās krāšņumā. To tulkot ir problemātiski, ja vēlaties, varat to saprast, bet ir arī cits veids - apskatiet, kā datu lapā ir aprakstīts savienojums ar funkcionāli līdzīgu TLC5940:

... Lai ievadītu datus ierīcē, ir nepieciešami tikai trīs tapas. SCLK signāla augošā mala novirza datus no SIN tapas uz iekšējo reģistru. Kad visi dati ir ielādēti, īss augsts XLAT signāls aiztur secīgi pārsūtītos datus iekšējos reģistros. Iekšējie reģistri ir vārti, ko iedarbina XLAT signāla līmenis. Visi dati vispirms tiek pārsūtīti vissvarīgākais bits.
Aizbīdnis – fiksators/fiksators/slēdzene.
Augošā mala – pulsa priekšējā mala
MSB vispirms – nozīmīgākais (kreisais) bits uz priekšu.
pulksteņa datiem – pārsūtīt datus secīgi (pa bitiem).
Vārds aizšaujamais bieži atrodams mikroshēmu dokumentācijā un tiek tulkots dažādos veidos, tāpēc sapratnes labad atļaušos
neliela izglītības programmaLED draiveris būtībā ir maiņu reģistrs. "Maiņa" (maiņa) nosaukumā - datu kustība pa bitiem ierīces iekšienē: katrs jauns iekšā ievietots bits nospiež visu ķēdi uz priekšu. Tā kā maiņas laikā neviens nevēlas novērot haotisku gaismas diožu mirgošanu, process notiek buferreģistros, kas no darba reģistriem atdalīti ar slāpētāju (aizšaujamais) ir sava veida uzgaidāmā telpa, kurā biti ir sakārtoti vēlamajā secībā. Kad viss ir gatavs, aizvars atveras un uzgaļi dodas uz darbu, nomainot iepriekšējo partiju. Vārds aizšaujamais mikroshēmu dokumentācijā gandrīz vienmēr nozīmē šādu slāpētāju neatkarīgi no tā, kādās kombinācijās tas tiek izmantots.
Tātad datu pārsūtīšana uz DM634 tiek veikta šādi: iestatiet DAI ievadi uz tālās gaismas diodes nozīmīgākā bita vērtību, velciet DCK uz augšu un uz leju; iestatiet DAI ievadi uz nākamā bita vērtību, velciet DCK; un tā tālāk, līdz visi biti ir pārsūtīti (iegriezies), pēc kura velkam LAT. To var izdarīt manuāli (bit-sprādziens), taču labāk ir izmantot īpaši šim nolūkam pielāgotu SPI interfeisu, jo tas ir pieejams mūsu STM32 divos eksemplāros.
Zilā tablete STM32F103
Ievads: STM32 kontrolieri ir daudz sarežģītāki nekā Atmega328, nekā varētu šķist biedējoši. Turklāt enerģijas taupīšanas nolūkos gandrīz visas perifērijas ierīces sākumā tiek izslēgtas, un pulksteņa frekvence ir 8 MHz no iekšējā avota. Par laimi, STM programmētāji uzrakstīja kodu, kas nodrošina mikroshēmu līdz “aprēķinātajam” 72 MHz, un visu man zināmo IDE autori to iekļāva inicializācijas procedūrā, tāpēc mums nav nepieciešams pulkstenis (bet ). Bet jums būs jāieslēdz perifērijas ierīces.
Dokumentācija: Blue Pill ir aprīkota ar populāro STM32F103C8T6 mikroshēmu, tam ir divi noderīgi dokumenti:
- mikrokontrolleriem STM32F103x8 un STM32F103xB;
- visai STM32F103 līnijai un citiem.
Datu lapā mūs varētu interesēt:
- Pinouts – skaidu pinouts – gadījumā, ja nolemjam dēļus izgatavot paši;
- Memory Map – atmiņas karte konkrētai mikroshēmai. Uzziņu rokasgrāmatā ir karte visai līnijai, un tajā ir minēti reģistri, kuru mūsu rīcībā nav.
- Pin Definition tabula – uzskaita tapu galvenās un alternatīvās funkcijas; “zilajai tabletei” internetā var atrast ērtākus attēlus ar tapu sarakstu un to funkcijām. Tāpēc mēs nekavējoties googlē Blue Pill pinout un paturam šo attēlu pie rokas:

NB: bildē no interneta bija kļūda, kas tika atzīmēta komentāros, paldies par to. Bilde ir nomainīta, bet šī ir mācība - labāk pārbaudīt informāciju nevis no datu lapām.
Mēs noņemam datu lapu, atveram uzziņu rokasgrāmatu un turpmāk izmantojam tikai to.
Procedūra: nodarbojamies ar standarta ievadi/izvadi, konfigurējam SPI, ieslēdzam nepieciešamās perifērijas ierīces.
Ieejas izejas
Atmega328 I/O ir ieviests ārkārtīgi vienkārši, tāpēc STM32 iespēju pārpilnība var radīt neskaidrības. Tagad mums ir vajadzīgi tikai secinājumi, bet pat tiem ir četras iespējas:

atvērta noteka, push-pull, alternatīva push-pull, alternatīva atvērta noteka
"Vilkt stumt" (stumt vilkt) ir parasta Arduino izvade, tapa var iegūt vērtību HIGH vai LOW. Bet ar "atvērto noteku" ir , lai gan patiesībā šeit viss ir vienkārši:


Izvades konfigurācija / ja ports ir piešķirts izvadei: / iespējots izvades buferis: / – atvērts iztukšošanas režīms: “0” izvades reģistrā iespējo N-MOS, “1” izvades reģistrā atstāj portu Hi-Z režīmā ( P-MOS nav aktivizēts ) / – push-pull režīms: “0” izejas reģistrā aktivizē N-MOS, “1” izvades reģistrā aktivizē P-MOS.
Visas atšķirības starp atvērtu kanalizāciju (atvērta kanalizācija) no “push-pull” (stumt vilkt) ir tas, ka pirmajā tapā nevar pieņemt HIGH stāvokli: ierakstot to izvades reģistrā, tas pāriet augstas pretestības režīmā (augstas pretestības, Sveiks-Z). Ierakstot nulli, tapa uzvedas vienādi abos režīmos gan loģiski, gan elektriski.
Parastā izvades režīmā tapa vienkārši pārraida izvades reģistra saturu. "Alternatīvā" to kontrolē attiecīgās perifērijas ierīces (sk. 9.1.4):
![]()
Ja porta bits ir konfigurēts kā alternatīvas funkcijas tapa, tapu reģistrs ir atspējots un tapa ir savienota ar perifērijas tapu.
Katra tapa alternatīva funkcionalitāte ir aprakstīta PIN definīcijas Datu lapa atrodas uz lejupielādētā attēla. Uz jautājumu, ko darīt, ja tapai ir vairākas alternatīvas funkcijas, atbildi sniedz zemsvītras piezīme datu lapā:
![]()
Ja vairākas perifērijas ierīces izmanto vienu tapu, lai izvairītos no konflikta starp alternatīvām funkcijām, vienlaikus jāizmanto tikai viena perifērijas ierīce, kas tiek pārslēgta, izmantojot perifērijas pulksteņa iespējošanas bitu (attiecīgajā RCC reģistrā).
Visbeidzot, tapām izvades režīmā ir arī pulksteņa ātrums. Šī ir vēl viena enerģijas taupīšanas funkcija; mūsu gadījumā mēs to vienkārši iestatām uz maksimumu un aizmirstam.
Tātad: mēs izmantojam SPI, kas nozīmē, ka diviem kontaktiem (ar datiem un ar pulksteņa signālu) jābūt "alternatīvai push-pull funkcijai", bet vēl vienam (LAT) jābūt "parastam push-pull". Bet pirms to piešķiršanas, tiksim galā ar SPI.
SPI
Vēl viena neliela izglītības programma
SPI jeb Serial Peripheral Interface (serial peripheral Interface) ir vienkāršs un ļoti efektīvs interfeiss MK savienošanai ar citiem MK un ārpasauli kopumā. Tās darbības princips jau ir aprakstīts iepriekš, kur par ķīniešu LED draiveri (atsauces rokasgrāmatā skatiet 25. sadaļu). SPI var darboties galvenā (“master”) un slave (“slave”) režīmā. SPI ir četri pamata kanāli, no kuriem ne visus var izmantot:
- MOSI, galvenā izeja / vergu ieeja: šī tapa pārsūta datus galvenajā režīmā un saņem datus pakārtotā režīmā;
- MISO, galvenā ieeja / vergu izeja: gluži pretēji, tas saņem galvenajā un pārraida vergu;
- SCK, Serial Clock: iestata datu pārraides frekvenci galvenajā ierīcē vai saņem pulksteņa signālu slave. Būtībā sitienu sitieni;
- SS, Slave Select: ar šī kanāla palīdzību vergs zina, ka no viņa kaut ko vēlas. Uz STM32 to sauc par NSS, kur N = negatīvs, t.i. kontrolieris kļūst par vergu, ja šajā kanālā ir zemējums. Tas labi apvienojas ar Open Drain Output režīmu, taču tas ir cits stāsts.
Tāpat kā viss pārējais, SPI STM32 ir bagāta ar funkcionalitāti, kas padara to nedaudz grūti saprotamu. Piemēram, tas var strādāt ne tikai ar SPI, bet arī ar I2S interfeisu, un dokumentācijā to apraksti ir sajaukti, ir nepieciešams savlaicīgi nogriezt lieko. Mūsu uzdevums ir ārkārtīgi vienkāršs: mums vienkārši jānosūta dati, izmantojot tikai MOSI un SCK. Mēs pārejam uz sadaļu 25.3.4 (pusdupleksā komunikācija, pusdupleksā komunikācija), kur atrodam 1 pulkstenis un 1 vienvirziena datu vads (1 pulksteņa signāls un 1 vienvirziena datu straume):

Šajā režīmā lietojumprogramma izmanto SPI tikai pārraides vai tikai saņemšanas režīmā. / Tikai pārraides režīms ir līdzīgs dupleksajam režīmam: dati tiek pārsūtīti uz pārraides tapu (MOSI galvenajā režīmā vai MISO pakārtotā režīmā), un saņemšanas tapu (attiecīgi MISO vai MOSI) var izmantot kā parasto I/O tapu. . Šajā gadījumā lietojumprogrammai ir tikai jāignorē Rx buferis (ja tas tiek nolasīts, tur netiks pārsūtīti dati).
Lieliski, MISO pin ir brīvs, pievienosim tam LAT signālu. Apskatīsim Slave Select, kuru uz STM32 var vadīt programmatiski, kas ir ārkārtīgi ērti. Tāda paša nosaukuma rindkopu lasām sadaļā 25.3.1 SPI Vispārīgs apraksts:

Programmatūras vadības NSS (SSM = 1) / Slave atlases informācija ir ietverta SPI_CR1 reģistra SSI bitā. Ārējais NSS pin paliek brīvs citām lietojumprogrammu vajadzībām.
Laiks rakstīt uz reģistriem. Es nolēmu izmantot SPI2, meklēt tā bāzes adresi datu lapā - sadaļā 3.3 Atmiņas karte:
![]()
Nu, sāksim:
#define _SPI2_(mem_offset) (*(volatile uint32_t *)(0x40003800 + (mem_offset)))
Atveriet sadaļu 25.3.3 ar pašsaprotamu nosaukumu “SPI konfigurēšana galvenajā režīmā”:

1. SPI_CR2 reģistrā iestatiet sērijas pulksteņa frekvenci ar bitiem BR[0:1].
Reģistri ir apkopoti tāda paša nosaukuma uzziņu rokasgrāmatas sadaļā. Adreses maiņa (Adreses nobīde) CR1 — 0x00, pēc noklusējuma visi biti ir notīrīti (Atiestatīt vērtību 0x0000):

BR biti iestata kontroliera pulksteņa dalītāju, tādējādi nosakot frekvenci, kādā SPI darbosies. Mūsu STM32 frekvence būs 72 MHz, LED draiveris saskaņā ar tā datu lapu darbojas ar frekvenci līdz 25 MHz, tāpēc jādala ar četri (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. Iestatiet CPOL un CPHA bitus, lai definētu saistību starp datu pārsūtīšanu un sērijas pulksteņa laiku (skatiet diagrammu 240. lpp.)
Tā kā mēs šeit lasām datu lapu, nevis skatāmies shēmas, sīkāk apskatīsim CPOL un CPHA bitu teksta aprakstu 704. lappusē (SPI vispārīgais apraksts):

Pulksteņa fāze un polaritāte
Izmantojot SPI_CR1 reģistra CPOL un CPHA bitus, varat programmatiski atlasīt četras laika attiecības. CPOL (pulksteņa polaritātes) bits kontrolē pulksteņa signāla stāvokli, kad netiek pārsūtīti dati. Šis bits kontrolē galveno un pakārtoto režīmu. Ja CPOL ir atiestatīts, atpūtas režīmā SCK tapa ir zema. Ja ir iestatīts CPOL bits, atpūtas režīma laikā SCK tapa ir augsta.
Kad ir iestatīts CPHA (pulksteņa fāzes) bits, augstais bitu slazds ir SCK signāla otrā mala (krīt, ja CPOL ir skaidrs, palielinās, ja ir iestatīts CPOL). Dati tiek uztverti ar otro pulksteņa signāla izmaiņu. Ja CPHA bits ir tīrs, augstais bitu slazds ir SCK signāla augošā mala (krītošā mala, ja ir iestatīta CPOL, augošā mala, ja CPOL ir notīrīta). Dati tiek uztverti pie pirmās pulksteņa signāla maiņas.
Apgūstot šīs zināšanas, mēs nonākam pie secinājuma, ka abiem bitiem jāpaliek nullēm, jo Mēs vēlamies, lai SCK signāls paliktu zems, kad tas netiek izmantots, un dati tiktu pārraidīti uz impulsa pieaugošās malas (sk. Rising Edge DM634 datu lapā).
Starp citu, šeit mēs pirmo reizi saskārāmies ar vārdu krājuma iezīmi ST datu lapās: tajās ir rakstīta frāze “atiestatīt bitu uz nulli”. lai mazliet atiestatītuUn ne lai mazliet notīrītu, piemēram, Atmega.
3. Iestatiet DFF bitu, lai noteiktu, vai datu bloks ir 8 bitu vai 16 bitu formātā
Es īpaši paņēmu 16 bitu DM634, lai netraucētu pārsūtīt 12 bitu PWM datus, piemēram, DM633. Ir lietderīgi iestatīt DFF uz vienu:
#define DFF 0x0800
_SPI2_ (_SPI_CR1) |= DFF; // 16-bit mode
4. Konfigurējiet LSBFIRST bitu SPI_CR1 reģistrā, lai noteiktu bloka formātu
LSBFIRST, kā norāda nosaukums, vispirms konfigurē pārraidi ar vismazāko bitu. Bet DM634 vēlas saņemt datus, sākot no visnozīmīgākā bita. Tāpēc mēs atstājam to atiestatīt.
5. Aparatūras režīmā, ja ir nepieciešama ievade no NSS kontakta, visas baitu pārsūtīšanas secības laikā uz NSS kontakta ievadiet augstu signālu. NSS programmatūras režīmā iestatiet SSM un SSI bitus SPI_CR1 reģistrā. Ja NSS tapu izmanto kā izvadi, ir jāiestata tikai SSOE bits.
Instalējiet SSM un SSI, lai aizmirstu par NSS aparatūras režīmu:
#define SSI 0x0100
#define SSM 0x0200
_SPI2_ (_SPI_CR1) |= SSM | SSI; //enable software control of SS, SS high
6. MSTR un SPE biti ir jāiestata (tie paliek iestatīti tikai tad, ja NSS signāls ir augsts)
Faktiski ar šiem bitiem mēs apzīmējam savu SPI kā galveno un ieslēdzam to:
#define MSTR 0x0004
#define SPE 0x0040
_SPI2_ (_SPI_CR1) |= MSTR; //SPI master
//когда все готово, включаем SPI
_SPI2_ (_SPI_CR1) |= SPE;
SPI ir konfigurēts, uzreiz rakstīsim funkcijas, kas sūta baitus draiverim. Turpināt lasīt 25.3.3 “SPI konfigurēšana galvenajā režīmā”:

Datu pārsūtīšanas pasūtījums
Pārsūtīšana sākas, kad baits tiek ierakstīts Tx buferī.
Datu baits tiek ielādēts maiņu reģistrā plkst paralēli režīmā (no iekšējās kopnes) pirmā bita pārraides laikā, pēc kura tas tiek pārsūtīts uz secīgi MOSI tapas režīms, pirmais vai pēdējais bits uz priekšu atkarībā no LSBFIRST bita iestatījuma CPI_CR1 reģistrā. TXE karodziņš tiek iestatīts pēc datu pārraides no Tx bufera uz maiņu reģistru, kā arī ģenerē pārtraukumu, ja ir iestatīts TXEIE bits CPI_CR1 reģistrā.
Es izcēlu dažus vārdus tulkojumā, lai pievērstu uzmanību vienai SPI ieviešanas iezīmei STM kontrolleros. Uz Atmega TXE karogs (Tx tukšs, Tx ir tukšs un gatavs datu saņemšanai) tiek iestatīts tikai pēc visa baita nosūtīšanas uz āru. Un šeit šis karogs tiek iestatīts pēc tam, kad baits ir ievietots iekšējā maiņu reģistrā. Tā kā tas tiek virzīts tur ar visiem bitiem vienlaikus (paralēli), un pēc tam dati tiek pārsūtīti secīgi, TXE tiek iestatīts pirms baita pilnīgas nosūtīšanas. Tas ir svarīgi, jo mūsu LED draivera gadījumā pēc nosūtīšanas mums ir jāizvelk LAT tapa viss datus, t.i. Ar TXE karogu vien mums nepietiks.
Tas nozīmē, ka mums ir vajadzīgs cits karogs. Apskatīsim 25.3.7. — “Statusa karogi”:

<…>

AIZņemts karogs
BSY karogu iestata un notīra aparatūra (rakstīšanai tajā nav nekādas ietekmes). BSY karodziņš norāda SPI komunikācijas slāņa stāvokli.
Tas tiek atiestatīts:
kad pārsūtīšana ir pabeigta (izņemot galvenajā režīmā, ja pārsūtīšana ir nepārtraukta)
kad SPI ir atspējots
kad rodas galvenā režīma kļūda (MODF=1)
Ja pārsūtīšana nav nepārtraukta, BSY karodziņš tiek notīrīts starp katru datu pārsūtīšanu
Labi, tas noderēs. Noskaidrosim, kur atrodas Tx buferis. Lai to izdarītu, izlasiet “SPI datu reģistrs”:

Biti 15:0 DR[15:0] Datu reģistrs
Saņemtie vai pārsūtāmie dati.
Datu reģistrs ir sadalīts divos buferos - viens rakstīšanai (pārsūtīšanas buferis) un viens lasīšanai (saņemšanas buferis). Ierakstot datu reģistrā, tiek ierakstīts Tx buferis, un nolasot no datu reģistra, tiks atgriezta vērtība, kas atrodas Rx buferī.
Un statusa reģistrs, kurā ir atrodami karogi TXE un BSY:

Mēs rakstām:
#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
}
Tā kā mums ir jāpārraida 16 reizes divi baiti, atkarībā no LED draivera izvadu skaita, apmēram šādi:
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();
}
Taču mēs vēl nezinām, kā izvilkt LAT tapu, tāpēc atgriezīsimies pie I/O.
Piespraudes piešķiršana
STM32F1 reģistri, kas ir atbildīgi par tapu stāvokli, ir diezgan neparasti. Skaidrs, ka to ir vairāk nekā Atmega, taču tie arī atšķiras no citiem STM čipiem. 9.1. sadaļa GPIO vispārīgs apraksts:

Katrs no vispārējas nozīmes I/O portiem (GPIO) ir divi 32 bitu konfigurācijas reģistri (GPIOx_CRL un GPIOx_CRH), divi 32 bitu datu reģistri (GPIOx_IDR un GPIOx_ODR), 32 bitu iestatīšanas/atiestatīšanas reģistrs (GPIOx_BSRR), 16 bitu atiestatīšanas reģistrs (GPIOx_BRR) un 32 bitu reģistrs. bitu bloķēšanas reģistrs (GPIOx_LCKR).
Pirmie divi reģistri ir neparasti un arī diezgan neērti, jo 16 portu tapas ir izkaisītas pa tiem formātā “četri biti uz brāli”. Tie. tapas no nulles līdz septiņām ir CRL, bet pārējās ir CRH. Tajā pašā laikā atlikušajos reģistros veiksmīgi ir visu porta tapu biti - bieži vien paliek puse “rezervēta”.
Vienkāršības labad sāksim no saraksta beigām.
Mums nav nepieciešams bloķēšanas reģistrs.
Iestatīšanas un atiestatīšanas reģistri ir diezgan smieklīgi ar to, ka tie daļēji dublē viens otru: visu var rakstīt tikai BSRR, kur augstākie 16 biti atiestatīs tapu uz nulli, bet zemākie tiks iestatīti uz 1, vai arī jūs varat arī izmantojiet BRR, kura apakšējie 16 biti atiestata tikai tapu . Man patīk otrais variants. Šie reģistri ir svarīgi, jo tie nodrošina atomu piekļuvi tapām:

![]()
Atomu iestatīšana vai atiestatīšana
Programmējot GPIOx_ODR bitu līmenī, pārtraukumi nav jāatspējo: vienu vai vairākus bitus var mainīt ar vienu atomu rakstīšanas operāciju APB2. To panāk, maināmā bita iestatīšanas/atiestatīšanas reģistrā (GPIOx_BSRR vai, tikai atiestatīšanai, GPIOx_BRR), ierakstot "1". Citi biti paliks nemainīgi.
Datu reģistriem ir diezgan pašsaprotami nosaukumi - IDR = ievade Virzienu reģistrs, ievades reģistrs; ODR = izvade Virzienu reģistrs, izvades reģistrs. Pašreizējā projektā tie mums nebūs vajadzīgi.
Un visbeidzot, kontroles reģistri. Tā kā mūs interesē otrie SPI tapas, proti, PB13, PB14 un PB15, mēs nekavējoties skatāmies uz CRH:

Un mēs redzam, ka mums būs kaut kas jāraksta bitos no 20 līdz 31.
Mēs jau iepriekš esam izdomājuši, ko mēs vēlamies no tapām, tāpēc šeit es iztikšu bez ekrānuzņēmuma, teikšu tikai to, ka MODE norāda virzienu (ievade, ja abi biti ir iestatīti uz 0) un tapas ātrumu (mums vajag 50MHz, t.i. abas tapas uz “1”), un CNF iestata režīmu: parastais “push-pull” – 00, “alternatīvais” – 10. Pēc noklusējuma, kā redzams iepriekš, visām tapām ir trešais bits no apakšas (CNF0), tas iestata tos režīmā peldošā ievade.
Tā kā es plānoju ar šo mikroshēmu darīt kaut ko citu, vienkāršības labad esmu definējis visas iespējamās MODE un CNF vērtības gan apakšējam, gan augšējam vadības reģistram.
Kaut kā šādi
#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
Mūsu tapas atrodas uz porta B (bāzes adrese – 0x40010C00), kods:
#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;
Un attiecīgi jūs varat rakstīt definīcijas LAT, kuras raustīs BRR un BSRR reģistri:
/*** LAT pulse – high, then low */
#define LAT_pulse() _PORTB_(_BSRR) = (1<<14); _PORTB_(_BRR) = (1<<14)
#define LAT_low() _PORTB_(_BRR) = (1<<14)
(LAT_zems tikai pēc inerces, tā vienmēr ir bijis, lai paliek)
Tagad viss ir lieliski, bet tas nedarbojas. Tā kā šis ir STM32, tie ietaupa elektroenerģiju, kas nozīmē, ka jums ir jāiespējo nepieciešamo perifērijas ierīču pulksteņa funkcija.
Ieslēdziet pulksteni
Pulkstenis, kas pazīstams arī kā pulkstenis, ir atbildīgs par pulksteni. Un jau varējām pamanīt saīsinājumu RCC. Mēs to meklējam dokumentācijā: šī ir Reset un Clock Control.
Kā jau minēts iepriekš, par laimi, visgrūtāko pulksteņa tēmas daļu mums veica cilvēki no STM, par ko mēs viņiem ļoti pateicamies (vēlreiz iedošu saiti uz , lai būtu skaidrs, cik tas ir mulsinoši). Mums ir nepieciešami tikai reģistri, kas ir atbildīgi par perifērijas pulksteņa iespējošanu (Peripheral Clock Enable Registers). Pirmkārt, atradīsim RCC bāzes adresi, tā atrodas “Memory Map” pašā sākumā:
![]()
#define _RCC_(mem_offset) (*(volatile uint32_t *)(0x40021000 + (mem_offset)))
Un tad vai nu noklikšķiniet uz saites, kur mēģināt kaut ko atrast plāksnē, vai, daudz labāk, izlasiet iespējojošo reģistru aprakstus no sadaļām par iespējot reģistrus. Kur mēs atradīsim RCC_APB1ENR un RCC_APB2ENR:


Un attiecīgi tajos ir biti, kas ietver SPI2, IOPB (I/O ports B) un alternatīvo funkciju (AFIO) taktiku.
#define _APB2ENR 0x18
#define _APB1ENR 0x1C
#define IOPBEN 0x0008
#define SPI2EN 0x4000
#define AFIOEN 0x0001
//включаем тактирование порта B и альт. функций
_RCC_(_APB2ENR) |= IOPBEN | AFIOEN;
//включаем тактирование SPI2
_RCC_(_APB1ENR) |= SPI2EN;
Galīgo kodu var atrast .
Ja ir iespēja un vēlme pārbaudīt, tad pieslēdz DM634 šādi: DAI pie PB15, DCK uz PB13, LAT pie PB14. Mēs barojam draiveri no 5 voltiem, neaizmirstiet pieslēgt zemējumu.

STM8 PWM
PWM uz STM8
Kad es tikai plānoju šo rakstu, es nolēmu, piemēram, mēģināt apgūt kādu nepazīstama mikroshēmas funkcionalitāti, izmantojot tikai datu lapu, lai es netiktu pie kurpnieka bez zābakiem. STM8 bija ideāli piemērots šai lomai: pirmkārt, man bija pāris ķīniešu dēļi ar STM8S103, otrkārt, tas nav īpaši populārs, un tāpēc kārdinājums lasīt un atrast risinājumu internetā balstās tieši uz šo risinājumu trūkumu.
Mikroshēmai arī ir и , pirmajā ir pinout un reģistra adreses, otrajā - viss pārējais. STM8 ir ieprogrammēts C valodā briesmīgā IDE .
Pulkstenis un I/O
Pēc noklusējuma STM8 darbojas ar 2 MHz frekvenci, tas nekavējoties jālabo.

HSI (ātrgaitas iekšējais) pulkstenis
HSI pulksteņa signāls tiek iegūts no iekšēja 16 MHz RC oscilatora ar programmējamu dalītāju (no 1 līdz 8). Tas ir iestatīts pulksteņa dalītāju reģistrā (CLK_CKDIVR).
Piezīme: sākumā HSI RC oscilators ar dalītāju 8 tiek izvēlēts kā galvenais pulksteņa signāla avots.
Datu lapā atrodam reģistra adresi, refman aprakstu un redzam, ka reģistrs ir jādzēš:
#define CLK_CKDIVR *(volatile uint8_t *)0x0050C6
CLK_CKDIVR &= ~(0x18);
Tā kā mēs palaidīsim PWM un pievienosim gaismas diodes, apskatīsim kontaktdakšu:

Mikroshēma ir maza, daudzas funkcijas ir apturētas uz tām pašām tapām. Tas, kas ir kvadrātiekavās, ir “alternatīva funkcionalitāte”, to pārslēdz “opciju baiti” (opciju baiti) – kaut kas līdzīgs Atmega drošinātājiem. Jūs varat mainīt to vērtības programmētiski, bet tas nav nepieciešams, jo Jaunā funkcionalitāte tiek aktivizēta tikai pēc pārstartēšanas. Vienkāršāk ir izmantot ST Visual Programmer (lejupielādēts kopā ar Visual Develop), kas var mainīt šos baitus. Spraude parāda, ka pirmā taimera CH1 un CH2 tapas ir paslēptas kvadrātiekavās; STVP ir jāiestata AFR1 un AFR0 biti, un otrs pārsūtīs arī otrā taimera CH1 izvadi no PD4 uz PC5.
Tādējādi gaismas diodes vadīs 6 tapas: PC6, PC7 un PC3 pirmajam taimeram, PC5, PD3 un PA3 otrajam.
Pašu I/O tapu iestatīšana STM8 ir vienkāršāka un loģiskāka nekā STM32:
- pazīstams no Atmega DDR datu virzienu reģistra (Datu virzienu reģistrs): 1 = izvade;
- pirmais vadības reģistrs CR1, kad tas tiek izvadīts, iestata push-pull režīmu (1) vai atvērtu aizplūšanu (0); tā kā gaismas diodes pievienoju mikroshēmai ar katodiem, tad šeit atstāju nulles;
- otrais vadības reģistrs CR2, izejot, iestata takts frekvenci: 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 iestatījums
Vispirms definēsim terminus:
- PWM frekvence – biežums, ar kādu taimeris tikšķ;
- Automātiskā pārlādēšana, AR – automātiski ielādējamā vērtība, līdz kurai taimeris skaitīs (impulsa periods);
- Atjaunināt notikumu, UEV – notikums, kas notiek, kad taimeris ir ieskaitījis AR;
- PWM darba cikls – PWM darba cikls, ko bieži sauc par “darba koeficientu”;
- Uzņemiet/salīdziniet vērtību – uztveršanas/salīdzināšanas vērtība, līdz kurai taimeris ir ieskaitījies kaut ko darīs (PWM gadījumā tas invertē izejas signālu);
- Iepriekšējas ielādes vērtība – iepriekš ielādēta vērtība. Salīdziniet vērtību nevar mainīties, kamēr taimeris tikšķ, pretējā gadījumā PWM cikls pārtrūks. Tāpēc jaunas pārraidītās vērtības tiek ievietotas buferī un izvilktas, kad taimeris sasniedz atpakaļskaitīšanas beigas un tiek atiestatīts;
- Izlīdzināts ar malu и Centrā izlīdzinātie režīmi – izlīdzinājums gar robežu un centrā, tāds pats kā Atmel Ātra PWM и Fāzei pareizs PWM.
- OCiREF, izvades salīdzināšanas atsauces signāls – atskaites izejas signāls, faktiski tas, kas parādās uz atbilstošās tapas PWM režīmā.
Kā jau redzams no pinout, diviem taimeriem ir PWM iespējas – pirmajam un otrajam. Abi ir 16 bitu, pirmajam ir daudz papildu funkciju (jo īpaši tas var skaitīt gan uz augšu, gan uz leju). Mums abiem jāstrādā vienādi, tāpēc nolēmu sākt ar acīmredzami nabadzīgāko otro, lai nejauši neizmantotu kaut ko, kas tur nav. Problēma ir tāda, ka visu taimeru PWM funkcionalitātes apraksts atsauces rokasgrāmatā ir sadaļā par pirmo taimeri (17.5.7 PWM režīms), tāpēc jums visu laiku ir jālēkā uz priekšu un atpakaļ visā dokumentā.
PWM uz STM8 ir svarīga priekšrocība salīdzinājumā ar PWM uz Atmega:

Robežas līdzināts PWM
Konta konfigurācija no apakšas uz augšu
Apakšējā skaitīšana ir aktīva, ja DIR bits TIM_CR1 reģistrā ir notīrīts
Piemērs
Piemērā tiek izmantots pirmais PWM režīms. PWM atsauces signāls OCiREF tiek turēts augstā līmenī, kamēr TIM1_CNT < TIM1_CCRI. Pretējā gadījumā tas aizņem zemu līmeni. Ja salīdzinājuma vērtība TIM1_CCRI reģistrā ir lielāka par automātiskās ielādes vērtību (TIM1_ARR reģistrs), OCiREF signāls tiek turēts uz 1. Ja salīdzināmā vērtība ir 0, OCiREF tiek turēts uz nulles....
STM8 taimeris laikā atjaunināšanas pasākums vispirms pārbauda salīdzināt vērtību, un tikai pēc tam rada atsauces signālu. Atmega taimeris vispirms pieskrūvē un pēc tam salīdzina, kā rezultātā compare value == 0 izeja ir adata, ar kuru kaut kā jātiek galā (piemēram, programmatiski apgriežot loģiku).
Tātad, ko mēs vēlamies darīt: 8 bitu PWM (AR == 255), skaitot no apakšas uz augšu, līdzinājums gar robežu. Tā kā spuldzes ir savienotas ar mikroshēmu ar katodiem, PWM vajadzētu izvadīt 0 (ieslēgts LED), līdz salīdzināt vērtību un 1 pēc.
Par dažiem jau esam lasījuši PWM režīms, tāpēc mēs atrodam vajadzīgo otrā taimera reģistru, atsauces rokasgrāmatā meklējot šo frāzi (18.6.8 - TIMx_CCMR1):

110: Pirmais PWM režīms — skaitot no apakšas uz augšu, pirmais kanāls ir aktīvs, ja vien TIMx_CNT < TIMx_CCR1. Pretējā gadījumā pirmais kanāls ir neaktīvs. [Pārējais dokuments ir kļūdaina 1. taimera kopija un ielīmēšana]
111: Otrais PWM režīms — skaitot no apakšas uz augšu, pirmais kanāls ir neaktīvs, līdz TIMx_CNT < TIMx_CCR1. Pretējā gadījumā pirmais kanāls ir aktīvs.
Tā kā gaismas diodes ir savienotas ar MK ar katodiem, mums der otrais režīms (arī pirmais, bet mēs to vēl nezinām).

3. bits OC1PE: iespējot 1. tapas priekšielādēšanu
0: TIMx_CCR1 priekšielādēšanas reģistrs ir atspējots. Jūs varat rakstīt uz TIMx_CCR1 jebkurā laikā. Jaunā vērtība darbojas nekavējoties.
1: TIMx_CCR1 priekšielādēšanas reģistrs ir iespējots. Lasīšanas/rakstīšanas operācijas piekļūst iepriekšējas ielādes reģistram. Iepriekš ielādētā vērtība TIMx_CCR1 tiek ielādēta ēnu reģistrā katra atjaunināšanas notikuma laikā.
*Piezīme. Lai PWM režīms darbotos pareizi, ir jāiespējo priekšielādēšanas reģistri. Tas nav nepieciešams viena signāla režīmā (OPM bits ir iestatīts TIMx_CR1 reģistrā).
Labi, ieslēdzam visu, kas nepieciešams otrā taimera trim kanāliem:
#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 sastāv no diviem astoņu bitu reģistriem, viss ir vienkāršs:
#define TIM2_ARRH *(volatile uint8_t *)0x00530F
#define TIM2_ARRL *(volatile uint8_t *)0x005310
TIM2_ARRH = 0;
TIM2_ARRL = 255;
Otrais taimeris var skaitīt tikai no apakšas uz augšu, līdzinājums gar robežu, nekas nav jāmaina. Iestatīsim frekvences dalītāju, piemēram, uz 256. Otrajam taimeram dalītājs ir iestatīts TIM2_PSCR reģistrā un ir divu pakāpju:
#define TIM2_PSCR *(volatile uint8_t *)0x00530E
TIM2_PSCR = 8;
Atliek tikai ieslēgt secinājumus un pašu otro taimeri. Pirmo problēmu atrisina reģistri Uzņemt/Salīdzināt dot iespēju: pa tiem ir asimetriski izkaisīti divi, trīs kanāli. Šeit arī varam uzzināt, ka ir iespējams mainīt signāla polaritāti, t.i. principā bija iespējams izmantot PWM režīmu 1. Mēs rakstām:
#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;
Visbeidzot, mēs iedarbinām taimeri TIMx_CR1 reģistrā:

#define TIM2_CR1 *(volatile uint8_t *)0x005300
TIM2_CR1 |= 1;
Uzrakstīsim vienkāršu AnalogWrite() analogu, kas salīdzināšanai pārsūtīs faktiskās vērtības uz taimeri. Reģistri nosaukti paredzami Uzņemt/salīdzināt reģistrus, katram kanālam ir divi no tiem: zemas kārtas 8 biti TIM2_CCRxL un augstākās kārtas TIM2_CCRxH. Tā kā mēs esam izveidojuši 8 bitu PWM, pietiek ar to, ka uzrakstiet tikai vismazāk nozīmīgus bitus:
#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;
}
Uzmanīgs lasītājs pamanīs, ka mums ir nedaudz bojāts PWM, kas nespēj nodrošināt 100% piepildījumu (maksimālajā vērtībā 255, signāls tiek apgriezts vienam taimera ciklam). Gaismas diodēm tas nav svarīgi, un uzmanīgs lasītājs jau var uzminēt, kā to salabot.
PWM uz otrā taimera darbojas, pāriesim pie pirmā.
Pirmajā taimerī ir tieši tādi paši biti tajos pašos reģistros (tikai tie biti, kas palika “rezervēti” otrajā taimeri, pirmajā tiek aktīvi izmantoti visādām uzlabotām lietām). Tāpēc pietiek datu lapā atrast to pašu reģistru adreses un nokopēt kodu. Nu, mainiet frekvences dalītāja vērtību, jo... pirmais taimeris vēlas saņemt nevis divu jaudu, bet precīzu 16 bitu vērtību divos reģistros Prescaler High и Zems. Daram visu un... pirmais taimeris nestrādā. Kas noticis?
Problēmu var atrisināt tikai caurskatot visu sadaļu par taimera 1 vadības reģistriem, kur meklējam to, kura otrajam taimeram nav. Tur būs 17.7.30 Pārrāvuma reģistrs (TIM1_BKR), kur ir šis bits:
![]()
Iespējot galveno izvadi
#define TIM1_BKR *(volatile uint8_t *)0x00526D
TIM1_BKR = (1<<7);
Tagad tas ir viss, kods .

STM8 multiplekss
Multipleksēšana uz STM8
Trešais mini projekts ir savienot astoņas RGB gaismas diodes ar otro taimeri PWM režīmā un likt tiem parādīt dažādas krāsas. Tas ir balstīts uz LED multipleksēšanas koncepciju, proti, ja ļoti, ļoti ātri ieslēdzat un izslēdzat gaismas diodes, mums šķitīs, ka tās pastāvīgi deg (redzes noturība, vizuālās uztveres inerce). Es reiz darīju .
Darba algoritms izskatās šādi:
- pieslēdza pirmās RGB gaismas diodes anodu;
- iedegas to, nosūtot nepieciešamos signālus uz katodiem;
- gaidīja līdz PWM cikla beigām;
- pievienoja otrās RGB gaismas diodes anodu;
- aizdedzies to...
Nu utt. Protams, skaistai darbībai ir nepieciešams pieslēgt anodu un vienlaikus “aizdegties” LED. Nu vai gandrīz. Jebkurā gadījumā mums ir jāraksta kods, kas izvadīs vērtības trīs otrā taimera kanālos, mainīs tās, kad tiek sasniegts UEV, un tajā pašā laikā jāmaina pašlaik aktīvais RGB LED.
Tā kā LED pārslēgšana ir automātiska, mums ir jāizveido "video atmiņa", no kuras pārtraukumu apstrādātājs saņems datus. Šis ir vienkāršs masīvs:
uint8_t colors[8][3];
Lai mainītu konkrētas gaismas diodes krāsu, pietiks ar vajadzīgās vērtības ierakstīšanu šajā masīvā. Un mainīgais būs atbildīgs par aktīvās gaismas diodes numuru
uint8_t cnt;
Demux
Pareizai multipleksēšanai mums, dīvainā kārtā, ir nepieciešams CD74HC238 demultiplekseris. Demultiplekseris - mikroshēma, kas ievieš operatoru aparatūrā <<. Caur trim ievades tapām (biti 0, 1 un 2) mēs tam ievadām trīs bitu skaitli X, un atbildot tas aktivizē izvades numuru (1<<X). Atlikušās mikroshēmas ievades tiek izmantotas, lai mērogotu visu dizainu. Šī mikroshēma mums ir nepieciešama ne tikai, lai samazinātu mikrokontrollera aizņemto tapu skaitu, bet arī drošībai - lai nejauši neiedegtu vairāk gaismas diodes nekā iespējams un nesadedzinātu MK. Mikroshēma maksā santīmu, un tā vienmēr jāglabā mājas aptieciņā.
Mūsu CD74HC238 būs atbildīgs par sprieguma padevi vēlamās gaismas diodes anodam. Pilnvērtīgā multipleksā tas pievadītu spriegumu kolonnai caur P-MOSFET, bet šajā demonstrācijā tas ir iespējams tieši, jo tas patērē 20 mA, saskaņā ar absolūti maksimālie vērtējumi datu lapā. No mums ir nepieciešami pinouts un šī krāpšanās lapa:

H = augsts sprieguma līmenis, L = zems sprieguma līmenis, X – vienalga
Mēs savienojam E2 un E1 ar zemi, E3, A0, A1 un A3 ar STM5 tapām PD3, PC4, PC5 un PC8. Tā kā augstāk esošajā tabulā ir gan zems, gan augsts līmenis, mēs konfigurējam šīs tapas kā stumšanas tapas.
PWM
PWM otrajā taimerī ir konfigurēts tāpat kā iepriekšējā stāstā, ar divām atšķirībām:
Pirmkārt, mums ir jāiespējo pārtraukums Atjaunināt notikumu (UEV), kas izsauks funkciju, kas pārslēdz aktīvo LED. Tas tiek darīts, mainot bitu Atjaunināšanas pārtraukuma iespējošana reģistrā ar izteiksmīgu nosaukumu

Pārtraukt iespējot reģistru
#define TIM2_IER *(volatile uint8_t *)0x005303
//enable interrupt
TIM2_IER = 1;
Otrā atšķirība ir saistīta ar multipleksēšanas fenomenu, piemēram, rēgošanās – diožu parazitārais spīdums. Mūsu gadījumā tas var parādīties tāpēc, ka taimeris, izraisījis UEV pārtraukumu, turpina atzīmēties, un pārtraukumu apstrādātājam nav laika pārslēgt LED, pirms taimeris sāk kaut ko rakstīt uz tapām. Lai to novērstu, jums būs jāapgriež loģika (0 = maksimālais spilgtums, 255 = nekas nedeg) un jāizvairās no ārkārtējām darba cikla vērtībām. Tie. nodrošināt, lai pēc UEV gaismas diodes pilnībā nodziest uz vienu PWM ciklu.
Polaritātes maiņa:
//set polarity
TIM2_CCER1 |= (CC1P | CC2P);
TIM2_CCER2 |= CC3P;
Izvairieties no r, g un b iestatīšanas uz 255 un atcerieties tos apgriezt, kad tos lietojat.
Pārtrauc
Pārtraukuma būtība ir tāda, ka noteiktos apstākļos mikroshēma pārtrauc galvenās programmas izpildi un izsauc kādu ārēju funkciju. Pārtraukumi rodas ārējas vai iekšējas ietekmes, tostarp taimera, dēļ.
Kad mēs pirmo reizi izveidojām projektu ST Visual Develop, papildus main.c mēs saņēmām logu ar noslēpumainu failu stm8_interrupt_vector.c, automātiski iekļauts projektā. Šajā failā katram pārtraukumam tiek piešķirta funkcija NonHandledInterrupt. Mums ir jāsaista mūsu funkcija ar vēlamo pārtraukumu.
Datu lapā ir pārtraukumu vektoru tabula, kurā atrodam vajadzīgos vektorus:

13 TIM2 atjaunināšana/pārpilde
14 TIM2 uztveršana/salīdzināšana
Mums ir jāmaina LED UEV, tāpēc mums ir nepieciešams pārtraukums Nr. 13.
Attiecīgi, pirmkārt, lietā stm8_interrupt_vector.c mainiet par pārtraukumu Nr. 13 (IRQ13) atbildīgās funkcijas noklusējuma nosaukumu uz savu:
{0x82, TIM2_Overflow}, /* irq13 */
Otrkārt, mums būs jāizveido fails main.h ar šādu saturu:
#ifndef __MAIN_H
#define __MAIN_H
@far @interrupt void TIM2_Overflow (void);
#endif
Un visbeidzot ierakstiet šo funkciju savā 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;
}
Atliek tikai iespējot pārtraukumus. Tas tiek darīts, izmantojot komandu assembler rim - jums tas būs jāmeklē :
//enable interrupts
_asm("rim");
Vēl viena montētāja komanda ir sim – izslēdz pārtraukumus. Tie ir jāizslēdz, kamēr “video atmiņā” tiek ierakstītas jaunas vērtības, lai nepareizā brīdī radīts pārtraukums nesabojātu masīvu.
Viss kods - .

Ja vismaz kādam šis raksts šķiet noderīgs, tad es to nerakstīju velti. Priecāšos saņemt komentārus un piezīmes, centīšos uz visu atbildēt.
Avots: www.habr.com
