Какие прерывания Cortex-M3 я могу использовать для работы общего назначения?
Я хотел бы иметь некоторый код, который должен быть запущен в результате срабатывания определенного прерывания.
Я не хочу выполнять его в контексте самого прерывания, но я также не хочу, чтобы он выполнялся в режиме потока.
Я хотел бы запустить его с приоритетом, который ниже, чем прерывание высокого уровня, которое ускорило его работу, но также с приоритетом, который выше уровня потока (и некоторых других прерываний также).
Я думаю, что мне нужно использовать один из других обработчиков прерываний.
Какие из них лучше всего использовать и как их лучше всего вызывать?
В настоящий момент я планирую просто использовать обработчики прерываний для некоторых периферийных устройств, которые я не использую, и вызывать их, устанавливая биты непосредственно через NVIC, но я надеялся, что есть лучший, более официальный способ.
Спасибо,
5 ответов
ARM Cortex поддерживает особый вид исключения, называемый PendSV. Похоже, вы могли бы использовать это исключение именно для выполнения своей работы. Практически все вытесняющие ОСРВ для ARM Cortex используют PendSV для реализации переключения контекста.
Чтобы это работало, вам нужно установить приоритет PendSV (записать 0xFF в регистр PRI_14 в NVIC). Вы также должны расставить приоритеты для всех IRQ над PendSV (запишите меньшие числа в соответствующие регистры приоритетов в NVIC). Когда вы будете готовы обработать все сообщение, запустите PendSV из ISR с высоким приоритетом:
*((uint32_t volatile *)0xE000ED04) = 0x10000000; // trigger PendSV
Процессор ARM Cortex затем завершит ваш ISR и все другие ISR, которые, возможно, были прерваны им, и в конечном итоге будет привязан к исключению PendSV. Вот где должен быть ваш код для разбора сообщения.
Обратите внимание, что PendSV может быть прервана другими ISR. Это все хорошо, но вы должны помнить, чтобы защитить все общие ресурсы критическим разделом кода (кратковременное отключение и включение прерываний). В ARM Cortex вы отключаете прерывания, выполняя __asm ("cpsid i"), и включаете прерывания с помощью __asm ("cpsie i"). (Большинство компиляторов C предоставляют встроенные функции или макросы для этой цели.)
Вы используете RTOS? Как правило, этот тип вещей может быть обработан наличием потока с высоким приоритетом, который получает сигнал для выполнения некоторой работы по прерыванию.
Если вы не используете ОСРВ, у вас есть только несколько задач, и работа, выполняемая прерыванием, не слишком ресурсоемкая, возможно, проще всего выполнить работу с высоким приоритетом в контексте обработчика прерываний. Если эти условия не выполняются, то реализация того, о чем вы говорите, станет началом самой базовой многозадачной ОС. Это может быть интересный проект сам по себе, но если вы хотите просто выполнить работу, вы можете рассмотреть простую ОСРВ.
Поскольку вы упомянули некоторые особенности работы, которую вы делаете, вот обзор того, как я справлялся с подобной проблемой в прошлом:
Для обработки полученных данных через UART один метод, который я использовал при работе с более простой системой, которая не имеет полной поддержки для постановки задач (т. Е. Задачи циклически разбираются в простой while
цикл) должен иметь общую очередь для данных, полученных из UART. Когда срабатывает прерывание UART, данные считываются из RDR (регистра данных приема) UART и помещаются в очередь. Уловка, чтобы справиться с этим таким образом, чтобы указатели очереди не были повреждены, состоит в том, чтобы тщательно сделать указатели очереди изменчивыми и убедиться, что только обработчик прерываний изменяет указатель хвоста и что только задача "переднего плана", которая читает данные вне очереди изменил указатель головы. Обзор высокого уровня:
производитель (обработчик прерываний UART):
- читать
queue.head
а такжеqueue.tail
в местных жителей; - увеличить локальный указатель хвоста (не фактический
queue.tail
указатель). Оберните его в начало буфера очереди, если вы увеличили значение до конца буфера очереди. - сравнить
local.tail
а такжеlocal.head
- если они равны, очередь заполнена, и вам придется делать все, что нужно для обработки ошибок. - в противном случае вы можете записать новые данные туда, где
local.tail
точки - только теперь вы можете установить queue.tail == local.tail
- возврат из прерывания (или, при необходимости, выполнение других задач, связанных с UART, например чтение из очереди передачи)
- читать
потребитель (на переднем плане "задача")
- читать
queue.head
а такжеqueue.tail
в местных жителей; - если
local.head
==local.tail
очередь пуста; вернуться, чтобы позволить следующему заданию сделать некоторую работу - прочитайте байт, на который указывает
local.head
- приращение
local.head
и оберните это в случае необходимости; - задавать
queue.head
знак равноlocal.head
- перейти к шагу 1
- читать
Удостоверься что queue.head
а также queue.tail
являются volatile
(или запишите эти биты в сборке), чтобы убедиться в отсутствии проблем с последовательностью.
Теперь просто убедитесь, что ваша очередь принятых данных UART достаточно велика, чтобы в ней содержались все байты, которые могли быть получены до того, как задача переднего плана получит шанс на выполнение. Задача переднего плана должна извлекать данные из очереди в свои собственные буферы для создания сообщений, которые передаются задаче "обработчик сообщений".
"Более официальным способом" или, скорее, обычным способом является использование приоритетного многозадачного планировщика с приоритетом и шаблона "обработчик отложенного прерывания".
То, что вы просите, довольно просто на Cortex-M3. Вам нужно включить регистр STIR, чтобы вы могли запускать ISR с низким приоритетом с помощью программного обеспечения. Когда высокоприоритетный ISR завершается с критическими вещами, он просто запускает низкоприоритетное прерывание и завершается. Затем NVIC будет привязан к обработчику с низким приоритетом, если не происходит ничего более важного.
Проверьте документацию вашего процессора. Некоторые процессоры будут прерывать, если вы запишете бит, который вы обычно должны очищать внутри прерывания. В настоящее время я использую SiLabs c8051F344 и в разделе спецификации 9.3.1:
"Программное обеспечение может имитировать прерывание, устанавливая любой флаг ожидания прерывания в логическое значение 1. Если для флага включены прерывания, будет сгенерирован запрос на прерывание, и ЦП будет преобразовывать вектор в адрес ISR, связанный с флагом ожидания прерывания".