Skaityti 2 duomenų lapus: SPI STM32; PWM, laikmačiai ir pertraukimai STM8

Skaityti 2 duomenų lapus: SPI STM32; PWM, laikmačiai ir pertraukimai STM8

В pirmoji dalis Mėginau pasakyti hobio elektronikos inžinieriams, kurie užaugo iš Arduino kelnių, kaip ir kodėl jie turėtų skaityti duomenų lapus ir kitą mikrovaldiklių dokumentaciją. Tekstas pasirodė didelis, todėl praktinius pavyzdžius pažadėjau parodyti atskirame straipsnyje. Na, jis save vadino pieno grybu...

Šiandien aš jums parodysiu, kaip naudoti duomenų lapus sprendžiant gana paprastas, bet daugeliui projektų, užduočių, reikalingų STM32 („Blue Pill“) ir STM8 valdikliuose. Visi demo projektai yra skirti mano mėgstamiems LED, mes juos apšviesime dideliais kiekiais, tam teks naudoti visokius įdomius periferinius įrenginius.

Tekstas vėlgi pasirodė didžiulis, todėl patogumo dėlei kuriu turinį:

STM32 Blue Pill: 16 šviesos diodų su DM634 tvarkykle
STM8: šešių PWM kaiščių nustatymas
STM8: 8 RGB šviesos diodai ant trijų kontaktų, pertraukimai

Atsakomybės apribojimas: Nesu inžinierius, neapsimetinėju, kad turiu gilių elektronikos žinių, straipsnis skirtas tokiems mėgėjams kaip aš. Tiesą sakant, prieš dvejus metus save laikiau tiksline auditorija. Jei kas nors man tada būtų pasakęs, kad nepažįstamo lusto duomenų lapus skaityti nebuvo baisu, nebūčiau daug laiko praleidęs ieškodamas kokių nors kodo fragmentų internete ir išradęs ramentus su žirklėmis ir lipnia juostele.

Šiame straipsnyje pagrindinis dėmesys skiriamas duomenų lapams, o ne projektams, todėl kodas gali būti ne itin tvarkingas ir dažnai ankštas. Patys projektai labai paprasti, nors ir tinkami pirmajai pažinčiai su naujuoju lustu.

Tikiuosi, kad mano straipsnis padės kam nors panašiai pasinerti į hobį.

STM32

16 šviesos diodų su DM634 ir SPI

Nedidelis projektas naudojant Blue Pill (STM32F103C8T6) ir DM634 LED tvarkyklę. Naudodami duomenų lapus išsiaiškinsime tvarkyklę, STM IO prievadus ir sukonfigūruosime SPI.

DM634

Taivano lustas su 16 16 bitų PWM išėjimų, gali būti jungiamas grandinėmis. Žemos klasės 12 bitų modelis žinomas iš vietinio projekto Lightpack. Vienu metu, rinkdamasis tarp DM63x ir gerai žinomo TLC5940, pasirinkau DM dėl kelių priežasčių: 1) Aliexpress TLC tikrai yra netikras, bet šis ne; 2) DM turi autonominį PWM su savo dažnio generatoriumi; 3) jį būtų galima nebrangiai nusipirkti Maskvoje, o ne laukti siuntinio iš Ali. Ir, žinoma, buvo įdomu išmokti valdyti lustą patiems, o ne naudoti paruoštą biblioteką. Lustai dabar daugiausia pateikiami SSOP24 pakete, juos lengva lituoti prie adapterio.

Kadangi gamintojas yra taivanietis, duomenų lapas lustas parašytas kinų anglų kalba, vadinasi, bus smagu. Pirmiausia pažiūrime į smeigtuką (Pin Connection) suprasti, prie kurios kojos prie ko prijungti, ir kaiščių aprašymą (Smeigtuko aprašymas). 16 kaiščių:

Skaityti 2 duomenų lapus: SPI STM32; PWM, laikmačiai ir pertraukimai STM8
Nuolatinės srovės kriauklės šaltiniai (atviras kanalizacija)

Kriaukle / Atviro nutekėjimo išėjimas – kanalizacija; įtekančios srovės šaltinis; išėjimas yra prijungtas prie įžeminimo aktyvioje būsenoje - šviesos diodai yra prijungti prie tvarkyklės katodais. Elektriškai tai, žinoma, nėra „atviras nutekėjimas“ (atidaryti kanalizaciją), tačiau duomenų lapuose šis smeigtukų žymėjimas išleidimo režimu dažnai randamas.

Skaityti 2 duomenų lapus: SPI STM32; PWM, laikmačiai ir pertraukimai STM8
Išoriniai rezistoriai tarp REXT ir GND nustatyti išėjimo srovės vertę

Tarp REXT kaiščio ir įžeminimo yra sumontuotas atskaitos rezistorius, kuris valdo išėjimų vidinę varžą, žr. grafiką duomenų lapo 9 puslapyje. DM634 šį pasipriešinimą taip pat galima valdyti programine įranga, nustatant bendrą ryškumą (pasaulinis ryškumas); Šiame straipsnyje nesileisiu į detales, čia tiesiog įdėsiu 2.2–3 kOhm rezistorių.

Norėdami suprasti, kaip valdyti lustą, pažvelkime į įrenginio sąsajos aprašymą:

Skaityti 2 duomenų lapus: SPI STM32; PWM, laikmačiai ir pertraukimai STM8

Taip, čia yra kinų anglų kalba visoje savo šlovėje. Išversti tai yra problematiška, jei norite, galite suprasti, bet yra ir kitas būdas – pažiūrėkite, kaip duomenų lape aprašytas ryšys su funkciškai panašaus TLC5940:

Skaityti 2 duomenų lapus: SPI STM32; PWM, laikmačiai ir pertraukimai STM8
... Norint įvesti duomenis į įrenginį, reikia tik trijų kaiščių. Kylantis SCLK signalo kraštas perkelia duomenis iš SIN kaiščio į vidinį registrą. Įkėlus visus duomenis, trumpas aukštas XLAT signalas užfiksuoja nuosekliai perduodamus duomenis į vidinius registrus. Vidiniai registrai yra vartai, suaktyvinami XLAT signalo lygio. Visi duomenys pirmiausia perduodami reikšmingiausiu bitu.

Latch – skląstis/užraktas/užraktas.
Kylantis kraštas – priekinis pulso kraštas
MSB pirmiausia – reikšmingiausias (kairysis) bitas į priekį.
laikrodžio duomenims – perduoti duomenis nuosekliai (bitas po bito).

Žodis velkė dažnai randamas lustų dokumentacijoje ir yra įvairiai išverstas, todėl supratimo dėlei leisiu sau

nedidelė edukacinė programaLED tvarkyklė iš esmės yra pamainų registras. „Shift“ (perkelti) pavadinime – bitinis duomenų judėjimas įrenginio viduje: kiekvienas naujas įstumtas bitas stumia visą grandinę į priekį priešais save. Kadangi niekas nenori stebėti chaotiško šviesos diodų mirksėjimo pamainos metu, procesas vyksta buferiniuose registruose, atskirtuose nuo darbinių registrų slopintuvu (velkė) yra tam tikras laukimo kambarys, kuriame bitai yra išdėstyti norima seka. Kai viskas bus paruošta, sklendė atsidaro ir antgaliai pradeda dirbti, pakeičiant ankstesnę partiją. Žodis velkė mikroschemų dokumentacijoje beveik visada reiškia tokį slopintuvą, nesvarbu, kokiais deriniais jis naudojamas.

Taigi, duomenų perdavimas į DM634 atliekamas taip: nustatykite DAI įvestį tolimojo šviesos diodo reikšmingiausio bito reikšmę, traukite DCK aukštyn ir žemyn; nustatykite DAI įvestį į kito bito reikšmę, traukite DCK; ir taip toliau, kol visi bitai bus perduoti (įsijungė), po to traukiame LAT. Tai galima padaryti rankiniu būdu (bit-bang), tačiau geriau naudoti specialiai tam pritaikytą SPI sąsają, nes mūsų STM32 ji pateikiama dviem egzemplioriais.

Mėlyna piliulė STM32F103

Įžanga: STM32 valdikliai yra daug sudėtingesni nei Atmega328, nei gali atrodyti baisūs. Be to, energijos taupymo sumetimais beveik visi išoriniai įrenginiai išjungiami, o laikrodžio dažnis yra 8 MHz nuo vidinio šaltinio. Laimei, STM programuotojai parašė kodą, kuris padidina lustą iki "apskaičiuoto" 72 MHz, o visų man žinomų IDE autoriai įtraukė jį į inicijavimo procedūrą, todėl mums nereikia laikrodžio (bet galite, jei tikrai norite). Bet turėsite įjungti periferinius įrenginius.

Dokumentacija: „Blue Pill“ yra su populiariu STM32F103C8T6 lustu, jai yra du naudingi dokumentai:

Duomenų lape mus gali sudominti:

  • Pinouts – chip pinouts – tuo atveju, jei nuspręstume lentas pasigaminti patys;
  • Atminties žemėlapis – konkrečios lusto atminties žemėlapis. Nuorodų vadove yra visos linijos žemėlapis ir minimi registrai, kurių mūsų neturi.
  • Pin Definitions lentelė – išvardijamos pagrindinės ir alternatyvios kaiščių funkcijos; „mėlynajai piliulei“ internete galite rasti patogesnių paveikslėlių su smeigtukų ir jų funkcijų sąrašu. Todėl iškart „Google“ ieškome „Blue Pill pinout“ ir laikome šį paveikslėlį po ranka:

Skaityti 2 duomenų lapus: SPI STM32; PWM, laikmačiai ir pertraukimai STM8
NB: nuotraukoje iš interneto buvo klaida, kuri buvo pažymėta komentaruose, ačiū už tai. Paveikslėlis pakeistas, bet tai pamoka – geriau informaciją tikrinti ne iš duomenų lapų.

Mes pašaliname duomenų lapą, atidarome informacinį vadovą ir nuo šiol naudojame tik jį.
Procedūra: užsiimame standartiniu įėjimu/išėjimu, sukonfigūruojame SPI, įjungiame reikalingus periferinius įrenginius.

Įvesties išvesties

Atmega328 įvestis/išvestis įdiegta itin paprastai, todėl STM32 parinkčių gausa gali suklaidinti. Dabar mums reikia tik išvadų, bet net ir šios turi keturias galimybes:

Skaityti 2 duomenų lapus: SPI STM32; PWM, laikmačiai ir pertraukimai STM8
atviras nutekėjimas, stumdomas, alternatyvus stūmimas, alternatyvus atviras nutekėjimas

"Traukti stumti" (stumti traukti) yra įprasta „Arduino“ išvestis, kaiščio reikšmė gali būti HIGH arba LOW. Tačiau su „atviru nutekėjimu“ yra sunkumų, nors iš tikrųjų čia viskas paprasta:

Skaityti 2 duomenų lapus: SPI STM32; PWM, laikmačiai ir pertraukimai STM8
Skaityti 2 duomenų lapus: SPI STM32; PWM, laikmačiai ir pertraukimai STM8
Išvesties konfigūracija / kai prievadas priskirtas išėjimui: / įjungtas išvesties buferis: / – atidarytas nutekėjimo režimas: „0“ išvesties registre įjungia N-MOS, „1“ išvesties registre palieka prievadą Hi-Z režimu ( P-MOS neįjungtas ) / – „push-pull“ režimas: „0“ išvesties registre aktyvuoja N-MOS, „1“ išvesties registre – P-MOS.

Visas skirtumas tarp atviro kanalizacijos (atidaryti kanalizaciją) iš „stumti-traukti“ (stumti traukti) yra tai, kad pirmame kaištyje negalima priimti HIGH būsenos: įrašant vieną į išvesties registrą, jis pereina į didelio pasipriešinimo režimą (didelės varžos, Sveiki-Z). Rašant nulį, kaištis abiejuose režimuose elgiasi vienodai – tiek logiškai, tiek elektra.

Įprastu išvesties režimu kaištis tiesiog transliuoja išvesties registro turinį. „Alternatyvoje“ jį valdo atitinkami išoriniai įrenginiai (žr. 9.1.4):

Skaityti 2 duomenų lapus: SPI STM32; PWM, laikmačiai ir pertraukimai STM8
Jei prievado bitas sukonfigūruotas kaip alternatyvios funkcijos kaištis, kaiščio registras išjungiamas ir kaištis prijungiamas prie periferinio kaiščio.

Alternatyvios kiekvieno kaiščio funkcijos aprašytos Smeigtukų apibrėžimai Duomenų lapas yra atsisiųstame paveikslėlyje. Į klausimą, ką daryti, jei kaištis turi keletą alternatyvių funkcijų, atsakymas pateikiamas išnašoje duomenų lape:

Skaityti 2 duomenų lapus: SPI STM32; PWM, laikmačiai ir pertraukimai STM8
Jei keli išoriniai įrenginiai naudoja tą patį kaištį, kad būtų išvengta konfliktų tarp alternatyvių funkcijų, vienu metu turėtų būti naudojamas tik vienas periferinis įrenginys, perjungiamas naudojant periferinio laikrodžio įjungimo bitą (atitinkamame RCC registre).

Galiausiai, kaiščiai išvesties režimu taip pat turi laikrodžio greitį. Tai dar viena energijos taupymo funkcija; mūsų atveju mes tiesiog nustatome ją maksimaliai ir pamirštame.

Taigi: mes naudojame SPI, o tai reiškia, kad du kontaktai (su duomenimis ir su laikrodžio signalu) turi būti „alternative push-pull function“, o dar vienas (LAT) turi būti „įprastas stūmimas“. Tačiau prieš priskirdami juos, panagrinėkime SPI.

SPI

Dar viena nedidelė edukacinė programa

SPI arba Serial Peripheral Interface (serijinė periferinė sąsaja) yra paprasta ir labai efektyvi sąsaja, skirta prijungti MK su kitais MK ir išoriniu pasauliu. Jo veikimo principas jau buvo aprašytas aukščiau, kur apie kinišką LED tvarkyklę (žr. 25 skyrių). SPI gali veikti pagrindiniu („master“) ir pavaldiniu („slave“) režimu. SPI turi keturis pagrindinius kanalus, iš kurių ne visi gali būti naudojami:

  • MOSI, pagrindinė išvestis / pavaldinė įvestis: šis kaištis perduoda duomenis pagrindiniu režimu ir priima duomenis pavaldiniu režimu;
  • MISO, Master Input / Slave Output: priešingai, priima pagrindiniame įrenginyje, o perduoda vergu;
  • SCK, Serial Clock: nustato duomenų perdavimo dažnį pagrindiniame įrenginyje arba priima laikrodžio signalą pagalbiniame įrenginyje. Iš esmės pataikyti ritmus;
  • SS, Slave Select: šio kanalo pagalba vergas žino, kad iš jo kažko norima. STM32 jis vadinamas NSS, kur N = neigiamas, t.y. valdiklis tampa vergu, jei šiame kanale yra įžeminimas. Jis puikiai derinamas su „Open Drain Output“ režimu, bet tai jau kita istorija.

Kaip ir visa kita, SPI STM32 yra daug funkcionalu, todėl jį šiek tiek sunku suprasti. Pavyzdžiui, gali veikti ne tik su SPI, bet ir su I2S sąsaja, o dokumentacijoje jų aprašymai maišomi, reikia laiku nupjauti perteklių. Mūsų užduotis yra labai paprasta: mums tereikia siųsti duomenis naudojant tik MOSI ir SCK. Einame į skyrių 25.3.4 (pusiau dvipusis ryšys, pusiau dvipusis ryšys), kur randame 1 laikrodis ir 1 vienkryptis duomenų laidas (1 laikrodžio signalas ir 1 vienkryptis duomenų srautas):

Skaityti 2 duomenų lapus: SPI STM32; PWM, laikmačiai ir pertraukimai STM8
Šiuo režimu programa naudoja SPI tik perdavimo arba tik priėmimo režimu. / Tik perdavimo režimas yra panašus į dvipusį režimą: duomenys perduodami perdavimo kaiščiu (MOSI pagrindiniu režimu arba MISO pavaldiniu), o priėmimo kaištis (atitinkamai MISO arba MOSI) gali būti naudojamas kaip įprastas įvesties / išvesties kaištis. . Tokiu atveju programai tereikia ignoruoti Rx buferį (jei jis nuskaitomas, ten nebus perkeliami duomenys).

Puiku, MISO kaištis laisvas, prie jo prijungkime LAT signalą. Pažiūrėkime į Slave Select, kurį STM32 galima valdyti programiškai, o tai yra itin patogu. To paties pavadinimo pastraipą skaitome 25.3.1 skirsnyje SPI Bendras aprašymas:

Skaityti 2 duomenų lapus: SPI STM32; PWM, laikmačiai ir pertraukimai STM8
Programinės įrangos valdymo NSS (SSM = 1) / Slave pasirinkimo informacija yra SPI_CR1 registro SSI bite. Išorinis NSS kaištis lieka laisvas kitiems programos poreikiams.

Pats laikas rašyti į registrus. Nusprendžiau naudoti SPI2, jo bazinio adreso ieškokite duomenų lape - 3.3 skyriuje Atminties žemėlapis:

Skaityti 2 duomenų lapus: SPI STM32; PWM, laikmačiai ir pertraukimai STM8

Na, pradėkime:

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

Atidarykite 25.3.3 skyrių su savaime suprantamu pavadinimu „SPI konfigūravimas pagrindiniame režime“:

Skaityti 2 duomenų lapus: SPI STM32; PWM, laikmačiai ir pertraukimai STM8

1. SPI_CR2 registre nustatykite nuoseklųjį laikrodžio dažnį bitais BR[0:1].

Registrai surinkti to paties pavadinimo žinyno skyriuje. Adreso poslinkis (Adreso poslinkis) CR1 – 0x00, pagal numatytuosius nustatymus visi bitai yra išvalyti (Iš naujo nustatyti vertę 0x0000):

Skaityti 2 duomenų lapus: SPI STM32; PWM, laikmačiai ir pertraukimai STM8

BR bitai nustato valdiklio laikrodžio daliklį, taip nustatydami dažnį, kuriuo veiks SPI. Mūsų STM32 dažnis bus 72 MHz, LED tvarkyklė pagal jos duomenų lapą veikia iki 25 MHz dažniu, todėl reikia dalinti iš keturių (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. Nustatykite CPOL ir CPHA bitus, kad apibrėžtumėte ryšį tarp duomenų perdavimo ir serijinio laikrodžio laiko (žr. diagramą 240 puslapyje).

Kadangi čia skaitome duomenų lapą, o ne žiūrime į schemas, atidžiau pažvelkime į CPOL ir CPHA bitų tekstinį aprašymą 704 puslapyje (SPI bendrasis aprašymas):

Skaityti 2 duomenų lapus: SPI STM32; PWM, laikmačiai ir pertraukimai STM8
Laikrodžio fazė ir poliškumas
Naudodami SPI_CR1 registro CPOL ir CPHA bitus, galite programiškai pasirinkti keturis laiko ryšius. CPOL (laikrodžio poliškumas) bitas kontroliuoja laikrodžio signalo būseną, kai nepersiunčiami jokie duomenys. Šis bitas valdo pagrindinį ir pavaldų režimus. Jei CPOL nustatytas iš naujo, ramybės režimu SCK kaištis yra žemas. Jei CPOL bitas nustatytas, poilsio režimu SCK kaištis yra aukštas.
Kai nustatytas CPHA (laikrodžio fazės) bitas, aukšto bitų gaudyklės stroboskopas yra antrasis SCK signalo kraštas (krenta, jei CPOL yra aiškus, kyla, jei CPOL nustatytas). Duomenys užfiksuojami antrą kartą pasikeitus laikrodžio signalui. Jei CPHA bitas yra aiškus, aukšto bitų gaudyklės blykstė yra kylantis SCK signalo kraštas (krentantis kraštas, jei nustatytas CPOL, kylantis kraštas, jei CPOL išvalytas). Duomenys fiksuojami pirmą kartą pasikeitus laikrodžio signalui.

Įsisavinę šias žinias, darome išvadą, kad abu bitai turi likti nuliai, nes Norime, kad SCK signalas liktų žemas, kai jis nenaudojamas, o duomenys būtų perduodami kylančioje impulso briaunoje (žr. Kylantis kraštas DM634 duomenų lape).

Beje, čia pirmą kartą susidūrėme su ST duomenų lapų žodyno savybe: juose parašyta frazė „atstatyti bitą į nulį“. šiek tiek atstatytiIr ne šiek tiek išvalyti, kaip, pavyzdžiui, Atmega.

3. Nustatykite DFF bitą, kad nustatytumėte, ar duomenų blokas yra 8 bitų ar 16 bitų formato

Aš specialiai paėmiau 16 bitų DM634, kad nesivarginčiau perduodant 12 bitų PWM duomenis, pavyzdžiui, DM633. Tikslinga nustatyti DFF į vieną:

#define DFF         0x0800

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

4. Sukonfigūruokite LSBFIRST bitą SPI_CR1 registre, kad nustatytumėte bloko formatą

LSBFIRST, kaip rodo jo pavadinimas, pirmiausia sukonfigūruoja perdavimą su mažiausiai reikšmingu bitu. Tačiau DM634 nori gauti duomenis pradedant nuo svarbiausio bito. Todėl paliekame jį nustatyti iš naujo.

5. Aparatūros režimu, jei reikalinga įvestis iš NSS kaiščio, per visą baitų perdavimo seką NSS kaiščiui pritaikykite aukštą signalą. NSS programinės įrangos režimu nustatykite SSM ir SSI bitus SPI_CR1 registre. Jei NSS kaištis turi būti naudojamas kaip išėjimas, reikia nustatyti tik SSOE bitą.

Įdiekite SSM ir SSI, kad pamirštumėte apie NSS aparatinės įrangos režimą:

#define SSI         0x0100
#define SSM         0x0200

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

6. Turi būti nustatyti MSTR ir SPE bitai (jie lieka nustatyti tik tada, kai NSS signalas yra aukštas)

Tiesą sakant, naudodami šiuos bitus mes priskiriame savo SPI kaip pagrindinį ir įjungiame:

#define MSTR        0x0004
#define SPE         0x0040

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

SPI sukonfigūruotas, iš karto rašykime funkcijas, kurios siunčia baitus vairuotojui. Skaitykite toliau 25.3.3 „SPI konfigūravimas pagrindiniame režime“:

Skaityti 2 duomenų lapus: SPI STM32; PWM, laikmačiai ir pertraukimai STM8
Duomenų perdavimo užsakymas
Perdavimas prasideda, kai baitas įrašomas į Tx buferį.
Duomenų baitas įkeliamas į pamainų registrą lygiagrečiai režimu (iš vidinės magistralės) perduodant pirmąjį bitą, po kurio jis perduodamas į nuosekliai MOSI kaiščio režimas, pirmasis arba paskutinis bitas į priekį, priklausomai nuo LSBFIRST bito nustatymo CPI_CR1 registre. TXE vėliavėlė nustatoma po duomenų perdavimo iš Tx buferio į pamainų registrą, taip pat generuoja pertraukimą, jei CPI_CR1 registre nustatytas TXEIE bitas.

Vertime paryškinau keletą žodžių, kad atkreipčiau dėmesį į vieną SPI diegimo STM valdikliuose ypatybę. Ant Atmega TXE vėliava (Tx tuščias, Tx tuščias ir paruoštas priimti duomenis) nustatomas tik tada, kai buvo išsiųstas visas baitas išėjo. Ir čia ši vėliavėlė nustatoma po to, kai baitas buvo įterptas į vidinį poslinkių registrą. Kadangi jis yra stumiamas ten su visais bitais vienu metu (lygiagrečiai), o tada duomenys perduodami nuosekliai, TXE nustatomas prieš baitą iki galo. Tai svarbu, nes mūsų LED tvarkyklės atveju po siuntimo turime ištraukti LAT kaištį visi duomenis, t.y. Vien TXE vėliavėlės mums neužteks.

Tai reiškia, kad mums reikia kitos vėliavos. Pažvelkime į 25.3.7 – „Būsenos vėliavėlės“:

Skaityti 2 duomenų lapus: SPI STM32; PWM, laikmačiai ir pertraukimai STM8
<…>
Skaityti 2 duomenų lapus: SPI STM32; PWM, laikmačiai ir pertraukimai STM8
UŽIMTA vėliava
BSY vėliavėlę nustato ir išvalo aparatinė įranga (įrašymas į ją neturi jokio poveikio). BSY vėliavėlė nurodo SPI ryšio sluoksnio būseną.
Iš naujo nustatoma:
kai perkėlimas baigtas (išskyrus pagrindinį režimą, jei perdavimas yra tęstinis)
kai SPI išjungtas
kai įvyksta pagrindinio režimo klaida (MODF=1)
Jei perdavimas nėra tęstinis, BSY vėliavėlė išvaloma tarp kiekvieno duomenų perdavimo

Gerai, tai pravers. Išsiaiškinkime, kur yra Tx buferis. Norėdami tai padaryti, skaitykite „SPI duomenų registras“:

Skaityti 2 duomenų lapus: SPI STM32; PWM, laikmačiai ir pertraukimai STM8
Bitai 15:0 DR[15:0] Duomenų registras
Gauti arba perduotini duomenys.
Duomenų registras yra padalintas į du buferius – vieną rašymui (perdavimo buferis) ir kitą skaitymui (gavimo buferis). Įrašant į duomenų registrą įrašoma į Tx buferį, o nuskaitant iš duomenų registro bus grąžinta reikšmė, esanti Rx buferyje.

Na, ir būsenos registras, kuriame yra TXE ir BSY vėliavėlės:

Skaityti 2 duomenų lapus: SPI STM32; PWM, laikmačiai ir pertraukimai STM8

Mes rašome:

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

Na, kadangi mums reikia perduoti 16 kartų du baitus, atsižvelgiant į LED tvarkyklės išėjimų skaičių, maždaug taip:

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čiau dar nežinome, kaip ištraukti LAT kaištį, todėl grįšime į I/O.

Smeigtukų priskyrimas

STM32F1 registrai, atsakingi už kaiščių būklę, yra gana neįprasti. Aišku, kad jų yra daugiau nei „Atmega“, tačiau jie skiriasi ir nuo kitų STM lustų. 9.1 skirsnis Bendrasis GPIO aprašas:

Skaityti 2 duomenų lapus: SPI STM32; PWM, laikmačiai ir pertraukimai STM8
Kiekvienas bendrosios paskirties I/O prievadas (GPIO) turi du 32 bitų konfigūracijos registrus (GPIOx_CRL ir GPIOx_CRH), du 32 bitų duomenų registrus (GPIOx_IDR ir GPIOx_ODR), 32 bitų nustatymo/atstatymo registrą (GPIOx_BSRR), 16 bitų nustatymo iš naujo registrą (GPIOx_BRR) ir 32 bitų registrą. bitų blokavimo registras (GPIOx_LCKR).

Pirmieji du registrai yra neįprasti, o taip pat gana nepatogūs, nes 16 prievadų kontaktų yra išsklaidyti juose „keturi bitai vienam broliui“ formatu. Tie. kaiščiai nuo nulio iki septynių yra CRL, o kiti yra CRH. Tuo pačiu metu likusiuose registruose sėkmingai yra visų prievado kaiščių bitai - dažnai lieka pusiau „rezervuoti“.

Kad būtų paprasčiau, pradėkime nuo sąrašo pabaigos.

Mums nereikia blokavimo registro.

Nustatyti ir atstatyti registrai gan juokingi tuo, kad dalinai dubliuoja vienas kitą: rašyti viską galima tik BSRR, kur didesni 16 bitų atstatys piną į nulį, o žemesni – į 1 arba galima ir naudokite BRR, kurio apatiniai 16 bitų atstato tik kaištį . Man patinka antras variantas. Šie registrai yra svarbūs, nes suteikia atominę prieigą prie kaiščių:

Skaityti 2 duomenų lapus: SPI STM32; PWM, laikmačiai ir pertraukimai STM8
Skaityti 2 duomenų lapus: SPI STM32; PWM, laikmačiai ir pertraukimai STM8
Atominis nustatymas arba nustatymas iš naujo
Programuojant GPIOx_ODR bitų lygiu pertraukimų išjungti nereikia: vieną ar daugiau bitų galima pakeisti viena atominio rašymo operacija APB2. Tai pasiekiama įrašant „1“ į nustatymo/atstatymo registrą (GPIOx_BSRR arba, tik atstatymui, GPIOx_BRR), kurį reikia pakeisti. Kiti bitai išliks nepakitę.

Duomenų registrai turi gana savaime suprantamus pavadinimus – IDR = Indėlis Krypties registras, įvesties registras; ODR = produkcija Krypties registras, išvesties registras. Dabartiniame projekte jų mums nereikės.

Ir galiausiai, valdymo registrai. Kadangi mus domina antrieji SPI kaiščiai, būtent PB13, PB14 ir PB15, iš karto žiūrime į CRH:

Skaityti 2 duomenų lapus: SPI STM32; PWM, laikmačiai ir pertraukimai STM8

Ir matome, kad reikės ką nors parašyti bitais nuo 20 iki 31.

Aukščiau jau išsiaiškinome, ko norime iš kaiščių, todėl čia apsieisiu be ekrano kopijos, tik pasakysiu, kad MODE nurodo kryptį (įvestis, jei abu bitai nustatyti į 0) ir kaiščio greitį (mums reikia 50MHz, t.y. abu kaiščiai yra „1“), o CNF nustato režimą: įprastas „push-pull“ – 00, „alternatyvus“ – 10. Pagal numatytuosius nustatymus, kaip matome aukščiau, visi kaiščiai turi trečiąjį bitą iš apačios (CNF0), tai nustato juos į režimą slankioji įvestis.

Kadangi su šiuo lustu planuoju daryti ką nors kita, dėl paprastumo apibrėžiau visas galimas MODE ir CNF reikšmes tiek apatiniame, tiek viršutiniame valdymo registruose.

Kažkaip šitaip

#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ūsų kaiščiai yra prievade B (bazinis adresas – 0x40010C00), kodas:

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

Ir, atitinkamai, galite parašyti LAT apibrėžimus, kuriuos sukels BRR ir BSRR registrai:

/*** LAT pulse – high, then low */
#define LAT_pulse() _PORTB_(_BSRR) = (1<<14); _PORTB_(_BRR) = (1<<14)

#define LAT_low() _PORTB_(_BRR) = (1<<14)

(LAT_žemas tik iš inercijos, visada taip buvo, tegul lieka)

Dabar viskas puiku, bet neveikia. Kadangi tai yra STM32, jie taupo elektros energiją, o tai reiškia, kad reikia įjungti reikalingų išorinių įrenginių laikrodį.

Įjunkite laikrodį

Laikrodis, taip pat žinomas kaip laikrodis, yra atsakingas už laikrodžio rodymą. Ir jau galėjome pastebėti santrumpą RCC. Mes jo ieškome dokumentacijoje: tai yra Reset ir Clock Control.

Kaip buvo sakyta aukščiau, laimei, sunkiausią laiko temos dalį mums atliko STM žmonės, už ką jiems labai dėkojame (dar kartą pateiksiu nuorodą į Di Halto svetainė, kad būtų aišku, kaip tai painu). Mums reikia tik registrų, atsakingų už periferinio laikrodžio įjungimą (Peripheral Clock Enable Registers). Pirmiausia suraskime bazinį RCC adresą, jis yra pačioje „Atminties žemėlapio“ pradžioje:

Skaityti 2 duomenų lapus: SPI STM32; PWM, laikmačiai ir pertraukimai STM8

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

Tada arba spustelėkite nuorodą, kurioje bandote ką nors rasti plokštelėje, arba, daug geriau, peržiūrėkite įgalinančių registrų aprašymus iš skyrių apie įjungti registrus. Kur rasime RCC_APB1ENR ir RCC_APB2ENR:

Skaityti 2 duomenų lapus: SPI STM32; PWM, laikmačiai ir pertraukimai STM8
Skaityti 2 duomenų lapus: SPI STM32; PWM, laikmačiai ir pertraukimai STM8

Atitinkamai juose yra bitų, apimančių SPI2, IOPB (I/O prievadas B) ir alternatyvias funkcijas (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;

Galutinį kodą galima rasti čia.

Jei turite galimybę ir noro išbandyti, tuomet DM634 prijunkite taip: DAI prie PB15, DCK prie PB13, LAT prie PB14. Vairuotoją maitiname nuo 5 voltų, nepamirškite prijungti įžeminimo.

Skaityti 2 duomenų lapus: SPI STM32; PWM, laikmačiai ir pertraukimai STM8

STM8 PWM

PWM STM8

Kai tik planavau šį straipsnį, nusprendžiau, kaip pavyzdį, pabandyti įvaldyti nepažįstamo lusto funkcionalumą naudojant tik duomenų lapą, kad neliktų batsiuvys be batų. STM8 puikiai tiko šiam vaidmeniui: pirma, turėjau porą kiniškų plokščių su STM8S103, antra, ji nėra labai populiari, todėl pagunda skaityti ir rasti sprendimą internete remiasi būtent šių sprendimų stoka.

Mikroschema taip pat turi duomenų lapas и informacinis vadovas RM0016, pirmajame yra pinout ir registro adresai, antrame - visa kita. STM8 yra užprogramuotas C baisioje IDE ST Visual Develop.

Laikrodis ir I/O

Pagal numatytuosius nustatymus STM8 veikia 2 MHz dažniu, tai reikia nedelsiant ištaisyti.

Skaityti 2 duomenų lapus: SPI STM32; PWM, laikmačiai ir pertraukimai STM8
HSI (didelio greičio vidinis) laikrodis
HSI laikrodžio signalas gaunamas iš vidinio 16 MHz RC osciliatoriaus su programuojamu dalikliu (nuo 1 iki 8). Jis nustatomas laikrodžio skirstytuvo registre (CLK_CKDIVR).
Pastaba: pradžioje HSI RC generatorius su dalikliu 8 pasirenkamas kaip pagrindinis laikrodžio signalo šaltinis.

Duomenų lape randame registro adresą, refman aprašą ir matome, kad registrą reikia išvalyti:

#define CLK_CKDIVR *(volatile uint8_t *)0x0050C6

CLK_CKDIVR &= ~(0x18);

Kadangi ketiname paleisti PWM ir prijungti šviesos diodus, pažiūrėkime į kištuką:

Skaityti 2 duomenų lapus: SPI STM32; PWM, laikmačiai ir pertraukimai STM8

Lustas yra mažas, daugelis funkcijų yra pakabintos ant tų pačių kaiščių. Tai, kas yra laužtiniuose skliaustuose, yra „alternatyvi funkcija“, ji perjungiama „parinkčių baitais“ (parinkčių baitų) – kažkas panašaus į Atmega saugiklius. Galite keisti jų reikšmes programiškai, bet tai nėra būtina, nes Nauja funkcija aktyvuojama tik paleidus iš naujo. Lengviau naudoti ST Visual Programer (atsisiųstas su Visual Develop), kuris gali pakeisti šiuos baitus. Smeigtukas rodo, kad pirmojo laikmačio CH1 ir CH2 kaiščiai yra paslėpti laužtiniuose skliaustuose; reikia nustatyti AFR1 ir AFR0 bitus STVP, o antrasis taip pat perkels antrojo laikmačio CH1 išvestį iš PD4 į PC5.

Taigi, 6 kontaktai valdys šviesos diodus: PC6, PC7 ir PC3 pirmajam laikmačiui, PC5, PD3 ir PA3 antrajam.

Pačių įvesties / išvesties kaiščių nustatymas STM8 yra paprastesnis ir logiškesnis nei STM32:

  • pažįstamas iš Atmega DDR duomenų krypčių registro (Duomenų krypties registras): 1 = išvestis;
  • pirmasis valdymo registras CR1, kai išvedamas, nustato „push-pull“ režimą (1) arba atvirą nutekėjimą (0); kadangi šviesos diodus prie lusto jungiu katodais, tai čia palieku nulius;
  • antrasis valdymo registras CR2, kai išvedamas, nustato taktinį dažnį: 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 nustatymas

Pirmiausia apibrėžkime terminus:

  • PWM dažnis – dažnis, kuriuo laikmatis tiksi;
  • Automatinis įkėlimas iš naujo, AR – automatiškai įkeliama vertė, iki kurios laikmatis skaičiuos (impulso periodas);
  • Atnaujinti įvykį, UEV – įvykis, įvykęs, kai laikmatis skaičiuoja iki AR;
  • PWM darbo ciklas – PWM darbo ciklas, dažnai vadinamas „darbo koeficientu“;
  • Užfiksuokite / palyginkite vertę – fiksavimo / palyginimo vertė, į kurią atsiskaito laikmatis ką nors padarys (PWM atveju jis apverčia išvesties signalą);
  • Išankstinės įkrovos vertė – iš anksto įkelta vertė. Palyginkite vertę negali pasikeisti, kol tiksi laikmatis, kitaip PWM ciklas nutrūks. Todėl naujos perduodamos reikšmės dedamos į buferį ir ištraukiamos, kai laikmatis pasiekia atgalinės atskaitos pabaigą ir nustatomas iš naujo;
  • Išlygintas kraštais и Išlygiuoti centre režimai – lygiavimas išilgai sienos ir centre, toks pat kaip Atmel Greitas PWM и Fazės teisingas PWM.
  • OCiREF, išvesties palyginimo atskaitos signalas – atskaitos išvesties signalas, iš tikrųjų tai, kas rodoma atitinkamame kaištyje PWM režimu.

Kaip jau aišku iš pinout, du laikmačiai turi PWM galimybes – pirmasis ir antrasis. Abu yra 16 bitų, pirmasis turi daug papildomų funkcijų (ypač, jis gali skaičiuoti ir aukštyn, ir žemyn). Reikia, kad abu dirbtų vienodai, todėl nusprendžiau pradėti nuo akivaizdžiai prastesnio antrojo, kad netyčia nepanaudotų kažko, ko nėra. Tam tikra problema yra ta, kad visų laikmačių PWM funkcionalumo aprašymas informaciniame vadove yra skyriuje apie pirmąjį laikmatį (17.5.7 PWM režimas), todėl visą laiką turite šokinėti pirmyn ir atgal visame dokumente.

PWM STM8 turi svarbų pranašumą prieš PWM Atmega:

Skaityti 2 duomenų lapus: SPI STM32; PWM, laikmačiai ir pertraukimai STM8
Ribų suderintas PWM
Paskyros konfigūracija iš apačios į viršų
Skaičiavimas iš apačios į viršų yra aktyvus, jei išvalytas DIR bitas TIM_CR1 registre
Pavyzdys
Pavyzdyje naudojamas pirmasis PWM režimas. PWM atskaitos signalas OCiREF laikomas aukštai tol, kol TIM1_CNT < TIM1_CCRI. Priešingu atveju reikia žemo lygio. Jei palyginimo vertė TIM1_CCRI registre yra didesnė už automatinio įkėlimo vertę (TIM1_ARR registras), OCiREF signalas laikomas 1. Jei palyginamoji vertė yra 0, OCiREF laikomas nuliu....

STM8 laikmatis metu atnaujinimo įvykis pirmiausia patikrina palyginti vertę, ir tik tada sukuria atskaitos signalą. „Atmega“ laikmatis pirmiausia įsijungia, o tada palygina, todėl gaunama compare value == 0 išvestis yra adata, su kuria reikia kažkaip susitvarkyti (pavyzdžiui, programiškai apverčiant logiką).

Taigi, ką mes norime padaryti: 8 bitų PWM (AR == 255), skaičiuojant iš apačios į viršų, išlygiavimas išilgai sienos. Kadangi lemputės prie lusto sujungtos katodais, PWM turėtų išvesti 0 (įsijungs šviesos diodas), kol palyginti vertę ir 1 po.

Apie kai kuriuos jau skaitėme PWM režimas, todėl reikiamą antrojo laikmačio registrą randame ieškodami informaciniame vadove šios frazės (18.6.8 – TIMx_CCMR1):

Skaityti 2 duomenų lapus: SPI STM32; PWM, laikmačiai ir pertraukimai STM8
110: Pirmasis PWM režimas – skaičiuojant iš apačios į viršų, pirmasis kanalas yra aktyvus, kai TIMx_CNT < TIMx_CCR1. Kitu atveju pirmasis kanalas bus neaktyvus. [toliau dokumente yra klaidingas kopijavimas-įklijavimas iš 1 laikmačio] 111: Antrasis PWM režimas – skaičiuojant iš apačios į viršų, pirmasis kanalas yra neaktyvus, o TIMx_CNT < TIMx_CCR1. Priešingu atveju pirmasis kanalas bus aktyvus.

Kadangi šviesos diodai su MK sujungti katodais, mums tinka antrasis režimas (pirmasis irgi, bet to dar nežinome).

Skaityti 2 duomenų lapus: SPI STM32; PWM, laikmačiai ir pertraukimai STM8
3 bitas OC1PE: įgalinkite 1 kaiščio išankstinį įkėlimą
0: TIMx_CCR1 išankstinio įkėlimo registras išjungtas. Į TIMx_CCR1 galite rašyti bet kuriuo metu. Nauja vertė veikia iš karto.
1: TIMx_CCR1 išankstinio įkėlimo registras įjungtas. Skaitymo / rašymo operacijos pasiekia išankstinio įkėlimo registrą. Iš anksto įkelta vertė TIMx_CCR1 įkeliama į šešėlių registrą kiekvieno atnaujinimo įvykio metu.
*Pastaba: kad PWM režimas veiktų tinkamai, turi būti įjungti išankstinio įkėlimo registrai. Tai nėra būtina vieno signalo režimu (OPM bitas nustatytas TIMx_CR1 registre).

Gerai, įjunkime viską, ko reikia trims antrojo laikmačio kanalams:

#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 susideda iš dviejų aštuonių bitų registrų, viskas paprasta:

#define TIM2_ARRH  *(volatile uint8_t *)0x00530F
#define TIM2_ARRL  *(volatile uint8_t *)0x005310

TIM2_ARRH = 0;
TIM2_ARRL = 255;

Antrasis laikmatis gali skaičiuoti tik iš apačios į viršų, lygiavimas palei sieną, nieko keisti nereikia. Pavyzdžiui, nustatykime dažnio daliklį į 256. Antrojo laikmačio daliklis nustatomas TIM2_PSCR registre ir yra dviejų laipsnis:

#define TIM2_PSCR  *(volatile uint8_t *)0x00530E

TIM2_PSCR = 8;

Belieka įjungti išvadas ir patį antrąjį laikmatį. Pirmąją problemą išsprendžia registrai Užfiksuoti/palyginti Įjungti: juose asimetriškai išsibarstę du, trys kanalai. Čia taip pat galime sužinoti, kad galima keisti signalo poliškumą, t.y. iš esmės buvo galima naudoti PWM režimą 1. Rašome:

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

Galiausiai paleidžiame laikmatį TIMx_CR1 registre:

Skaityti 2 duomenų lapus: SPI STM32; PWM, laikmačiai ir pertraukimai STM8

#define TIM2_CR1   *(volatile uint8_t *)0x005300

TIM2_CR1 |= 1;

Parašykime paprastą AnalogWrite() analogą, kuris palyginimui perduos faktines reikšmes į laikmatį. Registrai pavadinti nuspėjamai Užfiksuokite / palyginkite registrus, kiekvienam kanalui yra du iš jų: žemos eilės 8 bitai TIM2_CCRxL ir aukščiausios eilės TIM2_CCRxH. Kadangi sukūrėme 8 bitų PWM, pakanka parašyti tik mažiausiai reikšmingus 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;
}

Dėmesingas skaitytojas pastebės, kad turime šiek tiek sugedusį PWM, negalintį pagaminti 100% užpildymo (esant maksimaliai 255 vertei, signalas apverčiamas vienam laikmačio ciklui). Šviesos diodams tai nesvarbu, o dėmesingas skaitytojas jau gali atspėti, kaip tai ištaisyti.

PWM ant antrojo laikmačio veikia, pereikime prie pirmojo.

Pirmasis laikmatis turi lygiai tuos pačius bitus tuose pačiuose registruose (tiesiog tie bitai, kurie liko „rezervuoti“ antrajame laikmatyje, pirmajame aktyviai naudojami įvairiems pažangiems dalykams). Todėl pakanka duomenų lape surasti tų pačių registrų adresus ir nukopijuoti kodą. Na, pakeiskite dažnio daliklio reikšmę, nes... pirmasis laikmatis nori gauti ne dviejų laipsnį, o tikslią 16 bitų reikšmę dviejuose registruose Prescaler High и žemas. Viską darome ir... neveikia pirmas laikmatis. Kas nutiko?

Problemą galima išspręsti tik peržvelgus visą skyrių apie 1 laikmačio valdymo registrus, kur ieškome to, kurio antrasis laikmatis neturi. Bus 17.7.30 Pertraukų registras (TIM1_BKR), kur yra ši dalis:

Skaityti 2 duomenų lapus: SPI STM32; PWM, laikmačiai ir pertraukimai STM8
Įjungti pagrindinę išvestį

#define TIM1_BKR   *(volatile uint8_t *)0x00526D

TIM1_BKR = (1<<7);

Tai jau viskas aišku, kodas ten.

Skaityti 2 duomenų lapus: SPI STM32; PWM, laikmačiai ir pertraukimai STM8

STM8 Multiplex

Multipleksavimas naudojant STM8

Trečiasis mini projektas yra prijungti aštuonis RGB šviesos diodus prie antrojo laikmačio PWM režimu ir priversti juos rodyti skirtingas spalvas. Jis pagrįstas LED multipleksavimo koncepcija, ty jei labai labai greitai įjungsite ir išjungsite šviesos diodus, mums atrodys, kad jie nuolat dega (regėjimo išlikimas, vizualinio suvokimo inercija). kažkada padariau kažkas panašaus į Arduino.

Darbo algoritmas atrodo taip:

  • prijungtas pirmojo RGB šviesos diodo anodas;
  • jį uždegė, siųsdamas reikiamus signalus į katodus;
  • laukė iki PWM ciklo pabaigos;
  • prijungė anodą RGB LED anodą;
  • uždegė...

Na ir t.t. Žinoma, norint gražiai veikti, reikia, kad tuo pačiu metu būtų prijungtas anodas ir „uždegamas“ šviesos diodas. Na, arba beveik. Bet kokiu atveju turime parašyti kodą, kuris išves reikšmes trijuose antrojo laikmačio kanaluose, pakeis jas pasiekus UEV ir tuo pačiu pakeis šiuo metu aktyvų RGB šviesos diodą.

Kadangi šviesos diodų perjungimas yra automatinis, turime sukurti „vaizdo atmintį“, iš kurios pertraukimų tvarkytojas gaus duomenis. Tai paprastas masyvas:

uint8_t colors[8][3];

Norint pakeisti konkretaus šviesos diodo spalvą, pakaks į šį masyvą įrašyti reikiamas reikšmes. O kintamasis bus atsakingas už aktyvaus šviesos diodo skaičių

uint8_t cnt;

Demux

Norint tinkamai sutankinti, mums, kaip bebūtų keista, reikia CD74HC238 demultiplekserio. Demultiplekseris – lustas, įgyvendinantis operatorių aparatinėje įrangoje <<. Per tris įvesties kaiščius (bitai 0, 1 ir 2) mes jam tiekiame trijų bitų skaičių X, o atsakydamas jis suaktyvina išvesties numerį (1<<X). Likusios lusto įvestys naudojamos viso dizaino masteliui padidinti. Šis lustas reikalingas ne tik tam, kad sumažintume užimtų mikrovaldiklio kontaktų skaičių, bet ir dėl saugumo – kad netyčia neįsijungtume daugiau šviesos diodų nei įmanoma ir nesudegintume MK. Lustas kainuoja centą ir visada turėtų būti laikomas savo namų vaistinėlėje.

Mūsų CD74HC238 bus atsakingas už įtampos tiekimą į norimo LED anodo. Visavertiame multiplekse jis tiektų įtampą į kolonėlę per P-MOSFET, tačiau šioje demonstracijoje tai įmanoma tiesiogiai, nes jis naudoja 20 mA, pagal Absoliučiai maksimalus įvertinimas duomenų lape. Nuo duomenų lapas CD74HC238 mums reikia pinouts ir šio cheat sheet:

Skaityti 2 duomenų lapus: SPI STM32; PWM, laikmačiai ir pertraukimai STM8
H = aukštos įtampos lygis, L = žemos įtampos lygis, X – nerūpi

E2 ir E1 jungiame prie įžeminimo, E3, A0, A1 ir A3 prie STM5 kaiščių PD3, PC4, PC5 ir PC8. Kadangi aukščiau pateiktoje lentelėje yra ir žemų, ir aukštų lygių, šiuos kaiščius sukonfigūruojame kaip stumiamus kaiščius.

PWM

Antrojo laikmačio PWM sukonfigūruotas taip pat, kaip ir ankstesnėje istorijoje, su dviem skirtumais:

Pirmiausia turime įjungti pertraukimą Atnaujinti įvykį (UEV), kuri iškvies funkciją, kuri perjungia aktyvų šviesos diodą. Tai daroma keičiant bitą Atnaujinimo pertraukimo įgalinimas registre su iškalbingu vardu

Skaityti 2 duomenų lapus: SPI STM32; PWM, laikmačiai ir pertraukimai STM8
Pertraukti įjungimo registrą

#define TIM2_IER   *(volatile uint8_t *)0x005303

//enable interrupt
TIM2_IER = 1;

Antrasis skirtumas yra susijęs su tankinimo reiškiniu, pvz šešėlius – parazitinis diodų švytėjimas. Mūsų atveju tai gali pasirodyti dėl to, kad laikmatis, sukėlęs UEV pertraukimą, ir toliau tiksi, o pertraukų tvarkytojas nespėja perjungti šviesos diodo, kol laikmatis pradeda kažką rašyti į kaiščius. Norėdami su tuo kovoti, turėsite pakeisti logiką (0 = didžiausias ryškumas, 255 = niekas nedega) ir vengti ekstremalių darbo ciklo verčių. Tie. įsitikinkite, kad po UEV šviesos diodai visiškai užgęsta vienam PWM ciklui.

Poliškumo keitimas:

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

Venkite r, g ir b nustatyti iki 255 ir nepamirškite jų apversti, kai naudojate juos.

Pertraukia

Pertraukimo esmė yra ta, kad tam tikromis aplinkybėmis lustas nustoja vykdyti pagrindinę programą ir iškviečia kokią nors išorinę funkciją. Pertraukimai atsiranda dėl išorinių ar vidinių poveikių, įskaitant laikmatį.

Kai pirmą kartą sukūrėme projektą „ST Visual Develop“, be to main.c gavome langą su paslaptinga byla stm8_interrupt_vector.c, automatiškai įtrauktas į projektą. Šiame faile kiekvienai pertraukimui priskiriama funkcija NonHandledInterrupt. Turime susieti savo funkciją su norimu pertraukimu.

Duomenų lape yra pertraukimo vektorių lentelė, kurioje randame mums reikalingus:

Skaityti 2 duomenų lapus: SPI STM32; PWM, laikmačiai ir pertraukimai STM8
13 TIM2 atnaujinimas/perpildymas
14 TIM2 fiksavimas / palyginimas

Turime pakeisti šviesos diodą UEV, todėl mums reikia pertraukos Nr. 13.

Atitinkamai, pirma, byloje stm8_interrupt_vector.c pakeiskite numatytąjį funkcijos, atsakingos už pertraukimą Nr. 13 (IRQ13), pavadinimą į savo:

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

Antra, turėsime sukurti failą main.h su tokiu turiniu:

#ifndef __MAIN_H
#define __MAIN_H

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

Ir galiausiai įrašykite šią funkciją savo 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;
}

Belieka tik įjungti pertraukimus. Tai atliekama naudojant komandą assembler rim – teks jos ieškoti Programavimo vadovas:

//enable interrupts
_asm("rim");

Kita surinkėjo komanda yra sim – išjungia pertraukimus. Jie turi būti išjungti, kol į „vaizdo atmintį“ įrašomos naujos reikšmės, kad netinkamu momentu įvykęs pertraukimas nesugadintų masyvo.

Visas kodas - „GitHub“..

Skaityti 2 duomenų lapus: SPI STM32; PWM, laikmačiai ir pertraukimai STM8

Jei bent kažkam šis straipsnis pasirodė naudingas, tai aš jį parašiau ne veltui. Mielai sulauksiu pastabų ir pastabų, pasistengsiu į viską atsakyti.

Šaltinis: www.habr.com

Добавить комментарий