Home Programování C/C++ (MCU) STM32F4 Discovery - Tutorial (07) External interrupt/event controller - EXTI
V tomto článku budeme opět blikat diodou. Ovšem pomocí externího přerušení. Podíváme se na jeho nastavení a použití.
Externí přerušení je třeba ve chvílích, kdy k MCU připojíme nějaký další obvod a je třeba reagovat co nejrychleji na jeho činnost (třeba tlačítko :D). Ale pozor, k řadiči externího přerušení jsou připojeny také periferie RTC, USB OTG nebo Ethernet.
Řadič externího přerušení má 23 kanálů. Je tedy možné vytvořit až 23 různých přerušení. Prvních 16 kanálů je určeno portům GPIO. Lze tedy libovolný pin libovolného GPIO připojit na jeden kanál, ale bohužel s malým omezením. Ke kanálu 0 lze připojit jen nulté piny a jen jednoho GPIO (tedy pokud na kanál 0 napojím GPIOA, pak bude přerušení na kanálu 0 generovat změna na pinu PA0, žádný jiný nultý pin), ke kanálu 1 lze připojit jen první piny a jen jednoho GPIO (tedy pokud na kanál 1 napojím GPIOF, bude přerušení na kanálu 1 generovat změna na pinu AF1, žádný jiný první pin), a tak dále až do kanálu 15. Co z toho plyne? Že pokud nastavíme generování přerušení z pinu PB2, již nelze použít k externímu přerušení pin PA2, ani PC2, ani PD2, ani žádný další pin 2 libovolného portu.
Zbytek kanálů je přiřazen následovně:
Jedná se o periferii starající se o nastavení některých dalších vlastností. Nebude podrobně probírat jaké nastavení obsahuje. Nám stačí vědět, že obsahuje část nastavení externího přerušení. Konkrétně nastavení jaký pin bude připojený na daný kanál. V základním nastavení je vždy pin portu A připojen na kanál. Tedy například ke kanálu 0 jen připojen PA0, ke kanálu 1 je připojen PA1 atd. Pomocí registrů SYSCFG_EXTICRx lze toto změnit, například aby na kanál 0 byl připojen PB0. Pro toto nastavení existují čtyři registry. Programově je to řešeno jako pole, kde příslušný registr volíme indexem. Každý registr obsahuje nastavení pro 4 kanály.
Obr. 1: Registr SYSCFG_EXTICR1.
Na obrázku 1 je vidět registr SYSCFG_EXTICR1, ale stejně vypadají i další 3 registry. Je i vidět že k určení pinu slouží 4 bity. Jejich kombinace a volba pinu je následovná:
Než něco dále vysvětlovat uvedu dva příklady. Prvním nastavuji na kanál 0 pin PA0.
SYSCFG->EXTICR[0] = 0x0000; //nebo SYSCFG->EXTICR[0] = SYSCFG_EXTICR1_EXTI0_PA;
V druhém příkladu nastavuji na kanál 5 pin PC5.
SYSCFG->EXTICR[1] = 0x0020; //nebo SYSCFG->EXTICR[1] = SYSCFG_EXTICR2_EXTI5_PC;
IMR - Interrupt mask register
Jedná se o 32bit registr, ale je využito pouze 23 prvních bitů. Každý bit odpovídá jednomu kanálu. Logická jednička na příslušné pozici povoluje generování přerušení daného kanálu.
EMR - Event mask register
Opět se jedná o 32bit registr, ale je využito jen 23 prvních bitů. Každý bit odpovídá jednomu kanálu. Jeho nastavování je stejné jako u registru IMR. Nastavením logické jedničky povolíme generování události.
RTSR - Rising trigger selection register
Opět 32bit registr s použitelnými 23 bity, kde pozice každého bitu odpovídá danému kanálu. Nastavením příslušného bitu do logické jedničky říkáme, že na daném kanálu chceme generovat přerušení při náběžné hraně.
FTSR - Falling trigger selection register
Obdobný registr jako předešlý a stejně se používá. Ovšem nastavením bitu na log 1 říkáme, že chceme generovat přerušení při sestupné hraně.
(Pozn.: Lze nastavit, aby na jednom kanálu bylo generováno přerušení na obě hrany nastavením log 1 v obou registrech – RTSR i FTSR.)
SWIER - Software interrupt event register
Opět 32bit registr s 23 použitelnými bity. Tímto registrem lze softwarově vytvořit požadavek na přerušení konkrétního kanálu.
PR - Pending register
Kupodivu opět 32bit registr s 23 použitelnými bity. Tento registr plní dvě úlohy. Při vzniku přerušení je zde nastaven bit toho kanálu, jež přerušení vyvolal. Po dokončení rutiny přerušení je ale třeba žádost o přerušení zrušit, jinak by bylo přerušení generováno ihned znova. Ovšem rušení požadavku se dělá zápisem log 1 na příslušném bitu (čekal bych log 0, ale opravdu je to log 1).
Je to nuda, chápu. Pořád jen blikáme :D. Tentokrát, ale budeme diodu rozsvěcet a zhášet, sice tlačítkem, pomocí externího přerušení. Tak začneme (připomínám, že tlačítko je na PA0 a modrá dioda na PD15). Nastavit GPIO už umíme, takže nastavíme PD15 jako výstupní a PA0 jako vstupní.
Nejprve nastavíme řadič SYSCFG.
// Povoleni hodin pro radic SYSCFG RCC->APB2ENR |= RCC_APB2ENR_SYSCFGEN; // Reset radice SYSCFG RCC->APB2RSTR |= RCC_APB2RSTR_SYSCFGRST; RCC->APB2RSTR &= ~RCC_APB2RSTR_SYSCFGRST; // Nastaveni v registru SYSCFG_EXTIx SYSCFG->EXTICR[0] = SYSCFG_EXTICR1_EXTI0_PA; // Ke kanalu 0 pripojit PA0
První část známe. Povolíme hodiny a resetujeme periferii. Dále pak zápisem do registru SYSCFG_EXTICR1 (to je registr v poli s indexem 0) nastavíme že na kanál 0 bude připojen pin PA0. Dále nastavíme registry externího přerušení.
EXTI->IMR = EXTI_IMR_MR0; // Maskovani, povoleni kanalu 0 EXTI->RTSR = EXTI_RTSR_TR0; // Preruseni generovat pri nabezne hrane
Zápisem do registru IMR povolujeme generování přerušení na kanálu 0. Do registru RTSR ukládáme nastavení, aby přerušení bylo generováno při náběžné hraně (pokud chcete, můžete si nastavit i reakci na sestupnou hranu).
Právě máme tedy nastaveno, že přerušení se bude generovat na PA0 (tedy kanál 0, který jsme povolili) a jen při náběžné hraně. Teď je třeba ještě povolit přerušení v NVIC.
NVIC->ISER[EXTI0_IRQn >> 0x05] |= (0x01 << (EXTI0_IRQn & 0x1F));
Nakonec vytvořit funkci pro obsluhu přerušení, což známe už z předchozích článků.
void EXTI0_IRQHandler(void) { GPIOD->ODR ^= GPIO_ODR_ODR_15; EXTI->PR |= EXTI_PR_PR0; return; }
Takže nakonec používání externího přerušení není nijak obtížné. Je třeba nastavit jaký pin, jaký kanál, povolit přerušení v EXTI a NVIC, napsat funkci pro obsluhu a to je vlastně vše. Na závěr sem dám celý kód programu.
#include <stm32f4xx.h> void init(void) { /* GPIOA init ----------------------------------------------------------------------------*/ // Reset portu GPIA RCC->AHB1RSTR |= RCC_AHB1RSTR_GPIOARST; RCC->AHB1RSTR &= ~(RCC_AHB1RSTR_GPIOARST); // Povoleni hodin pro GPIOA RCC->AHB1ENR |= RCC_AHB1ENR_GPIOAEN; // Nastaveni modu GPIOA->MODER |= 0x0; //pin 0 jako vstupní /* GPIOD init ----------------------------------------------------------------------------*/ // Reset portu GPID RCC->AHB1RSTR |= RCC_AHB1RSTR_GPIODRST; RCC->AHB1RSTR &= ~(RCC_AHB1RSTR_GPIODRST); // Povoleni hodin pro GPIOD RCC->AHB1ENR |= RCC_AHB1ENR_GPIODEN; // Nastaveni modu GPIOD->MODER |= GPIO_MODER_MODER15_0; //pin 15 jako výstupní /* SYSCFG init ---------------------------------------------------------------------------*/ // Povoleni hodin pro radic SYSCFG RCC->APB2ENR |= RCC_APB2ENR_SYSCFGEN; // Reset radice SYSCFG RCC->APB2RSTR |= RCC_APB2RSTR_SYSCFGRST; RCC->APB2RSTR &= ~RCC_APB2RSTR_SYSCFGRST; // Nastaveni v registru SYSCFG_EXTIx SYSCFG->EXTICR[0] = SYSCFG_EXTICR1_EXTI0_PA; // Ke kanalu 0 pripojit PA0 /* EXTI init -----------------------------------------------------------------------------*/ EXTI->IMR = EXTI_IMR_MR0; // Maskovani, povoleni kanalu 0 EXTI->RTSR = EXTI_RTSR_TR0; // Preruseni generovat pri nabezne hrane /* Iterrupt init -------------------------------------------------------------------------*/ NVIC->ISER[EXTI0_IRQn >> 0x05] |= (0x01 << (EXTI0_IRQn & 0x1F)); return; } void EXTI0_IRQHandler(void) { GPIOD->ODR ^= GPIO_ODR_ODR_15; // invertuj stav na pinu 15 EXTI->PR |= EXTI_PR_PR0; // nuluj pozadavek na preruseni return; } int main(void) { init(); while(1); }
Odedneška umíme používat externí přerušení. Pokud pracujeme s dotykovým panelem, tak má vyvedený signál indikující, že došlo k dotyku a právě zde se dá externí přerušení například použít. O čem bude příští článek ještě nevím, ale určitě to bude zajímavé a užitečné :D.
Projekt ke stažení ZDE.
Komentáře
Nešel by ke každému článku dát link na stažení zdrojáků?