- Лоботрясы

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

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

Статьи по STM32

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



    STM32. Контроллер вложенных векторизованных прерываний (NVIC)
Просмотров: 5587
     Все прерывания в микроконтроллере управляются посредством NVIC (контроллером вложенных векторизованных прерываний или Nested Vectored Interrupt Controller), являющимся стандартным блоком ядра Cortex.
 
     NVIC поддерживает:
• до 32 маскируемых (т.е. которые можно запрещать битовыми манипуляциями с регистром маскирования прерываний) прерываний (не считая шестнадцати прерываний, использующихся для выявления исключительных ситуаций ядра Cortex-m0 (сброс, разные ошибки и аварии и срабатывание системного таймера));
• 
4 программируемых уровня приоритета (для этого предназначены 2 бита приоритета прерываний)Чем выше уровень, тем ниже приоритет, а уровень 0 – это самый высший приоритет;
• Управление питанием.
 
     16 внутренних (дополнительных) прерываний особого интереса при программировании не вызывают (за исключением разве что системного таймера
SysTick, предназначенного для реализации систем реального времени, но это – другая история…), а вот работа с остальными 32-мя пользовательскими (внешними) прерываниями от периферийных устройств как раз и представляет для нас интерес!
     Для использования контроллера прерываний (NVIC), помимо настройки его регистров, выбора приоритета и разрешения прерывания от определённого события, нужно иметь таблицу векторов прерываний (содержащую адреса процедур обработки прерываний), которая для микроконтроллеров STM32F0xx имеет следующий вид (указаны только прерывания от периферийных устройств!): 
                                              
     Обработчик                                  Приоритет                                                               Описание
WWDG_IRQHandler                               7                                 – Оконный сторожевой таймер (Window watchdog interrupt)
PVD_IRQHandler                                   8                                 – Блок контроля напряжения питания (PVD and VDDIO2 supply comparator interrupt)
RTC_IRQHandler                                   9                                 – Прерывание от часов реального времени (RTC interrupts)
FLASH_IRQHandler                               10                                 – Глобальное прерывание от Flash
RCC_IRQHandler                                  11                                 – Глобальное прерывание от RCC и CRS
EXTI0_1_IRQHandler                            12                                 – Внешнее прерывание EXTI по линии [1:0]
EXTI2_3_IRQHandler                            13                                 – Внешнее прерывание EXTI по линии [3:2]
EXTI4_15_IRQHandler                           14                                – Внешнее прерывание EXTI по линии [15:4]
TS_IRQHandler                                    15                                 – Прерывание от тач-сенсора
DMA1_Channel1_IRQHandler                  16                                 – Прерывание по 1 каналу DMA
DMA1_Channel2_3_IRQHandler               17                                 – Прерывание по 2 и 3 каналу DMA
DMA1_Channel4_5_IRQHandler               18                                 – Прерывание по 4, 5, 6 и 7 каналу DMA
ADC1_COMP_IRQHandler                       19                                 – Прерывание по АЦП и Компаратору (ADC и COMP)
TIM1_BRK_UP_TRG_COM_IRQHandler     20                                 – Прерывание при сбое, обновлении, пуске и коммутации таймера TIM1
TIM1_CC_IRQHandler                           21                                 – Прерывание по захвату/сравнению таймера TIM1
TIM2_IRQHandler                                22                                 – Глобальное прерывание от таймера TIM2
TIM3_IRQHandler                                23                                 – Глобальное прерывание от таймера TIM3
TIM6_DAC_IRQHandler                         24                                 – Глобальное прерывание от таймера TIM6 и прерывание по отставанию ЦАП (DAC)
TIM7_IRQHandler                                25                                 – Глобальное прерывание от таймера TIM7
TIM14_IRQHandler                               26                                 – Глобальное прерывание от таймера TIM14
TIM15_IRQHandler                               27                                 – Глобальное прерывание от таймера TIM15
TIM16_IRQHandler                               28                                 – Глобальное прерывание от таймера TIM16
TIM17_IRQHandler                               29                                 – Глобальное прерывание от таймера TIM17
I2C1_IRQHandler                                  30                                 – Глобальное прерывание от I2C1
I2C2_IRQHandler                                  31                                 – Глобальное прерывание от I2C2
SPI1_IRQHandler                                  32                                 – Глобальное прерывание от SPI1
SPI2_IRQHandler                                  33                                 – Глобальное прерывание от SPI2
USART1_IRQHandler                             34                                 – Глобальное прерывание от USART1
USART2_IRQHandler                             35                                 – Глобальное прерывание от USART2
USART3_4_IRQHandler                          36                                 – Глобальное прерывание от USART3 и USART4
CEC_IRQHandler                                   37                                 – Глобальное прерывание от CEC и CAN
USB_IRQHandler                                   38                                 – Глобальное прерывание от USB
 
     Красный –> в девайсе STM32F031F4P6 эта функция отсутствует!
 
     Таблица с данными векторами прерываний содержится в файле startup_stm32f0xx.s. Поэтому, при необходимости объявления в программе процедуры обработки прерывания, например по переполнению таймера TIM2, заглядываем в этот файл, и пишем в своём исходнике:
 
void  TIM2_IRQHandler (void)
{           
     что-то там важное и необходимое :-)
}
 
     Как можно видеть из таблицы, на возникновение запроса прерывания от разных событий одного и того же блока периферии, мы подпадём в одну и ту же (глобальную) процедуру обработки прерывания. А дальше уже (в этой процедуре обработки прерывания) опросом различных битов состояния статусных регистров интересующего нас блока периферии будем определять, каким же конкретным событием оно у нас было вызвано. В принципе, довольно мудрое решение разработчиков, учитывая, сколько блоков периферии и возможных источников прерываний существует даже в самых «простеньких» STM32!

     Вообще, за работу модуля NVIC отвечают такие регистры:
ISER – регистр разрешения прерывания, позволяющий также просмотреть (прочитать), какие прерывания разрешены;
ICER – регистр отключения прерывания, позволяющий также просмотреть, какие прерывания разрешены;
ISPR – регистр перевода прерывания в состояние ожидания (можно прочитать, какие прерывания находятся на рассмотрении);
ICPR – регистр вывода прерывания из состояния ожидания (можно прочитать, какие прерывания находятся на рассмотрении);
IPR0…IPR– регистры установки приоритета. Каждый регистр содержит по четыре 8-ми битовых  поля, предназначенных для установки приоритета на каждое прерывание.
 
     Зачем нужны регистры приоритета? По умолчанию, каждое прерывание имеет свой приоритет (как указано в таблице прерываний выше), однако вы можете установить его сами, с помощью манипуляций с регистрами IPR0…IPR7. И при одновременом возникновении двух разных прерываний (или при обработке прерывания с более низким приоритетом), обработка перейдёт к прерыванию с более высоким приоритетом. К тому же, система приоритетов, которой характеризуется ядро Cortex, позволяет после выполнения одного (высокоприоритетного) прерывания сразу перескакивать на выполнение другого (низкоприоритетного) прерывания, минуя возврат в основную программу. При этом экономится время работы – если с момента возникновения прерывания до начала его выполнения нужно 12 тактов внутренней частоты, то для перехода с одного прерывания (уже выполненного) на другое – нужно уже 6 тактов. Profit!
     Более подробно о всех описанных регистрах можно почитать на 70 странице мануала Programming Manual PM0215, который можно скачать внизу. Я же побитово эти регистры рассматривать не буду, т.к. работа с ними легко организована в виде функций в файле core_cm0.h (этот файл содержит в себе описание ядра микроконтроллера – Cortex-M0).     
     В этом файле есть такая структура, с помощью которой обеспечивается доступ к NVIC:
 
typedef struct
{
  __IO uint32_t   ISER[1]; // Interrupt Set Enable Register          
  __IO uint32_t   ICER[1]; // Interrupt Clear Enable Register         
  __IO uint32_t   ISPR[1]; // Interrupt Set Pending Register          
  __IO uint32_t   ICPR[1]; // Interrupt Clear Pending Register        
  __IO uint32_t   IP[8];    // Interrupt Priority
NVIC_Type;

     Так вот, в файле core_cm0.h описаны несколько полезных функций, позволяющих работать с вышеприведенной структурой и, соответственно с регистрами, обеспечивающими работу с блоком прерываний.
void  NVIC_EnableIRQ(IRQn_Type   IRQn) – функция разрешения прерывания, где IRQn – номер внешнего прерывания;
void  NVIC_DisableIRQ(IRQn_Type   IRQn) – а это, естественно, запрет прерывания;
uint32_t  NVIC_GetPendingIRQ(IRQn_Type  IRQn) – функция чтения статуса ожидания прерывания. Возвращает «1», если прерывание находится в состоянии ожидания обработки (иначе – «0»);
void  NVIC_SetPendingIRQ(IRQn_Type   IRQn) – переводит прерывание в режим ожидания обработки;
void  NVIC_ClearPendingIRQ(IRQn_Type   IRQn) – выход прерывания из режима ожидания;
void  NVIC_SetPriority(IRQn_Type   IRQn, uint32_t   priority) – установка приоритета прерывания;
uint32_t NVIC_GetPriority(IRQn_Type   IRQn– чтение уровня приоритета прерывания. 
     
     Во всех этих функциях IRQn  это переменная по шаблону (типу перечисления) IRQn_Type, который объявлен в файле stm32f0xx.h. Эта переменная может принимать один из объявленных в IRQn_Type номеров (векторов) прерываний, которые представлены в виде перечислений: TIM2_IRQn, TIM3_IRQn, IM14_IRQn и т.д. все основные блоки периферии микроконтроллера (АЦП, USART, I2C, внешние прерывания…), имеющие возможность вызова прерывания.
     Поэтому, например, разрешение прерывания по переполнению таймера TIM2 будет выглядеть так:
NVIC_EnableIRQ(TIM2_IRQn);

     Теперь, чтобы получить, например, работу таймера TIM2 с прерыванием по переполнению счётного регистра, соберётся приблизительно такой код:
 
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE); //Включение тактирования таймера TIM2 (подключение к шине APB)
NVIC_EnableIRQ(TIM2_IRQn ); // Включение прерывания от TIM2
TIM2->PSC = 47999; // Установка предделителя
TIM2->ARR = 1000; // Установка значения регистра автоперезагрузки (до которого будет вестись счёт)
TIM2->DIER |= TIM_DIER_UIE; // Разрешение прерывания по событию обновления
TIM2->CR1 |= TIM_CR1_CEN; // Включение тактирования таймера (разрешение подачи тактового сигнала)
 
     И обработчик прерывания для таймера:
void TIM2_IRQHandler(void)
{
   TIM2->SR &=~ TIM_SR_UIF; // Сбрасываем флаг прерывания
 
}

     Если уже забыли – флаг установки прерывания всегда нужно очищать ручками самому!
 
     И ещё! Помните, в AVR-ах (по крайне мере, в Code Vision AVR) часто использовались ассемблерные вставки #asm("sei") и #asm("cli"), позволяющие, соответственно, разрешать и запрещать глобальные прерывания? Так вот, и в STM32 такое есть в обязательном порядке! Только выглядят они уже в виде процедур:
 
void __enable_irq(void) // Разрешить прерывания
void __disable_irq(void) // Запретить прерывания
 
и теперь, чтобы просто разрешить глобальные прерывания (т.е. дать добро на работу выбранных нами прерываний, например, от таймеров TIM2 и TIM3), мы вставляем в программу такой вот код: __enable_irq();
     Кому интересно, находятся эти процедуры в файле core_cmFunc.h и обеспечивают сброс или установку бита I в регистре CPSR.

     Но мой рассказ о модуле NVIC и его возможностях остался бы не полным, если бы я не затронул в теме о блоке внешних прерываний EXTI, значительно расширяющем возможности микроконтроллера. Но так как в структуре микроконтроллера он идёт отдельным функциональным модулем, дабы не засиратьгромождать мозг новой порцией информации, опишу его уже в следующей статье :-)

     Скачать:

Опубликовано 27.05.2015
©Igoryosha, 2015
 
 
Назад к содержимому | Назад к главному меню