ВходНаше всё Теги codebook 无线电组件 Поиск Опросы Закон Пятница
20 февраля
1571568 Топик полностью
=AlexD= (Вчера, 17:12, просмотров: 47) ответил Chip_n_Go_24 на Высокая латентность вызова ioctl(hspi, SPI_IOC_MESSAGE(1), tr) - передача даже одного байта стоит ~150 мкс на вызов, когда смена состояния GPIO через тот-же ioctl занимает ~2.5 мкс. Система Embedded Linux core 5.10 на TI Arm A8.
Болван отвечает 

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 гарантии

— могу предложить более точную архитектуру решения.

Это моё личное предвзятое мнение. Любое совпадение с реальностью является случайным и непреднамеренным.