Si të konfiguroni telefonat inteligjentë dhe PC. Portali informativ
  • në shtëpi
  • Windows Phone
  • Mikrokontrolluesit procesorë, projekte, programim. Mbrojtja e biteve të konfigurimit të GPIO

Mikrokontrolluesit procesorë, projekte, programim. Mbrojtja e biteve të konfigurimit të GPIO

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.

Konfiguro portin në modaliteti i dëshiruar puna mund të bëhet duke përdorur regjistrat e mikrokontrolluesve. 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 nga nevoja për të llogaritur "me dorë" 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.

Ne shikuam punën me operacionet bit dhe numrat binare, duke hedhur kështu themelin për shqyrtim temë e re. Në këtë mësim do të shqyrtojmë një pyetje tjetër: çfarë janë regjistrat dhe si të punohet me ta?

Kujtesa dhe regjistrat

Një nga aftësitë më të rëndësishme që kërkohet kur punoni me mikrokontrollues është aftësia për të bashkëvepruar me regjistrat. Le ta kuptojmë vetë Çfarë është ajo?

Në përgjithësi, një regjistër është një lloj i veçantë memorie brenda një mikrokontrollues që përdoret për të kontrolluar procesorin dhe pajisjet periferike. Çdo regjistrim në Arkitektura ARMështë një qelizë memorie dhe ka një gjatësi prej 32 bit, ku çdo bit mund të përfaqësohet si një ndërprerës i vogël me të cilin kontrollohet një ose një parametër tjetër i mikrokontrolluesit.

Çdo regjistër ka të vetin numër serik- adresë. Adresa e regjistrit tregohet nga një numër 32-bit i përfaqësuar në sistemi heksadecimal Duke llogaritur. Duke shkruar në adresën e regjistrit kombinim i caktuar njësitë dhe zerot, të cilat zakonisht paraqiten në formë heksadecimal, përdoren për të konfiguruar dhe kontrolluar një nyje të veçantë në MK. Le të kujtojmë se në një program për të punuar me operacione bit, ne mund ta përfaqësojmë atë në formë numër heksadecimal një grup arbitrar i njësheve dhe zerove. Në përgjithësi, vlen të theksohet se ekzistojnë dy lloje regjistrash: regjistrat Qëllimi i përgjithshëm dhe regjistra të veçantë. Të parat janë të vendosura brenda bërthamës MK, dhe të dytat janë pjesë e memories RAM.

Gjithashtu vlen të theksohet se Manuali i referencës, të cilin e kemi shkarkuar, është një libër i madh referimi për regjistrat e përfshirë në mikrokontrolluesin e synuar dhe biblioteka CMSIS na lejon të operojmë me emra simbolikë të regjistrave në vend të adresave numerike. Për shembull, në regjistër 0x40011018 ne mund të hyjmë thjesht duke përdorur emër simbolik GPIOC_BSSR. Shembuj specifikë Ne do të shikojmë konfigurimin gjatë analizës së programit tonë që nga mësimi i parë.

Pra, zakonisht struktura e regjistrit përshkruhet në formën e një tabele të vogël që tregon:

  1. Regjistroni emrat dhe përshkrimet e qëllimit të tij
  2. Regjistroni adresat ose zhvendosni nga adresa bazë
  3. Vlerat e paracaktuara pas rivendosjes
  4. Lloji i aksesit në qelizat e regjistrimit (lexo, shkruaj, lexo/shkruaj)
  5. Vlerat dhe përshkrimet e parametrave të biteve që do të shkruhen
Le të shohim një shembull të punës me regjistra në një situatë specifike për të marrë ide e pergjithshme në lidhje me parimet e vendosjes së një mikrokontrollues.

Analiza e kodit nga mësimi i parë

Pra, le të kujtojmë problemin që zgjidhëm në mësimin e parë duke përdorur kod i gatshëm shembull: na duhej të shkruanim një program që do të ndizte në mënyrë alternative dy LED në tabelën Discovery (ndoshta jo dy, nëse keni një version të ndryshëm të tabelës Discovery) me një interval kohor.

Le të hedhim një vështrim tjetër në kodin e programit që kemi përdorur për të bërë kërcimin tonë MK me dy këmbët në të cilat ndodhen LED-et tona:

Kodi kryesor.c

/* Skedari i kokës për familjen tonë të mikrokontrolluesve */ #include "stm32f0xx.h" /* Trupi i programit kryesor */ int main(void) ( /* Aktivizo akordimin Porta GPIO*/ RCC->AHBENR |= RCC_AHBENR_GPIOCEN; /* Konfiguro mënyrën e funksionimit të portave PC8 dhe PC9 në Output*/ GPIOC ->MODER = 0x50000; /* Cakto llojin e daljes në modalitetin Push-Pull */ GPIOC->OTYPER = 0; /* Vendosni shpejtësinë e portit në Low */ GPIOC->OSPEEDR = 0; ndërsa (1) ( /* Ndizni LED PC8, fikni PC9 */ GPIOC->ODR = 0x100; për (int i=0; i<500000; i++){} // Искусственная задержка /* Зажигаем светодиод PC9, гасим PC8 */ GPIOC->ODR = 0x200; për (int i=0; i<500000; i++){} // Искусственная задержка } }


Para së gjithash, kur punojmë me STM32, edhe për një detyrë kaq të thjeshtë si ndezja dhe fikja e një LED, së pari duhet t'i përgjigjemi një numri pyetjesh:
  1. Si të konfigurojmë kunjat e portit GPIO që na duhen në mënyrë që të mund të ndezim LED?
  2. Si të ndizni dhe fikni LED?
Le t'u përgjigjemi atyre me radhë.

Ku janë të lidhura LED-et tona? Në cilën kunj të mikrokontrolluesit?

Për të parë se ku është gjithçka në tabelën Discovery, dhe në veçanti, LED-të që na duhen, duhet të hapim skedarin Skematik, qoftë atë që kemi shkarkuar nga faqja e internetit ST, ose direkt nga Keil:


Duke hapur skemën, do të shohim një diagram të gjithçkaje që është në tabelë - një diagram ST-Link, instalime elektrike të të gjitha pajisjeve periferike dhe shumë më tepër. Për momentin ne jemi të interesuar për dy LED, ne jemi duke kërkuar për përcaktimin e tyre:


Siç mund ta shohim, LED-et tona janë të lidhura me portën GPIOC në kunjat 8 dhe 9.

Si të aktivizoni akordimin në portën e dëshiruar GPIO?

Në përgjithësi, çdo punë me pajisje periferike në mikrokontrolluesit STM32 zbret në një sekuencë standarde veprimesh:
  1. Aktivizo akordimin e modulit periferik përkatës. Kjo bëhet përmes regjistrit RCC duke dhënë një sinjal të orës direkt nga autobusi në të cilin ndodhet ky modul. Si parazgjedhje, fiksimi i të gjitha pajisjeve periferike është i çaktivizuar për të minimizuar konsumin e energjisë.
  2. Konfigurimi përmes regjistrave të kontrollit, duke ndryshuar parametrat specifikë për një pajisje të veçantë periferike
  3. Nisja dhe përdorimi i drejtpërdrejtë i rezultateve të modulit
Kjo do të thotë, për të filluar, ne duhet të fillojmë të kontrollojmë në portën GPIOC. Kjo bëhet drejtpërdrejt duke hyrë në regjistrin RCC, i cili është përgjegjës për të kontrolluar çdo gjë dhe për të ndezur sinjalin e orës nga autobusi me të cilin është lidhur porti ynë GPIO.

Kujdes! Ne do ta shqyrtojmë pyetjen në lidhje me sistemin e orës, konfigurimin dhe përdorimin e tij në detaje në një artikull të veçantë.

Zbuloni se me cilin autobus është lidhur porta jonë GPIOC, mund ta gjeni në Fletën e të dhënave për MK-në tonë në seksionin Hartimi i kujtesës në tabelën 16. Adresat kufitare të regjistrit periferik STM32F051xx.


Siç mund ta keni vënë re tashmë, goma që na nevojitet quhet AHB2. Për t'u njohur më shumë me regjistrin në të cilin është aktivizuar ora për portin GPIO që na nevojitet në autobusin AHB, duhet të shkojmë në seksionin përkatës në Manualin e Referencës. Me emrin e regjistrave mund të përcaktojmë atë që na nevojitet:


Shkojmë në këtë pikë dhe shohim regjistrin tonë 32-bit, adresën e kompensimit të tij, vlerën e paracaktuar, mënyrën e hyrjes në regjistër dhe një listë të asaj për çfarë është përgjegjës çdo bit në regjistër.


Ne shikojmë tabelën dhe shohim diçka që i ngjan opsioneve për aktivizimin e klockimit në portet GPIO. Le të shkojmë te përshkrimi dhe të gjejmë opsionin që na nevojitet:


Në përputhje me rrethanat, nëse vendosim bitin 19 në "1", kjo do të mundësojë klockimin në portën I/O C - domethënë në GPIOC tonë. Përveç kësaj, ne duhet të aktivizojmë një bit nga grupi veçmas, pa ndikuar në pjesën tjetër sepse ne nuk duhet të ndërhyjmë ose të ndryshojmë në mënyrë të panevojshme cilësime të tjera.

Bazuar në materialet e mësimit të mëparshëm, ne e dimë se për të vendosur një bit të caktuar, duhet të përdorim operacionin logjik "OR" për të shtuar vlerën e regjistrit aktual me një maskë që përmban bitet që duhen ndezur. Për shembull, le të shtojmë vlerën e paracaktuar të regjistrit RCC->AHBENR, d.m.th. 0x14 dhe numri 0x80000 do të mundësojë klockimin e GPIOC duke vendosur bitin 19:

Si mund ta bëjmë këtë nga një program? Është mjaft e thjeshtë. Në këtë rast kemi dy mundësi:

  1. Shkrimi në një regjistër drejtpërdrejt i vlerës numerike të një regjistri direkt përmes adresës së tij.
  2. Konfigurimi duke përdorur bibliotekën CMSIS
Nuk ka probleme të veçanta me shkrimin e një vlere në një regjistër drejtpërdrejt, por ka disa të meta të rëndësishme. Së pari, një kod i tillë bëhet i palexueshëm dhe së dyti, ne nuk mund të përcaktojmë menjëherë se cilit regjistër i referohet një adresë e caktuar në memorie.

Kjo do të thotë, ne mund t'i qasemi adresave të regjistrave direkt me adresë dhe të shkruajmë këtë:

IO uint32_t * adresa_regjistrit = (uint32_t *) 0x40021014U; // Adresa e regjistrit tonë në memorie *(__IO uint32_t *)register_adresa |= 0x80000; // Aktivizoni 19 bit me parametrin tonë
Opsioni i dytë më duket më tërheqës, sepse... Biblioteka CMSIS është e organizuar në atë mënyrë që një regjistër mund të aksesohet duke përdorur vetëm emrin e tij. Parapërpunuesi, kur përpunon tekstin e programit përpara kompilimit, do të zëvendësojë automatikisht të gjitha vlerat dixhitale të adresës së regjistrit. Le ta shohim këtë pyetje pak më në detaje.

Unë sugjeroj hapjen e projektit tonë, të cilin e bëmë në mësimin e parë, ose shkarkojmë një të përgatitur paraprakisht nga këtu dhe fshijmë të gjitha përmbajtjet e programit, duke lënë vetëm skedarin e përfshirë të kokës, funksionin kryesor () dhe udhëzimet për aktivizimin e klockimit ( do të na duhet për një analizë të hollësishme të kodit).

Kodi ynë do të duket si ky:

/* Skedari i kokës për familjen tonë të mikrokontrolluesve */ #include "stm32f0xx.h" /* Trupi kryesor i programit */ int main(void) ( /* Aktivizo kronologjinë në portën GPIO */ RCC->AHBENR|=RCC_AHBENR_GPIOCEN; )
Le të gërmojmë thellë në bibliotekën CMSIS për t'u njohur.

Për të shkuar shpejt në vendin ku deklarohet kjo ose ajo konstante ose ndryshore, Keil zbaton një funksion të përshtatshëm. Ne klikojmë me të djathtën në konstantën që na nevojitet, për shembull, në RCC:


Dhe ne transportohemi në thellësitë e bibliotekës CMSIS, në të cilën do të shohim se të gjithë regjistrat e disponueshëm për kontroll programatik kanë formën e strukturave TypeDef, duke përfshirë RCC-në tonë:


Duke dështuar në këtë mënyrë në RCC_TypeDef, do të shohim një strukturë që përshkruan të gjitha fushat e regjistrit tonë:


Prandaj, ne mund të hyjmë në mënyrë të sigurtë në regjistrin që na nevojitet me një hyrje si PERIPH_MODULE->REGJISTROHU dhe i caktoni një vlerë specifike.

Përveç përcaktimit kujtimor të regjistrave, ekzistojnë edhe emërtime për bite specifike. Nëse nuk arrijmë të deklarojmë parametrin RCC_AHBENR_GPIOCEN nga programi ynë, do të shohim gjithashtu deklarimin e të gjithë parametrave:


Kështu, duke përdorur bibliotekën CMSIS, marrim një hyrje koncize dhe të lexueshme të parametrit që na nevojitet në një regjistër, përmes cilësimit të të cilit fillojmë të frekuentojmë portin që na nevojitet:

/* Aktivizo clocking në portën GPIO */ RCC->AHBENR|=RCC_AHBENR_GPIOCEN;
Si detyrë: Përcaktoni duke përdorur aftësitë e Keil se si adresa e regjistrit RCC->AHBENR doli të ishte 0x40021014.

Si të konfigurojmë kunjat GPIO që na duhen në mënyrë që të mund të ndezim LED?

Pra, ne e dimë se LED-të që na duhen janë të lidhura me portën GPIOC me kunjat PC8 dhe PC9. Ne duhet t'i vendosim ato në një mënyrë të tillë që LED të ndizet. Unë do të doja të bëja menjëherë një rezervim që ne do t'i shikojmë portet GPIO në mënyrë më të detajuar në një artikull tjetër dhe këtu do të përqendrohemi veçanërisht në punën me regjistrat.

Para së gjithash, duhet të kalojmë mënyrën e funksionimit të kunjave PC8 dhe PC9 në modalitetin Output. Parametrat e mbetur të portit mund të lihen si parazgjedhje. Shkoni te seksioni Manuali i referencës 9. I/O për qëllime të përgjithshme (GPIO) dhe hapni artikullin përgjegjës për mënyrën e funksionimit të kunjave të portit GPIO dhe shikoni që regjistri MODER është përgjegjës për këtë parametër:


Duke gjykuar nga përshkrimi, për të vendosur kunjat PC8 dhe PC9 në modalitetin Output, duhet të shkruajmë 01 në fushat përkatëse të regjistrit GPIOC.

Kjo mund të bëhet nëpërmjet instalimit të drejtpërdrejtë duke përdorur vlera numerike:


Ose duke përdorur përkufizime nga biblioteka:

/* Aktivizo clocking në portën GPIO */ GPIOC->MODER |= GPIO_MODER_MODER8_0 | GPIO_MODER_MODER9_0;
Pas këtij udhëzimi, kunjat tona PC8 dhe PC9 do të kalojnë në modalitetin e daljes.

Si të ndizni LED?

Nëse i kushtojmë vëmendje listës së regjistrave të disponueshëm për kontrollin e portit GPIO, mund të shohim regjistrin ODR:


Secili prej biteve përkatës është përgjegjës për një nga kunjat e portit. Më poshtë mund ta shihni strukturën e saj:


Për të siguruar një ndryshim të alternuar të gjendjeve LED, është e nevojshme të ndizni/fikni bitet 8 dhe 9 me një interval të caktuar kohor. Kjo do të thotë, caktoni në mënyrë alternative regjistrit vlerën 0x100 dhe 0x200.

Ne mund ta bëjmë këtë duke caktuar drejtpërdrejt vlera në regjistër:

GPIOC->ODR = 0x100; // Ndizni PC8, fikni PC9 GPIOC->ODR = 0x200; // Ndizni PC9, fikni PC8
Mund të përdorim përkufizime nga biblioteka:

GPIOC->ODR = GPIO_ODR_8; // Ndiz PC8, fik PC9 GPIOC->ODR = GPIO_ODR_9; // Ndizni PC9, fikni PC8
Por meqenëse mikrokontrolluesi funksionon shumë shpejt, ne nuk do të vërejmë ndryshime në gjendjet e LED-ve dhe vizualisht do të duket se ato janë të ndezura vazhdimisht. Në mënyrë që ata në të vërtetë të pulsojnë në mënyrë alternative, ne do të prezantojmë një vonesë artificiale në formën e një cikli që do të pushtojë MK me llogaritje të padobishme për ca kohë. Do të merrni kodin e mëposhtëm:

/* Ndizni LED PC8, fikni PC9 */ GPIOC->ODR = GPIO_ODR_8; për (int i=0; i<500000; i++){} // Искусственная задержка /* Зажигаем светодиод PC9, гасим PC8 */ GPIOC->ODR = GPIO_ODR_9; për (int i=0; i<500000; i++){} // Искусственная задержка
Këtu mund të përfundojmë njohjen tonë fillestare me regjistrat dhe metodat e punës me ta.

Kontrollimi i rezultateve të kodit tonë

Një shtesë e këndshme e vogël në fund të artikullit: Keil ka një mjet të shkëlqyer Debug me të cilin mund të kalojmë programin tonë dhe të shohim gjendjen aktuale të çdo njësie periferike. Për ta bërë këtë, pasi të kemi shkarkuar firmuerin pas përpilimit, mund të klikojmë butonin Start Debug Session:

STM32 ka shumë kohëmatës shumë të përshtatshëm dhe fleksibël. Edhe mikrokontrolluesi më i ri (STM32F030F4P6) ka 4 kohëmatës të tillë.

Për të përdorur kohëmatësin, do të duhet të përfshijmë skedarin e bibliotekës periferike stm32f10x_tim.c. Në të njëjtën mënyrë, kliko me të djathtën në Workspace (dritarja në të majtë) në grupin StdPeriphLib, Shto –> Shto skedarë, skedari LibrariesSTM32F10x_StdPeriph_Driversrcstm32f10x_tim.c.

Ju gjithashtu duhet të aktivizoni përdorimin e një titulli për këtë skedar. Hapni stm32f10x_conf.h (klikoni me të djathtën mbi emrin e këtij skedari në kodin, "Open stm32f10x_conf.h". Hiqni komentin rreshtin #include "stm32f10x_tim.h".

9. Shtoni një kohëmatës

Një vonesë me një cikël bosh është blasfemi, veçanërisht në një kristal kaq të fuqishëm si STM32, me një grup kohëmatëssh. Prandaj, ne do ta bëjmë këtë vonesë duke përdorur një kohëmatës.

STM32 ka kohëmatës të ndryshëm me grupe të ndryshme karakteristikash. Më të thjeshtët janë kohëmatësit bazë, më kompleksët janë kohëmatësit për qëllime të përgjithshme dhe më komplekset janë kohëmatësit e avancuar. Kohëmatësit e thjeshtë janë të kufizuar në numërimin e thjeshtë të cikleve të orës. Në kohëmatësit më kompleksë, shfaqet PWM. Kohëmatësit më kompleksë, për shembull, mund të gjenerojnë PWM 3-fazore me dalje të përparme dhe të kundërta dhe kohë të fundit. Një kohëmatës i thjeshtë, numri 6, do të na mjaftojë.

Pak teori

Gjithçka që na duhet nga kohëmatësi është të numërojmë deri në një vlerë të caktuar dhe të gjenerojmë një ndërprerje (po, do të mësojmë gjithashtu se si të përdorim ndërprerjet). Kohëmatësi TIM6 fiksohet nga autobusi i sistemit, por jo drejtpërdrejt, por përmes një parashkallëzuesi - një kundërndarëse e thjeshtë e programueshme (vetëm mendoni, numëruesit e veçantë të mikroqarqeve u prodhuan në BRSS, dhe ato të programueshme ishin veçanërisht në mungesë - dhe tani unë Unë thjesht po flas për një numërues të tillë kalimthi). Parashkallëzuesi mund të konfigurohet në çdo vlerë nga 1 (d.m.th. numëruesi do të marrë frekuencën e plotë të autobusit, 24 MHz) deri në 65536 (d.m.th. 366 Hz).

Sinjalet e orës, nga ana tjetër, rrisin numëruesin e brendshëm të kohëmatësit, duke filluar nga zero. Sapo vlera e numëruesit arrin vlerën ARR, numëruesi tejmbush dhe ndodh ngjarja përkatëse. Kur ndodh kjo ngjarje, kohëmatësi ngarkon përsëri 0 në numërues dhe fillon të numërojë nga zero. Në të njëjtën kohë, mund të shkaktojë një ndërprerje (nëse është konfiguruar).

Në fakt, procesi është pak më i ndërlikuar: ekzistojnë dy regjistra ARR - të jashtëm dhe të brendshëm. Gjatë numërimit, vlera aktuale krahasohet në mënyrë specifike me regjistrin e brendshëm, dhe vetëm kur ai tejmbushet, ajo e brendshme përditësohet nga ajo e jashtme. Në këtë mënyrë, ju mund të ndryshoni në mënyrë të sigurt ARR-në ndërsa kohëmatësi është në punë - në çdo kohë.

Kodi

Kodi do të jetë shumë i ngjashëm me atë të mëparshëm, sepse... Inicializimi i të gjitha pajisjeve periferike ndodh në të njëjtën mënyrë - me përjashtimin e vetëm që kohëmatësi TIM6 varet në autobusin APB1. Prandaj, aktivizimi i kohëmatësit: RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM6, ENABLE);

Tani krijojmë një strukturë të tipit TIM_TimeBaseInitTypeDef, e inicializojmë (TIM_TimeBaseStructInit), e konfigurojmë, ia kalojmë funksionit të inicializimit të kohëmatësit (TIM_TimeBaseInit) dhe në fund ndezim kohëmatësin (TIM_Cmd).

Cilët janë numrat magjikë? Siç e kujtojmë, autobusi ka një frekuencë të orës 24 MHz (me cilësimet tona të projektit). Duke vendosur parashkallëzuesin e kohëmatësit në 24000, ne e ndajmë këtë frekuencë me 24 mijë dhe marrim 1 kHz. Kjo është frekuenca që do të shkojë në hyrjen e numëruesit të kohëmatësit.

Vlera në numërues është 1000. Kjo do të thotë se numëruesi do të tejmbushet në 1000 cikle ore, d.m.th. në saktësisht 1 sekondë.

Pas kësaj ne në fakt kemi një kohëmatës pune. Por kjo nuk është e gjitha.

10. Le të merremi me ndërprerjet

Mirë, ndërprerje. Për mua dikur (në kohën e PIC) ata ishin një pyll i errët dhe u përpoqa të mos i përdorja fare - dhe nuk e dija se si, në fakt. Megjithatë, ato përmbajnë fuqi që është krejtësisht e padenjë për t'u injoruar. Vërtetë, ndërprerjet në STM32 janë një gjë edhe më komplekse, veçanërisht mekanizmi i zhvendosjes së tyre; por më shumë për këtë më vonë.

Siç e vumë re më herët, kohëmatësi gjeneron një ndërprerje kur numëruesi tejmbushet - nëse përpunimi i ndërprerjeve për këtë pajisje është i aktivizuar fare, ky ndërprerje i veçantë aktivizohet dhe ai i mëparshmi rivendoset. Duke analizuar këtë frazë, ne kuptojmë se çfarë na nevojitet:

  1. Aktivizo ndërprerjet e kohëmatësit TIM6 në përgjithësi;
  2. Aktivizo ndërprerjen e kohëmatësit TIM6 për tejmbushjen e numëruesit;
  3. Shkruani një procedurë të mbajtësit të ndërprerjeve;
  4. Pas përpunimit të ndërprerjes, rivendoseni atë.

Aktivizimi i ndërprerjeve

Për të qenë i sinqertë, nuk ka asgjë të komplikuar këtu. Para së gjithash, ne aktivizojmë ndërprerjet TIM6: NVIC_EnableIRQ(TIM6_DAC_IRQn); Pse ky emër? Sepse në bërthamën STM32, ndërprerjet nga TIM6 dhe nga DAC kanë të njëjtin numër. Nuk e di pse u bë kjo - kursime, mungesë numrash ose thjesht një lloj gjëje e trashëguar - në çdo rast, nuk do të shkaktojë asnjë problem, sepse ky projekt nuk përdor një DAC. Edhe nëse projekti ynë do të përdorte një DAC, ne mund të zbulonim se kush e thirri saktësisht kur hynte në ndërprerje. Pothuajse të gjithë kohëmatësit e tjerë kanë një ndërprerje të vetme.

Konfigurimi i ngjarjes së burimit të ndërprerjes: TIM_ITConfig(TIM6, TIM_DIER_UIE, ENABLE); - aktivizoni ndërprerjen e kohëmatësit TIM6 bazuar në ngjarjen TIM_DIER_UIE, d.m.th. Ngjarja e përditësimit të vlerës ARR. Siç e kujtojmë nga fotografia, kjo ndodh njëkohësisht me tejmbushjen e banakut - kështu që kjo është pikërisht ngjarja që na nevojitet.

Për momentin, kodi për rastet e kohëmatësit është si më poshtë:

Ndërpreni trajtimin

Tani nuk mund ta ekzekutoni projektin - ndërprerja e parë nga kohëmatësi nuk do ta gjejë mbajtësin e tij dhe kontrolluesi do të varet (më saktë, do të përfundojë në mbajtësin HARD_FAULT, që është në thelb e njëjta gjë). Duhet ta shkruajmë.

Pak teori

Duhet të ketë një emër shumë specifik, void TIM6_DAC_IRQHandler(void). Ky emër, i ashtuquajturi vektor i ndërprerjes, përshkruhet në skedarin e fillimit (në projektin tonë është startup_stm32f10x_md_vl.s - mund ta shihni vetë, rreshti 126). Në fakt, vektori është adresa e mbajtësit të ndërprerjeve dhe kur ndodh një ndërprerje, bërthama ARM ngjitet në zonën fillestare (në të cilën përkthehet skedari i fillimit - d.m.th. vendndodhja e tij është vendosur plotësisht në mënyrë të ngurtë, në fillimin e blicit memorie), kërkon vektorin atje dhe zhvendoset në vendin e duhur në kod.

Kontrolli i ngjarjes

Gjëja e parë që duhet të bëjmë kur futemi në një mbajtës të tillë është të kontrollojmë se cila ngjarje e ka shkaktuar ndërprerjen. Tani kemi vetëm një ngjarje, por në një projekt real mund të ketë disa ngjarje në një kohëmatës. Prandaj, ne kontrollojmë ngjarjen dhe ekzekutojmë kodin përkatës.

Në programin tonë, ky kontroll do të duket kështu: nëse (TIM_GetITStatus(TIM6, TIM_IT_Update) != RESET) - gjithçka është e qartë, funksioni TIM_GetITStatus kontrollon praninë e ngjarjes së specifikuar në kohëmatës dhe kthen 0 ose 1.

Pastrimi i flamurit UIF

Hapi i dytë është pastrimi i flamurit të ndërprerjes. Kthehuni te fotografia: grafiku i fundit UIF është flamuri i ndërprerjes. Nëse nuk fshihet, ndërprerja tjetër nuk do të thirret dhe kontrolluesi do të bjerë përsëri në HARD_FAULT (çfarë është kjo!).

Ekzekutimi i veprimeve në një ndërprerje

Ne thjesht do të ndërrojmë gjendjen e LED, si në programin e parë. Ndryshimi është se tani programi ynë e bën më të vështirë! Në fakt, është shumë më korrekte të shkruash në këtë mënyrë.

Ne përdorim variablin global int state=0;

11. I gjithë kodi i projektit me kohëmatës

Arkivi me projektin e kohëmatësit.

Epo, nga rruga, kohëmatësi mund të ndërrojë vetë këmbën, pa ndërprerje ose përpunim manual. Ky do të jetë projekti ynë i tretë.

I gjithë cikli:

1. Portet hyrëse/dalëse

2. Kohëmatësi dhe ndërprerjet

3. Rezultatet e kohëmatësit

4. Ndërprerjet e jashtme dhe NVIC

5. Instaloni FreeRTOS

Kohët e fundit, një koleg më futi në idenë e krijimit të një shtëpie të zgjuar dhe madje arrita të porosisja dhjetëra sensorë të ndryshëm për veten time. U ngrit pyetja për zgjedhjen Mikrokontrollues(në tekstin e mëtejmë MK) ose bordet. Pas disa kërkimeve gjeta disa opsione. Mes tyre ishin Arduino(përfshirë klonet e tij, njërin prej të cilëve e porosita për vete vetëm për t'u argëtuar) dhe Launchpad, por e gjithë kjo është e tepërt dhe e rëndë (edhe pse përsa i përket programimit është shumë më e thjeshtë, por nuk do ta ngre temën e holivarëve, secili ka shijet e veta). Në fund, vendosa të mos vendos për një bord të gatshëm, por të marr vetëm MK dhe të bëj gjithçka nga e para. Në fund zgjodha midis Atmel ATtiny (2313), Atmel ATmega(vendosi të refuzoj sepse nuk mund ta gjeja për para të mjaftueshme), STM32(Korteksi në thelb ARM). Unë kisha luajtur tashmë me adoleshentin, kështu që e mora për vete STM32VL-Zbulim. Kjo mund të quhet një hyrje në një seri artikujsh mbi STM32. Më lejoni të bëj një rezervë menjëherë: nuk do të jem autori i shumicës së këtyre artikujve, sepse... Unë thjesht po e mësoj vetë, këtu i publikoj kryesisht për veten time, që të jetë më e lehtë të kërkoj nëse harroj diçka. Dhe kështu le të shkojmë!

Informacion i pergjithshem

Mikrokontrolluesit familjet STM32 përmbajnë deri në shtatë porte I/O 16-bit me emra nga PORTA në PORTG. Në një model specifik mikrokontrollues Pa përjashtim, të gjitha kunjat e portit janë të disponueshme, numri i përgjithshëm i të cilave varet nga lloji i strehimit dhe specifikohet në DataSheet për nënfamiljen përkatëse.

Për të aktivizuar portën x, fillimisht duhet ta lidhni me autobusin APB2 duke vendosur bitin përkatës IOPxEN në regjistrin e aktivizimit të orës periferike. RCC_APB2ENR:

Menaxhimi i portit STM32 kryhet duke përdorur grupe prej shtatë regjistrash 32-bitësh:

  • GPIOx_CRL, GPIOx_CRH– vendosni mënyrat e funksionimit të secilit prej biteve të portit si hyrje ose dalje, përcaktoni konfigurimin e fazave hyrëse dhe dalëse.
  • GPIOx_IDR– Regjistri i të dhënave hyrëse për leximin e gjendjes fizike të portave x pins.
  • GPIOx_ODR– regjistri i daljes shkruan të dhënat direkt në port.
  • GPIOx_BSRR– regjistri i rivendosjes atomike dhe vendosjes së bitit të portit.
  • GPIOx_BSR– Regjistri i rivendosjes së bitit të portit.
  • GPIOx_LCKR– Regjistri i kyçjes së konfigurimit të pinit.

Mënyrat e funksionimit të pinit GPIO

Mënyrat e funksionimit të kunjave individuale përcaktohen nga një kombinim bitesh MODEy Dhe CNFy regjistrat GPIOx_CRL Dhe GPIOx_CRH(në tekstin e mëtejmë: x është emri i portit, y është numri i bitit të portit).

GPIOx_CRL- Regjistri i konfigurimit të pinit 0…7 porte x:

Struktura e regjistrit GPIOx_CRH strukturë të ngjashme GPIOx_CRL dhe është projektuar për të kontrolluar mënyrat e funksionimit të kunjave kryesore të portit (bitet 8…15).

Bitët MODEy të regjistrave të specifikuar përcaktojnë drejtimin e daljes dhe kufirin e shpejtësisë së ndërrimit në modalitetin e daljes:

  • MODEy = 00: Modaliteti i hyrjes (gjendja pas rivendosjes);
  • MODEy = 01: Modaliteti i daljes, shpejtësia maksimale – 10 MHz;
  • MODEy = 10: Modaliteti i daljes, shpejtësia maksimale – 2 MHz;
  • MODEy = 11: Modaliteti i daljes, shpejtësia maksimale - 50 MHz.

Bitët CNF specifikojnë konfigurimin e fazave të daljes së kunjave përkatëse:

në modalitetin e hyrjes:

  • CNFy = 00: Hyrja analoge;
  • CNFy = 01: Hyrja në gjendjen e tretë (gjendja pas rivendosjes);
  • CNFy = 10: Hyrja me rezistencë tërheqëse (nëse PxODR=1) ose pull-down (nëse PxODR=0);
  • CNFy = 11: Rezervuar.

në modalitetin e daljes:

  • CNFy = 00: Dalje shtytje-tërheqje për qëllime të përgjithshme;
  • CNFy = 01: Prodhimi i kullimit të hapur për qëllime të përgjithshme;
  • CNFy = 10: Push-pull output me funksion alternativ;
  • CNFy = 11: Hapni daljen e kullimit me funksion alternativ.

Për të rritur imunitetin ndaj zhurmës, të gjithë buferët e hyrjes përmbajnë shkas të Schmidt. Një pjesë e konkluzioneve STM32, të pajisura me dioda mbrojtëse të lidhura me autobusin e përbashkët dhe autobusin e energjisë, janë shënuar në fletën e të dhënave si FT (5V tolerant) - i pajtueshëm me një tension prej 5 volt.

Mbrojtja e biteve të konfigurimit të GPIO

Për të mbrojtur bitet në regjistrat e konfigurimit nga shkrimi i paautorizuar në STM32 ofrohet një regjistër i kyçjes së cilësimeve GPIOx_LCKR
GPIOx_LCKR- Regjistri i kyçjes së cilësimeve të daljes së portit:

Për të mbrojtur cilësimet e një pine porti individual, duhet të vendoset biti përkatës LCKy. Pastaj kryeni regjistrimin vijues në kategori LCKK vlerat "1" - "0" - "1" dhe dy operacione të leximit të regjistrit LCKR, i cili në rast bllokimi të suksesshëm do të japë për bitin LCKK vlerat "0" dhe "1".

Mbrojtja e pjesëve të cilësimeve do të mbetet në fuqi deri në rindezjen tjetër të mikrokontrolluesit.

Skedari i përkufizimit periferik mikrokontrolluesitSTM32 përcakton grupe të veçanta regjistrash të bashkuar nga një qëllim i përbashkët funksional (përfshirë GPIO), si struktura të gjuhës C, dhe vetë regjistrat si elementë të kësaj strukture.

STM32 - nga e para në RTOS. 2: Kohëmatësi dhe ndërprerjet

Për shembull:

GPIOC->BSRR– Vendosja/rivendosja e portës GPIOC e regjistrit BSRR.
Le të përdorim përkufizimet nga skedari stm32f10x.h për të ilustruar se si të punohet me regjistrat I/O të mikrokontrolluesit STM32F100RB instaluar në kompletin e starterit STM32VLDISCOVERY:

Shkruani dhe lexoni GPIO

Portat e hyrjes janë të destinuara për shkrim dhe lexim GPIOx_IDR dhe ditë pushimi GPIOx_ODR regjistrat e të dhënave.

Shkruani në regjistrin e daljes ODR porta e konfiguruar për dalje, vendos nivelet e daljes së të gjitha biteve të portës në përputhje me vlerën e regjistruar. Nëse një pin është konfiguruar si një hyrje tërheqëse, gjendja e bitit përkatës të regjistrit ODR aktivizon tërheqjen e daljes në autobusin e fuqisë (pull-up, ODR=1) ose autobus i përbashkët mikrokontrollues (pull-down, ODR=0).

Lexoni regjistrimin IDR kthen vlerën e gjendjes së kunjave të mikrokontrolluesit të konfiguruar si hyrje:

Rivendosja dhe vendosja e biteve të portit

Për rivendosjen atomike dhe vendosjen e biteve GPIO në mikrokontrolluesit STM32 regjistri është menduar GPIOx_BSRR. Tradicionale për arkitekturë ARM një metodë e menaxhimit të biteve të regjistrit që nuk kërkon përdorimin e një operacioni tip "lexo-modifiko-shkruaj" ju lejon të vendosni dhe rivendosni bitet e portit duke shkruar thjesht një në bitet e vendosura BS (BitSet) dhe rivendosni BR (BitReset) regjistrohen BSRR. Në këtë rast, shkrimi i zero biteve në regjistër nuk ndikon në gjendjen e kunjave përkatëse.

GPIOx_BSRR– regjistrohu për rivendosjen dhe vendosjen e biteve të portit:

Funksionet alternative GPIO dhe ricaktimin e tyre (rihartë)
Pothuajse të gjitha qarqet e jashtme për qëllime të veçanta STM32(përfshirë kabllot për lidhjen e rezonatorëve të kuarcit, JTAG/SWD dhe kështu me radhë) mund të aktivizohen në kunjat përkatëse të mikrokontrolluesit, ose të çaktivizohen prej tyre për të lejuar përdorimin e tyre si kunja për qëllime të përgjithshme. Zgjedhja e një funksioni alternativ të daljes kryhet duke përdorur regjistra me prefiksin "AFIO”_.
Përveç kësaj, regjistrat AFIO _ ju lejon të zgjidhni disa opsione paraqitjeje funksione të veçanta në kunjat e mikrokontrolluesit. Kjo vlen veçanërisht për daljet e ndërfaqeve të komunikimit, kohëmatësit (regjistrat AFIO_MAPR), kunjat e ndërprerjes së jashtme (regjistrat AFIO_EXTICR) etj.

Shikoni dokumentet për më shumë detaje "Manual referencë" në nëngrupin përkatës të mikrokontrolluesve.

Projektet për këtë artikull:

  1. µVizioni 4.13a-> STM32GPIO_emcu_uV
  2. IAR ARM 6.0-> STM32GPIO_emcu_iar
  3. IAR ARM 6.21-> STM32GPIO_emcu_iar_V6.21

Për ngarje GPIOSTM32 Ju mund të përdorni makro të shkruara si një alternativë ndaj bibliotekave ST që janë larg nga optimale, sipas shumë njerëzve: gpio_emcu.h

Material shtesë:

  1. Manuali i referencës STM32F10xxx. Udhëzuesi i ndihmës zhvilluesi
  2. Manuali i referencës STM32F100xx. Udhëzuesi i ndihmës së zhvilluesit
  3. Fleta e të dhënave STM32F105xx, STM32F107xx
  4. Fleta e të dhënave STM32F100x4, STM32F100x6, STM32F100x8, STM32F100xB
  5. Udhëzues për krijimin e projekteve për STM32DISCOVERY në IAR
  6. Udhëzues për krijimin e projekteve për STM32DISCOVERY në MDK-ARM, uVision

Pjesë të tjera

  1. (kjo pjese) Programimi STM32. Pjesa 1. Portat hyrëse/dalëse GPIO, STM32
  2. Programimi STM32. Pjesa 2. Sistemi i orës STM32
  3. Programimi STM32. Pjesa 3. Sistemi i ndërprerjes
  4. Programimi STM32. Pjesa 4. Ndërprerjet e jashtme EXTI

Kohëmatësit bazë në STM32

Kohëmatësit janë një periferi e tillë e kontrolluesit STM32 që na lejojnë të numërojmë me shumë saktësi intervalet kohore. Ky është ndoshta një nga funksionet më të rëndësishme dhe më të përdorura, por ka edhe të tjerë. Duhet të fillojmë me faktin se në kontrollorët STM32 ka kohëmatës me shkallë të ndryshme freskie. Më të thjeshtat janë bazëkohëmatës . Ato janë të mira sepse janë shumë të lehta për t'u konfiguruar dhe kontrolluar duke përdorur një minimum regjistrash. Gjithçka që ata mund të bëjnë është të numërojnë intervalet kohore dhe të gjenerojnë ndërprerje kur të arrijë kohëmatësi vlera e vendosur. Grupi tjetër ( kohëmatës për qëllime të përgjithshme ) janë shumë më të ftohta se i pari, mund të gjenerojnë PWM, mund të numërojnë pulset që arrijnë në këmbë të caktuara, mund të lidhni një kodues, etj. Dhe kohëmatësi më i lezetshëm është kohëmatës me kontroll të avancuar , mendoj se nuk do ta përdor për një kohë shumë të gjatë pasi nuk kam nevojë ta menaxhoj ende motor elektrik trefazor. Duhet të filloni të njiheni me kohëmatësit me diçka më të thjeshtë; vendosa të marr kohëmatësit Basic. Detyra që i vendosa vetes: Bëj kohëmatësin të gjenerojë ndërprerje çdo sekondë.

Para së gjithash, do të vërej se kohëmatësit bazë (TIM6 dhe TIM7) janë bashkangjitur në autobusin APB1, kështu që nëse frekuenca e pulsimit të orës në të ndryshon, kohëmatësit do të fillojnë të trokasin më shpejt ose më ngadalë. Nëse nuk ndryshoni asgjë në cilësimet e orës dhe i lini ato në parazgjedhje, atëherë frekuenca APB1është 24 MHz, me kusht që një kuarc i jashtëm të lidhet me një frekuencë prej 8 MHz. Në përgjithësi, sistemi i orës së STM32 është shumë i ndërlikuar dhe unë do të përpiqem të shkruaj një postim të veçantë në lidhje me të siç duhet. Tani për tani, ne do të përdorim vetëm cilësimet e orës që përcaktohen nga kodi i krijuar automatikisht nga CooCox. Vlen të filloni me regjistrin më të rëndësishëm - TIMx_CNT(në tekstin e mëtejmë x është numri i kohëmatësit bazë 6 ose 7). Ky është një regjistër numërimi 16-bitësh që merret drejtpërdrejt me numërimin e kohës. Çdo herë nga autobusi APB1 vjen një puls i orës, përmbajtja e këtij regjistri rritet me një. Kur regjistri tejmbush, gjithçka fillon nga e para. Në frekuencën tonë të paracaktuar të autobusit APB1, kohëmatësi shënon 24 milionë herë në një sekondë! Kjo është shumë, dhe për këtë arsye kohëmatësi ka një parashkallëzues, të cilin mund ta kontrollojmë duke përdorur një regjistër TIMx_PSC. Duke shkruar vlerën 24000-1 në të, ne do të detyrojmë regjistrin e numërimit TIMx_CNT rrisni vlerën e tij çdo milisekondë (Frekuenca APB1 pjesëtojeni me numrin në regjistrin parashkallëzues dhe merrni sa herë në sekondë rritet numëruesi). Njësia duhet të zbritet sepse nëse ka një zero në regjistër, kjo do të thotë se pjesëtuesi me një është i ndezur. Tani, kur regjistri i numërimit arrin në 1000, mund të themi patjetër se ka kaluar saktësisht një sekondë! Pse tani të anketoni regjistrin e numërimit dhe të prisni derisa të shfaqet 1000? Kjo nuk është metoda jonë, sepse mund të përdorim ndërprerje! Por problemi është se ne kemi vetëm një ndërprerje dhe kjo ndodh kur numëruesi shkon në zero. Në mënyrë që numëruesi të rivendoset përpara afatit, dhe jo kur të arrijë 0xFFFF, përdoret regjistri TIMx_ARR. Ne shkruajmë në të numrin në të cilin duhet të numërohet regjistri TIMx_CNT para se të shkojë në zero. Nëse duam që ndërprerja të ndodhë një herë në sekondë, atëherë duhet të shkruajmë 1000. Për sa i përket kohës së drejtpërdrejtë, kjo është e gjitha, por vetë kohëmatësi nuk do të fillojë të shënojë. Duhet të aktivizohet duke vendosur bitin CEN në regjistër TIMx_CR1. Ky bit lejon që numërimi mbrapsht të fillojë, kështu që nëse rivendoset, numërimi mbrapsht do të ndalet (C.O. juaj).

Kohëmatësi në STM32. Vendosja e kohëmatësit bazë

Ka pjesë të tjera në regjistër, por ato nuk janë veçanërisht interesante për ne. Por ne jemi të interesuar për një grimë tjetër, por tashmë në regjistër TIMx_DIER. Quhet UIE, Duke e vendosur atë, ne lejojmë kohëmatësin të gjenerojë ndërprerje kur regjistri i numërimit rivendoset. Kjo është e gjitha, nuk është edhe më e komplikuar se në disa AVR. Pra, një përmbledhje e vogël: Për të përdorur kohëmatësin bazë që ju nevojitet:

  1. Vendosni një parashkallëzues në mënyrë që kohëmatësi të mos shënojë shpejt ( TIMx_PSC)
  2. Cakto kufirin në të cilin duhet të arrijë kohëmatësi përpara se të rivendoset ( TIMx_ARR)
  3. Aktivizo numërimin e biteve CEN në regjistër TIMx_CR1
  4. Aktivizo ndërprerjen e tejmbushjes së bitit UIE në regjistër TIMx_DIER

Këtu është një sekuencë e thjeshtë. Dhe tani është koha për të hequr e dini se çfarë dhe të përpiqeni t'i pulsoni këto LED fatkeqe për të miliontën herë, por me ndihmën e një kohëmatës :)

#include "stm32f10x.h" #include "stm32f10x_gpio.h" #include "stm32f10x_rcc.h" int main() ( GPIO_InitTypeDef PORT; //Aktivizo portin C dhe kohëmatësin 6 RCC_APB2PeriphCPhP_APB2PeriphCPhP_Cm); APB1Peri phClockCmd(RCC_APB1Periph_TIM6,ENABLE ) ; // Konfiguro këmbët me LED për daljen PORT.GPIO_Pin = (GPIO_Pin_9 | GPIO_Pin_8); PORT.GPIO_Mode = GPIO_Mode_Out_PP; PORT.GPIO_Speed ​​= GPIO_Speed ​​= GPIO_Speed_2_MHz; 00 - 1; // Konfiguro ndarësin që kohëmatësi të shënojë 1000 herë në sekondë TIM6->ARR = 1000; // Kështu që ndërprerja të ndodhë një herë në sekondë TIM6->DIER |= TIM_DIER_UIE; //aktivizo ndërprerjen nga kohëmatësi TIM6-> CR1 |= TIM_CR1_CEN; // Fillo numërimin! NVIC_EnableIRQ(TIM6_DAC_IRQn); //Aktivizo ndërprerjen e TIM6_DAC_IRQn ndërsa(1) ( //Programi nuk bën asgjë në një cikli bosh) ) // TIM6_DAC mbajtësi i ndërprerjeve është i pavlefshëm (void TIMQ6) >SR &= ~TIM_SR_UIF; //Rivendosni flamurin UIF GPIOC->ODR^=(GPIO_Pin_9 | GPIO_Pin_8); //Inverto gjendjen e LED-ve)

Vlen të shtoni një shënim të vogël në mbajtësin e ndërprerjeve. Fakti është se përdoret nga dy blloqe periferike menjëherë: timer 6 dhe DAC. Kjo do të thotë që nëse shkruani një program që lejon ndërprerje nga të dyja këto pajisje periferike, atëherë në trupin e mbajtësit duhet të kontrolloni se cili prej tyre shkaktoi ndërprerjen. Në rastin tonë, unë nuk e bëra këtë, pasi nuk mund të ketë ndërprerje nga DAC. Nuk është i konfiguruar dhe ndërprerjet janë çaktivizuar si parazgjedhje. Herën tjetër do të shikojmë kohëmatësit për qëllime të përgjithshme dhe aplikimet e tyre praktike.

Gjenerimi i PWM në STM32

Në artikullin e mëparshëm në lidhje me kohëmatësit bazë, ne ndezëm edhe një herë LED, por këtë herë do të shkojmë shumë më tej dhe do të përpiqemi të kuptojmë se si ta bëjmë kontrolluesin STM32 të gjenerojë PWM. Për ta bërë këtë, do të duhet të përdorim një nga kohëmatësit me qëllim të përgjithshëm, sepse ata kanë gjithçka që na nevojitet për këtë. I gjithë funksionaliteti tjetër i këtyre kohëmatësve është sigurisht mbresëlënës, por në praktikën time nuk ka qenë ende i dobishëm. Megjithëse është e mundur që në të ardhmen të më duhen veçori të tilla të dobishme si funksioni i numërimit të pulseve të jashtme dhe aftësia për të përpunuar rrotullimet e koduesit në pajisje. Por tani për tani le të merremi me PWM. Ekziston një qark i tillë i përbërë nga një kontrollues, tre rezistorë dhe RGB LED të cilën do ta menaxhojmë. Kontrolli është që të ndriçohet pa probleme dhe të shuhet çdo ngjyrë. Sigurisht, mund të merrni tre LED të ndryshëm nëse nuk ka RGB.

Ne e lidhëm LED-in me këto kunja për një arsye. Kohëmatësit për qëllime të përgjithshme mund të gjenerojnë PWM vetëm në kunja të caktuara. Meqenëse do të përdorim kohëmatësin 2, kemi në dispozicion 4 këmbë (PA0-PA3). Në mënyrë që kohëmatësi t'i përdorë ato, duhet ta aktivizoni këtë në dy vende: Konfiguro tre këmbë (PA1-PA3) si një dalje me një funksion alternativ dhe lejo që në cilësimet e kohëmatësit të tërheqë këto këmbë për të gjeneruar PWM. Për këtë na duhet një regjistër CCER

Nëse vendosni një nga pjesët e theksuara me blu në një, kohëmatësi do të lejohet të përdorë këmbën përkatëse për PWM. Diagrami tregon se duhet të vendosim bitet CC2E, CC3E Dhe CC4E. Tani duhet të konfigurojmë modalitetin PWM: Direkt ose i kundërt (nuk pretendoj se e kam terminologjinë të saktë). Dallimi është mjaft i dukshëm - me PWM të drejtpërdrejtë sesa numër më i madh në regjistrin e krahasimit, aq më i madh është cikli i punës PWM. Në rastin e PWM inverse, e kundërta është e vërtetë. Ne kemi shkruar zero në regjistrin e krahasimit - faktori i mbushjes është 100%.

Ne punojmë me kohëmatës të thjeshtë të zbulimit STM32 F4

Për të zgjedhur modalitetin, përdoren dy regjistra CCMR1 dhe CCMR2:

Deri në 8 bit janë ndarë për vendosjen e secilit kanal! Por për fat të mirë, ne jemi të interesuar vetëm për tre pjesë OCxM, të cilat i shënova me blu. Ajo që shënohet me gri janë të njëjtat pjesë, por me një emër tjetër, ato përdoren nëse kanali i kohëmatësit është në modalitetin e kapjes. Unë nuk do t'i konsideroj të gjitha kombinimet e biteve, pasi shumica e tyre nuk kanë asnjë lidhje me PWM. Na duhen vetëm dy kombinime bit:

PWM direkte

PWM inverse

Unë kam një LED RGB me një katodë të përbashkët dhe për këtë arsye përdor PWM inverse. Kështu, ne duhet të vendosim të tre bitet OCxM për tre kanalet në të cilat varen LED. Dhe kjo është e gjitha! Konfigurimi i PWM ka përfunduar, tani ju vetëm duhet të filloni kohëmatësin duke vendosur bitin CEN në regjistrin CR1. Për të kontrolluar ciklin e punës, thjesht shkruani një numër nga 0x0000 në 0xFFFF në regjistrat CCR x, Ku x numri i kanalit. Në fakt, kodi i mëposhtëm zbaton atë që ishte planifikuar në fillim të këtij artikulli: Rrit ndriçimin e LED-it dhe më pas e zvogëlon atë në zero dhe kalon në tjetrin.

Procesi përsëritet pafund, duket bukur :)

#include "stm32f10x.h" #include "stm32f10x_gpio.h" #include "stm32f10x_rcc.h" void delay(void) ( volatile uint32_t i; për (i=1; i != 0xF000; it++)); ( //Aktivizo portën A RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA , ENABLE); //Aktivizo kohëmatësin 2 RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE); GPIO_InitPeriph_GPIOA , ENABLE; _Pin = (GPIO_Pin_1 | GPIO_Pin_2 | GPIO_Pin_3); // Ne do të përdorim modaliteti alternativ jo normalja GPIO PORT.GPIO_Mode = GPIO_Mode_AF_PP; PORT.GPIO_Speed ​​= GPIO_Speed_2MHz; GPIO_Init(GPIOA, &PORT); //Lejo kohëmatësin të përdorë këmbët PA1,PA2,PA3 për PWM TIM2->CCER |= (TIM_CCER_CC2E|TIM_CCER_CC3E|TIM_CCER_CC4E); // Vendos PWM inverse për të tre kanalet. TIM2->CCMR1|=(TIM_CCMR1_OC2M_0| TIM_CCMR1_OC2M_1 | TIM_CCMR1_OC2M_2); TIM2->CCMR2|=(TIM_CCMR2_OC3M_0 | TIM_CCMR2_OC3M_1 | TIM_CCMR2_OC3M_2 | TIM_CCMR2_OC4M_0 | TIM_CCMR2_OC4M_1 | TIM_CCMR2_OC4M_2); //Filloni kohëmatësin! TIM2->CR1 |= TIM_CR1_CEN; //Pas kësaj ne i shkruajmë të dhënat në TIM2->CCRx - dhe ndriçimi i LED-ve ndryshon uint32_t pwm_arr=(0,0,6553,13107,19660,26214,32768, 39321,45875,52428,58); uint8_t i; ndërsa (1) (për (i=1;i<=11;i++) { TIM2->CCR3=pwm_arr[i]; vonesë (); ) për (i=11;i>=1;i—) ( TIM2->CCR3=pwm_arr[i]; vonesë(); ) për (i=1;i<=10;i++) { TIM2->CCR2=pwm_arr[i]; vonesë (); ) për (i=11;i>=1;i—) (TIM2->CCR2=pwm_arr[i]; vonesë(); ) për (i=1;i<=10;i++) { TIM2->CCR4=pwm_arr[i]; vonesë (); ) për (i=11;i>=1;i—) (TIM2->CCR4=pwm_arr[i]; vonesë(); ) ) )

Shpresoj se ky kod do t'ju ndihmojë të ekzekutoni PWM në STM32 dhe do të më ndihmojë të mos harroj atë që kam pirë duhan për pothuajse gjysmë dite. Pyetjet jo të vështira mund të shkruhen më poshtë.

Oshiloskop xhepi "Lori" në mikrokontrolluesin STM32F103

Maksim Kerimov
dhjetor 2016

Formulimi i problemit

Bëni një oshiloskop të thjeshtë xhepi me kosto minimale kohë dhe para.

Lista e komponentëve

  • Klon kinez i tabelës Maple Mini me një mikrokontrollues STM32F103C8T6.
  • Ekrani 1.8 TFT 128×160 SPI me drejtues ST7735.
  • Pesë rezistorë dhe dy kondensatorë (Fig. 3).
  • Rregullator linear i ulët i braktisjes AMS1117-3.3 (opsionale).
  • "Pskatore" me kapëse sondë - 2 copë.
  • Butoni është miniaturë, normalisht i hapur pa u kyçur, me një klikim.

Oriz. 1. Testimi i oshiloskopit. Vala sinusit u krijua nga një shpërthyes tingulli, për këtë arsye është e shkallëzuar.

Karakteristikat

7 vargje me vlera të ndarjes (qeliza): 7 µS, 28 µS, 113 µS, 559 µS, 2 mS, 10 mS, 20 mS.
Ndjeshmëria: 0,25 dhe 1,0 V/div.
Amplituda maksimale e sinjalit të hyrjes: 6 V.
Impedanca e hyrjes: 20 kΩ.
Fuqia: 4 bateri AA.
Konsumi aktual: 80 mA.

Çfarë sinjali të frekuencës mund të shihni?

Teorikisht, ju mund të shihni 477 kHz. Teorikisht, është e mundur të dalloni një gjarpërim nga një sharrë në frekuencat 350 kHz dhe më poshtë. Në praktikë, ju mund të vëzhgoni pak a shumë lehtësisht sinjale deri në 200 kHz. Madhësia e qelizës: 20 x 20 px.

"Frekuenca e fshirjes" e oshiloskopit tonë varet nga shpejtësia e ADC. Në STM32F103, gjerësia e bitit ADC është fikse dhe e barabartë me 12. Kjo është një herë e gjysmë më shumë se sa na nevojitet. Në STM32F407, për shembull, thellësia e bitit mund të reduktohet, gjë që do të zvogëlojë kohën e matjes. Por kjo është një histori tjetër me një buxhet tjetër.

Oriz. 2. Lidhja e ekranit.

Oriz. 3. Furnizimi me energji elektrike dhe qarku i hyrjes.

Ndarësi i tensionit R1-R2 përdoret për të kontrolluar nivelin e ngarkimit të baterisë. Ne te djathte këndi i sipërm ekran - ikona e baterisë, si në celular(jo në foto).

Nuk nevojitet gjithmonë një rregullator i jashtëm i tensionit. Pllaka e mikrokontrolluesit ka rregullatorin e vet 3,3 V 100 mA. Nëse e ndizni ekranin nga ai, ai do të nxehet. Në llojet e tjera të pllakave (me një lidhës të madh JTAG) ekziston vetëm AMS1117, për ta nuk nevojitet një i jashtëm. Disa ekrane kanë gjithashtu AMS1117 (dhe kërcyesin). Vendosni vetë.

Ka kuptim të instaloni një çelës energjie PD9-1 ose të ngjashëm në seri me bateritë.

Nëse dëshironi të rrisni madhësinë e rezistencës suaj, mund të shtoni një ndjekës op-amp jo-invertues në hyrje, i cili do t'ju lejojë të arrini një vlerë prej 1 MΩ ose më shumë. Op-amp duhet të furnizohet drejtpërdrejt nga bateritë me një tension prej 4.8 - 5.4 V.

Parimi i funksionimit

Gjysma e tekstit të programit është të gjitha llojet e inicializimeve. Parimi i funksionimit të një oshiloskopi dixhital është i thjeshtë dhe i dukshëm.

ADC bën një seri matjesh të vazhdueshme sekuenciale të nivelit të sinjalit. Vlerat që rezultojnë shtohen në kujtesë duke përdorur DMA. Çdo herë ne regjistrojmë kohën dhe përcaktojmë kohëzgjatjen e serisë së matjeve. Kështu e zbulojmë çmimin e pjesëtimit të boshtit kohor.

Duke analizuar vlerat e nivelit të sinjalit të regjistruar, ne kërkojmë ekstremin e parë, pas së cilës nxjerrim sinjalin në ekran. Pra, ne po përpiqemi të bëjmë një lloj sinkronizimi. Funksionon mirë në sinjale të qetë dhe është praktikisht i padobishëm në ato me brez të gjerë.

Ne e lëmë përdoruesin të shijojë foton për një sekondë, ndërsa butonin e anketojmë vetë. Një shtypje e shkurtër e butonit ndërron intervalet në një rreth. Shtypja e gjatë ndryshon ndjeshmërinë.

STM32 për fillestarët. Mësimi 3. Timers STM32.

Pastaj gjithçka përsëritet.

Unë përdor CooCox CoIDE për përpilim. Nuk e postova projektin Cox këtu sepse përmban rrugët absolute te skedarët. Është më e lehtë të krijosh një të re sesa të modifikosh të gjitha shtigjet. Pas krijimit të projektit, mos harroni të lidhni bibliotekat: RCC, GPIO, DMA, SPI, TIM, ADC.

E kam qepur duke përdorur programuesin-debugger ST-Link V2. Mund ta bëni pa të, përmes një përshtatësi USB-Serial.

Materialet e përdorura:
Projekt nga mjeshtri skocez Pingumacpenguin
Shfaqja e Adafruit
STM32 Ruajtja e të dhënave ADC duke përdorur DMA

Teksti i programit

  • kryesore.c
  • lcd7735.c - Ekrani dhe SPI. Inicializimi dhe funksionet.
  • vonesë.c - Numëruesi: inicializimi, funksionet e pauzës.
  • ADC.c - ADC dhe DMA.
  • font7x15.h - Font.

STM32 nga e para. Timers.

Mikrokontrolluesit STM32 kanë disa kohëmatës që mund të funksionojnë modulimi i gjerësisë së pulsit. Të gjithë kohëmatësit përveç kohëmatësve bazë (TIM6 dhe TIM7) e kanë këtë funksionalitet.

Kjo është e gjitha, kjo është e mjaftueshme për [Mikrokontrolluesit STM32 kanë disa kohëmatës që mund të funksionojnë në modulimin e gjerësisë së pulsit. Të gjithë kohëmatësit përveç kohëmatësve bazë (TIM6 dhe TIM7) e kanë këtë funksionalitet.

Unë do të jap një shembull të përdorimit të kohëmatësit TIM3 në këtë modalitet.

Kjo është e gjitha, kjo mjafton për](http://catethysis.ru/stm32-%e2%86%92-%d0%bf%d0%be%d1%80%d1%82%d1%8b-gpio /” STM32 → portet GPIO”) u shfaq një sinjal PWM. Si bëhet kjo?

Kohëmatësi merr impulse të orës nga autobusi APB, frekuenca e të cilit është gjysma e frekuencës bazë (24 MHz në rastin tonë) (lidhja me RCC), dhe ato kalojnë përmes parashkallëzuesit, të cilin e vendosëm në 10 - d.m.th. rezulton të jetë 1.2 MHz. Kohëmatësi është konfiguruar që të numërojë 1000 cikle të orës, pas së cilës merr një vlerë të re nga regjistri ARR, të cilën ne nuk e ndryshojmë - d.m.th. e njëjta 1000, kjo është periudha e sinjalit PWM. Në fillim të ciklit, kohëmatësi nxjerr "1" në dalje, dhe pas 200 ciklesh rivendoset në "0" - ky është cikli i detyrës PWM.

Ne shikuam punën me operacionet bit dhe numrat binarë, duke hedhur kështu themelin për shqyrtimin e një teme të re. Në këtë mësim do të shqyrtojmë një pyetje tjetër: çfarë janë regjistrat dhe si të punohet me ta?

Kujtesa dhe regjistrat

Një nga aftësitë më të rëndësishme që kërkohet kur punoni me mikrokontrollues është aftësia për të bashkëvepruar me regjistrat. Le ta kuptojmë vetë Çfarë është ajo?

Në përgjithësi, një regjistër është një lloj i veçantë memorie brenda një mikrokontrollues që përdoret për të kontrolluar procesorin dhe pajisjet periferike. Çdo regjistër në arkitekturën ARM është një qelizë memorie dhe ka një gjatësi prej 32 bit, ku çdo bit mund të përfaqësohet si një ndërprerës i vogël që kontrollon një ose një parametër tjetër të mikrokontrolluesit.

Secili prej regjistrave ka numrin e vet të serisë - adresën. Adresa e regjistrit tregohet nga një numër 32-bit i përfaqësuar në shënim heksadecimal. Duke shkruar në adresën e regjistrit një kombinim të caktuar të njësheve dhe zerove, të cilat zakonisht paraqiten në formë heksadecimal, konfigurohet dhe kontrollohet një ose një nyje tjetër në MK. Kujtojmë se në një program për të punuar me operacione bit, ne mund të përfaqësojmë një grup arbitrar të njësheve dhe zerove si një numër heksadecimal. Në përgjithësi, vlen të theksohet se ekzistojnë dy lloje regjistrash: regjistra për qëllime të përgjithshme dhe regjistra të veçantë. Të parat janë të vendosura brenda bërthamës MK, dhe të dytat janë pjesë e memories RAM.

Gjithashtu vlen të theksohet se Manuali i referencës, të cilin e shkarkuam në mësimin e parë, është një libër i madh referimi mbi regjistrat që gjenden në mikrokontrolluesin e synuar dhe biblioteka CMSIS na lejon të operojmë me emra simbolikë të regjistrave në vend të adresave numerike. Për shembull, në regjistër 0x40011018 mund t'i referohemi thjesht duke përdorur një emër simbolik GPIOC_BSSR. Ne do të shikojmë shembuj specifikë të konfigurimit gjatë analizës së programit tonë nga .

Pra, zakonisht struktura e regjistrit përshkruhet në formën e një tabele të vogël që tregon:

  1. Regjistroni emrat dhe përshkrimet e qëllimit të tij
  2. Regjistroni adresat ose zhvendosni nga adresa bazë
  3. Vlerat e paracaktuara pas rivendosjes
  4. Lloji i aksesit në qelizat e regjistrimit (lexo, shkruaj, lexo/shkruaj)
  5. Vlerat dhe përshkrimet e parametrave të biteve që do të shkruhen
Le të shohim një shembull të punës me regjistra në një situatë specifike për të marrë një kuptim të përgjithshëm të parimeve të konfigurimit të një mikrokontrollues.

Analiza e kodit nga mësimi i parë

Pra, le të kujtojmë problemin që zgjidhëm duke përdorur kodin e gatshëm të shembullit: na duhej të shkruanim një program që do të siguronte që dy LED në tabelën Discovery të ndizen në mënyrë alternative (ndoshta jo dy, nëse keni një version të ndryshëm të Discovery tabelë) me një interval kohor.

Le të hedhim një vështrim tjetër në kodin e programit që kemi përdorur për të bërë kërcimin tonë MK me dy këmbët në të cilat ndodhen LED-et tona:

Kodi kryesor.c

/* Skedari i kokës për familjen tonë të mikrokontrolluesve */ #include "stm32f0xx.h" /* Trupi i programit kryesor */ int main(void) ( /* Aktivizo kronologjinë në portën GPIO */ RCC->AHBENR |= RCC_AHBENR_GPIOCEN ; /* Konfiguro mënyrën e funksionimit të portave PC8 dhe PC9 në Output*/ GPIOC ->MODER = 0x50000; /* Vendosni llojin e daljes në modalitetin Push-Pull */ GPIOC->OTYPER = 0; /* Vendosni shpejtësinë e portit në Low */ GPIOC->OSPEEDR = 0; ndërsa (1) ( /* Ndizni LED PC8, fikni PC9 */ GPIOC-> ODR = 0x100; për (int i=0; i<500000; i++){} // Искусственная задержка /* Зажигаем светодиод PC9, гасим PC8 */ GPIOC->ODR = 0x200; për (int i=0; i<500000; i++){} // Искусственная задержка } }


Para së gjithash, kur punojmë me STM32, edhe për një detyrë kaq të thjeshtë si ndezja dhe fikja e një LED, së pari duhet t'i përgjigjemi një numri pyetjesh:
  1. Si të konfigurojmë kunjat e portit GPIO që na duhen në mënyrë që të mund të ndezim LED?
  2. Si të ndizni dhe fikni LED?
Le t'u përgjigjemi atyre me radhë.

Ku janë të lidhura LED-et tona? Në cilën kunj të mikrokontrolluesit?

Për të parë se ku është gjithçka në tabelën Discovery, dhe në veçanti, LED-të që na duhen, duhet të hapim skedarin Skematik, qoftë atë që kemi shkarkuar nga faqja e internetit ST, ose direkt nga Keil:


Duke hapur skemën, do të shohim një diagram të gjithçkaje që është në tabelë - një diagram ST-Link, instalime elektrike të të gjitha pajisjeve periferike dhe shumë më tepër. Për momentin ne jemi të interesuar për dy LED, ne jemi duke kërkuar për përcaktimin e tyre:


Siç mund ta shohim, LED-et tona janë të lidhura me portën GPIOC në kunjat 8 dhe 9.

Si të aktivizoni akordimin në portën e dëshiruar GPIO?

Në përgjithësi, çdo punë me pajisje periferike në mikrokontrolluesit STM32 zbret në një sekuencë standarde veprimesh:
  1. Aktivizo akordimin e modulit periferik përkatës. Kjo bëhet përmes regjistrit RCC duke dhënë një sinjal të orës direkt nga autobusi në të cilin ndodhet ky modul. Si parazgjedhje, fiksimi i të gjitha pajisjeve periferike është i çaktivizuar për të minimizuar konsumin e energjisë.
  2. Konfigurimi përmes regjistrave të kontrollit, duke ndryshuar parametrat specifikë për një pajisje të veçantë periferike
  3. Nisja dhe përdorimi i drejtpërdrejtë i rezultateve të modulit
Kjo do të thotë, për të filluar, ne duhet të fillojmë të kontrollojmë në portën GPIOC. Kjo bëhet drejtpërdrejt duke hyrë në regjistrin RCC, i cili është përgjegjës për të kontrolluar çdo gjë dhe për të ndezur sinjalin e orës nga autobusi me të cilin është lidhur porti ynë GPIO.

Kujdes! Ne do ta shqyrtojmë pyetjen në lidhje me sistemin e orës, konfigurimin dhe përdorimin e tij në detaje në një artikull të veçantë.

Zbuloni se me cilin autobus është lidhur porta jonë GPIOC, mund ta gjeni në Fletën e të dhënave për MK-në tonë në seksionin Hartimi i kujtesës në tabelën 16. Adresat kufitare të regjistrit periferik STM32F051xx.


Siç mund ta keni vënë re tashmë, goma që na nevojitet quhet AHB2. Për t'u njohur më shumë me regjistrin në të cilin është aktivizuar ora për portin GPIO që na nevojitet në autobusin AHB, duhet të shkojmë në seksionin përkatës në Manualin e Referencës. Me emrin e regjistrave mund të përcaktojmë atë që na nevojitet:


Shkojmë në këtë pikë dhe shohim regjistrin tonë 32-bit, adresën e kompensimit të tij, vlerën e paracaktuar, mënyrën e hyrjes në regjistër dhe një listë të asaj për çfarë është përgjegjës çdo bit në regjistër.


Ne shikojmë tabelën dhe shohim diçka që i ngjan opsioneve për aktivizimin e klockimit në portet GPIO. Le të shkojmë te përshkrimi dhe të gjejmë opsionin që na nevojitet:


Në përputhje me rrethanat, nëse vendosim bitin 19 në "1", kjo do të mundësojë klockimin në portën I/O C - domethënë në GPIOC tonë. Përveç kësaj, ne duhet të aktivizojmë një bit nga grupi veçmas, pa ndikuar në pjesën tjetër sepse ne nuk duhet të ndërhyjmë ose të ndryshojmë në mënyrë të panevojshme cilësime të tjera.

Bazuar në materialet e mësimit të mëparshëm, ne e dimë se për të vendosur një bit të caktuar, duhet të përdorim operacionin logjik "OR" për të shtuar vlerën e regjistrit aktual me një maskë që përmban bitet që duhen ndezur. Për shembull, le të shtojmë vlerën e paracaktuar të regjistrit RCC->AHBENR, d.m.th. 0x14 dhe numri 0x80000 do të mundësojë klockimin e GPIOC duke vendosur bitin 19:

Si mund ta bëjmë këtë nga një program? Është mjaft e thjeshtë. Në këtë rast kemi dy mundësi:

  1. Shkrimi në një regjistër drejtpërdrejt i vlerës numerike të një regjistri direkt përmes adresës së tij.
  2. Konfigurimi duke përdorur bibliotekën CMSIS
Nuk ka probleme të veçanta me shkrimin e një vlere në një regjistër drejtpërdrejt, por ka disa të meta të rëndësishme. Së pari, një kod i tillë bëhet i palexueshëm dhe së dyti, ne nuk mund të përcaktojmë menjëherë se cilit regjistër i referohet një adresë e caktuar në memorie.

Kjo do të thotë, ne mund t'i qasemi adresave të regjistrave direkt me adresë dhe të shkruajmë këtë:

IO uint32_t * adresa_regjistrit = (uint32_t *) 0x40021014U; // Adresa e regjistrit tonë në memorie *(__IO uint32_t *)register_adresa |= 0x80000; // Aktivizoni 19 bit me parametrin tonë
Opsioni i dytë më duket më tërheqës, sepse... Biblioteka CMSIS është e organizuar në atë mënyrë që një regjistër mund të aksesohet duke përdorur vetëm emrin e tij. Parapërpunuesi, kur përpunon tekstin e programit përpara kompilimit, do të zëvendësojë automatikisht të gjitha vlerat dixhitale të adresës së regjistrit. Le ta shohim këtë pyetje pak më në detaje.

Unë sugjeroj hapjen e projektit tonë, të cilin e bëmë në mësimin e parë, ose shkarkojmë një të përgatitur paraprakisht nga këtu dhe fshijmë të gjitha përmbajtjet e programit, duke lënë vetëm skedarin e përfshirë të kokës, funksionin kryesor () dhe udhëzimet për aktivizimin e klockimit ( do të na duhet për një analizë të hollësishme të kodit).

Kodi ynë do të duket si ky:

/* Skedari i kokës për familjen tonë të mikrokontrolluesve */ #include "stm32f0xx.h" /* Trupi kryesor i programit */ int main(void) ( /* Aktivizo kronologjinë në portën GPIO */ RCC->AHBENR|=RCC_AHBENR_GPIOCEN; )
Le të gërmojmë thellë në bibliotekën CMSIS për t'u njohur.

Për të shkuar shpejt në vendin ku deklarohet kjo ose ajo konstante ose ndryshore, Keil zbaton një funksion të përshtatshëm. Ne klikojmë me të djathtën në konstantën që na nevojitet, për shembull, në RCC:


Dhe ne transportohemi në thellësitë e bibliotekës CMSIS, në të cilën do të shohim se të gjithë regjistrat e disponueshëm për kontroll programatik kanë formën e strukturave TypeDef, duke përfshirë RCC-në tonë:


Duke dështuar në këtë mënyrë në RCC_TypeDef, do të shohim një strukturë që përshkruan të gjitha fushat e regjistrit tonë:


Prandaj, ne mund të hyjmë në mënyrë të sigurtë në regjistrin që na nevojitet me një hyrje si PERIPH_MODULE->REGJISTROHU dhe i caktoni një vlerë specifike.

Përveç përcaktimit kujtimor të regjistrave, ekzistojnë edhe emërtime për bite specifike. Nëse nuk arrijmë të deklarojmë parametrin RCC_AHBENR_GPIOCEN nga programi ynë, do të shohim gjithashtu deklarimin e të gjithë parametrave:


Kështu, duke përdorur bibliotekën CMSIS, marrim një hyrje koncize dhe të lexueshme të parametrit që na nevojitet në një regjistër, përmes cilësimit të të cilit fillojmë të frekuentojmë portin që na nevojitet:

/* Aktivizo clocking në portën GPIO */ RCC->AHBENR|=RCC_AHBENR_GPIOCEN;
Si detyrë: Përcaktoni duke përdorur aftësitë e Keil se si adresa e regjistrit RCC->AHBENR doli të ishte 0x40021014.

Si të konfigurojmë kunjat GPIO që na duhen në mënyrë që të mund të ndezim LED?

Pra, ne e dimë se LED-të që na duhen janë të lidhura me portën GPIOC me kunjat PC8 dhe PC9. Ne duhet t'i vendosim ato në një mënyrë të tillë që LED të ndizet. Unë do të doja të bëja menjëherë një rezervim që ne do t'i shikojmë portet GPIO në mënyrë më të detajuar në një artikull tjetër dhe këtu do të përqendrohemi veçanërisht në punën me regjistrat.

Para së gjithash, duhet të kalojmë mënyrën e funksionimit të kunjave PC8 dhe PC9 në modalitetin Output. Parametrat e mbetur të portit mund të lihen si parazgjedhje. Shkoni te seksioni Manuali i referencës 9. I/O për qëllime të përgjithshme (GPIO) dhe hapni artikullin përgjegjës për mënyrën e funksionimit të kunjave të portit GPIO dhe shikoni që regjistri MODER është përgjegjës për këtë parametër:


Duke gjykuar nga përshkrimi, për të vendosur kunjat PC8 dhe PC9 në modalitetin Output, duhet të shkruajmë 01 në fushat përkatëse të regjistrit GPIOC.

Kjo mund të bëhet nëpërmjet instalimit të drejtpërdrejtë duke përdorur vlera numerike:


Ose duke përdorur përkufizime nga biblioteka:

/* Aktivizo clocking në portën GPIO */ GPIOC->MODER |= GPIO_MODER_MODER8_0 | GPIO_MODER_MODER9_0;
Pas këtij udhëzimi, kunjat tona PC8 dhe PC9 do të kalojnë në modalitetin e daljes.

Si të ndizni LED?

Nëse i kushtojmë vëmendje listës së regjistrave të disponueshëm për kontrollin e portit GPIO, mund të shohim regjistrin ODR:


Secili prej biteve përkatës është përgjegjës për një nga kunjat e portit. Më poshtë mund ta shihni strukturën e saj:


Për të siguruar një ndryshim të alternuar të gjendjeve LED, është e nevojshme të ndizni/fikni bitet 8 dhe 9 me një interval të caktuar kohor. Kjo do të thotë, caktoni në mënyrë alternative regjistrit vlerën 0x100 dhe 0x200.

Ne mund ta bëjmë këtë duke caktuar drejtpërdrejt vlera në regjistër:

GPIOC->ODR = 0x100; // Ndizni PC8, fikni PC9 GPIOC->ODR = 0x200; // Ndizni PC9, fikni PC8
Mund të përdorim përkufizime nga biblioteka:

GPIOC->ODR = GPIO_ODR_8; // Ndiz PC8, fik PC9 GPIOC->ODR = GPIO_ODR_9; // Ndizni PC9, fikni PC8
Por meqenëse mikrokontrolluesi funksionon shumë shpejt, ne nuk do të vërejmë ndryshime në gjendjet e LED-ve dhe vizualisht do të duket se ato janë të ndezura vazhdimisht. Në mënyrë që ata në të vërtetë të pulsojnë në mënyrë alternative, ne do të prezantojmë një vonesë artificiale në formën e një cikli që do të pushtojë MK me llogaritje të padobishme për ca kohë. Do të merrni kodin e mëposhtëm:

/* Ndizni LED PC8, fikni PC9 */ GPIOC->ODR = GPIO_ODR_8; për (int i=0; i<500000; i++){} // Искусственная задержка /* Зажигаем светодиод PC9, гасим PC8 */ GPIOC->ODR = GPIO_ODR_9; për (int i=0; i<500000; i++){} // Искусственная задержка
Këtu mund të përfundojmë njohjen tonë fillestare me regjistrat dhe metodat e punës me ta.

Kontrollimi i rezultateve të kodit tonë

Një shtesë e këndshme e vogël në fund të artikullit: Keil ka një mjet të shkëlqyer Debug me të cilin mund të kalojmë programin tonë dhe të shohim gjendjen aktuale të çdo njësie periferike. Për ta bërë këtë, pasi të kemi shkarkuar firmuerin pas përpilimit, mund të klikojmë butonin Start Debug Session:

Portat hyrëse/dalëse GPIO në STM32 kanë 16 linja, secila prej të cilave mund të konfigurohet sipas nevojës. Mbështet hyrjen dixhitale, daljen dixhitale, hyrjen e ndërprerjes së jashtme dhe funksionet I/O të moduleve të tjera të mikrokontrolluesve. Programimi i STM32 për të punuar me GPIO bazohet në përdorimin e regjistrave të konfigurimit, leximit, shkrimit, konfigurimit dhe aksesit në bit.

Regjistrat e konfigurimit të portit.

Regjistri i konfigurimit të portit është i ulët (GPIOx_CRL) (x=A..G)

Regjistri i konfigurimit të portit është i lartë (GPIOx_CRH) (x=A..G)

Për të programuar mënyrat e funksionimit të portave I/O STM32, përdoren dy regjistra 32-bitësh për çdo GPIO. Ato ju lejojnë të konfiguroni në mënyrë arbitrare mënyrën e funksionimit të çdo linje individuale. Regjistri GPIOx_CRL është përgjegjës për linjat me numra nga 0 në 7, GPIOx_CRH - për rreshtat 8-15. Për secilën prej tyre, regjistri ka dy fusha me dy bit CNFy dhe MODEy. E para përcakton llojin e funksionimit të linjës, e dyta - drejtimin e shkëmbimit përgjatë linjës. të gjithë bitat lexohen/shkruhen.

Regjistrohu GPIOx_CRL

Bitregjistrohen

Fusha

Linja hyrëse/dalëse

Regjistrohu pak

Fusha

Linja hyrëse/prodhimit

Regjistri GPIOX_CRH

Regjistrohu pak

Fusha

Linja hyrëse/dalëse

Regjistrohu pak

Fusha

Linja hyrëse/dalëse

Fusha MODEy mund të marrë vlerat e mëposhtme:

  • 00 - linja përdoret për hyrje. Kjo gjendje vendoset pas rivendosjes.
  • 01 – Linja funksionon si një dalje, me një frekuencë maksimale komutimi prej 10 MHz
  • Linja 10 funksionon si dalje, me një frekuencë komutimi maksimal prej 20 MHz
  • 11 – Linja funksionon si një dalje, me një frekuencë komutimi maksimal prej 50 MHz

Fusha CNFy varet nga drejtimi i transmetimit. Kur funksionon si hyrje (MODEy=0), disponohen gjendjet e mëposhtme:

  • 00 - hyrje analoge.
  • 01 – hyrje në gjendjen e tretë. (Cilësohet pas rivendosjes).
  • 10 – hyrje me rezistencë tërheqëse
  • 11 – e rezervuar për përdorim në të ardhmen.

Kur vepron si një dalje (MODEy>0), fusha CNFy mund të ketë gjendjet e mëposhtme:

  • 00 – dalje dixhitale
  • 01 – dalje dixhitale me kullim të hapur
  • 10 – dalje dixhitale e lidhur me njësi të specializuara
  • 11 – dalje dixhitale e lidhur me njësi të specializuara të kullimit të hapur

Regjistri i mbrojtjes së cilësimeve

Regjistri i kyçjes së konfigurimit të portit (GPIOx_LCKR) (x=A..G)

Fusha

Fusha

Vendosja e bitit të kyçur në GPIOx_LCKR Për ta bërë të pamundur ndryshimin e cilësimeve të portit në mikrokontrolluesit STM32, përdoret regjistri GPIOx_LCKR. 15-bitët e tij të ulët janë përgjegjës për linjat përkatëse të portit I/O. Biti 16 i vendosur në 1 mundëson çaktivizimin e ndryshimeve të cilësimeve. të gjithë bitat lexohen/shkruhen. Për ta bërë jetën më të vështirë për përdoruesit ;-) , përdoret një algoritëm i veçantë për instalimin e mbrojtjes. Nëse zbatohet, atëherë ndryshimi tjetër i konfigurimit është i disponueshëm vetëm pas një rivendosjeje. Algoritmi për instalimin e mbrojtjes është si më poshtë:

  1. Cakto bitin 16 GPIOx_LCKR.
  2. Rivendos bitin 16 GPIOx_LCKR.
  3. Cakto bitin 16 GPIOx_LCKR.
  4. Lexoni GPIOx_LCKR
  5. Rilexoni GPIOx_LCKR

Regjistrat e statusit të linjës

Ndryshe nga modelet e zakonshme 8-bitësh, STM32 ka disa regjistra përgjegjës për gjendjen e linjave të portit I/O. Në mënyrë konvencionale, ato ndahen në dy grupe - regjistrat e porteve dhe regjistrat për vendosjen e biteve individuale.

Regjistri i daljes së portit I/O

Regjistri i të dhënave të daljes së portit (GPIOx_ODR) (x=A..G)

Fusha

Fusha

Ky regjistër ka një gjerësi prej 32, por përdoren vetëm 16 bitet e poshtme. Bitet 16 deri në 31 nuk përdoren. Kur një vlerë shkruhet në GPIOx_ODR, kjo vlerë vendoset në linjat e daljes së portit përkatës. Bitet e regjistrit lexohen/shkruhen vetëm.

Regjistri i hyrjes

Regjistri i të dhënave të hyrjes së portit (GPIOx_IDR) (x=A..G)

Bit 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16
Fusha Rezervë
Bit 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 1
Fusha IDR15 IDR14 IDR13 IDR12 IDR11 IDR10 IDR9 IDR8 IDR7 IDR6 IDR5 IDR4 IDR3 IDR2 IDR1 IDR0

Ngjashëm me regjistrin e daljes, regjistri i hyrjes ka vetëm 16 bit efektivë të ulët nga 32. Leximi i GPIOx_IDR kthen vlerën e statusit të të gjitha linjave në port. Bitët e regjistrimit janë vetëm për lexim.

Regjistri i funksionimit të bitit

Regjistri i vendosjes/rivendosjes së bitit të portës (GPIOx_BSRR) (x=A..G)

Bit 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16
Fusha BR15 BR14 BR13 BR12 BR11 BR10 BR9 BR8 BR7 BR6 BR5 BR4 BR3 BR2 BR1 BR0
Bit 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 1
Fusha BS15 BS14 BS13 BS12 BS11 BS10 BS9 BS8 BS7 BS6 BS5 BS4 BS3 BS2 BS1 BS0

Ky regjistër ju lejon të aksesoni një linjë specifike hyrëse/dalëse të mikrokontrolluesit STM32. Shkrimi i një në një nga bitët e rendit të lartë rivendos daljen e linjës, dhe shkrimi i një në një bit të rendit të ulët vendos nivelin e sinjalit të lartë në linjën përkatëse. Regjistri është shkruar në formatin word, dhe zero bit nuk kanë asnjë efekt. Bitët e regjistrimit janë vetëm për shkrim.

Rivendos regjistrin

Regjistri i rivendosjes së bitit të portës (GPIOx_BRR) (x=A..G)

Bit 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16
Fusha Rezervë
Bit 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 1
Fusha BR15 BR14 BR13 BR12 BR11 BR10 BR9 BR8 BR7 BR6 BR5 BR4 BR3 BR2 BR1 BR0

Ky regjistër rivendos nivelin e lartë të linjës së vendosur në regjistrin GPIOx_ODR. Përdoren vetëm 16 bitët e poshtëm, të cilët janë vetëm për shkrim.

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