Я кодирую что-то, используя прямой контроль над GPIO, для этого есть несколько хороших ресурсов, таких как http://elinux.org/RPi_Low-level_peripherals#GPIO_hardware_hacking ; процесс включает open ("/ dev / mem"), а затем операция mmap эффективно отображает нужный физический адрес в ваше виртуальное адресное пространство. Затем вы читаете раздел 6 этого http://www.raspberrypi.org/wp-content/uploads/2012/02/BCM2835-ARM-Peripherals.pdf, чтобы узнать о том, как контролируется ввод-вывод.
Чтобы изменить функцию вывода (вход, выход или другие специальные функции), вы изменяете эти 3-битные поля в регистрах ввода-вывода GPFSELx (000 = вход, 001 = экземпляр выходного объекта). Эти операции модификации скомпилированы для операций с обычной загрузкой и сохранением (например, чтобы изменить GPIO0 на input: * (regptr) & = ~ 7; который компилируется в нечто вроде
ldr r2, [r3, #0] ; r = *ptr (load r2 from I/O register)
bic r2, r2, #7 ; r2 &= ~7
str r2, [r3, #0] ; *ptr = r2 (store r2 to I/O register)
Проблема заключается в следующем: если между загрузкой и сохранением происходит прерывание, и другой процесс или ISR изменяет тот же регистр ввода-вывода, операция сохранения (на основе устаревшего чтения в r2) будет возвращать эффекты этой другой операции. Таким образом, изменение этих регистров ввода / вывода действительно необходимо выполнить с помощью атомарной (заблокированной) операции чтения / изменения / записи. Примеры, которые я видел, не используют заблокированную операцию.
Поскольку эти регистры ввода-вывода обычно меняются только при настройке чего-либо, маловероятно, что возникнут проблемы, но «никогда» всегда лучше, чем «маловероятно». Кроме того, если у вас есть приложение, в котором вы обрабатываете биты для эмуляции вывода с открытым коллектором, то (насколько я могу судить) это включает в себя программирование выхода на 0, а затем переключение его между выходом (для низкого уровня) или входом ( для выключенного / высокого). Таким образом, в этом случае будут частые моды для этих регистров ввода / вывода, и небезопасные модификации будут с гораздо большей вероятностью вызывать проблемы.
Итак, возможно, для этого можно использовать ARM «сравнить и установить» или аналогичную операцию, может ли кто-нибудь указать мне на это, и как это сделать из кода C?
[Примечание: ничего особенного не требуется, если вы запрограммировали ввод / вывод как выход и просто изменили его с 0 на 1 или наоборот; поскольку существует регистр ввода / вывода, в который вы записываете, для установки выбранных битов на 1, а другой для сброса выбранных битов на 0. Для этой операции не требуется чтение / запись, поэтому нет опасности от прерываний].
/dev/mem
кажется, что ваш код - это код пользовательского пространства. Я не думаю, что в любой современной ОС нужно быть осторожным с прерываниями, меняющими значения регистров в коде пользовательского пространства. Я считаю, что это не будет проблемой даже в коде пространства ядра, поскольку Linux восстанавливает все регистры, когда обработчик прерываний завершает свою работу.Ответы:
Я смотрел на это, ARM имеет инструкции 'ldrex и' strex ', strex будет возвращать результат сбоя, если исключительность потеряна (или, возможно, была потеряна) после ldrex, который включает переключение контекста (или другой процессор, модифицирующий тот же самый). зарегистрироваться в многопроцессорной среде). Так что это можно сделать с помощью этого; если strex не удается, вы зацикливаетесь и снова делаете операцию (с новым ldrex).
ссылка: http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dht0008a/ch01s02s01.html
Представленные ниже подпрограммы, кажется, работают на Raspberry Pi (поскольку они генерируют ассемблер, который я ожидал; и что влияние на биты, когда я их использую, является ожидаемым. Я не проверял, что они защищают от проблемы переключения контекста) , Обратите внимание, что это встроенные функции, а не функции, поэтому они должны быть помещены в заголовочный файл.
[ РЕДАКТИРОВАТЬ : Это не работает для обсуждаемой цели, кажется, это как-то запрещено. Если я использую эти подпрограммы, где * addr - обычная переменная, она работает нормально. Когда я использую его, где * addr указывает на сопоставленный регистр GPIO, процесс получает ошибку шины. (Когда я изменяю ldrex / strex на ldr / str и отключаю цикл do, он тогда работает). Таким образом, кажется, что эксклюзивный монитор ARM не может или не настроен на работу с регистрами ввода-вывода с отображением в памяти, и вопрос остается открытым.]
источник