STM32F4Discovery – подключаем камеру по интерфейсу DCMI

  • Tutorial

STM32F4Discovery – подключаем камеру по интерфейсу DCMI



Когда-то, подключая камеру от мобильного телефона к микроконтроллеру STM32F407VGT6 (который имеет место быть на плате STM32F4Discovery), я даже не думал о том, что данный контроллер имеет специальный аппаратный интерфейс для данного дела. Может быть, невнимательно читал даташит, но я всегда считал, что интерфейс DCMI имеется только у чипов в корпусах UFBGA176 и LQFP от 144 ног. Однако, не так давно, открыл для себя озвученную деталь: 100-ногий STM32F407 также имеет DCMI на борту.
Являясь большим любителем изучения и совместного запуска различного мобильного железа (в частности, LCD и камер) с МК, мимо такого открытия я просто так пройти не смог, и решил восполнить данный пробел в изучении периферии STM32. Собственно, данный материал и посвящен описанию осуществления возникшей затеи.

Совсем немножко теории.


Прежде всего, нужно представлять, о чем идет речь – а точнее, что такое CMOS-камера, и с чем ее едят.
Данный вид камер осуществляет вывод информации с сенсора в цифровом виде: RGB, YCbCr, а также в сжатом виде – JPEG. У различных камер имеются свои нюансы в плане возможностей, я буду рассматривать вполне конкретный случай камеры с небольшим разрешением (VGA, 640x480), вытащенной мною в незапамятные времена из телефона «Siemens C72» (сенсор PixelPlus PO2030N). Данная камера является наиболее подходящей для изучения в виду простоты функционирования и принадлежности к типу более-менее распространенному. Давным-давно я вытравил для нее небольшую плату (для большего удобства подключения) – со стабилизатором на 2.8 В и подтягивающими резисторами на шине I2C. Вот она (шлейф и разъем камеры скрыты под кожухом).

image

Кроме нюансов в области формата данных, камеры также могут отличаться в области количества выводов синхронизации. У большинства (по моему мнению) сенсоров в наличии имеются специальные выводы строчной и кадровой синхронизации; но есть камеры, имеющие только лишь вывод строба пикселя, а о начале новой строки/кадра они дают знать с помощью специальных передаваемых кодов (к примеру, 0x00 или 0xFF). Камера, что есть у меня в наличии, имеет выводы внешней синхронизации.
Можно прикинуть примерное схематическое изображение камеры в виде блока.



По большей части CMOS-камеры управляются по интерфейсу I2C (хотя я встречал устройства, управляющиеся и по UART). По I2C производится настройка различных параметров, таких как: разрешение, цветовая гамма, формат данных на выходе, и т.д.
Вывод EXTCLK – тактирование камеры, которым нужно обеспечить ее извне. DCLK – строб-сигнал, по переднему или заднему фронту которого на шине данных камеры фиксируются данные (к примеру, байт данных одного пикселя матрицы, либо байт данных «полупикселя», если камера работает в режиме RGB565). HSYNC – сигнал горизонтальной синхронизации, свидетельствующий о начале новой строки, а VSYNC – сигнал синхронизации, активный уровень которого указывает на начало нового кадра. Выводы D0..D7 – шина данных; как правило, у подобных камер она восьмиразрядная.
Теперь подробнее о сигналах синхронизации.



На графиках видно, что камера настроена на активность сигнала DCLK только в активную фазу HSYNC (а именно эта фаза нас и интересует, тактовый сигнал в период «перевода строки» нам не интересен). Если камера настроена на разрешение 320x240, то в период каждого импульса HSYNC укладывается 320 импульсов DCLK, а в период VSYNC – 240 HSYNC.
При увеличении масштаба, видим, что творится на шине данных.



По переднему фронту (в данном случае) с шины данных снимается байт, который можно отправлять сразу на дисплей для отображения, либо «складывать» в буфер для последующей обработки.

В теории все более-менее понятно, теперь об интерфейсе DCMI микроконтроллера STM32.


Интерфейс DCMI способен работать с шиной данных шириной до 14 разрядов, поддерживает как аппаратную, так и программную синхронизацию, а также форматы данных: YCbCr, RGB и JPEG.
Кроме того, DCMI содержит буфер FIFO, имеет возможность настройки прерываний (в том числе и по заполнению регистра данных) и настройки работы через DMA.



Прерывания от DCMI могут вызываться при наступлении следующих условий: окончание линии, окончание кадра, переполнение приемного буфера, обнаружение ошибки синхронизации (при внутренней синхронизации).
В некоторое недоумение меня ввело отсутствие специального вывода тактирования камеры. Я не знаю, по каким причинам разработчики из SGS Microelectronics от него отказались, но по мне, было бы весьма удобно иметь, к примеру, настраиваемый источник тактовой частоты.
Лично я задействовал таймер-счетчик общего назначения, включенный в режиме ШИМ на генерацию меандра частотой 4 МГц. Большого FPS, конечно, с такой тактовой не получить, но сразу оговорюсь – дисплей, который я использую, подключен не к FSMC, поэтому самая длительная функция во всей цепи – функция вывода на LCD, следовательно, при бОльшей частоте происходит срыв вывода изображения на экран. Посему перед выгрузкой я глушу таймер, а после нее – включаю таймер снова.
Аппаратный модуль DCMI содержит, кроме регистра данных, десять регистров управления/статуса. Это: регистр управления (DCMI_CR), регистр состояния (DCMI_SR), регистр состояния прерываний (DCMI_RIS), регистр разрешения прерываний (DCMI_IER), регистр маски прерываний (DCMI_MIS), регистр сброса флагов прерываний (DCMI_ICR), регистр кодов внутренней синхронизации (DCMI_ESCR), регистр сброса маски кодов внутренней синхронизации (DCMI_ESUR), регистр стартовых значений при захвате части кадра (DCMI_CWSTRT) и регистр величины фрагмента кадра в режиме CropWindow (DCMI_CWSIZE). И, само собой, регистр данных – DCMI_DR.
В данном случае регистры, относящиеся к захвату части кадра и внутренней синхронизации нас не интересуют. Прерывания я тоже решил пока оставить в покое, поэтому рассмотреть подробнее стоит только регистр управления DCMI_CR и регистр состояния DCMI_SR.

Регистр управления дает нам возможность полностью настроить формат взаимодействия с камерой: размер шины данных, активные уровни линий HSYNC и VSYNC, и т.д.



По порядку. Бит ENABLE – само собой разумеется, включение интерфейса в работу. Поле EDM (extended data mode) – размер шины данных; шина у моей камеры восьмиразрядная, так что это поле следует установить в значение «00». Поле FCRC (frame capture rate control) дает возможность немного регулировать FPC: 00 – захватываются все приходящие кадры, 01 – каждый второй кадр, 10 – каждый четвертый. Биты VSPOL и HSPOL – активные уровни линий кадровой и строчной синхронизации. Активные уровни игнорируются, и данные в периоды активности не захватываются, это следует учитывать. PCKPOL – бит активного уровня строба пикселя – по какому фронту сигнала считывать данные с шины: переднему, или заднему. ESS – бит выбора способа синхронизации: внешняя, либо внутренняя. JPEG – выбор формата приходящих данных – сжатый, или нет. CROP – бит выбора захвата фрагмента кадра (crop window). Если данный бит установить в единицу, то интерфейс будет захватывать данные в окне, определяемом значениями в регистрах DCMI_CWSTRT и DCMI_CWSIZE.

Итак, настраиваем.

Так как я привык использовать стандартную библиотеку периферии от ST (хотя в первых итерациях работы с новой периферией никогда ее не использую, пока не поковыряюсь в регистрах «ручками»), настройку привожу именно с использованием библиотеки.

void DCMIInitialRoutine(void) {
    DCMI_InitTypeDef        DCMI_CamType;
    DCMI_DeInit();
    DCMI_CamType.DCMI_CaptureMode = DCMI_CaptureMode_Continuous;
    DCMI_CamType.DCMI_CaptureRate = DCMI_CaptureRate_All_Frame;
    DCMI_CamType.DCMI_ExtendedDataMode = DCMI_ExtendedDataMode_8b;
    DCMI_CamType.DCMI_SynchroMode = DCMI_SynchroMode_Hardware;
    DCMI_Init(&DCMI_CamType);
    DCMI_CaptureCmd(ENABLE);
    DCMI_Cmd(ENABLE);
    return;
}


Собственно, для моих нужд можно было не трогать ни одного бита в регистре DCMI_CR – по умолчанию они сброшены – кроме битов CAPTURE и ENABLE.
Интерфейс сконфигурирован и готов к работе. После подачи тактового сигнала камере, интерфейс начнет принимать данные, которые нам необходимо обрабатывать.
Задачу для начала я поставил перед собой максимально простую – выводить изображение на дисплей, так что и обработка данных будет минимальной.
В своевременном считывании данных из приемного буфера нам поможет статусный регистр DCMI_SR.



Для чтения доступно весьма скудное количество битов – всего три. Биты HSYNC и VSYNC сигнализируют о состоянии соответствующих линий: активная фаза, либо перевод строки; самым интересным является бит FNE. Он указывает нам на заполнение буфера данными. Или на не заполнение.
Проверяя в постоянном цикле состояние бита FNE в DCMI_SR, узнаем о приходе данных в приемный тридцатидвухразрядный буфер. В моем случае данные будут располагаться так:



При установке бита FNE в регистре состояния DCMI_SR в приемном буфере будут содержаться четыре байта, данные двух соседних пикселей: Byte0 и Byte1 – 16 разрядов пикселя n, а Byte2 и Byte3 – 16 разрядов пикселя n+1. Мне останется только их объединить и отправить для отображения на дисплей. Итак, вот каким образом выглядит основной цикл:
while (1) {
               while ((DCMI_GetFlagStatus(DCMI_FLAG_FNE)) == RESET); //Waiting for the buffer
                    TIM_Cmd(TIM3, DISABLE);                          //Disable CAM clock
                    cam_grab = (DCMI->DR);                           //Reading buffer
                    SendDataByte_LCD (cam_grab);
                    cam_grab = (DCMI->DR)>>16;                       //Reading 2nd part of the buffer
                    SendDataByte_LCD (cam_grab);
                    TIM_Cmd(TIM3, ENABLE);                           //Enable CAM clock again
    }

То есть, я жду установки бита FNE в регистре состояния DCMI_SR, а после – в два захода выгружаю по 16 бит данных на дисплей.
На этом моменте хотелось бы подойти к логическому завершению, но не тут-то было.
После прошивки и перезапуска МК на дисплее я увидел… нет, я увидел вполне себе знакомую собственную физию, но в черно-синих оттенках. Красный и зеленый цвета отсутствовали напрочь.
После недолгих разборок с дебагером было обнаружено следующее: регистр данных интерфейса содержал лишь 16 бит данных одного пикселя, причем младшие 8 бит располагались на месте Byte0 (см. рис. выше), а старшие – на месте Byte2. Пространства Byte1 и Byte3 же были пусты. До сих пор я не понял, откуда такое несоответствие документации действительности, и, возможно, обращусь в STM.
В итоге удалось получить изображение с камеры с помощью интерфейса DCMI, хотя и не без некоторых сложностей. На рисунке привожу фотографию дисплея, на который выводилось изображение демо-борды STM32F3Discovery с моей камеры.



А вот что увидим на выводах EXTCLK, PIXCK, HSYNC и VSYNC, если подключить логический анализатор.



Всё выглядит именно так, как и ожидалось: 240 импульсов HSYNC укладывается в длительность одного VSYNC, 320 PIXCK – в одном HSYNC. В активную фазу HSYNC камера не выдает сигналов PIXCK – именно так, как она была настроена.
Вообще говоря, интерфейс меня несколько разочаровал. Отсутствие «штатной» ноги тактирования камеры, отсутствие мало-мальски интересных встроенных фишек (а как насчет аппаратного кодера JPEG?), да еще и танцы с бубном вокруг располовиненного FIFO
Организуя работу с камерой на прерываниях PIXCK, HSYNC и VSYNC я не имел столько головняка, сколько поимел, работая с камерой с помощью аппаратного DCMI.
Тем не менее, в ближайшее время буду пробовать осуществлять захват кадра, сжатие оного в JPEG, и пробовать писать картинку на SD карту.
PS. На всякий случай даю ссылку на проект для «Code::Blocks» — вдруг пригодится кому.

www.dropbox.com/sh/nfjdwqsdzlr7djm/9v2mQM8uYV
Поделиться публикацией
Комментарии 22
    0
    Так для клоков же пины MCO1/MCO2 с настраиваемым источником.
      +2
      Неудобно. Очень корявая и грубая настройка. Пробовал — ерунда.
      0
      От какого устройства камера? Анализатор хардварый?
        +2
        Там написано.
        из телефона «Siemens C72»

        Анализатор софтовый. Китай-аналог «Saleae logic».
          0
          Сори, с утра что то не доглядел.
          Прикольный анализатор.
            0
            Главное — дешевый. Даже с поправкой на пресловутое «китайское» качество, покупать новый можно хоть каждый месяц. Но у меня работает (тьфу-тьфу-тьфу) уже почти год.
        0
        Диаграммка расшифровки регистра статуса понравилась!))

        Интересно было бы попробовать выполнить какую-то обработку на борту, эдж детекшн, например
          0
          Спасибо. С диаграммками просидел несколько часов (учитывая то, что в фотошопе я далеко не гуру).
          Да, с обработкой поиграться интересно. Жалко — оперативки мало, не разгуляешься. Хотя можно купить двухмегабайтный F407 с 256 кБ ОЗУ, взять фен, и заменить установленный МК на новый, более продвинутый. Уже будет веселее.
          0
          А не подскажете случайно библиотеки для ILI9325? А то та, что есть у меня, выводит только в одном цвете — видимо где-то ошибки. И вообще довольно примитивная, вывести то можно, а вот огранизовать двойную буферизацию или нормальный вывод текста с очисткой знакоместа не очень то получается.
            0
            Я был бы счастлив помочь, но — увы. =(
            С ILI вообще дела не имел (хотя — пробел, нужно восполнить :D). Попробуйте поискать на том же радиокоте или изиэлектрониксе.
              0
              Да вот пока не нашел я удобной библиотеки. То зачем-то пишут примеры с загрузкой картинки в видеопамять побайтово, то какого-то монстра строят, который в CooCox просто не собрать — заточено под другую среду жестко. А вот нормальной библиотеки, чтобы рисовать примитивы и выводить текст со стиранием областей нету.
                0
                Тогда у Вас один вариант — копать дисплей глубоко, и писать самостоятельно. =)
                  0
                  проблема в том, что он мне нужен в основном для отладки, т.к писать не нем полноценные интрфейсы пока не собираюсь- незачем. Да и слишком много пинов съедает его подключение. Поэтому жаль тратить время на его изучение глубоко. Есть более интересные задачи. )
                  Разве что сразу изучить какую-нибудь RTOS с uC-GUI, например, которая его поддерживает.
                    0
                    А я бы потратил время. Если это будет отладочный терминал, то пусть он лучше работает надежно (кстати, вот этот дисплей, что в материале, от Nokia 5300, тоже у меня для отладки предназначен — потратил дня полтора-два на него). Кому нужны сюрпризы в процессе отладки?)
                      0
                      К счастью, у меня есть UART и SWD, которые позволяют не отлаживать в стиле Arduino :) записью в порт, поэтому и необходимость этого экранчика весьма условная. Просто было бы удобно. Но удобство пока не стоит затраченного времени. Если хотите — присоединяйтесь, раз есть желание. У меня вот такой девайс:
                      image
                      платка на STM32F103VET6 вместе с экранчиком на 2.4" стоит всего примерно 50 баксов.
                        0
                        очень знакомая платка. У меня подобная на SSD1289 — вместе с SD и микросхемой флэш-памяти.
                        Чтобы присоединиться, нужно приобрести сам дисплей. Иначе-то ковырять нечего. =)
                          0
                          Сам дисплей стоит что-то около $15-20, он с тач-скрином, так что можете попробовать )
                          У меня она тоже с SD разъемом и флешом на SPI кажется.
                            0
                            Да, там SD и флэш висят на одной шине, CS-ом дифференцируются. В принципе, деньги не деньги, можно и приобрести. Авось пригодится.
                              0
                              ну плата прикольная, я ее время от времени для тестов юзаю. Хотя без LCD мне больше нравится другая.
            0
            Валяется камера, валяется f4, а вот руки не доходят :(
            Не могли бы Вы посчитать задержку видео. Очень надо!
              0
              Прошу прощения за поздний ответ… что Вы имеете в виду под задержкой?
              0
              проще через DMA делать это все — добавить в адресное пространство контроллера дисплей, а DMA настроить на передачу с периферии(DCMI) в дисплей — процессор при этом не грузится сиовершенно. Или 2 потока DMA — один считывает в память, проц обрабатывает как то картинку — результат обработки во втором буфере, а из того — через DMA в дисплей, в итоге проц занимается только обработкой картинки — данные гоняются без него

              Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

              Самое читаемое