понедельник, 19 ноября 2012
NVIC, векторный контроллер прерываний, есть во всех контроллерах на базе ядер Cortex-M, так как он входит в состав ядра. В старых ядрах контроллер прерываний был отдельно и не отличался хорошими характеристиками в плане скорости.
Описывается он подробно в Cortex-M4 Programming Manual.
читать дальшеОн управляет прерываниями от всей периферии и может их сортировать в порядке важности, если сработало сразу несколько. То есть, система прерываний тут многоуровневая. Каждому прерыванию можно назначить свой приоритет. И, кроме того, прерывания могут быть вложенными. Прерывания с более высоким приоритетом могут вытеснить менее приоритетные прерывания.
Есть ряд групп регистров:
NVIC_ISER0-NVIC_ISER7 — маска для разрешения прерываний;
NVIC_ICER0-NVIC_ICER7 — маска для запрещения прерываний;
NVIC_ISPR0-NVIC_ISPR7 — маска для активации заклипрерывания;
NVIC_ICPR0-NVIC_ICPR7 — маска сброса активного прерывания;
NVIC_IABR0-NVIC_IABR7 — флаги активных прерываний;
Плюс отдельно целая пачка регистров, где настраиваются приоритеты: NVIC_IPR0-NVIC_IPR59
И один регистр для программной активации любого прерывания по его номеру: NVIC_STIR.
Маски все устроены одинаково: каждая содержит по 32 флага, к каждому приписано какое-то прерывание. Конкретное распределение по периферии всегда на совести производителя контроллера и даётся в соответствующей литературе по нему.
В нулевом регистре содержатся флаги прерываний под номерами 0-31, в первом 32-63, и т.д.
То есть младшая часть номера определяется положением бита, старшая — номером регистра.
Рассчитать положение флага в регистре и номер регистра можно по следующим формулам:
Таблица прерываний заранее определена в файле startup_stm32f4xx.s. Там все прерывания по умолчанию указывают на бесконечный цикл.
Если создать в своём модуле экспортируемую функцию с таким же названием, то именно на неё будет указывать адрес в этой таблице прерываний.
У нас же есть таймер и единственное его прерывание (совмещённое с прерыванием от ЦАП) TIM6_DAC_IRQHandler.
В прерывании можно делать всё, что захотим. Это привелегированный режим. Главное, не забыть снять флаг, который вызвал это прерывание (у нас это UIF из регистра TIM6->SR), иначе в нём можно застрять навсегда.
Как и во всех прерываниях, контроллер предусмотрительно сохранил в стеке перед входом в прерывание регистры R0-R3, R12, LR, PC и xPSR. Потому, если это возможно, лучше использовать только их. Остальные регистры надо сохранять уже самостоятельно.
Для прерываний можно иметь отдельный стек, если основной код выполняется в пользовательском режиме.
Возвращение из прерывания происходит так же, как и из функции, то есть записью содержимого LR в PC. Правда, там лежит не адрес, а специальный код возврата, лучше который не трогать и не менять.
Чтобы в прерывание, после того, как мы его создали, попасть, надо его разрешить и в периферии (чтобы она посылала запрос в NVIC), и в самом NVIC, чтобы он это дело разруливал.
Собственно, для этого достаточно записать единичку в нужный регистр ISER. Я не стал рассчитывать вручную, а написал функцию NVIC_EnableIRQ (описана в Programming Manual, пример см. в nvic.s), которая это делает сама.
Номер прерывания я вытащил из файла stm32f4xx.h: 54. Хотя, конечно, он есть и в документации.
Итоговый вид обработчика прерывания:
Здесь он просто увеличивает значение переменной. Примерно так же обрабатываются и остальные прерывания.
И да, если не хотите, чтоб прерывание кем-то ещё прерывалось, запрещайте прерывания на время нахождения в нём с помощью CPSIE/CPSID (какое-то масло масляное).
проект
main.s
nvic.s<< Предыдущее Следующее >>
@темы:
arm,
программизмы,
электроника,
ассемблер,
stm32f4discovery,
stm32
Спасибо!
При переполнении таймера ставится флаг "pending", и в обработчик входить не желает. Что делать?
На f103 при тех же самых настройках работает как и должен. Почему? Каких-то кординальных отличий в Tim4 на f103 и f429 не заметил.
Просто я сейчас попробовал (не кодом, в system viewer), всё отлично запрерывалось, а я всего-то nvic разрешил, UIE в DIER и CEN в CR1.
MOV R1, #0x4
STR R1, [R0, #RCC_APB1ENR]
MOV32 R0, #TIM4
MOV R1, #0xAFC8
STR R1, [R0, #0x28] ; TIMx_PSC - делитель
MOV R1, #0x07D0
STR R1, [R0, #0x2C] ; TIMx_ARR - максимум счета
MOV R1, #0x0000
STR R1, [R0, #0x24] ; TIMx_CNT
MOV R1, #0x0001
STR R1, [R0, #0x0C] ; TIMx_DIER - тут только разрешаю прерывания UIE
MOV R1, #0x0085
STR R1, [R0] ; TIMx_CR1 - APRE+URS+CEN биты
MOV32 R0, #NVIC_Base
MOV32 R1, #0x40000000
STR R1, [R0] ; - Тут у регистра NVIC_ISER0 смещение равно 0
CPSIE I
System Viewer пользовали? Программа не улетает в стартап?
По крайней мере для STM32f103 +1 не требовалось, все и так работало. Тем более Startup файл создается автоматом и там как я думаю все првильно.
Название прерывания скопировал из Startup, как оно может быть неправильным?
Да и... прерывание не то чтобы не вызывается... оно вызывается, только в NVIC почему-то сразу после запроса ставится статус отложенного прерывания - "pending" флаг. Поэтому обработчик и не вызывается! А как исправить?
Я смог добиться невызывания прерывания только отключив глобально прерывания или отключив в nvic 30е прерывание. Оно вызывается железно =D
Когда я работал на Pinboard II с stm32f103 и отлаживал через JTAG картина была другая: после переполнения сразу вылетало прерывание, и если в обработчике опять возникало переполнение
(т.к. таймер/периферия при отладке не останавливается и работает независимо от ядра), то после выхода из него обработчик вызывался повторно.
Как я понял с SWD как-то по другому... но как? Сейчас получается, что когда я пошагово отлаживаю программу обработчик не вызывается (а таймер как видно по регистру счета CNT так же не останавливается), и только если нажму "RUN" в него можно попасть.
Самое интересное, что прерывание вызывается не через строго одинаковые интервалы времени, а всегда по разному. Допустим задал я период счета 2 секунды, но судя по счетчику каждый раз прерывание может вызываться через разные интервалы времени.
С чем это может быть связано?
Такие тонкости лучше узнавать из документации на ядро и отладку =) Возможно, специально сделано так, чтоб при пошаговом выполнении он не кидался во все обработчики подряд, не давая выполнять основной код. В авр студии это раздражает — там надо специально глобальные прерывания запрещать, а то он не даст пошагать по коду.
Точность хода лучше проверять анализатором логическим при постоянном выполнении. При остановке выполнения есть куда разных задержек (передача инфы на комп, перенастройка IDE, поиск обработчика по адресу и т.д.), из-за которых время срабатывания субъективно казаться больше и разным.