Leximi i fletëve të të dhënave 2: SPI në STM32; PWM, kohëmatës dhe ndërprerje në STM8

Leximi i fletëve të të dhënave 2: SPI në STM32; PWM, kohëmatës dhe ndërprerje në STM8

В Pjesa e parë Unë u përpoqa t'u tregoja inxhinierëve të elektronikës hobi që u rritën nga pantallonat Arduino se si dhe pse duhet të lexojnë fletët e të dhënave dhe dokumentacione të tjera për mikrokontrolluesit. Teksti doli të ishte i madh, kështu që premtova të tregoja shembuj praktikë në një artikull të veçantë. Epo, ai e quajti veten një kërpudha qumështi ...

Sot do t'ju tregoj se si të përdorni fletët e të dhënave për të zgjidhur detyra mjaft të thjeshta, por të nevojshme për shumë projekte, në kontrollorët STM32 (Blue Pill) dhe STM8. Të gjitha projektet demo i dedikohen LED-ve të mia të preferuara, ne do t'i ndezim ato në sasi të mëdha, për të cilat do të duhet të përdorim të gjitha llojet e pajisjeve periferike interesante.

Teksti përsëri doli të ishte i madh, kështu që për lehtësi po e bëj përmbajtjen:

Pilula blu STM32: 16 LED me drejtues DM634
STM8: Vendosja e gjashtë kunjave PWM
STM8: 8 LED RGB në tre kunja, ndërprerje

Mohim përgjegjësie: Unë nuk jam inxhinier, nuk pretendoj të kem njohuri të thella në elektronikë, artikulli është i destinuar për amatorë si unë. Në fakt, para dy vitesh e konsideroja veten si audiencën e synuar. Nëse dikush do të më kishte thënë atëherë se fletët e të dhënave në një çip të panjohur nuk ishin të frikshme për t'u lexuar, nuk do të kisha kaluar shumë kohë duke kërkuar disa pjesë kodi në internet dhe duke shpikur paterica me gërshërë dhe shirit ngjitës.

Fokusi i këtij artikulli është te fletët e të dhënave, jo projektet, kështu që kodi mund të mos jetë shumë i rregullt dhe shpesh i ngushtë. Vetë projektet janë shumë të thjeshta, megjithëse të përshtatshme për një njohje të parë me çipin e ri.

Shpresoj që artikulli im do të ndihmojë dikë në një fazë të ngjashme të zhytjes në hobi.

STM32

16 LED me DM634 dhe SPI

Një projekt i vogël duke përdorur Blue Pill (STM32F103C8T6) dhe drejtues LED DM634. Duke përdorur fletët e të dhënave, ne do të kuptojmë drejtuesin, portat STM IO dhe konfigurojmë SPI.

DM634

Çipi tajvanez me 16 dalje PWM 16-bit, mund të lidhet me zinxhirë. Modeli 12-bit i nivelit të ulët është i njohur nga një projekt vendas Paketa me dritë. Në një kohë, duke zgjedhur midis DM63x dhe TLC5940 të mirënjohur, zgjodha DM për disa arsye: 1) TLC në Aliexpress është padyshim false, por kjo nuk është; 2) DM ka një PWM autonome me gjeneratorin e vet të frekuencës; 3) mund të blihej me çmim të ulët në Moskë, në vend që të priste një parcelë nga Ali. Dhe, sigurisht, ishte interesante të mësoje se si ta kontrollosh vetë çipin, në vend që të përdorësh një bibliotekë të gatshme. Çipat tani paraqiten kryesisht në paketën SSOP24; ato janë të lehta për t'u bashkuar me një përshtatës.

Meqenëse prodhuesi është Tajvanez, fletën e të dhënave çipi është i shkruar në anglisht kineze, që do të thotë se do të jetë argëtues. Së pari ne shikojmë në pinout (Lidhja e pinit) për të kuptuar se me cilën këmbë duhet të lidhni, dhe një përshkrim të kunjave (Përshkrimi i kunjit). 16 kunja:

Leximi i fletëve të të dhënave 2: SPI në STM32; PWM, kohëmatës dhe ndërprerje në STM8
Burimet e lavamanit DC (kullimi i hapur)

lavaman / Prodhimi i kullimit të hapur – kullimi; burimi i rrymës hyrëse; dalja është e lidhur me tokën në gjendje aktive - LED janë të lidhura me drejtuesin me katodë. Nga pikëpamja elektrike, kjo, natyrisht, nuk është një "kullim i hapur" (kullimi i hapur), por në fletët e të dhënave shpesh gjendet ky përcaktim për kunjat në modalitetin e shkarkimit.

Leximi i fletëve të të dhënave 2: SPI në STM32; PWM, kohëmatës dhe ndërprerje në STM8
Rezistenca të jashtme midis REXT dhe GND për të vendosur vlerën aktuale të daljes

Një rezistencë referencë është instaluar midis pinit REXT dhe tokëzimit, i cili kontrollon rezistencën e brendshme të daljeve, shihni grafikun në faqen 9 të fletës së të dhënave. Në DM634, kjo rezistencë mund të kontrollohet gjithashtu nga softueri, duke vendosur ndriçimin e përgjithshëm (shkëlqim global); Unë nuk do të hyj në detaje në këtë artikull, thjesht do të vendos një rezistencë 2.2 - 3 kOhm këtu.

Për të kuptuar se si të kontrolloni çipin, le të shohim përshkrimin e ndërfaqes së pajisjes:

Leximi i fletëve të të dhënave 2: SPI në STM32; PWM, kohëmatës dhe ndërprerje në STM8

Po, ja ku është, anglishtja kineze me gjithë lavdinë e saj. Përkthimi i kësaj është problematik, mund ta kuptoni nëse dëshironi, por ka një mënyrë tjetër - shikoni se si përshkruhet lidhja me TLC5940 funksionalisht të ngjashme në fletën e të dhënave:

Leximi i fletëve të të dhënave 2: SPI në STM32; PWM, kohëmatës dhe ndërprerje në STM8
... Kërkohen vetëm tre kunja për të futur të dhëna në pajisje. Skaji në rritje i sinjalit SCLK i zhvendos të dhënat nga pini SIN në regjistrin e brendshëm. Pasi të jenë ngarkuar të gjitha të dhënat, një sinjal i shkurtër XLAT i lartë i mbyll të dhënat e transferuara në mënyrë sekuenciale në regjistrat e brendshëm. Regjistrat e brendshëm janë porta të aktivizuara nga niveli i sinjalit XLAT. Të gjitha të dhënat transmetohen së pari biti më i rëndësishëm.

shul – shul/shul/kyç.
Buzë në rritje – buza kryesore e pulsit
MSB së pari – pjesa më e rëndësishme (më e majta) përpara.
për të dhënat e orës – transmetoni të dhënat në mënyrë sekuenciale (pak nga pak).

Fjala shul gjendet shpesh në dokumentacionin për patate të skuqura dhe përkthehet në mënyra të ndryshme, kështu që për hir të mirëkuptimit do t'ia lejoj vetes

një program të vogël arsimorDrejtuesi LED është në thelb një regjistër ndërrimi. "Shift" (ndryshim) në emrin - lëvizja në bit e të dhënave brenda pajisjes: çdo bit i ri i futur brenda e shtyn të gjithë zinxhirin përpara përpara tij. Meqenëse askush nuk dëshiron të vëzhgojë ndezjen kaotike të LED-ve gjatë ndërrimit, procesi zhvillohet në regjistra tampon të ndarë nga regjistrat e punës me një damper (shul) është një lloj dhome pritjeje ku pjesët janë të renditura në sekuencën e dëshiruar. Kur gjithçka është gati, grila hapet dhe pjesët shkojnë në punë, duke zëvendësuar grupin e mëparshëm. fjalë shul në dokumentacionin për mikroqarqet pothuajse gjithmonë nënkupton një amortizues të tillë, pavarësisht se në çfarë kombinimesh përdoret.

Pra, transferimi i të dhënave në DM634 kryhet si kjo: vendosni hyrjen DAI në vlerën e pjesës më të rëndësishme të LED-së së largët, tërhiqeni DCK lart e poshtë; vendosni hyrjen DAI në vlerën e bitit tjetër, tërhiqni DCK; dhe kështu me radhë derisa të gjitha bitet të jenë transmetuar (me orar brenda), pas së cilës ne tërheqim LAT. Kjo mund të bëhet me dorë (bit-bang), por është më mirë të përdorni një ndërfaqe SPI të përshtatur posaçërisht për këtë, pasi ajo është paraqitur në STM32 tonë në dy kopje.

Pilula blu STM32F103

Hyrje: Kontrollorët STM32 janë shumë më kompleks se Atmega328 sesa mund të duken të frikshëm. Për më tepër, për arsye të kursimit të energjisë, pothuajse të gjitha pajisjet periferike janë fikur në fillim, dhe frekuenca e orës është 8 MHz nga burimi i brendshëm. Për fat të mirë, programuesit STM shkruan kodin që e çon çipin deri në 72 MHz të "llogaritur" dhe autorët e të gjitha IDE-ve që unë njoh e përfshinë atë në procedurën e inicializimit, kështu që ne nuk kemi nevojë të frekuentojmë (por mundesh nëse vërtet dëshiron). Por do të duhet të ndizni pajisjet periferike.

Dokumentacioni: Blue Pill është i pajisur me çipin popullor STM32F103C8T6, ka dy dokumente të dobishme për të:

  • Data Sheet për mikrokontrolluesit STM32F103x8 dhe STM32F103xB;
  • Manual referimi për të gjithë linjën STM32F103 dhe më shumë.

Në fletën e të dhënave ne mund të jemi të interesuar për:

  • Pinouts - pinouts chip - në rast se vendosim t'i bëjmë vetë dërrasat;
  • Harta e kujtesës – harta e kujtesës për një çip specifik. Manuali i Referencës ka një hartë për të gjithë linjën dhe përmend regjistra që i yni nuk i ka.
  • Tabela e përkufizimeve të kunjave – renditja e funksioneve kryesore dhe alternative të kunjave; për "pilulën blu" mund të gjeni fotografi më të përshtatshme në internet me një listë të kunjave dhe funksionet e tyre. Prandaj, ne menjëherë kërkojmë në Google pinout Blue Pill dhe e mbajmë këtë fotografi pranë:

Leximi i fletëve të të dhënave 2: SPI në STM32; PWM, kohëmatës dhe ndërprerje në STM8
NB: kishte një gabim në foto nga interneti, i cili u shënua në komente, faleminderit për këtë. Fotografia është zëvendësuar, por ky është një mësim - është më mirë të kontrolloni informacionin jo nga fletët e të dhënave.

Ne heqim fletën e të dhënave, hapim Manualin e Referencës dhe tani e tutje përdorim vetëm atë.
Procedura: merremi me hyrje/dalje standarde, konfigurojmë SPI, ndezim pajisjet periferike të nevojshme.

Hyrje dalje

Në Atmega328, I/O zbatohet jashtëzakonisht thjeshtë, kjo është arsyeja pse bollëku i opsioneve STM32 mund të jetë konfuz. Tani na duhen vetëm përfundime, por edhe këto kanë katër opsione:

Leximi i fletëve të të dhënave 2: SPI në STM32; PWM, kohëmatës dhe ndërprerje në STM8
kullim i hapur, shtytje-tërheqje, shtytje alternative, kullim alternativ i hapur

"Pull-shty" (shtytje) është dalja e zakonshme nga Arduino, pin mund të marrë vlerën ose LARTË ose LOW. Por me "kullimin e hapur" ka vështirësitë, megjithëse në fakt gjithçka është e thjeshtë këtu:

Leximi i fletëve të të dhënave 2: SPI në STM32; PWM, kohëmatës dhe ndërprerje në STM8
Leximi i fletëve të të dhënave 2: SPI në STM32; PWM, kohëmatës dhe ndërprerje në STM8
Konfigurimi i daljes / kur porta caktohet në dalje: / buferi i daljes i aktivizuar: / - modaliteti i hapjes së shkarkimit: "0" në regjistrin e daljes mundëson N-MOS, "1" në regjistrin e daljes e lë portin në modalitetin Hi-Z ( P-MOS nuk aktivizohet ) / – modaliteti push-pull: “0” në regjistrin e daljes aktivizon N-MOS, “1” në regjistrin e daljes aktivizon P-MOS.

I gjithë ndryshimi midis kullimit të hapur (kullimi i hapur) nga "shty-tërheq" (shtytje) është se në pinin e parë nuk mund të pranojë gjendjen LARTË: kur shkruani një në regjistrin e daljes, ai kalon në modalitetin e rezistencës së lartë (rezistencë e plotë të lartë, Hi-Z). Kur shkruani zero, pini sillet njësoj në të dy mënyrat, si logjikisht ashtu edhe elektrike.

Në modalitetin normal të daljes, pini thjesht transmeton përmbajtjen e regjistrit të daljes. Në "alternativën" kontrollohet nga pajisjet periferike përkatëse (shih 9.1.4):

Leximi i fletëve të të dhënave 2: SPI në STM32; PWM, kohëmatës dhe ndërprerje në STM8
Nëse një bit porti është konfiguruar si një kunj funksioni alternativ, regjistri i pinit çaktivizohet dhe kunja lidhet me pinin periferik.

Funksionaliteti alternativ i secilës kunj përshkruhet në Përkufizimet e pinit Fleta e të dhënave është në imazhin e shkarkuar. Në pyetjen se çfarë të bëni nëse një kunj ka disa funksione alternative, përgjigja jepet nga një fusnotë në fletën e të dhënave:

Leximi i fletëve të të dhënave 2: SPI në STM32; PWM, kohëmatës dhe ndërprerje në STM8
Nëse shumë pajisje periferike përdorin të njëjtin pin, për të shmangur konfliktin midis funksioneve alternative, duhet të përdoret vetëm një periferik në të njëjtën kohë, duke u ndërruar duke përdorur bitin e aktivizimit të orës periferike (në regjistrin përkatës RCC).

Së fundi, kunjat në modalitetin e daljes kanë gjithashtu një shpejtësi të orës. Ky është një tjetër veçori e kursimit të energjisë; në rastin tonë, ne thjesht e vendosim atë në maksimum dhe e harrojmë atë.

Pra: ne përdorim SPI, që do të thotë se dy kunja (me të dhëna dhe me sinjal orë) duhet të jenë "funksion alternativ push-pull", dhe një tjetër (LAT) duhet të jetë "shty-tërheqje e rregullt". Por para se t'i caktojmë, le të merremi me SPI.

SPI

Një tjetër program i vogël arsimor

SPI ose Serial Peripheral Interface (ndërfaqja periferike serike) është një ndërfaqe e thjeshtë dhe shumë efektive për lidhjen e një MK me MK-të e tjera dhe me botën e jashtme në përgjithësi. Parimi i funksionimit të tij është përshkruar tashmë më lart, ku ka të bëjë me drejtuesin kinez LED (në manualin e referencës, shihni seksionin 25). SPI mund të funksionojë në modalitetin master ("master") dhe slave ("skllav"). SPI ka katër kanale bazë, nga të cilat jo të gjitha mund të përdoren:

  • MOSI, Dalja kryesore / Hyrja Slave: ky pin transmeton të dhëna në modalitetin master dhe merr të dhëna në modalitetin slave;
  • MISO, Master Input / Slave Output: përkundrazi, merr në master dhe transmeton në slave;
  • SCK, Serial Clock: cakton frekuencën e transmetimit të të dhënave në master ose merr një sinjal të orës në skllavë. Në thelb goditja e rrahjeve;
  • SS, Slave Select: me ndihmën e këtij kanali, skllavi e di se diçka kërkohet prej tij. Në STM32 quhet NSS, ku N = negative, d.m.th. kontrolluesi bëhet skllav nëse ka tokë në këtë kanal. Kombinohet mirë me modalitetin Open Drain Output, por kjo është një histori tjetër.

Si çdo gjë tjetër, SPI në STM32 është i pasur me funksionalitet, gjë që e bën disi të vështirë për t'u kuptuar. Për shembull, mund të funksionojë jo vetëm me SPI, por edhe me një ndërfaqe I2S, dhe në dokumentacion përshkrimet e tyre janë të përziera, është e nevojshme të ndërpritet teprica në kohën e duhur. Detyra jonë është jashtëzakonisht e thjeshtë: ne vetëm duhet të dërgojmë të dhëna duke përdorur vetëm MOSI dhe SCK. Shkojmë në seksionin 25.3.4 (komunikim gjysmë dupleks, komunikim gjysmë dupleks), ku gjejmë 1 orë dhe 1 tel të dhënash me një drejtim (1 sinjal i orës dhe 1 transmetim i njëanshëm i të dhënave):

Leximi i fletëve të të dhënave 2: SPI në STM32; PWM, kohëmatës dhe ndërprerje në STM8
Në këtë modalitet, aplikacioni përdor SPI ose në modalitetin vetëm transmetim ose vetëm në marrjen. / Modaliteti vetëm transmetimi është i ngjashëm me modalitetin dupleks: të dhënat transmetohen në kutinë e transmetimit (MOSI në modalitetin master ose MISO në modalitetin slave), dhe kunja e marrjes (përkatësisht MISO ose MOSI) mund të përdoret si një pin i rregullt I/O . Në këtë rast, aplikacioni duhet vetëm të injorojë tampon Rx (nëse lexohet, nuk do të ketë të dhëna të transferuara atje).

E shkëlqyeshme, pini MISO është falas, le të lidhim sinjalin LAT me të. Le të shohim Slave Select, i cili në STM32 mund të kontrollohet në mënyrë programore, gjë që është jashtëzakonisht e përshtatshme. Ne lexojmë paragrafin me të njëjtin emër në seksionin 25.3.1 Përshkrimi i Përgjithshëm i SPI:

Leximi i fletëve të të dhënave 2: SPI në STM32; PWM, kohëmatës dhe ndërprerje në STM8
Kontrolli i softuerit NSS (SSM = 1) / Informacioni i përzgjedhjes së skllevërve gjendet në bitin SSI të regjistrit SPI_CR1. Pini i jashtëm NSS mbetet i lirë për nevoja të tjera të aplikacionit.

Është koha për të shkruar në regjistrat. Vendosa të përdor SPI2, të kërkoj adresën e tij bazë në fletën e të dhënave - në seksionin 3.3 Harta e kujtesës:

Leximi i fletëve të të dhënave 2: SPI në STM32; PWM, kohëmatës dhe ndërprerje në STM8

Epo, le të fillojmë:

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

Hapni seksionin 25.3.3 me titullin vetë-shpjegues "Konfigurimi i SPI në modalitetin Master":

Leximi i fletëve të të dhënave 2: SPI në STM32; PWM, kohëmatës dhe ndërprerje në STM8

1. Vendosni frekuencën e orës serike me bit BR[2:0] në regjistrin SPI_CR1.

Regjistrat mblidhen në seksionin e manualit të referencës me të njëjtin emër. Ndërrimi i adresës (Kompensimi i adresës) për CR1 – 0x00, si parazgjedhje të gjitha bitet janë pastruar (Rivendos vlerën 0x0000):

Leximi i fletëve të të dhënave 2: SPI në STM32; PWM, kohëmatës dhe ndërprerje në STM8

Bitët BR vendosin ndarësin e orës së kontrolluesit, duke përcaktuar kështu frekuencën në të cilën do të funksionojë SPI. Frekuenca jonë STM32 do të jetë 72 MHz, drejtuesi LED, sipas të dhënave të tij, funksionon me një frekuencë deri në 25 MHz, kështu që duhet të pjesëtojmë me katër (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. Vendosni bitet CPOL dhe CPHA për të përcaktuar marrëdhënien midis transferimit të të dhënave dhe kohës së orës serike (shih diagramin në faqen 240)

Meqenëse ne po lexojmë një fletë të dhënash këtu dhe nuk po shikojmë skemat, le të hedhim një vështrim më të afërt në përshkrimin e tekstit të biteve CPOL dhe CPHA në faqen 704 (Përshkrim i Përgjithshëm SPI):

Leximi i fletëve të të dhënave 2: SPI në STM32; PWM, kohëmatës dhe ndërprerje në STM8
Faza e orës dhe polariteti
Duke përdorur bitet CPOL dhe CPHA të regjistrit SPI_CR1, mund të zgjidhni në mënyrë programore katër marrëdhënie kohore. Biti CPOL (polariteti i orës) kontrollon gjendjen e sinjalit të orës kur nuk transmetohet asnjë e dhënë. Ky bit kontrollon modalitetin master dhe slave. Nëse CPOL është rivendosur, kunja SCK është e ulët në modalitetin e pushimit. Nëse biti CPOL është vendosur, kunja SCK është e lartë gjatë modalitetit të pushimit.
Kur vendoset biti CPHA (faza e orës), strobi i kurthit të bitit të lartë është skaji i dytë i sinjalit SCK (bie nëse CPOL është i pastër, rritet nëse CPOL është vendosur). Të dhënat kapen nga ndryshimi i dytë në sinjalin e orës. Nëse biti CPHA është i qartë, strobi i kurthit të bitit të lartë është skaji në rritje i sinjalit SCK (buza në rënie nëse është vendosur CPOL, skaji në rritje nëse CPOL pastrohet). Të dhënat kapen në ndryshimin e parë në sinjalin e orës.

Pasi kemi përvetësuar këtë njohuri, arrijmë në përfundimin se të dy bitet duhet të mbeten zero, sepse Ne duam që sinjali SCK të mbetet i ulët kur nuk përdoret dhe të dhënat të transmetohen në skajin në rritje të pulsit (shih Fig. Rising Edge në fletën e të dhënave DM634).

Nga rruga, këtu ne fillimisht hasëm një veçori të fjalorit në fletët e të dhënave ST: në to është shkruar fraza "rivendosni bitin në zero". për të rivendosur pakDhe jo për të pastruar pak, si, për shembull, Atmega.

3. Vendosni bitin DFF për të përcaktuar nëse blloku i të dhënave është format 8-bit ose 16-bit

Kam marrë në mënyrë specifike një DM16 634-bit, në mënyrë që të mos shqetësohem me transmetimin e të dhënave PWM 12-bit, si DM633. Ka kuptim të vendosni DFF në një:

#define DFF         0x0800

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

4. Konfiguro bitin LSBFIRST në regjistrin SPI_CR1 për të përcaktuar formatin e bllokut

LSBFIRST, siç sugjeron emri i tij, fillimisht konfiguron transmetimin me bitin më pak të rëndësishëm. Por DM634 dëshiron të marrë të dhëna duke filluar nga biti më domethënës. Prandaj, e lëmë të rivendoset.

5. Në modalitetin harduer, nëse kërkohet hyrje nga pini NSS, aplikoni një sinjal të lartë në pinin NSS gjatë gjithë sekuencës së transferimit të bajtit. Në modalitetin e softuerit NSS, vendosni bitet SSM dhe SSI në regjistrin SPI_CR1. Nëse pini NSS do të përdoret si një dalje, duhet të vendoset vetëm biti SSOE.

Instaloni SSM dhe SSI për të harruar modalitetin e harduerit NSS:

#define SSI         0x0100
#define SSM         0x0200

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

6. Bitët MSTR dhe SPE duhet të vendosen (ato mbeten të vendosur vetëm nëse sinjali NSS është i lartë)

Në fakt, me këto pjesë ne e caktojmë SPI-në tonë si master dhe e ndezim:

#define MSTR        0x0004
#define SPE         0x0040

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

SPI është konfiguruar, le të shkruajmë menjëherë funksionet që dërgojnë bajt te drejtuesi. Vazhdo të lexosh 25.3.3 "Konfigurimi i SPI në modalitetin master":

Leximi i fletëve të të dhënave 2: SPI në STM32; PWM, kohëmatës dhe ndërprerje në STM8
Urdhri i transferimit të të dhënave
Transmetimi fillon kur një bajt shkruhet në buferin Tx.
Bajti i të dhënave ngarkohet në regjistrin e ndërrimit në paralele modaliteti (nga autobusi i brendshëm) gjatë transmetimit të bitit të parë, pas së cilës ai transmetohet në vijues Modaliteti i pinit MOSI, biti i parë ose i fundit përpara në varësi të cilësimit të bitit LSBFIRST në regjistrin CPI_CR1. Flamuri TXE vendoset pas transmetimit të të dhënave nga buferi Tx tek regjistri i zhvendosjes, dhe gjithashtu gjeneron një ndërprerje nëse biti TXEIE në regjistrin CPI_CR1 është vendosur.

Theksova disa fjalë në përkthim për të tërhequr vëmendjen te një veçori e zbatimit të SPI në kontrollorët STM. Në Atmega flamuri TXE (Tx Bosh, Tx është bosh dhe gati për të marrë të dhëna) vendoset vetëm pasi të jetë dërguar i gjithë bajt jashtë. Dhe këtu ky flamur vendoset pasi bajt të jetë futur në regjistrin e brendshëm të zhvendosjes. Meqenëse shtyhet atje me të gjitha bitet në të njëjtën kohë (paralelisht), dhe më pas të dhënat transmetohen në mënyrë sekuenciale, TXE vendoset përpara se bajt të dërgohet plotësisht. Kjo është e rëndësishme sepse në rastin e drejtuesit tonë LED, duhet të tërheqim kunjin LAT pas dërgimit Të gjithë të dhënat, d.m.th. Nuk do të na mjaftojë vetëm flamuri TXE.

Kjo do të thotë se na duhet një flamur tjetër. Le të shohim 25.3.7 - "Flamujt e statusit":

Leximi i fletëve të të dhënave 2: SPI në STM32; PWM, kohëmatës dhe ndërprerje në STM8
<…>
Leximi i fletëve të të dhënave 2: SPI në STM32; PWM, kohëmatës dhe ndërprerje në STM8
Flamuri i zënë
Flamuri BSY vendoset dhe pastrohet nga hardueri (shkrimi në të nuk ka efekt). Flamuri BSY tregon gjendjen e shtresës së komunikimit SPI.
Ai rivendos:
kur transferimi të përfundojë (përveç në modalitetin master nëse transferimi është i vazhdueshëm)
kur SPI është i çaktivizuar
kur ndodh një gabim i modalitetit master (MODF=1)
Nëse transferimi nuk është i vazhdueshëm, flamuri BSY pastrohet ndërmjet çdo transferimi të të dhënave

Mirë, kjo do të jetë e dobishme. Le të zbulojmë se ku ndodhet buferi Tx. Për ta bërë këtë, lexoni "Regjistrin e të Dhënave SPI":

Leximi i fletëve të të dhënave 2: SPI në STM32; PWM, kohëmatës dhe ndërprerje në STM8
Bit 15:0 DR[15:0] Regjistri i të dhënave
Të dhënat e marra ose të dhënat që do të transmetohen.
Regjistri i të dhënave është i ndarë në dy bufera - një për shkrim (buferi i transmetimit) dhe një për lexim (buferi i marrjes). Shkrimi në regjistrin e të dhënave shkruan në buferin Tx, dhe leximi nga regjistri i të dhënave do të kthejë vlerën që përmban buferi Rx.

Epo, dhe regjistri i statusit, ku gjenden flamujt TXE dhe BSY:

Leximi i fletëve të të dhënave 2: SPI në STM32; PWM, kohëmatës dhe ndërprerje në STM8

Ne shkruajmë:

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

Epo, meqenëse duhet të transmetojmë 16 herë dy bajt, sipas numrit të daljeve të drejtuesit LED, diçka si kjo:

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();
}

Por ne nuk dimë ende se si ta tërheqim pinin LAT, kështu që do të kthehemi te I/O.

Caktimi i kunjave

Në STM32F1, regjistrat përgjegjës për gjendjen e kunjave janë mjaft të pazakontë. Është e qartë se ka më shumë prej tyre sesa Atmega, por ato janë gjithashtu të ndryshme nga çipat e tjerë STM. Seksioni 9.1 Përshkrimi i përgjithshëm i GPIO:

Leximi i fletëve të të dhënave 2: SPI në STM32; PWM, kohëmatës dhe ndërprerje në STM8
Secila nga portet hyrëse/dalëse për qëllime të përgjithshme (GPIO) ka dy regjistra të konfigurimit 32-bit (GPIOx_CRL dhe GPIOx_CRH), dy regjistra të të dhënave 32-bitësh (GPIOx_IDR dhe GPIOx_ODR), një regjistër vendosje/rivendosje 32-bitësh (GPIOx_BSRR), një regjistër rivendosjeje 16-bitësh (GPIOx_BRR) dhe një regjistri i bllokimit të biteve (GPIOx_LCKR).

Dy regjistrat e parë janë të pazakontë, dhe gjithashtu mjaft të papërshtatshëm, sepse 16 kunjat e portave janë të shpërndara nëpër to në një format "katër bit për vëlla". ato. kunjat zero deri në shtatë janë në CRL, dhe pjesa tjetër janë në CRH. Në të njëjtën kohë, regjistrat e mbetur përmbajnë me sukses pjesët e të gjitha kunjave të portit - shpesh mbeten gjysmë të "rezervuara".

Për thjeshtësi, le të fillojmë nga fundi i listës.

Ne nuk kemi nevojë për një regjistër bllokues.

Regjistrat e vendosjes dhe të rivendosjes janë mjaft qesharakë në atë që ato kopjojnë pjesërisht njëri-tjetrin: mund të shkruani gjithçka vetëm në BSRR, ku 16 bitet më të larta do të rivendosin pinin në zero, dhe ato më të ulëtat do të vendosen në 1, ose mundeni gjithashtu përdorni BRR, 16 bitet e poshtme të të cilave rivendosin vetëm pinin . Më pëlqen opsioni i dytë. Këta regjistra janë të rëndësishëm sepse sigurojnë qasje atomike në kunjat:

Leximi i fletëve të të dhënave 2: SPI në STM32; PWM, kohëmatës dhe ndërprerje në STM8
Leximi i fletëve të të dhënave 2: SPI në STM32; PWM, kohëmatës dhe ndërprerje në STM8
Vendosja ose rivendosja atomike
Nuk ka nevojë të çaktivizoni ndërprerjet kur programoni GPIOx_ODR në nivelin e bitit: një ose më shumë bit mund të ndryshohen me një operacion të vetëm të shkrimit atomik APB2. Kjo arrihet duke shkruar një "1" në regjistrin e vendosjes/rivendosjes (GPIOx_BSRR ose, vetëm për rivendosje, GPIOx_BRR) të bitit që duhet ndryshuar. Pjesët e tjera do të mbeten të pandryshuara.

Regjistrat e të dhënave kanë emra mjaft të vetëshpjegueshëm - IDR = të dhëna Regjistri i drejtimit, regjistri i hyrjeve; ODR = prodhim Regjistri i drejtimit, regjistri i daljes. Nuk do të na duhen në projektin aktual.

Dhe së fundi, regjistrat e kontrollit. Meqenëse jemi të interesuar për kunjat e dyta SPI, përkatësisht PB13, PB14 dhe PB15, ne menjëherë shikojmë CRH:

Leximi i fletëve të të dhënave 2: SPI në STM32; PWM, kohëmatës dhe ndërprerje në STM8

Dhe ne shohim se do të na duhet të shkruajmë diçka në bit nga 20 në 31.

Ne kemi kuptuar tashmë më lart se çfarë duam nga kunjat, kështu që këtu do të bëj pa një pamje të ekranit, thjesht do të them që MODE specifikon drejtimin (hyrjen nëse të dy bitet janë vendosur në 0) dhe shpejtësinë e pinit (na duhen 50 MHz, d.m.th. si pini në "1"), dhe CNF vendos modalitetin: "shtytje-tërheqje" e rregullt - 00, "alternative" - ​​10. Si parazgjedhje, siç e shohim më lart, të gjitha kunjat kanë bitin e tretë nga poshtë (CNF0). i vendos në modalitet hyrje lundruese.

Meqenëse planifikoj të bëj diçka tjetër me këtë çip, për thjeshtësi kam përcaktuar të gjitha vlerat e mundshme MODE dhe CNF si për regjistrat e kontrollit të poshtëm ashtu edhe për atë të sipërm.

Disi si kjo

#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

Kunjat tona janë të vendosura në portin B (adresa bazë – 0x40010C00), kodi:

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

Dhe, në përputhje me rrethanat, ju mund të shkruani përkufizime për LAT, të cilat do të tunden nga regjistrat BRR dhe BSRR:

/*** 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 vetëm nga inercia, kështu ka qenë gjithmonë, le të qëndrojë)

Tani gjithçka është mirë, por nuk funksionon. Për shkak se ky është STM32, ata kursejnë energjinë elektrike, që do të thotë që ju duhet të aktivizoni klockimin e pajisjeve periferike të kërkuara.

Aktivizo orët

Ora, e njohur edhe si Ora, është përgjegjëse për orën. Dhe ne mund të vëmë re tashmë shkurtesën RCC. Ne e kërkojmë atë në dokumentacion: ky është Reset dhe Clock Control.

Siç u tha më lart, për fat të mirë, pjesën më të vështirë të temës së orës na e bënë njerëzit e STM, për të cilën i falënderojmë shumë (edhe një herë do të jap një lidhje me Faqja e internetit e Di Halt, për ta bërë të qartë se sa konfuze është). Ne kemi nevojë vetëm për regjistra përgjegjës për aktivizimin e klockimit periferik (Peripheral Clock Enable Registers). Së pari, le të gjejmë adresën bazë të RCC, ajo është në fillim të "Hartës së Kujtesës":

Leximi i fletëve të të dhënave 2: SPI në STM32; PWM, kohëmatës dhe ndërprerje në STM8

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

Dhe pastaj ose klikoni në lidhjen ku përpiqeni të gjeni diçka në pjatë, ose, shumë më mirë, kaloni nëpër përshkrimet e regjistrave të mundshëm nga seksionet rreth aktivizoni regjistrat. Ku do të gjejmë RCC_APB1ENR dhe RCC_APB2ENR:

Leximi i fletëve të të dhënave 2: SPI në STM32; PWM, kohëmatës dhe ndërprerje në STM8
Leximi i fletëve të të dhënave 2: SPI në STM32; PWM, kohëmatës dhe ndërprerje në STM8

Dhe ato, në përputhje me rrethanat, përmbajnë bit që përfshijnë rrahjen e SPI2, IOPB (I/O Port B) dhe funksionet alternative (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;

Kodi përfundimtar mund të gjendet këtu.

Nëse keni mundësinë dhe dëshirën për të provuar, atëherë lidhni DM634 si kjo: DAI në PB15, DCK në PB13, LAT në PB14. Ne e fuqizojmë shoferin nga 5 volt, mos harroni të lidhni bazat.

Leximi i fletëve të të dhënave 2: SPI në STM32; PWM, kohëmatës dhe ndërprerje në STM8

STM8 PWM

PWM në STM8

Kur sapo po planifikoja këtë artikull, vendosa, si shembull, të përpiqesha të zotëroja disa funksione të një çipi të panjohur duke përdorur vetëm një fletë të dhënash, në mënyrë që të mos përfundoja me një këpucar pa çizme. STM8 ishte ideale për këtë rol: së pari, kisha disa dërrasa kineze me STM8S103, dhe së dyti, nuk është shumë popullor, dhe për këtë arsye tundimi për të lexuar dhe gjetur një zgjidhje në internet qëndron në mungesën e këtyre zgjidhjeve.

Çipi gjithashtu ka fletën e të dhënave и Manuali i referencës RM0016, në të parën ka adresa pinout dhe regjistri, në të dytën - gjithçka tjetër. STM8 është programuar në C në një IDE të tmerrshme ST Visual Develop.

Clocking dhe I/O

Si parazgjedhje, STM8 funksionon në një frekuencë prej 2 MHz, kjo duhet të korrigjohet menjëherë.

Leximi i fletëve të të dhënave 2: SPI në STM32; PWM, kohëmatës dhe ndërprerje në STM8
Ora HSI (High Speed ​​Internal).
Sinjali i orës HSI rrjedh nga një oshilator i brendshëm RC 16 MHz me një ndarës të programueshëm (1 deri në 8). Është vendosur në regjistrin e ndarësit të orës (CLK_CKDIVR).
Shënim: në fillim, një oshilator HSI RC me një ndarës 8 zgjidhet si burimi kryesor i sinjalit të orës.

Ne gjejmë adresën e regjistrit në fletën e të dhënave, përshkrimin në refman dhe shohim që regjistri duhet të pastrohet:

#define CLK_CKDIVR *(volatile uint8_t *)0x0050C6

CLK_CKDIVR &= ~(0x18);

Meqenëse do të ekzekutojmë PWM dhe do të lidhim LED-et, le të shohim pikën:

Leximi i fletëve të të dhënave 2: SPI në STM32; PWM, kohëmatës dhe ndërprerje në STM8

Çipi është i vogël, shumë funksione janë pezulluar në të njëjtat kunja. Ajo që është në kllapa katrore është "funksionaliteti alternativ", ai ndërrohet nga "bajt opsionesh" (byte opsionesh) – diçka si siguresat Atmega. Ju mund t'i ndryshoni vlerat e tyre në mënyrë programore, por nuk është e nevojshme, sepse Funksionaliteti i ri aktivizohet vetëm pas një rindezjeje. Është më e lehtë të përdoret ST Visual Programmer (i shkarkuar me Visual Develop), i cili mund t'i ndryshojë këto bajt. Pika tregon se kunjat CH1 dhe CH2 të kohëmatësit të parë janë të fshehura në kllapa katrore; është e nevojshme të vendosni bitet AFR1 dhe AFR0 në STVP, dhe i dyti do të transferojë gjithashtu daljen CH1 të kohëmatësit të dytë nga PD4 në PC5.

Kështu, 6 kunja do të kontrollojnë LED-et: PC6, PC7 dhe PC3 për kohëmatësin e parë, PC5, PD3 dhe PA3 për të dytin.

Vendosja e vetë kunjave I/O në STM8 është më e thjeshtë dhe më logjike sesa në STM32:

  • i njohur nga regjistri i drejtimit të të dhënave Atmega DDR (Regjistri i drejtimit të të dhënave): 1 = dalje;
  • regjistri i parë i kontrollit CR1, kur del në dalje, vendos modalitetin push-tërheqës (1) ose kullimin e hapur (0); meqenëse LED-të i lidh me çipin me katodë, lë zero këtu;
  • regjistri i dytë i kontrollit CR2, kur del në dalje, vendos shpejtësinë e orës: 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

Vendosja e PWM

Së pari, le të përcaktojmë termat:

  • Frekuenca e PWM – frekuenca me të cilën shënon kohëmatësi;
  • Rimbushje automatike, AR – vlerë e ngarkueshme automatike deri në të cilën do të numërojë kohëmatësi (periudha e pulsit);
  • Përditëso ngjarjen, UEV – një ngjarje që ndodh kur kohëmatësi ka numëruar në AR;
  • Cikli i detyrës PWM – Cikli i punës PWM, i quajtur shpesh “faktori i detyrës”;
  • Kapni/Krahaso vlerën – vlera për kapjen/krahasimin, të cilës i ka llogaritur kohëmatësi do të bëjë diçka (në rastin e PWM, ai përmbys sinjalin e daljes);
  • Vlera e parangarkesës – vlera e parangarkuar. Krahasoni vlerën nuk mund të ndryshojë ndërsa kohëmatësi po shënon, përndryshe cikli PWM do të prishet. Prandaj, vlerat e reja të transmetuara vendosen në një tampon dhe tërhiqen kur kohëmatësi arrin fundin e numërimit të tij mbrapsht dhe rivendoset;
  • E përafruar me buzë и Modalitetet e rreshtuara në qendër – shtrirje përgjatë kufirit dhe në qendër, njësoj si ajo e Atmelit PWM i shpejtë и PWM e saktë sipas fazës.
  • OCiREF, Sinjali i referencës krahasuese në dalje - sinjali i daljes referencë, në fakt, ajo që shfaqet në pinin përkatës në modalitetin PWM.

Siç është e qartë tashmë nga pika, dy kohëmatës kanë aftësi PWM - i pari dhe i dyti. Të dy janë 16-bit, i pari ka shumë veçori shtesë (në veçanti, mund të numërojë si lart ashtu edhe poshtë). Ne kemi nevojë për të dyja për të punuar në mënyrë të barabartë, kështu që vendosa të filloj me të dytën dukshëm më të varfër, në mënyrë që të mos përdor aksidentalisht diçka që nuk është aty. Një problem është se përshkrimi i funksionalitetit PWM të të gjithë kohëmatësve në manualin e referencës është në kapitullin për kohëmatësin e parë (17.5.7 Modaliteti PWM), kështu që ju duhet të hidheni përpara dhe mbrapa përgjatë dokumentit gjatë gjithë kohës.

PWM në STM8 ka një avantazh të rëndësishëm ndaj PWM në Atmega:

Leximi i fletëve të të dhënave 2: SPI në STM32; PWM, kohëmatës dhe ndërprerje në STM8
PWM e përafruar me kufi
Konfigurimi i llogarisë nga poshtë lart
Numërimi nga poshtë lart është aktiv nëse biti DIR në regjistrin TIM_CR1 fshihet
Shembull
Shembulli përdor modalitetin e parë PWM. Sinjali i referencës PWM OCiREF mbahet i lartë për sa kohë që TIM1_CNT < TIM1_CCRi. Përndryshe merr një nivel të ulët. Nëse vlera e krahasimit në regjistrin TIM1_CCRi është më e madhe se vlera e ngarkimit automatik (regjistri TIM1_ARR), sinjali OCiREF mbahet në 1. Nëse vlera e krahasimit është 0, OCiREF mbahet në zero....

Kohëmatësi STM8 gjatë ngjarje përditësuese kontrollet e para krahasoni vlerën, dhe vetëm atëherë prodhon një sinjal referimi. Kohëmatësi i Atmega-s fillimisht vidhos dhe më pas krahasohet, duke rezultuar në compare value == 0 dalja është një gjilpërë, e cila duhet të trajtohet disi (për shembull, duke përmbysur programatikisht logjikën).

Pra, çfarë duam të bëjmë: PWM 8-bit (AR == 255), duke numëruar nga poshtë lart, shtrirja përgjatë kufirit. Meqenëse llambat janë të lidhura me çipin me katodë, PWM duhet të nxjerrë 0 (LED ndezur) derisa krahasoni vlerën dhe 1 pas.

Ne kemi lexuar tashmë për disa Modaliteti PWM, kështu që ne gjejmë regjistrin e kërkuar të kohëmatësit të dytë duke kërkuar në manualin e referencës për këtë frazë (18.6.8 - TIMx_CCMR1):

Leximi i fletëve të të dhënave 2: SPI në STM32; PWM, kohëmatës dhe ndërprerje në STM8
110: Modaliteti i parë PWM – kur numëroni nga poshtë lart, kanali i parë është aktiv ndërsa TIMx_CNT < TIMx_CCR1. Përndryshe, kanali i parë është joaktiv. [Më tej në dokument ka një copy-paste të gabuar nga kohëmatësi 1] 111: Modaliteti i dytë PWM – kur numërohet nga poshtë lart, kanali i parë është joaktiv ndërsa TIMx_CNT < TIMx_CCR1. Përndryshe, kanali i parë është aktiv.

Meqenëse LED-et janë të lidhura me MK me katodë, mënyra e dytë na përshtatet (edhe e para, por ne ende nuk e dimë këtë).

Leximi i fletëve të të dhënave 2: SPI në STM32; PWM, kohëmatës dhe ndërprerje në STM8
Biti 3 OC1PE: Aktivizo parangarkimin e pin 1
0: Regjistri i parangarkesës në TIMx_CCR1 është i çaktivizuar. Mund t'i shkruani TIMx_CCR1 në çdo kohë. Vlera e re funksionon menjëherë.
1: Regjistri i parangarkesës në TIMx_CCR1 është aktivizuar. Operacionet e leximit/shkrimit hyjnë në regjistrin e parangarkesës. Vlera e parangarkuar TIMx_CCR1 ngarkohet në regjistrin hije gjatë çdo ngjarje përditësimi.
*Shënim: Që modaliteti PWM të funksionojë siç duhet, duhet të aktivizohen regjistrat e parangarkesës. Kjo nuk është e nevojshme në modalitetin e një sinjali të vetëm (biti OPM është vendosur në regjistrin TIMx_CR1).

Mirë, le të aktivizojmë gjithçka që na nevojitet për tre kanalet e kohëmatësit të dytë:

#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 përbëhet nga dy regjistra tetë-bitësh, gjithçka është e thjeshtë:

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

TIM2_ARRH = 0;
TIM2_ARRL = 255;

Kohëmatësi i dytë mund të numërojë vetëm nga poshtë lart, shtrirja përgjatë kufirit, asgjë nuk duhet të ndryshohet. Le të vendosim ndarësin e frekuencës, për shembull, në 256. Për kohëmatësin e dytë, ndarësi vendoset në regjistrin TIM2_PSCR dhe është një fuqi prej dy:

#define TIM2_PSCR  *(volatile uint8_t *)0x00530E

TIM2_PSCR = 8;

E tëra që mbetet është të ndizni përfundimet dhe vetë kohëmatësin e dytë. Problemi i parë zgjidhet me regjistra Kapni / Krahasoni Aktivizo: ka dy, tre kanale të shpërndara nëpër to në mënyrë asimetrike. Këtu mund të mësojmë gjithashtu se është e mundur të ndryshohet polariteti i sinjalit, d.m.th. në parim, ishte e mundur të përdorni PWM Mode 1. Ne shkruajmë:

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

Dhe së fundi, ne fillojmë kohëmatësin në regjistrin TIMx_CR1:

Leximi i fletëve të të dhënave 2: SPI në STM32; PWM, kohëmatës dhe ndërprerje në STM8

#define TIM2_CR1   *(volatile uint8_t *)0x005300

TIM2_CR1 |= 1;

Le të shkruajmë një analog të thjeshtë të AnalogWrite(), i cili do të transferojë vlerat aktuale në kohëmatës për krahasim. Regjistrat emërtohen në mënyrë të parashikueshme Kapni/Krahaso regjistrat, ka dy prej tyre për çdo kanal: 8 bit të rendit të ulët në TIM2_CCRxL dhe ato të rendit të lartë në TIM2_CCRxH. Meqenëse kemi krijuar një PWM 8-bitësh, mjafton të shkruajmë vetëm bitet më pak të rëndësishme:

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

Lexuesi i vëmendshëm do të vërejë se kemi një PWM pak të dëmtuar, që nuk mund të prodhojë mbushje 100% (në një vlerë maksimale prej 255, sinjali përmbyset për një cikël kohëmatës). Për LED, kjo nuk ka rëndësi, dhe lexuesi i vëmendshëm tashmë mund të hamendësojë se si ta rregullojë atë.

PWM në kohëmatësin e dytë funksionon, le të kalojmë te i pari.

Kohëmatësi i parë ka saktësisht të njëjtat bit në të njëjtët regjistra (thjesht ato bit që mbetën "të rezervuar" në kohëmatësin e dytë përdoren në mënyrë aktive në të parën për të gjitha llojet e gjërave të avancuara). Prandaj, mjafton të gjeni adresat e të njëjtëve regjistra në fletën e të dhënave dhe të kopjoni kodin. Epo, ndryshoni vlerën e ndarësit të frekuencës, sepse ... kohëmatësi i parë dëshiron të marrë jo një fuqi prej dy, por një vlerë të saktë 16-bit në dy regjistra Prescaler Lartë и ulët. Ne bëjmë gjithçka dhe ... kohëmatësi i parë nuk funksionon. Per Cfarë bëhet fjalë?

Problemi mund të zgjidhet vetëm duke parë të gjithë seksionin për regjistrat e kontrollit të kohëmatësit 1, ku kërkojmë atë që nuk e ka kohëmatësi i dytë. do të ketë 17.7.30 Regjistri i ndërprerjeve (TIM1_BKR), ku ka këtë bit:

Leximi i fletëve të të dhënave 2: SPI në STM32; PWM, kohëmatës dhe ndërprerje në STM8
Aktivizo daljen kryesore

#define TIM1_BKR   *(volatile uint8_t *)0x00526D

TIM1_BKR = (1<<7);

Kjo është e gjitha e sigurt tani, kodi atje.

Leximi i fletëve të të dhënave 2: SPI në STM32; PWM, kohëmatës dhe ndërprerje në STM8

STM8 Multiplex

Multipleksimi në STM8

Mini-projekti i tretë është të lidhni tetë LED RGB me kohëmatësin e dytë në modalitetin PWM dhe t'i bëni ato të shfaqin ngjyra të ndryshme. Ai bazohet në konceptin e multipleksimit LED, që është se nëse ndizni dhe fikni LED shumë, shumë shpejt, do të na duket se ato janë vazhdimisht të ndezura (këmbëngulja e shikimit, inercia e perceptimit vizual). Dikur e bëra diçka e tillë në Arduino.

Algoritmi i punës duket si ky:

  • lidhi anodën e LED-it të parë RGB;
  • e ndezi atë, duke dërguar sinjalet e nevojshme në katodë;
  • priti deri në fund të ciklit PWM;
  • lidhi anodën e LED-it të dytë RGB;
  • e ndezi...

Epo, etj. Sigurisht, për funksionim të bukur, kërkohet që anoda të lidhet dhe LED të "ndezet" në të njëjtën kohë. Epo, ose pothuajse. Në çdo rast, duhet të shkruajmë një kod që do të nxjerrë vlerat në tre kanale të kohëmatësit të dytë, do t'i ndryshojë ato kur të arrihet UEV dhe në të njëjtën kohë do të ndryshojë LED RGB aktualisht aktiv.

Meqenëse ndërrimi i LED-ve është automatik, duhet të krijojmë një "memorie video" nga e cila mbajtësi i ndërprerjeve do të marrë të dhëna. Ky është një grup i thjeshtë:

uint8_t colors[8][3];

Për të ndryshuar ngjyrën e një LED specifik, do të jetë e mjaftueshme të shkruani vlerat e kërkuara në këtë grup. Dhe ndryshorja do të jetë përgjegjëse për numrin e LED-it aktiv

uint8_t cnt;

Demux

Për multipleksimin e duhur, na duhet, çuditërisht, një demultipleksues CD74HC238. Demultiplekser - një çip që implementon operatorin në harduer <<. Nëpërmjet tre kunjave hyrëse (bitet 0, 1 dhe 2) ne e ushqejmë atë një numër tre-bitësh X, dhe si përgjigje ai aktivizon numrin e daljes (1<<X). Hyrjet e mbetura të çipit përdoren për të shkallëzuar të gjithë dizajnin. Ne kemi nevojë për këtë çip jo vetëm për të zvogëluar numrin e kunjave të zëna të mikrokontrolluesit, por edhe për sigurinë - në mënyrë që të mos ndezim aksidentalisht më shumë LED se sa është e mundur dhe të mos djegim MK. Çipi kushton një qindarkë dhe duhet të mbahet gjithmonë në kabinetin tuaj të ilaçeve në shtëpi.

CD74HC238 ynë do të jetë përgjegjës për furnizimin e tensionit në anodën e LED-it të dëshiruar. Në një multipleks të plotë, ai do të furnizonte tensionin në kolonë përmes një P-MOSFET, por në këtë demonstrim është e mundur drejtpërdrejt, sepse tërheq 20 mA, sipas vleresime absolute ne maksimum në fletën e të dhënave. Nga Fleta e të dhënave CD74HC238 ne kemi nevojë për pika dhe kjo fletë mashtrimi:

Leximi i fletëve të të dhënave 2: SPI në STM32; PWM, kohëmatës dhe ndërprerje në STM8
H = niveli i tensionit të lartë, L = niveli i ulët i tensionit, X - mos u interesoni

Ne lidhim E2 dhe E1 me tokën, E3, A0, A1 dhe A3 me kunjat PD5, PC3, PC4 dhe PC5 të STM8. Meqenëse tabela e mësipërme përmban nivele të ulëta dhe të larta, ne i konfigurojmë këto kunja si kunja shtytëse.

PWM

PWM në kohëmatësin e dytë është konfiguruar në të njëjtën mënyrë si në historinë e mëparshme, me dy dallime:

Së pari, duhet të aktivizojmë ndërprerjen Përditëso ngjarjen (UEV) i cili do të thërrasë një funksion që ndërron LED-in aktiv. Kjo bëhet duke ndryshuar bitin Aktivizo ndërprerjen e përditësimit në një regjistër me një emër specifik

Leximi i fletëve të të dhënave 2: SPI në STM32; PWM, kohëmatës dhe ndërprerje në STM8
Regjistri i aktivizimit të ndërprerjes

#define TIM2_IER   *(volatile uint8_t *)0x005303

//enable interrupt
TIM2_IER = 1;

Dallimi i dytë lidhet me fenomenin e multipleksimit, si p.sh ghosting – shkëlqim parazitar i diodave. Në rastin tonë, mund të shfaqet për faktin se kohëmatësi, pasi ka shkaktuar një ndërprerje në UEV, vazhdon të shënojë, dhe mbajtësi i ndërprerjes nuk ka kohë të ndërrojë LED përpara se kohëmatësi të fillojë të shkruajë diçka në kunjat. Për ta luftuar këtë, do t'ju duhet të përmbysni logjikën (0 = ndriçimi maksimal, 255 = asgjë nuk është ndezur) dhe të shmangni vlerat ekstreme të ciklit të punës. ato. sigurohuni që pas UEV LED-të të fiken plotësisht për një cikël PWM.

Ndryshimi i polaritetit:

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

Shmangni vendosjen e r, g dhe b në 255 dhe mos harroni t'i përmbysni kur i përdorni.

Ndërpret

Thelbi i një ndërprerjeje është se në rrethana të caktuara çipi ndalon së ekzekutuari programin kryesor dhe thërret një funksion të jashtëm. Ndërprerjet ndodhin për shkak të ndikimeve të jashtme ose të brendshme, duke përfshirë kohëmatësin.

Kur krijuam për herë të parë një projekt në ST Visual Develop, përveç main.c morëm një dritare me një skedar misterioz stm8_interrupt_vector.c, i përfshirë automatikisht në projekt. Në këtë skedar, çdo ndërprerje i caktohet një funksion NonHandledInterrupt. Ne duhet të lidhim funksionin tonë me ndërprerjen e dëshiruar.

Fleta e të dhënave ka një tabelë të vektorëve të ndërprerjes, ku gjejmë ata që na duhen:

Leximi i fletëve të të dhënave 2: SPI në STM32; PWM, kohëmatës dhe ndërprerje në STM8
13 Përditësimi/mbushja e TIM2
14 Kapja/krahaso TIM2

Duhet të ndryshojmë LED në UEV, kështu që na duhet ndërprerja #13.

Prandaj, së pari, në dosje stm8_interrupt_vector.c ndryshoni emrin e paracaktuar të funksionit përgjegjës për ndërprerjen nr. 13 (IRQ13) në emrin tuaj:

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

Së dyti, ne do të duhet të krijojmë një skedar main.h me përmbajtjen e mëposhtme:

#ifndef __MAIN_H
#define __MAIN_H

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

Dhe së fundi, shkruani këtë funksion në tuaj 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;
}

Gjithçka që mbetet është të mundësohen ndërprerjet. Kjo bëhet duke përdorur komandën asembler rim - do të duhet ta kërkosh brenda Manuali i Programimit:

//enable interrupts
_asm("rim");

Një tjetër komandë asembler është sim – fik ndërprerjet. Ato duhet të fiken ndërsa vlerat e reja po shkruhen në "memorien video", në mënyrë që një ndërprerje e shkaktuar në momentin e gabuar të mos prishë grupin.

I gjithë kodi - në GitHub.

Leximi i fletëve të të dhënave 2: SPI në STM32; PWM, kohëmatës dhe ndërprerje në STM8

Nëse të paktën dikush e gjen të dobishëm këtë artikull, atëherë nuk e shkrova kot. Do të jem i lumtur të marr komente dhe vërejtje, do të përpiqem t'i përgjigjem gjithçkaje.

Burimi: www.habr.com

Shto një koment