Kur krijoni aplikacionin e parë në mikrokontrolluesin STM32, ka disa mënyra për të shkuar. E para, klasike, marrim përshkrimin e saktë të kontrolluesit në faqen e internetit www.st.com, i cili shfaqet me emrin "Manuali i referencës" dhe lexojmë përshkrimin e regjistrave periferikë. Pastaj përpiqemi t'i shkruajmë dhe të shohim se si funksionojnë pajisjet periferike. Leximi i këtij dokumenti është shumë i dobishëm, por në fazën e parë të zotërimit të mikrokontrolluesit, kjo mund të braktiset, çuditërisht. Inxhinierët e STMicroelectronics kanë shkruar një bibliotekë standarde të drejtuesve të pajisjeve periferike. Për më tepër, ata kanë shkruar shumë shembuj të përdorimit të këtyre drejtuesve, të cilët mund të reduktojnë programimin e aplikacionit tuaj në shtypjen e tasteve Ctrl + C dhe Ctrl + V, e ndjekur nga një ndryshim i vogël në shembullin e drejtuesit për t'iu përshtatur nevojave tuaja. Kështu, lidhja e bibliotekës periferike të drejtuesve me projektin tuaj është metoda e dytë e ndërtimit të një aplikacioni. Përveç shpejtësisë së shkrimit, ka edhe avantazhe të tjera të kësaj metode: universaliteti i kodit dhe përdorimi i bibliotekave të tjera pronësore, si USB, Ethernet, kontrolli i diskut, etj., të cilat ofrohen në burime dhe përdorin një drejtues standard periferik. Ka edhe disavantazhe të kësaj metode: Aty ku mund t'ia dilni me një rresht kodi, drejtuesi standard periferik STM32 do të shkruajë 10. Vetë biblioteka periferike ofrohet gjithashtu si skedarë burimi, kështu që ju mund të gjurmoni se cilin pjesë e regjistrimit ose ai funksion ndryshon. Nëse dëshironi, do të jetë e mundur të kaloni nga metoda e dytë e shkrimit të një programi në të parën duke komentuar një pjesë të kodit që përdor bibliotekën standarde më vete, e cila kontrollon drejtpërdrejt regjistrin periferik. Si rezultat i një veprimi të tillë, ju do të fitoni shpejtësinë e kontrollit, sasinë e RAM-it dhe ROM-it dhe do të humbni në universalitetin e kodit. Në çdo rast, inxhinierët e Promelectronica rekomandojnë përdorimin e bibliotekës së pajisjeve periferike standarde të paktën në fazën e parë.
Vështirësitë më të mëdha e presin zhvilluesin kur lidh bibliotekën me projektin e tij. Nëse nuk dini si ta bëni këtë, mund të shpenzoni shumë kohë në këtë ngjarje, gjë që bie në kundërshtim me vetë idenë e përdorimit të një shoferi të gatshëm. Materiali i kushtohet lidhjes së bibliotekës standarde me çdo familje STM32.
Çdo familje STM32 ka bibliotekën e saj të pajisjeve periferike standarde. Kjo për faktin se vetë periferia është e ndryshme. Për shembull, pajisjet periferike të kontrollorëve STM32L kanë një funksion të kursimit të energjisë si një nga detyrat, i cili përfshin shtimin e funksioneve të kontrollit. Një shembull klasik është ADC, i cili në STM32L ka aftësinë për të fikur harduerin, në mungesë të një komande konvertimi për një kohë të gjatë - një nga pasojat e detyrës së kursimit të energjisë. Kontrollorët ADC të familjeve STM32F nuk e kanë një funksion të tillë. Në fakt, për shkak të pranisë së një ndryshimi harduerik në periferi, ne kemi biblioteka të ndryshme drejtuese. Përveç ndryshimit të dukshëm në funksionet e kontrolluesit, ka një përmirësim në pajisjet periferike. Pra, pajisjet periferike të kontrollorëve të familjeve që u lëshuan më vonë mund të jenë më të menduara dhe më të përshtatshme. Për shembull, pajisjet periferike të kontrollorëve STM32F1 dhe STM32F2 kanë dallime në kontroll. Sipas mendimit të autorit, menaxhimi i pajisjeve periferike STM32F2 është më i përshtatshëm. Dhe kjo është e kuptueshme pse: familja STM32F2 u lëshua më vonë dhe kjo i lejoi zhvilluesit të merrnin parasysh disa nga nuancat. Prandaj, për këto familje - bibliotekat individuale të kontrollit periferik. Ideja e sa më sipër është e thjeshtë: në faqen e mikrokontrolluesit që do të përdorni, ekziston një bibliotekë periferike e përshtatshme për të.
Pavarësisht ndryshimit të pajisjeve periferike në familje, shoferët fshehin 90% të dallimeve brenda vetes. Për shembull, funksioni akordues i ADC-së i përmendur më sipër duket i njëjtë për të gjitha familjet:
void ADC_Init (ADC_Nom, ADC_Param),
ku ADC_Nom është numri ADC në formën e ADC1, ADC2, ADC3, etj.
ADC_Param - treguesi i strukturës së të dhënave, si të konfiguroni ADC (nga çfarë të filloni, nga sa kanale të dixhitalizoni, nëse duhet bërë në mënyrë ciklike, etj.)
10% e dallimeve familjare, në këtë shembull, të cilat do të duhet të korrigjohen kur lëvizni nga një familje STM32 në tjetrën, janë të fshehura në strukturën ADC_Param. Në varësi të familjes, numri i fushave në këtë strukturë mund të jetë i ndryshëm. Pjesa e përgjithshme ka të njëjtën sintaksë. Kështu, transferimi i një aplikacioni për një familje STM32, i shkruar në bazë të bibliotekave standarde periferike, në një tjetër është shumë i thjeshtë. Përsa i përket universalizimit të zgjidhjeve të bazuara në mikrokontrollues, STMicroelectronics është e parezistueshme!
Pra, ne shkarkuam bibliotekën për STM32 të përdorur. Ç'pritet më tej? Më pas, duhet të krijojmë një projekt dhe të lidhim skedarët e kërkuar me të. Merrni parasysh krijimin e një projekti duke përdorur si shembull mjedisin e zhvillimit IAR Embedded Workbench. Ne fillojmë mjedisin e zhvillimit dhe shkojmë te skeda "Project", zgjedhim artikullin "Krijo projekt" për krijimin e projektit:
Në projektin e ri që shfaqet, futni cilësimet duke qëndruar pezull mbi emrin e projektit, duke shtypur butonin e djathtë të miut dhe duke zgjedhur "Opsionet" nga menyja rënëse:
Zonat e memories së RAM dhe ROM:
Kur klikoni butonin "Ruaj", mjedisi do të ofrojë për të shkruar një skedar të ri përshkrimi të kontrolluesit në dosjen e projektit. Autori rekomandon krijimin e një skedari individual *.icp për çdo projekt dhe ruajtjen e tij në dosjen e projektit.
Nëse do të korrigjoni projektin tuaj në qark, gjë që rekomandohet, atëherë vendosni llojin e korrigjuesit që do të përdorni:
Në skedën e korrigjuesit të zgjedhur, specifikoni ndërfaqen për lidhjen e korrigjuesit (në rastin tonë, ST-Link është zgjedhur) me kontrolluesin:
![]() |
Tani e tutje, projekti ynë pa biblioteka është gati për t'u kompiluar dhe ngarkuar në kontrollues. Mjedise të tjera si Keil uVision4, Resonance Ride7, etj. do të duhet të ndjekin të njëjtat hapa.
Nëse shkruani rreshtin në skedarin main.c:
#include "stm32f10x.h" ose
#include "stm32f2xx.h" ose
#include "stm32f4xx.h" ose
#include "stm32l15x.h" ose
#include "stm32l10x.h" ose
#include "stm32f05x.h"
duke treguar vendndodhjen e këtij skedari, ose duke e kopjuar këtë skedar në dosjen e projektit, atëherë disa zona memorie do të shoqërohen me regjistrat periferikë të familjes përkatëse. Vetë skedari ndodhet në dosjen e bibliotekës standarde periferike në seksionin: \CMSIS\CM3\DeviceSupport\ST\STM32F10x (ose emri i ngjashëm për familjet e tjera). Tani e tutje, ju zëvendësoni adresën e regjistrit periferik si numër me emrin e saj. Edhe nëse nuk do të përdorni funksionet e bibliotekës standarde, rekomandohet të bëni një lidhje të tillë.
Nëse do të përdorni ndërprerje në projektin tuaj, rekomandohet të përfshini një skedar fillestar me shtesën *.s, i cili ndodhet përgjatë shtegut \CMSIS\CM3\DeviceSupport\ST\STM32F10x\startup\iar, ose të ngjashme, për familjet e tjera. Është e rëndësishme të theksohet se çdo mjedis ka skedarin e vet. Prandaj, nëse përdorim IAR EWB, duhet të marrim skedarin nga dosja IAR. Kjo është për shkak të një ndryshimi të vogël në sintaksën e mjediseve. Prandaj, në mënyrë që projekti të fillojë menjëherë, inxhinierët e STMicroelectronics shkruan disa variante të skedarëve fillestarë për disa nga mjediset më të njohura të zhvillimit. Shumica e familjeve STM32 kanë një skedar. Familja STM32F1 ka disa skedarë fillestarë:
- startup_stm32f10x_cl.s - për mikrokontrolluesit STM32F105/107
- startup_stm32f10x_xl.s - për mikrokontrolluesit STM32F101/STM32F103 768 kb ose më shumë
- startup_stm32f10x_hd.s - për mikrokontrolluesit STM32F101/STM32F103 me memorie flash 256-512 kb
- startup_stm32f10x_md.s - për mikrokontrolluesit STM32F101/ STM32F102/STM32F103 me memorie flash 64-128 kb
- startup_stm32f10x_ld.s - për mikrokontrolluesit STM32F101/ STM32F102/STM32F103 me memorie flash më pak se 64 kb
- startup_stm32f10x_hd_vl.s për mikrokontrolluesit STM32F100 me memorie flash 256-512 kb
- startup_stm32f10x_md_vl.s për mikrokontrolluesit STM32F100 me memorie flash 64-128 kb
- startup_stm32f10x_ld_vl.s për mikrokontrolluesit STM32F100 me memorie flash 32 kb ose më pak
Pra, në varësi të familjes, nënfamiljes dhe mjedisit të zhvillimit, shtoni skedarin e nisjes në projekt:
Këtu përfundon mikrokontrolluesi kur fillon programi. Ndërprerja thërret në mënyrë sekuenciale funksionin SystemInit() dhe më pas __iar_program_start. Funksioni i dytë rivendos ose shkruan vlerat e paracaktuara të variablave globale, pas së cilës kalon në programin e përdoruesit kryesor (). Funksioni SystemInit() konfiguron orën e mikrokontrolluesit. Është ajo që u përgjigjet pyetjeve:
- A duhet të kaloj në kristal të jashtëm (HSE)?
- Si të shumëzoni frekuencën nga HSI/HSE?
- A kërkohet të lidhni radhën e shkarkimit të komandës?
- Çfarë vonese kërkohet gjatë ngarkimit të një komande (për shkak të shpejtësisë së ulët të memories Flash)
- Si të ndahet kronologjia e autobusëve periferikë?
- A duhet të vendoset kodi në RAM të jashtëm?
Funksioni SystemInit() mund të shkruhet manualisht në projektin tuaj. Nëse e lëshoni këtë funksion si bosh, atëherë kontrolluesi do të punojë në një gjenerator të brendshëm RC me një frekuencë prej rreth 8 MHz (në varësi të llojit të familjes). Opsioni 2 - lidhni skedarin system_stm32f10x.c me projektin (ose emri i ngjashëm në varësi të llojit të familjes së përdorur), i cili ndodhet në bibliotekë përgjatë shtegut: Libraries\CMSIS\CM3\DeviceSupport\ST\STM32F10x. Ky skedar përmban funksionin SystemInit(). Kushtojini vëmendje frekuencës së kristalit të jashtëm HSE_VALUE. Ky parametër vendoset në skedarin e titullit stm32f10x.h. Vlera standarde është 8 dhe 25 MHz, në varësi të familjes STM32. Detyra kryesore e funksionit SystemInit () është të kalojë orën në një kuarc të jashtëm dhe të shumëzojë këtë frekuencë në një mënyrë të caktuar. Çfarë ndodh nëse vlera e HSE_VALUE është 8 MHz, bërthama duhet të jetë e klockuar në 72 MHz, dhe në fakt bordi ka një kuarc 16 MHz? Si rezultat i veprimeve të tilla të pasakta, bërthama do të marrë një frekuencë 144 MHz, e cila mund të jetë përtej funksionimit të garantuar të sistemit në STM32. ato. kur përfshini skedarin system_stm32f10x.c, do t'ju duhet të specifikoni vlerën HSE_VALUE. E gjithë kjo do të thotë që skedarët system_stm32f10x.c, system_stm32f10x.h dhe stm32f10x.h (ose emra të ngjashëm për familjet e tjera) duhet të jenë individuale për çdo projekt. DHE
Inxhinierët e STMicroelectronics kanë krijuar mjetin e konfigurimit të orës, i cili ju lejon të konfiguroni saktë orën e sistemit. Ky është një skedar Excel që gjeneron një skedar system_stm32xxx.c (i ngjashëm në emër me një familje të caktuar) pas vendosjes së parametrave të hyrjes dhe daljes së sistemit. Konsideroni punën e tij në shembullin e familjes STM32F4.
Opsionet: oshilator i brendshëm RC, oshilator i brendshëm RC me shumëzim frekuence, ose kristal i jashtëm me shumëzim frekuence. Pas zgjedhjes së burimit të orës, futim parametrat e konfigurimit të dëshiruar të sistemit, si frekuenca e hyrjes (kur përdoret kuarci i jashtëm), frekuenca e orës bazë, pjesëtuesit e frekuencës së orës së autobusëve periferikë, funksionimi i tamponit të tërheqjes së instruksioneve, dhe të tjerët. Duke klikuar në butonin "Generate", ne kemi një dritare
![]() |
Lidhja e skedarit system_stm32f4xx.c dhe analogëve të tij do të kërkojë lidhjen e një skedari tjetër të bibliotekës standarde periferike. Për të kontrolluar orën, ekziston një grup i tërë funksionesh që thirren nga skedari system_stm32xxxxxx.c. Këto funksione janë të vendosura në skedarin stm32f10x_rcc.c dhe kokën e tij. Prandaj, kur lidhni skedarin system_stm32xxxxxx.c me projektin, kërkohet të përfshihet stm32f10x_rcc.c, përndryshe lidhësi i mjedisit do të raportojë se nuk ka përshkrim funksioni me emrin RCC_xxxxxxx. Skedari i specifikuar ndodhet në bibliotekën periferike përgjatë shtegut: Libraries\STM32F10x_StdPeriph_Driver\src dhe titulli i tij \Libraries\STM32F10x_StdPeriph_Driver\inc.
Skedarët e kokës së drejtuesit periferik janë të lidhur në skedarin stm32f10x_conf.h, i cili referohet nga stm32f10x.h. Skedari stm32f10x_conf.h është thjesht një grup skedarësh të kokës së drejtuesit për pajisje periferike të kontrolluesit që do të përfshihen në projekt. Fillimisht, të gjitha titujt "#include" janë shënuar si komente. Lidhja e skedarit të kokës së periferisë konsiston në heqjen e komentit nga emri i skedarit përkatës. Në rastin tonë, kjo është linja #include "stm32f10x_rcc.h". Natyrisht, skedari stm32f10x_conf.h është individual për çdo projekt, sepse projekte të ndryshme përdorin pajisje të ndryshme periferike.
Dhe e fundit. Ju duhet të specifikoni disa direktiva për paraprocesorin e përpiluesit dhe shtigjet drejt skedarëve të kokës.
![]() |
Rrugët drejt skedarëve të kokës mund të jenë të ndryshme, në varësi të vendndodhjes së bibliotekës periferike në lidhje me dosjen e projektit, por prania e "USE_STDPERIPH_DRIVER" është e detyrueshme kur lidhni drejtuesit standardë periferikë të bibliotekës.
Pra, ne kemi lidhur bibliotekën standarde me projektin. Për më tepër, ne kemi lidhur një nga drejtuesit standardë periferikë me projektin që kontrollon orën e sistemit.
Ne kemi mësuar se si duket pajisja e bibliotekës nga brenda, tani disa fjalë se si duket nga jashtë.
![]() |
Kështu, përfshirja e skedarit të kokës stm32f10x.h në një aplikacion kërkon përfshirjen e skedarëve të tjerë të kokës dhe skedarëve të kodit. Disa nga ato të paraqitura në figurë janë përshkruar më sipër. Disa fjalë për pjesën tjetër. Skedarët STM32F10x_PPP.x janë skedarë drejtues periferikë. Një shembull i lidhjes së një skedari të tillë është treguar më lart, ky është RCC - periferia e kontrollit të orës së sistemit. Nëse duam të lidhim drejtuesit e pajisjeve të tjera periferike, atëherë emri i skedarëve të lidhur merret duke zëvendësuar "PPP" me emrin e pajisjes periferike, për shembull, ADC - STM32F10x_ADC.s, ose portet I / O STM32F10x_GPIO.s, ose DAC - STM32F10x_DAC.s. Në përgjithësi, është intuitivisht e qartë se cili skedar duhet të lidhet kur lidhni një pajisje periferike të caktuar. Skedarët "misc.c", "misc.h" janë në përgjithësi të njëjtat STM32F10x_PPP.x, ata kontrollojnë vetëm kernelin. Për shembull, konfigurimi i vektorëve të ndërprerjes, i cili është i integruar në kernel, ose menaxhimi i kohëmatësit SysTick, i cili është pjesë e kernelit. Skedarët xxxxxxx_it.c përshkruajnë vektorët NMI të kontrolluesit. Ato mund të plotësohen me vektorë të ndërprerjeve periferike. Skedari core_m3.h përshkruan bërthamën CortexM3. Kjo bërthamë është e standardizuar dhe mund të gjendet në mikrokontrolluesit e prodhuesve të tjerë. Për universalizimin ndër-platformë, STMicroelectronics punoi për të krijuar një bibliotekë të veçantë bërthamore CortexM, pas së cilës ARM e standardizoi atë dhe ia shpërndau prodhuesve të tjerë të mikrokontrolluesve. Kështu që kalimi në STM32 nga kontrollorët nga prodhuesit e tjerë me një bërthamë CortexM do të jetë pak më i lehtë.
Pra, ne mund të lidhim bibliotekën standarde periferike me çdo familje STM32. Ai që ka mësuar ta bëjë këtë është duke pritur për një çmim: një programim shumë të thjeshtë të mikrokontrolluesve. Biblioteka, përveç drejtuesve në formën e skedarëve burimor, përmban shumë shembuj të përdorimit të pajisjeve periferike. Si shembull, merrni parasysh krijimin e një projekti që përfshin rezultatet e krahasimit të kohëmatësit. Me qasjen tradicionale, ne do të studiojmë me kujdes përshkrimin e regjistrave të kësaj pajisjeje periferike. Por tani mund të studiojmë tekstin e programit të ekzekutimit. Ne hyjmë në dosjen e shembujve të pajisjeve periferike standarde, e cila ndodhet përgjatë shtegut ProjectSTM32F10x_StdPeriph_Examples. Këtu janë dosjet e shembujve me emrin e pajisjeve periferike të përdorura. Shkojmë te dosja "TIM". Kohëmatësit në STM32 kanë shumë funksione dhe cilësime, kështu që është e pamundur të demonstrohen aftësitë e kontrolluesit me një shembull. Prandaj, brenda drejtorisë së specifikuar, ka shumë shembuj të përdorimit të kohëmatësve. Ne jemi të interesuar në gjenerimin e një sinjali PWM nga një kohëmatës. Shkojmë te dosja "7PWM_Output". Brenda ka një përshkrim të programit në anglisht dhe një grup skedarësh:
main.c stm32f10x_conf.h stm32f10x_it.h stm32f10x_it.c system_stm32f10x.c |
Nëse projekti nuk ka ndërprerje, atëherë përmbajtja ndodhet tërësisht në skedarin main.c. Kopjoni këto skedarë në drejtorinë e projektit. Pas përpilimit të projektit, do të marrim një program për STM32, i cili do të konfigurojë kohëmatësin dhe portat I/O për të gjeneruar 7 sinjale PWM nga kohëmatësi 1. Më pas, mund të përshtatim kodin e shkruar tashmë në detyrën tonë. Për shembull, zvogëloni numrin e sinjaleve PWM, ndryshoni ciklin e punës, drejtimin e numërimit, etj. Funksionet dhe parametrat e tyre janë përshkruar mirë në skedarin stm32f10x_stdperiph_lib_um.chm. Emrat e funksioneve dhe parametrat e tyre lidhen lehtësisht me qëllimin e tyre për ata që dinë pak anglisht. Për qartësi, këtu është një pjesë e kodit të shembullit të marrë:
/* Konfigurimi i bazës kohore */ TIM_TimeBaseStructure.TIM_Prescaler = 0; // nuk ka parashkallëzues të impulseve numërues (regjistër 16-bitësh) TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; // duke numëruar drejtimin lart TIM_TimeBaseStructure.TIM_Period = TimerPeriod; // numëro deri në vlerën e TimerPeriod (konstante në program) TIM_TimeBaseStructure.TIM_ClockDivision = 0; // nuk ka para-ndarje të numëruesve TIM_TimeBaseStructure.TIM_RepetitionCounter = 0; // numëruesi i tejmbushjes për gjenerimin e ngjarjeve (nuk përdoret në program) TIM_TimeBaseInit(TIM1, &TIM_TimeBaseStructure); // futja e vlerave të TimeBaseStructure në regjistrat e kohëmatësit 1 (futja e të dhënave për këtë variabël // është më lart) /* Konfigurimi i kanalit 1, 2,3 dhe 4 në modalitetin PWM */ // konfigurimi i daljeve PWM TIM_OCInitStructure.TIM_OCMode = TIMWMOC2_ ; // Mënyra e funksionimit PWM2 TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; // aktivizoni daljen e sinjaleve të kohëmatësit PWM TIM_OCInitStructure.TIM_OutputNState = TIM_OutputNState_Enable; // aktivizoni daljen plotësuese të kohëmatësit PWM TIM_OCInitStructure.TIM_Pulse = Channel1Pulse; // gjerësia e pulsit Channel1Pulsi është një konstante në programin TIM_OCInitStructure.TIM_OCPolariteti = TIM_OCPolariteti_I ulët; // vendosja e polaritetit të daljes TIM_OCInitStructure TIM_OCNPolariteti = TIM_OCNPolariteti_I lartë; // vendosja e polaritetit të daljes plotësuese TIM_OCInitStructure.TIM_OCIdleState = TIM_OCIdleState_Set; // vendos gjendjen e sigurt të daljes PWM TIM_OCInitStructure.TIM_OCNIdleState = TIM_OCIdleState_Reset; // vendos gjendjen e sigurt të daljes PWM plotësuese TIM_OC1Init(TIM1, &TIM_OCInitStructure); // futni vlerat e ndryshores TIM_OCInitStructure në regjistrat PWM të kanalit 1 // të timer1 TIM_OCInitStructure.TIM_Pulse = Channel2Pulse; // ndryshoni gjerësinë e pulsit në variablin OCInitStructure dhe futeni atë në TIM_OC2Init(TIM1, &TIM_OCInitStructure); // timer1 kanali 2 PWM regjistrat TIM_OCInitStructure.TIM_Pulse = Channel3Pulse; // ndryshoni gjerësinë e pulsit në variablin OCInitStructure dhe futeni atë në TIM_OC3Init(TIM1, &TIM_OCInitStructure); // timer1 kanali 3 PWM regjistrat TIM_OCInitStructure.TIM_Pulse = Channel4Pulse; // ndryshoni gjerësinë e pulsit në variablin OCInitStructure dhe futeni atë në TIM_OC4Init(TIM1, &TIM_OCInitStructure); // timer1 kanal 4 regjistrat PWM /* numëruesi TIM1 aktivizon */ TIM_Cmd(TIM1, ENABLE); // start timer1 /* TIM1 Dalja kryesore Aktivizo */ TIM_CtrlPWMOoutputs(TIM1, ENABLE); // aktivizoni rezultatet e krahasimit të kohëmatësit 1 |
Në anën e djathtë, autori la një koment në Rusisht për secilën rresht të programit. Nëse hapim të njëjtin shembull në përshkrimin e funksioneve të bibliotekave stm32f10x_stdperiph_lib_um.chm, do të shohim që të gjithë parametrat e funksionit të përdorur kanë një lidhje me përshkrimin e tyre, ku do të tregohen vlerat e tyre të mundshme. Vetë funksionet kanë gjithashtu një lidhje me përshkrimin dhe kodin e tyre burimor. Kjo është shumë e dobishme sepse Duke ditur se çfarë bën funksioni, ne mund të gjurmojmë se si e bën atë, cilat pjesë të regjistrave periferikë dhe si ndikon. Ky është, së pari, një burim tjetër informacioni për zotërimin e kontrolluesit, bazuar në përdorimin praktik të kontrolluesit. ato. ju së pari zgjidhni problemin teknik, dhe më pas studioni vetë zgjidhjen. Së dyti, kjo është një fushë për optimizimin e programit për ata që nuk janë të kënaqur me bibliotekën për sa i përket shpejtësisë dhe madhësisë së kodit.
![]() |
Pra, ne jemi ngritur tashmë në këmbë, në kuptimin që kemi gjithçka që na nevojitet të lidhur me kunjat e mikrokontrolluesit në tabelën STM32VL Discovery, kemi mësuar të flasim, në gjuhën e programimit C, do të ishte koha për të krijuar një projekt në klasën e parë.
Shkrimi i programit
Pasi të keni përfunduar me krijimin dhe konfigurimin e projektit, mund të filloni të shkruani programin real. Siç është zakon për të gjithë programuesit, programi i parë i shkruar për të punuar në një kompjuter është një program që shfaq mbishkrimin "HelloWorld" në ekran dhe për të gjithë mikrokontrolluesit, programi i parë për mikrokontrolluesin pulson LED. Ne nuk do të bëjmë përjashtim nga kjo traditë dhe do të shkruajmë një program që do të kontrollojë LED-in LD3 në tabelën STM32VL Discovery.
Pas krijimit të një projekti bosh në IAR, ai krijon një kod programi minimal:
Tani programi ynë do të "rrotullohet" gjithmonë në një lak derisa.
Në mënyrë që ne të kontrollojmë LED-in, duhet të aktivizojmë klocking-un e portës me të cilën është lidhur dhe të vendosim pinin përkatës të portës së mikrokontrolluesit në dalje. Siç e diskutuam më herët në pjesën e parë, për lejimin e klockimit të portit NGA përgjigjet pak IOPCEN regjistroheni RCC_APB2ENR. Sipas dokumentit " RM0041referencëmanual.pdf» për të aktivizuar kalimin e autobusit të portit NGA kërkohet në regjistër RCC_APB2ENR vendos pak IOPCEN për njësi. Kështu që kur vendosim këtë bit, të mos rivendosim të tjerët në këtë regjistër, duhet të zbatojmë operacionin e shtimit logjik (logjik "OR") në gjendjen aktuale të regjistrit dhe më pas të shkruajmë vlerën që rezulton në përmbajtjen e regjistroheni. Në përputhje me strukturën e bibliotekës ST, qasja në vlerën e regjistrit për lexim dhe shkrim bëhet përmes një treguesi në strukturë. RCC-> APB2 ENR. Kështu, duke kujtuar materialin nga pjesa e dytë, mund të shkruajmë kodin e mëposhtëm që kryen një cilësim bit IOPCEN në regjistër RCC_APB2ENR:
Siç mund ta shihni, nga skedari "stm32f10x.h", vlera e bitit IOPCEN definuar si 0x00000010, që korrespondon me bitin e katërt ( IOPCEN) regjistroheni APB2ENR dhe përputhet me vlerën e specifikuar në fletën e të dhënave.
Tani le të vendosim daljen në të njëjtën mënyrë 9 port NGA. Për ta bërë këtë, ne duhet të konfigurojmë këtë kunj porti që të dalë në modalitetin push-pull. Regjistri është përgjegjës për vendosjen e modalitetit të portit për hyrje / dalje GPIOC_CRH, ne e kemi konsideruar tashmë në , përshkrimi i tij është gjithashtu në seksionin "7.2.2 Regjistri i konfigurimit të portit të lartë" të fletës së të dhënave. Për të vendosur daljen në modalitetin e daljes me një shpejtësi maksimale prej 2 MHz, është e nevojshme në regjistër GPIOC_CRH instaloni MODI 9 në një dhe rivendosni bitin MODI 9 në zero. Bitët janë përgjegjës për vendosjen e mënyrës së funksionimit të daljes si funksionin kryesor me daljen push-tërheqëse. CNF9 Dhe CNF9 , për të konfiguruar mënyrën e funksionimit që kërkojmë, të dy këta bit duhet të rivendosen në zero.
Tani kunja e portës me të cilën është lidhur LED është vendosur në dalje, për të kontrolluar LED-in, duhet të ndryshojmë gjendjen e pinit të portit duke vendosur daljen në një njësi logjike. Ka dy mënyra për të ndryshuar gjendjen e një pin porti, e para është të shkruani drejtpërdrejt në regjistrin e gjendjes së portit përmbajtjen e ndryshuar të regjistrit të portit, ashtu siç bëmë vendosjen e portit. Kjo metodë nuk rekomandohet duke pasur parasysh mundësinë e një situate në të cilën një vlerë e pasaktë mund të shkruhet në regjistrin e portit. Kjo situatë mund të ndodhë nëse gjatë një ndryshimi në gjendjen e regjistrit, nga momenti kur gjendja e regjistrit tashmë është lexuar dhe deri në momentin kur gjendja e ndryshuar është shkruar në regjistër, ndonjë pajisje periferike ose ndërprerje do të ndryshojë gjendjen e këtij porti. Pas përfundimit të operacionit për ndryshimin e gjendjes së regjistrit, vlera do të shkruhet në regjistër pa marrë parasysh ndryshimet që kanë ndodhur. Megjithëse probabiliteti i kësaj situate është shumë i ulët, ia vlen të përdoret një metodë tjetër në të cilën situata e përshkruar është e përjashtuar. Për ta bërë këtë, ekzistojnë dy regjistra në mikrokontrollues. GPIOx_BSRR Dhe GPIOx_BRR. Kur shkruani një njësi logjike në bitin e kërkuar të regjistrit GPIOx_BRR kunja e portit përkatës do të rivendoset në zero logjike. Regjistrohu GPIOx_BSRR mund të vendosë dhe rivendosë gjendjen e kunjave të portit, për të vendosur pinin e portit në një logjik, duhet të vendosni bitet BSn, që korrespondon me numrin e bitit të kërkuar, këta bit ndodhen në regjistrat e poshtëm të bajtit. Për të rivendosur gjendjen e pinit të portit në zero logjike, duhet të shkruani bitet BRn kunjat përkatëse, këto bit janë të vendosura në pjesët e sipërme të regjistrit të portit.
LED LD3 i lidhur me daljen 9 port NGA. Për të ndezur këtë LED, duhet të aplikojmë një njësi logjike në pinin përkatës të portës në mënyrë që të "ndizet" LED.
Le të shtojmë kodin për vendosjen e daljes së portit LED në programin tonë, dhe gjithashtu shtojmë një funksion të vonesës së softuerit për të zvogëluar frekuencën e ndërrimit të LED:
//Mos harroni të lidhni skedarin e kokës me një përshkrim të regjistrave të mikrokontrolluesit #include "stm32f10x.h" i pavlefshëm Vonesa ( i pavlefshëm); i pavlefshëm Vonesa ( i pavlefshëm) //Funksioni ynë kryesor i pavlefshëm kryesore( i pavlefshëm)
//pastro bit MODE9 (rivendos bit MODE9_1 dhe MODE9_0 në zero) //Cakto bitin MODE9_1 për të vendosur daljen në një dalje me një shpejtësi prej 2 MHz //pastroni bitet CNF (të vendosura si dalje për qëllime të përgjithshme, simetrike (shtytje-tërheqje)) derisa(1) //Vendosja e pinit 9 të portës C në një logjike ("ndizet" LED)
} |
Ju mund ta shkarkoni arkivin me kodin burimor të programit të shkruar duke përdorur kontrollin e drejtpërdrejtë të regjistrave të mikrokontrolluesit në lidhjen.
Programi ynë i parë i zbatueshëm u shkrua, kur e shkruanim, për të punuar dhe konfiguruar pajisjet periferike, ne përdorëm të dhëna nga fleta zyrtare e të dhënave " RM0041referencëmanual.pdf”, ky burim informacioni për regjistrat e mikrokontrolluesit është më i sakti, por për ta përdorur duhet të rilexoni shumë informacione, gjë që e ndërlikon shkrimin e programeve. Për të lehtësuar procesin e konfigurimit të pajisjeve periferike të mikrokontrolluesit, ekzistojnë gjeneratorë të ndryshëm kodi, mjeti zyrtar nga ST është Microxplorer, por ai është ende pak funksional dhe për këtë arsye, zhvilluesit e palëve të treta kanë krijuar një program alternativ "STM32 Gjeneruesi i kodit të programit » . Ky program ju lejon të merrni me lehtësi kodin e cilësimeve periferike duke përdorur një ndërfaqe grafike të përshtatshme dhe intuitive (shih Fig. 2).
Oriz. 2 Pamja e ekranit të gjeneratorit të kodit STM32
Siç mund ta shihni nga Figura 2, kodi i krijuar nga programi për vendosjen e daljes LED përputhet me kodin që kemi shkruar më parë.
Për të ekzekutuar programin e shkruar, pas përpilimit të kodit burimor, duhet të ngarkoni programin tonë në mikrokontrollues dhe të shihni se si funksionon.
Video e mënyrës së korrigjimit të programit ndezës LED
Video e programit të ndezjes LED në tabelën STM32VL Discovery
Biblioteka funksionon për të punuar me pajisjet periferike
Për të thjeshtuar punën me vendosjen e regjistrave të pajisjeve periferike të mikrokontrolluesit, ST ka zhvilluar biblioteka, falë përdorimit të të cilave, nuk është e nevojshme të lexohet aq mirë datasheet, sepse gjatë përdorimit të këtyre bibliotekave, puna e shkrimit të një programi do të bëhet më afër shkrimit të programeve të nivelit të lartë, pasi të gjitha funksionet e nivelit të ulët zbatohen në nivelin e funksioneve të bibliotekës. Sidoqoftë, nuk duhet të braktiset plotësisht përdorimi i punës së drejtpërdrejtë me regjistrat e mikrokontrolluesve, duke pasur parasysh faktin se funksionet e bibliotekës kërkojnë më shumë kohë procesori për ekzekutimin e tyre, si rezultat, përdorimi i tyre në seksionet kritike për kohën e programit nuk justifikohet. Por prapëseprapë, në shumicën e rasteve, gjëra të tilla si inicializimi periferik nuk janë kritike në kohën e ekzekutimit dhe komoditeti i përdorimit të funksioneve të bibliotekës është më i preferueshëm.
Tani le të shkruajmë programin tonë duke përdorur bibliotekën ST. Programi duhet të konfigurojë portet I / O, për të përdorur funksionet e bibliotekës për vendosjen e porteve, duhet të lidhni skedarin e kokës " stm32f10x_gpio.h» (shih tabelën 1). Ky skedar mund të lidhet duke hequr komentin e linjës përkatëse në skedarin e konfigurimit të kokës së lidhur " stm32f10x_conf.h". Në fund të dosjes stm32f10x_gpio.h» ekziston një listë e deklaratave të funksioneve për të punuar me portet. Një përshkrim i detajuar i të gjitha funksioneve të disponueshme mund të gjendet në skedar " stm32f10x_stdperiph_lib_um.chm”, një përshkrim i shkurtër i më të përdorurve është dhënë në tabelën 2.
Tabela 2. Përshkrimi i funksioneve kryesore të cilësimeve të portit
Funksioni |
Përshkrimi i funksionit, parametrat e kaluar dhe të kthyer |
GPIO_DeInit( |
Vendos vlerat e regjistrave të konfigurimit të portit GPIOx në vlerat e paracaktuara |
GPIO_Init( |
Vendos regjistrat e konfigurimit të portit GPIOx sipas parametrave të specifikuar në strukturën GPIO_InitStruct |
GPIO_StructInit( |
Plotëson të gjitha fushat e strukturës GPIO_InitStruct me vlerat e paracaktuara |
uint8_t GPIO_ReadInputDataBit( |
Leximi i vlerës hyrëse të pinit GPIO_Pin të portës GPIOx |
uint16_t GPIO_ReadInputData( |
Lexoni vlerat hyrëse të të gjitha kunjave të portit GPIOx |
GPIO_SetBits( |
Vendosja e vlerës së daljes së pinit GPIO_Pin të portës GPIOx në një vlerë logjike |
GPIO_ResetBits( |
Rivendosja e vlerës së daljes së pinit GPIO_Pin të portës GPIOx në zero logjike |
GPIO_WriteBit( |
Shkruani vlerën BitVal në pinin GPIO_Pin të portës GPIOx |
GPIO_Write( |
Shkruani vlerën PortVal në portën GPIOx |
Siç mund të shihet nga përshkrimi i funksioneve, si parametra për cilësimet e portit, etj., jo shumë parametra të ndryshëm individualë i kalohen funksionit, por një strukturë. Strukturat janë të dhëna të grumbulluara që kanë një lidhje logjike. Ndryshe nga vargjet, strukturat mund të përmbajnë të dhëna të llojeve të ndryshme. Me fjalë të tjera, një strukturë përfaqëson një grup variablash të ndryshëm me lloje të ndryshme të kombinuara në një ndryshore unike. Variablat që janë në këtë strukturë quhen fusha të strukturës, dhe ato aksesohen si më poshtë, fillimisht shkruhet emri i strukturës, pastaj shkruhet një pikë dhe emri i fushës së strukturës (emri i ndryshores në këtë strukturë).
Lista e variablave të përfshirë në strukturat për funksionet e punës me portet përshkruhet në të njëjtin skedar pak më lart se përshkrimi i funksioneve. Kështu, për shembull, struktura GPIO_InitTypeDef"ka strukturën e mëposhtme:
typedef struct uint16_t GPIO_Pin; /*!< Specifies the GPIO pins to be configured. GPIOSpeed_TypeDef GPIO_Speed; /*!< Specifies the speed for the selected pins. GPIOMode_TypeDef GPIO_Mode; /*!< Specifies the operating mode for the selected pins. )GPIO_InitTypeDef; |
Fusha e parë e kësaj strukture përmban variablin " GPIO_ Gjilpere» lloji e panënshkruar shkurt, në këtë variabël është e nevojshme të shkruhen flamujt e numrave të daljeve përkatëse për të cilat supozohet të bëhen cilësimet e nevojshme. Mund të konfiguroni disa dalje në të njëjtën kohë duke vendosur disa konstante si parametër përmes operatorit pjesërisht OSE(cm. ). Bitwise OR do të "mbledhë" të gjitha nga konstantat e listuara, dhe vetë konstantat janë një maskë, e destinuar vetëm për një përdorim të tillë. Përkufizimet makro të konstantave janë specifikuar në të njëjtin skedar më poshtë.
Fusha e dytë e strukturës " GPIO_InitTypeDef» cakton shpejtësinë maksimale të mundshme të daljes së portit. Lista e vlerave të mundshme për këtë fushë është renditur më sipër:
Përshkrimi i vlerave të mundshme:
- GPIO_Mode_AIN- hyrje analoge (Anglisht Analog INput);
- GPIO_Mode_IN_FLOATING- hyrje pa tërheqje, e varur (eng. Input float) në ajër
- GPIO_Mode_IPD- tërheqje e hyrjes
- GPIO_Mode_IPU- Tërheqja e hyrjes
- GPIO_Mode_Out_OD- dalje me një kullim të hapur (Eng. Output Open Drain)
- GPIO_Mode_Out_PP- dalje me dy gjendje (Anglisht Output Push-Pull - mbrapa dhe mbrapa)
- GPIO_Mode_AF_OD- Hapni daljen e kullimit për Funksionin Alternativ. Përdoret në rastet kur dalja duhet të kontrollohet nga pajisjet periferike të bashkangjitura në këtë kunj porti (për shembull, kunja USART1 Tx, etj.)
- GPIO_Mode_AF_PP- njësoj, por me dy gjendje
Në mënyrë të ngjashme, mund të shikoni strukturën e variablave të strukturave të tjera të nevojshme për të punuar me funksionet e bibliotekës.
Për të punuar me strukturat, ato, si variablat, duhet të deklarohen dhe t'u caktohet një emër unik, pas të cilit mund të hyni në fushat e strukturës së deklaruar me emrin që i është caktuar.
//Deklaroni strukturën /* Për të kaluar vlerat e një strukture në një funksion, paraprini emrin e strukturës me simbolin &. Ky simbol i tregon përpiluesit se është e nevojshme të kalohen në funksion jo vlerat e përmbajtura në strukturë, por adresa e kujtesës ku ndodhen këto vlera. Kjo është bërë për të zvogëluar numrin e veprimeve të nevojshme të procesorit për të kopjuar përmbajtjen e strukturës, dhe gjithashtu kursen RAM. Kështu, në vend që të kalohen shumë bajtë të përmbajtur në strukturë tek funksioni, do të kalohet vetëm një, që përmban adresën e strukturës. /* Shkruani numrin e pinit të portit në fushën GPIO_Pin të strukturës GPIO_Init_struct, të cilën do ta konfigurojmë më tej */ GPIO_Init_struct.GPIO_Pin=GPIO_Pin_9; /* Plotësoni fushën GPIO_Speed në të njëjtën mënyrë */ /* |
Pothuajse të gjitha pajisjet periferike janë konfiguruar afërsisht në të njëjtën mënyrë, ndryshimet janë vetëm në parametrat dhe komandat specifike për secilën pajisje.
Tani le të shkruajmë programin tonë ndezës LED duke përdorur vetëm funksionet e bibliotekës.
// Mos harroni të lidhni skedarin e kokës me një përshkrim të regjistrave të mikrokontrolluesit #include "stm32f10x.h" //deklarimi i funksionit të vonesës së programit i pavlefshëm Vonesa ( i pavlefshëm); //Vetë funksioni i vonesës së softuerit i pavlefshëm Vonesa ( i pavlefshëm) //Funksioni ynë kryesor i pavlefshëm kryesore( i pavlefshëm) //Aktivizo klockimin e autobusit të portit C //Deklaroni një strukturë për të konfiguruar portin //Mbush strukturën me vlera fillestare /* Shkruani numrin e pinit të portit në fushën GPIO_Pin të strukturës GPIO_Init_struct, të cilën do ta konfigurojmë më tej */ // Plotësoni fushat GPIO_Speed dhe GPIO_Mode në mënyrë të ngjashme //Ne e kalojmë strukturën e kompletuar për të kryer veprime për vendosjen e regjistrave //Cakulli ynë kryesor i pafund // Shtoni një vonesë softueri në mënyrë që LED të ndizet për një kohë //Rivendosja e gjendjes së pinit 9 të portës C në zero logjike //Shtimi i vonesës së softuerit përsëri |
lidhje .
Nga shembulli i mësipërm, mund të shihet se përdorimi i funksioneve të bibliotekës për të punuar me pajisje periferike ju lejon të afroni programet e shkrimit për mikrokontrolluesin më afër programimit të orientuar nga objekti, dhe gjithashtu zvogëlon nevojën për qasje të shpeshtë në fletën e të dhënave për të lexuar përshkrimin të regjistrave të mikrokontrolluesit, por përdorimi i funksioneve të bibliotekës kërkon një njohuri më të lartë të gjuhës së programimit. Në funksion të kësaj, për njerëzit që nuk janë veçanërisht të njohur me programimin, një opsion më i thjeshtë për të shkruar programe do të jetë një mënyrë për të shkruar programe pa përdorur funksionet e bibliotekës, me akses të drejtpërdrejtë në regjistrat e mikrokontrolluesit. Për ata që e njohin mirë gjuhën e programimit, por nuk njohin mirë mikrokontrolluesit, veçanërisht STM32, përdorimi i funksioneve të bibliotekës thjeshton shumë procesin e shkrimit të programeve.
Kjo rrethanë, si dhe fakti që ST është kujdesur për një shkallë të lartë të përputhshmërisë, si në aspektin harduerik ashtu edhe në atë softuer, të mikrokontrolluesve të ndryshëm të tij, kontribuon në studimin më të lehtë të tyre, pasi nuk është e nevojshme të thellohet në veçoritë strukturore të kontrollues të ndryshëm.Seria STM32 dhe ju lejon të zgjidhni cilindo nga mikrokontrolluesit e disponueshëm në linjën STM32 si mikrokontrollues për studim.
Trajtuesi i ndërprerjeve
Mikrokontrolluesit kanë një aftësi të jashtëzakonshme - të ndalojnë ekzekutimin e programit kryesor në një ngjarje specifike dhe të vazhdojnë në ekzekutimin e një nënprogrami të veçantë - mbajtës i ndërprerjeve. Burimet e ndërprerjes mund të jenë ose ngjarje të jashtme - ndërprerje për marrjen / transmetimin e të dhënave përmes çdo ndërfaqeje të transferimit të të dhënave, ose një ndryshim në gjendjen e daljes, ose ato të brendshme - tejmbushja e kohëmatësit, etj. Një listë e burimeve të mundshme të ndërprerjeve për mikrokontrolluesit e serisë STM32 është dhënë në fletën e të dhënave " Manuali i referencës RM0041"në seksion" 8 Ndërprerjet dhe ngjarjet».
Meqenëse mbajtësi i ndërprerjeve është gjithashtu një funksion, ai do të shkruhet si një funksion i rregullt, por në mënyrë që përpiluesi të dijë se ky funksion është një mbajtës specifik i ndërprerjeve, duhet të zgjidhen emrat e paracaktuar si emër të funksionit, tek i cili ridrejtohet vektori i ndërprerjes. janë të specifikuara. Lista e emrave të këtyre funksioneve me një përshkrim të shkurtër është në skedarin assembler " startup_stm32f10x_md_vl.s". Mund të ketë disa burime ndërprerjesh për një mbajtës të ndërprerjeve, për shembull, funksioni i mbajtësit të ndërprerjeve " USART1_IRQ Handler» mund të thirret në rast të përfundimit të marrjes dhe përfundimit të transmetimit të një bajt, etj.
Për të filluar me ndërprerjet, duhet të konfiguroni dhe inicializoni kontrolluesin e ndërprerjeve NVIC. Në arkitekturën Cortex M3, çdo ndërprerje mund të vendoset në grupin e vet prioritar për rastet kur ndodhin ndërprerje të shumta në të njëjtën kohë. Pastaj duhet të konfiguroni burimin e ndërprerjes.
Fusha NVIC_IRQChannel tregon se cilin ndërprerje duam të konfigurojmë. Konstanta USART1_IRQn përcakton kanalin përgjegjës për ndërprerjet e lidhura me USART1. Përcaktohet në skedar stm32f10x.h”, konstante të tjera të ngjashme janë përcaktuar aty.
Dy fushat e ardhshme tregojnë përparësinë e ndërprerjeve (vlerat maksimale të këtyre dy parametrave përcaktohen nga grupi i përzgjedhur i përparësisë). Fusha e fundit, në fakt, përfshin përdorimin e ndërprerjeve.
Në funksion NVIC_Init, si dhe gjatë konfigurimit të porteve, një tregues në një strukturë transmetohet për të aplikuar cilësimet e bëra dhe për t'i shkruar ato në regjistrat përkatës të mikrokontrolluesit.
Tani, në cilësimet e modulit, duhet të vendosni parametrat me të cilët ky modul do të gjenerojë një ndërprerje. Së pari ju duhet të aktivizoni ndërprerjen, kjo bëhet duke thirrur funksionin emri_ITConfig(), i cili ndodhet në skedarin e kokës së pajisjes periferike.
// Aktivizo ndërprerjet në fund të transferimit të bajtit nëpërmjet USART1 // Aktivizo ndërprerjet në fund të marrjes së një bajt nga USART1 |
Një përshkrim i parametrave të kaluar në funksion mund të gjendet në skedarin e kodit burimor të pajisjes periferike, pak mbi vendndodhjen e vetë funksionit. Ky funksion mundëson ose çaktivizon ndërprerjet në ngjarje të ndryshme nga moduli periferik i specifikuar. Kur ky funksion të ekzekutohet, mikrokontrolluesi do të jetë në gjendje të gjenerojë ndërprerje për ngjarjet që na duhen.
Pasi të futemi në funksionin e trajtimit të ndërprerjeve, duhet të kontrollojmë se cila ngjarje e ka shkaktuar ndërprerjen dhe më pas të rivendosim grupin e flamurit, përndryshe, me daljen nga ndërprerja, mikrokontrolluesi do të vendosë që ne nuk e kemi përpunuar ndërprerjen, pasi flamuri i ndërprerjes është ende i vendosur.
Për të kryer veprime të ndryshme, të vogla, të përsëritura me një periudhë të saktë, mikrokontrolluesit me një bërthamë Cortex-M3 kanë një kohëmatës të sistemit të krijuar posaçërisht për këtë. Funksioni i këtij kohëmatësi përfshin vetëm thirrjen e ndërprerjes në intervale kohore të specifikuara rreptësisht. Si rregull, në ndërprerjen e thirrur nga ky kohëmatës, vendoset kodi për të matur kohëzgjatjen e proceseve të ndryshme. Deklarata e funksionit të vendosjes së kohëmatësit ndodhet në skedar " bërthamë_ cm3. h". Argumenti i kaluar në funksion specifikon numrin e cikleve të autobusit të sistemit ndërmjet intervaleve të thirrjeve të mbajtësit të ndërprerjeve të kohëmatësit të sistemit.
SysTick_Config(clk); |
Tani, duke u marrë me ndërprerjet, ne do të rishkruajmë programin tonë, duke përdorur kohëmatësin e sistemit si një element të përcaktimit të kohës. Sepse timer SysTick”është një sistem dhe mund të përdoret nga blloqe të ndryshme funksionale të programit tonë, atëherë do të ishte e arsyeshme që funksioni i trajtimit të ndërprerjeve të zhvendosej nga kohëmatësi i sistemit në një skedar të veçantë, nga ky funksion thirrjet e funksioneve për secilin bllok funksional veç e veç.
Një shembull i skedarit "main.c" të programit për ndezjen e LED duke përdorur një ndërprerje:
//Lidhni skedarin e kokës me përshkrimin e regjistrave të mikrokontrolluesit #include "stm32f10x.h" i panënshkruar ndër LED_timer; //Funksioni i thirrur nga funksioni i mbajtësit të ndërprerjeve të kohëmatësit të sistemit i pavlefshëm SysTick_Timer_main( i pavlefshëm) //përndryshe, nëse më pak se ose e barabartë me 1500, atëherë çaktivizoni //Le të zvogëlojmë variablin LED_timer //Nëse vlera e ndryshores ka arritur zero, vendosni një vlerë të re prej 2000 //Funksioni ynë kryesor i pavlefshëm kryesore( i pavlefshëm) //Aktivizo klockimin e autobusit të portit C //Deklaroni një strukturë për të konfiguruar portin //Mbush strukturën me vlera fillestare /* Shkruani numrin e pinit të portit në fushën GPIO_Pin të strukturës GPIO_Init_struct, të cilën do ta konfigurojmë më tej */ // Plotësoni fushat GPIO_Speed dhe GPIO_Mode në mënyrë të ngjashme //Ne e kalojmë strukturën e kompletuar për të kryer veprime për vendosjen e regjistrave // zgjidhni grupin e përparësisë për ndërprerjet //Konfiguro funksionimin e kohëmatësit të sistemit me një interval prej 1ms //Kyshi ynë kryesor i pafund |
Një pjesë e kodit burimor në skedarin "stm32f10x_it.c":
… /** i pavlefshëm SysTick_Handler( i pavlefshëm) |
Një shembull i një drafti funksional të programit për ndezjen e një LED duke përdorur një ndërprerje mund të shkarkohet nga lidhja.
Mbi këtë, historia ime në lidhje me bazat e zhvillimit të programeve për mikrokontrolluesin STM32 mund të konsiderohet e plotë. Unë kam dhënë të gjithë informacionin e nevojshëm për mundësinë e vetë-studimit të mëtejshëm të mikrokontrolluesve STM32. Materiali i ofruar është vetëm një fillestar, pasi një përshkrim i plotë i punës me mikrokontrolluesit nuk mund të përshkruhet brenda kornizës së asnjë artikulli. Përveç kësaj, studimi i mikrokontrolluesve pa fituar përvojë praktike është i pamundur, dhe përvoja reale vjen gradualisht me vite të tëra pune, eksperimente, me grumbullimin e zhvillimeve të ndryshme softuerike dhe harduerike, si dhe leximin e artikujve dhe dokumentacionit të ndryshëm për mikrokontrolluesit. Por mos lejoni që kjo t'ju trembë, sepse informacioni i dhënë në artikull është mjaft i mjaftueshëm për të krijuar pajisjen tuaj të parë në një mikrokontrollues, dhe ju mund të fitoni njohuri dhe përvojë të mëtejshme vetë, duke zhvilluar pajisje gjithnjë e më komplekse dhe më të mira çdo herë. dhe duke përmirësuar aftësitë tuaja.
Shpresoj se kam qenë në gjendje t'ju interesoj në studimin e mikrokontrolluesve dhe zhvillimin e pajisjeve bazuar në to, dhe puna ime do të jetë e dobishme dhe interesante për ju.
Deri në këtë pikë, ne kemi përdorur bibliotekën standarde të kernelit - CMSIS. Për të konfiguruar çdo port për mënyrën e dëshiruar të funksionimit, ne duhej t'i referoheshim për të gjetur regjistrin përgjegjës për një funksion të caktuar, si dhe të kërkonim informacione të tjera në lidhje me këtë proces në një dokument të madh. Gjërat do të marrin kthesa edhe më të dhimbshme dhe rutinë kur të fillojmë të punojmë me një kohëmatës ose ADC. Numri i regjistrave atje është shumë më i madh se ai i porteve I/O. Akordimi manual kërkon shumë kohë dhe rrit mundësinë për të bërë një gabim. Prandaj, shumë njerëz preferojnë të punojnë me bibliotekën standarde periferike - StdPeriph. Çfarë jep ajo? Është e thjeshtë - niveli i abstraksionit rritet, nuk keni nevojë të futeni në dokumentacion dhe të mendoni për regjistrat në pjesën më të madhe. Në këtë bibliotekë, të gjitha mënyrat e funksionimit dhe parametrat e periferisë MK përshkruhen në formën e strukturave. Tani, për të konfiguruar një pajisje periferike, është e nevojshme vetëm të thirrni funksionin e inicializimit të pajisjes me një strukturë të mbushur.
Më poshtë është një fotografi me një paraqitje skematike të niveleve të abstraksionit.
Ne kemi punuar me CMSIS (i cili është "më i afërt" me thelbin) për të treguar se si funksionon mikrokontrolluesi. Hapi tjetër është biblioteka standarde, të cilën do të mësojmë se si ta përdorim tani. Tjetra janë drejtuesit e pajisjes. Ata janë skedarë *.c \ *.h - që ofrojnë një ndërfaqe të përshtatshme softuerike për menaxhimin e çdo pajisjeje. Kështu, për shembull, në këtë kurs, ne do t'ju ofrojmë drejtues për çipin max7219 dhe modulin WiFi esp8266.
Një projekt standard do të përfshijë skedarët e mëposhtëm:
![](https://i0.wp.com/66.media.tumblr.com/3e7f15dfcd52555c25b4d7af99c034f7/tumblr_inline_occxcrxjf41t55lnu_500.png)
Së pari, sigurisht, janë skedarët CMSIS që lejojnë bibliotekën standarde të punojë me kernelin, për të cilin kemi folur tashmë. Së dyti, skedarët standardë të bibliotekës. Dhe së treti, skedarët e përdoruesve.
Skedarët e bibliotekës mund të gjenden në faqen kushtuar objektivit MK (për ne është stm32f10x4), në seksionin Burimet e projektimit(në CooCox IDE, këta skedarë shkarkohen nga depoja e mjedisit të zhvillimit). Çdo periferik korrespondon me dy skedarë - kokën (*.h) dhe kodin burimor (*.c). Një përshkrim i detajuar mund të gjendet në skedarin mbështetës, i cili është në arkiv me bibliotekën në sit.
- stm32f10x_conf.h - skedari i konfigurimit të bibliotekës. Përdoruesi mund të aktivizojë ose çaktivizojë modulet.
- stm32f10x_ppp.h - skedari i kokës periferike. Në vend të ppp, mund të jetë gpio ose adc.
- stm32f10x_ppp.c është një drejtues pajisjeje periferike e shkruar në gjuhën C.
- stm32f10x_it.h - skedari i kokës që përfshin të gjithë mbajtësit e mundshëm të ndërprerjeve (prototipet e tyre).
- stm32f10x_it.c - skedari i kodit burimor të shabllonit që përmban ndërprerje rutinë shërbimi (eng. rutina e shërbimit të ndërprerjes , ISR) për situata të jashtëzakonshme në Cortex M3. Përdoruesi mund të shtojë ISR-të e tij për pajisjet periferike të përdorura.
Biblioteka standarde dhe pajisjet periferike kanë një konventë në emërtimin e funksioneve dhe shënimeve.
- PPP është një akronim për një pajisje periferike, siç është ADC.
- Skedarët e sistemit, kokës dhe kodit burimor - filloni me stm32f10x_.
- Konstantet e përdorura në një skedar përcaktohen në atë skedar. Konstantet e përdorura në më shumë se një skedar përcaktohen në skedarët e kokës. Të gjitha konstantet në bibliotekën periferike më së shpeshti shkruhen me shkronja të mëdha.
- Regjistrat trajtohen si konstante dhe emërtohen gjithashtu me shkronja KAPITALE.
- Emrat e funksioneve periferike përmbajnë një akronim, të tillë si USART_SendData() .
- Çdo periferik është konfiguruar duke përdorur strukturën PPP_InitTypeDef, e cila i kalohet funksionit PPP_Init().
- Për deinitializimin (vendosjen e një vlere të paracaktuar), mund të përdorni funksionin PPP_DeInit().
- Funksioni që ju lejon të aktivizoni ose çaktivizoni pajisjet periferike quhet PPP_Cmd().
- Funksioni i aktivizimit/çaktivizimit të ndërprerjes quhet PPP_ITConfig.
Përsëri, listën e plotë mund ta gjeni në skedarin e mbështetjes së bibliotekës. Dhe tani le të rishkruajmë LED-in që pulson duke përdorur bibliotekën standarde periferike!
Para fillimit të punës, le të shohim skedarin stm32f10x.h dhe të gjejmë rreshtin:
#define USE_STDPERIPH_DRIVER
Nëse e konfiguroni projektin nga e para duke përdorur skedarët e bibliotekës nga arkivi i shkarkuar, atëherë do t'ju duhet ta çkomentoni këtë rresht. Kjo do t'ju lejojë të përdorni bibliotekën standarde. Ky përkufizim (makro) do të urdhërojë paraprocesorin të përfshijë skedarin stm32f10x_conf.h:
#ifdef USE_STDPERIPH_DRIVER #include "stm32f10x_conf.h" #endif
Modulet janë të përfshira në këtë skedar. Nëse keni nevojë vetëm për ato specifike, çaktivizoni pjesën tjetër, kjo do të kursejë kohë gjatë përpilimit. Ne, siç mund ta keni marrë me mend, kemi nevojë për module RTC dhe GPIO (megjithatë, në të ardhmen do të na duhen edhe _bkp.h , _flash , _pwr.h , _rtc.h , _spi.h , _tim.h , _usart.h ):
#include "stm32f10x_flash.h" // për init_pll() #include "stm32f10x_gpio.h" #include "stm32f10x_rcc.h"
Ashtu si herën e kaluar, fillimisht duhet të aktivizoni klockimin e portit B. Kjo bëhet nga funksioni i deklaruar në stm32f10x_rcc.h:
Void RCC_APB2PeriphClockCmd(uint32_t RCC_APB2Periph, FunctionalState NewState);
Numri FunctionalState përcaktohet në stm32f10x.h:
Typedef enum (DISABLE = 0, ENABLE = !DISABLE) FunctionalState;
Le të deklarojmë një strukturë për konfigurimin e këmbës sonë (mund ta gjeni në skedarin stm32f10x_gpio.h):
GPIO_InitTypeDef LED;
Tani duhet ta plotësojmë. Le të shohim përmbajtjen e kësaj strukture:
Struktura Typedef ( uint16_t GPIO_Pin; GPIOSpeed_TypeDef GPIO_Speed; GPIOMode_TypeDef GPIO_Mode; ) GPIO_InitTypeDef;
Të gjitha numërimet dhe konstantet e nevojshme mund të gjenden në të njëjtin skedar. Pastaj funksioni i rishkruar init_leds() do të marrë formën e mëposhtme:
Void led_init() ( // Aktivizo kronologjinë RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); // Deklaroni strukturën dhe plotësoni atë me LED GPIO_InitTypeDef; LED.GPIO_Pin = GPIO_Pin_0;LEDZE. (GPIOInit GPIOB, &LED); )
Le të rishkruajmë funksionin main():
Int main(void) ( led_init(); ndërsa (1) ( GPIO_SetBits (GPIOB, GPIO_Pin_0); vonesë (10000000); GPIO_ResetBits (GPIOB, GPIO_Pin_0); vonesë (10000000); )
Gjëja kryesore është të ndjeni rendin e inicializimit: aktivizoni ciklin e periferisë, deklaroni strukturën, mbushni strukturën, thirrni metodën e inicializimit. Pajisjet e tjera periferike zakonisht konfigurohen në një mënyrë të ngjashme.
Software i nevojshëm për zhvillim. Në këtë artikull do t'ju tregoj se si ta konfiguroni dhe lidhni siç duhet. Të gjitha mjediset komerciale si IAR EWARM ose Keil uVision zakonisht e bëjnë vetë këtë integrim, por në rastin tonë, gjithçka do të duhet të konfigurohet manualisht, duke shpenzuar shumë kohë për të. Avantazhi është se ju keni një shans për të kuptuar se si funksionon gjithçka nga brenda, dhe më tej të personalizoni gjithçka në mënyrë fleksibël për veten tuaj. Para fillimit të konfigurimit, merrni parasysh strukturën e mjedisit në të cilin do të punojmë:
Eclipse do të përdoret për të modifikuar lehtësisht skedarët e zbatimit të funksionit ( .c), skedarët e kokës ( .h), si dhe skedarët e montimit ( .S). Me "i përshtatshëm" nënkuptoj përdorimin e plotësimit të kodit, theksimit të sintaksës, rifaktorimit, navigimit përmes funksioneve dhe prototipeve të tyre. Skedarët ushqehen automatikisht te përpiluesit e duhur që gjenerojnë kodin e objektit (në skedarë .o). Deri më tani, ky kod nuk përmban adresa absolute të variablave dhe funksioneve, dhe për këtë arsye nuk është i përshtatshëm për ekzekutim. Skedarët e objekteve që rezultojnë bashkohen nga lidhësi. Për të ditur se cilat pjesë të hapësirës së adresave të përdorë, koleksionisti përdor një skedar të veçantë ( .ld), i cili quhet skript lidhës. Zakonisht përmban përkufizime të adresave të seksioneve dhe madhësive të tyre (seksioni i kodit i përcaktuar në flash, seksioni i ndryshueshëm i hartuar në RAM, etj.).
Në fund, lidhësi gjeneron një skedar .elf (Format i ekzekutueshëm dhe i lidhur), i cili përmban, përveç udhëzimeve dhe të dhënave, informacionin e korrigjimit të përdorur nga korrigjuesi. Për ndezje normale me vsprog, ky format nuk është i përshtatshëm, pasi kjo kërkon një skedar imazhi memorie më primitiv (për shembull, Intel HEX - .hex). Për ta gjeneruar atë, ekziston gjithashtu një mjet nga grupi Sourcery CodeBench (arm-none-eabi-objcopy), dhe ai integrohet në mënyrë të përsosur në eklips duke përdorur shtojcën e instaluar ARM.
Për të zbatuar vetë korrigjimin, përdoren tre programe:
- vetë eklipsi, i cili i lejon programuesit të përdorë "vizualisht" korrigjimin, të ecë përgjatë linjave, të lëvizë kursorin e miut mbi variabla për të parë vlerat e tyre dhe lehtësira të tjera
- arm-none-eabi-gdb - klienti GDB - korrigjues, i cili fshihet i kontrolluar nga eklipset (nëpërmjet stdin) si reagim ndaj veprimeve të specifikuara në paragrafin 1. Nga ana tjetër, GDB lidhet me serverin OpenOCD Debug dhe të gjitha komandat hyrëse përkthehen nga korrigjuesi GDB në komanda të kuptueshme për OpenOCD. Kanali GDB<->OpenOCD zbatohet mbi protokollin TCP.
- OpenOCD është një server korrigjimi që mund të komunikojë drejtpërdrejt me programuesin. Ai funksionon përpara klientit dhe pret për një lidhje TCP.
Kjo skemë mund t'ju duket shumë e padobishme: pse të përdorni klientin dhe serverin veçmas dhe të kryeni përkthimin e komandës edhe një herë, nëse e gjithë kjo mund të bëhej me një korrigjues? Çështja është se një arkitekturë e tillë teorikisht lejon që të bëhet shkëmbimi i klientit dhe serverit në mënyrë të përshtatshme. Për shembull, nëse duhet të përdorni një programues tjetër në vend të versaloon, i cili nuk do të mbështesë OpenOCD, por do të mbështesë një server tjetër special Debug (për shembull, texane / stlink për programuesin stlink - i cili është në tabelën e korrigjimit STM32VLDdiscovery), atëherë në vend të kësaj duke ekzekutuar OpenOCD, thjesht do të nisni serverin e dëshiruar dhe gjithçka duhet të funksionojë, pa ndonjë gjest shtesë. Në të njëjtën kohë, situata e kundërt është e mundur: le të themi se dëshironi të përdorni mjedisin IAR EWARM në vend të paketës Eclipse + CodeBench, së bashku me versionin versaloon. IAR ka klientin e vet të integruar Debug që do të komunikojë me sukses me OpenOCD dhe do ta drejtojë atë, si dhe do të marrë të dhënat e nevojshme si përgjigje. Sidoqoftë, e gjithë kjo ndonjëherë mbetet vetëm në teori, pasi standardet për komunikimin midis klientit dhe serverit nuk janë të rregulluara rreptësisht, dhe në disa vende ato mund të ndryshojnë, megjithatë, konfigurimet që tregova me st-link + eclipse dhe IAR + versaloon ishin të suksesshme për mua.
Në mënyrë tipike, klienti dhe serveri funksionojnë në të njëjtën makinë dhe lidhja me serverin ndodh në localhost:3333(Për openocd), ose localhost:4242(për texane/stlink st-util). Por askush nuk shqetësohet të hapë portin 3333 ose 4242 (dhe ta përcjellë këtë port në ruter në rrjetin e jashtëm) dhe kolegët tuaj nga një qytet tjetër do të jenë në gjendje të lidhin dhe korrigjojnë copën tuaj të hekurit. Ky truk përdoret shpesh nga ndërtuesit që punojnë në sajte të largëta me akses të kufizuar.
Fillimi
Ekzekutoni eclipse dhe zgjidhni File->New->C Project, zgjidhni llojin e projektit ARM Linux GCC (Sorcery G++ Lite) dhe emrin "stm32_ld_vl" (Nëse keni STV32VLDiscovery, atëherë do të ishte më logjike ta emërtoni "stm32_md_vl") :
Klikoni Finish, minimizoni ose mbyllni dritaren e mirëseardhjes. Pra, projekti është krijuar dhe dosja stm32_ld_vl duhet të shfaqet në hapësirën tuaj të punës. Tani ajo duhet të mbushet me bibliotekat e nevojshme.
Siç e kuptove nga emri i projektit, unë do të krijoj një projekt për pamjen e sundimtarit linjë vlerash me densitet të ulët(LD_VL). Për të krijuar një projekt për mikrokontrollues të tjerë, duhet të zëvendësoni të gjithë skedarët dhe përcaktimet në emrin e të cilëve ka _LD_VL (ose_ld_vl) për ato që ju nevojiten, sipas tabelës:
Pamje vizore | Emërtimi | Mikrokontrolluesit (x mund të ndryshojë) |
Linja e vlerës me densitet të ulët | _LD_VL | STM32F100x4 STM32F100x6 |
densitet të ulët | _LD | STM32F101x4 STM32F101x6 STM32F102x4 STM32F102x6 STM32F103x4 STM32F103x6 |
Linja e vlerës me densitet të mesëm | _MD_VL | STM32F100x8 STM32F100xB |
me densitet mesatar |
_MD |
STM32F101x8 STM32F101xB STM32F102x8 STM32F102xB STM32F103x8 STM32F103xB |
Linja e vlerës me densitet të lartë | _HD_VL | STM32F100xC STM32F100xD STM32F100xE |
densitet i lartë | _HD | STM32F101xC STM32F101xD STM32F101xE STM32F103xC STM32F103xD STM32F103xE |
XL-densitet | _XL | STM32F101xF STM32F101xG STM32F103xF STM32F103xG |
Linja e lidhjes | _CL | STM32F105xx dhe STM32F107xx |
Për të kuptuar logjikën e tabelës, duhet të njiheni me shenjat STM32. Kjo do të thotë, nëse keni VLDiscovery, atëherë më tej do të duhet të zëvendësoni gjithçka që lidhet me _LD_VL me _MD_VL, pasi çipi STM32F100RB i lidhur me linjën e vlerës me densitet të mesëm është bashkuar në zbulim.
Shtimi i CMSIS dhe biblioteka standarde periferike STM32F10x në projekt
CMSIS(Standard i ndërfaqes së softuerit të mikrokontrolluesit Cortex) - një bibliotekë e standardizuar për të punuar me mikrokontrolluesit Cortex që zbaton nivelin HAL (Hardware Abstraction Layer), domethënë ju lejon të abstraktoni nga detajet e punës me regjistra, duke kërkuar adresat e regjistrave duke përdorur fletë të dhënash, etj. Biblioteka është një grup kodesh burimore në C dhe Asm. Pjesa kryesore (Bërthama) e bibliotekës është e njëjtë për të gjitha kortekset (qoftë ST, NXP, ATMEL, TI apo kushdo tjetër), dhe është zhvilluar nga ARM. Pjesa tjetër e bibliotekës është përgjegjëse për pajisjet periferike, të cilat natyrisht ndryshojnë nga prodhuesi në prodhues. Prandaj, në fund, biblioteka e plotë ende shpërndahet nga prodhuesi, megjithëse pjesa e kernelit mund të shkarkohet ndaras nga faqja e internetit e ARM. Biblioteka përmban përkufizimet e adresave, kodin e inicializimit të gjeneratorit të orës (i përshtatshëm i konfigurueshëm me definicione) dhe gjithçka tjetër që e kursen programuesin nga futja manuale në projektet e tyre përkufizimin e adresave të të gjithë regjistrave periferikë dhe përcaktimin e biteve të vlerave të këto regjistra.
Por djemtë nga ST shkuan më tej. Përveç mbështetjes CMSIS, ata ofrojnë një bibliotekë tjetër për STM32F10x të quajtur Biblioteka standarde periferike(SPL) që mund të përdoret përveç CMSIS. Biblioteka ofron akses më të shpejtë dhe më të përshtatshëm në pajisjet periferike, dhe gjithashtu kontrollon (në disa raste) funksionimin e saktë me pajisjet periferike. Prandaj, kjo bibliotekë shpesh quhet një grup drejtues për modulet periferike. Ai shoqërohet me një paketë shembujsh të ndarë në kategori për pajisje të ndryshme periferike. Biblioteka është gjithashtu e disponueshme jo vetëm për STM32F10x, por edhe për seri të tjera.
Mund ta shkarkoni të gjithë versionin 3.5 të SPL+CMSIS këtu: STM32F10x_StdPeriph_Lib_V3.5.0 ose në faqen e internetit të ST. Zhblloko arkivin. Krijoni dosjet CMSIS dhe SPL në dosjen e projektit dhe filloni të kopjoni skedarët në projektin tuaj:
Çfarë të kopjoni | Ku të kopjoni (duke marrë parasysh |
Përshkrimi i skedarit |
Bibliotekat/CMSIS/CM3/ Mbështetja kryesore/ bërthamë_cm3.c |
stm32_ld_vl/CMSIS/ bërthamë_cm3.c | Përshkrimi i bërthamës Cortex M3 |
Bibliotekat/CMSIS/CM3/ Mbështetja kryesore/ bërthamë_cm3.h |
stm32_ld_vl/CMSIS/core_cm3.h | Titujt e përshkrimit të kernelit |
ST/STM32F10x/ system_stm32f10x.c |
stm32_ld_vl/CMSIS/system_stm32f10x.c | funksionet e inicializimit dhe kontrolli i orës |
Bibliotekat/CMSIS/CM3/DeviceSupport/ ST/STM32F10x/ system_stm32f10x.h |
stm32_ld_vl/CMSIS/system_stm32f10x.h | Titujt për këto funksione |
Bibliotekat/CMSIS/CM3/DeviceSupport/ ST/STM32F10x/ stm32f10x.h |
stm32_ld_vl/CMSIS/stm32f10x.h | Përshkrimi bazë i pajisjeve periferike |
Bibliotekat/CMSIS/CM3/DeviceSupport/ ST/STM32F10x/startup/gcc_ride7/ startup_stm32f10x_ld_vl.s |
stm32_ld_vl/CMSIS/startup_stm32f10x_ld_vl.S (!!! Vëmendje shtesë e skedarit CAPITAL S) |
Skedari i tabelës vektoriale ndërpret dhe init-ami mbi asm |
Projekt/STM32F10x_StdPeriph_Template/ stm32f10x_conf.h |
stm32_ld_vl/CMSIS/ stm32f10x_conf.h | Model për personalizim modulet periferike |
inc/ * |
stm32_ld_vl/SPL/inc/ * | SPL skedarët e kokës |
Bibliotekat/STM32F10x_StdPeriph_Driver/ src/ * |
stm32_ld_vl/SPL/src/ * | Zbatimi i SPL |
Pas kopjimit, shkoni te Eclipse dhe bëni Refresh në menynë e kontekstit të projektit. Si rezultat, në Project Explorer duhet të merrni të njëjtën strukturë si në foton në të djathtë.
Ju mund të keni vënë re se ka dosje për IDE të ndryshme në dosjen Libraries/CMSIS/CM3/DeviceSupport/ST/STM32F10x/startup/ (përpilues të ndryshëm përdoren në IDE të ndryshme). Zgjodha Ride7 IDE sepse përdor përpiluesin GNU Tools for ARM Embedded, i cili është i pajtueshëm me Sourcery CodeBench.
E gjithë biblioteka është konfiguruar duke përdorur paraprocesorin (duke përdorur defines), kjo do t'ju lejojë të zgjidhni të gjitha degët e nevojshme në fazën e përpilimit (ose më mirë, edhe para tij) dhe të shmangni ngarkesën në vetë kontrolluesin (që do të vërehej nëse konfigurimi u krye në RunTime). Për shembull, të gjitha pajisjet janë të ndryshme për vizore të ndryshme, dhe për këtë arsye, në mënyrë që biblioteka të "dijë" se cilin vizore dëshironi të përdorni, ju kërkohet të hiqni komentin në skedar stm32f10x.h një nga përcakton (që korrespondon me linjën tuaj):
/* #define STM32F10X_LD */ /*!< STM32F10X_LD: STM32 Low density devices */
/* #define STM32F10X_LD_VL */ /*!< STM32F10X_LD_VL: STM32 Low density Value Line devices */
/* #define STM32F10X_MD */ /*!< STM32F10X_MD: STM32 Medium density devices */
Etj...
Por unë nuk e rekomandoj ta bëni këtë. Nuk do t'i prekim skedarët e bibliotekës për momentin, por do të përcaktojmë definimin më vonë duke përdorur cilësimet e përpiluesit në Eclipse. Dhe më pas Eslipse do të thërrasë përpiluesin me çelësin -D STM32F10X_LD_VL, që për paraprocesorin është absolutisht ekuivalente me situatën nëse nuk komentoni "#define STM32F10X_LD_VL". Kështu, ne nuk do ta ndryshojmë kodin, si rezultat, nëse dëshironi, një ditë do të jeni në gjendje ta zhvendosni bibliotekën në një drejtori të veçantë dhe të mos e kopjoni atë në dosjen e çdo projekti të ri.
Skript lidhës
Në menynë e kontekstit të projektit, zgjidhni New->File->Tjetër->Të përgjithshme->Skedari, Next. Zgjidhni dosjen rrënjë të projektit (stm32_ld_vl). Futni emrin e skedarit "stm32f100c4.ld" (ose "stm32f100rb.ld" për zbulim). Tani kopjoni dhe ngjisni në eklips:
HYRJA (Reset_Handler) MEMORY ( FLASH (rx) : ORIGJINA = 0x08000000, GJATHËSIA = 16K RAM (xrw) : ORIGJINA = 0x20000000, GJATËZIA = 4K ) _estack = ORIGINRATH (M) ORIGIN(M) ; MIN_HEAP_SIZE = 0; MIN_STACK_SIZE = 256; SECTIONS ( /* Tabela e vektorit të ndërprerjes */ .isr_vector: ( . = ALIGN(4); KEEP(*(.isr_vector)) . = ALIGN(4); ) >FLASH /* Kodi i programit dhe të dhënat e tjera hyjnë në FLASH * / .text: ( . = ALIGN(4); /* Kodi */ *(.tekst) *(.tekst*) /* Konstantet */ *(.rodata) *(.rodata*) /* ARM->Thumb dhe Thumb-> Kodi i ngjitësit ARM */ *(.glue_7) *(.glue_7t) KEEP (*(.init)) KEEP (*(.fini)) .= ALIGN(4); _etext = .; ) >FLASH . ARM.extab: ( *(.ARM.extab* .gnu.linkonce.armextab.*) ) >FLASH .ARM: ( __exidx_start = .; *(.ARM.exidx*) __exidx_end = .; ) >FLASH .ARM. atributet: ( *(.ARM.atributet) ) > FLASH .array_preinit: ( PROVIDE_HIDDEN (__preinit_array_start = .); KEEP (*(.preinit_array*)) PROVIDE_HIDDEN (__preinit_array_end = .); (ID_EN_Rayit_FL = .); (ID_EN_DE_array_EN) = .); KEEP (*(SORT(.init_array.*))) KEEP (*(.init_array*)) PROVIDE_HIDDEN (__init_array_end = .); ) >FLASH .array_fini: ( PROVIDE_HIDDEN (__fini_array_KEEP = ); (.fini_array*)) KEEP (*(SORT(.fini_array.*))) PROVIDE_HIDDEN (__fundi_array_fini = .); ) >FLASH _sidata = .; /* Të dhënat e inicuara */ .data: AT (_sidata) ( . = ALIGN(4); _sdata = .; /* krijoni një simbol global në fillimin e të dhënave */ *(.data) *(.data*) . = ALIGN (4); _edata = .; /* përcaktoni një simbol global në fund të të dhënave */ ) >RAM /* Të dhëna të painitializuara */. = ALIGN (4); .bss: ( /* Kjo përdoret nga startup-i për të inicializuar seksionin .bss */ _sbss = .; /* të përcaktojë një simbol global në fillimin bss */ __bss_start__ = _sbss; *) *(ZAKON) .= ALIGN(4); _ebss = .; /* përcakto një simbol global në fundin bss */ __bss_end__ = _ebss; PROVIDE (_fund = _ebss); PROVIDE (__HEAP_START = _ebss); /* Seksioni User_heap_stack, përdoret për të kontrolluar nëse ka mbetur mjaftueshëm RAM */ ._user_heap_stack: ( . = ALIGN(4); . = . + MIN_HEAP_SIZE; . = . + MIN_STACK_SIZE; . = ALIGN(4); ) >RAM / HIQ/ : ( libc.a(*) libm.a(*) libgcc.a(*) ) )
Kjo l skripti inker do të projektohet posaçërisht për kontrolluesin STM32F100C4 (i cili ka 16 KB flash dhe 4 KB RAM), nëse keni një tjetër, do t'ju duhet të ndryshoni parametrat LENGTH të zonave FLASH dhe RAM në fillim të skedarin (për STM32F100RB, i cili është në Discovery: Flash 128K dhe RAM 8K).
Ne e ruajmë skedarin.
Konfigurimi i ndërtimit (C/C++ Build)
Shkoni te Projekti-> Vetitë-> Ndërtimi i C/C++-> Cilësimet-> Cilësimet e veglave dhe filloni të konfiguroni veglat e ndërtimit:
1) Pararendësi i synuar
Ne zgjedhim nën cilën bërthamë Cortex do të funksionojë përpiluesi.
- Procesori: cortex-m3
2) ARM Sourcery Linux GCC C Compiler -> Preprocessor
Shtojmë dy define-a duke i kaluar përmes çelësit -D te kompajleri.
- STM32F10X_LD_VL - përcakton vizoren (kam shkruar për këtë përkufizim më lart)
- USE_STDPERIPH_DRIVER - i tregon bibliotekës CMSIS se duhet të përdorë drejtuesin SPL
3) ARM Sourcery Linux GCC C Compiler -> Drejtoritë
Shtoni shtigje për të përfshirë bibliotekat.
- "$(workspace_loc:/$(ProjName)/CMSIS)"
- "$(workspace_loc:/$(ProjName)/SPL/inc)"
Tani, për shembull, nëse shkruajmë:
#include "stm32f10x.h
Pastaj përpiluesi duhet së pari të kërkojë skedarin stm32f10x.h në drejtorinë e projektit (ai e bën gjithmonë këtë), ai nuk do ta gjejë atë atje dhe do të fillojë të kërkojë në dosjen CMSIS, shtegun në të cilin kemi treguar, mirë, ai do ta gjejë atë.
4) ARM Sourcery Linux GCC C Compiler -> Optimization
Aktivizo optimizimin e veçorive dhe të të dhënave
- -funksion-seksione
- -fdata-seksione
Si rezultat, të gjitha funksionet dhe elementët e të dhënave do të vendosen në seksione të veçanta, dhe mbledhësi do të jetë në gjendje të kuptojë se cilat seksione nuk përdoren dhe thjesht t'i hedhë ato.
5) ARM Sourcery Linux GCC C Compiler -> General
Shtoni shtegun në skriptin tonë lidhës: "$(workspace_loc:/$(ProjName)/stm32f100c4.ld)" (ose sido që ta quani).
Dhe vendosni opsionet:
- Mos përdorni skedarë fillestarë standardë - mos përdorni skedarë standardë të fillimit.
- Hiqni seksionet e papërdorura - hiqni seksionet e papërdorura
Kjo është e gjitha, konfigurimi ka përfunduar. NE RREGULL.
Që nga krijimi i projektit, ne kemi bërë shumë gjëra dhe diçka që Eclipse mund të mos e vërë re, ndaj duhet t'i themi që të rishikojë strukturën e skedarëve të projektit. Për ta bërë këtë, nga menyja e kontekstit të projektit, duhet të bëni Indeksi -> rindërtimi.
Përshëndetje LED në STM32
Është koha për të krijuar skedarin kryesor të projektit: Skedari -> I ri -> C/C++ -> Skedari burimor. tjetër. Emri i skedarit Skedari burimor: main.c.
Kopjoni dhe ngjisni sa vijon në skedar:
#include "stm32f10x.h" uint8_t i=0; int main(void) ( RCC->APB2ENR |= RCC_APB2ENR_IOPBEN; // Aktivizo orën periferike PORTB RCC->APB1ENR |= RCC_APB1ENR_TIM2EN; // Aktivizo orën periferike TIM2 // Çaktivizo JTAG për lëshimin e PIN LED RCCAFR2_APREN-> AFIO->MAPR |= AFIO_MAPR_SWJ_CFG_JTAGDISABLE; // Pastro bitet e regjistrit të kontrollit PB4 dhe PB5 GPIOB->CRL &= ~(GPIO_CRL_MODE4 | GPIO_CRL_CNF4 | GPIO_CRL_MODE5 | GPIO_CRL_MODE5, si dhe në PushNx5 | GPIO_CRL_MODE5 | si PloshNx5, si Pul. 10 Mhz GPIOB->CRL |= GPIO_CRL_MODE4_0 | GPIO_CRL_MODE5_0; TIM2->PSC = SystemCoreClock / 1000 - 1; // 1000 shenjë/sek TIM2->ARR = 1000; // sec | // Aktivizo ndërprerjen tim2 TIM2->CR1 |= TIM_CR1_CEN; // Nis numërimin NVIC_EnableIRQ(TIM2_IRQn); // Aktivizo IRQ while(1); // Cikli i pafundësisë ) void TIM2_IRQHandler(void) ( TIM2->SR &=_F . GPIO_BSRR_B S5; // Vendos PB5 bit GPIOB->BSRR = GPIO_BSRR_BR4; // Rivendos bit PB4 ))
Edhe pse kemi përfshirë bibliotekat e SPL, ajo nuk është përdorur këtu. Të gjitha akseset në terren si RCC->APB2ENR janë të dokumentuara plotësisht në CMSIS.
Ju mund të ekzekutoni Project -> Build All. Nëse gjithçka funksionoi, atëherë skedari stm32_ld_vl.hex duhet të shfaqet në dosjen Debug të projektit. Ai u krijua automatikisht nga kukudh nga mjetet e integruara. Ne ndezim skedarin dhe shohim se si LED pulsojnë me një frekuencë prej një herë në sekondë:
Vsprog -sstm32f1 -ms -oe -owf -I /home/user/workspace/stm32_ld_vl/Debug/stm32_ld_vl.hex -V "tvcc.set 3300"
Natyrisht, në vend të /home/user/workspace/ ju duhet të futni rrugën tuaj për në hapësirën e punës.
Për zbulimin STM32VLD
Kodi është paksa i ndryshëm nga ai që dhashë më sipër për shallin tim të korrigjimit. Dallimi qëndron në kunjat në të cilat "varen" LED. Nëse në bordin tim ishte PB4 dhe PB5, atëherë në Discovery është PC8 dhe PC9.
#include "stm32f10x.h" uint8_t i=0; int main(void) ( RCC->APB2ENR |= RCC_APB2ENR_IOPCEN; // Aktivizo orën PORTC Periph RCC->APB1ENR |= RCC_APB1ENR_TIM2EN; // Aktivizo orën periferike TIM2 // Pastro bitet e regjistrit të kontrollit PC8 dhe PC8 &=GPI (GPIO_CRH_MODE8 | GPIO_CRH_CNF8 | GPIO_CRH_MODE9 | GPIO_CRH_CNF9) >ARR = 1000; // 1 Ndërprerje/sek (1000/100) TIM2->DIER |= TIM_DIER_UIE; // Aktivizo ndërprerjen tim2 TIM2->CR1 |= TIM_CR1_CEN; // Nis numërimin NVIC_EnableIRQ(TIM2_IRQn); while(1); // Cikli i pafundësisë ) void TIM2_IRQHandler(void) (TIM2->SR &= ~TIM_SR_UIF; //Pastro flamurin UIF nëse (1 == (i++ & 0x1)) (GPIOC->BSRR = GPIO_BSRR_BS8 ; / / Cakto PC8 bit GPIOC->BSRR = GPIO_BSRR_BR9; // Rivendos bit PC9 ) tjetër (GPIOC->BSRR = GPIO_BSRR_BS9; // Cakto PC9 bit GPIOC->BSRR = GPIO_BSRR_BR8; // Rivendos bit PC8 ) )
Nën Windows, mund të ndezni heksin që rezulton (/workspace/stm32_md_vl/Debug/stm32_md_vl.hex) me një mjet nga ST.
Epo, nën linux, mjeti st-flash. POR!!! Shërbimi nuk përdor hex të formatit Intel HEX (i cili gjenerohet si parazgjedhje), kështu që është jashtëzakonisht e rëndësishme të zgjidhni formatin binar në cilësimet për krijimin e një imazhi Flash:
Shtesa e skedarit nuk do të ndryshojë (hex do të mbetet ashtu siç ishte), por formati i skedarit do të ndryshojë. Dhe vetëm pas kësaj ju mund të bëni:
Shkruaj St-flash v1 /home/user/workspace/stm32_md_vl/Debug/stm32_md_vl.hex 0x08000000
Nga rruga, në kurriz të zgjerimit dhe formatit: zakonisht skedarët binar shënohen me shtesën .bin, ndërsa skedarët e formatit Intel HEX quhen ekstensioni .hex. Dallimi në këto dy formate është më shumë teknik sesa funksional: formati binar thjesht përmban byte instruksionesh dhe të dhënash që thjesht do t'i shkruhen kontrolluesit nga programuesi "siç është". IntelHEX, nga ana tjetër, nuk ka një format binar, por një tekst: saktësisht të njëjtat bajt ndahen në 4 bit dhe paraqiten karakter pas karakter në formatin ASCII, dhe përdoren vetëm karakteret 0-9, AF ( bin dhe hex janë sisteme numrash me baza të shumta, domethënë, 4 bit në kosh mund të përfaqësohen si një shifër e vetme në heks). Pra, formati ihex është më shumë se 2 herë më i madh se një skedar binar i rregullt (çdo 4 bit zëvendësohen nga një bajt + ndërprerje rreshtash për lexim të lehtë), por mund të lexohet në një redaktues teksti të rregullt. Prandaj, nëse do t'ia dërgoni këtë skedar dikujt, ose do ta përdorni në programe të tjera programuesi, atëherë këshillohet ta riemërtoni në stm32_md_vl.bin, në mënyrë që të mos mashtroni ata që do të shikojnë emrin e tij.
Kështu që ne konfiguruam montimin e firmuerit për stm32. Herën tjetër do t'ju tregoj se si
Kur sapo po filloni të programoni mikrokontrolluesit ose nuk jeni duke programuar për një kohë të gjatë, nuk është e lehtë të kuptoni kodin e dikujt tjetër. Pyetjet "Çfarë është ajo?" dhe "Nga erdhi?" shfaqen pothuajse në çdo kombinim shkronjash dhe numrash. Dhe sa më shpejt të jetë kuptimi i logjikës "çfarë? pse? dhe nga ku?", aq më e lehtë është të studiosh kodin e dikujt tjetër, duke përfshirë shembuj. Vërtetë, ndonjëherë duhet më shumë se një ditë për të "kaluar mbi kodin" dhe "të shikoni manualet" për këtë.
Të gjithë mikrokontrolluesit STM32F4xx kanë mjaft pajisje periferike. Secilës pajisje periferike të mikrokontrolluesve i caktohet një zonë memorie e caktuar, specifike dhe e pandryshueshme. Çdo zonë memorie përbëhet nga regjistra memorie, dhe këta regjistra mund të jenë 8-bit, 16-bit, 32-bit ose diçka tjetër, në varësi të mikrokontrolluesit. Në mikrokontrolluesin STM32F4, këta regjistra janë 32-bit dhe secili regjistër ka qëllimin e tij dhe adresën e tij specifike. Asgjë nuk i pengon programet tuaja që t'i qasen drejtpërdrejt duke specifikuar adresën. Në cilën adresë ndodhet ky apo ai regjistër dhe cilës pajisje periferike i përket, tregohet në hartën e memories. Për STM32F4, një kartë e tillë memorie disponohet në dokumentin DM00031020.pdf, i cili mund të gjendet në faqen e internetit st.com. Dokumenti quhet
RM0090
Manuali i referencës
STM32F405xx/07xx, STM32F415xx/17xx, STM32F42xxx dhe STM32F43xxx MCU të avancuara 32-bitësh të bazuar në ARM
Në seksion 2.3 Karta e memories në faqen 64, një tabelë fillon me adresat e zonave të regjistrit dhe përkatësinë e tyre në një pajisje periferike. Në të njëjtën tabelë, ekziston një lidhje me një seksion me shpërndarje më të detajuar të memories për çdo pajisje periferike.
Në të majtë në tabelë është diapazoni i adresave, në mes është emri i pajisjes periferike dhe në kolonën e fundit - ku mund të gjeni një përshkrim më të detajuar të ndarjes së memories.
Pra, për portet GPIO I/O për qëllime të përgjithshme në tabelën e shpërndarjes së memories, mund të zbuloni se adresat ndahen për to duke filluar nga 0x4002 0000. Porta GPIOA I / O me qëllim të përgjithshëm zë gamën e adresave nga 0x4002 000 në 0x4002 03FF. Porta GPIOB zë gamën e adresave 0x4002 400 - 0x4002 07FF. etj.
Për të parë një shpërndarje më të detajuar në vetë gamën, thjesht duhet të ndiqni lidhjen.
Ekziston edhe një tabelë këtu, por me një kartë memorie për gamën e adresave GPIO. Sipas kësaj harte memorie, 4 bajtët e parë i përkasin regjistrit MODER, 4 bajtët e tjerë i përkasin regjistrit OTYPER, e kështu me radhë. Adresat e regjistrit numërohen nga fillimi i diapazonit që i përket një porti specifik GPIO. Kjo do të thotë, çdo regjistër GPIO ka një adresë specifike që mund të përdoret gjatë zhvillimit të programeve për mikrokontrolluesin.
Por përdorimi i adresave të regjistrit është i papërshtatshëm për një person dhe është i mbushur me një numër të madh gabimesh. Prandaj, prodhuesit e mikrokontrolluesve krijojnë biblioteka standarde që e bëjnë më të lehtë punën me mikrokontrolluesit. Në këto biblioteka, adresat fizike shoqërohen me përcaktimin e shkronjave të tyre. Për STM32F4xx, këto korrespondenca janë të specifikuara në skedar stm32f4xx.h. Skedari stm32f4xx.h i përket bibliotekës CMSIS dhe ndodhet në dosjen Bibliotekat\CMSIS\ST\STM32F4xx\Include\.
Le të shohim se si përcaktohet porti GPIOA në biblioteka. Çdo gjë tjetër përcaktohet në të njëjtën mënyrë. Mjaft për të kuptuar parimin. Skedari stm32f4xx.h mjaft i madh dhe për këtë arsye është më mirë të përdorni kërkimin ose mundësitë që ofron zinxhiri juaj i veglave.
Për portin GPIOA, gjeni rreshtin që përmend GPIOA_BASE
GPIOA_BASE përcaktohet nëpërmjet AHB1PERIPH_BASE
AHB1PERIPH_BASE përcaktohet nga ana tjetër nëpërmjet PERIPH_BASE
Dhe nga ana tjetër, PERIPH_BASE përcaktohet si 0x4000 0000. Nëse shikoni hartën e kujtesës së pajisjeve periferike (në seksionin 2.3 Karta e memories në faqen 64), do ta shohim këtë adresë në fund të tabelës. Regjistrat e të gjithë periferisë së mikrokontrolluesit STM32F4 fillojnë nga kjo adresë. Kjo do të thotë, PERIPH_BASE është adresa fillestare e të gjithë periferisë së mikrokontrolluesve STM32F4xx në përgjithësi, dhe mikrokontrolluesit STM32F407VG në veçanti..
AHB1PERIPH_BASE përcaktohet si shuma e (PERIPH_BASE + 0x00020000). (shih fotot mbrapa). Kjo do të jetë adresa 0x4002 0000. Në kartën e kujtesës, portat e hyrjes / daljes GPIO me qëllime të përgjithshme fillojnë në këtë adresë.
GPIOA_BASE përcaktohet si (AHB1PERIPH_BASE + 0x0000), d.m.th. është adresa fillestare e grupit të regjistrit të portit GPIOA.
Epo, vetë porti GPIOA përcaktohet si një strukturë regjistrash, vendndodhja e të cilave në memorie fillon me adresën GPIOA_BASE (shih linjën #define GPIOA ((GPIO_TypeDef *) GPIOA_BASE).
Struktura e çdo porti GPIO përcaktohet si një lloj GPIO_TypeDef.
Pra, bibliotekat standarde, në këtë rast skedari stm32f4xx.h, thjesht humanizoni adresimin e makinës. Nëse shihni hyrjen GPIOA->ODR = 1234, atëherë kjo do të thotë se numri 1234 do të shkruhet në adresën 0x40020014. GPIOA ka një adresë fillestare prej 0x40020000 dhe regjistri ODR ka adresën 0x14 nga fillimi i diapazonit, kështu që GPIOA- >ODR ka adresën 0x40020014.
Ose, për shembull, nuk ju pëlqen hyrja GPIOA->ODR, mund ta përcaktoni #define GPIOA_ODR ((uint32_t *) 0x40020014) dhe merrni të njëjtin rezultat duke shkruar GPIOA_ODR = 1234;. Por sa e përshtatshme është kjo? Nëse vërtet dëshironi të prezantoni emërtimet tuaja, atëherë është më mirë thjesht të ricaktoni ato standarde. Si bëhet kjo, mund ta shihni në skedar stm32f4_discovery.h Për shembull, kjo është se si një nga LED-të përcaktohet atje:
#define LED4_PIN GPIO_Pin_12
#define LED4_GPIO_PORT GPIOD
#define LED4_GPIO_CLK RCC_AHB1Periph_GPIOD
Një përshkrim më i detajuar i periferisë së portit mund të gjendet në stm32f4xx_gpio.h