ProHoster > Blog > lajme në internet > 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:
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:
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.
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:
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:
... 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ë:
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:
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:
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):
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:
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):
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:
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:
Hapni seksionin 25.3.3 me titullin vetë-shpjegues "Konfigurimi i SPI në modalitetin Master":
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):
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).
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):
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ë:
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:
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":
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":
<…>
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":
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:
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:
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:
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:
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.
(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":
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:
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).
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.
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.
Si parazgjedhje, STM8 funksionon në një frekuencë prej 2 MHz, kjo duhet të korrigjohet menjëherë.
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:
Meqenëse do të ekzekutojmë PWM dhe do të lidhim LED-et, le të shohim pikën:
Ç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
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:
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):
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ë).
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ë:
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:
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ë:
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:
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:
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:
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
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.
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:
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:
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.
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.