Lexo fletët datashit 2: SPI në STM32; PWM, timerët dhe ndërprerjet në STM8

Lexo fletët datashit 2: SPI në STM32; PWM, timerët dhe ndërprerjet në STM8

pjesën e parë Provova të shpjegoj elektronikëve hobi, të rritur nga Arduino, se si dhe pse duhet të lexojnë datashit dhe dokumentacionin tjetër për mikrocontrolerët. Teksti doli i gjatë, prandaj premtova të paraqes shembuj praktikë në një artikull të veçantë. Mirë, atëherë quhem kërpudha...

Sot do të tregoj se si, me ndihmën e datashit, të zgjidhim disa detyra të thjeshta, por të nevojshme për shumë projekte mbi kontrolerët STM32 (Blue Pill) dhe STM8. Të gjitha projektet demo i kushtohen dritave të mia të preferuara LED, do t'i ndezim ato në numër të madh, për të cilin do të angazhojmë periferi interesante.

Teksti përsëri doli jashtëzakonisht i gjatë, prandaj për lehtësi po bëj përmbajtje:

STM32 Blue Pill: 16 drita LED me driver DM634
STM8: Konfigurojmë gjashtë linja PWM
STM8: 8 drita LED RGB në tre pin, ndërprerje

Diskleimer: Unë nuk jam inxhinier, nuk pretendoj për njohuri të thella në elektronikë, ky artikull është për të njëjtit si unë pasionantë. Në të vërtetë, për publikun target e konsideroja veten time dy vjet më parë. Po të më kishte thënë dikush atëherë se leximin e datashit për një çip të panjohur nuk është e frikshme, nuk do të kisha humbur shumë kohë në kërkimin e disa copave kodi në internet dhe shpikjen e ndihmave me gishta dhe ndihma adhezuese.

Qendra e këtij artikulli janë datashitët, jo projektet, prandaj kodi mund të mos jetë shumë i pastër dhe shpesh i përcaktuar. Vetë projektet janë shumë të thjeshta, megjithatë të përshtatshme për një njohje të parë me një çip të ri.

Shpresoj që artikulli im të ndihmojë dikë në një fazë të ngjashme të futjes në hobin.

STM32

16 drita LED me DM634 dhe SPI

Një projekt i vogël duke përdorur Blue Pill (STM32F103C8T6) dhe driverin e dritës LED DM634. Me ndihmën e datashit, do të kuptojmë driverin, portet IO të STM dhe do të konfigurojmë SPI.

DM634

Çipi tajvanez me 16 dalje PWM 16-bitëshe, mund të lidhen në zinxhir. Modeli i vogël 12-bit është i njohur për projektin e brendshëm Lightpack. Në kohën e saj, duke zgjedhur midis DM63x dhe TLC5940 të njohur, vendosa për DM për disa arsye: 1) TLC në Aliexpress është me siguri i falsifikuar, ndërsa ky – jo; 2) DM ka PWM autonom me gjeneratorin e tij të frekuencës; 3) mund ta blija me çmim të arsyeshëm në Moskë, e jo të prisja një dërgesë nga Ali. Po ashtu, ishte interesante të mësoja si ta menaxhoja çipin vetë, e jo të përdorja një bibliotekë gati. Çipat tani kryesisht janë të disponueshëm në formën SSOP24, dhe nuk është e vështirë t’i bashkëngjitim në një adaptues.

Duke qenë se prodhuesi është tajvanez, fletë të dhënash për çipin është shkruar në anglisht kinez, kështu që do të jetë argëtues. Fillimisht shikojmë lidhjet (Pin Connection), në mënyrë që të kuptojmë se cila këmbë lidhet me çfarë dhe përshkrimi i pinëve (Pin Description). 16 daljet:

Lexo fletët datashit 2: SPI në STM32; PWM, timerët dhe ndërprerjet në STM8
Burimet e rrymës continue (stoku i hapur)

Sink / Open-drain output – stoku; burim i rrymës së ardhshme; dalje, kur është aktive, është e lidhur me tokën, – LED-të lidhen me drejtuesin nga katodet. Në mënyrë elektrike, sigurisht që nuk është ndonjë «stok i hapur» (open drain), por në datasheet-et kjo shënjë përdoret shpesh për pinët në mënyrën e stokut.

Lexo fletët datashit 2: SPI në STM32; PWM, timerët dhe ndërprerjet në STM8
Rezistorët e jashtëm midis REXT dhe GND për vendosjen e vlerës së rrymës së daljes

Midis pin-it REXT dhe tokës vendoset një rezistor reference, që kontrollon rezistencën e brendshme të daljeve, shih grafikun në fq. 9 të datasheet-it. Në DM634, kjo rezistencë gjithashtu mund të menaxhohet në mënyrë programore, duke vendosur ndriçimin e përgjithshëm (global brightness); nuk do të hyj në detaje në këtë artikull, thjesht do të vendos një rezistor prej 2.2 – 3 kΩ këtu.

Për të kuptuar se si të menaxhojmë çipin, shikojmë përshkrimin e ndërfaqes së pajisjes:

Lexo fletët datashit 2: SPI në STM32; PWM, timerët dhe ndërprerjet në STM8

Ah, ja ai, anglishtja kineze në gjithë shkëlqimin e saj. Të përkthesh këtë është problematike, mund ta kuptosh nëse dëshiron, por ka një rrugë tjetër – të shohim si përshkruhet lidhja në datasheet për TLC5940, që është funksionalisht e ngjashme:

Lexo fletët datashit 2: SPI në STM32; PWM, timerët dhe ndërprerjet në STM8
… Për të futur të dhënat në pajisje ne duam vetëm tre pinë. Ura e përparme e sinjalit SCLK zhvendos të dhënat nga pini SIN në regjistrin e brendshëm. Pas ngarkimit të të gjitha të dhënave, një sinjal i shkurtër i lartë XLAT fikson të dhënat e dërguara radhazi në regjistrat e brendshëm. Regjistrat e brendshëm – peshore që aktivizohen nga niveli i sinjalit XLAT. Të gjitha të dhënat dërgohen me bitin më të lartë përpara.

Latch – peshore/fiksues.
Rising edge – ura e përparme e impulsit
MSB first – bitin më të lartë (bitin ekstrim të majtë) përpara.
për të clock datat – për të transmetuar të dhënat në mënyrë sekondare (bit për bit).

Fjala latch shpesh paraqitet në dokumentacionin e çipeve dhe përkthehet në mënyra të ndryshme, prandaj për të kuptuar lejoj veten një

përmbledhje të vogëlLED-drajveri – në thelb një regjistër lëvizës. "Lëvizje" (shift) në emër do të thotë lëvizje bitësh brenda pajisjes: çdo bit i ri që shtohet brenda shtyn të gjithë zinxhirin përpara. Duke qenë se gjatë lëvizjes askush nuk dëshiron të shohë një ndricim kaotik të LED-ëve, procesi ndodh në regjistra buffer, të ndarë nga një bllokim (latch) – kjo është një lloj pritjeje, ku bitët rreshtohen në rendin e nevojshëm. Kur gjithçka është gati, bllokimi hapet dhe bitët dërgohen të punojnë, duke zëvendësuar grupin e kaluar. Fjala latch në dokumentacionin e mikroçipave shpesh nënkupton një bllokim të tillë, pa marrë parasysh kombinimet në të cilat përdoret.

Pra, transmetimi i të dhënave në DM634 bëhet kështu: vendosim hyrjen DAI në vlerën e bitit më të lartë të LED-it të largët, tërheqim DCK lart-poshtë; vendosim hyrjen DAI në vlerën e bitit tjetër, tërheqim DCK; dhe kështu me radhë, derisa të gjithë bitët të jenë transmetuar (clocked in), pas së cilës tërheqim LAT. Këtë mund ta bëjmë manualisht (bit-bang), por është më mirë të përdorim një ndërfaqe të veçantë për këtë, dhe fatmirësisht ajo është e pranishme në STM32 tonë në dy kopje.

Pilula Blu STM32F103

Hyrjet: kontrollorët STM32 – janë ndjeshëm më të komplikuar se Atmega328, për sa i përket asaj që mund të trembin. Në të njëjtën kohë, për arsye energjetike, në fillim pothuajse e gjithë periferia është e ndaluar, dhe frekuenca e orës është 8 MHz nga burimi i brendshëm. Fatmirësisht, programuesit e STM shkruan kodin që e çon çipin në "frekuencat" 72 MHz, dhe autorët e të gjitha IDE-ve të njohura që di unë e përfshinë atë në procedurën e inicimit, kështu që nuk na duhet ta orientojmë (por mundemi nëse dëshirojmë shumë). Megjithatë, do të duhet të aktivizojmë periferi.

Dokumentacioni: në Blue Pill është instaluar çipi popullor STM32F103C8T6, për të cilin ka dy dokumente të dobishme:

Në fletën e dhënash na interesojnë:

  • Pinouts – definicionet e çipave – për rasti kur vendosim të bëjmë vetë pllakata;
  • Hartë Memorje – harta e memories për një çip specifik. Në Manualin Reference ka një hartë për të gjithë linjën, ku përmenden regjistrat që nuk gjenden te ne.
  • Tabela e Definimin e Pin-ëve – një listë me funksionet kryesore dhe alternative të pin-ëve; për "tabletën blu", në internet mund të gjeni figura më të përshtatshme me listën e pin-ëve dhe funksioneve të tyre. Pra, menjëherë gjejmë pinout për Blue Pill dhe mbajmë këtë figurë në dorë:

Lexo fletët datashit 2: SPI në STM32; PWM, timerët dhe ndërprerjet në STM8
NB: në figurën nga interneti kishte një gabim, i cili u vërejt në komentet, për të cilin falënderojmë. Figura është zëvendësuar, por kjo është një mësim — informacioni që nuk vjen nga datasheet-et është më mirë të verifikohet.

Hiqim datasheet-in, hapim Manualin Reference, tani do të përdorim vetëm atë.
Renditja e veprimeve: mësojmë mbi hyrjen/daljen standarde, konfiguroni SPI, aktivizoni periferi të nevojshme.

Hyrje-dalje

Në Atmega328, hyrja-dalja është realizuar në mënyrë të thjeshtë, për shkak të së cilës shumica e opsioneve të STM32 mund të krijojnë konfuzion. Tani na duhen vetëm daljet, por madje edhe ato kanë katër variante:

Lexo fletët datashit 2: SPI në STM32; PWM, timerët dhe ndërprerjet në STM8
dalje me stok të hapur, dalje "tërheqëse-dhënëse", alternative "tërheqëse-dhënëse", stok i hapur alternativ

"Tërheqëse-dhënëse" (push-pull) – një dalje e njohur nga Arduino, pin-i mund të ketë ose vlerën HIGH ose LOW. Ndërsa me "stok të hapur" dalin vështirësi, megjithatë në të vërtetë, gjithçka është e thjeshtë:

Lexo fletët datashit 2: SPI në STM32; PWM, timerët dhe ndërprerjet në STM8
Lexo fletët datashit 2: SPI në STM32; PWM, timerët dhe ndërprerjet në STM8
Konfigurimi i daljes / kur porta është caktuar për dalje: / aktivizohet buferi i daljes: / – mod i stokut të hapur: "0" në regjistrin e daljes aktivizon N-MOS, "1" në regjistrin e daljes e lë portën në modin Hi-Z (P-MOS nuk aktivizohet) / – mod "tërheqëse-dhënëse": "0" në regjistrin e daljes aktivizon N-MOS, "1" në regjistrin e daljes aktivizon P-MOS.

E gjithë dallimi i stokut të hapur (open drain) nga "tërheqëse-dhënëse" (push-pull) qëndron në faktin se në të parin, pin-i nuk mund të marrë gjendjen HIGH: kur shkruhet një njësi në regjistrin e daljes, ai kalon në modin e rezistencës së lartë (high impedance, Hi-Z). Kur shkruhet zero, pin-i në të dyja modalitetet vepron në të njëjtën mënyrë, si logjikisht ashtu edhe elektrikisht.

Në modin normal të daljes, pin-i thjesht transmeton përmbajtjen e regjistrit të daljes. Në "modalitetin alternativ", ai menaxhohet nga periferia përkatëse (shih. 9.1.4):

Lexo fletët datashit 2: SPI në STM32; PWM, timerët dhe ndërprerjet në STM8
Nëse bita i portës është i konfiguruar si një dalje funksion i alternativ, regjistri i daljes shkëputet, dhe pin-i lidhet me sinjalin e daljes nga periferi.

Funksionaliteti alternativ i çdo pin-i përshkruhet në Definimet e Pin-ëve flet, dhe ajo është në imazhin e shkarkuar. Për pyetjen se çfarë të bëni nëse pini ka disa funksione alternative, përgjigjen e jep nota në flet.

Lexo fletët datashit 2: SPI në STM32; PWM, timerët dhe ndërprerjet në STM8
Nëse disa blloqe periferike përdorin të njëjtin pin, për të shmangur konfliktin midis funksioneve alternative, duhet të përdoret një bllok periferik në të njëjtën kohë, duke kaluar me bitin e aktivizimit të taktimeve të periferisë (në regjistrin përkatës RCC).

Së fundmi, pikat në modalitetin e daljes kanë gjithashtu shpejtësinë e taktimeve. Kjo është një tjetër tipare kursimi energjie, në rastin tonë thjesht vendosim në maksimum dhe harrojmë.

Pra: ne përdorim SPI, kjo do të thotë që dy pinit (me të dhëna dhe me sinjal taktim) duhet të jenë "funksion alternativ tërheqësi-dhënës", dhe një tjetër (LAT) – "tërheqësi i zakonshëm-dhënës". Por para se t'i caktojmë, le të sqarojmë SPI-në.

SPI

Një shpjegim i vogël

SPI ose Serial Peripheral Interface (ndërfaqja periferike seriale) – një ndërfaqe e thjeshtë dhe tepër efektive për komunikimin e MK me MK të tjera dhe në përgjithësi me botën e jashtme. Prinsipi i saj i punës është përshkruar tashmë më sipër, atje ku flitet për drejtorin LED kinez (në manualin e referencës, shiko seksionin 25). SPI mund të funksionojë në modalitetin e masterit („pronarit”) dhe të skllavit („skllavit”). SPI ka katër kanale bazë, nga të cilat mund të aktivizohen jo të gjithë:

  • MOSI, Master Output / Slave Input: ky pin në modalitetin e masterit jep të dhëna, dhe në modalitetin e skllavit merr të dhëna;
  • MISO, Master Input / Slave Output: përkundrazi, në master merr, në skllav jep;
  • SCK, Serial Clock: përcakton frekuencën e transmetimit të të dhënave në master ose merr sinjal taktim në skllav. Në thelb, ai rrah bitet;
  • SS, Slave Select: me këtë kanal skllavi merr vesh se dikush e dëshiron atë. Në STM32 quhet NSS, ku N = negative, dmth kontrollori bëhet skllav nëse në këtë kanal është toka. Kombinohet mirë me modalitetin Open Drain Output, por kjo është një histori tjetër.

Siç është çdo gjë tjetër, SPI në STM32 është i pasur me funksionalitete, të cilat paksa e komplikojnë kuptimin e tij. Për shembull, ai di të punojë jo vetëm si SPI, por edhe si një ndërfaqe I2S, dhe për më tepër, në dokumentacionin e tyre përshkrimi është i ngatërruar, duhet të ndajmë të tepërtin në kohë. Misioni ynë është shumë i thjeshtë: duhet thjesht të dërgojmë të dhëna, duke aktivizuar vetëm MOSI dhe SCK. Shkoni në seksionin 25.3.4 (komunikimi me gjysmë-dupleks, komunikimi gjysmë-dhënës), ku gjejmë 1 clock and 1 unidirectional data wire (1 sinjal taktim dhe 1 rrjedhë të dhënash njëanësore):

Lexo fletët datashit 2: SPI në STM32; PWM, timerët dhe ndërprerjet në STM8
Në këtë modalitet, aplikacioni përdor SPI ose në modalitetin e transmetimit të vetëm, ose të pranimit të vetëm. / Modaliteti i transmetimit të vetëm është i ngjashëm me modalitetin dupleks: të dhënat transmetohen përmes pin-it transmetues (MOSI në modalitetin e mjeshtrit ose MISO në modalitetin e skllavit), dhe pini i pranuar (MISO ose MOSI përkatësisht) mund të përdoret si një pin i zakonshëm hyrës-dalës. Në këtë rast, aplikacioni thjesht duhet të injorojë tamponin Rx (nëse e lexoni, aty nuk do të ketë të dhëna të transmetuara).

Shkëlqyeshëm, pini MISO tani është i lirë, do ta lidhim me sinjalin LAT. Do të merremi me Slave Select, i cili në STM32 mund të kontrollohet programatikisht, që është jashtëzakonisht e përshtatshme. Lexojmë paragrafi me të njëjtin emër të seksionit 25.3.1 Përshkrimi i Përgjithshëm të SPI:

Lexo fletët datashit 2: SPI në STM32; PWM, timerët dhe ndërprerjet në STM8
Kontrolli programatik i NSS (SSM = 1) / Informacioni mbi zgjedhjen e skllavit përmbanet në bitin SSI të regjistrit SPI_CR1. Pini i jashtëm NSS mbetet i lirë për nevoja të tjera të aplikacionit.

Është koha të shkruajmë në regjistra. Kam vendosur të përdor SPI2, gjejmë adresën e tij bazë në dokumentin e të dhënave – në seksionin 3.3 Hartë e Memorisë:

Lexo fletët datashit 2: SPI në STM32; PWM, timerët dhe ndërprerjet në STM8

Pra, le të fillojmë:

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

Hapim seksionin 25.3.3 me emrin që flet ‘Konfigurimi i SPI në modalitetin mjeshtër’:

Lexo fletët datashit 2: SPI në STM32; PWM, timerët dhe ndërprerjet në STM8

1. Cilësoni frekuencën e taktimeve të interfeces sekuencial me bits BR[2:0] në regjistrin SPI_CR1.

Regjistrat janë të mbledhur në seksionin e njëjtë të manualit referues. Shkalla e adresës (Address offset) te CR1 – 0x00, si rregull të gjithë bitët janë resetuar (Reset value 0x0000):

Lexo fletët datashit 2: SPI në STM32; PWM, timerët dhe ndërprerjet në STM8

Bitët BR vendosin ndarësin e frekuencës së kontrollorit, duke përcaktuar kështu frekuencën me të cilën do të punojë SPI. Frekuenca e STM32 do të jetë 72 MHz, dhe LED-drivieri, sipas dokumentit të tij të të dhënave, punon me një frekuencë deri në 25 MHz, kështu që duhet të ndajmë 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 bitët CPOL dhe CPHA për të përcaktuar marrëdhëniet ndërmjet transmetimit të të dhënave dhe taktimeve të interfeces sekuencial (shih skemën në fq. 240)

Duke qenë se ne po lexojmë dokumentin e të dhënave dhe jo duke shqyrtuar skemat, le të studiojmë përshkrimin tekstual të bitëve CPOL dhe CPHA në fq. 704 (Përshkrimi i Përgjithshëm të SPI):

Lexo fletët datashit 2: SPI në STM32; PWM, timerët dhe ndërprerjet në STM8
Faza dhe polariteti i sinjalit të taktimeve
Me ndihmën e bitëve CPOL dhe CPHA të regjistrit SPI_CR1, mund të zgjidhni programatikisht katër variante marrëdhëniesh të kohëve. Biti CPOL (polariteti i sinjalit të taktimeve) kontrollon gjendjen e sinjalit të taktimeve kur të dhënat nuk transmetohen. Ky bit kontrollon modalitetet mjeshtër dhe skllav. Nëse CPOL është resetuar, pini SCK në modalitetin e qetësisë është në nivelin e ulët. Nëse biti CPOL është vendosur, pini SCK në modalitetin e qetësisë është në nivelin e lartë.
Nëse është vendosur bita CPHA (fazën e ciklit të sinjalit), brenda sinjalit SCK, fronti i dytë i sinjalit shërben si strobe për bitin më të lartë (rdhjesa, nëse CPOL është zero, ose rritja, nëse CPOL është e vendosur). Të dhënat regjistrohen me ndryshimin e dytë të ciklit të sinjalit. Nëse bita CPHA është zero, fronti i parë i sinjalit SCK shërben si strobe për bitin më të lartë (rdhjesa, nëse CPOL është i vendosur, ose rritja, nëse CPOL është zero). Të dhënat regjistrohen me ndryshimin e parë të ciklit të sinjalit.

Duke u thelluar në këto njohuri, arrijmë në përfundimin se të dy bita duhet të mbeten zero, sepse duam që sinjali SCK të mbetet i ulur kur nuk përdoret, dhe të dhënat të transmetohen me frontin e rritjes së impulsit (shih Rising Edge në fletën e të dhënave DM634).

Sigurisht, këtu për herë të parë u përballë me një veçori të leksikut në fletat e të dhënave ST: në to fraza ‘të rivendosësh bitin në zero’ shkruhet to reset a bit, e jo to clear a bit, si për shembull në Atmega.

3. Vendosni bitin DFF për të përcaktuar formatin e blokut të të dhënave 8-bitë ose 16-bitë

E mora veçanërisht DM634 16-bitë për të mos u shqetësuar me transmetimin e të dhënave 12-bitë PWM, siç është DM633. DFF ka kuptim të vendoset në një:

#define DFF         0x0800

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

4. Konfiguroni bitin LSBFIRST në regjistrin SPI_CR1 për të përcaktuar formatin e blokut

LSBFIRST, siç tregon emri i tij, konfiguroni transmetimin duke filluar nga bita më i ulët. Por DM634 dëshiron të marrë të dhëna duke filluar nga bita më e lartë. Prandaj, e lëmë të paprekur.

5. Në modalitetin harduerik, nëse kërkohet hyrja nga pina NSS, jepni një sinjal të lartë në pinin NSS gjatë tërë sekuencës së transmetimit të byte-ëve. Në modalitetin programues NSS vendosni bita SSM dhe SSI në regjistrin SPI_CR1. Nëse pini NSS duhet të punojë si dalëse, duhet të vendosni vetëm bitin SSOE.

Vendosim SSM dhe SSI, për të harruar modalitetin harduerik NSS:

#define SSI         0x0100
#define SSM         0x0200

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

6. Duhet të jenë vendosur bita MSTR dhe SPE (këta mbeten të vendosur vetëm nëse në NSS jepet një sinjal i lartë)

Në fakt, me këta bita ne emërojmë SPI-në tonë si master dhe e aktivizojmë atë:

#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ë byte-ët tek drejtuesi. Vazhdoni të lexoni 25.3.3 'Konfigurimi i SPI në modalitetin master':

Lexo fletët datashit 2: SPI në STM32; PWM, timerët dhe ndërprerjet në STM8
Rendi i transmetimit të të dhënave
Transmetimi fillon kur një byte shkruhet në tamponin Tx.
Byte i të dhënave ngarkohet në regjistrin e lëvizjes në modalitetin paralel (nga autobusi i brendshëm) gjatë transmetimit të bitit të parë, pas së cilës transmetohet në modalitetin e në modalitetin e pin MOSI, me bitin e parë ose të fundit përpara në varësi të vendosjes së bitit LSBFIRST në regjistrin CPI_CR1. Flagi TXE vendoset pas dërgesës së të dhënave nga buferri Tx në regjistrin e zhvendosjes, dhe gjithashtu krijohet një ndërprerje nëse blloku TXEIE është vendosur në regjistrin CPI_CR1.

Kam nxjerrë disa fjalë në përkthim për të theksuar një veçori të realizimit të SPI në kontrolerët STM. Në Atmega, flagi TXE (Tx Empty, Tx është bosh dhe gati për të pranuar të dhëna) vendoset vetëm pas dërgimit të gjithë bajtëve jashtë. Ndërsa këtu, ky flag vendoset pasi bajti është futur në regjistrin e brendshëm të zhvendosjes. Pavarësisht se ai futet me të gjithë bitët njëherësh (paralelisht), të dhënat dërgohen më pas në mënyrë të njëpasnjëshme, TXE vendoset para se bajti të dërgohet plotësisht. Kjo është e rëndësishme, sepse në rastin e drejtuesit tonë të LED-ve, na duhet të lëvizim pinin LAT pas dërgimit të gjitha të dhënave, dmth, vetëm flagu TXE nuk do të jetë i mjaftueshëm.

Kjo do të thotë se na nevojitet edhe një flag tjetër. Le të shohim në 25.3.7 – "Flakët e Statusit":

Lexo fletët datashit 2: SPI në STM32; PWM, timerët dhe ndërprerjet në STM8
<…>
Lexo fletët datashit 2: SPI në STM32; PWM, timerët dhe ndërprerjet në STM8
Flagi BUSY
Flagi BSY vendoset dhe resetohet në mënyrë harduerike (shkrimi në të nuk ndikon në asgjë). Flagi BSY tregon gjendjen e shtresës komunikative SPI.
Ai resetohet:
kur dërgimi përfundon (përveç modalitetit master, nëse dërgimi është i vazhdueshëm)
kur SPI është i çaktivizuar
kur ndodh një gabim në modalitetin master (MODF=1)
Nëse dërgimi nuk është i vazhdueshëm, flagi BSY resetohet midis çdo dërgimi të dhënash

Mirë, do të na duhen. Po përcaktojmë se ku ndodhet buferri Tx. Për këtë, le të lexojmë "Regjistrin e të Dhënave SPI":

Lexo fletët datashit 2: SPI në STM32; PWM, timerët dhe ndërprerjet në STM8
Bitët 15:0 DR[15:0] Regjistri i të Dhënave
Të dhënat e marra ose të dhënat për dërgim.
Regjistri i të dhënave ndahen në dy bufra – një për shkrim (bufri i dërgimit) dhe një tjetër për lexim (bufri i pranimit). Shkrimi në regjistrin e të dhënave shkruan në bufri Tx, ndërsa leximi nga regjistri i të dhënave do të kthejë vlerën që gjendet në bufri Rx.

Po ashtu, regjistri i statusit, ku do të gjejmë flakët TXE dhe BSY:

Lexo fletët datashit 2: SPI në STM32; PWM, timerët dhe ndërprerjet në STM8

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
}

Dhe pasi na duhen të dërgojmë 16 herë nga dy bajta, për numrin e daljeve të drejtuesit të LED-ve, atëherë ndoshta kështu:

void sendLEDdata()
{
    LAT_low();
    uint8_t k = 16;
    do
    {   k--;
        dm_shift16(leds[k]);
    } while (k);

    while (_SPI2_(_SPI_SR) & BSY); // përfundo dërgimin

    LAT_pulse();
}

Por për momentin nuk dimë si të lëvizim pinin LAT, prandaj le të kthehemi në I/O.

Dërgojmë pinët

Në STM32F1, regjistrat që lidhen me gjendjen e pinëve janë mjaft të pazakontë. E qartë është se ka më shumë prej tyre se sa në Atmega, por gjithashtu dallohet nga çipat e tjerë STM. Seksioni 9.1 Përshkrimi i përgjithshëm i GPIO:

Lexo fletët datashit 2: SPI në STM32; PWM, timerët dhe ndërprerjet në STM8
Çdo port i hyrje/ daljeve të përgjithshme (GPIO) ka dy regjistra konfigurimi 32-bitë (GPIOx_CRL dhe GPIOx_CRH), dy registre të dhënash 32-bitë (GPIOx_IDR dhe GPIOx_ODR), një regjir 32-bitë të vendosjes/zhbllokimit (GPIOx_BSRR), një regjir 16-bitë të zhbllokimit (GPIOx_BRR) dhe një regjir 32-bitë bllokues (GPIOx_LCKR).

Sa të pazakontë, gjithashtu mjaft të pakëndshëm, këtu janë dy regjistra të parë, sepse 16 pinë të portit janë shpërndarë në to në formatin "nga katër bits për vëlla". Domethënë, pinët nga zero deri në shtatë janë në CRL, ndërsa të tjerët janë në CRH. Ndërkohë, regjistrat e tjerë njësi të gjithë bitët e pinëve të portit – shpesh duke mbetur gjysmë "të rezervuar".

Për lehtësinë, do të fillojmë nga fundi i listës.

Ne nuk do ta kemi nevojën për regjistrin bllokues.

Regjistrat e vendosjes dhe zhbllokimit janë mjaft argëtues sepse pjesërisht dublojnë njëri-tjetrin: mund të shkruani gjithçka vetëm në BSRR, ku 16 bitët më të lartë do ta zbresin pinin në zero, ndërsa të voglat – do ta vendosin në 1, ose gjithashtu mund të përdorni BRR, 16 bitët e tij më të vegjël që vetëm e zbresin pinin. Më pëlqen shumë varianti i dytë. Këta regjistra janë të rëndësishëm sepse sigurojnë akses atomik në pinë:

Lexo fletët datashit 2: SPI në STM32; PWM, timerët dhe ndërprerjet në STM8
Lexo fletët datashit 2: SPI në STM32; PWM, timerët dhe ndërprerjet në STM8
Vendosje ose zhbllokim atomik
Nuk është nevoja të çaktivizoni ndërprerjet kur programoni GPIOx_ODR në nivelin e bitëve: mund të ndryshoni një ose më shumë bita me një operacion të vetëm atomik shkrimi APB2. Kjo arrihet duke shkruar "1" në regjistrin e vendosjes/zhbllokimit (GPIOx_BSRR ose, vetëm për zhbllokim, në GPIOx_BRR) të bitit që duhet të ndryshohet. Bitët e tjerë do të qëndrojnë të pandryshuar.

Regjistrat e dhënash kanë emra mjaft të folur – IDR = Input Direction Register, regjistri i hyrjes; ODR = Output Direction Register, regjistri i daljes. Në projektin aktual, ata nuk do na nevojiten.

Për ta përfunduar, kemi regjistrat kontrollues. Duke pasur parasysh se na interesojnë pinët e dytë SPI, konkretisht PB13, PB14 dhe PB15, menjëherë shikojmë në CRH:

Lexo fletët datashit 2: SPI në STM32; PWM, timerët dhe ndërprerjet në STM8

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

Ne kemi diskutuar më parë për atë që duam nga pins, prandaj këtu do ta kaloj pa screenshot, thjesht do të them se MODE përcakton drejtimin (input, nëse të dy bitët janë të vendosur në 0) dhe shpejtësinë e pin-it (na nevojitet 50MHz, dmth. të dy pinët në "1"), ndërsa CNF përcakton modalitetin: normal "tërheqje-dhënies" – 00, "alternativ" – 10. Sipas parashikimit, siç e shohim më sipër, të gjithë pinët kanë bitin e tretë nga poshtë (CNF0), ai i vendos ata në modalitet floating input.

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

Kështu po, ndoshta kështu

#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

Pins tanë ndodhen 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, për rrjedhojë, mund të shkruajmë definicionet për LAT, që do të aktivizojë 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 thjesht nga zakoni, ka qenë gjithmonë kështu, le të mbetet kështu)

Tani gjithçka është fantastike, por nuk funksionon. Sepse është STM32, këtu kursen energji elektrike, dhe domethënë, duhet të aktivizojmë orarin e periferi tuaj.

Aktivizojmë orarin

Orari menaxhohet nga orë, ato janë Clock. Dhe ne tashmë mund të kemi vënë re shkurtesën RCC. Ta kërkojmë në dokumentacion: kjo është Kontrolli i Rindizjes dhe Orarit (Reset and Clock Control).

Siç thuhet më sipër, fatmirësisht, më e komplikuara nga tema e orarizimit është bërë për ne nga njerëzit e STM, për të cilët faleminderit shumë (do të jap përsëri linkun në faqen e Di Halt’it, në mënyrë që të kuptohet sa e komplikuar është kjo). Na duhen vetëm regjistrat që menaxhojnë aktivizimin e orarizimit të periferi (Peripheral Clock Enable Registers). Për fillim, le të gjejmë adresën bazë të RCC, ajo është në fillim të "Hartës së Memories":

Lexo fletët datashit 2: SPI në STM32; PWM, timerët dhe ndërprerjet në STM8

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

Dhe pastaj ose të klikoni në lidhjen ku të provoni të gjeni diçka në tabelë, ose, shumë më mirë, të kaloni përmes përshkrimeve të regjistrave aktivizues nga seksionet për enable registers. Ku do të gjejmë RCC_APB1ENR dhe RCC_APB2ENR:

Lexo fletët datashit 2: SPI në STM32; PWM, timerët dhe ndërprerjet në STM8
Lexo fletët datashit 2: SPI në STM32; PWM, timerët dhe ndërprerjet në STM8

Dhe në to, përkatësisht, bitët që aktivizojnë orarizimin 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 ka mundësi dhe dëshirë për të provuar, atëherë lidhim DM634 kështu: DAI në PB15, DCK në PB13, LAT në PB14. Furnizojmë driverin nga 5 volt, mos harrojmë të bashkojmë tokat.

Lexo fletët datashit 2: SPI në STM32; PWM, timerët dhe ndërprerjet në STM8

STM8 PWM

PWM në STM8

Kur kur planifikuar këtë artikull, vendosa të provoj të zotëroj ndonjë funksionalitet të një çipi të panjohur vetëm me fletushkën teknike, për të mos përfunduar si një shampanjë pa shampanjë. STM8 ishte i përshtatshëm për këtë rol: së pari, kisha disa pllaka kineze me STM8S103, dhe së dyti, ai nuk është shumë i njohur, kështu që tundimi për të kërkuar një zgjidhje në internet përballet me mungesën e këtyre zgjidhjeve.

Për çipin ka gjithashtu fletë të dhënash dhe manualin reference RM0016, në të parin pinout dhe adresat e regjistrave, në të dytin – gjithçka tjetër. STM8 programohet në C në një IDE jo shumë tërheqëse ST Visual Develop.

Taktime dhe hyrje-dalja

Për default, STM8 punon në një frekuencë prej 2 MHz, kjo duhet të korrigohet menjëherë.

Lexo fletët datashit 2: SPI në STM32; PWM, timerët dhe ndërprerjet në STM8
Sinjali takt HSI (sinjali i brendshëm i shpejtë)
Sinjali takt HSI merret nga një gjenerator RC të brendshëm 16 MHz me një ndarës të programueshëm (nga 1 deri në 8). Ai përcaktohet në regjistrin e ndarësit të sinjalit takt (CLK_CKDIVR).
Shënim: në fillim, burimi kryesor i sinjalit takt zgjidhet gjeneratori HSI RC me ndarës 8.

Gjejmë adresën e regjistrit në fletushkë, përshkrimi në refman dhe shohim se regjistri duhet të pastrohet:

#define CLK_CKDIVR *(volatile uint8_t *)0x0050C6

CLK_CKDIVR &= ~(0x18);

Duke qenë se po planifikojmë të aktivizojmë PWM dhe të lidhem me LED, shikojmë pinout-in:

Lexo fletët datashit 2: SPI në STM32; PWM, timerët dhe ndërprerjet në STM8

Çipi është i vogël, shumë funksione janë të lidhura me pin të njëjtë. Ajo që është në kllapa katrore është "funksionalitet alternativ", ai kalon me "bytet e mundësive" (option bytes) – diçka si fuzat e Atmegës. Mund t'i ndryshoni vlerat programatikisht, por nuk është e nevojshme, pasi funksionaliteti i ri aktivizohet vetëm pas ri-ngritjes. Më e lehtë është të përdorni ST Visual Programmer (shkarkohet së bashku me Visual Develop), i cili di të ndryshojë këto byte. Në pinout duket se daljet CH1 dhe CH2 të timerit të parë janë të fshehura në kllapa; duhet të vendosim bitet AFR1 dhe AFR0 në STVP, dhe bita e dytë gjithashtu do ta çojë daljen CH1 të timerit të dytë nga PD4 në PC5.

Kështu, ndriçimi i LED do të menaxhohet nga 6 pin: PC6, PC7 dhe PC3 për timerin e parë, PC5, PD3 dhe PA3 për të dytin.

Konfigurimi i vetë pini të hyrje-daljes në STM8 është më i thjeshtë dhe më logjik se sa në STM32:

  • regjistri i drejtimeve të të dhënave DDR (Data Direction Register): 1 = dalje;
  • regjistri i kontrollit CR1 për dalje përcakton modalitetin "tërheq dhe shty" (1) ose stok të hapur (0); pasi unë po lidhem me LED me katodet, lë këtu zerot;
  • regjistri i dytë të kontrollit CR2 për dalje përcakton shpejtësinë e taktimit: 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

Konfigurimi PWM

Në fillim, le të sqarojmë disa termine:

  • Frekuenca PWM – frekuenca me të cilën tikuar timeri;
  • Auto-reload, AR – vlera që ngarkohet automatikisht deri në të cilën do të llogarisë timeri (periudha e impulsit);
  • Ngjarja e përditësimit, UEV – ngjarja që ndodh kur timeri arriti në AR;
  • Cikli i Përmbushjes PWM – koeficienti i mbushjes PWM, shpesh quhet "shkalla e mbushjes";
  • Vlera e Kapjes/Shtesës – vlera për kapjen/shtesën, deri në të cilën timeri do të bëjë diçka (në rastin e PWM – inverton sinjalin dalës);
  • Vlera e Parakohshme – vlerë e ngarkuar paraprakisht. Vlera e krahasimit nuk mund të ndryshohet ndërsa timeri tikun, në të kundërt ndodh kollapsi i ciklit të PWM. Prandaj, vlerat e reja të dërguara vendosen në një buffer dhe nxirren prej andej kur timeri arrin fundin e numërimit dhe reset-het;
  • Modet e Palosjes me Rrymë të Drejtë dhe Modet e Palosjes Qendrore – palosja në skaj dhe në qendër, ato janë të njëjta si ato të ATMEL. PWM e Shpejtë dhe PWM e Korrigjuar në Fazë.
  • OCiREF, Sinjali Referues i Krahasimit të Daljes – sinjali referues dalës, ndoshta, ajo që në modin PWM gjendet në pinsin përkatës.

Siç është e qartë nga pinout-i, PWM është i disponueshëm nga dy timerat – i pari dhe i dyti. Të dy janë 16-bit, i pari disponon shumë karakteristika shtesë (për shembull, mund të llogarisë edhe në mënyrë rritëse dhe zvogëlues). Na nevojitet që të dy të funksionojnë njësoj, prandaj vendosa të filloj me të dytin që është më i kufizuar, për të mos rënë rastësisht në përdorim të ndonjë funksioni që nuk e ka. Një problem i caktuar është se përshkrimi i funksionalitetit të PWM për të gjitha timerat është në manualin referues në kapitullin për timerin e parë (17.5.7 Moda PWM), prandaj ndonjëherë duhet të kalojmë nga njëra në tjetrën në dokument.

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

Lexo fletët datashit 2: SPI në STM32; PWM, timerët dhe ndërprerjet në STM8
PWM me Palosje në Skaj
Konfigurimi i numërimit nga poshtë lart
Numërimi nga poshtë lart është aktiv, nëse b it DIR në regjistrin TIM_CR1 është resetuar.
Shembulli
Shembulli përdor modin e parë të PWM. Sinjali referues PWM OCiREF mbahet në nivel të lartë, për sa kohë TIM1_CNT < TIM1_CCRi. Në të kundërt, ai merr nivel të ulët. Nëse vlera për krahasim në regjistrin TIM1_CCRi është më e madhe se vlera e auto-ngarkuar (regjistri TIM1_ARR), sinjali OCiREF mbahet në 1. Nëse vlera për krahasim është 0, OCiREF mbahet në zero.

Timeri STM8 gjatë ngjarjes së përditësimit për herë të parë kontrollon vlerën e krahasimit, dhe pastaj jep sinjalin referues. Në Atmega, timeri së pari punon dhe pastaj krahasohet, si rezultat, kur vlera e krahasimit == 0 Në daljen ka një gjilpërë me të cilën duhet të luftojmë ndonjë mënyrë (p.sh. duke invertuar logjikën programore).

Pra, çfarë duam të bëjmë: 8-bit PWM (AR == 255), llogaritur nga poshtë lart, me rregullim në kufi. Duke qenë se llambat janë lidhur me çipin me katode, PWM duhet të japë 0 (LED ndezur) deri vlerën e krahasimit dhe 1 pas.

Kemi lexuar tashmë për disa modalitetet PWM, prandaj gjejmë regjistrin e dytë të timerit duke kërkuar në manualin referues me këtë frazë (18.6.8 – TIMx_CCMR1):

Lexo fletët datashit 2: SPI në STM32; PWM, timerët dhe ndërprerjet në STM8
110: Modaliteti i parë PWM – në llogaritjen nga poshtë lart, kanali i parë është aktiv derisa TIMx_CNT < TIMx_CCR1. Në të kundërt, kanali i parë nuk është aktiv. [më pas në dokument ka kopjime të gabuara nga timeri 1]
111: Modaliteti i dytë PWM – në llogaritjen nga poshtë lart, kanali i parë nuk është aktiv derisa TIMx_CNT < TIMx_CCR1. Në të kundërt, kanali i parë është aktiv.

Duke qenë se LED-et janë lidhur me MK me katode, na përshtatet modaliteti i dytë (edhe i pari, por ende nuk e dimë këtë).

Lexo fletët datashit 2: SPI në STM32; PWM, timerët dhe ndërprerjet në STM8
Bit 3 OC1PE: Aktivizo ngarkimin paraprak të daljes 1
0: Regjistri i ngarkimit paraprak në TIMx_CCR1 është i fikur. Mund të shkruhet në TIMx_CCR1 në çdo kohë. Vlera e re funksionon menjëherë.
1: Regjistri i ngarkimit paraprak në TIMx_CCR1 është i aktivizuar. Operacionet e leximit/shkrimit adresojnë regjistrin e ngarkimit paraprak. Vlera e ngarkuar paraprakisht TIMx_CCR1 ngarkohet në regjistrin e hijeve gjatë secilës ngjarje përditësimi.
*Shënim: për funksionimin e saktë të modalitetit PWM, regjistrat e ngarkimit paraprak duhet të aktivizohen. Kjo nuk është e nevojshme në modalitetin e sinjalit të vetëm (kur bita OPM në regjistrin TIMx_CR1 është e vendosur).

Ok, aktivizojmë gjithçka që na nevojitet për tre kanale të timerit 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, këtu është shumë e thjeshtë:

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

TIM2_ARRH = 0;
TIM2_ARRL = 255;

Timeri i dytë mund të numërojë vetëm nga poshtë lart, me rregullim në kufi, nuk ka nevojë të ndryshohet asgjë. Do të vendosim pjesëtimin e frekuencës, p.sh. në 256. Te timeri i dytë, pjesëtimi vendoset në regjistrin TIM2_PSCR dhe përfaqëson një fuqi të dyshes:

#define TIM2_PSCR  *(volatile uint8_t *)0x00530E

TIM2_PSCR = 8;

E mbetur është të aktivizojmë daljet dhe vetë timerin e dytë. Problemi i parë zgjidhet me regjistrat Capture/Compare Aktivizo: ka dy nga ata, tre kanale të shpërndara në mënyrë asimetrike. Këtu gjithashtu mund të mësojmë se mund të ndryshohet polariteti i sinjalit, pra në parim mund të ishte përdorur edhe PWM Mode 1. 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 përfundimisht, nisëm timerin në regjistrin TIMx_CR1:

Lexo fletët datashit 2: SPI në STM32; PWM, timerët dhe ndërprerjet në STM8

#define TIM2_CR1   *(volatile uint8_t *)0x005300

TIM2_CR1 |= 1;

Do të shkruajmë një analog më të thjeshtë të AnalogWrite(), e cila do t'i dërgojë timerit vlerat për krahasim. Regjistrat me siguri quhen Capture/Compare registers, dy dy dy kanale: 8 bitët e para në TIM2_CCRxL dhe 8 bitët e lartë në TIM2_CCRxH. Duke qenë se kemi vendosur PWM me 8 bitë, mjafton të shkruajmë vetëm bitët e poshtëm:

#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 kujdesshëm do të vërejë se kemi një PWM pak defekt, i paaftë për të dhënë 100% mbushje (në vlerën maksimale 255, si si është e invertohet për një cikël timer). Për LED-të, kjo nuk ka rëndësi, dhe lexuesi i kujdesshëm tashmë e kupton se si ta rregullojë këtë.

PWM në timerin e dytë funksionon, kalojmë te i pari.

Timeri i parë ka të njëjtat bite në të njëjtat regjistra (thjesht ato bite që mbetën "të rezervuar" në timerin e dytë, në të parin përdoren aktivisht për gjëra të avancuara). Prandaj, mjafton të gjeni adresat e këtyre regjistrave në fletëkujtim dhe të kopjoni kodin. Po ashtu, duhen ndryshuar vlerat e ndarësit të frekuencës, sepse timeri i parë do të marrë një vlerë precize 16-bit në dy regjistra, jo një fuqi e dyshes. Prescaler High dhe I Ulët. E gjithë kjo bëhet dhe… timeri i parë nuk funksionon. Çfarë ndodhi?

Problemi mund të zgjidhet vetëm duke shikuar tërë seksionin mbi regjistrat e kontrollit të timerit 1, ku kërkojmë atë që nuk e ka timeri i dytë. Do të gjendet 17.7.30 Regjistri i Prishjes (TIM1_BKR), ku ka një bit të tillë:

Lexo fletët datashit 2: SPI në STM32; PWM, timerët dhe ndërprerjet në STM8
Aktivizo daljen kryesore

#define TIM1_BKR   *(volatile uint8_t *)0x00526D

TIM1_BKR = (1<<7);

Tani gjithçka është e saktë, kodi atje gjithashtu.

Lexo fletët datashit 2: SPI në STM32; PWM, timerët dhe ndërprerjet në STM8

STM8 Multiplex

Multipleximi në STM8

Projekti i tretë përbëhet nga lidhja e tetë RGB LED-ësh në timerin e dytë në mënyrë PWM dhe të bëjë që ata të shfaqin ngjyrat e ndryshme. Në thelb - koncepti i multipleximit të LED-ve, që e bën deri në atë pikë që nëse ndizni dhe fikni LED-të shumë-shumë shpejt, do të duket se ata janë të ndezur vazhdimisht (përshtypja e qëndrueshmërisë, inercia e perceptimit vizual). Njëherë e kam bërë diçka të tillë në Arduino.

Algoritmi i punës duket kështu:

  • lidhi anodin e LED-it të parë RGB;
  • e ndizni atë, duke dërguar sinjale të nevojshme për katodet;
  • pritni fundin e ciklit PWM;
  • lidhi anodin e LED-it të dytë RGB;
  • e ndizni atë…

Dhe kështu me radhë. Sigurisht, për të funksionuar bukur, është e nevojshme që lidhja e anodit dhe "ndizja" e LED-it të ndodhin njëkohësisht. Ose pothuajse. Në çdo rast, duhet të shkruajmë kodin që do të japë vlera në tri kanale të timerit të dytë, dhe kur të arrihet UEV, t'i ndryshojë ato dhe njëkohësisht të ndryshojë RGB-LED-in aktiv në atë moment.

Duke switch LED bëhet automatikisht, duhet të krijohet një "memorie video", nga e cila manipuluesi i ndërprerjeve do të marrë të dhënat. Kjo është një masiv i thjeshtë:

uint8_t colors[8][3];

Për të ndryshuar ngjyrën e një LED të veçantë, mjafton të shkruani vlerat e nevojshme në këtë masiv. Ndërsa për numrin e LED aktiv do të kujdeset variabla

uint8_t cnt;

Demuks

Për multipleximin e duhur, na nevojitet, siç është e pritur, demultiplexori CD74HC238. Demultiplexori është një çip, i cila implementon operatorin <<. Duke përdorur tre pinë hyrës (bitët 0, 1 dhe 2) i ofrojmë një numër tre-bitësh X, dhe ai në përgjigje aktivizon daljen numër (1<<X). Hyrjet e tjera të çipit përdoren për të shkallëzuar të gjithë konstrukcionin. Ky çip na nevojitet jo vetëm për të reduktuar numrin e pinëve të zënë të mikro-kontrollorit, por edhe për siguri – që aksidentalisht të mos ndizni më shumë LED se sa është e mundur dhe të mos digjni MK. Çipi është shumë i lirë, ia vlen të mbahet gjithmonë në kitin tuaj të shtëpisë.

CD74HC238 do të përgjigjet që të furnizojë tensionin në ankorën e LED të duhur. Në një multiplex të plotë, do të dërgonte tensionin në kolonë përmes P-MOSFET, por në këtë demo mund të bëhet direkt, pasi ai tërheq 20 mA, sipas vlerave maksimale absolute në fletën e të dhënave. Nga fleta e të dhënave CD74HC238 na nevojitet pinout dhe ky shënim:

Lexo fletët datashit 2: SPI në STM32; PWM, timerët dhe ndërprerjet në STM8
H = nivel i lartë tensioni, L = nivel i ulët tensioni, X – çdo gjë është në rregull

Lidhni E2 dhe E1 me tokë, E3, A0, A1 dhe A3 me pinët PD5, PC3, PC4 dhe PC5 STM8. Duke qenë se tabela më sipër përmban dhe nivele të ulëta dhe të larta, konfiguroni këta pinë si dalje push-pull.

PWM

PWM në timerin e dytë konfigurohet ashtu si në historinë e mëparshme, me dy përjashtime:

Së pari, na duhet të aktivizojmë ndërprerjen për Ngjarje e Azhurnimit (UEV), e cila do të thërrasë funksionin që kalon LED aktiv. Kjo bëhet duke ndryshuar bitin Aktivizimi i Ndërprerjes në regjistrin me emrin e dukshëm

Lexo fletët datashit 2: SPI në STM32; PWM, timerët dhe ndërprerjet në STM8
Regjistri i Aktivizimit të Ndërprerjeve

#define TIM2_IER   *(volatile uint8_t *)0x005303

//enable interrupt
TIM2_IER = 1;

Përjashtimi i dytë lidhet me një fenomen të multipleximit, siç është ghosting – ndriçimi parazitar i diodave. Në rastin tonë, ky ndriçim mund të shfaqet për shkak se timer-i, që shkakton ndërprerjen në UEV, vazhdon të ikë, dhe manipuluesi i ndërprerjes nuk arrin ta ndërron LED përpara se timer-i të fillojë të shkruajë diçka në daljet. Për të luftuar këtë, do të duhet të invertim logjikën (0 = ndriçim maksimal, 255 = asgjë nuk ndriçon) dhe të mos lejojmë vlerat ekstreme të ngjeshjes. Pra, duhet të arrijmë që pas UEV LED-at të fikën plotësisht për një cikël PWM.

Ndryshojmë polaritetin:

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

Evitojmë vendosjen e r, g dhe b në 255 dhe mos harrojmë t'i invertim kur i përdorim.

Ndërprerjet

Qëllimi i ndërprerjes është që në rrethana të caktuara, çipi ndalon ekzekutimin e programit kryesor dhe thërret një funksion të jashtëm. Ndërprerjet ndodhin për shkak të efekteve të jashtme ose të brendshme, përfshirë ato nga timer-i.

Kur krijuam për herë të parë projektin në ST Visual Develop, morëm përveç main.c një dritare me një skedar misterios stm8_interrupt_vector.c, që u përfshi automatikisht në projekt. Në këtë skedar, për çdo ndërprerje është lidhur një funksion NonHandledInterrupt. Ne duhet të lidhim funksionin tonë me ndërprerjen e duhur.

Në datasheet ekziston një tabelë e vektorëve të ndërprerjeve, ku gjejmë të nevojshmet:

Lexo fletët datashit 2: SPI në STM32; PWM, timerët dhe ndërprerjet në STM8
13 Riatdhesim/mbushje TIM2
14 Kapje/krahasim TIM2

Na duhet të ndryshojmë LED-in për UEV, kështu që na nevojitet ndërprerja nr. 13.

Përkatësisht, së pari, në skedarin stm8_interrupt_vector.c ndryshojmë emrin e funksionit që i përgjigjet ndërprerjes nr. 13 (IRQ13) në atë që kemi përzgjedhur:

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

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

#ifndef __MAIN_H
#define __MAIN_H

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

Dhe, në fund, të shkruajmë këtë funksion në main.c:

@far @interrupt void TIM2_Overflow (void)
{
 PD_ODR &= ~(1<<5); // fikim demultiplexorin
 PC_ODR = (cnt<<3); // shkruajmë në demultiplexorin vlerën e re
 PD_ODR |= (1<<5); // ndizim demultiplexorin

 TIM2_SR1 = 0; // rivendosim flamurin e Ndërprerjes së Pritur të Azhurnimit

 cnt++; 
 cnt &= 7; // lëvizim numëruesin e LED-it

 TIM2_CCR1L = ~colors[cnt][0]; // dërgojmë vlerat e invertuara në buffer
 TIM2_CCR2L = ~colors[cnt][1]; // për ciklin e ardhshëm të PWM
 TIM2_CCR3L = ~colors[cnt][2]; // 

 return;
}

Kemi mbetur vetëm të aktivizojmë ndërprerjet. Kjo bëhet me komandën assemblore rim – do ta kërkojmë në Manualin e Programimit:

//enable interrupts
_asm("rim");

Një komandë tjetër assemblore – sim – çon në fikjen e ndërprerjeve. Ato duhet të çohen përkohësisht gjatë shkrimit të vlerave të reja në 'memorinë vizuale', në mënyrë që një ndërprerje e papërshtatshme të mos prishë masën.

I gjithë kodi është në GitHub.

Lexo fletët datashit 2: SPI në STM32; PWM, timerët dhe ndërprerjet në STM8

Nëse kjo artikull është e dobishme për dikë, atëherë nuk e kam shkruar kot. Do të jem i lumtur për komentet dhe sugjerimet, do të përpiqem të përgjigjem për të gjitha.

Burimi: habr.com

Blini hostim të besueshëm për faqe interneti me mbrojtje DDoS, serverë VPS VDS 🔥 Blini hostim të besueshëm për faqe interneti me mbrojtje DDoS, serverë VPS VDS - ProHoster