Программа выполняется, светодиоды светятся - что может быть круче? Но тут не круто, что программа линейная и пора бы освоить работу с прерываниями.
Кстати, я узнал, что в новых архитектурах ARM есть смециальная последовательность команд для загрузки 32-битного числа в регистр.
MOVW Rn,<младшие 2 байта>
MOVT Rn,<старшие 2 байта>
Но в ассемблере есть честный макрос: MOV32.
То есть все предыдущие инструкции LDR Rn, =<32-битное число> можно спокойно поменять на MOV32 Rn, <32-битное число>, что я и сделал. Правда, эти правки я не стал выкладывать в виде отдельных файлов, ибо не суть.
читать дальшеКод станет менее переносимым (на старые платформы), но кто собирается это делать, раз пишет на ассемблере?=D
UPD (от 12.11.2012): Кстати, ядро Cortex-M0 такое комбо тоже не умеет, так что если хотите код использовать и на нём, то лучше эту команду не трогайте, остановившись на старом добром LDR Rn, =Value.
И ещё одна необычная инструкция, устанавливающая правила условного выполнения до четырёх последующих команд: IT (If-Then), — которая появилась только на ядрах новой архитектуры.
Запись её такова: IT{X{X{X}}} <условие>
Работает это дело так: проверяется условие и, если оно верное, то следующая за этой команда выполнится.
Если хотим, чтоб две команды подряд подчинялись условию, то добавляем ещё один суффикс T: ITT. И так до четырёх штук.
Но это ещё не всё! Оно ещё умеет проверять обратное условие, то есть реализовывать поведение else. Для этого вместо суффикса T нужно применить суффикс E: ITTE. В этой команде две команды выполнятся, если условие истинно и пропустится третья, и наоборот, если условие ложно.
Очень интересная операция. Пример:
ITE EQ ; Если равно...
MOVEQ R0, #1 ; T: В регистр запишем 1
MOVNE R0, #0 ; E: В регистр запишем 0
На си это же:
Или:
Суффиксы условного выполнения к командам при этом писать обязательно. При этом нельзя помещать внутрь блока вызовы подпрограмм — это влечёт за собой непредсказуемое поведение.
Что насчёт прерываний... Обработчик прерывания объявляется очень просто. Таблица наименований прерываний есть в файле запуска startup_stm32f4xx.s. Для примера возьмём не самое простое (но и не сложное) прерывание: программное. Запишем его обработчик:
Ничем оно от подпрограммы пока не отличается. И это хорошо =)
Вызвать его можно командой SVC с каким-нибудь номером, который обозначает функцию. Прям как в DOS. В принципе, желающие могут написать что-то типа доса для практики на тех же принципах =D
Например: SVC 1
Разрешённые номера: от 0 до 255.
И всё! Программа прекрасно будет попадать в это прерывание.
Теперь необходимо, как минимум, добавить таблицу функций, а то толку с прерывания такого.
Сначала нужно узнать номер вызванного прерывания. Для этого придётся заглянуть в код, откуда вызывали, и вытащить номер из опкода инструкции SVC. Он сидит в младшем байте.
Заметьте! В старых версиях ядра регистр LR содержал в прерывании, как и в подпрограмме, адрес возврата. Теперь адрес возврата со всеми остальными регистрами лежит в стеке, а LR содержит специальный код для возвращения в обычный режим. Есть и другие коды, их можно посмотреть в PM0214 (Cortex-M4 Programming Manual).
В стек кладутся регистры: R0-R3, R12, LR, PC, xPSR. (Остальные надо сохранять вручную, если они будут использоваться). На R0 будет как раз указывать SP. До PC смещение 0x18. Ну и плюс сохранённый LR. Получится что-то вроде:
LDR R0, [SP, #0x1C] ; PC
LDRB R0, [R0, #-2] ; Младший байт инструкции (номер)
Ну а дальше получить адрес функции из специальной таблицы и перейти по нему. Лучше бы для всего этого запользовать регистр R12. В R0-R3 сидят аргументы всё ж. Большее количество аргументо использовать неразумно. Ищи-свищи потом в стеке остальное. Там же при включённом FPU ещё десяток с лишним регистров сохраняется (учтите при расчёте быстродействия).
Ещё одна тонкость - результат через регистры нет смысла передавать. По выходу из прерывания всё затрётся сохранённым содержимым стека. В принципе, можно копировать содержимое R0 куда надо и всё будет ок.
Пример таблицы:
Функции типа swi_LedOff - самые что ни на есть обычные. Я их даже не менял.
Вызовы функций я поменял на вызов SVC:
Для конечного кода ничего не поменялось. Но теперь можно делать API для RTOS. Ещё б RTOS эта была =D
Вызов, конечно, стал длиннее, но за всё надо платить.
Вот основные изменённые файлы:
main.s
swi.s
swi_table.inc
led.s
accel.s
И проект. << Предыдущее Следующее >>