понедельник, 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
Переписали сами?
Хотя по уму, их надо либо целиком перевести автоматически с Си на асм, написав или найдя соответствующую утилиту, либо сгенерировать инклюд из SVD-файлов... Хотя, по-моему, там только описание регистров... Но вручную переводить всё слишком уж муторно ^^"
Я переписал весь h файл но там нет offset, могу поделиться. ток подскажи смещения для ADC)
База указана в карте памяти там же и в файле stm32f4xx.h
Про них у меня мимоходом было сказано: Есть такая полезная инструкция CPS (Change Processor State). Раньше флаг глобального разрешения прерываний хранился в регистре CPSR и требовалось его считать, установить и записать (Команды MSR, MRS). Теперь всё делается за одну команду.
Вообще изначальная идея была использовать asm вставки в код но чтот они отказались пахать.
LDR R0, =ADC1 ; Это адрес же? да адрес
SHL R2, #2 ; Вроде ж так можно? незнаю, не пользовался командой shl.
_waitloop
; Читаем содержимое регистра
LDR R2, [R0, #ADC_SR_OFFSET]
; Накладываем маску и ставим флаги результата
ANDS R2, R1
BNE _waitloop ; Если не равно нулю, пробуем ещё раз
Занимательно. Я использовал немножко другой подход
Proverka PROC
mov32 r0, GPIO_IDR_IDR_7
ldr r1, =GPIOC_BASE
str r0,[r1]
pov tst r0, #GPIO_IDR_IDR_7
BNE pov
bx lr
ENDP
Тут еще и ограничение U-mail это печально..
Если не выполнится условие один раз, то оно не выполнится никогда) Хотя чтоб ему не выполниться, если сравнивается число само с собой?
Не замечал, я u-mail почти не пользуюсь)
Проверка нажатия кнопки. Сравниваются состояния PIN0 есть на нем 1 или нет, точно работало (stm32asm.ru/periph_pll_init.html).
Дак всеже можно вызывать функции си из asm при помощи BL?)
Что ж нельзя-то?) Можно, если соблюдать соглашения о вызове. Я где-то в самом начале stm32f4 об этом писал и приводил примерны =)
В принципе, можно использовать ANSI, — в сети есть патч, который добавляет русские символы в неё (на рутрекере в частности). Единственный минус — не понимает «ё». Ну и есть только под некоторый диапазон более-менее новых версий.
регистр - адрес со сдвигом/маска
RCC_APB2ENR - 0x40023844/0x00004000
SYSCFG_EXTICR1 - 0x40013808/0x0000000
EXTY_IMR - 0x40013C00/0x00000001
EXTI_RTSR - 0x40013C08/0x00000001
EXTI_FTSR - 0x40013C0C/0x00000001
NVIC_ISER0 - 0xE000E100/0x00000020
В качестве основы взят проект с морганием светодиодами и в него уже дописывалась вся инициализация. может как то неправильно описал процедуру обработчик? Хотя сделал все как у вас вроде: в startup_stm32f4xx.s ничего не менял, а код обработчика вот
п.с. в кейле можно пользоваться и отладчиком. только для отладки камешек f103 попробуй
п.с.с лутше бы весь проект в студию, т.к возможно причины могут быть в другом
EXTI0 — 6 канал, а значит маска 0x40, а не 0x20. Потому функции удобнее. Сейчас у вас выбрано прерывание от RCC.
HellEvil
Вы уверены что он крутиться в mainloop?
я пользуюсь st-link дебагером так что видел и состояние регистров, и какая команда выполняется.
А в асме предпочту использовать именованные константы, там, где их много, (перечислены все) видно, кого не хватает и где что потеряно. По-моему, Кейл и в асме так делать умеет.
Вначале совсем забыл "при включении всё выключено", к прерываниям тоже относится.
Потом не сразу въехал в NVIC и EXTI. EXTI описывается в референсе на F100xx, а NVIC - нет. Можно было бы написать, мол "Про NVIC почитайте в референсе F10xxx"
И потом всё рассчитано под код на сцях, примерами весь инет завален, а вот с асмом беда.
В общем IRQ для TIM6 ровно 54 (на F100 камни)
По вышеописанным формулам получаем номер регистра: 1 = ( 54 >> 5 ) и номер бита: 16 = ( 54 & 0x1F )
Базовый адрес NVIC_ISER равен 0xE000E100, а первый регистр равен 0xE000E104 (NVIC_ISER1)
Значит по адресу NVIC_ISER1 нужно накатить значение 0x8000 ( без чтения и модификации, предположим там нули ) что будет равно 0b1000000000000000
Тем самым разрешим прерывания от TIM6
Не работает блин
Берём nvic.s от уважаемого teplofizik и включаем его в проект.
NVIC_EnableIRQ по адресу NVIC_ISER1 записывает значение 0x400000 о_О
В общем и этот вариант тоже не пашет.
Открываем Сишные либы от ST, роемся, находим: NVIC->ISER[((uint32_t)(IRQn) >> 5)] = (1 << ((uint32_t)(IRQn) & 0x1F)); ( сдвиг в лево это маска? )
В этих же либах можно найти: #define NVIC_ISER_SETENA_16 ((uint32_t)0x00010000) /*!< bit 16 */
Ничерта не понятно....
Перепробовал все варианты, ни один не работает.
Я прикреплю код, если будет не лень посмотреть, указать на ошибку.
Кстати ещё вопрос, в векторной таблице обязательно должно быть значение +1 на всех обработчиках прерывания?
pastebin.com/urQ4iKic - основной
pastebin.com/Q6DXaj5D - таблица векторов
pastebin.com/DWRB90h9 - линкер
Плата: STM32 VL Discovery
Компилятор: arm-none-linux-gnueabi
Про NVIC надо читать в Cortex-M3 Programming Manual, раздел 4.3 =) В доках по контроллерам максимум упомянут кратко регистры и порядок следования векторов. Ну и ссылка на этот документ)
16 = ( 54 & 0x1F )
Позволю заметить, что 0x36 & 0x1F = 0x16 (22 бит). То есть 0x400000 — это правильно.
А что конкретно не пашет? Не попадает в прерывание или светодиодом не моргает?) Так я для светодиода не вижу настройки (на выход)... Да и битбенд, он всё куда надо пишет?=)
в векторной таблице обязательно должно быть значение +1 на всех обработчиках прерывания
Думаю, да. Тут суть в том, что изначально армы, которые имели два набора инструкции ARM и Thumb переключали наборы переходя с помощью команды BX, а набор команд выбирался последним битом: 0 означал 32битный арм, а 1 — 16битный Thumb. В кортексах набор всего один, Thumb2, но единичка в конце адреса сохранилась. В Кейле такого нет, но, думаю, он добавляет единички к адресам процедур во время сборки.
Плохо, что у меня нет платки, на которой проверить можно было бы =) У меня только F0 и F4...
Уже полистал, увидел у вас в сносках, спасибо.
А что конкретно не пашет?
Да вот хрен его знает
Вернее сказать не срабатывает обработчик прерывания. Обработчик должен потушить диод, что будет являться показателем его срабатывания.
Вроде с векторной таблицей всё правильно, проверил всё много раз.
И с включением прерываний как бы не должно быть ошибки, я даже в NVIC_ISER1 записал 0xFFFFFFFF чтобы уж точно
Вариантов думаю всего два: ошибка в таблице векторов или ошибка с включением прерывания для TIM6 через NVIC.
Таймер считает, флаг UIF выставляется таймером, UIE выставлен, а вот прерывание не срабатывает.
По поводу векторной таблицы:
читать дальше
Эх, жаль в своё время не было литературы по асму для спектрума, наверное знаний бы тогда в этой области у меня сейчас побольше было бы
А отладка что показывает? Там же можно все регистры прям руками помацать, светодиодом мышкой поморгать (чтоб проверить, что он работает), настроить таймер без кода и проверить, попадает ли он в прерывание и без светодиода, поставив точку останова =)
Имеете ввиду тактирование и настройка ножек на выход?
Я просто забыл сказать, в платке до сих пор вшита стандартная прошивка с завода, я её даже не менял. Программу гружу в память и оттуда запускаю, соответственно на момент запуска уже всё настроено, порты затактированны, ножка на кнопку настроена на вход, а на диоды - на выход.
В опубликованном коде диод должен переключаться, но сейчас там просто команда на его отключение.
А много ли дебагером можно уловить в такой ситуации? Я конечно смотрел, но там ничего особенного, основная программа доходит до Main и там зацикливается. А команда в обработчике прерывания на гашение диода равносильна точке останова в этом же обработчике. К слову точку ставил, не срабатывает
Кстати надо бы гуишный отладчик настроить, а то сижу в gdb. Это в кейле можно мышкой подёргать светодиодом и таймер настроить? Блин, удобно, не поспоришь
Пришла мысль. Попробую код на Си, если будет работать, посмотрю какие значения он пишет в NVIC_ISER
Да, это правильный подход =)
Нужно было просто переназначить таблицу векторов на новое место.
MOV32 R0, 0xE000ED08
MOV32 R1, newaddr
STR R1, [R0]
И сразу всё заработало
В Си так и не полез, дебри, лучше пока асм покурю.
Или надо было в оперативку загнать?)
Не вижу смысла грузить каждый раз в флеш когда можно грузить в оперативку.
Да я же этого не знал, совсем недавно начал изучать мк как таковые
Вас не затруднит объяснить вот этот момент
Позволю заметить, что 0x36 & 0x1F = 0x16 (22 бит). То есть 0x400000 — это правильно.
Не могу понять как связано 0x16, 22бита и 0x400000. То есть почему в ручном получаем 0х16, а функция выставляет значение 0х400000
0x16 — это 22 в десятичной системе счисления. 0x400000 — это число, где установлен только 22 бит (маска) =)