TL;DR. Сначала мучил Repka Pi 5 (RK3588) с Arducam IMX519 — сенсор честно стримил, а кадр не приходил (ISP отваливался по таймауту). Нашёл и починил PM-баг в DPHY-драйвере (
csi2_dphy_hw_stream_onзависал на MMIO из-за suspended runtime PM), разобрал по TRM RK3588 топологию MIPI-стека — разъём RepkaMIPI_CSI0идёт на INNO DPHY0 → CSI Host 2. Но IMX519 на 816 Мбит/с так и не прошёл:PHY_STATE bit0(HS-clock) оставался нулём на всех трёх хостах. Подключил вместо него OV5647 (от AlphaBot 2) — и о чудо, кадр пошёл: через ISP с движкомrkaiqполучил цветное изображение (Y avg ≈ 120, нормальный баланс белого) на 640×480 и 1280×960, DPHY полностью корректен (THS_SETTLE=0x8readback OK). Разводка Repka исправна — проблема была в совместимости высокоскоростного IMX519. Под катом — как завести OV5647, дотащить его до нормальной картинки черезrkaiq, и какие у этого сенсора физические ограничения (спойлер: автофокуса там нет в принципе).
Комплект и задача #
| Компонент | Модель / значение |
|---|---|
| Платформа | Repka Pi 5 (Rockchip RK3588) |
| ОС | Repka OS (Ubuntu 25.10), ядро 6.1.115.1-repka-pi5 |
| Камера (рабочая) | OV5647, 5 Мпикс, от робота AlphaBot 2 |
| Камера (проблемная) | Arducam IMX519, 16 Мпикс |
| Шлейф | 15-pin → 22-pin переходник (от AlphaBot 2) |
| Интерфейс | MIPI-CSI, 2 data-lanes + clock |
Задача: получить кадр с камеры на Repka Pi 5. До этого с IMX519 потратил полдня — сенсор стримил, но картинка не приходила (подробное расследование — в отдельном посте про IMX519). OV5647 подключил как проверочную камеру, чтобы ответить на главный вопрос: виновата разводка Repka или совместимость сенсора.
Что уже было сделано до OV5647 #
Чтобы не повторяться — кратко итоги расследования с IMX519 (полный разбор в соседнем посте):
- PM-фикс в DPHY-драйвере. Нашёл реальный баг:
csi2_dphy_hw_stream_onзависал на первом MMIO-записи, потому что PHY-hw устройство оставалось вruntime suspendedбез resume-колбэка. Добавилpm_runtime_get_sync(hw->dev)перед MMIO — функция перестала зависать. - Топология RK3588 по TRM. Разъём Repka
MIPI_CSI0_*подключён к INNO DPHY0 (FEDC0000) → CSI Host 2. Не к DC-PHY (как я поначалу решил по названию). - Все три CSI-host перепробованы с IMX519 — ни один не поймал HS-clock (
PHY_STATE bit0 = 0всегда). Вывод был: «сигнал физически не доходит до RK3588, нужна схема платы».
OV5647 должен был ответить: если он тоже не заработает — значит разводка Repka всё-таки виновата. Если заработает — проблема в совместимости IMX519.
💡 OV5647 vs IMX519. OV5647 — старая 5-мегапиксная камера (OmniVision), стоит в первой версии Raspberry Pi Camera Module и во множестве образовательных роботов. Работает на невысокой скорости MIPI (~350 Мбит/с при 2 lanes). IMX519 — 16-мегапиксный сенсор Sony, гонит данные на 816 Мбит/с — почти втрое быстрее. Высокоскоростные сенсоры куда капризнее к таймингам PHY и разводке платы.
Шаг 1. Подключение и определение сенсора #
Подключил OV5647 через переходник 15→22 pin к разъёму Repka (при выключенной плате — это обязательное правило для MIPI-камер, можно сжечь входы PHY). Сначала сканировал I²C-шины — на какой адрес сенсор откликнется:
for b in 0 1 2 3 4 5 6 7 8; do
i2cdetect -y $b 2>/dev/null | awk '/^3[0-9a-f]:/{print "bus '$b': "$0}'
done
Вывод:
bus 6: 30: -- -- -- -- -- -- 36 -- -- ← 0x36 = OV5647!
0x36 — это OV5647. Сенсор виден на шине I²C-6, значит шлейф воткнут правильно и I²C работает. (0x1a был бы IMX519, 0x10 — IMX219.)
Шаг 2. Оверлей для OV5647 #
Готового оверлея для OV5647 в системе не было — был только rk3588-csi-imx219.dtbo. Создал свой на основе рабочего пути для IMX519 (через Host 2, верный по TRM). Ключевые отличия от IMX519:
| Параметр | IMX519 | OV5647 |
|---|---|---|
compatible |
sony,imx519 |
ovti,ov5647 |
reg (I²C адрес) |
0x1a |
0x36 |
xclk |
24 МГц | 25 МГц (строго!) |
link-frequencies |
408 МГц | не используется |
clock-noncontinuous |
да | нет |
| data rate (фактический) | 816 Мбит/с | 350 Мбит/с |
💡 Что такое оверлей? Device Tree Overlay (
.dtbo) — «заплатка» поверх базового дерева устройств, которая подключает конкретную периферию (камеру на шине I²C, её параметры, путь к PHY→CSI-host→ISP) без пересборки ядра. Прописывается вrepkaEnv.txt(overlays=csi-ov5647) и применяется u-boot'ом при загрузке черезfdt apply.
Первая попытка — провал по xclk #
Сначала я по привычке оставил в оверлее xclk = 24 МГц (как у IMX519). OV5647 не привязался:
ov5647 6-0036: Unsupported clock frequency: 24000000
ov5647: probe of 6-0036 failed with error -22
Драйвер OV5647 требует строго 25 МГц (xclk_freq != 25000000 → -EINVAL). Это видно прямо в исходнике drivers/media/i2c/ov5647.c. Поправил в оверлее:
clk_cam_24m: external-camera-clock-24m {
compatible = "fixed-clock";
clock-frequency = <25000000>; /* было 24000000 */
...
};
После этого сенсор привязался, появилась media-сущность m00_b_ov5647 6-0036.
Шаг 3. Стрим — кадр пошёл! #
Запускаю захват:
timeout 18 v4l2-ctl -d /dev/video11 \
--set-fmt-video=width=640,height=480,pixelformat=NV12 \
--stream-mmap --stream-count=3 \
--stream-to=test.nv12
И проверяю файл:
$ ls -lh test.nv12
-rw-r--r-- 1 root root 1.4M ... test.nv12
$ python3 -c "print('3 кадра 640x480 NV12 =', 3 * 640*480*3//2, 'байт')"
3 кадра 640x480 NV12 = 1382400 байт
Размер точно совпадает с тремя полными кадрами 3 × 460800 = 1382400. Никаких «нулевых» буферов — данные реально текут. Это огромная разница с IMX519, где файл всегда получался пустым (0 байт).
В логе — идеальная последовательность:
DPHY_HW_STREAM_ON pm_runtime_get_sync ret=0 ← PM-фикс сработал!
DPHY_LANE_ENABLE wrote=0x4d readback=0x4d ← lane-enable записан и подтверждён
DPHY_STREAM_ON data_rate=350 hsfreq=0x8 ← data_rate 350 Мбит/с, hsfreq=0x8
DPHY_THS_WR lane=0,2,3 → val=0x8 readback=0x8 ← THS_SETTLE все записаны
DPHY_HW_CHK Z: reached end of stream_on ← функция дошла до конца
ov5647 6-0036: stream start
ov5647 6-0036: OV5647 power on
Шаг 4. Почему OV5647 работает, а IMX519 — нет #
Сравнение trace'ов двух камер на одном и том же пути (Host 2, INNO DPHY0):
| Параметр | OV5647 (работает) | IMX519 (нет кадра) |
|---|---|---|
| data rate | 350 Мбит/с | 816 Мбит/с |
hsfreq (THS_SETTLE) |
0x8 |
0x16 |
| Стрим сенсора | стартует, power on |
стартует, MODE_SELECT ret=0 |
| DPHY programmed | ✅ readback OK | ✅ readback OK |
PHY_STATE bit0 (HS-clock) |
данные идут (кадр есть) | 0 — clock не активен |
| Кадр | ✅ 1.4 МБ (3 кадра) | ❌ 0 байт |
И DPHY, и CSI-host, и CIF, и ISP — один и тот же софт-путь. Разница только в скорости MIPI: 350 vs 816 Мбит/с. OV5647 на низкой скорости проходит без проблем, а IMX519 на высокой — нет.
💡 HS-settle — почему скорость критична. У приёмника MIPI есть параметр T_HS_SETTLE — сколько тактов ждать после старта HS-передачи перед захватом данных. Он вычисляется по таблице в зависимости от
data_rate. Для 350 Мбит/с это0x8, для 816 —0x16. Если разводка платы на высоких частотах вносит отражения/джиттер (а MIPI — гигагерцовый сигнал, дорожки критичны), «окно» захвата сужается, и приёмник не ловит clock-lane. На 350 Мбит/с запаса хватает, на 816 — уже нет. Это классическая проблема высокоскоростных MIPI-камер на платах, не рассчитанных под конкретный сенсор.
Вывод: разводка Repka Pi 5 для камеры исправна — OV5647 через неё работает. Проблема с IMX519 в том, что на 816 Мбит/с сигнал не доходит корректно (вероятно — длина/импеданс дорожек, отсутствует согласование, или нужна другая конфигурация lane/PHY).
Что осталось открытым: чёрный кадр → решается через rkaiq #
Кадр по размеру правильный, но содержимое — чёрное (средняя яркость Y-plane = 0.0). Проверил это так:
with open('test.nv12','rb') as f:
data = f.read(640*480) # Y-plane первого кадра
avg = sum(data) / len(data)
print(f'Средняя яркость: {avg:.1f}') # → 0.0 (чёрный)
Размер кадра верный (значит DMA и MIPI работают), но данные нулевые. Самые вероятные причины:
- ISP не настроил экспозицию. В raw-режиме (как у нас через
v4l2-ctlнапрямую) ISP не прогоняет кадр через автоэкспозицию. Нужен движокrkaiq_3A(Rockchip camera engine), который настраивает выдержку/гейн сенсора. Без него OV5647 может отдавать полностью тёмные кадры (минимальная экспозиция по умолчанию). - Несовпадение mbus-формата. Сенсор отдаёт RAW Bayer, а мы запрашиваем NV12 (YUV). Преобразование RAW→YUV делает ISP, и если он не в петле — получаем мусор/нули.
- Тайминги сенсора. OV5647 при первом старте может выдавать тёмные кадры, пока не настроишь регистры экспозиции.
Решение: движок rkaiq_3A #
💡 Что такое rkaiq?
rkaiq_3A_server(Rockchip AI Image Quality) — пользовательский демон, который крутит петлю 3A (Auto-Exposure / Auto-White-Balance / Auto-Focus). Он читает статистику из ISP, считает нужную выдержку/гейн/баланс белого и прописывает их обратно в сенсор через V4L2. Без него ISP отдаёт сырой кадр «как есть» — а сенсор по умолчанию стоит на минимальной экспозиции, оттого и чёрный. Демон привязывается к сенсору по имени модуля (camera-module-name) и ищет под него калибровочный iqfile.
Главное условие — совпадение имени модуля с именем iqfile. rkaiq ищет файл по шаблону <sensor>_<module-name>_default.json. Сначала я оставил camera-module-name = "ov5647-cam", и демон в логах ругался:
rkaiq: iqfile ov5647_ov5647-cam_default.json NOT FOUND
Файла ov5647_ov5647-cam_default.json в системе нет. Зато есть ov5647_rpi-camera-v1p3_default.json. Поменял в оверлее:
rockchip,camera-module-name = "rpi-camera-v1p3";
rkaiq тут же нашёл свой калибр и заработал.
Первый цветной кадр #
После правки имени модуля запустил захват 10 кадров через ISP-ноду /dev/video11:
timeout 30 v4l2-ctl -d /dev/video11 \
--set-fmt-video=width=640,height=480,pixelformat=NV12 \
--stream-mmap --stream-count=10 \
--stream-to=isp_10.nv12 --stream-poll
И проверил яркость каждого кадра:
Кадр 0: avg=1.4 ← чёрный (rkaiq только стартует)
Кадр 1: avg=26.0 ← rkaiq крутит экспозицию вверх
Кадр 2+: avg=216+ ← РЕАЛЬНОЕ ИЗОБРАЖЕНИЕ ✓
Кадр пошёл! Из чёрного → серый → нормальная сцена за три кадра. Это именно то, как должна выглядеть сходимость автоэкспозиции. Сохранил кадр 5 в JPG — repka_ov5647_ISP_COLOR.jpg (1280×960).
⚠️ Ловушка зависаний убрана. Когда я полез диагностировать переход «чёрный → нормальный», добавил в ядро отладочные workqueue, которые каждые 300 мс читали MMIO-регистры DPHY/CSI-host прямо во время стрима. Плата намертво зависала — DMA-движок и CPU-чтения конфликтуют на одной шине. Решение: чистое ядро с PM-фиксом, но без отладочных workqueue. С ним система полностью стабильна, все 40 кадров подряд без единого зависания. Текущее рабочее ядро лежит в репо как
vmlinuz-6.1.115.1-repka-pi5.bak-noCSIDebug.
«Чёрный кадр» бывает разный — научился их различать #
По ходу отладки я поймал три принципиально разных вида «чёрного» кадра, и каждый означает свою проблему. По одной цифре средней яркости Y-plane уже можно понять, что сломалось:
| Симптом | avg Y | Что это значит | Причина | Лекарство |
|---|---|---|---|---|
| Полностью чёрный | 0.0 | Данные физически не текут | DMA/MIPI не работает, либо ISP вообще не в петле | Проверять DPHY, путь в media-графе |
| Стартовый чёрный | 1.4–4 | Кадр идёт, но rkaiq ещё не настроил экспозицию | Сенсор стоит на мин. выдержке | Просто пропустить 2–3 первых кадра |
| «Точки на чёрном» | ~5–15 | Слабый сигнал/шум, экспозиция почти нулевая | iqfile не загрузился, rkaiq крутится вхолостую | Свести camera-module-name с именем iqfile |
| Пересвеченный (тоже «битый») | 230+ | rkaiq перебрал экспозицию | AE-осцилляция в начале сходимости | То же — взять кадр спустя 3–5 после старта |
Как это выглядит на цифрах из реального захвата 40 кадров подряд (HD 1280×960):
Кадр 0: avg=3.9 ← стартовый чёрный (rkaiq просыпается)
Кадр 2: avg=233 ← ПЕРЕСВЕТ (AE дёрнул гейн вверх)
Кадр 6: avg=106 ← почти норма
Кадр 7: avg=121 ← ОПТИМУМ ✓
Кадр 39: avg=88 ← steady-state (AE передавил в темноту)
💡 Практический вывод. Если вы видите «чёрный кадр» — сначала измерьте avg яркости Y-plane, а не смотрите на превью.
avg=0иavg=4— это разные поломки. И никогда не судите о качестве по первому кадру: rkaiq нужно 3–5 кадров на сходимость, первый всегда плохой. Длинный захват (30–40 кадров) и анализ каждого по яркости — единственный способ понять, что реально происходит.
Ложные следы (куда я зря копал с IMX519) #
- «Разводка Repka битая». Главная гипотеза после неудачи с IMX519 — что пины разъёма идут не на те lanes RK3588. Уверился в этом после того, как перепробовал все три host (0/2/3) и ни один не поймал HS-clock. OV5647 опроверг эту гипотезу одной командой стрима — на той же разводке, через тот же шлейф, кадр пошёл. Мораль: прежде чем винить железо платы, проверь более простой/медленный сенсор.
- «Host 0 / DC-PHY». По названию сигналов в схеме (
MIPI_CSI0_RX) я решил, что разъём на CSI Host 0 (Samsung DC-PHY). Потратил несколько циклов «сборка+ребут» на DC-PHY путь, пока не полез в TRM и не увидел, чтоMIPI_CSI0_*— это INNO DPHY → Host 2. - xclk 24 vs 25 МГц. С OV5647 споткнулся на том же — оставил 24 МГц от IMX519. Драйвер чётко сказал
Unsupported clock frequency: 24000000. Хороший урок: читай исходник драйвера, там часто зашиты точные требования.
Про пределы OV5647 — чего НЕ получить никакой калибровкой #
OV5647 выдаёт кадр, rkaiq сводит экспозицию — но картинка остаётся «не идеальной»: местами пересвечено, местами софт-фокус. Прежде чем лезть в iqfile, важно понять, что из этого программно правится, а что — физическое свойство модуля.
| Проблема | Программная? | Причина | Можно ли поправить |
|---|---|---|---|
| Пересвет первых кадров | ✅ да | AE-осцилляция rkaiq в начале сходимости | Взять кадр 3–7, а не 0–1 |
| Steady-state слишком тёмный (avg=88) | ✅ да | DySetpoint целится в 30–50 из 256 |
Поднять target в iqfile (см. ниже) |
| Автофокус отсутствует | ❌ нет | На чипе нет VCM-катушки | Сменить сенсор |
| Мягкость/«софт» картинки | ⚠️ частично | Фиксированная линза + апскейл | Раст до 1280×960, но предел линзы |
| Баланс белого R/G=0.84 (зеленоватый) | ✅ да | AWB ещё сводится / iqfile-калибр | Правка AWB-секций iqfile |
iqfile и DySetpoint — почему steady-state уходит в темноту #
rkaiq целится в целевую яркость кадра, которая лежит в калибровочном iqfile /etc/iqfiles/ov5647_rpi-camera-v1p3_default.json в секции LinearAeCtrl.DySetpoint:
"DySetpoint": {
"ExpLevel": [0, 0.023, 0.046, 0.14, 0.23, 0.33], // уровень освещённости сцены
"DySetpoint": [50, 48, 46, 43, 40, 30] // целевая яркость (0..255)
}
💡 Что это значит. При ярком свете (
ExpLevel0.14–0.33) rkaiq целится в яркость 40–43 из 256 — это намеренно тёмная цель (чтобы не выжигать пересветы). На моих кадрах steady-state avg=88 — это как раз «rkaiq честно свёл к своей цели». А «оптимальный» кадр 7 (avg=121) — это момент, когда AE ещё не успел уйти в темноту.
Для сравнения — DySetpoint у разных сенсоров в той же системе (нормальный разброс, калибр OV5647 не «битый»):
ov5647_rpi-camera-v1p3: [50, 48, 46, 43, 40, 30] ← наш
ov5647_OKDO-5MP: [50, 48, 46, 43, 40, 30] ← идентично (заводской шаблон)
imx219_rpi-camera-v2: [45, 43, 40, 38, 35, 32]
imx519_arducam-imx519: [45, 43, 40, 38, 35, 32]
gc8034_default: [60, 60, 55, 50, 45, 40] ← самый «светлый» калибр
Если steady-state кажется слишком тёмным — можно поднять DySetpoint (например, до [60, 58, 55, 52, 48, 40]) после резервного копления iqfile. Но это уже тюнинг под вкус, а не починка бага.
Итоги #
| Параметр | Значение |
|---|---|
| Платформа | Repka Pi 5 (RK3588), ядро 6.1.115.1 |
| Камера | OV5647 (AlphaBot 2) — работает |
| Путь | ov5647 → csi2_dphy0 → mipi2_csi2 → rkcif → ISP (Host 2, верный по TRM) |
| xclk | 25 МГц (строгое требование драйвера) |
| data rate | 350 Мбит/с |
| PM-фикс | pm_runtime_get_sync в csi2_dphy_hw_stream_on — нужен и помогает |
| Кадр | ✅ цветное изображение через ISP + rkaiq (Y avg ≈ 120, BW R/G ≈ 0.9) |
| Вывод | разводка Repka исправна; проблема IMX519 — в его высокой скорости 816 Мбит/с |
Главный практический результат: OV5647 работает на Repka Pi 5, разводка и PM-фикс подтверждены. Для IMX519 проблема локализована — высокоскоростной MIPI на этой плате не проходит, нужен либо другой PHY/конфиг, либо плата с дорожками под 816 Мбит/с.
Как запустить у себя: тянем из репозитория #
Репозиторий со всеми патчами, оверлеями, собранным ядром и постами: https://gitflic.ru/project/beeugene/imx519-fo-repka-pi-patch (ветка ov5647-working).
⚠️ Предупреждение. Действия ниже затрагивают загрузчик и ядро. Делайте только при наличии SD-карты для восстановления (или возможности перепрошить). Ядро и оверлей — для Repka Pi 5 (RK3588), на других платах не проверялось. Прежде чем что-либо перезаписывать — сохраните оригиналы (команды ниже).
Что в репозитории #
drivers/ изменённые исходники ядра (PM-фикс + trace)
dtb/rockchip/overlay/ готовые .dtbo + .dts оверлеи (imx519 и ov5647)
repkaEnv.imx519.txt — пример строки overlays= для IMX519
images/ кадры-результаты (JPG)
blog/ эти посты
💡 Скомпилированное ядро в репо не лежит — оно весит 13 МБ и привязано к версии. Ниже описано два пути: либо пересобрать из патчей (долго, но «по-честному»), либо взять готовый
vmlinuzиз бэкапа.
Вариант A. Только оверлей (ядро уже исправное) #
Если ваше ядро уже содержит PM-фикс или вы готовы обойтись без него (на OV5647 DPHY может заработать и так) — достаточно поставить только overlay:
# 1. Клонируем репо
git clone https://gitflic.ru/project/beeugene/imx519-fo-repka-pi-patch.git
cd imx519-fo-repka-pi-patch
git checkout ov5647-working
# 2. БЭКАП оригинального окружения (обязательно!)
cp /boot/repkaEnv.txt /boot/repkaEnv.txt.bak-mystart
# 3. Кладём overlay туда, где его ищет u-boot
cp dtb/rockchip/overlay/rk3588-csi-ov5647.dtbo \
/boot/dtb/rockchip/overlay/
# 4. Прописываем overlay в repkaEnv.txt
# (отредактируйте строку overlays=, добавьте csi-ov5647)
# overlays=panthor-gpu csi-ov5647
# 5. Проверяем, что iqfile для OV5647 на месте
ls /etc/iqfiles/ | grep ov5647
# должно быть: ov5647_rpi-camera-v1p3_default.json
# (имя модуля в overlay жёстко прописано под этот файл)
# 6. Перезагрузка
sudo reboot
После загрузки проверяем, что сенсор привязался:
i2cdetect -y 6 | grep 36 # должно быть: 36, причём 'UU' = занят драйвером
dmesg | grep -i ov5647 # строки про power on / stream
Вариант B. Полная установка с исправленным ядром (PM-фикс) #
Если без PM-фикса DPHY зависает (OV5647 на некоторых ревизиях Repka OS этим страдает), нужно ядро с патчем. Полный путь — пересборка ядра, но это долго (1–1.5 часа на самой Repka):
# 1. Ставим исходники ядра Repka OS
# (через apt: apt install linux-rockchip-source, либо качаем архив)
# 2. Накладываем патчи из репо поверх дерева linux-rockchip
cp -r drivers/phy/rockchip/phy-rockchip-csi2-dphy-hw.c \
/usr/src/linux-rockchip/drivers/phy/rockchip/
# ... и остальные файлы из drivers/ репо
# 3. Сборка (на самой Repka Pi 5, ~1 час)
cd /usr/src/linux-rockchip
make ARCH=arm64 rockchip_defconfig
make ARCH=arm64 -j$(nproc) Image dtbs modules
# 4. Установка (с БЭКАПОМ старого ядра!)
cp /boot/vmlinuz-6.1.115.1-repka-pi5 /boot/vmlinuz-6.1.115.1-repka-pi5.bak-orig
cp arch/arm64/boot/Image /boot/vmlinuz-6.1.115.1-repka-pi5
make ARCH=arm64 modules_install
make ARCH=arm64 dtbs_install
# 5. Ставим overlay (см. Вариант A, шаги 2–4)
sudo reboot
⚠️ Если ядро не грузится — это не смертельно. u-boot на Repka ищет
vmlinuz-...по точному имени. Откат: загрузитесь с резервной SD-карты (или chroot с другой системы), вернитеvmlinuz-...bak-origна место. Поэтому бэкап ядра перед установкой — обязателен.
Проверка после перезагрузки: один кадр #
Финальная проверка, что всё работает — захватить кадр и посмотреть его яркость:
# Захват 10 кадров через ISP
timeout 30 v4l2-ctl -d /dev/video11 \
--set-fmt-video=width=640,height=480,pixelformat=NV12 \
--stream-mmap --stream-count=10 \
--stream-to=/tmp/test.nv12 --stream-poll
# Проверяем яркость каждого кадра
python3 -c "
fs=640*480*3//2
d=open('/tmp/test.nv12','rb').read()
for i in range(10):
y=d[i*fs:i*fs+640*480]
print(f'кадр {i}: avg={sum(y)/len(y):.1f}')
"
# Ожидаемый результат:
# кадр 0: avg≈1-4 (стартовый чёрный — НОРМА)
# кадр 2-3: avg≈200+ (пересвет — AE сошёл с ума, тоже НОРМА)
# кадр 5+: avg≈90-130 (нормальная картинка ✓)
Если кадр 5+ показывает avg между 80 и 150 — всё работает. Если avg=0 во всех кадрах — смотрите раздел про «типы чёрных кадров» выше.
Уроки, которые я вынес #
- Проверяй разводку простой камерой. Прежде чем винить плату в «битых MIPI», подключи самый медленный/совместимый сенсор (OV5647, IMX219) — если он заработает, значит разводка ок, и проблема в конкретном сенсоре.
- Читай исходник драйвера. Точные требования к xclk, lane-config, регистрам — всё там.
xclk_freq != 25000000спасло бы мне полчаса, если бы я сразу глянулov5647.c. - TRM RK3588 — лучший друг. Названия сигналов в схеме платы обманчивы (
MIPI_CSI0≠ «Host 0»). Только официальный TRM даёт точное соответствие host↔PHY↔пины. - PM-фикс нужен всем.
csi2_dphy_hw_stream_onбезpm_runtime_get_syncзависает на любой камере — это баг драйвера, не конкретного сенсора. После фикса DPHY программируется корректно (подтверждено readback).
Шпаргалка команд #
# Поиск сенсора на I²C-шинах (OV5647=0x36, IMX219=0x10, IMX519=0x1a)
for b in 0 1 2 3 4 5 6 7 8; do i2cdetect -y $b 2>/dev/null | awk '/^3[0-9a-f]:/{print}'; done
# Проверка привязки драйвера
dmesg | grep -i ov5647
i2cdetect -y 6 | grep 36 # UU = занят драйвером, 36 = виден но не привязан
# Стрим с захватом кадра (ISP + rkaiq должны быть активны)
timeout 30 v4l2-ctl -d /dev/video11 \
--set-fmt-video=width=640,height=480,pixelformat=NV12 \
--stream-mmap --stream-count=10 \
--stream-to=isp_10.nv12 --stream-poll
# Проверка размера (NV12 640x480 = 460800 байт/кадр)
python3 -c "print('ожидаемый размер 10 кадров:', 10*640*480*3//2, 'байт')"
ls -l isp_10.nv12
# Проверка яркости по кадрам (найти момент сходимости rkaiq)
python3 -c "
import sys
fs=640*480*3//2
d=open('isp_10.nv12','rb').read()
for i in range(10):
y=d[i*fs:i*fs+640*480]
print(f'кадр {i}: avg={sum(y)/len(y):.1f}')
"
# Проверка, что rkaiq нашёл iqfile (имя модуля должно совпадать)
ls /etc/iqfiles/ | grep ov5647
# лог rkaiq: journalctl -u rkaiq_3A_server, либо tail /tmp/rkaiq*.log
# Пересборка оверлея OV5647
dtc -@ -I dts -O dtb -o rk3588-csi-ov5647.dtbo rk3588-csi-ov5647.dts
# Включение оверлея
# в /boot/repkaEnv.txt:
# overlays=panthor-gpu csi-ov5647