- Лоботрясы

Поиск
Перейти к контенту

Главное меню:

Статьи по STM32

Читать в Яндекс.Подписках



    STM32. Контроллер внешних прерываний (EXTI)
Просмотров: 4338
     Кто работал (и работает) с AVR, тот помнит, с какими мучениями приходилось сталкиваться, если нужно было организовывать прерывания от изменения внешних сигналов (фронта, среза, или наличия логического нуля) на выводах микроконтроллера. Мучения состояли в том, что выводов, способных на такой героический поступок (внешнее прерывание) было 3 штуки максимум – INT0, INT1 и INT2. В этом плане STM32 выгодно отличается от своего малоразрядного неконкурентабрата по разуму. Поэтому, уже давно логичным является завершение описания системы прерывания, начатое ещё в прошлом году и несправедливо незаконченное :-) Поехали!..
     Контроллер внешних прерываний и событий (extended interrupts and events controller - EXTI) – это блок в архитектуре микроконтроллеров STM32, отвечающий за настройку прерываний портов ввода-вывода (GPIO). Он (EXTI) организован отдельным от NVIC блоком для того, чтобы обеспечить более гибкую и удобную настройку выводов микроконтроллера (каждый из которых может быть источником внешнего прерывания) и внутренних линий событий, да и чтобы чрезмерно не засиратьусложнять блок NVIC. Тем не менее, эти блоки тесно связаны, и при возникновении внешнего прерывания/события, оно обслуживается именно контроллером NVIC. (Событие (event) – это что-то типа флага, возникающее (устанавливающееся) при определённом действии (например, при переполнении счётчика таймера или завершении АЦП-преобразования), и вызывающее в свою очередь (если это разрешено и возможно) определённое прерывание) К бонусам EXTI можно отнести и возможность пробуждения контроллера из «спячки» (режима ожидания).

     EXTI позволяет работать с 32 (до 32-х) внешних/внутренних линий событий (23 линии внешних событий и 9 линий внутренних событий). Активный уровень сигнала каждой линии внешнего прерывания мы можем выбрать сами, а вот для внутреннего прерывания активным уровнем всегда является возрастающий (по фронту).
 
     Перечень всех регистров, необходимых для работы с EXTI (их немного:-)
Interrupt mask register (EXTI_IMR) – Регистр разрешения прерываний
Event mask register (EXTI_EMR) – Регистр разрешения событий
Rising trigger selection register (EXTI_RTSR) – Регистр разрешения срабатывания по переднему фронту
Falling trigger selection register (EXTI_FTSR) – Регистр разрешения срабатывания по заднему фронту (срезу).
Software interrupt event register (EXTI_SWIER) – Регистр программного разрешения (вызова) прерывания/события.
Pending register (EXTI_PR) – Регистр ожидания прерываний. При возникновении прерывания, устанавливается соответствующий бит (флаг).
     Регистры эти несложные – каждый (или почти каждый) бит 32-разрядного регистра (по мере возрастания, есно) отвечает за свою, одну из 32-х, линию прерывания/события.
 
Эти регистры оформлены в шаблон
typedef struct
{
  __IO uint32_t IMR;          //EXTI Interrupt mask register                                 
  __IO uint32_t EMR;          //EXTI Event mask register                                    
  __IO uint32_t RTSR;         //EXTI Rising trigger selection register                  
  __IO uint32_t FTSR;         //EXTI Falling trigger selection register                    
  __IO uint32_t SWIER;       //EXTI Software interrupt event register                     
  __IO uint32_t PR;            //EXTI Pending register                                   
}EXTI_TypeDef;
И представлены в виде структуры EXTI (stm32f0xx.h).
 
     Диаграмма для лучшего понимания :-)
     Источниками внешних прерываний в STM32 могут быть любые 16 выводов любых портов, с тем лишь ограничением, что на прерывание не могут быть настроены одновременно два вывода (порта) с одинаковым номером (например, PA0 и PB0).
     Порты ввода/вывода (GPIO) подключены к 16 линиям событий/прерываний (EXTI0…EXTI15) следующим образом:
     Однако, если посмотреть на таблицу векторов прерываний (предыдущая часть про модуль NVIC), то можно увидеть, что для нашего контроллера STM32F031F4P6 (STM32F0xx) имеются только три вектора внешних прерываний:
EXTI0_1_IRQHandler                  – Внешнее прерывание EXTI по линии [1:0]
EXTI2_3_IRQHandler                  – Внешнее прерывание EXTI по линии [3:2]
EXTI4_15_IRQHandler                 – Внешнее прерывание EXTI по линии [15:4]
     То есть, при возникновении прерывания по линии EXTI0 (например, с ножки PB0) или же по линии EXTI1 (например, с ножки PA1), обработка что того, что другого прерывания будет выполняться процедурой
void EXTI0_1_IRQHandler (void)
    … ;        
}
     Аналогично, прерываниями по линиям EXTI2 и EXTI3 занимается процедура EXTI2_3_IRQHandler.
     А вот вектор прерывания EXTI4_15_IRQHandler вообще обслуживает внешние события по 4…15 линиям.
 
     Но для настройки внешних прерываний нужно ещё настроить и сами выводы (пины) выбранного нами порта GPIO. Т.е. определить, какой именно вывод (от 0 до 15) и какого именно порта (PA, PB, PC…) у нас будет источником внешних прерываний! (Конфигурация портов ввода/вывода должна выполняться в первую очередь!)
     Для этого существуют 4 регистра:
SYSCFG external interrupt configuration register 1 (SYSCFG_EXTICR1) – 1-й Регистр конфигурации внешних прерываний. Позволяет сконфигурировать (настроить) 0…3 выводы портов GPIO.
SYSCFG external interrupt configuration register 2 (SYSCFG_EXTICR2) – 2-й Регистр конфигурации внешних прерываний. Позволяет сконфигурировать (настроить) 4…7 выводы портов GPIO.
SYSCFG external interrupt configuration register 3 (SYSCFG_EXTICR3) – 3-й Регистр конфигурации внешних прерываний. Позволяет сконфигурировать (настроить) 8…11 выводы портов GPIO.
SYSCFG external interrupt configuration register 4 (SYSCFG_EXTICR4) – 4-й Регистр конфигурации внешних прерываний. Позволяет сконфигурировать (настроить) 12…15 выводы портов GPIO.
 
     Настройку выводов покажу на первом регистре SYSCFG_EXTICR1:
     Этот регистр позволяет настроить (назначить) 0…3 номера выводов (ног или пинов…) на определённый порт записью 4-х битов в соответствующее номеру вывода поле:
x000: вывод PA[0…3]
x001: вывод PB[0…3]
x010: вывод PC[0…3]
x011: вывод PD[0…3]
x100: вывод PE[0…3]
x101: вывод PF[0…3]
     Например, чтобы выбрать источником внешнего прерывания вывод PB1, то в 4–7-й биты регистра нужно записать 0001. По умолчанию, во все битовые поля всех 4-х регистров записано 0000, т.е. изначально все внешние прерывания настроены на порт PA.

     Для работы с этими регистрами в файле stm32f0xx.h создана структура (а точнее – указатель) SYSCFG, которая ссылается на определенную ячейку FLASH-памяти микроконтроллера. Создан этот SYSCFG по шаблону

typedef struct
{
   __IO uint32_t     CFGR1; // SYSCFG configuration register 1
   uint32_t     RESERVED; // Reserved
   __IO uint32_t     EXTICR[4]; // SYSCFG external interrupt configuration register
   __IO uint32_t     CFGR2; // SYSCFG configuration register 2
} SYSCFG_TypeDef;

     Как видите, в этом шаблоне нам встречаются наши старые знакомые – регистры EXTICR, доступ к которым (а они расположены один за другим в ячейках памяти) осуществляется уже на основе массива EXTICR[4];
     Например, мы хотим выбрать в качестве источника прерывания вывод PB1, да не проблема:

SYSCFG->EXTICR[0] |= (uint16_t)0x0010;
     Для особо ленивых, в том же файле stm32f0xx.h можно взять уже готовые макросы (нужно только порыться в дебрях файла – начиная где-то с 2605 строки кода :-), типа такого:
#define SYSCFG_EXTICR1_EXTI1_PB           ((uint16_t)0x0010) /*!< PB[1] pin */

     И предыдущее выражение станет выглядеть так:

SYSCFG->EXTICR[0] |= SYSCFG_EXTICR1_EXTI1_PB;

     Теперь обобщим! Настройка внешних событий/прерываний выполняется следующим образом:

     Для Аппаратного прерывания
Для настройки линии в качестве источника прерывания, необходимо:
- Разрешить прерывание установкой соответствующего бита в регистре EXTI_IMR.
- Разрешить срабатывание по фронту или (и) срезу сигнала (регистры EXTI_RTSR и EXTI_FTSR)
- Активировать прерывание по соответствующей линии (EXTIx_x_IRQHandler) в модуле NVIC.

     Для Аппаратного события
Для настройки линии в качестве источника события, необходимо:
- Разрешить возникновение событий установкой соответствующего бита в регистре EXTI_EMR.
- Разрешить срабатывание по фронту или (и) срезу сигнала (регистры EXTI_RTSR и EXTI_FTSR)

     Для Программного прерывания/события
Любая внешняя линия может быть сконфигурирована в качестве линии программного прерывания/события, и для генерации программного прерывания нужно выполнить следующее:.
- Настроить соответствующий регистр разрешения прерывания/события (EXTI_IMR, EXTI_EMR)
- Установить требуемый бит регистра программного вызова прерывания (EXTI_SWIER)


     С первыми 16-ю линиями внешних событий/прерываний разобрались. Остальные 16 линий событий подключены к блокам микроконтроллера следующим образом:

• EXTI линия 16 подключена к выходу PVD (Power Voltage Detector – блок контроля напряжения питания)
• EXTI линия 17 подключена к событию RTC Alarm event
• EXTI линия 18 подключена к событию внутреннего пробуждения (возобновления работы) USB (internal USB wakeup event)
• EXTI линия 19 подключена к событиям RTC Tamper и TimeStamp
• EXTI линия 20 подключена к событию RTC Wakeup event (доступно только в STM32F07x)
• EXTI линия 21 подключена к выходу Comparator 1
• EXTI линия 22 подключена к выходу Comparator 2
• EXTI линия 23 подключена к событию внутреннего пробуждения I2C1
• EXTI линия 24 – зарезервирована
• EXTI линия 25 подключена к событию внутреннего пробуждения USART1
• EXTI линия подключена к событию внутреннего пробуждения USART2 (доступно только в STM32F07x)
• EXTI линия 27 подключена к событию внутреннего пробуждения CEC
• EXTI линия 28 – зарезервирована
• EXTI линия 29 – зарезервирована
• EXTI линия 30 – зарезервирована
• EXTI линия 31 подключена к выходу питания компаратора VDDIO2

     А теперь как организовать практическую работу с модулем внешних прерываний.
     В репозитории CooCoxa к сишному проекту можно подключить библиотеку EXTI, которая позволяет облегчить настройку прерываний, не вдаваясь в игру с битовыми манипуляциями. Эта библиотека имеет несколько функций для работы с внешними прерываниями, а настройка самого прерывания выполняется с помощью вспомогательной структуры. При этом к вашему проекту нужно будет добавить (естественно через #include) файл stm32f0xx_exti.h, в котором упомянутая структура описывается в виде такого шаблона:

typedef struct
{
    uint32_t     EXTI_Line; //Здесь объявляется линия, необходимая для включения/отключения
    EXTIMode_TypeDef    EXTI_Mode; //Выбор событие/прерывание
    EXTITrigger_TypeDef    EXTI_Trigger; // Задаётся активный уровень сигнала
    FunctionalState    EXTI_LineCmd; // Выбор состояния линии прерывания: ENABLE или DISABLE
}EXTI_InitTypeDef;

     Параметр EXTI_Line может принять одно из значений EXTI_Line0... EXTI_Line27 (EXTI_Line0... EXTI_Line15 – внешние прерывания) – как внешние линии прерываний, так и внутренние (подробнее - смотреть файл stm32f0xx_exti.h).
Параметр EXTI_Mode принимает значения EXTI_Mode_Interrupt (прерывание) или EXTI_Mode_Event (событие).
Параметр EXTI_Trigger может принять одно из значений: EXTI_Trigger_Rising (активный сигнал – по фронту), EXTI_Trigger_Falling (активный сигнал – по срезу) и EXTI_Trigger_Rising_Falling (и то, и другое :-)

     Теперь, в вашем коде программы, помимо включения библиотеки внешних прерываний строкой #include <stm32f0xx_exti.h>, на основе вышеописанного шаблона нужно будет создать свою структуру (EXTI_InitStructure) для работы с EXTI:
EXTI_InitTypeDef     EXTI_InitStructure;

     И в ваше распоряжение предоставляются несколько библиотечных функций, которые можно посмотреть в файле stm32f0xx_exti.c:
void EXTI_DeInit(void) – Сброс всех регистров EXTI в первоначальное состояние;
void EXTI_Init(EXTI_InitTypeDef*   EXTI_InitStruct) – Инициализация периферии EXTI согласно параметров, присвоенных элементам структуры EXTI_InitTypeDef;
void EXTI_StructInit(EXTI_InitTypeDef* EXTI_InitStruct) – Сброс всех элементов структуры EXTI_InitTypeDef значениями по умолчанию;
void EXTI_GenerateSWInterrupt(uint32_t EXTI_Line) – Функция вызова программного прерывания на линии EXTI_Line (любое значение из EXTI_Line0... EXTI_Line27);
FlagStatus EXTI_GetFlagStatus(uint32_t EXTI_Line) – Функция проверки установки флага прерывания (проверяются биты регистра EXTI_PR). Функция возвращает SET или RESET;
void EXTI_ClearFlag(uint32_t EXTI_Line) – Функция сброса флага прерывания;
ITStatus EXTI_GetITStatus(uint32_t EXTI_Line) – Функция проверки линии прерывания на наличие флага – полностью аналогична функции EXTI_GetFlagStatus() (даже не понимаю, зачем её вообще нужно было добавлять?!);
void EXTI_ClearITPendingBit(uint32_t EXTI_Line) – ещё один непонятный клон функции EXTI_ClearFlag() с тем же эффектом.

     Но ещё нужно будет настроить сами выводы портов! Для начальной настройки выводов в качестве источников прерывания нужно будет в репозитарии дополнительно добавить библиотеку SYSCFG (к проекту будут добавлены два файла stm32f0xx_syscfg.h и stm32f0xx_syscfg.c, которые нужно будет подключить к своему СИ-шному коду посредством макроса #include "stm32f0xx_syscfg.h").
     Сама библиотека имеет несколько функций (предназначенных для конфигурирования DMA, I2C и GPIO), из которых нас интересует только одна функция

void SYSCFG_EXTILineConfig(uint8_t EXTI_PortSourceGPIOx, uint8_t EXTI_PinSourcex) – Функция настройки вывода GPIO, который будет использоваться в качестве источника внешнего прерывания. Где значение x в параметре EXTI_PortSourceGPIOx может принимать значение A, B, C, D или F (т.е. выбираем порт ввода/вывода), а x в параметре EXTI_PinSourcex может принимать значение 0…15.

     И напоследок, для сравнения, приведу небольшой пример кода (инициализации и применения EXTI) как с использованием рассмотренных библиотечных функций, так и при прямом доступе к регистрам. Установим внешнее прерывание по фронту сигнала на выводе РА1…


RCC->AHBENR |= RCC_AHBENR_GPIOAEN; // Включили тактирование модуля GPIOA
RCC->APB2ENR |= RCC_APB2ENR_SYSCFGEN; // Включили тактирование модуля SYSCFG

SYSCFG->EXTICR[0] |= SYSCFG_EXTICR1_EXTI1_PA; // Конфигурируем вывод РА1(макрос берём из файла stm32f0xx.h)
EXTI->IMR = 0x0002; // Разрешил прерывание
EXTI->RTSR = 0x0002; // Разрешил срабатывание по фронту
// Конфигурируем NVIC для внешних прерываний
NVIC_EnableIRQ(EXTI0_1_IRQn); // Разрешаем прерывание на линии EXTI0_1

void EXTI0_1_IRQHandler (void) // И процедура обработки прерывания!
{   
   if(EXTI->PR & (1<<1))
   {
      Делаем что-то там…
      EXTI->PR|=0x02; //Очистка флага прерывания
   }
}


     И то же самое с библиотечными функциями…
(показываю добавление только тех библиотек, имеющих отношение к примеру :-)
#include <stm32f0xx_rcc.h> // Библиотека для подключения модулей микроконтроллера к тактированию
#include <stm32f0xx_exti.h>
#include <stm32f0xx_syscfg.h>

EXTI_InitTypeDef EXTI_InitStructure; // Создаём структуру для работы с EXTI


RCC->AHBENR |= RCC_AHBENR_GPIOAEN; // Включили тактирование модуля GPIOA
RCC->APB2ENR |= RCC_APB2ENR_SYSCFGEN; // Включили тактирование модуля SYSCFG

SYSCFG_EXTILineConfig(uint8_t EXTI_PortSourceGPIOA, EXTI_PinSource1) ; // Конфигурируем вывод РА1

EXTI_InitStructure.EXTI_Line = EXTI_Line1;
EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising;
EXTI_InitStructure.EXTI_LineCmd = ENABLE;
EXTI_Init(&EXTI_InitStructure); // Сохраняем настройки конфигурации в регистрах EXTI

NVIC_EnableIRQ(EXTI0_1_IRQn); // Разрешаем прерывание на линии EXTI0_1


void EXTI0_1_IRQHandler (void) // И процедура обработки прерывания!
{    
    if(EXTI->PR & (1<<1))
    {
         Делаем что-то там…
         EXTI_ClearFlag(EXTI_Line1); //Очистка флага прерывания
    }
}

Вот так вот…


Опубликовано 8.04.2016
©Igoryosha, 2016

 
 
Назад к содержимому | Назад к главному меню