четверг, 28 сентября 2017 г.

STM32. Maple Mini. EXTI. Внешние прерывания



В этой статье изучим внешние прерывания. И попробуем знания на практике.


Два ядра внешних прерываний.


Прерывания в stm32 задействует две системы:

  • EXTI - контроллер внешних прерываний/событий.
  • NVIC - контроллер вложенных векторных прерываний.

EXTI контроллер предназначен для:

  • обнаружение внешних сигналов;
  • содержит регистры для конфигурирования линий прерываний;
  • содержит биты состояния для каждой линии прерывания;
  • возможность генерации программных событий/прерываний.

NVIC контроллер предназначен для:

  • управления и обработки всех прерываний, в том числе прерываний ядра (т.е. при обнаружении прерывания, контроллер прерывает выполнение текущей программы и вызывает соответствующую функцию обработчик);
  • содержит возможность управлением приоритетом прерываний.


Немного об EXTI регистрах.


В EXTI контроллере мы определяем какой GPIO вывод будет вызывать внешнее прерывание.
Читая главу 10.2 мануала, видно что существует 20 линий событий/прерываний. Первые 16 можно использовать для внешних прерываний, где порядковый номер каждой линии соответствует номеру GPIO вывода. Т.е одновременно использовать два GPIO вывода с одним и тем же порядковым номером на разных GPIO портах нет возможности. Но об этом станет понятнее далее.

Давайте посмотрим на 4 регистра: AFIO_EXTICR1AFIO_EXTICR2AFIO_EXTICR3AFIO_EXTICR4
Заметим, что они находятся в AFIO, значит AFIO порт нужно будет тактировать.
В каждом из этих регистров используются только первые 16 битов, которые в свою очередь сгрупированны по 4 бита.

Это AFIO_EXTICR1 регистр, который содержит первые четыре EXTIx подрегистра.

Эти группки битов имееют название EXTIx (где x порядковый номер каждой группы). В EXTIx записывается значение соответствующие букве порта ввода/вывода, который будет использоваться как вход внешнего прерывания:

0000 - порт A
0001 - порт B
0010 - порт C
0011 - порт D
0100 - порт E
0101 - порт F
0110 - порт G

Порядковый номер (x) используемого подрегистра EXTIx будет соответстовать номеру линии порта ввода/вывода, который будет использоваться как вход внешнего прерывания. Например, записав в EXTI3 значение 0010, то входом внешнего прерывания будет 3 линия порта С или записав в EXTI6 значение 0011, входом внешнего прерывания будет 6 линия порта D (глава 10.2.5).

Еще существуют следующие регистры для внешних прерываний (глава 10.3 мануала):
  • EXTI_IMR - регистр маскирования прерываний, позволяет включать/выключать линии прерываний.
  • EXTI_EMR - регистр маскирования событий, позволяет включать/выключать линии событий.
  • EXTI_RTSR - регистр срабатывания по возрастающему фронту, происходит генерация события прерывания когда на линии появляется возрастающий фронт (например, скачек от 0 до 3.3В).
  • EXTI_FTSR - регистр срабатывания по падающему фронту, происходит генерация события прерывания когда на линии появляется падающий фронт(от 3.3В до 0).
  • EXTI_SWIER - регистр программных прерываний/событий.
  • EXTI_PR  - регистр флагов прерывания. Будет 1 на соответствующем бите при появлении запроса на прерывание. Если не сбросить бит, то будет вечный вызов обработчика прерывания. Его необходимо сбрасывать вручную в теле функции-обработчика прерывания.

Конфигурирование NVIC.


CMSIS содержит уже готовые функции для работы с NVIC контроллером, которые можно найти в файле core_cm3.h.
Нам понадобятся:
  • NVIC_EnableIRQ - включает внешнее прерывание в NVIC контроллере;
  • NVIC_DisableIRQвыключает внешнее прерывание в NVIC контроллере.
Также есть функции управлением приоритета прерывания, но сейчас мы этого касаться не будем. Если не назначать приоритет, то он останется дефолтным (см. таблицу 61 главы 10.1.2).
Названия векторов для этих функций нужно искать в stm32f10x.h.


Название векторов прерывания в CMSIS.


Чтобы найти имя вектора прерывания нужно заглянуть в startup_stm32f10x_md.s. Имя вектора прерывания будет использоваться в названии функции обработчика прерывания.

Например, найдем вектор прерывания для встроенной кнопки. Она находится на линии PB8.
8 линия находится в AFIO_EXTICR3 и называется EXTI8. Поиском попытаемся найти все имена с EXTI в файле startup_stm32f10x_md.s и в результате наткнемся на такую строчку:

DCD     EXTI9_5_IRQHandler        ; EXTI Line 9..5

Из нее следует что линии с 5 по 9 используют один и тот же вектор прерывания. Так было задумано разработчиком МК смотри главу 10.1.2 мануала (так же и 10 - 15 линии используют один и тот же вектор).
Значит наш вектор прерывания будет EXTI9_5_IRQHandler.


Небольшая инструкция.


Теперь пишу последовательность шагов для включения внешнего прерывания.
  1. Выбираем GPIO линию и порт для входа прерывания.
  2. Включить тактирование для альтернативного порта. Т.к. прерывания это альтернативная функция.
  3. Включить обычное тактирование для используемого GPIO порта.
  4. Записываем в соответсвующий регистр AFIO_EXTICR в соответсвующую EXTI группу битов, имя GPIO порта (соответсвующие значения смотри выше).
  5. Включаем возможность прерывания на используемой линии EXTI установив 1 в соответсвующий бит в EXTI_IMR регистре.
  6. Настраиваем фронт срабатывания прерывания используя EXTI_RTSR (возрастающий) или EXTI_RTSR (падающий) регистры.
  7. Настраиваем GPIO линию прерывания как вход.
  8. Добавляем вектор прерывания в NVIC контроллер.
  9. Пишем функцию-обработчик прерывания и не забываем вручную сбрасывать флаг прерывания в EXTI_PR регистре. Сброс происходит установкой 1 в соответсвующий линии бит.

Пример.

Используемые кнопка и светодиод на плате.

Встроенной кнопкой (PB8) управлять встроенным светодиодом PB1 используя прерывания.
Не забываем читать комментарии!

#include "stm32f10x.h"

void main(void{
  SystemInit();
 
  //включаем тактирование для GPIO
  RCC->APB2ENR |= RCC_APB2ENR_IOPBEN;
 
  //включаем тактирование для альтернативного порта,
  //тк внешние прерывания это альтернативная функция
  RCC->APB2ENR |= RCC_APB2ENR_AFIOEN;
 
  //настраиваем линию светодиода (PB1) на двухтактный выход
  GPIOB->CRL &=~(GPIO_CRL_CNF1_0);
  GPIOB->CRL &=~(GPIO_CRL_CNF1_1);
  //настраиваем линию светодиода на выход с частотой 50 МГц
  GPIOB->CRL |= (GPIO_CRL_MODE1_1);
  GPIOB->CRL |= (GPIO_CRL_MODE1_0);

  //разрешаем прерывание на 8 линии
  EXTI->IMR |= EXTI_IMR_MR8;
  //прерывание будет срабатывать по нарастающему фронту
  EXTI->RTSR |= EXTI_RTSR_TR8;
  //настраиваем PB8 как линию входа для прерывани
  //нужно быть осторожным тк 8 линия находится в EXTICR3 регистре,
  // а мы в левой части пишем AFIO->EXTICR[2]
  AFIO->EXTICR[2] |= AFIO_EXTICR3_EXTI8_PB;
  //добавляем наш ветор прерывания в nvic контроллер
  NVIC_EnableIRQ(EXTI9_5_IRQn);
  //в данном примере я не буду настраивать приоритет прерываний
  //разрешаем глобальные прерывания
  __enable_irq();

  //теперь настраиваем кнопку PB8 как вход с подтягивающим резистором на землю
  //более подробно в предыдущей статье в 20 таблице
  GPIOB->CRH &=~(GPIO_CRH_CNF8_0);
  GPIOB->CRH |=(GPIO_CRH_CNF8_1);

  GPIOB->CRH &=~ (GPIO_CRH_MODE8_1);
  GPIOB->CRH &=~ (GPIO_CRH_MODE8_0);
  //включаем подтягивающий резистор
  GPIOB->ODR &=~ GPIO_ODR_ODR2;
 
  while (1{
  }
}

//обработчик прерывания
void EXTI9_5_IRQHandler(void{
  //тк вектор EXTI9_5_IRQHandler используют с 5 по 9 линии,
  //нужно явно проверять что прерывание случилось на 8 лиинии
  if ((EXTI->PR & EXTI_PR_PR8) == EXTI_PR_PR8) {
    //сюда можно добавить небольшую задержку, но это уже будет явно быдлокод
    //проверяем что кнопка еще нажата, чтобы исклюить дребезг контактов
    if ((GPIOB->IDR & GPIO_IDR_IDR8) == GPIO_IDR_IDR8) {
      GPIOB->ODR ^= GPIO_ODR_ODR1;
    }
    //сбрасываем флаг прерывания для 8й линии вручную
    EXTI->PR |= EXTI_PR_PR8;
  }
}

На этом пока что все. В следующей статье я буду рассматривать таймеры.


Комментариев нет:

Отправить комментарий