Процессоры Cortex-M3 поддерживают два режима работы: режим Thread и режим Handler. РежимThread вводится при сбросе и может быть введен через возврат исключения. Режим Handler вводится в результате выполнения исключения. Процессоры Cortex-M3 поддерживают два разных указателя стека — как показано на рисунке. После сброса весь код использует основной стек; однако процессор может быть сконфигурирован так, чтобы код приложения использовал отдельный «стек процессов». Всякий раз, когда процессор вызывает исключение, обработчик выполняется с использованием основного стека, а когда выполнение возвращается из исключения, процессор возобновляет использование стека, использованного ранее. за исключением. При вызове исключения состояние выполняемого в данный момент кода должно быть сохранено в главном стеке, чтобы его можно было восстановить после завершения обработчика исключения. В однопоточных приложениях, которые мы рассматривали до сих пор, нет существенного преимущества использования отдельных стеков; однако при использовании потоков, поддерживающих операционную систему, обычно считается желательным использовать отдельный стек для обработки и выполнения исключений в ядре ОС.
Сохранение и восстановление состояния — это взаимодействие между процессором и обработчиками исключений. Архитектура Cortex-M3 предполагает, что код обработчика исключений будет подчиняться стандарту вызова процедур архитектуры Arm, который предписывает, чтобы все процедуры (включая обработчики исключений) сохраняли и восстанавливали определенные регистры, если они были изменены. Аппаратное исключение Cortex-M3 берет на себя ответственность за сохранение любых других регистров. В частности, когда берется исключение, процессор помещает восемь регистров — xPSR, PC, LR, r12, r3, r2, r1 и r0 — в основной стек, как показано на рисунке. При возврате из обработчика исключений процессор автоматически выталкивает их из основного стека. После входа в обработчик исключений регистр связи (LR) содержит специальное значение, которое контролирует возврат исключения.
Процессор Cortex-M3 поддерживает несколько приоритетов исключений. Даже если в настоящее время выполняется обработчик исключения, исключение с более высоким приоритетом может быть вызвано с вытеснением текущего обработчика (приоритеты определяются как целые числа с меньшими целыми числами, имеющими более высокие приоритеты). В этом случае состояние вытесненного обработчика будет помещено в основной стек перед выполнением нового обработчика. Исключением с наивысшим приоритетом является Reset(-3), который может вытеснять все остальные. Приоритет большинства исключений может быть установлен под управлением программы.
Ядро процессора Cortex-M3 находит обработчики прерываний (исключений) через векторную таблицу. Каждая запись в таблице состоит из адреса обработчика прерываний. Таблица индексируется по уникальному номеру каждого источника прерывания. Формат таблицы векторов STM32 определяется как справочным руководством Cortex-M3, так и соответствующим справочным руководством STM32. Фрагмент этой таблицы для устройств STM32 F100xx показан в таблице.
Первые 16 записей (через SysTick_Handler) определяются спецификацией CortexM3, а остальные зависят от процессора. Первая запись в таблице — это не адрес обработчика прерываний, а адрес начального стека. При сбросе ядро загружает указатель стека из ячейки памяти 0x0000_0000 и начинает выполнение в ячейке Reset_Handler, хранящейся в ячейке памяти 0x0000_0004. Этот фрагмент также включает записи для TIM3, USART1 и EXTI0, которые мы рассмотрим далее.
Cortex-M3 указывает, что таблица векторов начинается с ячейки памяти 0x0000_0000. Есть пара важных исключений. STM32, при загрузке с flash, «псевдонимы» флэш-памяти, которые начинаются с местоположения от 0x0800_0000 до 0x0000_0000-таким образом, память считывает из местоположения 0x0000_0000 фактически возвращает значение, хранящееся в 0x0800_0000 . Также возможно перемещать местоположение активной векторной таблицы во время выполнения — важная функция для поддержки фиксированных загрузчиков, которые могут размещать векторные таблицы приложений в других местах. Таблица векторов прерываний определяется в коде запуска, а его местоположение определяется сценарием компоновщика.
Каждый вектор прерывания имеет начальное «weak» определение. Чтобы переопределить это определение, достаточно определить процедуру с правильным именем. Например, мы ранее определили обработчик системного таймера:
void SysTick_Handler(void){
if (TimingDelay != 0x00)
TimingDelay --;
}
Аналогично, мы определим обработчик для USART1 как:
void USART1_IRQHandler(void) {
// Check interrupt cause
...
// Clear interrupt cause
}
Обратите внимание, что в отличие от обработчика SysTick, большинство обработчиков должны, по крайней мере, определять причину прерывания — при использовании USART это может быть пустой буфер передачи или полный буфер приема — и устранять причину прерывания. Определение причины, как правило, выполняется путем чтения конкретного периферийного регистра состояния. Очистка прерывания выполняется путем выполнения необходимого действия (например, считывания регистра данных) или прямого сброса соответствующего бита состояния.
Обязательные имена обработчиков, определенные в коде запуска, показаны на рисунке STM32 F100xx Interrupt Vector Table. Обратите внимание, что разные члены семейства STM32 поддерживают разные периферийные устройства и, следовательно, имеют разные наборы имен обработчиков. Кроме того, эти имена определены в коде запуска, а не библиотечными функциями. Если вы сомневаетесь, вы должны посмотреть на источники запуска! Кроме того, будьте осторожны с опечатками в именах векторов, которые могут быть сложными для диагностики.



