Введение #
Современные одноплатные компьютеры, такие как Repka Pi, позволяют управлять периферийными устройствами напрямую, минуя библиотеки и абстракции. Это возможно благодаря технике под названием MMIO (Memory-Mapped I/O) — отображение регистров оборудования в адресное пространство памяти процессора.
Рассмотрим, как это работает, как получить доступ к таким регистрам на Linux и как с их помощью напрямую управлять GPIO.
Важно учитывать, что общий принцип MMIO одинаков для всех моделей Repka Pi, но конкретные физические адреса, состав регистров и правила настройки GPIO зависят от SoC. В линейке Repka Pi это особенно важно: Repka Pi 3 использует Allwinner H5, Repka Pi 4 — Allwinner H6, а Repka Pi 5 — Rockchip RK3588. Поэтому низкоуровневые примеры и адреса из документации для одной модели нельзя автоматически переносить на другую.
Что такое MMIO #
MMIO (Memory-Mapped I/O) — это механизм, при котором специальные области памяти не содержат обычные данные, а представляют собой аппаратные регистры. Эти регистры отвечают за управление различными функциями чипа: GPIO, таймерами, SPI, UART и т. д.
Когда процессор обращается к определённому адресу памяти — например, 0x01C20800 на одной из платформ семейства Allwinner — он фактически читает или пишет значение в регистр периферийного устройства. Благодаря этому, управление внешними модулями становится возможным без драйверов или библиотек — напрямую на уровне байтов и битов.
Такой способ:
-
Очень быстрый и низкоуровневый,
-
Не зависит от библиотек,
-
Требует точного знания адресов и битовой разметки регистров (из datasheet).
Использование /dev/mem и mmap()
В Linux физическая память (включая MMIO-области) недоступна обычным процессам. Однако системное устройство /dev/mem предоставляет доступ к физическому адресному пространству.
Чтобы с ним работать:
-
Открываем
/dev/memс правами суперпользователя. -
С помощью функции
mmap()отображаем нужный диапазон физических адресов в виртуальное адресное пространство нашего процесса. -
Работаем с полученным указателем как с обычным массивом памяти.
Ниже представлен пример кода на языке программирования C:
int fd = open("/dev/mem", O_RDWR | O_SYNC);
void *map = mmap(NULL, length, PROT_READ | PROT_WRITE, MAP_SHARED, fd, phys_addr);
volatile uint32_t *reg = (uint32_t *)map;
reg[offset] = 1; // записываем значение в регистр
Важно: Сам по себе пример с
mmap()универсален как техника работы с MMIO, ноphys_addr,length, смещения регистров и допустимые значения битов всегда нужно брать из документации именно на ваш SoC и конкретную модель Repka Pi.