https://chatgpt.com/s/t_69971a23f9e4819188fe20704426be4f
Высокая латентность ioctl(hspi, SPI_IOC_MESSAGE(1), tr) на Embedded Linux (ядро 5.10, TI AM335x / ARM Cortex-A8) — это нормальная картина для userspace SPI через spidev. ~150 мкс за вызов — типично. Ниже разбор причин и что с этим делать.
Почему так медленно
1️⃣ Каждый SPI_IOC_MESSAGE — это полный userspace → kernel → driver → controller цикл
При вызове:
ioctl(fd, SPI_IOC_MESSAGE(1), &tr);
происходит:
переключение контекста userspace → kernel
копирование структуры spi_ioc_transfer
копирование tx/rx буфера
захват mutex'ов SPI master
настройка контроллера
запуск передачи
ожидание завершения (обычно sleep + IRQ)
возврат обратно в userspace
Это десятки микросекунд только на инфраструктуру, даже если передаётся 1 байт.
2️⃣ SPI master на TI AM335x не оптимизирован под микро-транзакции
На платформах типа:
Texas Instruments
процессор AM335x
SPI контроллер:
не рассчитан на ультракороткие транзакции
имеет программируемый старт/стоп на каждый transfer
часто работает через PIO, не DMA, для коротких пакетов
3️⃣ SPI — синхронный API
SPI_IOC_MESSAGE блокирует поток до окончания передачи.
В отличие от GPIO:
GPIO toggle — это просто запись в регистр
SPI — это ожидание физической передачи по шине
Поэтому:
GPIO ioctl ≈ 2–5 мкс
SPI ioctl ≈ 100–200 мкс
Это нормально.
Что можно сделать
✅ 1. Объединять байты
Вместо:
for (i=0; i<100; i++)
ioctl(... 1 byte ...)
делать:
uint8_t buf[100];
ioctl(fd, SPI_IOC_MESSAGE(1), &tr_for_100_bytes);
Latency будет ~150 мкс + время передачи,
а не 150 мкс × 100.
Это даёт радикальный выигрыш.
✅ 2. Использовать несколько transfer в одном ioctl
Можно:
struct spi_ioc_transfer tr[2];
ioctl(fd, SPI_IOC_MESSAGE(2), tr);
Это всё равно один системный вызов.
✅ 3. Перенести в kernel driver
Если нужно:
передавать 1 байт часто
гарантированная латентность < 20 мкс
real-time поведение
Тогда:
писать kernel driver
работать через spi_sync() напрямую
или даже через прямой доступ к контроллеру
Userspace spidev для микро-RT не подходит.
✅ 4. Проверить DMA
В device tree:
&spi0 {
status = "okay";
dmas = <...>;
};
Если SPI использует DMA — короткие пакеты всё равно могут идти через PIO.
На AM335x DMA обычно включается только при размере > FIFO.
✅ 5. Перейти на PRU (если TI AM335x)
У AM335x есть PRU:
deterministic timing
нет scheduler latency
можно реализовать собственный SPI
Если требуется <10 мкс детерминированность — это лучший вариант.
✅ 6. Проверить scheduler
Для эксперимента:
chrt -f 99 ./app
Или:
sched_setscheduler(0, SCHED_FIFO, ...)
Это может убрать 20–40 мкс джиттера,
но 150 мкс базовой стоимости не уберёт.
Почему GPIO быстрее
Через:
libgpiod
или /dev/gpiochip
происходит просто запись в регистр через драйвер GPIO.
Нет:
запуска контроллера
IRQ ожидания
clock gating
SPI FSM
Поэтому 2–3 мкс — нормально.
Важный момент
150 мкс — это не "время передачи байта".
Это "стоимость транзакции".
Если SPI clock 10 MHz:
1 байт передаётся за 0.8 мкс
остальное — софт.
Итог
На Embedded Linux 5.10 + TI ARM A8:
Метод Оценка
SPI_IOC_MESSAGE(1 byte) ~100–200 мкс
GPIO toggle ~2–5 мкс
Bulk SPI transfer эффективно
PRU лучший RT вариант
Kernel driver существенно быстрее userspace
Если опишете:
частоту SPI
режим (polling / DMA)
сколько вызовов в секунду нужно
нужны ли жёсткие RT гарантии
— могу предложить более точную архитектуру решения.
-
- Спасибо, а то некоторые отвечают как в анекдоте: Chip_n_Go_24(537 знак., Вчера, 17:20,
)
- Спасибо, а то некоторые отвечают как в анекдоте: Chip_n_Go_24(537 знак., Вчера, 17:20,