Какой объем оперативной памяти занимает вектор прерываний

Обновлено: 07.07.2024

Микропроцессоры типа х86 имеют два входа запросов внешних аппаратных прерываний :

  • NMI - немаскируемое прерывание , используется обычно для запросов прерываний по нарушению питания;
  • INT - маскируемое прерывание , запрос от которого можно программным образом замаскировать путем сброса флага IF в регистре флагов.

Рис. 14.4. Структура контроллера приоритетных прерываний

Единственный вход запроса маскируемых прерываний микропроцессора не позволяет подключить к нему напрямую сигналы запросов от большого числа различных внешних устройств, которые входят в состав современного компьютера: таймера, клавиатуры, "мыши", принтера, сетевой карты и т.д. Для их подключения к одному входу INT микропроцессора используется контроллер приоритетных прерываний (рис. 14.4). Его функции:

  • восприятие и фиксация запросов прерываний от внешних устройств;
  • определение незамаскированных запросов среди поступивших запросов ;
  • проведение арбитража : выделение наиболее приоритетного запроса из незамаскированных запросов в соответствии с установленным механизмом назначения приоритетов ;
  • сравнение приоритета выделенного запроса с приоритетом запроса , который в данный момент может обрабатываться в микропроцессоре, формирование сигнала запроса на вход INT микропроцессора в случае, если приоритет нового запроса выше;
  • передача в микропроцессор по шине данных типа прерывания , выбранного в процессе арбитража , для запуска соответствующей программы - обработчика прерывания ; это действие выполняется по сигналу разрешения прерывания INTA от микропроцессора, который выдается в случае, если прерывания в регистре флагов микропроцессора не замаскированы ( IF=1 ).

Переход к соответствующему обработчику прерывания осуществляется (в реальном режиме работы микропроцессора) посредством таблицы векторов прерываний . Эта таблица (рис. 14.5) располагается в самых младших адресах оперативной памяти, имеет объем 1 Кбайт и содержит значения сегментного регистра команд ( CS ) и указателя команд ( IP ) для 256 обработчиков прерываний .


Рис. 14.5. Структура таблицы векторов прерываний

Здесь мы разберем такие важные темы, как: обработка прерываний, векторы прерываний, программные прерывания, IRQ, в общем поговорим на темы прерывания.

Идея прерывания была предложена в середине 50-х годов и основная цель введения прерываний – реализация синхронного режима работы и реализация параллельной работы отдельных устройств ЭВМ.

Прерывания и обработка прерываний зависят от типа ЭВМ, поэтому их реализацию относят к машинно-зависимым свойствам операционных систем.

Прерывание (interrupt) – это сигнал, заставляющий ЭВМ менять обычный порядок выполнения команд процессором.

Возникновение подобных сигналов обусловлено такими событиями, как:

  • завершение операций ввода-вывода.
  • истечение заранее заданного интервала времени.
  • попытка деления на нуль.
  • сбой в работе аппаратного устройства и др.

Обработка прерывания


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

Предположим, что в момент поступления сигнала прерывания от некоторого источника программа А находится в решении. В результате управление автоматически передается обработчику прерываний. После завершения обработки управление может быть снова передано в ту точку программы А, где ее выполнение было прервано:


Векторы прерываний

Адреса программ, соответствующих различным прерываниям, собраны в таблицу, которая называется таблицей векторов прерываний.

Для микропроцессора требуется простой способ определения местоположения программы обработки прерывания и это осуществляется путем использования таблицы векторов прерываний.

Можно просмотреть таблицу векторов прерываний в компьютере, если воспользоваться программой DEBUG. Используйте команду D для вывода содержимого начала памяти: D 0:0. Программа DEBUG покажет вам первые 128 байтов или 32 вектора, которые могут иметь вид наподобие следующего:

0000:0000 E8 4E 9A 01 00 00 00 00-C3 E2 00 F0 00 00 00 00
0000:0010 F0 01 70 00 54 FF 00 F0-05 18 00 F0 05 18 00 F0
0000:0020 2C 08 51 17 D0 0A 51 17-AD 08 54 08 E8 05 01 2F
0000:0030 FA 05 01 2F 05 18 00 F0-57 EF 00 F0 F0 01 70 00
0000:0040 90 13 C7 13 4D F8 00 F0-41 F8 00 F0 3E 0A 51 17
0000:0050 5C 00 B7 25 59 F8 00 F0-E2 0A 51 17 9C 00 B7 25
0000:0060 00 00 00 F6 8E 00 DE 09-6E FE 00 F0 F2 00 7B 09
0000:0070 27 08 51 17 A4 F0 00 F0-22 05 00 00 00 00 00 F0

Векторы хранятся как «слова наоборот»: сначала смещение, а потом сегмент. Например, первые четыре байта, которые программа DEBUG показала выше (E8 4E 9A 01) можно преобразовать в сегментированный адрес 019A:4EE8.

Можно встретить три вида адресов в таблице векторов. Это могут быть адреса, указывающие на ROM-BIOS, которые можно идентифицировать шестнадцатеричной цифрой F, которая предшествует номеру сегмента. Это могут быть адреса, которые указывают на главную память (как в примере: 019A:4EE8). Эти адреса могут указывать на подпрограммы ДОС или на резидентную программу (например, SideKick или Prokey), либо они могут указывать на саму программу DEBUG (поскольку DEBUG должна временно управлять прерыванием). Также векторы могут состоять из одних нулей, когда прерывание с данным номером не обрабатывается в текущий момент.

Инициализация таблицы происходит частично BIOS после тестирования аппаратуры и перед началом загрузки операционной системой, частично при загрузке операционной системы.

Ниже приведено назначение некоторых векторов:

Механизм обработки прерываний

При обработке каждого прерывания должна выполняться следующая последовательность действий:

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

Главные функции механизма прерывания:

  1. распознавание или классификация прерываний.
  2. передача управления соответственно обработчику прерываний.
  3. корректное возвращение к прерванной программе (перед передачей управления обработчику прерываний содержимое регистров процессора запоминается либо в памяти с прямым доступом либо в системном стеке).

Типы прерываний

Прерывания, возникающие при работе вычислительной системы, можно разделить на 4 группы:


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

Аппаратные прерывания не координируются c работой программного обеспечения. Когда вызывается прерывание, то процессор оставляет свою работу, выполняет прерывание, a затем возвращается на прежнее место.

Внешние прерывания возникают по сигналу какого-либо внешнего устройства например:

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

Прерывание по таймеру вызывается интервальным таймером. Этот таймер содержит регистр, которому может быть присвоено определенное начальное значение посредством специальной привилегированной команды. Значение этого регистра автоматически уменьшается на 1 по истечении каждой миллисекунды времени. Когда это значение становятся равным нулю, происходит прерывание по таймеру. Подобный интервальный таймер используется операционной системой для определения времени, в течение которого программа пользователя может оставаться под управлением машины.

Маскируемые и немаскируемые внешние прерывания

Существуют два специальных внешних сигнала среди входных сигналов процессора, при помощи которых можно прервать выполнение текущей программы и тем самым переключить работу центрального процессора. Это сигналы NMI (Non Mascable Interrupt, немаскируемое прерывани) INTR (interrupt request, запрос на прерывание).

Соответственно внешние прерывания подразделяются на два вида: немаскируемые и маскируемые.

Часто при выполнении критических участков программ, для того чтобы гарантировать выполнение определенной последовательности команд целиком, приходится запрещать прерывания (т.е. сделать систему нечувствительной ко всем или отдельным прерываниям). Это можно сделать командой CLI. Ее нужно поместить в начало критической последовательности команд, а в конце расположить команду STI, разрешающую процессору воспринимать прерывания. Команда CLI запрещает только маскируемые прерывания, немаскируемые всегда обрабатываются процессором.

Внутренние прерывания вызываются событиями, которые связаны с работой процессора и являются синхронными с его операциями, а именно прерывание происходит, когда:

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

Программные прерывания

Программы могут сами вызывать прерывания с заданным номером. Для этого они используют команду INT. По этой команде процессор осуществляет практически те же действия, что и при обычных прерываниях, но только это происходит в предсказуемой точке программы – там, где программист поместил данную команду. Поэтому программные прерывания не являются асинхронными (программа «знает», когда она вызывает прерывание).

Механизм программных прерываний был специально введен для того, чтобы:

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

Пример (программные прерывания):

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

В упрощенном виде схему обработки различных видов прерываний можно представить следующим образом:


КП – контроллер прерываний, имеет несколько уровней (линий) для подключения контроллеров устройств (на схеме обозначены КУ). Возможно каскадное подключение контролеров, когда на один из его входов подключается еще одни контроллер прерываний. ЦП – центральный процессор.

Аппаратные прерывания вырабатываются устройствами компьютера, когда возникает необходимость их обслуживания. В отличие от программных прерываний, вызываемых запланировано самой прикладной программой, аппаратные прерывания всегда происходят асинхронно по отношению к выполняющимся программам. Кроме того, может возникнуть одновременно несколько прерываний. Выбор одного из них для обработки осуществляется на основе приоритетов, приписанных каждому типу прерывания.

Каждому прерыванию назначается свой уникальный приоритет. Если происходит одновременно несколько прерываний, то система отдает предпочтение самому высокоприоритетному, откладывая на время обработку остальных прерываний.

Пpepывaнию вpeмeни cутoк дан мaкcимaльный пpиopитeт, пocкoльку ecли oнo будет пocтoяннo тepятьcя, то будут нeвepными пoкaзaния cиcтeмныx чacoв. Пpepывaниe от клaвиaтуpы вызывaeтcя при нaжaтии или oтпуcкaнии клавиши; oнo вызывaeт цепь coбытий, кoтopaя oбычнo зaкaнчивaeтcя тем, что код клавиши пoмeщaeтcя в буфep клaвиaтуpы (oткудa он зaтeм мoжeт быть пoлучeн пpoгpaммными пpepывaниями).

Ну и наконец реализация механизма обработки прерываний

В машине для каждого класса прерываний имеется соответствующая ему рабочая область прерываний. Например, имеется область, соответствующая прерыванию по таймеру. Когда происходит прерывание по таймеру, содержимое всех регистров сохраняется в этой области (например, пропустив первые несколько слов). Затем из этих пропущенных слов извлекаются заранее занесенные туда значения, которые перезаписываются в счетчик (указатель) команд машины и в слово состояния (или во флаговый регистр). Загрузка и сохранение регистров осуществляется аппаратными средствами машины автоматически.

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

После выполнения в ответ на запрос на прерывание любого требуемого действия стандартная программа обработки прерываний выполняет команду загрузки состояния процессора, в результате чего управление передается прерванной программе. Происходит это следующим образом: команда загрузки состояния процессора вызывает загрузку сохраненного содержимого слова состояния, счетчика команд и других регистров из соответствующих слов области сохранения, начиная с адреса, указанного в команде. Это приводит к восстановлению содержимого регистров и состояния процессора, которые были в момент прерывания. Управление затем передается на команду, перед выполнением которой произошло прерывание.

Сохранение и восстановление состояния процессора и содержимого регистров называют операцией контекстного переключения.

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

Обычные программы находятся в пользовательском режиме (признак равен нулю). Когда происходит прерывание, новое загружаемое содержимое слово состояния имеет признак, равный 1, что автоматически переводит процессор в режим супервизора. В этом режиме становится возможным использование привилегированных команд. Перед тем, как значение слова состояния будет сохранено, в другом его элементе (например, втором) будет установлено значение, указывающее на причину прерывания:

  • при программном прерывании отражается тип вызвавшего его условия, например деление на нуль.
  • при прерывании по вводу-выводу заносится номер канала, вызвавший прерывание.

В третьем элементе указывается, выполняет ли процессор команды или простаивает. В четвертом элементе содержится указатель, идентифицирующий текущую выполняемую программу. В пятом элементе содержится маска прерываний, которая используется для контроля за разрешением прерываний (поле MASK).

Это поле используется, чтобы не допустить наступления прерываний определенного типа, пока первое из них не будет обработано. В MASK каждый бит соответствует некоторому классу прерываний. Если какой-то бит установлен в 1, то прерывания соответствующего класса разрешены, если в 0, то запрещены. В последнем случае говорят, что они маскированы (их также называют запрещеннымиили закрытыми). Однако маскированные прерывания не теряются, потому что сигнал, вызвавший прерывание, сохраняется аппаратурой. Временно задержанное таким способом прерывание называется отложенным. Когда (вследствие того, что значение MASK сброшено) прерывания соответствующего класса вновь разрешаются, сигнал опознается и происходит прерывание.

Маскирование прерываний находится под контролем операционной системы и зависит от значения MASK в слове состояния, которое заранее сохраняется в рабочей области каждого прерывания. Можно запретить все прерывания, установив все биты MASK в нуль. В действительности поступать подобным образом нет необходимости.

Сегодня мы поговорим о прерываниях процессоров семейства x86 (-64). Подробнее под катом.
Прерывания — это как бы сигнал процессору, что надо прервать выполнение (их поэтому и назвали прерываниями) текущего кода и срочно сделать то, что указано в обработчике.

  • 0b0101: 32-битный гейт задачи, при появлении такого прерывания происходит хардверное переключение задачи (да-да, есть и такое, но его уже давно не используют)
  • 0b0110: 16-битный гейт прерывания
  • 0b0111: 16-битный гейт trap'a (я не знаю, как это перевести на русский язык, извините)
  • 0b1110: 32-битный гейт прерывания
  • 0b1111: 32-битный гейт trap'a
  1. Поиск вектора №0 в IDT.
  2. Сравнение уровня привилегий дескриптора и текущего уровня привилегий процессора.
  3. Если текущий уровень привилегий процессора меньше уровня привилегий дескриптора, то просто вызвать генеральную ошибку защиты и не вызывать прерывание.
  4. Происходит сохранение адреса возвращения, регистра (E)FLAGS и другой информации.
  5. Происходит переход на адрес, указанный в векторе №0 IDT.
  6. После выполнения обработчика инструкция iret возвращает управление прерванному коду.

Существует особый тип прерываний — IRQ (Interrupt ReQuest), или же аппаратные прерывания, но я буду их для краткости называть просто IRQ. Технически они почти не отличаются от любых других прерываний, но генерируются не процессором или самим кодом, а устройствами, подключенными к компьютеру. К примеру, IRQ №0 генерируется PIT (таймер с программируемым интервалом), IRQ 1 генерируется при нажатии клавиши на клавиатуре, а IRQ 12 — при действии с PS/2-мышью.


Еще есть так называемые программные прерывания. Их, как понятно из названия, программа должна вызывать сама — никто их за нее не вызывает. Таковыми являются, например, системные вызовы в некоторых системах. В Linux, например, они висят на векторе 0x80. Во многих хобби-ОС они тоже висят на векторе 0x80. Теперь немного отсебятины — я думаю, что сисвызовы сделаны в виде прерываний из-за того, что 1) их так очень легко вызывать, 2) их можно вызвать из любого кода, работающего в ОС — IDT-то одна на всю систему.
Информация взята с OSDev Wiki.

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

Вот это и будет темой сегодняшней статьи. Причем рассматривать вопрос сегодня мы будем безотносительно конкретной реализации в конкретном микроконтроллере. Есть очень много общих моментов в обработке прерываний не требующих погружения в детали реализации. Кроме того, это позволит избежать повторов при дальнейшем изучении различных семейств микроконтроллеров.

Общее, частное, тонкости

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

При этом нет принципиальной разницы между обработкой программных и обработкой аппаратных прерываний. За исключением того, что обработка аппаратных требует взаимодействия с контроллером прерываний, но это лишь малая часть кода обработчика.

Поскольку языки программирования высокого уровня, в большинстве своем, не предусматривают работу с прерываниями, во многих компиляторах предусматриваются расширения стандарта языка. Поэтому программы с обработкой прерываний являются не только аппаратно-зависимыми, но зачастую и компиляторо-зависимыми.

Что бы не утонуть в деталях различных реализаций я сегодня буду использовать С-подобный синтаксис и обобщенное описание процедуры обработчика

__interrupt ISR_proc(VECT_NUM, PRI)

здесь __interrupt это модификатор, который и указывает компилятору, что функция ISR_proc, кстати, имя может быть любым на ваш вкус, является не просто функцией, а именно обработчиком прерывания. И компилятор будет обрабатывать специальным образом.

Хорошо видно, что функция не возвращает, да и не может возвращать, значения. Зато имеет два параметра, которые параметрами вызова на самом деле не являются . И оба параметра могут отсутствовать.

Первый параметр VECT_NUM определяет номер прерывания, или номер вектора, что одно и тоже. Именно номер, не адрес. Это важный момент. Этот параметр используется компилятором для размещения вызова функции по правильному адресу в таблице векторов прерываний. В собственно вызове он не используется и в функцию не передается.

Второй параметр PRI определяет приоритет обработчика. Он используется если процессор или контроллер прерываний поддерживают работу с изменяемыми приоритетами прерываний. Гибкое управление приоритетами, если поддерживается аппаратно, позволяет задавать приоритет обработчика не связанным жестко с номером вектора.

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

Что в векторе тебе моем?

Про векторы прерываний я говорил уже много раз. А в статье

есть иллюстрация показывающая использование векторов. Я повторю эту иллюстрацию здесь

Вызов обработчика прерывания переходом по адресу вектора. Показан вектор вмещающий две команды. Иллюстрация моя, из 26 статьи цикла "микроконтроллеры для начинающих". Вызов обработчика прерывания переходом по адресу вектора. Показан вектор вмещающий две команды. Иллюстрация моя, из 26 статьи цикла "микроконтроллеры для начинающих".

Поскольку в рассматриваемых микроконтроллерах обычно векторы прерываний реализованы именно так, я не буду уделять внимание другим вариантам.

Итак, вектор прерывания это небольшой фрагмент памяти программ, который обеспечивает передачу управления непосредственно обработчику. Обычно размер этого фрагмента небольшой, например, 4 или 8 команд. А его начальный адрес однозначно связан с номером прерывания. Как может формироваться адрес из номера я показал в предыдущей статье.

Поскольку команда перехода к обработчику прерывания может быть довольно длинной, например, переход по адресу внешней памяти программ, размер вектора обычно задается разработчиком микроконтроллера с некоторым запасом.

Таблица векторов прерываний обычно размещается в начале памяти программ. Причина проста, для нее должно найтись место даже в микроконтроллерах с минимальным объемом памяти.

Во многих случаях не требуется размещать таблицу максимального размера, достаточно учесть максимальный номер используемых прерываний. Это позволяет сэкономить небольшой объем памяти. Однако, если "не ожидаемое" прерывание все таки произойдет, то результат будет катастрофическим. И такие случаи действительно иногда случаются!

Поэтому рекомендуется всегда задавать обработчики для всех прерываний, а не только реально используемых в программе. А что бы компилятор не вздумал своевольничать достаточно описать обработчик, даже пустой, для прерывания с максимальным номером возможным для конкретного используемого микроконтроллера.

Способ заставить компилятор сформировать полную таблицу векторов прерываний. Иллюстрация моя Способ заставить компилятор сформировать полную таблицу векторов прерываний. Иллюстрация моя

В данном случае компилятор разместил в векторах, для которых программист не определил в явном виде обработчики прерываний, команды возврата из прерывания. Это позволит, хотя бы, избежать катастрофы с передачей управления в неподходящее для этого место.

Такой способ приходится применять, так как в языке высокого уровня нет возможности формировать таблицу векторов вручную. Да, мы можем задать адрес начала процедуры (через расширение языка). Но мы не можем разместить код вне функции. А при описании функции мы неизбежно получаем добавление компилятором пролога и эпилога к коду функции, что не позволит втиснуть ее в вектор.

В некоторых случаях формирование пролога/эпилога можно избежать с помощью модификатора naked , если он поддерживается компилятором. Таким способом мы можем сформировать именованные фрагменты кода, которые полноценными функциями не являются. Но зато можно задать адрес из размещения. То есть, мы можем сформировать таблицу векторов вручную.

Что еще от нас скрывает компилятор. Сохранение контекста

Точнее не совсем скрывает, а просто старается не обременять лишней информацией тех, кому это не очень нужно. Но иногда настолько успешно, что даже в документации не найти требуемой информации и приходится лезть в сгенерированный код.

Сразу скажу, что касается это в большей части тех, кто использует возможность прямой вставки ассемблерного кода в текст программы на языке высокого уровня. И тех, кто использует специальные возможности компилятора и расширения языка для ручной оптимизации кода. Например, если для перебора элементов массива используется не переменная, а непосредственно индексный регистр.

Вспомните, что я говорил раньше. Программа не должна знать, что прерывание возникло и обработано, если это не предусмотрено программистом в явном виде. А асинхронное прерывание может возникнуть в любой момент времени, даже в середине выполнения машинной команды. Но вот обработка прерывания начнется только после завершения выполнения текущей команды. Выполнение команды прервано быть не может.

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

Выполнение команд обработчика прерывания тоже может изменить флаги состояния процессора. А значит, когда управление вернется команде перехода, состояние флагов будет отличаться от ожидаемого. А это является проблемой.

Поэтому перед обработкой прерывания надо сохранить не только адрес возврата, как показано на первой иллюстрации, но и все состояние процессора. То есть, несколько дополнительных, но важных, регистров. Процессор может оказывать для этого некоторую аппаратную поддержку. Но может и не оказывать.

Но состояние процессора это еще не все. Компилятор использует регистры процессора по своему усмотрению, по своим правилам. И вот эти то правила и приходится иногда искать. Например, компилятор может использовать часть доступных регистров для хранения промежуточных результатов вычислений. Если обработчик прерываний использует те же регистры, то надо сохранять и их. Может потребоваться сохранять указатель стека, если обработчику нужен доступ к переменным размещенным в стеке до возникновения прерывания.

Состояние процессора и состояние общих регистров используемых компилятором для внутренних целей (состояние программы) часто называется одним термином - контекст. Вот сохранение этого контекста перед обработкой прерывания и его восстановление перед возвратом управления и добавляется автоматически компилятором вместо стандартных пролога и эпилога для функций.

В большинстве случаев какого-либо вмешательства программиста не требуется. Компилятор отлично справляется сам. Но если обработчик прерывания написан на ассемблере, или где то в тексте программы используются ассемблерные вставки, компилятор может просто не знать, что нужно сохранять какие то дополнительные регистры. А значит, об этом должен позаботиться сам программист.

Таблица векторов прерываний в микроконтроллерах 1986ВЕ1Т(3Т) всегда расположена с нулевого адреса и не может быть перенесена. При разработке программного обеспечения, исполняющегося из ОЗУ, нужно это учитывать и для возможности использования прерываний применять следующие методы:

1. Во Flash-памяти расположить программу, в которой описать функции-подпрограммы обработки прерываний, например, для Timer 1 это может выглядеть так:

TIMER1_IRQHandler(void); Фрагмент кода 1

Эта функция описана в файле startup_MDR1986VE1T.s. А уже внутри этой функции (TIMER1_IRQHandler) должен вызываться реальный обработчик прерываний, расположенный в памяти ОЗУ.

2. Обработчики прерываний расположены в ОЗУ, а во Flash-памяти, в таблице векторов прерываний указать верные адреса этих обработчиков. Тогда при возникновении прерывания процессор обратится к таблице во Flash память, в которой возьмёт адрес обработчика прерывания, расположенного в ОЗУ.


Реализация первого метода

Дополнительно для скачивания в конце статьи доступен проект, загружаемый во Flash-память, чтобы наладить корректную работу прерываний.

Нужно не забыть прикрепить к основному проекту файл Setup.ini для запуска из ОЗУ согласно этой статье. Для удобства запуск производится с начального адреса 0x20100000.

После первичного запуска проекта из ОЗУ и отправки на отладочную плату данных прерывания не возникают. Обработчик в программе называется UART1_Handler.

Теперь во Flash-память необходимо записать программу, которая будет считывать значение адреса этого прерывания и "прыгать" на него после его возникновения.
Программа исполняется в микроконтроллере 1986ВЕ1Т с адресов 0x20100000, для UART1_Hundler необходимо считать значение памяти по адресу 0x20100058 (адрес высчитывается согласно таблице "Принцип формирования прерываний" из спецификации на микросхему), а затем перейти по полученному адресу. Таким образом произойдет выполнение обработчика прерывания из ОЗУ.

Код программы, записываемой во Flash-память:

int main()
NVIC_EnableIRQ(UART1_IRQn); //Enable UART1 interrupt
while(1)
UART1_IRQHandler();
>
>


//--- UART1 interrupt routine ---
void UART1_IRQHandler()
int A;
A = HWREG(0x20100058); // Занести в переменную А значение памяти по адресу 0x20100058 (UART_Hundler), чтобы корректно прыгнуть в обработчик прерывания в программе ОЗУ
typedef void (*funcptr)();
funcptr funcInRAM = (funcptr) (A);
funcInRAM(); // Прыжок
>

Фрагмент кода 2

Реализация второго метода

Второй метод менее мобильный, но тоже имеет право на жизнь.

Внесём следующие изменения в проект для Flash-памяти:

    Необходимо в main.c оставить только следующие строки кода:

int main()
NVIC_EnableIRQ(UART1_IRQn); //Enable UART1 interrupt
while(1)
UART1_IRQHandler();
> Фрагмент кода 3

Читайте также: