Cortex M3 поддерживает полезную пару операций (также распространенную на многих других машинах), называемых «исключающими нагрузку» (LDREX) и «исключающими хранилище» (STREX). Концептуально операция LDREX выполняет загрузку, а также устанавливает некоторое специальное оборудование для наблюдения, может ли загруженное местоположение быть записано чем-то другим. Выполнение STREX по адресу, используемому последним LDREX, приведет к тому, что этот адрес будет записан только в том случае, если больше ничего не было написано первым . Инструкция STREX загрузит регистр с 0, если хранилище имело место, или с 1, если оно было прервано.
Обратите внимание, что STREX часто пессимистичен. Существуют различные ситуации, когда он может решить не работать с магазином, даже если это место не было затронуто. Например, прерывание между LDREX и STREX заставит STREX предположить, что наблюдаемое местоположение может быть повреждено. По этой причине обычно рекомендуется минимизировать объем кода между LDREX и STREX. Например, рассмотрим что-то вроде следующего:
inline void safe_increment (uint32_t * addr)
{
uint32_t new_value;
делать
{
new_value = __ldrex (addr) + 1;
} while (__strex (new_value, addr));
}
который компилируется во что-то вроде:
; Предположим, R0 содержит рассматриваемый адрес; R1 разгромили
LP:
ldrex r1, [r0]
добавить r1, r1, # 1
Strex R1, R1, [R0]
cmp r1, # 0; Проверьте, не ненулевой
бн лп
.. код продолжается
Подавляющее большинство времени выполнения кода, между LDREX и STREX ничего не случится, чтобы «потревожить» их, поэтому STREX преуспеет без дальнейших церемоний. Однако, если прерывание происходит сразу же после инструкции LDREX или ADD, STREX не будет выполнять сохранение, а вместо этого код вернется обратно, чтобы прочитать (возможно, обновленное) значение [r0] и вычислить новое увеличенное значение основываясь на этом.
Использование LDREX / STREX для формирования таких операций, как safe_increment, позволяет не только управлять критическими секциями, но и во многих случаях избегать необходимости в них.
while(STREXW(new_value, addr);
как мы можем поверить в то, что вы говорите правильно, если ваш код даже не скомпилируется?Похоже, вам нужны циклические буферы или FIFO в программном обеспечении MCU. Отслеживая два индекса или указателя в массиве для чтения и записи, вы можете получить доступ к одному и тому же буферу как переднего, так и заднего плана без помех.
Код переднего плана свободен для записи в кольцевой буфер в любое время. Он вставляет данные в указатель записи, затем увеличивает указатель записи.
Фоновый код (обработка прерываний) потребляет данные из указателя чтения и увеличивает указатель чтения.
Когда указатели чтения и записи равны, буфер пуст и фоновый процесс не отправляет данные. Когда буфер заполнен, процесс переднего плана отказывается писать больше (или может перезаписать старые данные, в зависимости от ваших потребностей).
Использование кольцевых буферов для разделения читателей и писателей должно устранить необходимость отключения прерываний.
источник
Я не могу вспомнить точное местоположение, но в библиотеках, которые приходят из ARM (не TI, ARM, он должен быть под CMSIS или что-то в этом роде, я использую ST, но я помню, как где-то читал, что этот файл пришел из ARM, поэтому вы должны иметь его также ) есть опция отключения глобального прерывания. Это вызов функции. (Я не на работе, но завтра посмотрю точную функцию). Я хотел бы завершить это с хорошим именем в вашей системе и отключить прерывания, сделать свое дело и включить снова. Сказав это, лучшим вариантом будет реализация семафора или структуры очереди вместо глобального отключения прерывания.
источник