Kako podesiti pametne telefone i računare. Informativni portal
  • Dom
  • Programi
  • EasySTM32 - Portovi za mikrokontroler. STM32

EasySTM32 - Portovi za mikrokontroler. STM32

Razumijem da je članak već ispao prilično dugačak i svi žele na brzinu napisati nekakav program koji radi barem nešto korisno i učitati ga u kontroler, ali se slučajno dogodilo da su STM32 kontroleri nešto složeniji od najjednostavniji osmobitni komadi hardvera, pa ćemo sada ponovo, kako pišu neki nestrpljivi čitaoci, “ne o tome”.

U ovom dijelu ćemo govoriti o tome s čime najčešće moramo da operišemo pri radu sa kontrolerom - o radnim registrima Cortex-M3 jezgre, o njegovim režimima rada i kako se kontroler uključuje.

Dakle, postoji 13 registara u Cortex-M3 jezgru opće namjeneR0..R12, registar koji se koristi za pohranjivanje pokazivača steka je R13, registar komunikacija - R14, brojač komandi - R15 i 5 registara posebne namjene.

Registri opšte namene se dele na niski registri - R0..R7 I visoki registri - R8..R12. Razlika između njih je u tome što neke 16-bitne thumb-2 instrukcije mogu raditi samo sa niskim registrima, ali ne mogu raditi s visokim.

Zapravo, postoje dva registra R13, a ne jedan. Prvi se zove MSP - pokazivač glavnog steka, i drugo PSP - pokazivač steka procesa. Međutim, samo jedan od ovih registara je dostupan u svakom trenutku. Koji se utvrđuje u jednom od registara posebne namjene. Zašto je to potrebno? Ovo je učinjeno kako bi se operativni sistem mogao zaštititi (da, možete instalirati OS na ovaj kontroler ako želite) od pokvarenih aplikativnih programa. MSP koriste rukovaoci izuzetne situacije i svi programi koji koriste privilegirani nivo izvršavanja (na primjer, jezgro OS), a PSP koriste programi koji ne zahtijevaju privilegirani nivo izvršavanja (na primjer, aplikativni programi od kojih želimo da zaštitimo jezgro OS). Pokazivači steka moraju uvijek biti poravnati sa 32-bitnom granicom riječi, tj. njihova dva najmanje značajna bita moraju uvijek biti resetirana na nulu.

Registar R14 se zove LR (link register) - link registar i koristi se za pamćenje povratne adrese prilikom pozivanja potprograma.

Registar R15 se zove PC (programski brojač) - brojač programa i koristi se za pohranjivanje adrese naredbe koja se trenutno izvršava.

Sada o posebnim registrima.

Registar xPSR sadrži oznake za rezultate aritmetičkih i logičkih operacija, stanje izvršenja programa i broj prekida koji se trenutno obrađuje. Ponekad se o ovom registru piše plural. Ovo je učinjeno jer se njegova tri dijela mogu pristupiti kao zasebni registri. Ovi "podregistri" se zovu: APSR - Registar statusa aplikacije(ovdje su pohranjene zastave), IPSR - Registar statusa prekida(sadrži broj prekida koji se obrađuje) i EPSR - Registar statusa izvršenja. Kompletna struktura xPSR registra prikazana je na donjoj slici.

Zastavice u APSR registru su standardne:

  1. N (negativna zastavica) - negativan rezultat operacije
  2. Z (nula flag) — nula rezultat operacije
  3. C (carry flag) - nosi/posuđuje zastavicu
  4. V (overflow flag) - overflow flag
  5. Q (saturation flag) - zastavica zasićenja

PRIORITY MASK registar koristi samo nulti bit (PRIMASK), koji, kada je postavljen na jedan, onemogućuje sve prekide sa podesivim prioritetom. Nakon uključivanja, PRIMASK bit se resetuje na nulu - svi prekidi su omogućeni.

Registar MASKA GREŠKE također koristi samo nulti bit (FAULTMASK), koji, kada je postavljen na jedan, onemogućuje sve prekide i izuzetke osim nemaskiranog prekida (NMI). Nakon uključivanja, bit FAULTMASK se resetuje na nulu - svi prekidi su omogućeni.

BASEPRI registar se koristi za onemogućavanje svih prekida čija je vrijednost prioriteta veća ili jednaka onoj zabilježenoj u ovom registru. Ovdje se mora reći da što je niža vrijednost, to je viši nivo prioriteta. BASEPRI registar koristi samo nižih 8 bitova.

Registar CONTROL se koristi za kontrolu jednog od načina rada procesora—thread mode. Nulti bit ovog registra (nPRIV) određuje nivo izvršenja (privilegiran - privilegiran, ili neprivilegiran - neprivilegiran), a prvi bit (SPSEL) određuje pokazivač steka koji će se koristiti (MSP ili PSP). Razlika između privilegovanog i neprivilegovanog nivoa izvršenja je u tome što su za privilegovani nivo dostupne sve memorijske oblasti i sve instrukcije procesora, dok su za neprivilegovani nivo neka memorijska područja zatvorena (na primer, registri posebne namene, osim APSR, sistemsko područje) i , shodno tome, instrukcijama pristup ovim prostorima je zabranjen. Pokušaj izvršavanja nedozvoljenih naredbi koje pokušavaju pristupiti privatnim memorijskim područjima uzrokuje generiranje izuzetka neuspjeha.

Sada o načinima rada procesora.

Cortex-M3 procesor ima dva režima rada: režim navoja (Thread) i režim rukovanja (Handle). Način upravljanja se koristi za rukovanje izuzecima, a Thread mod se koristi za izvršavanje svih ostalih kodova. Prebacivanje iz jednog režima u drugi se dešava automatski. Kao što smo već rekli kada smo se bavili CONTROL registrom, u Thread modu procesor može koristiti i privilegirani i neprivilegirani nivo izvršavanja, au Handle modu - samo privilegirani. Slično, Thread mod može koristiti i glavni stog (MSP) i procesni stog (PSP), dok Handle način može koristiti samo glavni stek.

Važno je razumjeti da, na primjer, ako se prebacimo s privilegovanog nivoa na neprivilegirani nivo u Thread modu, izgubit ćemo pristup CONTROL registru i moći ćemo se vratiti samo u Handle modu. U Handle modu, nPRIV bit CONTROL registra se čita/piše, ali ne utječe na trenutni način izvršavanja. Ovo vam omogućava da promijenite razinu izvršenja koju će program imati kada procesor izađe iz načina rukovanja u način rada niti. SPSEL bit u Handle modu se ne može pisati i uvijek se čita kao nula, te se automatski vraća kada se iz moda rukovanja izađe u stream mod. Sve opcije za prelaze između različitih načina i nivoa izvršenja ilustrirane su usmjerenim grafom prikazanim na donjoj slici:

Kontroler se uvijek pokreće na internom oscilatoru, na frekvenciji od 8 MHz. Odakle u budućnosti dobiti signal sata, koliko ga pomnožiti ili podijeliti, konfigurira se u programu. Ako to nije urađeno u programu, onda spustite najmanje deset vanjskih kvarca, kontroler će i dalje raditi od internog oscilatora od 8 MHz.

Prilikom pokretanja, kontroler analizira kombinaciju nivoa na svoje dvije noge - BOOT0, BOOT1 i, ovisno o ovoj kombinaciji, počinje učitavanje ili iz flash memorije, ili iz RAM-a, ili iz sistemsko područje memorija. To se radi pomoću mehanizma pseudonimizacije koji smo već opisali. U teoriji, preuzimanje uvijek počinje od nulte adrese, ovisno o tome
kombinacije na nogama BOOT0, BOOT1 početne memorijske adrese se dodeljuju kao aliasi jednoj od tri oblasti: fleš, ugrađena RAM memorija ili sistemska oblast. Desno je ploča koja označava koja se oblast projektuje u početne memorijske adrese u zavisnosti od kombinacije nogu BOOT0, BOOT1.

Istovremeno, u oblasti sistema proizvođač je zaštićen poseban program(bootloader), koji vam omogućava da programirate fleš memoriju. Ali više o tome kasnije.

Prvo, kontroler čita 32-bitnu riječ na adresi 0x00000000 i stavlja je u registar R13 (pokazivač steka). Zatim čita 32-bitnu riječ na adresi 0x00000004 i stavlja je u registar R15 (programski brojač). Poslednja akcija uzrokuje prijelaz na početak programskog koda i tada počinje izvršavanje programa.

Riječ na adresi 0x00000004 (početna adresa glavnog programa) naziva se reset vektor. Generalno, u memoriji kontrolera, nakon pokazivača steka na adresi 0x00000000, počevši od adrese 0x00000004, treba da postoji tabela vektora izuzetaka i prekida, prvi vektor u kome je vektor resetovanja, a preostali vektori su adrese procedure za rukovaoce raznim izuzecima i prekidima. U najjednostavnijim programima, ako ne namjeravate da rukujete izuzecima i prekidima, svi ostali vektori osim vektora resetovanja mogu nedostajati. Skrećem vašu pažnju na činjenicu da su u vektorskoj tabeli naznačene adrese početka rukovalaca, a ne naredbe za odlazak do ovih rukovalaca, kao na primjer u 8-bitnim vrhovima ili atmels.

Dobar dan! Danas ćemo učiti o GPIO! I, prije svega, pogledajmo u kojim režimima mogu raditi I/O portovi u STM32F10x. A ovih modova postoji more, naime:

  • Ulaz plutajući
  • Input pull-up
  • Input-pull-down
  • Analog
  • Izlaz otvorenog odvoda
  • Izlaz push-pull
  • Alternativna funkcija push-pull
  • Alternativna funkcija otvorenog odvoda

A ako po našem mišljenju, onda kada radite na ulazu:

  • Prijava – Hi-Z
  • Ulaz – povucite se
  • Ulaz – spuštanje
  • Ulaz – analogni

Kada upravljamo izlaznim portom, imamo sljedeće opcije:

  • Izlaz - otvoreni kolektor
  • Izlaz – push-pull
  • Alternativne funkcije - Otvoreni izlaz kolektora
  • Alternativne funkcije - push-pull izlaz

Usput, evo dokumentacije za STM32F103CB -

Datasheet ima impresivnu tabelu koja pokazuje koje alternativne funkcije određeni pin ima.

Evo, na primjer, zaključaka PA9, PA10:

U koloni Default vidimo koje funkcije će ovi pinovi obavljati kada su konfigurirani za rad Alternativna funkcija. Odnosno, postavljanjem ovih pinova u skladu s tim, oni će se iz samo PA9 i PA10 pretvoriti u Rx I Tx za USART1. Čemu onda služi kolona? Remap? A ovo nije ništa drugo nego vrlo korisna funkcija remapping porta. Zahvaljujući remapu, Tx USARTA 'A, na primjer, može se kretati sa pina PA9 na PB6. Često se ova funkcija pokaže prokleto korisnom.

Pa, čini se da je sve više-manje jasno sa modovima, vrijeme je da pogledamo registre koji kontroliraju I/O portove.

Pošto smo upravo raspravljali u kojim režimima mogu postojati pinovi STM32F10x, hajde da odmah pogledamo kako se oni zapravo mogu prevesti u željeni način rada. A za to su dodijeljena dva registra - CRL i CRH. U prvom su konfigurisani pinovi od 0 do 7, u drugom od 8 do 15. Registri su, kao što se sjećate, 32-bitni. Odnosno, ima 32 bita po 8 pinova - to je 4 bita po pinu. Otvorite tablicu sa podacima i pogledajte:

Na primjer, trebamo konfigurirati PB5 nogu. Idemo u registar GPIOB->CRL i postavljamo odgovarajuće bitove kako nam je potrebno (na slici je 32-bitni CRL registar). Za PB5 ovo su bitovi:

Nakon osam bitova sve može izgledati prilično komplicirano i nekako nespretno, ali u stvari je sve implementirano prilično elegantno =). Da vidimo šta još ima.

Izlazni registar GPIOx_ODR - podsjeća na PORTx registar u AVR-u. Sve što uđe u ovaj registar odmah završi u vanjskom svijetu. Registar je 32-bitni i ima samo 16 krakova.Šta mislite za šta se koristi preostalih 16? Sve je vrlo jednostavno, bitovi registra od 15 do 31 se uopće ne koriste)

Ulazni registar GPIOx_IDR je analog PINx u AVR-u. Njegova struktura je slična pomenutoj ODR strukturi. Sve što se pojavi na ulazu mikrokontrolera odmah završava u ulaznom registru IDR.

Još dva korisna registra su GPIOx_BSSR i GPIOx_BRR. Oni vam omogućavaju da direktno promijenite vrijednosti bitova u ODR registru, bez korištenja uobičajenih bitnih maski. To jest, želim, na primjer, postaviti peti bit ODR-a na jedan. Upisujem jedan u peti bit GPIOx_BSSR i to je to, cilj je postignut. Odjednom sam poželio da resetujem peti bit ODR - jedinica od 5 bita GPIOx_BRR i to je to.

Dakle, pogledali smo glavne registre, ali, zapravo, u našim primjerima ćemo sve učiniti drugačije, koristeći Standardnu ​​perifernu biblioteku. Pa hajde da prošetamo po biblioteci. Datoteke su odgovorne za GPIO u SPL stm32f10x_gpio.h I stm32f10x_gpio.c. Otvaramo ih oba i vidimo puno nerazumljivih brojeva, slova, ikona itd.

U stvari, sve je vrlo jednostavno i jasno. Struktura je odgovorna za konfiguraciju porta GPIO_InitTypeDef.

typedef struktura (uint16_t GPIO_Pin; // Određuje GPIO pinove koje treba konfigurirati. Ovaj parametar može biti bilo koja vrijednost @ref GPIO_pins_define */ GPIOSpeed_TypeDef GPIO_Speed; // Određuje brzinu za odabrane pinove. Ovaj parametar može biti vrijednost @ref GPIOSpeed_TypeDef */ GPIOMode_TypeDef GPIO_Mode; // Određuje način rada za odabrane pinove. Ovaj parametar može biti vrijednost @ref GPIOMode_TypeDef */ ) GPIO_InitTypeDef;

Vidimo da struktura ima tri polja: GPIO_PIN, GPIO_Brzina I GPIO_Mode. Lako je pretpostaviti da je prvi odgovoran za broj pina porta koji želimo da konfigurišemo, drugi je za brzinu porta, a treći, zapravo, za režim rada. Dakle, da bismo prilagodili izlaz, samo trebamo deklarirati varijabla tipa strukturirati i popuniti svoja polja tražene vrijednosti. Sve moguće vrijednosti polja su upravo tu - unutra stm32f10x_gpio.h. Na primjer,

typedef enum ( GPIO_Mode_AIN = 0x0 , GPIO_Mode_IN_FLOATING = 0x04 , GPIO_Mode_IPD = 0x28 , GPIO_Mode_IPU = 0x48 , GPIO_Mode_Out_OD = 0x14 GPIO_Mode ,_GPIO_Mode , AF_ OD = 0x1C , GPIO_Mode_AF_PP = 0x18 ) GPIOMode_TypeDef;

Kreatori SPL-a su već izračunali sve vrijednosti, tako da možete konfigurirati bilo koji izlaz za rad u načinu rada Izlaz push-pull samo trebate postaviti polje u odgovarajuću strukturu: GPIO_Mode = GPIO_Mode_Out_PP.

Pa struktura je deklarisana, polja popunjena po potrebi, šta dalje? Na kraju krajeva, upravo smo kreirali varijablu. Kakve veze s tim imaju registri, mikrokontroleri i općenito elektronika? Uđimo u fajl stm32f10x_gpio.c i tamo nalazimo oblak razne funkcije za rad sa STM32 GPIO. Razmotrite funkciju GPIO_Init()(Neću davati kod, sve je u bibliotečkom fajlu). Dakle, ova funkcija precizno povezuje našu kreiranu strukturu i specifične registre kontrolera. To jest, ovoj funkciji prosljeđujemo varijablu, u skladu s kojom se postavljaju potrebni bitovi potrebnih registara mikrokontrolera. Sve je vrlo jednostavno, ali ništa manje briljantno. Pogledajte još neke datoteke biblioteke. Postoje funkcije za svaku priliku) Inače, vrlo je zgodno - prije funkcije stoji opis varijabli koje prihvata i vraća, kao i opis onoga što bi ova funkcija trebala raditi. Dakle, nije teško to shvatiti, ali morate malo tečno govoriti engleski. Mada bez toga se ne može ;)

Hajde da na trenutak napravimo pauzu od I/O portova i razmotrimo jednu prilično suptilnu tačku. Da biste koristili portove ili bilo koju drugu periferiju, MORATE omogućiti taktiranje. I portovi i periferni uređaji su u početku isključeni iz takta, tako da bez ove akcije ništa neće početi. Program će se kompajlirati, ali ništa zapravo neće raditi. Fajlovi su odgovorni za taktiranje u SPL stm32f10x_rcc.c I stm32f10x_rcc.h. Ne zaboravite ih dodati projektu.

Pređimo na programiranje. Kao što je uobičajeno, učinit ćemo da dioda treperi) Da bismo bolje razumjeli Standardnu ​​perifernu biblioteku, malo ćemo zakomplikovati uobičajeno treptanje diode - prozvat ćemo dugme, a ako se pritisne, dioda svijetli, u suprotnom ide van. Pokrećemo Keil, kreiramo projekat, dodajemo sve potrebne datoteke, ne zaboravite na CMSIS. Od SPL-a za ovaj projekat će nam trebati 4 fajla, već spomenuta gore. Kreiranje novog projekta opisano je u prethodnom članku obuka. Tamo možete pronaći i linkove do biblioteka)

Dakle, kod:

/******************************gpio.c******************** ***************/ //Poveži sve potrebne datoteke#include "stm32f10x.h" #include "stm32f10x_rcc.h" #include "stm32f10x_gpio.h" //Ovdje će biti kompletna inicijalizacija svih korištenih perifernih uređaja void initAll() ( //Deklarišemo varijablu porta tipa GPIO_InitTypeDef GPIO_InitTypeDef port; //Ovo je funkcija iz datoteke stm32f10x_rcc.c, omogućava taktiranje na GPIOA //GPIOA se nalazi na APB2 sabirnici RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE) ; //O ovoj funkciji ću pisati u nastavku GPIO_StructInit(& port) ; //Popunite polja strukture potrebnim vrijednostima //Prvi izlaz je ulaz za obradu pritiska na dugme - PA1 port.GPIO_Mode = GPIO_Mode_IPD; port.GPIO_Pin = GPIO_Pin_1; port.GPIO_Speed ​​= GPIO_Speed_2MHz; //I već smo pričali o ovoj funkciji //Zabilježimo samo jedan od parametara - pointer(!) na//naša struktura GPIO_Init(GPIOA, & port) ; //Konfigurirajte pin na kojem će dioda visjeti – PA0 port.GPIO_Mode = GPIO_Mode_Out_PP; port.GPIO_Pin = GPIO_Pin_0; port.GPIO_Speed ​​= GPIO_Speed_2MHz; GPIO_Init(GPIOA, & port) ; ) /*******************************************************************/ int main() ( //Deklarišemo varijablu za pohranjivanje stanja gumba uint8_t buttonState = 0; initAll() ; dok (1) ( //Koristeći funkciju iz SPL-a čitamo iz vanjskog svijeta // stanje dugmeta buttonState = GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_1) ; if (buttonState == 1 ) ( GPIO_SetBits(GPIOA, GPIO_Pin_0) ; ) else ( GPIO_ResetBits(GPIOA, GPIO_Pin_0) ; ) )) /******************************Kraj fajla******************** **********/

Inače, možda će neko obratiti pažnju na prisustvo zagrada ( ), uprkos samo jednoj instrukciji u telu ako I ostalo. I to je već navika) Veoma je preporučljivo pisati ovako, posebno kada razvijate velike projekte. Prilikom dodavanja/ispravljanja programa, nepažljivi programer možda neće obratiti pažnju na nedostatak zagrada i dodati drugu instrukciju, koja će, kao što razumijete, već biti u cijelom bloku ako ili ostalo. Isti problem sa ciklusima. Kada ima puno ljudi koji rade na projektu, nema garancija da neko neće biti nepažljiv, pa da ne bi gubili minute/sate na naknadna traženja dovratka, preporučujem da uvijek stavite ove zagrade) Iako će možda neko ne slažem se sa ovom logikom.

Pritisnemo F7, kompajliramo i sada je naš prvi program za STM spreman. Čini se da je kod dosta detaljno komentarisan, pa ću objasniti samo nekoliko stvari.

Funkcija GPIO_StructInit(&port)– uzima adresu varijable kao argument luka.

Ova funkcija popunjava polja strukture koja su joj proslijeđena kao argument sa zadanim vrijednostima. Ovo nije neophodno, ali kako biste izbjegli bilo kakve nepredvidive zastoje, bolje je uvijek pozvati ovu funkciju.

Još dvije funkcije koje smo koristili:

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

Pa pogađate čemu služe 😉

Tako smo završili sa pregledom STM32 I/O portova. U sljedećem članku ćemo se upoznati s Keilovim alatima za otklanjanje grešaka.

Glavni registri I/O porta STM32 mikrokontrolera

Port se odnosi na određeni imenovani skup nogu mikrokontrolera. U STM mikrokontrolerima se zovu GPIOA, GPIOB, GPIOC, itd. I/O portovi u STM32 mikrokontrolerima obično imaju 16 linija (noga). Linija se odnosi na jednu ili drugu nogu mikrokontrolera. Svaka linija porta može se konfigurirati na specifičan način i obavljati sljedeće funkcije:

  • digitalni ulaz;
  • digitalni izlaz;
  • ulaz eksternog prekida;
  • I/O funkcija ostalih modula mikrokontrolera.

Možete konfigurirati port za željeni način rada pomoću registara mikrokontrolera. Ovim registrima se može pristupiti direktno ili se mogu koristiti posebne metode iz periferne biblioteke.

Pogledajmo glavne registre potrebne za rad sa I/O portovima.

Registri odgovorni za konfiguraciju porta

Prije nego što počnete raditi s izlaznim portom, morate ga konfigurirati tako da odgovara vašim potrebama.

Konfiguracijski registri su odgovorni za postavljanje ili konfiguraciju porta. U mikrokontrolerima porodica STM32F302xx, STM32F303xx i STM32F313xx, ovo su sljedeći registri:

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

Registrirajte GPIOx_MODER (gdje je x=A...F)

Ovo je 32 bit Registar je odgovoran za način rada linije. Za konfiguraciju određene linije potrebna su 2 bita. Moguće su sljedeće kombinacije:

  • 00 - linija je konfigurisana za unos;
  • 01 - izlaz;
  • 10 - alternativni način rada;
  • 11 - analogni način rada.

Registrirajte GPIOx_TYPER (gdje je x=A...F)


Ovaj registar se koristi za konfigurisanje tipa operacije na liniji; koristi 16 bitova u radu, preostalih 16 je rezervisano. Prihvata sljedeće vrijednosti:

  • 0 - push-pull mod;
  • 1 - otvoreni odvod

Registrirajte GPIOx_PUPDR (gdje je x=A...F)


Za zatezanje je odgovoran registar podataka. Prihvata sljedeće vrijednosti:

  • 00 - nema lift
  • 01 - povlačenje na napajanje plus
  • 10 - povlačenje do tla

Registrirajte GPIOx_SPEEDR (gdje je x=A...F)


Registar za podešavanje brzine linije.

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

Izlazni registar (izlazni registar) GPIOx_ODR (gdje je x=A…F) – registar izlaznih podataka


Ovaj registar se koristi za izlaz podataka na port. Prilikom pisanja određenih vrijednosti u ovaj registar slična vrijednost je postavljena na liniji (kraku). Pošto imamo 16 linija, a registar ima 32 bita, koristi se samo prvih (nižih) 16 bita.

Ulazni registar (status porta ili registar ulaznih podataka) GPIOx_IDR (gdje je x=A…F) – registar ulaznih podataka


Ovaj registar je samo za čitanje. Neka vrsta indikatora statusa porta. Analog PINx-a u AVR seriji mikrokontrolera.

Registar bitova izlaznog porta GPIOx_BSRR


Ovaj registar postavlja stanje pina u izlaznom portu bit po bit.

Više informacija o svim registrima određenog mikrokontrolera možete pronaći u referens manual.pdf koji se može preuzeti sa službene web stranice www.st.com

Konfiguriranje porta mikrokontrolera pomoću biblioteke

Port se također može konfigurirati pomoću posebne biblioteke koja sadrži različite metode za rad sa I/O registrima, a deklarirane su posebne varijable. Ova biblioteka oslobađa programera od potrebe da "ručno" izračunava koja vrijednost treba upisati u određeni registar.

Pogledajmo primjer koda koji uključuje LED

#include "stm32f30x.h" // Zaglavlje uređaja #include "stm32f30x_rcc.h" #include "stm32f30x_gpio.h" void init_led(void) ( RCC_APB2PeriphClockCmd (GPIOE,ENABLE); GPIODENSITU_GPIODENICNIT; ture.GPIO_Spe ed = GPIO_Speed_2MHz; // Postavite maksimalnu brzinu GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT; //Izlazni način rada GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8; //Naznačite pin na koji je LED spojen GPIO_Init(GPIOE,GPIO_InitStructure) (Glavnu strukturu uvedenog u strukturu); (); GPIO_SetBits( GPIOE,GPIO_Pin_8); //Postavi pin u visoko stanje while(1) ( _NOP(); ) )

Radi uštede energije sve periferne jedinice mikrokontrolera su onemogućene. Da biste "aktivirali" jednu ili drugu periferiju, prvo morate na nju primijeniti sat signala.

Radimo sa GPIOE portom tako da moramo omogućiti taktiranje pomoću metode

RCC_APB2PeriphClockCmd(uint32_t RCC_APB2Periph, FunctionalState NewState);

Što uzima dvije vrijednosti: prva je sam stvarni port koji trebamo omogućiti, a druga je stanje ove luke, uključeno ili isključeno.

Kada se dogodi neki događaj, kontroler prekida automatski prekida izvršenje glavnog programa i poziva odgovarajuću funkciju za rukovanje prekidom. Nakon što funkcija rukovaoca prekidom izađe, program nastavlja sa izvršavanjem od tačke na kojoj je došlo do prekida. Sve se dešava automatski (ako je NVIC ispravno konfigurisan, ali više o tome u nastavku).

Iz samog imena jasno je da NVIC kontroler podržava ugniježđenje prekida i prioritete. Svakom prekidu se dodjeljuje vlastiti prioritet prilikom konfigurisanja NVIC-a. Ako dođe do prekida visokog prioriteta dok se obrađuje prekid niskog prioriteta, on će zauzvrat prekinuti rukovalac prekida niskog prioriteta.

Kako radi?

Ovaj post ne tvrdi da je potpuno potpun; savjetujem vam da proučite odjeljak o prekidima u Cortex™-M3 tehničkom referentnom priručniku. Kako ovaj dio jezgre nije pretrpio promjene, njegov opis je dat u prvoj reviziji r1p1 za Cortex-M3 jezgro.
Ulazak i izlazak iz prekida
Kada se pokrene prekid, NVIC prebacuje jezgro u režim obrade prekida. Nakon prelaska u režim obrade prekida, registri kernela se stavljaju na stek. Direktno kada se vrijednosti registra upišu u stog, dohvaća se početna adresa funkcije za rukovanje prekidom.

Registar statusa programa ( Registar statusa programa (PSR)), brojač programa ( Brojač programa (PC)) i komunikacijski registar ( Link registar (LR)). Opis registara kernela dat je u Cortex-M4 Generičkom korisničkom vodiču. Zahvaljujući tome, pamti se stanje u kojem je jezgra bila prije ulaska u režim obrade prekida.

Registri R0 - R3 i R12 se također čuvaju. Ovi registri se koriste u uputstvima za prosleđivanje parametara, tako da ih guranjem u stog čini mogućim za upotrebu u funkciji usluge prekida, a R12 često deluje kao radni registar programa.

Po završetku obrade prekida, sve radnje će se izvršiti u obrnutim redosledom: Sadržaj steka se iskače i paralelno se dohvaća povratna adresa.

Od trenutka kada je prekid pokrenut do izvršenja prve naredbe rukovaoca prekidom, prekid traje 12 ciklusa takta, isto vrijeme je potrebno za nastavak glavnog programa nakon završetka obrade prekida.

Prekini gniježđenje
Kao što je gore spomenuto, NVIC podržava prekide s različitim prioritetima, koji mogu međusobno prekidati. U tom slučaju mogu se pojaviti različite situacije čija se obrada različito optimizira.

1. Obustavite prekid niskog prioriteta
U ovoj situaciji se zaustavlja obrada prekida niskog prioriteta. Tokom sljedećih 12 ciklusa, novi skup podataka se pohranjuje u stog i započinje obrada prekida visokog prioriteta. Nakon što se obradi, sadržaj steka se automatski iskače i obrada prekida niskog prioriteta se nastavlja.
Nema većih razlika u odnosu na prekid glavnog programa.

2. Kontinuirana obrada prekida
Ova situacija može nastati u dva slučaja: ako dva prekida imaju isti prioritet i nastaju istovremeno, ako dođe do prekida niskog prioriteta dok se obrađuje prekid visokog prioriteta.
U ovom slučaju, na steku se ne izvode nikakve međuoperacije. Učitava se samo adresa rukovaoca prekida niskog prioriteta i dolazi do prijelaza na njegovo izvršenje. Eliminisanje stek operacija štedi 6 ciklusa takta. Prijelaz na sljedeći prekid se ne događa u 12 taktova, već u samo 6.

3. Kašnjenje prekida visokog prioriteta
Situacija nastaje ako dođe do prekida visokog prioriteta tokom prelaska na obradu niskog prioriteta (u toku tih istih 12 taktova). U ovom slučaju, prijelaz na prekid visokog prioriteta će se desiti najmanje 6 taktova od trenutka kada se dogodi (vrijeme potrebno za učitavanje adrese rukovaoca prekida i prelazak na nju). Povratak na niski prioritet je već opisan gore.

Prioriteti prekida
Pored jednostavnog postavljanja prioriteta prekida, NVIC implementira mogućnost grupiranja prioriteta.
Prekidi u grupi sa više od visok prioritet rukovaoci prekida grupa sa nižim prioritetom mogu biti prekinuti. prekidi iz iste grupe, ali sa različitim prioritetima unutar grupe, ne mogu prekidati jedni druge. Prioritet unutar grupe samo određuje redoslijed kojim se rukovalac poziva kada su oba događaja aktivirana.

Vrijednost prioriteta prekida se postavlja u registrima Registri prioriteta prekida(Pogledajte generički korisnički vodič za Cortex-M4). U ovom slučaju, neki bitovi su odgovorni za prioritet grupe u kojoj se nalazi prekid, a neki su odgovorni za prioritet unutar grupe.
Postavljanje distribucije bitova na grupni prioritet ili prioritet unutar grupe vrši se pomoću registra Kontrolni registar prekida i resetovanja aplikacije(OPREZ!!! pogledajte Cortex-M4 Generički korisnički priručnik).

Kao što ste možda primijetili, Cortex-M4 Generički korisnički vodič navodi da su postavke prioriteta i grupiranje prioriteta specifične za implementaciju. implementacija definisana.
Ali ono što slijedi nije baš prijatna stvar. Gotovo da nema informacija o NVIC-u u STM32F407 MK. Ali postoji veza do zasebnog dokumenta. Da biste razumeli implementaciju NVIC-a u STM32, moraćete da pročitate još jedan dokument -. Uopšteno govoreći, savjetujem vam da pažljivo proučite ovaj dokument a po svim ostalim pitanjima, opisuje rad kernela detaljnije nego u dokumentaciji iz ARM-a.
U njemu već možete pronaći:

Programabilni nivo prioriteta od 0-15 za svaki prekid. Viši nivo odgovara a
niži prioritet, tako da je nivo 0 najviši prioritet prekida

Od mogućih 8 bitova prioriteta, koriste se samo 4. Ali to je sasvim dovoljno za većinu zadataka.
Maskiranje prekida
Pretpostavimo da imamo zadatak da lansiramo lansirno vozilo kada se pritisne crveno dugme, ali samo ako je ključ okrenut.
Apsolutno nema smisla generirati prekid za okretanje ključa. Ali biće nam potreban prekid zbog pritiska na crveno dugme. Da biste omogućili/onemogućili različite vektore prekida, postoji maskiranje prekida.
Maskiranje prekida se vrši pomoću registara Registri koji omogućavaju postavljanje prekida .
Ako je prekid maskiran, to ne znači da periferija ne generiše događaje! Samo što NVIC ne poziva rukovaoca za ovaj događaj.
Tablica vektora prekida
Svi mogući prekidi koje podržava NVIC se snimaju u tabeli vektora prekida. U suštini, tabela vektora prekida nije ništa drugo do lista adresa funkcija rukovaoca prekidom. Broj na listi odgovara broju prekida.
Kreirajte vektorsku tabelu i postavite je na pravo mesto
Da bi tabela vektora sa tačne adrese funkcije rukovaoca prekida nalaze se na početku fleš memorije MK-a, kreiraćemo i povezati startup.c fajl sa projektom.

Sadržaj datoteke

// Omogući IAR ekstenzije za ovu izvornu datoteku. #pragma language=extended #pragma segment="CSTACK" // Proslijedi deklaracija zadanih rukovatelja greškama. void ResetISR(void); statička praznina NmiSR(praznina); statička praznina FaultISR(void); static void IntDefaultHandler(void); // Ulazna točka za kod za pokretanje aplikacije. ekstern void __iar_program_start(void); ekstern void EXTI_Line0_IntHandler(void); eksterni void EXTI_Line6_IntHandler(void); // Unija koja opisuje unose vektorske tabele. Unija je potrebna // jer je prvi unos pokazivač steka, a ostatak su funkcijski // pokazivači. typedef union ( void (*pfnHandler)(void); void * ulPtr; ) uVectorEntry; // Tablica vektora. Imajte na umu da se na ovo moraju postaviti odgovarajuće konstrukcije // kako bi se osiguralo da završi na fizičkoj adresi 0x0000.0000. __root const uVectorEntry __vector_table @ ".intvec" = ( ( .ulPtr = __sfe("CSTACK") ), // Početni pokazivač steka ResetISR, // Rukovalac resetovanja NmiSR, // NMI rukovalac FaultISR, // Teška greška Handler IntodeFaulthandler, // MPU FAULT HANDLER IntDEFULTHANDER, // Bus Fault Handler IntDEFULTHANDER, // USAGE FAULT HANDLENER INTDEFULTHANDER, // DSERVEDDD Intodefaulthandler, // Rezervirano Intdefaulthaner, // Def. Handler IntefaulTaander, // Debug Monitor Handler IntDefaultHandler, // Rezervirani IntDefaultHandler, // PendSV Handler IntDefaultHandler, // SysTick Handler // Eksterni prekidi IntDefaultHandler, // Window WatchDog IntDefaultHandler, // PVD kroz EXTI Line detection IntDefaultHandler, // Tampers kroz vrijemeSta EXTI linija IntDefaultHandler, // RTC Wakeup kroz EXTI liniju IntDefaultHandler, // FLASH IntDefaultHandler, // RCC EXTI_Line0_IntHandler, // EXTI Line0 IntDefaultHandler, // EXTI Line1 IntDefaultHandler, // EXTI Line1 IntDefaultHandler, // EXTI Line2, IntDefault // EXTI Line2 i //tDefault. EXTI Line4 IntDefaultHandler, // DMA1 Stream 0 IntDefaultHandler, // DMA1 Stream 1 IntDefaultHandler, // DMA1 Stream 2 IntDefaultHandler, // DMA1 Stream 3 IntDefaultHandler, // DMA1 Stream 4 IntDefaultHandler, // DMA1 Stream1 Stream 5 //t DMAH Stream 5 // 6 IntDefaultHandler, // ADC1, ADC2 i ADC3s IntDefaultHandler, // CAN1 TX IntDefaultHandler, // CAN1 RX0 IntDefaultHandler, // CAN1 RX1 IntDefaultHandler, // CAN1 SCE EXTI_Line6_IntHandler, // i TH Vanjski i Default Lines Int. // TIM1 ažuriranje i Tim10 Intdefaulthaner, // Tim1 Trigger i komunikacija i TIM11 IntDEFAULTHANDER, // TIM1 Capture Comparee IntefaultHander, // TIM2 IntDE Faulthandler, // TIM3 IntDEFAULTHANDER, // TIM4 IntDEFULTHANDER, // TIM4 IntDEFULTHANDER, // TIM4 IntDEFULTHANDER, // I IntDefaultHandler, // I2C2 Event IntDefaultHandler, // I2C2 Error IntDefaultHandler, // SPI1 IntDefaultHandler, // SPI2 IntDefaultHandler, // USART1 IntDefaultHandler, // USART2 IntDefaultHandler, // USART3 IntDefaultHandler, // ADefaultHandler, // A Zadani i vanjski redovi A i B) kroz EXTI Line IntDefaultHandler, // USB OTG FS Wakeup kroz EXTI liniju IntDefaultHandler, // TIM8 Break i TIM12 IntDefaultHandler, // TIM8 Update i TIM13 IntDefaultHandler, // TIM8 Trigger and Commutation i TIM14 TIMDefaultHandler, // Usporedi IntDefaultHandler, // DMA1 Stream7 IntDefaultHandler, // FSMC IntDefaultHandler, // SDIO IntDefaultHandler, // TIM5 IntDefaultHandler, // SPI3 IntDefaultHandler, // UART4 IntDefaultHandler, // UART5 IntDefaultHandler, // UART5 IntDefaultHandler, // TIM6 i underru DAC1 greška / TIM7 IntDEFAULTHANDER, // DMA2 Stream 0 Intdefaulthaner, // DMA2 Stream 1 IntDEFULTHANDER, // DMA2 Stream 2 Intdefaulthaner, // DMA2 Stream 3 IntdefaultHandler, // DMA2 Stream 4 IntdefaultHandler, // DMA2 Stream 4 Intodefaulthaner, //i Intodefaulthandler Ethernet, //i Intodefaulthandler Line IntDefaultHandler, // CAN2 TX IntDefaultHandler, // CAN2 RX0 IntDefaultHandler, // CAN2 RX1 IntDefaultHandler, // CAN2 SCE IntDefaultHandler, // USB OTG FS IntDefaultHandler, // DMA2 Stream 5 IntDefaultHandler, // DMAtDefaultHandler // DMAtDefaultHandler // IntDefaultHandler DMA2 Stream 7 IntDefaultHandler, // USART6 IntDefaultHandler, // I2C3 događaj IntDefaultHandler, // I2C3 greška IntDefaultHandler, // USB OTG HS End Point 1 Out IntDefaultHandler, // USB OTG HS End Point 1 U IntDefaultHandler, // Wakeup OTGHS kroz EXTI IntDefaultHandler, // USB OTG HS IntDefaultHandler, // DCMI IntDefaultHandler, // CRYP crypto IntDefaultHandler, // Hash i Rng IntDefaultHandler, // FPU ); // Ovo je kod koji se poziva kada procesor prvi put započne izvršavanje // nakon događaja resetiranja. Izvodi se samo apsolutno neophodan skup, // nakon čega se poziva rutina entry() koju je dostavila aplikacija. Sve fensi // radnje (kao što je donošenje odluka na osnovu registra uzroka resetovanja i // resetovanje bitova u tom registru) prepuštene su isključivo rukama // aplikacije

Upotreba
@ ".intvec" Smješta __vector_table na početak sekcije deklarirane u datoteci povezivača. Sam fajl možete pogledati ovde:

Sam odeljak je naveden na početku ROM memorija. Adrese se mogu pogledati (dokument koji opisuje adresiranje STM32 flash memorije):

Kombinacija IAR direktive i posebne funkcije IAR-a:
#pragma segment="CSTACK" __sfe("CSTACK") Zapisuje pokazivač na vrh steka na početku fleša.

Sama tabela je ispunjena adresama funkcija koje implementiraju vječnu petlju. Izuzetak je napravljen samo za funkcije koje nas zanimaju:
ekstern void EXTI_Line0_IntHandler(void); eksterni void EXTI_Line6_IntHandler(void);
U funkciji koja se poziva pri pokretanju, jednostavno se vrši prijelaz na
ekstern void __iar_program_start(void); Ova funkcija je main(). Sam simbol se po želji može redefinirati:

Idemo na glavni fajl
Prvo, napišimo i redefinirajmo sve adrese i bitna polja koja su nam potrebna.

Listing

//Definitions for SCB_AIRCR register #define SCB_AIRCR (*(unsigned volatile long*)0xE000ED0C) //acces to SCB_AIRCR #define SCB_AIRCR_GROUP22 0x05FA0500 //change priority data //Definitions for RCC_AHB1_ENR register #define RCC_AHB1_ENR (*(unsigned volatile long *) (0x40023830)) //pristup na RCC_AHB1ENR reg #define RCC_AHB1_ENR_GPIOA 0x1 //GPIOA bitfield #define RCC_AHB1_ENR_GPIOC 0x4 //GPIOC bitfield #define RCC_AHB1_ENR_2 //define RCC_AHB1_ENR_ENR_GPIOD_GPIOD_GPIOD polje //GODPI R registar # definiše RCC_APB2_ENR (*(nepotpisano dugotrajno * )(0x40023844)) //pristup na RCC_APB2ENR reg #define RCC_APB2_ENR_SYSCFG 0x4000 //SYSCFG bitfield //Definicije za GPIO MODE registre #define GPIOA_MODER (*(unsigned volatile acces long *) g #define GPIOC_ MODER (*(unsigned volatile long*)(0x40020800)) //pristup GPIOC_MODER reg #define GPIOD_MODER (*(unsigned volatile long*)(0x40020C00)) //pristup GPIOD_MODER reg //GPIO ODR G registra definicija #deOfine * (unsigned volatile long*)(0x40020C14)) //pristup GPIOD_MODER reg #define GPIO_ODR_13PIN 0x2000 #define GPIO_ODR_14PIN 0x4000 //Bitfields definicije #define GPIOTS_MODER bit //define3GPIO_MODERe GPIO_MODER DER_0IN 0x0 //Pin 0 način unosa # define GPIO_MODER_6BITS 0x300 //Pin 6 mod bitova #define GPIO_MODER_6IN 0x000 //Pin 6 ulazni mod #define GPIO_MODER_13BITS 0xC000000 //Pin 13 mod bitova #define GPIO_MODER_6IN 0x000 //define GPIO_dein00 mod #define GPIO_de100 e GPIO_MODER _14BITS 0x30000000 //Pin 14 mod bitova #define GPIO_MODER_14OUT 0x10000000 //Pin 14 izlazni način //GPIOC_PUPDR definicija registra #define GPIOC_PUPDR (*(unsigned volatile long*)(0x4002080C)) //pristup GPIOC_PUPDR GPI reg #BIOC_PUPDR bit #BIP0000 //define0 fino GPIOC_PUPDR_6 PU 0x1000 / /PC6 pull-up enable //SYSCFG_EXTIx definicije registra #define SYSCFG_EXTICR1 (*(unsigned volatile long*)0x40013808) //SYSCFG_EXTICR1 pristup #define SYSCFG_EXTICR1_0BITS 0xSC #define SYSCFG_EXTICR #EXTI_0BIT 0xF #EXTI_0 / CR_EX00 TI 0 - port A #defini SYSCFG_EXTICR2(* (unsigned volatile long*)0x4001380C) //SYSCFG_EXTICR2 pristupa #define SYSCFG_EXTICR2_6BITS 0xF00 //EXTI 6 bitova #define SYSCFG_EXTICR2_6PC port //define SYSCFG_EXTICR2_6PC //EXTI #EXTI R (*(nepotpisano promjenjivo dugo*) 0x40013C 00) / /EXTI_IMR reg acces #define EXTI_LINE0 0x1 //LINE 0 definicija #define EXTI_LINE6 0x40 //LINE 6 definicija #define EXTI_RTSR (*(nepotpisana volatile long*)0x40013c refined #EX40013C0 ( Neigned isparljive duge *) 0x40013C0C) // exti_ftsr reg acces #define exti_pr (* (nepotpisani isparljivi dugačak *) // exti_pr reg acces // nvic definicije #define nvic_iser0_reg (* (nepotpisano hlapljivo dugačko *) 0xe000e100) //NVIC_ISER0 reg pristup #define NVIC_ISER0_6VECT 0x40 //vect 6 definicija #define NVIC_ISER0_23VECT 0x800000 //vect 30 definicija #define NVIC_IPR0_ADD (0xE000EPRe (0xE000EPRe40) (0xE000EPRe40) (0xE000EPRe40 le char*)(NVIC_IPR0_ADD + 23) ) #define NVIC_IPR6_REG ( *(nepotpisani promjenjivi znak*)(NVIC_IPR0_ADD + 6))

Imajte na umu da su vrijednosti MK posebnih registara deklarirane kao volatile. To je neophodno kako kompajler ne pokušava optimizirati operacije pristupajući im, jer to nisu samo memorijske lokacije i njihove vrijednosti se mogu mijenjati bez sudjelovanja kernela.

Postavljanje prioritetnog grupiranja
Prije svega, vrijedi podesiti grupiranje prioriteta prekida: SCB_AIRCR = SCB_AIRCR_GROUP22; .Ovu radnju treba izvesti samo jednom. U složenim projektima koji koriste biblioteke trećih strana vrijedi provjeriti ovu činjenicu. Promjena podjele prioriteta u grupe može dovesti do neispravan rad firmware.
Omogućavanje taktiranja korištenih perifernih uređaja
Da vas podsjetim da prije nego počnete raditi s perifernim jedinicama morate omogućiti njihovo taktiranje:
//Omogući taktiranje SYSCFG, GPIO porta A i D RCC_AHB1_ENR |= RCC_AHB1_ENR_GPIOA|RCC_AHB1_ENR_GPIOC|RCC_AHB1_ENR_GPIOD; RCC_APB2_ENR |= RCC_APB2_ENR_SYSCFG;
Ne možete odmah raditi sa SYSCFG; morate pričekati nekoliko ciklusa takta. Ali nećemo. Počnimo inicijalizirati GPIO.
GPIO inicijalizacija
LED diode se inicijaliziraju na isti način kao prošli put:
//LED3 i LED5 inicijalizacija GPIOD_MODER = (GPIOD_MODER & (~GPIO_MODER_13BITS)) | GPIO_MODER_13OUT; GPIOD_MODER = (GPIOD_MODER & (~GPIO_MODER_14BITS)) | GPIO_MODER_14OUT;
Dugme PA0 i pin PC7 se inicijaliziraju kao ulaz:
//PA0 i PC6 inicijalizacija pinova GPIOA_MODER = (GPIOA_MODER & (~GPIO_MODER_0BITS)) | GPIO_MODER_0IN; GPIOC_MODER = (GPIOC_MODER & (~GPIO_MODER_6BITS)) | GPIO_MODER_6IN;
Ali za pin PC6 morate uključiti napajanje. Povlačenje se aktivira pomoću GPIOC_PUPDR registra:
//Enable PC6 pull-up GPIOC_PUPDR = (GPIOC_PUPDR & (~GPIOC_PUPDR_7BITS)) | GPIOC_PUPDR_6PU;
Postavljanje EXTI
Dakle, potrebno je da konfigurišete sledeće parametre - omogućite prekide za linije 0 i 6, za liniju 0 prekid na rastućoj ivici, za liniju 6 - prekid na opadajućoj ivici:
//Podešavanje EXTI EXTI_RTSR |= EXTI_LINE0; EXTI_FTSR |= EXTI_LINE6; EXTI_IMR = EXTI_LINE0|EXTI_LINE6;
Ostaje samo konfigurirati pinove čiji su portovi povezani na EXTI liniju (čudno rješenje, na primjer stellaris MCU-ovi mogu generirati prekid sa bilo kojom kombinacijom pinova, ovo je teže za STM32):
//EXTI do veze porta SYSCFG_EXTICR1 = (SYSCFG_EXTICR1&(~SYSCFG_EXTICR1_0BITS)) | SYSCFG_EXTICR1_0PA; SYSCFG_EXTICR2 = (SYSCFG_EXTICR2&(~SYSCFG_EXTICR2_6BITS)) | SYSCFG_EXTICR2_6PC;
Postavljanje NVIC-a
Sve što preostaje je konfigurirati prioritete prekida i maskirati ih za pokretanje obrade. Imajte na umu da se registruje NVIC_IPR dostupno za bajt-bajt pristup, što uvelike pojednostavljuje pristup samo neophodnim prioritetnim bajtovima pojedinačnih vektora prekida. Dovoljno je samo napraviti pomak za vrijednost broja vektora prekida (vidi listu definicija). Podsjetimo se još jednom da je EXTI linija 0 broj 6 u vektorskoj tabeli, a EXTI linija 5_9 je broj 23. Za STM32 su značajna samo 4 najviša prioritetna bita:
//Postavi prioritet prekida NVIC_IPR6_REG = 0xF0; NVIC_IPR23_REG = 0x0;
Za demonstraciju prioriteti su postavljeni drugačije.
Sada možete omogućiti prekide:
//Omogući prekide NVIC_ISER0_REG |= NVIC_ISER0_6VECT | NVIC_ISER0_23VECT;
Od sada, pritisak na dugme i kratki spoj PC6 i GND će dovesti do pozivanja funkcija rukovaoca prekida EXTI_Line0_IntHandler i EXTI_Line6_IntHandler.
Obrada prekida
U funkcijama rukovanja prekidima, prekid se prvo mora obrisati, nakon čega se LED diode mogu upaliti. Da bi se demonstrirali prioriteti prekida, jednom od rukovatelja je dodana vječna petlja. Ako je prioritet prekida s vječnom petljom niži od prioriteta drugog, onda se ne može pozvati. U suprotnom će moći prekinuti prvu. Predlažem da sami isprobate različite vrijednosti prioriteta prekida i jasno vidite čemu to vodi ( PAŽNJA - ne zaboravite na prekidne grupe!).
void EXTI_Line0_IntHandler(void) ( //Obriši prekid EXTI_PR = EXTI_LINE0; //Uključi LED 3 GPIOD_ODR |= GPIO_ODR_13PIN; ) void EXTI_Line6_IntHandler(void) ( //Obriši prekid EXTI_PR = EXTI_LINE0; //Uključi LED 3 GPIOD_ODR |= GPIO_ODR_13PIN; ) void EXTI_Line6_IntHandler(void) ( //Obriši prekid = EXTI_PRDRR4; GPIO_ODR_14P IN; dok (1); )

Umjesto zaključka

Za svaki slučaj daću kompletan spisak rezultirajućeg programa.

Listing

//Definitions for SCB_AIRCR register #define SCB_AIRCR (*(unsigned volatile long*)0xE000ED0C) //acces to SCB_AIRCR #define SCB_AIRCR_GROUP22 0x05FA0500 //change priority data //Definitions for RCC_AHB1_ENR register #define RCC_AHB1_ENR (*(unsigned volatile long *) (0x40023830)) //pristup na RCC_AHB1ENR reg #define RCC_AHB1_ENR_GPIOA 0x1 //GPIOA bitfield #define RCC_AHB1_ENR_GPIOC 0x4 //GPIOC bitfield #define RCC_AHB1_ENR_2 //define RCC_AHB1_ENR_ENR_GPIOD_GPIOD_GPIOD polje //GODPI R registar # definiše RCC_APB2_ENR (*(nepotpisano dugotrajno * )(0x40023844)) //pristup na RCC_APB2ENR reg #define RCC_APB2_ENR_SYSCFG 0x4000 //SYSCFG bitfield //Definicije za GPIO MODE registre #define GPIOA_MODER (*(unsigned volatile acces long *) g #define GPIOC_ MODER (*(unsigned volatile long*)(0x40020800)) //pristup GPIOC_MODER reg #define GPIOD_MODER (*(unsigned volatile long*)(0x40020C00)) //pristup GPIOD_MODER reg //GPIO ODR G registra definicija #deOfine * (unsigned volatile long*)(0x40020C14)) //pristup GPIOD_MODER reg #define GPIO_ODR_13PIN 0x2000 #define GPIO_ODR_14PIN 0x4000 //Bitfields definicije #define GPIOTS_MODER bit //define3GPIO_MODERe GPIO_MODER DER_0IN 0x0 //Pin 0 način unosa # define GPIO_MODER_6BITS 0x300 //Pin 6 mod bitova #define GPIO_MODER_6IN 0x000 //Pin 6 ulazni mod #define GPIO_MODER_13BITS 0xC000000 //Pin 13 mod bitova #define GPIO_MODER_6IN 0x000 //define GPIO_dein00 mod #define GPIO_de100 e GPIO_MODER _14BITS 0x30000000 //Pin 14 mod bitova #define GPIO_MODER_14OUT 0x10000000 //Pin 14 izlazni način //GPIOC_PUPDR definicija registra #define GPIOC_PUPDR (*(unsigned volatile long*)(0x4002080C)) //pristup GPIOC_PUPDR GPI reg #BIOC_PUPDR bit #BIP0000 //define0 fino GPIOC_PUPDR_6 PU 0x1000 / /PC6 pull-up enable //SYSCFG_EXTIx definicije registra #define SYSCFG_EXTICR1 (*(unsigned volatile long*)0x40013808) //SYSCFG_EXTICR1 pristup #define SYSCFG_EXTICR1_0BITS 0xSC #define SYSCFG_EXTICR #EXTI_0BIT 0xF #EXTI_0 / CR_EX00 TI 0 - port A #defini SYSCFG_EXTICR2(* (unsigned volatile long*)0x4001380C) //SYSCFG_EXTICR2 pristupa #define SYSCFG_EXTICR2_6BITS 0xF00 //EXTI 6 bitova #define SYSCFG_EXTICR2_6PC port //define SYSCFG_EXTICR2_6PC //EXTI #EXTI R (*(nepotpisano promjenjivo dugo*) 0x40013C 00) / /EXTI_IMR reg acces #define EXTI_LINE0 0x1 //LINE 0 definicija #define EXTI_LINE6 0x40 //LINE 6 definicija #define EXTI_RTSR (*(nepotpisana volatile long*)0x40013c refined #EX40013C0 ( Neigned isparljive duge *) 0x40013C0C) // exti_ftsr reg acces #define exti_pr (* (nepotpisani isparljivi dugačak *) // exti_pr reg acces // nvic definicije #define nvic_iser0_reg (* (nepotpisano hlapljivo dugačko *) 0xe000e100) //NVIC_ISER0 reg pristup #define NVIC_ISER0_6VECT 0x40 //vect 6 definicija #define NVIC_ISER0_23VECT 0x800000 //vect 30 definicija #define NVIC_IPR0_ADD (0xE000EPRe (0xE000EPRe40) (0xE000EPRe40) (0xE000EPRe40 le char*)(NVIC_IPR0_ADD + 23) ) #define NVIC_IPR6_REG ( *(nepotpisani promjenjivi znak*)(NVIC_IPR0_ADD + 6)) void EXTI_Line0_IntHandler(void); void EXTI_Line6_IntHandler(void); void main() ( //NVIC SCB_AIRCR = SCB_AIRCR_GROUP22; //Omogući taktiranje SYSCFG , GPIO porta A, C i D RCC_AHB1_ENR |= RCC_AHB1_ENR_GPIOA|RCC_AHB1_ENR_GPIOC|RCC_AHBPI_ENRCC_AHBPI_ENRCC_AHBPI_2; _SYSC FG; //LED3 i LED5 inicijalizacija GPIOD_MODER = (GPIOD_MODER & (~GPIO_MODER_13BITS)) | GPIO_MODER_13OUT; GPIOD_MODER = (GPIOD_MODER & (~GPIO_MODER_14BITS)) | GPIO_MODER_14OUT; //PA0 i PC6 inicijalizacija pinova GPIOA_MODER = (GPIOA_MODER ) (GPIOA_MODER;) (GPIOA_MODER;) _MODER = (GPIOC_MODER & (~GPIO_MODER_6BITS ) ) | GPIO_MODER_6IN; //Omogući PC7 pull-up GPIOC_PUPDR = (GPIOC_PUPDR & (~GPIOC_PUPDR_6BITS)) | GPIOC_PUPDR_6PU; //Podešavanje EXTI EXTI_RTSR |= EXTI_LINE0; EXTI_FTEXTI_6= EXTI_FTEXTI_IM | LINE6; //EXTI do veza porta SYSCFG_EXTICR1 = (SYSCFG_EXTICR1&(~SYSCFG_EXTICR1_0BITS)) | SYSCFG_EXTICR1_0PA; SYSCFG_EXTICR2 = (SYSCFG_EXTICR2&(~SYSCFG_EXTICR2_6BITS)GC //Prioritet NEXTICR2_6SYSC2FGC; IPR6 _REG = 0xF0;NVIC_IPR23_REG = 0x00; //Omogući prekide NVIC_ISER0_REG |= NVIC_ISER0_6VECT | NVIC_ISER0_23VECT; while(1) ( ) ) void EXTI_Line0_IntHandler(void) ( //Obriši prekid EXTI_PR = EXTI_LINE0; //Uključi LED 3 GPIOD_ODR |= GPIO_ODR_13PIN; ) void EXTI_Line6_IntHandler (void) LED4 GPIOD_ODR |= GPIO_ODR_14PIN; while(1); )


Da biste provjerili učinak prioriteta prekida i prioriteta grupe prekida, pokušajte promijeniti prioritete i promatrati šta se događa (dva bita - prioritet unutar grupe, 2 bita - prioritet grupe).

Tagovi:

  • STM32
  • Cortex-M
  • ARM
  • mikrokontroleri
Dodaj oznake

Upalimo LED!

Budući da su STM32 mikrokontroleri prave 32-bitne ARM jezgre, ovo neće biti lako učiniti. Ovdje se sve jako razlikuje od uobičajenih metoda u PIC-u ili AVR-u, gdje je bilo dovoljno konfigurirati izlazni port s jednom linijom, a vrijednost ispisati u njega drugom linijom - ali je utoliko zanimljivije i fleksibilnije.

STM32 arhitektura

Arhitektura mikrokontrolera je detaljno opisana u članku, ali da vas podsjetim na glavne točke koje su nam sada zanimljive.

Jezgro se taktira kvarcom, obično preko PLL-a. Ovo - brzina takta jezgra, ili SYSCLK. STM32VLDiscovery ploča ima kristal od 8 MHz, a PLL je u većini slučajeva konfiguriran kao množitelj od 3 - tj. SYSCLK na STM32VLDiscovery ploči je obično 24 MHz.

Iz jezgra se proteže autobus AHB, koji ima svoju frekvenciju takta - možete postaviti određeni predskaler u odnosu na SYSCLK, ali ga možete ostaviti jednakim jedan. Ova magistrala je slična magistrali između procesora i sjevernog mosta računara – na isti način služi za povezivanje ARM jezgre i perifernog procesora, kao i memorije i naravno DMA kontrolera.

Dvije periferne magistrale su povezane na AHB sabirnicu - APB1 I APB2. Oni su ekvivalentni, samo služe različitim kontrolerima interfejsa. Frekvencije obje sabirnice APB1 i APB2 mogu se podesiti vlastitim predskalerima u odnosu na AHB, ali se mogu i ostaviti jednako jedan. Podrazumevano, nakon pokretanja mikrokontrolera, sve periferije su na APB1 i APB2 magistralama onemogućeno u cilju uštede energije.

Kontroleri I/O porta koji nas zanimaju vise na APB2 sabirnici.

Periferni model u STM32

Svi periferni uređaji STM32 mikrokontrolera su konfigurisani prema standardnoj proceduri.

  1. Uključivanje takta odgovarajućeg kontrolera - doslovno, opskrbljujući ga taktnim signalom iz APB sabirnice;
  2. Postavke specifične za određenu periferiju - upisujemo nešto u kontrolne registre;
  3. Odabir izvora prekida - svaki periferni blok može generirati prekide iz različitih razloga. Možete odabrati određene „razloge“;
  4. Dodjela rukovaoca prekida;
  5. Pokretanje kontrolera.

Ako prekidi nisu potrebni, koraci 3 i 4 se mogu preskočiti.

Evo, na primjer, inicijalizacija tajmera (naznačeni su koraci iz niza):

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

I/O port kontroler

Konačno dolazimo do glavne teme članka.

Ovako je uređena jedna I/O noga mikrokontrolera STM32F100:

Izgleda komplikovanije nego u PIC-u ili AVR-u, ali u stvari, to je u redu.

Na ulazu su zaštitne diode koje sprečavaju da potencijal noge padne ispod zemlje ili da se podigne iznad napona napajanja. Zatim se ugrađuju kontrolirani pull-up otpornici - po želji, noga se može povući na tlo ili na napajanje. Međutim, morate imati na umu da su ove proteze prilično slabe.

Ulaz

Razmotrimo "ulaz". Signal ide direktno na “Analognu” liniju, a ako je pin konfiguriran kao ulaz za ADC ili komparator - i ako su ovi blokovi na ovom pinu - signal ide direktno na njih. Za rad sa digitalnim signalima instaliran je Schmittov okidač (ovo je onaj sa histerezom), a njegov izlaz ide u registar zatvarača ulaznih podataka - sada se stanje pina može očitati u programu čitanjem ovog registra (po način, naziva se IDR - registar ulaznih podataka). Da bi se osigurao rad perifernih uređaja koji nisu GPIO koji visi na ovoj nozi kao ulaz, napravljen je dodir pod nazivom “Ulaz alternativne funkcije”. Ovaj periferni uređaj može biti UART/USART, SPI, USB i mnogi drugi kontroleri.

Važno je shvatiti da su sve ove slavine istovremeno uključene i rade, samo s njima možda ništa nije povezano.

Izlaz

Sada "izlaz". Digitalni podaci upisani u port kao izlaz nalaze se u ODR registru - registru izlaznih podataka. Dostupan je i za pisanje i za čitanje. Kada čitate iz ODR-a, ne čitate stanje noge kao ulaz! Pročitao si šta si u njemu napisao.

Evo izlaza iz perifernih uređaja koji nisu GPIO, koji se nazivaju “Izlaz alternativne funkcije”, i ulazimo u izlazni drajver. Ovdje je konfiguriran način rada izlaza sa stanovišta dizajna kola - možete napraviti push-pull izlaz (vod je čvrsto privučen uzemljenje ili napajanje), izlaz otvorenog kolektora (povlačimo vod do napajanje, a uzemljenje je osigurano nečim vanjskim okačenim na kontakt) ili potpuno isključite izlaz. Nakon drajvera, analogni izlaz iz DAC-a, komparatora ili op-pojačala ulazi u liniju, a mi opet završavamo s pull-up otpornicima i diodama.

Digitalni izlazni drajver takođe ima kontrolu nagiba, odnosno brzinu porasta napona. Možete podesiti maksimalnu strminu i moći trzati nogu na frekvenciji od 50 MHz - ali na ovaj način ćemo dobiti i jake elektromagnetne smetnje zbog oštrih zvonjavih frontova. Možete postaviti minimalni nagib, sa maksimalna frekvencija“samo” 2 MHz - ali i značajno smanjuju radio smetnje.

Na slici možete vidjeti još jedan registar, “Bit set/reset registers”. Poenta je da možete pisati direktno u ODR registar, ili možete koristiti BRR/BSRR registre. U stvari, ovo je vrlo cool karakteristika, o kojoj ću govoriti u nastavku.

Mogućnosti

Sada je sve postalo kao haos - nejasno je kako upravljati svim tim mogućnostima. Međutim, ne, port kontroler nadzire moguće izlazne režime rada i eliminiše pogrešne kombinacije - na primer, neće dozvoliti da i drajver digitalnog izlaza i analogni izlaz rade istovremeno na istoj izlaznoj liniji. Ali prisustvo tolikog broja postavki pruža široke mogućnosti.

Na primjer, u starijim serijama možete konfigurirati izlaz s otvorenim kolektorom i uključiti povlačenje prema zemlji. Ispostavilo se da je to upravo ono što je potrebno za 1-Wire sabirnicu. Međutim, u STM32F1xx seriji ne postoji takva opcija i potrebno je ugraditi vanjski pull-up otpornik.

Atomske operacije

U starim mikrokontrolerima se često javljala situacija - ako smo htjeli promijeniti neke bitove u portu (a zapravo samo uključiti ili isključiti pin) - morali smo pročitati cijeli registar porta, postaviti/resetirati potrebne bitove u i napiši nazad. Sve je bilo u redu do trenutka kada je ova operacija prekinuta na sredini prekidom. Ako je rukovalac za ovaj prekid takođe uradio nešto sa istim portom, dogodila se izuzetno suptilna greška. To se rješavalo na različite načine, na primjer, globalno su zabranili prekide dok se port obrađuje - ali morate priznati, ovo je neka vrsta štake.

U STM32 ovaj problem je hardverski riješen - imate bit set i reset registre (BSRR i BRR), a ovdje su tri ptice ubijene odjednom:

  1. nema potrebe za čitanjem porta za rad s njim
  2. da biste uticali na određene pinove, morate raditi sa određenim bitovima, umjesto da pokušavate promijeniti cijeli port
  3. ove operacije su atomske - odvijaju se u jednom ciklusu i ne mogu se prekinuti u sredini.

Više detalja o “specifičnim bitovima” - u svakom ciklusu takta APB2 čitaju se BSRR i BRR registri, a njihov sadržaj se odmah primjenjuje na ODR registar, a sami ovi registri se brišu. Dakle, ako trebate postaviti bitove 3 i 5 u port, upišite riječ 10100 u BSRR i sve je uspješno instalirano.

Zaključavanje konfiguracije

Ako želite, možete blokirati konfiguraciju bilo kojeg pina od daljnjih promjena - svaki pokušaj upisivanja u registar konfiguracije neće uspjeti. Ovo je pogodno za kritične aplikacije u kojima će slučajno prebacivanje, na primjer, iz načina rada otvorenog odvoda u push-pull, izgorjeti sve što je povezano s ovim pinom, ili sam pin. LCKR registar je namijenjen za omogućavanje blokiranja, samo što je opremljen zaštitom od slučajnog nenamjernog upisivanja - da bi promjene stupile na snagu, potrebno je predati posebnu sekvencu LCKK bitu.

Kontrolni registri

Sva kontrola GPIO kontrolera koncentrisana je u 32-bitnim registrima GPIOx_RRR, gdje je x broj porta, a RRR ime registra.

Registar niske konfiguracije GPIOx_CRL

Konfigurira prvih 8 nogu, numeriranih 0..7. Svaka noga ima dva parametra, MODE i CNF.

MODE je odgovoran za ulaz/izlaz mod i brzinu uspona signala.

00 - ulaz (zadani način rada)

01 - izlaz pri brzini od 10 MHz

10 - izlaz pri brzini od 2 MHz

11 - izlaz pri brzini od 50 MHz

CNF je odgovoran za konfiguraciju pinova.

  • U načinu unosa (MODE=00):

    00 - analogni način rada

    01 - plutajući ulaz (zadano)

    10 - ulaz sa povlačenjem na zemlju ili napajanje

    11 - rezervisano

  • U izlaznom modu (MODE=01, 10 ili 11):

    00 - GPIO Push-pull izlaz

    01 - GPIO izlaz Otvoren odvod

    10 - izlaz alternativne funkcije Push-pull

    11 - izlaz alternativne funkcije Otvoren odvod

Registar visoke konfiguracije GPIOx_CRH

Konfigurira drugih 8 nogu, sa brojevima 8..15. Sve je slično GPIOx_CRL.

Registar ulaznih podataka GPIOx_IDR

Svaki IDRy bit sadrži stanje odgovarajućeg I/O pina. Samo za čitanje.

Registar ulaznih podataka GPIOx_ODR

Svaki ODRy bit sadrži stanje odgovarajućeg I/O pina. Možete pisati podatke i oni će se pojaviti na izlazu porta, možete čitati podatke čitanjem prethodne upisane vrijednosti.

Atomski izlazni bit set/reset registar GPIOx_BSRR

Najznačajnijih 16 bitova služe za resetovanje odgovarajućih pinova na 0. 0 ne radi ništa, 1 resetuje odgovarajući bit. Donjih 16 bitova služe za postavljanje bitova na 1. Slično, pisanje “0” ne čini ništa, pisanje “1” postavlja odgovarajući bit na 1.

GPIOx_BRR Registar za poništavanje bita atomskih izlaznih podataka

Donjih 16 bitova služe za resetovanje odgovarajućih pinova. 0 - ne radi ništa, 1 - resetuje odgovarajući bit.

Registar je samo za pisanje - resetuje se na nulu u svakom ciklusu takta APB2.

Registar zaključavanja konfiguracije GPIOx_LCKR

Svaki LCKy bit zaključava odgovarajuće MODE/CNF bitove CRL/CRH registara od promjene, tako da se konfiguracija pinova ne može promijeniti do ponovnog pokretanja. Da biste aktivirali blokiranje, potrebno je da zapišete sekvencu blokiranja u LCKK bit: 1, 0, 1, pročitajte 0, pročitajte 1. Čitanje LCKK bita prijavljuje trenutni status blokiranja: 0 - nema blokiranja, 1 - da.

Rad u različitim režimima

Način unosa

  • Izlazni drajver je onemogućen
  • Otpornici za povlačenje su uključeni prema vašim postavkama, u jednom od tri stanja - "ulaz povučen na zemlju", "ulaz povučen na napajanje" ili "plutajući ulaz"
  • Ulazni signal se uzorkuje svaki ciklus takta APB2 magistrale i upisuje u IDR registar, a čitanje ovog registra izvještava o stanju pina.

Režim izlaza

  • Izlazni drajver je omogućen i ponaša se ovako:

    U "Push-Pull" načinu rada radi kao polumost, uključujući gornji tranzistor u slučaju "1" i donji u slučaju "0",

    U režimu „Otvoreni odvod“ uključuje donji tranzistor u slučaju „0“, a u slučaju „1“ ostavlja liniju nepovezanom (tj. u trećem stanju).

  • Schmitt okidač je omogućen
  • Pull-up otpornici su isključeni

Alternativni način rada (ne-GPIO periferije)

  • Izlazni drajver - u Push-Pull modu (na primjer, ovako radi TX noga USART modula) ili Open drain, ovisno o zahtjevima kontrolera
  • Izlaznim drajverom upravljaju periferni signali, a ne ODR registar
  • Schmitt okidač je omogućen
  • Otpornici za povlačenje su onemogućeni
  • Izlazni signal se uzorkuje svaki ciklus takta APB2 magistrale i upisuje u IDR registar, a čitanje ovog registra izvještava o stanju pina u Open drain modu.
  • Čitanje ODR registra izvještava o posljednjem napisanom stanju u Push-Pull modu.

Analogni način rada

  • Izlazni drajver je onemogućen
  • Schmittov okidač je potpuno onemogućen kako ne bi utjecao na ulazni napon
  • Otpornici za povlačenje su onemogućeni
  • IDR registar je uvijek 0.

Sve interne analogne periferije imaju visoku ulaznu impedanciju, tako da će sam pin imati visoku ulaznu impedanciju u odnosu na ostatak kola.

Konačno paljenje LED diode

Sada znamo sve za uključivanje ove LED diode! Idemo od samog početka.

Morate omogućiti taktiranje GPIO porta. Pošto koristimo LED na Discovery ploči, izabraćemo zeleno - spojeno je na PC9 port. To jest, morate omogućiti GPIOC taktiranje.

Sada govorimo o Push-pull izlazu. Ovo odgovara 00 u CNF registru.

Pa, da budem iskren, to je sve. Konačno, lista trepćućih LED dioda

#include "stm32f10x.h" int main(void) ( RCC->APB2ENR |= RCC_APB2ENR_IOPCEN; GPIOC->CRH &= !(GPIO_CRH_CNF9_0 | GPIO_CRH_CNF9_1); GPIOC->CRH |= GPIO03_02; 0 0; dok (1) ( GPIOC->BSRR |= GPIO_BSRR_BS9; i=0; while(i++ BRR |= GPIO_BRR_BR9; i=0; while(i++

itacone biblioteka

A ipak to nije sve. Da pojednostavim sve vrste podešavanja, napravim biblioteku itakone. Trenutno implementira rad sa GPIO pinovima i nekoliko funkcija opšte upotrebe - ali rad se nastavlja.

Najbolji članci na ovu temu