Si të konfiguroni telefonat inteligjentë dhe PC. Portali informativ

EasySTM32 - Portat e mikrokontrolluesit. STM32

E kuptoj që artikulli tashmë ka rezultuar mjaft i gjatë dhe të gjithë duan të shkruajnë shpejt një lloj programi që bën të paktën diçka të dobishme dhe ta ngarkojë atë në kontrollues, por ndodh që kontrollorët STM32 janë disi më të komplikuar se sa pjesët më të thjeshta të harduerit me tetë bit, kështu që tani do të flasim përsëri, siç shkruajnë disa lexues të paduruar, "jo për këtë".

Në këtë pjesë do të flasim për atë që duhet të operojmë më shpesh kur punojmë me kontrolluesin - për regjistrat e punës të bërthamës Cortex-M3, për mënyrat e funksionimit të tij dhe mënyrën se si ndizet kontrolluesi.

Pra, ka 13 regjistra në bërthamën Cortex-M3 Qëllimi i përgjithshëmR0..R12, regjistri i përdorur për të ruajtur treguesin e stivës është R13, regjistri i komunikimit - R14, numëruesi i komandës - R15 dhe 5 regjistra për qëllime të veçanta.

Regjistrat për qëllime të përgjithshme ndahen në regjistrat e ulët - R0..R7 Dhe regjistra të lartë - R8..R12. Dallimi midis tyre është se disa instruksione thumb-2 16-bit mund të funksionojnë vetëm me regjistra të ulët, por nuk mund të funksionojnë me regjistra të lartë.

Në fakt, ekzistojnë dy regjistra R13, jo një. I pari quhet MSP - treguesi kryesor i stivës, dhe e dyta PSP - treguesi i stivës së procesit. Megjithatë, vetëm një nga këto regjistra është në dispozicion në çdo kohë. Cili përcaktohet në një nga regjistrat për qëllime të veçanta. Pse është e nevojshme kjo? Kjo është bërë për të qenë në gjendje të mbroni sistemin operativ (po, nëse dëshironi, mund të instaloni një OS në këtë kontrollues) nga programet e shtrembëruara të aplikimit. MSP përdoret nga mbajtësit situata të jashtëzakonshme dhe të gjitha programet që përdorin një nivel ekzekutimi të privilegjuar (për shembull, kernel OS), dhe PSP përdoret nga programe që nuk kërkojnë një nivel ekzekutimi të privilegjuar (për shembull, programet e aplikimit nga i cili duam të mbrojmë bërthamën e OS). Treguesit e stivës duhet të jenë gjithmonë të lidhur me një kufi fjalësh 32-bitësh, d.m.th. dy bitet e tyre më pak të rëndësishme duhet të rivendosen gjithmonë në zero.

Regjistri R14 quhet LR (regjistri i lidhjes) - regjistri i lidhjeve dhe përdoret për të kujtuar adresën e kthimit kur thirrni nënprograme.

Regjistri R15 quhet PC (program counter) - numërues programi dhe përdoret për të ruajtur adresën e komandës që po ekzekutohet aktualisht.

Tani për regjistrat specialë.

Regjistri xPSR përmban flamuj për rezultatet e operacioneve aritmetike dhe logjike, gjendjen e ekzekutimit të programit dhe numrin e ndërprerjes që po përpunohet aktualisht. Ndonjëherë shkruhet për këtë regjistër shumësi. Kjo është bërë sepse tre pjesët e tij mund të aksesohen si regjistra të veçantë. Këta "nënregjistra" quhen: APSR - Regjistri i Statusit të Aplikimit(këtu ruhen flamujt), IPSR - Regjistri i Statusit të Ndërprerjes(përmban numrin e ndërprerjes që përpunohet) dhe EPSR - Regjistri i Statusit të Ekzekutimit. Struktura e plotë e regjistrit xPSR është paraqitur në figurën më poshtë.

Flamujt në regjistrin APSR janë standarde:

  1. N (flamur negativ) - rezultat negativ i operacionit
  2. Z (flamuri zero) - rezultati zero i operacionit
  3. C (mbart flamurin) - flamur mbaj/huazoj
  4. V (flamur i tejmbushur) - flamur i tejmbushur
  5. Q (flamur saturation) - flamur saturation

Regjistri PRIORITY MASK përdor vetëm bitin zero (PRIMASK), i cili, kur vendoset në një, çaktivizon të gjitha ndërprerjet me një përparësi të konfigurueshme. Pas ndezjes, biti PRIMASK rivendoset në zero - të gjitha ndërprerjet janë të aktivizuara.

Regjistri FAULT MASK përdor gjithashtu vetëm bitin zero (FAULTMASK), i cili, kur vendoset në një, çaktivizon të gjitha ndërprerjet dhe përjashtimet përveç ndërprerjes së pa maskuar (NMI). Pas ndezjes, biti FAULTMASK rivendoset në zero - të gjitha ndërprerjet janë të aktivizuara.

Regjistri BASEPRI përdoret për të çaktivizuar të gjitha ndërprerjet, vlera e përparësisë së të cilave është më e madhe ose e barabartë me atë të regjistruar në këtë regjistër. Këtu duhet thënë se sa më e ulët të jetë vlera, aq më i lartë është niveli i prioritetit. Regjistri BASEPRI përdor vetëm 8 bitet e poshtme.

Regjistri CONTROL përdoret për të kontrolluar një nga mënyrat e procesorit - modalitetin thread. Biti zero i këtij regjistri (nPRIV) përcakton nivelin e ekzekutimit (i privilegjuar - i privilegjuar, ose i paprivilegjuar - i paprivilegjuar), dhe biti i parë (SPSEL) përcakton treguesin e stivës që do të përdoret (MSP ose PSP). Dallimi midis niveleve të ekzekutimit të privilegjuar dhe të paprivilegjuar është se për nivelin e privilegjuar të gjitha zonat e memories dhe të gjitha udhëzimet e procesorit janë të disponueshme, ndërsa për nivelin e paprivilegjuar disa zona të memories janë të mbyllura (për shembull, regjistrat për qëllime të veçanta, përveç APSR, zona e sistemit) dhe , në përputhje me rrethanat, udhëzimet qasja në këto zona është e ndaluar. Përpjekja për të ekzekutuar komanda të paligjshme që tentojnë të hyjnë në zonat e memories private shkakton krijimin e një përjashtimi të dështimit.

Tani në lidhje me mënyrat e funksionimit të procesorit.

Procesori Cortex-M3 ka dy mënyra funksionimi: modaliteti i fijes (Thread) dhe modaliteti i mbajtësit (Handle). Modaliteti Handle përdoret për të trajtuar përjashtimet dhe Modaliteti Thread përdoret për të ekzekutuar të gjithë kodin tjetër. Kalimi nga një mënyrë në tjetrën ndodh automatikisht. Siç thamë tashmë kur u morëm me regjistrin CONTROL, në modalitetin Thread procesori mund të përdorë një nivel ekzekutimi të privilegjuar dhe të paprivilegjuar, dhe në modalitetin Handle - vetëm një të privilegjuar. Në mënyrë të ngjashme, modaliteti Thread mund të përdorë si stivën kryesore (MSP) ashtu edhe pirgun e proceseve (PSP), ndërsa modaliteti Handle mund të përdorë vetëm pirgun kryesor.

Është e rëndësishme të kuptohet se, për shembull, nëse kalojmë nga një nivel i privilegjuar në një nivel të paprivilegjuar në modalitetin Thread, do të humbasim aksesin në regjistrin CONTROL dhe do të mund të kalojmë vetëm në modalitetin Handle. Në modalitetin Handle, biti nPRIV i regjistrit CONTROL lexohet/shkruhet, por nuk ndikon në modalitetin aktual të ekzekutimit. Kjo ju lejon të ndryshoni nivelin e ekzekutimit që do të ketë programi kur procesori të dalë nga modaliteti mbajtës në modalitetin thread. Biti SPSEL në modalitetin Handle nuk mund të shkruhet dhe lexohet gjithmonë si zero dhe rikthehet automatikisht kur del nga modaliteti i mbajtësit në modalitetin e transmetimit. Të gjitha opsionet për kalimet ndërmjet mënyrave të ndryshme dhe niveleve të ekzekutimit janë ilustruar nga grafiku i drejtuar i paraqitur në figurën më poshtë:

Kontrolluesi gjithmonë fillon në oshilatorin e brendshëm, me një frekuencë prej 8 MHz. Nga mund ta merrni sinjalin e orës në të ardhmen, me sa duhet shumëzuar ose pjesëtuar, është konfiguruar në program. Nëse kjo nuk është bërë në program, atëherë mbyllni të paktën dhjetë kuarc të jashtëm, kontrolluesi do të vazhdojë të punojë nga oshilatori i brendshëm 8 MHz.

Në fillim, kontrolluesi analizon kombinimin e niveleve në dy këmbët e tij - BOOT0, BOOT1 dhe, në varësi të këtij kombinimi, fillon ngarkimin ose nga memoria flash, ose nga RAM, ose nga zona e sistemit memorie. Kjo bëhet duke përdorur mekanizmin e pseudonimit që kemi përshkruar tashmë. Në teori, shkarkimi fillon gjithmonë nga adresa zero, vetëm në varësi të
kombinimet në këmbët BOOT0, BOOT1 adresat e memories fillestare caktohen si pseudonime në një nga tre zonat: flash, RAM i integruar ose zona e sistemit. Në të djathtë është një pllakë që tregon se cila zonë është projektuar në adresat e memories fillestare në varësi të kombinimit të këmbëve BOOT0, BOOT1.

Në të njëjtën kohë, në zonën e sistemit prodhuesi është i mbrojtur program të veçantë(bootloader), i cili ju lejon të programoni memorien flash. Por më shumë për këtë më vonë.

Së pari, kontrolluesi lexon një fjalë 32-bit në adresën 0x00000000 dhe e vendos atë në regjistrin R13 (treguesi i stivës). Më pas, lexon një fjalë 32-bit në adresën 0x00000004 dhe e vendos atë në regjistrin R15 (numëruesi i programit). Veprimi i fundit shkakton një kalim në fillim të kodit të programit dhe më pas fillon ekzekutimi i programit.

Fjala në adresën 0x00000004 (adresa e fillimit të programit kryesor) quhet vektori i rivendosjes. Në përgjithësi, në kujtesën e kontrolluesit, pas treguesit të stivës në adresën 0x00000000, duke filluar nga adresa 0x00000004, duhet të ketë një tabelë të vektorëve të përjashtimit dhe ndërprerjes, vektori i parë në të cilin është vektori i rivendosjes, dhe vektorët e mbetur janë adresat e procedurat për trajtuesit e përjashtimeve dhe ndërprerjeve të ndryshme. Në programet më të thjeshta, nëse nuk keni ndërmend të trajtoni përjashtimet dhe ndërprerjet, të gjithë vektorët e tjerë përveç vektorit të rivendosjes mund të mungojnë. Unë do të doja të tërhiqja vëmendjen tuaj për faktin se në tabelën vektoriale janë adresat e fillimit të mbajtësve që tregohen, dhe jo komandat për të shkuar te këta mbajtës, si, për shembull, në majat 8-bit ose atmels.

Diten e mire! Sot do të mësojmë për GPIO! Dhe, para së gjithash, le të shohim se në cilat mënyra mund të funksionojnë portat I/O në STM32F10x. Dhe ka një det të këtyre mënyrave, domethënë:

  • Hyrja lundruese
  • Tërheqja e hyrjes
  • Input-pull-down
  • Analoge
  • Dalje të hapur-kullues
  • Push-tërheqje dalëse
  • Funksioni alternativ push-tërheq
  • Funksioni alternativ i kullimit të hapur

Dhe nëse sipas mendimit tonë, atëherë kur punoni në hyrje:

  • Hyr – Hi-Z
  • Hyrja - tërhiqe lart
  • Hyrja - tërhiqeni poshtë
  • Hyrja - analoge

Kur përdorim portin e daljes, kemi opsionet e mëposhtme:

  • Prodhimi - kolektor i hapur
  • Dalje - shtytje-tërheqje
  • Funksionet Alternative - Open Output Collector
  • Funksionet alternative - prodhimi shtytës-tërheqës

Nga rruga, këtu është dokumentacioni për STM32F103CB -

Fleta e të dhënave ka një tabelë mbresëlënëse që tregon se cilat funksione alternative ka një kunj i veçantë.

Këtu, për shembull, janë përfundimet PA9, PA10:

Në kolonë E paracaktuar ne shohim se çfarë funksionesh do të kryejnë këto kunja kur të konfigurohen për të funksionuar Funksioni alternativ. Kjo do të thotë, duke i vendosur këto kunja në përputhje me rrethanat, ato do të kthehen nga vetëm PA9 dhe PA10 në Rx Dhe Tx për USART1. Për çfarë është atëherë kolona? Rimarrë? Dhe kjo nuk është gjë tjetër veçse shumë veçori e dobishme rimaptimi i portit. Falë rihartës, Tx USARTA 'A, për shembull, mund të lëvizë nga pin PA9 në PB6. Shumë shpesh kjo veçori rezulton të jetë shumë e dobishme.

Epo, gjithçka duket se është pak a shumë e qartë me modalitetet, është koha t'i hedhim një sy regjistrave që kontrollojnë portat I/O.

Meqenëse sapo kemi diskutuar se në çfarë mënyrash mund të ekzistojnë kunjat STM32F10x, le të shohim menjëherë se si ato mund të përkthehen në të vërtetë modaliteti i dëshiruar. Dhe për këtë, ndahen dy regjistra - CRL dhe CRH. Në të parën konfigurohen kunjat nga 0 në 7, në të dytën përkatësisht nga 8 në 15. Regjistrat, siç e mbani mend, janë 32-bit. Kjo do të thotë, ka 32 bit për 8 kunja - kjo është 4 bit për pin. Hapni fletën e të dhënave dhe shikoni:

Për shembull, ne duhet të konfigurojmë këmbën PB5. Shkojmë në regjistrin GPIOB->CRL dhe vendosim bitet përkatëse siç kërkojmë (në foto është një regjistër CRL 32-bit). Për PB5 këto janë pjesët:

Pas tetë biteve, gjithçka mund të duket mjaft e ndërlikuar dhe disi e ngathët, por në fakt gjithçka zbatohet mjaft elegante =). Le të shohim se çfarë tjetër ka.

Regjistri i daljes GPIOx_ODR - i ngjan regjistrit PORTx në AVR. Çdo gjë që hyn në këtë regjistër përfundon menjëherë në botën e jashtme. Regjistri është 32-bit dhe ka vetëm 16 këmbë. Për çfarë mendoni se përdoren 16-të e mbetura? Gjithçka është shumë e thjeshtë, bitet e regjistrit 15 deri në 31 nuk përdoren fare)

Regjistri i hyrjes GPIOx_IDR është një analog i PINx në AVR. Struktura e saj është e ngjashme me strukturën e përmendur ODR. Çfarëdo që shfaqet në hyrjen e mikrokontrolluesit përfundon menjëherë në regjistrin e hyrjes IDR.

Dy regjistra të tjerë të dobishëm janë GPIOx_BSSR dhe GPIOx_BRR. Ato ju lejojnë të ndryshoni drejtpërdrejt vlerat e biteve në regjistrin ODR, pa përdorur maskat e zakonshme të biteve. Kjo do të thotë, unë dua, për shembull, të vendos bitin e pestë të ODR në një. Unë shkruaj një në bitin e pestë të GPIOx_BSSR, dhe kaq, qëllimi është arritur. Papritur doja të rivendosja bitin e pestë të ODR - një njësi prej 5 bitësh GPIOx_BRR dhe kaq.

Pra, ne shikuam regjistrat kryesorë, por, në fakt, në shembujt tanë do të bëjmë gjithçka ndryshe, duke përdorur Bibliotekën Standarde Periferike. Pra, le të shkojmë të gërmojmë nëpër bibliotekë. Skedarët janë përgjegjës për GPIO në SPL stm32f10x_gpio.h Dhe stm32f10x_gpio.c. I hapim të dyja dhe shohim shumë numra, shkronja, ikona, etj të pakuptueshme.

Në fakt, gjithçka është shumë e thjeshtë dhe e qartë. Struktura është përgjegjëse për konfigurimin e portit GPIO_InitTypeDef.

Struktura typedef (uint16_t GPIO_Pin; // Specifikon kunjat GPIO që do të konfigurohen. Ky parametër mund të jetë çdo vlerë e @ref GPIO_pins_define */ GPIOSpeed_TypeDef GPIO_Speed; // Përcakton shpejtësinë për kunjat e zgjedhura. Ky parametër mund të jetë një vlerë e @ref GPIOSpeed_TypeDef */ GPIOMode_TypeDef GPIO_Mode; // Përcakton mënyrën e funksionimit për kunjat e zgjedhura. Ky parametër mund të jetë një vlerë prej @ref GPIOMode_TypeDef */ ) GPIO_InitTypeDef;

Ne shohim se struktura ka tre fusha: GPIO_PIN, GPIO_Speed Dhe GPIO_Mode. Është e lehtë të merret me mend se e para është përgjegjëse për numrin e pinit të portit që duam të konfigurojmë, e dyta është për shpejtësinë e portit dhe e treta, në fakt, për mënyrën e funksionimit. Pra, për të personalizuar prodhimin, ne vetëm duhet të deklarojmë variabël tip strukturën dhe plotësoni fushat e saj vlerat e kërkuara. Të gjitha vlerat e mundshme të fushës janë aty - brenda stm32f10x_gpio.h. Për shembull,

numërimi typedef (GPIO_Mode_AIN = 0x0, GPIO_Mode_IN_FLOATING = 0x04, GPIO_Mode_IPD = 0x28, GPIO_Mode_IPU = 0x48, GPIO_Mode_Out_OD = 0x14 Mode_POOde_0, GPIO_Mode_IPD, AF_ OD = 0x1C , GPIO_Mode_AF_PP = 0x18 ) GPIOMode_TypeDef;

Të gjitha vlerat tashmë janë llogaritur nga krijuesit e SPL, kështu që për të konfiguruar çdo dalje për të punuar në modalitetin Push-tërheqje dalëse ju vetëm duhet të vendosni fushën në strukturën e duhur: GPIO_Mode = GPIO_Mode_Out_PP.

Epo, struktura është deklaruar, fushat janë plotësuar sipas nevojës, çfarë më pas? Në fund të fundit, ne sapo krijuam një ndryshore. Çfarë lidhje kanë regjistrat, mikrokontrolluesit dhe elektronika në përgjithësi? Le të futemi në dosje stm32f10x_gpio.c dhe ne gjejmë një re atje funksione të ndryshme për të punuar me STM32 GPIO. Merrni parasysh funksionin GPIO_Init()(Unë nuk do të jap kodin, gjithçka është në skedarin e bibliotekës). Pra, ky funksion lidh saktësisht strukturën tonë të krijuar dhe regjistrat specifikë të kontrolluesve. Kjo do të thotë, ne i kalojmë këtij funksioni një ndryshore, në përputhje me të cilën vendosen bitet e nevojshme të regjistrave të nevojshëm të mikrokontrolluesve. Gjithçka është shumë e thjeshtë, por jo më pak e shkëlqyer. Shikoni disa skedarë të tjerë të bibliotekës. Ka funksione për çdo rast) Nga rruga, është shumë i përshtatshëm - para funksionit ka një përshkrim të variablave që ai pranon dhe kthen, si dhe një përshkrim të asaj që duhet të bëjë ky funksion. Pra, nuk është e vështirë ta kuptosh, por duhet të jesh pak rrjedhshëm në anglisht. Edhe pse nuk mund të bëni pa të ;)

Le të bëjmë një pushim nga portat I/O për një moment dhe të diskutojmë një pikë mjaft delikate. Për të përdorur porte ose çdo pajisje tjetër periferike, DUHET të aktivizoni klocking. Si portet ashtu edhe pajisjet periferike fillimisht janë shkëputur nga kronometri, kështu që pa këtë veprim asgjë nuk do të fillojë. Programi do të përpilohet, por asgjë nuk do të funksionojë. Skedarët janë përgjegjës për kontrollimin në SPL stm32f10x_rcc.c Dhe stm32f10x_rcc.h. Mos harroni t'i shtoni ato në projekt.

Le të kalojmë te programimi. Siç është zakon, ne do të bëjmë që dioda të pulsojë) Për të kuptuar më mirë Bibliotekën Standarde Periferike, do të ndërlikojmë pak ndezjen e zakonshme të diodës - do ta zgjedhim butonin dhe nëse shtypet, dioda ndizet, përndryshe shkon jashtë. Ne nisim Keil, krijojmë një projekt, shtojmë të gjithë skedarët e nevojshëm, mos harroni për CMSIS. Nga SPL për këtë projekt do të na duhen 4 skedarë, të përmendur tashmë më lart. Krijimi i një projekti të ri është përshkruar në artikullin e mëparshëm kurs trajnimi. Ju gjithashtu mund të gjeni lidhje me bibliotekat atje)

Pra, kodi:

/**************************** gpio.c******************** **************/ //Lidhni të gjithë skedarët e nevojshëm#include "stm32f10x.h" #include "stm32f10x_rcc.h" #include "stm32f10x_gpio.h" //Këtu do të jetë i gjithë inicializimi i të gjitha pajisjeve periferike të përdorura void initAll() ( //Deklaroni një variabël porti të tipit GPIO_InitTypeDef Porta GPIO_InitTypeDef; //Ky është një funksion nga skedari stm32f10x_rcc.c, mundëson kontrollimin në GPIOA //GPIOA ulet në autobusin APB2 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); //Unë do të shkruaj për këtë funksion më poshtë GPIO_StructInit(& port); //Plotësoni fushat e strukturës me vlerat e kërkuara //Dalja e parë është hyrja për përpunimin e shtypjes së butonit - PA1 port.GPIO_Mode = GPIO_Mode_IPD; port.GPIO_Pin = GPIO_Pin_1; port.GPIO_Speed ​​= GPIO_Speed_2MHz; //Dhe ne kemi folur tashmë për këtë funksion //Le të shënojmë vetëm një nga parametrat - një tregues (!) në//struktura jonë GPIO_Init(GPIOA, & port) ; //Konfiguro gjilpërën në të cilën do të varet dioda – PA0 port.GPIO_Mode = GPIO_Mode_Out_PP; port.GPIO_Pin = GPIO_Pin_0; port.GPIO_Speed ​​= GPIO_Speed_2MHz; GPIO_Init(GPIOA, & port) ; ) /*******************************************************************/ int main() ( //Deklaroni një variabël për të ruajtur gjendjen e butonit butoni uint8_t Gjendja = 0 ; initAll() ; ndersa (1) ( //Duke përdorur një funksion nga SPL ne lexojmë nga bota e jashtme //gjendja e butonit buttonState = GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_1); nëse (buttonState == 1 ) ( GPIO_SetBits (GPIOA, GPIO_Pin_0) ; ) tjetër (GPIO_ResetBits (GPIOA, GPIO_Pin_0) ; ) ) /****************************Fundi i dosjes******************** **********/

Nga rruga, ndoshta dikush do t'i kushtojë vëmendje pranisë së kllapave ( ), pavarësisht vetëm një udhëzimi në trup nëse Dhe tjetër. Dhe kjo është tashmë një zakon) Rekomandohet shumë të shkruani kështu, veçanërisht kur zhvilloni projekte të mëdha. Kur shtoni/korrigjoni një program, një programues i pavëmendshëm mund të mos i kushtojë vëmendje mungesës së kllapave dhe të shtojë një udhëzim të dytë, i cili, siç e kuptoni, tashmë do të jetë në të gjithë bllokun nëse ose tjetër. E njëjta çështje me ciklet. Kur ka shumë njerëz që punojnë në një projekt, nuk ka garanci që dikush nuk do të jetë i pavëmendshëm, kështu që për të mos humbur minuta/orë në kërkimet e mëvonshme për një bllokim, unë rekomandoj që gjithmonë të vendosni këto kllapa) Edhe pse ndoshta dikush do të nuk pajtohem me këtë logjikë.

Shtypim F7, përpilojmë dhe tani programi ynë i parë për STM është gati. Duket se kodi është komentuar me shumë detaje, kështu që unë do të shpjegoj vetëm disa pika.

Funksioni GPIO_StructInit(&port)– merr si argument adresën e një ndryshoreje port.

Ky funksion plotëson fushat e strukturës që i kalohen si argument me vlerat e paracaktuara. Kjo nuk është e nevojshme, por për të shmangur ndonjë bllokim të paparashikueshëm, është më mirë ta thërrisni gjithmonë këtë funksion.

Dy funksione të tjera kemi përdorur:

  • GPIO_SetBits(GPIOA, GPIO_Pin_0);
  • GPIO_ResetBits (GPIOA, GPIO_Pin_0);

Epo, e keni marrë me mend se për çfarë janë ato 😉

Kështu që ne përfunduam shikimin e porteve I/O STM32. Në artikullin vijues do të njihemi me mjetet e korrigjimit të Keil.

Regjistrat kryesorë të portës I/O të mikrokontrolluesit STM32

Një port i referohet një grupi të caktuar me emër të këmbëve të mikrokontrolluesit. Në mikrokontrolluesit STM quhen GPIOA, GPIOB, GPIOC, etj. Portat I/O në mikrokontrolluesit STM32 zakonisht kanë 16 linja (këmbë). Një linjë i referohet një ose një tjetër këmbë të mikrokontrolluesit. Çdo linjë porti mund të konfigurohet në një mënyrë specifike dhe të kryejë funksionet e mëposhtme:

  • hyrje dixhitale;
  • dalje dixhitale;
  • hyrje me ndërprerje të jashtme;
  • Funksioni I/O i moduleve të tjera të mikrokontrolluesve.

Mund ta konfiguroni portën për mënyrën e dëshiruar të funksionimit duke përdorur regjistrat e mikrokontrolluesit. Këta regjistra mund të aksesohen drejtpërdrejt, ose mund të përdoren metoda të veçanta nga biblioteka periferike.

Le të shohim regjistrat kryesorë të nevojshëm për të punuar me portet I/O.

Regjistrat përgjegjës për konfigurimin e portit

Përpara se të filloni të punoni me një portë dalëse, duhet ta konfiguroni atë për t'iu përshtatur nevojave tuaja.

Regjistrat e konfigurimit janë përgjegjës për vendosjen ose konfigurimin e një porti. Në mikrokontrolluesit e familjeve STM32F302xx, STM32F303xx dhe STM32F313xx, këta janë regjistrat e mëposhtëm:

  • GPIOx_MODER;
  • GPIOx_OTYPER;
  • GPIOx_OSPEEDR;
  • GPIOx_PUPDR.

Regjistro GPIOx_MODER (ku x=A...F)

Kjo është 32 bit Regjistri është përgjegjës për mënyrën e funksionimit të linjës. Kërkon 2 bit për të konfiguruar një linjë specifike. Kombinimet e mëposhtme janë të mundshme:

  • 00 - linja është konfiguruar për hyrje;
  • 01 - dalje;
  • 10 - mënyra e funksionit alternativ;
  • 11 - modaliteti analog.

Regjistro GPIOx_TYPER (ku x=A...F)


Ky regjistër përdoret për të konfiguruar llojin e funksionimit të linjës; ai përdor 16 bit në funksion, 16 të mbeturit janë të rezervuar. Pranon vlerat e mëposhtme:

  • 0 - modaliteti shtytje-tërheqje;
  • 1 - kullimi i hapur

Regjistrohu GPIOx_PUPDR (ku x=A...F)


Regjistri i të dhënave është përgjegjës për shtrëngimin. Pranon vlerat e mëposhtme:

  • 00 - pa ashensor
  • 01 - tërheqje në furnizimin me energji elektrike plus
  • 10 - tërheqje në tokë

Regjistrohu GPIOx_SPEEDR (ku x=A...F)


Regjistri i përcaktimit të shpejtësisë së linjës.

  • 00 - 2 MHz;
  • 01 - 10 MHz;
  • 10 - 50MHz.

Regjistri i daljes (regjistri i daljes) GPIOx_ODR (ku x=A…F) – regjistri i të dhënave dalëse


Ky regjistër përdoret për të nxjerrë të dhëna në port. Kur shkruani vlera të caktuara në këtë regjistër një vlerë e ngjashme vendoset në vijë (këmbë). Meqenëse kemi 16 rreshta dhe regjistri ka 32 bit, përdoren vetëm 16 bitët e parë (të poshtëm).

Regjistri i hyrjes (statusi i portit ose regjistri i të dhënave hyrëse) GPIOx_IDR (ku x=A…F) – regjistri i të dhënave hyrëse


Ky regjistër është vetëm për lexim. Një lloj treguesi i statusit të portit. Analog i PINx në mikrokontrolluesit e serisë AVR.

Regjistri i grupit të biteve të daljes GPIOx_BSRR


Ky regjistër vendos pak nga pak gjendjen e pinit në portën e daljes.

Më shumë informacion për të gjithë regjistrat e një mikrokontrollues specifik mund të gjenden në manualin e referencës.pdf i cili mund të shkarkohet nga faqja zyrtare e internetit www.st.com

Konfigurimi i një porti mikrokontrollues duke përdorur bibliotekën

Porti gjithashtu mund të konfigurohet duke përdorur një bibliotekë të veçantë që përmban metoda të ndryshme për punën me regjistrat I/O, dhe deklarohen variabla të veçanta. Kjo bibliotekë e liron programuesin që të llogarisë "manualisht" se cila vlerë duhet të shkruhet në një regjistër të caktuar.

Le të shohim një shembull kodi që ndez një LED

#include "stm32f30x.h" // Kreu i pajisjes #include "stm32f30x_rcc.h" #include "stm32f30x_gpio.h" void init_led(void) ( RCC_APB2PeriphClockCmd (GPIOE,ENABLE;GPIOE,ENABLETypeofittureGPIOInTrupeFit); ture.GPIO_Spe ed = GPIO_Speed_2MHz; // Cakto shpejtësinë maksimale GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT; //Modaliteti i daljes GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8; //Tregoni pinin në të cilin është bashkangjitur LED GPIO_Init(GPIOE,GPIO_InitStructure) (); GPIO_SetBits (GPIOE,GPIO_Pin_8); //Vendosni pinin në një gjendje të lartë while(1) ( _NOP(); ) )

Për të kursyer energji, të gjitha pajisjet periferike të mikrokontrolluesve janë çaktivizuar. Për të "aktivizuar" një ose një pajisje tjetër periferike, së pari duhet të aplikoni sinjalet e orës në të.

Ne jemi duke punuar me portën GPIOE, kështu që duhet të aktivizojmë klockimin duke përdorur metodën

RCC_APB2PeriphClockCmd(uint32_t RCC_APB2Periph, FunctionalState NewState);

E cila merr dy vlera: e para është vetë porti aktual që duhet të aktivizojmë dhe e dyta është gjendja të këtij porti, ndezur ose fikur.

Kur ndodh një ngjarje, kontrolluesi i ndërprerjeve ndërpret automatikisht ekzekutimin e programit kryesor dhe thërret funksionin përkatës të trajtimit të ndërprerjeve. Pas daljes së funksionit të mbajtësit të ndërprerjeve, programi vazhdon ekzekutimin nga pika ku ka ndodhur ndërprerja. Gjithçka ndodh automatikisht (nëse NVIC është konfiguruar saktë, por më shumë për këtë më poshtë).

Nga vetë emri është e qartë se kontrolluesi NVIC mbështet folenë dhe prioritetet e ndërprerjeve. Çdo ndërprerje i caktohet përparësia e vet kur konfigurohet NVIC. Nëse ndodh një ndërprerje me prioritet të lartë ndërsa përpunohet një ndërprerje me prioritet të ulët, ai nga ana tjetër do të ndërpresë mbajtësin e ndërprerjeve me prioritet të ulët.

Si punon?

Ky postim nuk pretendon të jetë plotësisht i plotë; unë ju këshilloj të studioni seksionin e ndërprerjeve në Manualin e Referencës Teknike Cortex™-M3. Meqenëse kjo pjesë e bërthamës nuk ka pësuar ndryshime, përshkrimi i saj është dhënë në rishikimin e parë të r1p1 për bërthamën Cortex-M3.
Hyrja dhe dalja nga një ndërprerje
Kur inicohet një ndërprerje, NVIC ndërron bërthamën në modalitetin e përpunimit të ndërprerjes. Pas kalimit në modalitetin e përpunimit të ndërprerjes, regjistrat e kernelit vendosen në stek. Direkt kur vlerat e regjistrit shkruhen në pirg, merret adresa fillestare e funksionit të trajtimit të ndërprerjeve.

Regjistri i statusit të programit ( Regjistri i statusit të programit (PSR)), numëruesi i programit ( Numëruesi i programit (PC)) dhe regjistri i komunikimit ( Lidhja e regjistrimit (LR)). Një përshkrim i regjistrave të kernelit jepet në Udhëzuesin e Përgjithshëm të Përdoruesit Cortex-M4. Falë kësaj, mbahet mend gjendja në të cilën ishte bërthama përpara se të kalonte në modalitetin e përpunimit të ndërprerjes.

Regjistrat R0 - R3 dhe R12 ruhen gjithashtu. Këta regjistra përdoren në instruksione për kalimin e parametrave, kështu që shtyrja e tyre në pirg e bën të mundur përdorimin e tyre në një funksion shërbimi të ndërprerjes dhe R12 shpesh vepron si një regjistër funksionues i programit.

Pas përfundimit të përpunimit të ndërprerjeve, të gjitha veprimet do të kryhen në rend i kundërt: Përmbajtja e pirgut hapet dhe, paralelisht, merret adresa e kthimit.

Që nga momenti i inicimit të ndërprerjes deri në ekzekutimin e komandës së parë të mbajtësit të ndërprerjeve, ndërprerja zgjat 12 cikle ore, e njëjta kohë kërkohet për të rifilluar programin kryesor pas përfundimit të përpunimit të ndërprerjeve.

Ndërprerja e foleve
Siç u përmend më lart, NVIC mbështet ndërprerje me prioritete të ndryshme, të cilat mund të ndërpresin njëra-tjetrën. Në këtë rast, mund të lindin situata të ndryshme, përpunimi i të cilave optimizohet ndryshe.

1. Pezulloni ndërprerjen me prioritet të ulët
Në këtë situatë, përpunimi i ndërprerjes me prioritet të ulët ndalet. Gjatë 12 cikleve të ardhshme, një grup i ri të dhënash ruhet në pirg dhe fillon përpunimi i ndërprerjeve me prioritet të lartë. Pasi të përpunohet, përmbajtja e pirgut shfaqet automatikisht dhe rifillon përpunimi i ndërprerjeve me prioritet të ulët.
Nuk ka dallime të mëdha nga ndërprerja e programit kryesor.

2. Përpunimi me ndërprerje të vazhdueshme
Kjo situatë mund të lindë në dy raste: nëse dy ndërprerje kanë të njëjtin prioritet dhe ndodhin njëkohësisht, nëse ndodh një ndërprerje me prioritet të ulët ndërsa përpunohet një ndërprerje me prioritet të lartë.
Në këtë rast, nuk kryhen operacione të ndërmjetme në pirg. Vetëm adresa e mbajtësit të ndërprerjeve me prioritet të ulët ngarkohet dhe ndodh kalimi në ekzekutimin e tij. Eliminimi i operacioneve të stivës kursen 6 cikle orësh. Kalimi në ndërprerjen tjetër ndodh jo në 12 cikle orësh, por vetëm në 6.

3. Vonesa e ndërprerjes me prioritet të lartë
Situata lind nëse ndodh një ndërprerje me prioritet të lartë gjatë kalimit në përpunimin e një ndërprerje me prioritet të ulët (gjatë atyre 12 cikleve të orës). Në këtë rast, kalimi në një ndërprerje me prioritet të lartë do të ndodhë të paktën 6 cikle ore nga momenti që ndodh (koha e nevojshme për të ngarkuar adresën e mbajtësit të ndërprerjeve dhe për të kaluar në të). Kthimi në prioritet të ulët është përshkruar tashmë më lart.

Ndërprisni prioritetet
Përveç vendosjes së thjeshtë të prioritetit të ndërprerjes, NVIC zbaton aftësinë për të grupuar prioritetet.
Ndërprerje në një grup me më shumë se prioritet i lartë mbajtësit e ndërprerjeve të grupeve me prioritet më të ulët mund të ndërpriten. ndërprerjet nga i njëjti grup, por me prioritete të ndryshme brenda grupit, nuk mund të ndërpresin njëri-tjetrin. Përparësia brenda një grupi përcakton vetëm rendin në të cilin trajtuesi thirret kur të dy ngjarjet janë aktivizuar.

Vlera e prioritetit të ndërprerjes vendoset në regjistra Regjistrat e Prioritetit të Ndërprerjes(Shih Udhëzuesin e Përgjithshëm të Përdoruesit Cortex-M4). Në këtë rast, disa bit janë përgjegjës për prioritetin e grupit në të cilin ndodhet ndërprerja, dhe disa janë përgjegjës për prioritetin brenda grupit.
Vendosja e shpërndarjes së biteve në prioritet grupi ose prioritet brenda një grupi kryhet duke përdorur regjistrin Regjistri i kontrollit të ndërprerjes dhe rivendosjes së aplikacionit(KUJDES!!! shih Udhëzuesin e Përgjithshëm të Përdoruesit Cortex-M4).

Siç mund ta keni vënë re, Udhëzuesi i Përgjithshëm i Përdoruesit Cortex-M4 thotë se cilësimet e përparësisë dhe grupimet e përparësisë janë specifike për zbatimin. zbatimi i përcaktuar.
Por ajo që vijon nuk është një gjë shumë e këndshme. Nuk ka pothuajse asnjë informacion në lidhje me NVIC në STM32F407 MK. Por ka një lidhje me një dokument të veçantë. Për të kuptuar zbatimin e NVIC në STM32, do t'ju duhet të lexoni një dokument tjetër -. Në përgjithësi, ju këshilloj të studioni me kujdes këtë dokument dhe për të gjitha çështjet e tjera, ai përshkruan funksionimin e kernelit më në detaje sesa në dokumentacionin nga ARM.
Në të tashmë mund të gjeni:

Një nivel prioriteti i programueshëm 0-15 për çdo ndërprerje. Një nivel më i lartë korrespondon me a
prioritet më i ulët, kështu që niveli 0 është prioriteti më i lartë i ndërprerjes

Nga 8 bitet prioritare të mundshme përdoren vetëm 4. Por kjo është mjaft e mjaftueshme për shumicën e detyrave.
Ndërpreni maskimin
Le të supozojmë se kemi për detyrë të lëshojmë një mjet lëshues kur shtypet butoni i kuq, por vetëm nëse çelësi është i kthyer.
Nuk ka absolutisht asnjë pikë për të gjeneruar një ndërprerje për rrotullimin e çelësit. Por do të na duhet një ndërprerje për të shtypur butonin e kuq. Për të aktivizuar/çaktivizuar vektorë të ndryshëm të ndërprerjeve, ekziston maskimi i ndërprerjeve.
Maskimi i ndërprerjeve bëhet duke përdorur regjistra Interrupt Set-aktivizo Regjistrat .
Nëse një ndërprerje është e maskuar, kjo nuk do të thotë se pajisja periferike nuk po gjeneron ngjarje! Vetëm se NVIC nuk thërret një mbajtës për këtë ngjarje.
Tabela e vektorit të ndërprerjeve
Të gjitha ndërprerjet e mundshme të mbështetura nga NVIC regjistrohen në tabelën e vektorit të ndërprerjeve. Në thelb, tabela e vektorit të ndërprerjeve nuk është gjë tjetër veçse një listë e adresave të funksioneve të mbajtësit të ndërprerjeve. Numri në listë korrespondon me numrin e ndërprerjes.
Krijoni një tabelë vektoriale dhe vendoseni në vendin e duhur
Në mënyrë që tabela e vektorëve me adresat e sakta funksionet e mbajtësve të ndërprerjeve ishin vendosur në fillim të memories flash të MK, ne do të krijojmë dhe lidhim skedarin startup.c me projektin.

Përmbajtja e skedarit

// Aktivizo shtesat IAR për këtë skedar burimor. #pragma language=extended #pragma segment="CSTACK" // Deklarata përpara e trajtuesve të gabimeve të paracaktuar. void ResetISR(void); zbrazëti statike NmiSR(void); zbrazëti statike FaultISR(void); static void IntDefaultHandler(void); // Pika hyrëse për kodin e nisjes së aplikacionit. extern void __iar_program_start(void); boshllëk i jashtëm EXTI_Line0_IntHandler(void); boshllëk i jashtëm EXTI_Line6_IntHandler(void); // Një bashkim që përshkruan hyrjet e tabelës vektoriale. Bashkimi është i nevojshëm // pasi hyrja e parë është treguesi i stivës dhe pjesa tjetër janë tregues të funksionit //. typedef union ( void (*pfnHandler)(void); void * ulPtr; ) uVectorEntry; // Tabela vektoriale. Vini re se konstruksionet e duhura duhet të vendosen në këtë për // të siguruar që të përfundojë në adresën fizike 0x0000.0000. __root const uVectorEntry __vector_table @ ".intvec" = ( ( .ulPtr = __sfe("CSTACK")), // Treguesi fillestar i stivës ResetISR, // Trajtuesi i rivendosjes NmiSR, // Trajtuesi NMI FaultISR, // Faji i fortë Handler IntodeFaulthandler, // MPU FAULT HANDLER IntDEFULTHANDER, // Bus Fault Handler IntDEFULTHANDER, // USAGE FAULT HANDLENER INTDEFULTHANDER, // DSERVEDDD Intodefaulthandler, //Intodefaulthandler, //Reserved, //Reserved shërbeu Intodefaultler, // Svcall Handler IntefaulTaander, ( kalon nëpër Linja EXTI IntDefaultHandler, // Zgjimi RTC përmes linjës EXTI IntDefaultHandler, // FLASH IntDefaultHandler, // RCC EXTI_Line0_IntHandler, // EXTI Line0 IntDefaultHandler, // EXTI Line1 IntDefaultHandler, // EXTI Line1 IntDefault, EXT/ TI Line3 IntDefaultHandler, // EXTI Line4 IntDefaultHandler, // DMA1 Stream 0 IntDefaultHandler, // DMA1 Stream 1 IntDefaultHandler, // DMA1 Stream 2 IntDefaultHandler, // DMA1 Stream 3 IntDefaultHandler, // DMA1 Stream 1 IntDefaultHandler, // DMA1 Stream 2 Default Handler, // DMA1 Stream 6 IntDefaultHandler, // ADC1, ADC2 dhe ADC3s IntDefaultHandler, // CAN1 TX IntDefaultHandler, // CAN1 RX0 IntDefaultHandler, // CAN1 RX1 IntDefaultHandler, // CANLine SCE dhe IntDefaultHandler , // TIM1 Break dhe TIM9 IntDefaultHandler, ( Gabim I2C1 IntDefaultHandler, // I2C2 Event IntDefaultHandler, // I2C2 Error IntDefaultHandler, // SPI1 IntDefaultHandler, // SPI2 IntDefaultHandler, // USART1 IntDefaultHandler, // USART2 IntDefaultHandler, // USART2 IntDefaultHandler3 Fault Handler, // Alarmi RTC ( A dhe B) përmes EXTI Line IntDefaultHandler, // USB OTG FS Wakeup përmes linjës EXTI IntDefaultHandler, // TIM8 Break dhe TIM12 IntDefaultHandler, // TIM8 Update dhe TIM13 IntDefaultHandler, // TIM8 TimDefaultHandler, // TIM8 Trigger and Commutation Krahaso IntDefaultHandler, // DMA1 Stream7 IntDefaultHandler, // FSMC IntDefaultHandler, // SDIO IntDefaultHandler, // TIM5 IntDefaultHandler, // SPI3 IntDefaultHandler, // UART4 IntDefaultHandler, // UART4 IntDefaultHandler, // UART4 IntDefaultHandler &2 gabime nën ekzekutim IntDef aultHandler, / / TIM7 IntDEFAULTHANDER, // DMA2 Stream 0 Intdefaulthaner, // DMA2 Stream 1 IntDEFULTHANDER, // DMA2 Stream 2 Intdefaulthaner, // DMA2 Stream 3 IntdefaultHandler , // DMA2 Stream IntDEFULTHANDER, // DMA2 Stream 2 Intdefaulthaner Zgjimi Ethernet përmes Exti Line IntDefaultHandler, // CAN2 TX IntDefaultHandler, // CAN2 RX0 IntDefaultHandler, // CAN2 RX1 IntDefaultHandler, // CAN2 SCE IntDefaultHandler, // USB OTG FS IntDefaultHandlert, //Dam St. 6 IntDefaultHandler, // DMA2 Stream 7 IntDefault Handler, // USART6 IntDefaultHandler, // I2C3 Wat IntDefaultHandler, // I2C3 error IntDefaultHandler, // USB OTG HS End Point 1 Out IntDefaultHandler, // USB OTG HS IntDefaultHandler dhe End përmes EXTI IntDefaultHandler, // USB OTG HS IntDefaultHandler, // DCMI IntDefaultHandler, // CRYP kripto IntDefaultHandler, // Hash dhe Rng IntDefaultHandler, // FPU ); // Ky është kodi që thirret kur procesori fillon ekzekutimin për herë të parë // pas një ngjarje të rivendosjes. Kryhet vetëm grupi absolutisht i nevojshëm, // pas së cilës thirret rutina e hyrjes () e furnizuar me aplikacionin. Çdo veprim fantastik // (siç është marrja e vendimeve bazuar në regjistrin e shkakut të rivendosjes dhe // rivendosja e biteve në atë regjistër) lihen vetëm në duart e aplikuesit //

Përdorimi
@ ".intvec" Vendos tabelën __vector_në fillim të seksionit të deklaruar në skedarin lidhës. Vetë skedari mund të shihet këtu:

Vetë seksioni është specifikuar në fillim ROM memorie. Adresat mund të shihen (një dokument që përshkruan adresimin e memories flash STM32):

Kombinimi i Direktivës IAR dhe Funksionit Special të IAR:
#pragma segment="CSTACK" __sfe("CSTACK") Shkruan një tregues në krye të pirgut në fillim të blicit.

Tabela në vetvete është e mbushur me adresa funksionesh që zbatojnë një lak të përjetshëm. Një përjashtim bëhet vetëm për funksionet që na interesojnë:
boshllëk i jashtëm EXTI_Line0_IntHandler(void); boshllëk i jashtëm EXTI_Line6_IntHandler(void);
Në funksionin e thirrur në fillim, thjesht bëhet një kalim në
extern void __iar_program_start(void); Ky funksion është main(). Vetë simboli mund të ripërcaktohet nëse dëshironi:

Le të shkojmë te skedari kryesor
Së pari, le të shkruajmë dhe ripërcaktojmë të gjitha adresat dhe fushat e biteve që na duhen.

Listimi

//Përkufizimet për regjistrin SCB_AIRCR #define SCB_AIRCR (*(i panënshkruar i paqëndrueshëm i gjatë*)0xE000ED0C) //akses në SCB_AIRCR #define SCB_AIRCR_GROUP22 0x05FA0500 //Ndrysho të dhënat e prioritetit të regjistrimit RBHde1// B1_ENR (*(e paqëndrueshme e gjatë *) (0x40023830)) //qasja në RCC_AHB1ENR reg #define RCC_AHB1_ENR_GPIOA 0x1 //GPIOA bitfield #define RCC_AHB1_ENR_GPIOC 0x4 //GPIOC bitfield #define RCC_AHB1_ENR_GPIOA /Përkufizimet për regjistrin RCC_APB2_ENR # define RCC_APB2_ENR (*(i pa nënshkruar i gjatë i paqëndrueshëm * )(0x40023844)) //akses në RCC_APB2ENR reg #define RCC_APB2_ENR_SYSCFG 0x4000 //SYSCFG bitfield //Përkufizime për regjistrat GPIO MODE #define GPIOA_MODER (*0x0 volum) te GPIOA_MODER reg #define GPIOC_ MODER (*(i gjatë i paqëndrueshëm i panënshkruar*)(0x40020800)) //hyn në reg GPIOC_MODER #define GPIOD_MODER (*(i paqëndrueshëm i gjatë*)(0x40020C00)) //qasja në regjistrin GPIOD_MODER GPIODE reg //GPIODE #përkufizimi i GPIOD * (i gjatë i paqëndrueshëm i panënshkruar*) (0x40020C14)) //hyn në reg GPIOD_MODER #define GPIO_ODR_13PIN 0x2000 #define GPIO_ODR_14PIN 0x4000 //Fushat bit definicionet #define bit-moderi GPIO_MODER_0IN 0x0 //Pin 0 modaliteti i hyrjes # define GPIO_MODER_6BITS 0x300 //Pin 6 bit modaliteti #define GPIO_MODER_6IN 0x000 //Modaliteti i hyrjes Pin 6 #define GPIO_MODER_13BITS 0xC000000 //Pin 13 bit modaliteti #define GPIO_MODER_6IN 0x000 //Pin 6 modaliteti i daljes #define GPIO_MODER _14BITS 0x30000000 //Pin 14 bit modaliteti #define GPIO_MODER_14OUT 0x10000000 //Pin 14 modaliteti i daljes //Përkufizimi i regjistrit GPIOC_PUPDR #define GPIOC_PUPDR (*(i gjatë i paqëndrueshëm i panënshkruar*) (0x4002080C)) //qasja në GPIOC_0000DrGPIOC6/PUx3 /PC6 bitfield #define GPIOC_PUPDR_6 PU 0x1000 / /PC6 pull-up aktivizon //SYSCFG_EXTIx regjistron përkufizimet #define SYSCFG_EXTICR1 (*(i panënshkruar i gjatë i paqëndrueshëm*)0x40013808) //SYSCFG_EXTICR1 hyn #define SYSCFG_EXTICR_EXTICR1_0BITSG_0BITSGde _0PA 0x0 //EXTI 0 - porta A #define SYSCFG_EXTICR2/ gjobë EXTI_IMR (*(i gjatë i paqëndrueshëm i panënshkruar*) 0x40013C 00) / /EXTI_IMR reg acces #define EXTI_LINE0 0x1 //LINE 0 definition #define EXTI_LINE6 0x40 //LINE 6 define #define EXTI_RTSR (*(unsigned volatile EXTI_RTSR) 0x0800 EXTI_TI_R FTSR (*( unsigned volatile long* )0x40013C0C) //EXTI_FTSR reg acces #define EXTI_PR (*(unsigned volatile long*)0x40013C14) //EXTI_PR reg acces //NVIC regjistrat dhe përkufizimet e biteve (define*ISERE0_REX) 0E100) //NVIC_ISER0 reg acces #define NVIC_ISER0_6VECT 0x40 //vect 6 definition #define NVIC_ISER0_23VECT 0x800000 //vect 30 definition #define NVIC_ISER0_6VECT 0x40 //vect 6 Definition ed volatile char*)(NVIC_IPR0_ADD + 23) ) #define NVIC_IPR6_REG ( *(karakteristikë e paqëndrueshme e panënshkruar*)(NVIC_IPR0_ADD + 6))

Ju lutemi vini re se vlerat e regjistrave specialë MK deklarohen si i paqëndrueshëm. Kjo është e nevojshme në mënyrë që përpiluesi të mos përpiqet të optimizojë operacionet që i qasen ato, pasi këto nuk janë vetëm vendndodhje të memories dhe vlerat e tyre mund të ndryshojnë pa pjesëmarrjen e kernelit.

Vendosja e grupimit prioritar
Para së gjithash, ia vlen të vendosni grupimin e përparësisë së ndërprerjes: SCB_AIRCR = SCB_AIRCR_GROUP22; .Ky veprim duhet të kryhet vetëm një herë. Në projektet komplekse që përdorin biblioteka të palëve të treta ia vlen të kontrollohen ky fakt. Ndryshimi i ndarjes së prioriteteve në grupe mund të çojë në funksionimi i gabuar firmware.
Mundësimi i klockimit të pajisjeve periferike të përdorura
Më lejoni t'ju kujtoj se përpara se të filloni të punoni me njësitë periferike, duhet të aktivizoni rrahjen e tyre:
//Aktivizo SYSCFG , portat GPIO A dhe D të klockimit RCC_AHB1_ENR |= RCC_AHB1_ENR_GPIOA|RCC_AHB1_ENR_GPIOC|RCC_AHB1_ENR_GPIOD; RCC_APB2_ENR |= RCC_APB2_ENR_SYSCFG;
Ju nuk mund të punoni me SYSCFG menjëherë; duhet të prisni disa cikle ore. Por ne nuk do ta bëjmë. Le të fillojmë të inicializojmë GPIO.
Inicializimi i GPIO
LED janë inicializuar në të njëjtën mënyrë si herën e kaluar:
//Inicializimi LED3 dhe LED5 GPIOD_MODER = (GPIOD_MODER & (~GPIO_MODER_13BITS)) | GPIO_MODER_13 OUT; GPIOD_MODER = (GPIOD_MODER & (~GPIO_MODER_14BITS)) | GPIO_MODER_14 OUT;
Butoni PA0 dhe pin PC7 inicializohen si hyrje:
//Inicializimi i kunjave PA0 dhe PC6 GPIOA_MODER = (GPIOA_MODER & (~GPIO_MODER_0BITS)) | GPIO_MODER_0IN; GPIOC_MODER = (GPIOC_MODER & (~GPIO_MODER_6BITS)) | GPIO_MODER_6IN;
Por për pin PC6 duhet të ndizni furnizimin me energji elektrike. Tërheqja aktivizohet duke përdorur regjistrin GPIOC_PUPDR:
//Aktivizo tërheqjen e PC6 GPIOC_PUPDR = (GPIOC_PUPDR & (~GPIOC_PUPDR_7BITS)) | GPIOC_PUPDR_6PU;
Vendosja e EXTI
Dhe kështu, duhet të konfiguroni parametrat e mëposhtëm - aktivizoni ndërprerjet për linjat 0 dhe 6, për ndërprerjen e linjës 0 në një skaj në rritje, për rreshtin 6 - ndërprerjen në një skaj në rënie:
//Konfiguro EXTI EXTI_RTSR |= EXTI_LINE0; EXTI_FTSR |= EXTI_LINE6; EXTI_IMR = EXTI_LINE0|EXTI_LINE6;
E tëra që mbetet është të konfiguroni kunjat e të cilave porte janë të lidhura me linjën EXTI (një zgjidhje e çuditshme, për shembull MCU-të stellaris mund të gjenerojnë një ndërprerje me çdo kombinim të kunjave, kjo është më e vështirë për STM32):
//EXTI në lidhjen e portit SYSCFG_EXTICR1 = (SYSCFG_EXTICR1&(~SYSCFG_EXTICR1_0BITS)) | SYSCFG_EXTICR1_0PA; SYSCFG_EXTICR2 = (SYSCFG_EXTICR2&(~SYSCFG_EXTICR2_6BITS)) | SYSCFG_EXTICR2_6PC;
Konfigurimi i NVIC
Gjithçka që mbetet është të konfiguroni prioritetet e ndërprerjeve dhe t'i maskoni ato për të filluar përpunimin. Ju lutemi vini re se regjistrohet NVIC_IPR janë të disponueshme për akses në bajt, gjë që thjeshton shumë aksesin vetëm në bajtët e nevojshëm prioritar të vektorëve individualë të ndërprerjeve. Mjafton vetëm të bëhet një zhvendosje nga vlera e numrit të vektorit të ndërprerjes (shiko listën e përkufizimeve). Le të kujtojmë edhe një herë se Linja EXTI 0 është numri 6 në tabelën vektoriale, dhe rreshti EXTI 5_9 është numri 23. Për STM32, vetëm 4 bitet e përparësisë më të lartë janë të rëndësishme:
//Vendosja e prioritetit të ndërprerjeve NVIC_IPR6_REG = 0xF0; NVIC_IPR23_REG = 0x0;
Për demonstrim, prioritetet vendosen ndryshe.
Tani mund të aktivizoni ndërprerjet:
//Aktivizo ndërprerjet NVIC_ISER0_REG |= NVIC_ISER0_6VECT | NVIC_ISER0_23VECT;
Tani e tutje, shtypja e butonit dhe qarku i shkurtër i PC6 dhe GND do të çojë në thirrjen e funksioneve të mbajtësit të ndërprerjeve EXTI_Line0_IntHandler dhe EXTI_Line6_IntHandler, përkatësisht.
Ndërpreni trajtimin
Në funksionet e trajtimit të ndërprerjeve, ndërprerja duhet së pari të pastrohet, pas së cilës LED-të mund të ndizen. Për të demonstruar prioritetet e ndërprerjes, një lak i përjetshëm i është shtuar një prej trajtuesve. Nëse përparësia e një ndërprerjeje me një lak të përjetshëm është më e ulët se përparësia e të dytit, atëherë nuk mund të thirret. Përndryshe, do të jetë në gjendje të ndërpresë të parën. Unë sugjeroj që të provoni vetë vlera të ndryshme të përparësisë së ndërprerjes dhe të shihni qartë se çfarë çon kjo ( KUJDES - mos harroni për grupet e ndërprerjes!).
i pavlefshëm EXTI_Line0_IntHandler(void) ( //Pastro ndërprerjen EXTI_PR = EXTI_LINE0; //Ndiz LED 3 GPIOD_ODR |= GPIO_ODR_13PIN; ) i pavlefshëm EXTI_Line6_IntHandler(void) ( //TIOD_4 = ndërprerje LED EXTI_4 ODR |= GPIO_ODR_14P IN; ndërsa (1); )

Në vend të një përfundimi

Për çdo rast, unë do të jap një listë të plotë të programit që rezulton.

Listimi

//Përkufizimet për regjistrin SCB_AIRCR #define SCB_AIRCR (*(i panënshkruar i paqëndrueshëm i gjatë*)0xE000ED0C) //akses në SCB_AIRCR #define SCB_AIRCR_GROUP22 0x05FA0500 //Ndrysho të dhënat e prioritetit të regjistrimit RBHde1// B1_ENR (*(e paqëndrueshme e gjatë *) (0x40023830)) //qasja në RCC_AHB1ENR reg #define RCC_AHB1_ENR_GPIOA 0x1 //GPIOA bitfield #define RCC_AHB1_ENR_GPIOC 0x4 //GPIOC bitfield #define RCC_AHB1_ENR_GPIOA /Përkufizimet për regjistrin RCC_APB2_ENR # define RCC_APB2_ENR (*(i pa nënshkruar i gjatë i paqëndrueshëm * )(0x40023844)) //akses në RCC_APB2ENR reg #define RCC_APB2_ENR_SYSCFG 0x4000 //SYSCFG bitfield //Përkufizime për regjistrat GPIO MODE #define GPIOA_MODER (*0x0 volum) te GPIOA_MODER reg #define GPIOC_ MODER (*(i gjatë i paqëndrueshëm i panënshkruar*)(0x40020800)) //hyn në reg GPIOC_MODER #define GPIOD_MODER (*(i paqëndrueshëm i gjatë*)(0x40020C00)) //qasja në regjistrin GPIOD_MODER GPIODE reg //GPIODE #përkufizimi i GPIOD * (i gjatë i paqëndrueshëm i panënshkruar*) (0x40020C14)) //hyn në reg GPIOD_MODER #define GPIO_ODR_13PIN 0x2000 #define GPIO_ODR_14PIN 0x4000 //Fushat bit definicionet #define bit-moderi GPIO_MODER_0IN 0x0 //Pin 0 modaliteti i hyrjes # define GPIO_MODER_6BITS 0x300 //Pin 6 bit modaliteti #define GPIO_MODER_6IN 0x000 //Modaliteti i hyrjes Pin 6 #define GPIO_MODER_13BITS 0xC000000 //Pin 13 bit modaliteti #define GPIO_MODER_6IN 0x000 //Pin 6 modaliteti i daljes #define GPIO_MODER _14BITS 0x30000000 //Pin 14 bit modaliteti #define GPIO_MODER_14OUT 0x10000000 //Pin 14 modaliteti i daljes //Përkufizimi i regjistrit GPIOC_PUPDR #define GPIOC_PUPDR (*(i gjatë i paqëndrueshëm i panënshkruar*) (0x4002080C)) //qasja në GPIOC_0000DrGPIOC6/PUx3 /PC6 bitfield #define GPIOC_PUPDR_6 PU 0x1000 / /PC6 pull-up aktivizon //SYSCFG_EXTIx regjistron përkufizimet #define SYSCFG_EXTICR1 (*(i panënshkruar i gjatë i paqëndrueshëm*)0x40013808) //SYSCFG_EXTICR1 hyn #define SYSCFG_EXTICR_EXTICR1_0BITSG_0BITSGde _0PA 0x0 //EXTI 0 - porta A #define SYSCFG_EXTICR2/ gjobë EXTI_IMR (*(i gjatë i paqëndrueshëm i panënshkruar*) 0x40013C 00) / /EXTI_IMR reg acces #define EXTI_LINE0 0x1 //LINE 0 definition #define EXTI_LINE6 0x40 //LINE 6 define #define EXTI_RTSR (*(unsigned volatile EXTI_RTSR) 0x0800 EXTI_TI_R FTSR (*( unsigned volatile long* )0x40013C0C) //EXTI_FTSR reg acces #define EXTI_PR (*(unsigned volatile long*)0x40013C14) //EXTI_PR reg acces //NVIC regjistrat dhe përkufizimet e biteve (define*ISERE0_REX) 0E100) //NVIC_ISER0 reg acces #define NVIC_ISER0_6VECT 0x40 //vect 6 definition #define NVIC_ISER0_23VECT 0x800000 //vect 30 definition #define NVIC_ISER0_6VECT 0x40 //vect 6 Definition ed volatile char*)(NVIC_IPR0_ADD + 23) ) #define NVIC_IPR6_REG ( *(karakteristikë e paqëndrueshme e panënshkruar*)(NVIC_IPR0_ADD + 6)) void EXTI_Line0_IntHandler(void); void EXTI_Line6_IntHandler(void); void main() ( //NVIC SCB_AIRCR = SCB_AIRCR_GROUP22; //Aktivizo SYSCFG , portin GPIO A, C dhe D duke klikuar RCC_AHB1_ENR |= RCC_AHB1_ENR_GPIOA|RCC_AHB1_1RC_CCBA ENR |= RCC_APB2_ENR_SYSC FG; //Inicializimi LED3 dhe LED5 GPIOD_MODER = (GPIOD_MODER & (~GPIO_MODER_13BITS)) | GPIO_MODER_13OUT; GPIOD_MODER = (GPIOD_MODER & (~GPIO_MODER_14BITS)) | GPIO_MODER_14OUT; //Inicializimi i kunjave PA0 dhe PC6 GPIOA_MODER = (GPIOD_BIOTS) 0IN; GPIOC_MODER = (GPIOC_MODER & (~GPIO_MODER_6BITS ) | 0|EXTI_LINE6; //EXTI në lidhja e portës SYSCFG_EXTICR1 = (SYSCFG_EXTICR1&(~SYSCFG_EXTICR1_0BITS)) | SYSCFG_EXTICR1_0PA; SYSCFG_EXTICR2 = (SYSCFG_EXTICR2&(~SYSCFG_EXTICR1&(~SYSCFG_EXTICR1_0BITS)) prioritet NVIC_IPR6 _REG = 0xF0;NVIC_IPR23_REG = 0x00; //Aktivizo ndërprerjet NVIC_ISER0_REG |= NVIC_ISER0_6VECT | NVIC_ISER0_23VECT; ndërsa (1) ( ) ) i pavlefshëm EXTI_Line0_IntHandler(void) ( //Pastro ndërprerjen EXTI_PR = EXTI_LINE0; //Ndiz LED 3 GPIOD_ODR |= GPIO_ODR_13PIN; ) i pavlefshëm EXTI_Line6_IntHandler; Kthehuni LED4 GPIOD_ODR |= GPIO_ODR_14PIN; ndërsa (1); )


Për të kontrolluar efektin e prioriteteve të ndërprerjes dhe prioriteteve të grupit të ndërprerjes, provoni të ndryshoni prioritetet dhe të vëzhgoni se çfarë ndodh (dy bit - prioritet brenda grupit, 2 bit - përparësi grupi).

Etiketa:

  • STM32
  • Korteksi-M
  • ARM
  • mikrokontrolluesit
Shto etiketa

Le të ndezim LED-in!

Meqenëse mikrokontrolluesit STM32 janë bërthama të vërteta ARM 32-bitësh, kjo nuk do të jetë e lehtë për t'u bërë. Gjithçka këtu është shumë e ndryshme nga metodat e zakonshme në PIC ose AVR, ku mjaftonte të konfigurosh portin e daljes me një rresht dhe të printosh vlerën në të me rreshtin e dytë - por është akoma më interesante dhe fleksibël.

Arkitektura STM32

Arkitektura e mikrokontrolluesve përshkruhet në detaje në artikull, por më lejoni t'ju kujtoj pikat kryesore që janë interesante për ne tani.

Bërthama fiksohet nga kuarci, zakonisht përmes një PLL. kjo - shpejtësia e orës bazë, ose SYSCLK. Pllaka e zbulimit STM32VLD ka një kristal 8 MHz, dhe PLL në shumicën e rasteve është konfiguruar si një shumëzues prej 3 - d.m.th. SYSCLK në tabelën e zbulimit STM32VLD është zakonisht 24 MHz.

Një autobus shtrihet nga thelbi AHB, e cila ka frekuencën e vet të orës - mund të vendosni një parashkallëzues të caktuar në lidhje me SYSCLK, por mund ta lini atë të barabartë me një. Ky autobus është i ngjashëm me autobusin midis procesorit dhe urës veriore të kompjuterit - në të njëjtën mënyrë shërben për të lidhur bërthamën ARM dhe procesorin periferik, si dhe memorien dhe, natyrisht, kontrolluesin DMA.

Dy autobusë periferikë janë të lidhur me autobusin AHB - APB1 Dhe APB2. Ato janë ekuivalente, ato thjesht shërbejnë për kontrollues të ndryshëm të ndërfaqes. Frekuencat e të dy autobusëve APB1 dhe APB2 mund të vendosen me parakaluesit e tyre në lidhje me AHB, por ato gjithashtu mund të lihen e barabartë me një. Si parazgjedhje, pas ndezjes së mikrokontrolluesit, të gjitha pajisjet periferike janë në autobusët APB1 dhe APB2 me aftësi të kufizuara për të kursyer energji.

Kontrollorët e portit I/O për të cilët ne jemi të interesuar të varen në autobusin APB2.

Modeli periferik në STM32

Të gjitha pajisjet periferike të mikrokontrolluesve STM32 janë konfiguruar sipas një procedure standarde.

  1. Aktivizimi i orës së kontrolluesit përkatës - fjalë për fjalë, duke e furnizuar atë me një sinjal të orës nga autobusi APB;
  2. Cilësimet specifike për një pajisje periferike të veçantë - ne shkruajmë diçka në regjistrat e kontrollit;
  3. Zgjedhja e burimeve të ndërprerjes - secili blloku periferik mund të gjenerojë ndërprerje për arsye të ndryshme. Ju mund të zgjidhni "arsye" specifike;
  4. Caktimi i një mbajtësi të ndërprerjeve;
  5. Nisja e kontrolluesit.

Nëse nuk nevojiten ndërprerje, hapat 3 dhe 4 mund të anashkalohen.

Këtu, për shembull, është inicializimi i kohëmatësit (tregohen hapat nga sekuenca):

/* 1 */ RCC->APB2ENR |= RCC_APB2ENR_TIM1EN; /* 2 */ TIM6->PSC = 24000; TIM6->ARR = 1000; /* 3 */ TIM6->DIER |= TIM_DIER_UIE; /* 4 */ NVIC_EnableIRQ(TIM6_DAC_IRQn); /* 5 */ TIM6->CR1 |= TIM_CR1_CEN;

Kontrolluesi i portit I/O

Më në fund kalojmë në temën kryesore të artikullit.

Kështu është rregulluar një këmbë hyrëse/dalëse e mikrokontrolluesit STM32F100:

Duket më e ndërlikuar sesa në PIC ose AVR Por në fakt, është në rregull.

Në hyrje ka dioda mbrojtëse që parandalojnë rënien e potencialit të këmbës nën tokë ose ngritjen e saj mbi tensionin e furnizimit. Më pas, instalohen rezistorë tërheqës të kontrolluar - nëse dëshironi, këmba mund të tërhiqet në tokë ose në furnizimin me energji elektrike. Megjithatë, duhet të mbani mend se këto mbajtëse janë mjaft të dobëta.

Hyrja

Le të shqyrtojmë "inputin". Sinjali shkon direkt në linjën "Analog", dhe nëse pini është konfiguruar si një hyrje në një ADC ose krahasues - dhe nëse këto blloqe janë në këtë pin - sinjali shkon drejtpërdrejt tek ata. Për të punuar me sinjale dixhitale, është instaluar një këmbëz Schmitt (ky është ai me histerezë) dhe dalja e tij shkon në regjistrin e mbylljes së të dhënave hyrëse - tani gjendja e pinit mund të lexohet në program duke lexuar këtë regjistër (nga mënyrë, quhet IDR - regjistri i të dhënave hyrëse). Për të siguruar funksionimin e pajisjeve periferike jo-GPIO të varura në këtë këmbë si hyrje, u bë një trokitje e lehtë e quajtur "Hyrja e funksionit alternativ". Ky periferik mund të jetë UART/USART, SPI, USB dhe shumë kontrollues të tjerë.

Është e rëndësishme të kuptohet se të gjitha këto çezma janë të ndezura dhe funksionojnë njëkohësisht, thjesht mund të mos ketë asgjë të lidhur me to.

Dilni

Tani "dalje". Të dhënat dixhitale të shkruara në portë si dalje ndodhen në regjistrin ODR - regjistri i të dhënave dalëse. Është i disponueshëm si për shkrim ashtu edhe për lexim. Kur lexoni nga ODR, nuk po lexoni gjendjen e këmbës si hyrje! Ju lexoni atë që keni shkruar në të.

Këtu është dalja nga pajisjet periferike jo-GPIO, të quajtur "Dalja e funksionit alternativ", dhe ne futemi në drejtuesin e daljes. Mënyra e funksionimit të daljes nga pikëpamja e dizajnit të qarkut është konfiguruar këtu - mund të bëni një dalje shtytëse (linja tërhiqet fort nga toka ose fuqia), një dalje kolektori të hapur (ne e tërheqim linjën në fuqia, dhe toka sigurohet nga diçka e jashtme e varur në kontakt) ose fikeni plotësisht daljen. Pas drejtuesit, dalja analoge nga DAC, krahasuesi ose op-amp hyn në linjë dhe ne përsëri përfundojmë me rezistorë tërheqës dhe diodë.

Drejtuesi i daljes dixhitale ka gjithashtu kontrollin e pjerrësisë, ose shkallën e rritjes së tensionit. Ju mund të vendosni pjerrësinë maksimale dhe të jeni në gjendje të tundni këmbën në një frekuencë prej 50 MHz - por në këtë mënyrë do të marrim gjithashtu ndërhyrje të forta elektromagnetike për shkak të fronteve të mprehta të ziles. Mund të vendosni pjerrësinë minimale, me frekuencë maksimale"vetëm" 2 MHz - por gjithashtu zvogëloni ndjeshëm ndërhyrjen në radio.

Në foto mund të shihni një regjistër tjetër, “Bit set/reset registers”. Çështja është që ju mund të shkruani direkt në regjistrin ODR, ose mund të përdorni regjistrat BRR/BSRR. Në fakt, kjo është një veçori shumë e lezetshme, për të cilën do të flas në vijim.

Mundësitë

Tani gjithçka është bërë si kaos - është e paqartë se si të menaxhohen të gjitha këto mundësi. Sidoqoftë, jo, kontrolluesi i portit monitoron mënyrat e mundshme të funksionimit të daljes dhe eliminon kombinimet e pasakta - për shembull, nuk do të lejojë që drejtuesi i daljes dixhitale dhe dalja analoge të funksionojnë njëkohësisht në të njëjtën linjë dalëse. Por prania e kaq shumë cilësimeve ofron mundësi të gjera.

Për shembull, në seritë e vjetra mund të konfiguroni daljen me një kolektor të hapur dhe të aktivizoni tërheqjen në tokë. Kjo rezulton të jetë pikërisht ajo që nevojitet për një autobus 1-Wire. Sidoqoftë, në serinë STM32F1xx nuk ka një mundësi të tillë, dhe ju duhet të instaloni një rezistencë të jashtme tërheqëse.

Operacionet atomike

Në mikrokontrolluesit e vjetër, shpesh krijohej një situatë - nëse donim të ndryshonim disa pjesë në port (dhe në fakt thjesht të ndiznim ose çaktivizonim një kunj) - duhej të lexonim të gjithë regjistrin e portit, të vendosnim/rivendosnim bitet e nevojshme në atë dhe shkruani përsëri. Gjithçka ishte në rregull deri në momentin kur ky operacion u ndërpre në mes nga një ndërprerje. Nëse mbajtësi për këtë ndërprerje bëri gjithashtu diçka me të njëjtën portë, ndodhi një gabim jashtëzakonisht delikat. Kjo u trajtua me mjete të ndryshme, për shembull, ata ndaluan globalisht ndërprerjet ndërsa porti po përpunohej - por duhet ta pranoni, ky është një lloj opsioni paterica.

Në STM32, ky problem zgjidhet në harduer - ju keni vendosur dhe rivendosur regjistrat e bitave (BSRR dhe BRR), dhe këtu tre zogj vriten menjëherë:

  1. nuk ka nevojë të lexoni portin për të punuar me të
  2. për të ndikuar në kunjat specifike, duhet të punoni me pjesë specifike, në vend që të përpiqeni të ndryshoni të gjithë portin
  3. këto operacione janë atomike - ato zhvillohen në një cikël dhe nuk mund të ndërpriten në mes.

Më shumë detaje rreth "biteve specifike" - çdo cikël orësh APB2 lexohen regjistrat BSRR dhe BRR dhe përmbajtja e tyre aplikohet menjëherë në regjistrin ODR dhe vetë këta regjistra pastrohen. Kështu, nëse duhet të vendosni bitet 3 dhe 5 në portin, shkruani fjalën 10100 në BSRR dhe gjithçka instalohet me sukses.

Bllokimi i një konfigurimi

Nëse dëshironi, mund të bllokoni konfigurimin e çdo pin nga ndryshimet e mëtejshme - çdo përpjekje për të shkruar në regjistrin e konfigurimit do të dështojë. Ky është i përshtatshëm për aplikacione kritike ku kalimi aksidental, për shembull, nga modaliteti i shkarkimit të hapur në tërheqje me shtytje do të djegë gjithçka që lidhet me këtë kunj, ose vetë kunja. Regjistri LCKR ka për qëllim të mundësojë bllokimin, vetëm ai është i pajisur me mbrojtje kundër shkrimit të paqëllimshëm aksidental - që ndryshimet të hyjnë në fuqi, duhet të paraqisni një sekuencë të veçantë në bitin LCKK.

Regjistrat e kontrollit

I gjithë kontrolli i kontrolluesit GPIO është i përqendruar në regjistrat 32-bit GPIOx_RRR, ku x është numri i portit dhe RRR është emri i regjistrit.

Regjistri me konfigurim të ulët GPIOx_CRL

Konfiguron 8 këmbët e para, me numër 0..7. Çdo këmbë ka dy parametra, MODE dhe CNF.

MODEështë përgjegjës për mënyrën e hyrjes/daljes dhe shpejtësinë e lëvizjes së sinjalit.

00 - hyrje (modaliteti i parazgjedhur)

01 - dalje me shpejtësi 10 MHz

10 - dalje me shpejtësi 2 MHz

11 - dalje me shpejtësi 50 MHz

CNFështë përgjegjës për konfigurimin e pinit.

  • Në modalitetin e hyrjes (MODE=00):

    00 - modaliteti analog

    01 - hyrje lundruese (e parazgjedhur)

    10 - hyrje me tërheqje në tokë ose fuqi

    11 - e rezervuar

  • Në modalitetin e daljes (MODE=01, 10 ose 11):

    00 - GPIO Push-pull output

    01 - Dalja GPIO Hap kullimin

    10 - prodhimi i funksionit alternativ Push-pull

    11 - dalja e funksionit alternativ Hap kullimin

Regjistri me konfigurim të lartë GPIOx_CRH

Konfiguron 8 këmbët e dyta, me numrat 8..15. Gjithçka është e ngjashme me GPIOx_CRL.

Regjistri i të dhënave hyrëse GPIOx_IDR

Çdo bit IDry përmban gjendjen e pinit përkatës I/O. Lexo vetem.

Regjistri i të dhënave hyrëse GPIOx_ODR

Çdo bit ODry përmban gjendjen e pinit përkatës I/O. Mund të shkruani të dhëna dhe ato do të shfaqen në daljen e portit, mund t'i lexoni të dhënat duke lexuar vlerën e mëparshme të shkruar.

Vendosja/rivendosja e regjistrit të bitit të daljes atomike GPIOx_BSRR

16 bitet më domethënëse janë për rivendosjen e kunjave përkatëse në 0. 0 nuk bën asgjë, 1 rivendos bitin përkatës. 16 bitet e poshtme janë për vendosjen e biteve në 1. Në mënyrë të ngjashme, shkrimi i një "0" nuk bën asgjë, duke shkruar një "1" vendos bitin përkatës në 1.

Regjistri i rivendosjes së bitit të të dhënave të daljes atomike GPIOx_BRR

16 bitet e poshtme janë për rivendosjen e kunjave përkatëse. 0 - nuk bën asgjë, 1 - rivendos bitin përkatës.

Regjistri është vetëm për shkrim - ai rivendoset në zero në çdo cikël të orës APB2.

Regjistri i kyçjes së konfigurimit GPIOx_LCKR

Çdo bit LCKy bllokon bitet përkatëse MODE/CNF të regjistrave CRL/CRH nga ndryshimi, kështu që konfigurimi i pinit nuk mund të ndryshohet deri në një rindezje. Për të aktivizuar bllokimin, duhet të shkruani sekuencën e bllokimit në bitin LCKK: 1, 0, 1, lexoni 0, lexoni 1. Leximi i bitit LCKK raporton statusin aktual të bllokimit: 0 - pa bllokim, 1 - po.

Punoni në mënyra të ndryshme

Modaliteti i hyrjes

  • Drejtuesi i daljes është i çaktivizuar
  • Rezistencat e tërheqjes ndizen sipas cilësimeve tuaja, një nga tre gjendjet - "hyrja e tërhequr në tokë", "hyrja e tërhequr në energji" ose "hyrja lundruese"
  • Sinjali i hyrjes merret në çdo cikël të orës të autobusit APB2 dhe shkruhet në regjistrin IDR, dhe leximi i këtij regjistri raporton gjendjen e pinit.

Modaliteti i daljes

  • Drejtuesi i daljes është i aktivizuar dhe vepron kështu:

    Në modalitetin "Push-Pull" funksionon si një gjysmë urë, duke përfshirë transistorin e sipërm në rastin e "1" dhe atë të poshtëm në rastin e "0".

    Në modalitetin "Shkarkimi i hapur", ai ndez tranzistorin e poshtëm në rastin "0", dhe në rastin "1" e lë linjën të palidhur (d.m.th. në gjendjen e tretë).

  • Aktivizuesi i Schmitt është aktivizuar
  • Rezistencat tërheqëse janë fikur

Modaliteti i funksionit alternativ (Periferikët jo-GPIO)

  • Drejtuesi i daljes - në modalitetin Push-Pull (për shembull, kështu funksionon këmba TX e modulit USART) ose Hapni kullimin, në varësi të kërkesave të kontrolluesit
  • Drejtuesi i daljes kontrollohet nga sinjalet periferike, jo nga regjistri ODR
  • Aktivizuesi i Schmitt është aktivizuar
  • Rezistencat tërheqëse janë çaktivizuar
  • Sinjali i daljes merret si kampion në çdo cikël të orës të autobusit APB2 dhe shkruhet në regjistrin IDR, dhe leximi i këtij regjistri raporton gjendjen e pinit në modalitetin e shkarkimit të hapur.
  • Leximi i regjistrit ODR raporton gjendjen e fundit të shkruar në modalitetin Push-Pull.

Modaliteti analog

  • Drejtuesi i daljes është i çaktivizuar
  • Shkaku i Schmitt është plotësisht i çaktivizuar në mënyrë që të mos ndikojë në tensionin e hyrjes
  • Rezistencat tërheqëse janë çaktivizuar
  • Regjistri IDR është gjithmonë 0.

Të gjitha pajisjet periferike analoge të brendshme kanë rezistencë të lartë hyrëse, kështu që vetë kunja do të ketë rezistencë të lartë hyrëse në krahasim me pjesën tjetër të qarkut.

Më në fund ndizni LED

Tani dimë gjithçka për të ndezur këtë LED! Le të shkojmë nga fillimi.

Duhet të aktivizoni orën e portit GPIO. Meqenëse po përdorim LED në tabelën Discovery, do të zgjedhim jeshile - është e lidhur me portën PC9. Kjo do të thotë, duhet të aktivizoni orën e GPIOC.

Tani ne po flasim për Push-pull output. Kjo korrespondon me 00 në regjistrin CNF.

Epo, për të qenë i sinqertë, kjo është e gjitha. Më në fund, një listë e LED-it që vezullon

#include "stm32f10x.h" int main(void) ( RCC->APB2ENR |= RCC_APB2ENR_IOPCEN; GPIOC->CRH &= !(GPIO_CRH_CNF9_0 | GPIO_CRH_CNF9_1); GPIOC->PIMO_1t=u, GPIOC->CRH_1 | 00000 0; ndërsa (1) ( GPIOC->BSRR |= GPIO_BSRR_BS9; i=0; ndërsa (i++ BRR |= GPIO_BRR_BR9; i=0; ndërsa (i++

biblioteka itacon

E megjithatë kjo nuk është e gjitha. Për të thjeshtuar të gjitha llojet e cilësimeve, unë krijoj bibliotekën itacon. Aktualisht, ai zbaton punën me kunjat GPIO dhe disa funksione të përdorimit të përgjithshëm - por puna vazhdon.

Artikujt më të mirë mbi këtë temë