Как реализованы обработчики прерываний в CMSIS Cortex M0?

9

У меня есть комплект LPC1114. Последние несколько дней я копал CMSIS-реализацию Cortex M0, чтобы узнать, как в ней все делается. До сих пор я понимал, как каждый регистр отображается и как я могу получить к нему доступ. Но все же я не знаю, как в нем реализованы прерывания. Все, что я знаю о прерываниях в CMSIS, - это имена некоторых обработчиков прерываний, упомянутых в файле запуска. И я могу написать свои собственные обработчики, просто написав функцию C с теми же именами, которые указаны в файле запуска. Что меня смущает, так это то, что в руководстве пользователя сказано, что все GPIO могут использоваться в качестве внешних источников прерываний. Но в файле запуска есть только 4 прерывания PIO. Так скажи мне:

  1. Как я могу реализовать внешние обработчики прерываний для других GPIO?
  2. Где таблица прерываний отображается в CMSIS?
  3. Каковы основные различия между NVIC и реализацией прерываний в AVR / PIC? (за исключением того, что NVIC может отображаться в любом месте флэш-памяти)
0xakhil
источник

Ответы:

14

Следующая информация дополняет отличный ответ Игоря.

С точки зрения программирования на C, обработчики прерываний определены в файле cr_startup_xxx.c (например, файл cr_startup_lpc13.c для LPC1343). Все возможные обработчики прерываний определяются как псевдоним WEAK. Если вы не определяете свой собственный XXX_Handler () для источника прерывания, то будет использоваться функция обработчика прерываний по умолчанию, определенная в этом файле. Линкер определит, какую функцию включить в конечный двоичный файл вместе с таблицей векторов прерываний из cr_startup_xxx.c

Пример прерываний GPIO от портов показан в демонстрационных файлах в gpio.c. Существует один вход прерывания для NVIC на порт GPIO. Каждый отдельный бит в порту может быть включен / отключен для генерации прерывания на этом порту. Если вам требуются прерывания на портах PIO1_4 и PIO1_5, например, то вы должны включить отдельные биты прерываний PIO1_4 и PIO1_5 в GPIO0IE. Когда сработает ваша функция обработчика прерываний PIOINT0_Handler (), вы можете определить, какие из прерываний PIO1_4 или PIO1_5 (или оба) ожидают, прочитав регистр GPIO0RIS и обработав прерывание соответствующим образом.

Остин Филлипс
источник
10

(Обратите внимание, что пункты 1 и 2 являются деталями реализации, а не архитектурными ограничениями.)

  1. В более крупных чипах NXP (таких как LPC17xx) есть пара выделенных контактов прерываний (EINTn), которые имеют свой собственный обработчик прерываний. Остальные GPIO должны использовать одно общее прерывание (EINT3). Затем вы можете опросить регистр состояния прерывания, чтобы увидеть, какие контакты вызвали прерывание.
  2. Я не очень знаком с LPC11xx, но кажется, что он имеет одно прерывание на порт GPIO. Вам снова нужно будет проверить регистр состояния, чтобы выяснить конкретные выводы. Есть также до 12 пинов, которые могут служить источниками пробуждения. Я не уверен, что вы можете угнать их как обычные прерывания (т.е. они, вероятно, будут срабатывать только в состоянии сна).
  3. Таблица обработчиков по умолчанию размещается по адресу 0 (который находится во флэш-памяти). Первая запись - это значение сброса для регистра SP, вторая - вектор сброса, а остальные - другие исключения и векторы прерываний. Несколько первых (таких как NMI и HardFault) исправлены ARM, остальные - для чипа. Если вам нужно изменить векторы во время выполнения, вы можете переназначить их в ОЗУ (сначала вам нужно скопировать таблицу). В LPC11xx переназначение фиксируется для начала SRAM (0x10000000), другие чипы могут быть более гибкими.
  4. NVIC оптимизирован для эффективной обработки прерываний:
    • программируемый уровень приоритета 0-3 для каждого прерывания. Прерывания с более высоким приоритетом вытесняют прерывания с более низким приоритетом (вложение). Выполнение с более низким приоритетом возобновляется, когда прерывание с более высоким приоритетом закончено.
    • автоматическая укладка состояния процессора при входе прерывания; это позволяет писать обработчики прерываний непосредственно в C и устраняет необходимость в обертках сборки.
    • цепочка хвостов: вместо того, чтобы снова нажимать и толкать состояние, следующее ожидающее прерывание обрабатывается немедленно
    • позднее прибытие: если прерывание с более высоким приоритетом прибывает во время стекирования состояния процессора, оно выполняется немедленно вместо ранее ожидающего.

Поскольку вы знакомы с PIC, взгляните на это приложение. Примечание. Переход с микроконтроллеров PIC на Cortex-M3

Речь идет о M3, но большинство пунктов относится и к M0.

Игорь Скочинский
источник
8

Остин и Игорь ответы достаточно подробно. Тем не менее, я хочу ответить на это по-другому, может быть, вы найдете это полезным.

LPC11xx (Cortex-M0) имеет 4 уровня для выводов GPIO, все выводы от GPIO0.0 до GPIO0.n имеют одинаковый номер прерывания, а все выводы от GPIO3.0 до GPIO3.m имеют одинаковый номер прерывания.

Есть шесть шагов для инициализации прерывания GPIO в LPC11xx

  1. Настройте функцию контактов, изменив регистры блока подключения контактов.
  2. Установите направление вывода, изменив регистр направления данных GPIO (по умолчанию вводится значение).
  3. Настройте прерывание для каждого отдельного вывода, вам нужно перейти в регистр маски прерывания GPIO GPIOnIE и установить бит (соответствующий выводу) логики 1.
  4. Настройте прерывание для переднего или заднего фронта, либо изменив регистры чувствительности прерываний GPIO GPIOnIBE и GPIOnIS.
  5. Включите источник прерывания PIO_0 / PIO_1 / PIO_2 / PIO_3 в Nested Vectored Interrupt Control, используя функции CMSIS.
  6. Установите приоритет прерывания с помощью функций CMSIS.

Реализации кода. Вам нужны две функции: одна инициализирует 6 описанных выше шагов, а вторая - это обработчик прерываний, имя которого должно совпадать с именем обработчика, определенного в startup_LPC11xx.sфайле кодов запуска . Имена от PIOINT0_IRQHandlerдо PIOINT3_IRQHandler. Если вы используете другое имя, вы должны изменить имена в файле запуска.

/*Init the GPIO pin for interrupt control */
void GPIO_Init(){
    LPC_IOCON-> =..              //Pin configuration register
    LPC_GPIO1->FIODIR = ...      //GPIO Data direction register
    LPC_GPIO1->FIOMASK = ..      //GPIO Data mask register - choose  the right pin
    LPC_GPIO1->GPIOnIE = ..      //Set up falling or rising edge 
    NVIC_EnableIRQ(PIO_1);       //Call API to enable interrupt in NVIC
    NVIC_SetPriority(PriorityN); //Set priority if needed
}


/*Must have the same name as listed in start-up file startup_LPC11xx.s */
void PIOINT1_IRQHandler(void){
   //Do something here
}
Фуонг Фам
источник