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

20.07.2023
  • 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 . Вот она (шлейф и разъем камеры скрыты под кожухом).

Кроме нюансов в области формата данных, камеры также могут отличаться в области количества выводов синхронизации. У большинства (по моему мнению) сенсоров в наличии имеются специальные выводы строчной и кадровой синхронизации; но есть камеры, имеющие только лишь вывод строба пикселя, а о начале новой строки/кадра они дают знать с помощью специальных передаваемых кодов (к примеру, 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 » - вдруг пригодится кому.

  • 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 . Вот она (шлейф и разъем камеры скрыты под кожухом).

Кроме нюансов в области формата данных, камеры также могут отличаться в области количества выводов синхронизации. У большинства (по моему мнению) сенсоров в наличии имеются специальные выводы строчной и кадровой синхронизации; но есть камеры, имеющие только лишь вывод строба пикселя, а о начале новой строки/кадра они дают знать с помощью специальных передаваемых кодов (к примеру, 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 » - вдруг пригодится кому.

Подключение карты проводится с помощью двух функций:
1. С помощью функции disk_initialize карта инициализируется.
2. Далее необходимо ее примонтировать f_mount .

После успешного выполнения этих действий можно осуществлять различные операции с картой, такие как:
1. f_open – открытие файла,
2. f_close – закрытие файла;
3. f_mkdir – создание директории;
4. f_chdir – выбор директории;
5. f_write – запись в файл;
6. f_read – чтение из фала.

Работу с камерой и SD картой я описал, теперь рассмотрим работу программы в целом:
1. Инициализируем карту SD;
2. Инициализируем модуль камеры;
3. Открываем файл time.txt (в файле должна быть одна строчка формата 01.01.2014_12:00 ), в котором хранится время для отсчета таймера RTC;
4. Считываем время и дату из файла и настраиваем RTC;
5. Закрываем файл time.txt;
6. Создаем директорию для хранения фотографий «LinkSpritePhoto»;
7. При нажатии кнопки PA0 на плате делаем фото и сохраняем на карточку (Пока делается фотография, горит синий светодиод);
8. В случае невыполнения какой либо из функций, сваливаемся в бесконечный цикл и мигаем зеленым светодиодом.

Для запуска камеры в непрерывном режиме необходимо раскомментировать в main.c следующие строчки:

//EnabledButtonStart = 101; // Для запуска в цикле раскомментировать эту строку //Delay(300); // Задержка для запуска в цикле
И закомментировать эту строку:

EnabledButtonStart = 0; // Для запуска в цикле закомментировать эту строку
Тогда при включении, камера начнет писать фотографии непрерывно.

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

Или замедленная киносъёмка - это киносъёмка с частотой, меньшей стандартной частоты съемки и проекции в 24 кадра в секунду.

Начав изучать микроконтроллеры STM32 и написав «HellowWorld» с миганием светодиодом, я понял, что для лучшего понимания работы STM32 мне необходимо реализовать что-то более сложное с использованием большего количества периферии микроконтроллера. Так возникла идея создания Time-lapse камеры.

Разработанная мной камера будет делать фотографии примерно раз в 5 секунд и сохранять их на SD карту в формате jpeg. Далее их необходимо объединить на компьютере в видео файл.

Для создания камеры я использовал следующие компоненты:

Подключение карты проводится с помощью двух функций:
1. С помощью функции disk_initialize карта инициализируется.
2. Далее необходимо ее примонтировать f_mount .

После успешного выполнения этих действий можно осуществлять различные операции с картой, такие как:
1. f_open – открытие файла,
2. f_close – закрытие файла;
3. f_mkdir – создание директории;
4. f_chdir – выбор директории;
5. f_write – запись в файл;
6. f_read – чтение из фала.

Работу с камерой и SD картой я описал, теперь рассмотрим работу программы в целом:
1. Инициализируем карту SD;
2. Инициализируем модуль камеры;
3. Открываем файл time.txt (в файле должна быть одна строчка формата 01.01.2014_12:00 ), в котором хранится время для отсчета таймера RTC;
4. Считываем время и дату из файла и настраиваем RTC;
5. Закрываем файл time.txt;
6. Создаем директорию для хранения фотографий «LinkSpritePhoto»;
7. При нажатии кнопки PA0 на плате делаем фото и сохраняем на карточку (Пока делается фотография, горит синий светодиод);
8. В случае невыполнения какой либо из функций, сваливаемся в бесконечный цикл и мигаем зеленым светодиодом.

Для запуска камеры в непрерывном режиме необходимо раскомментировать в main.c следующие строчки:

//EnabledButtonStart = 101; // Для запуска в цикле раскомментировать эту строку //Delay(300); // Задержка для запуска в цикле
И закомментировать эту строку:

EnabledButtonStart = 0; // Для запуска в цикле закомментировать эту строку
Тогда при включении, камера начнет писать фотографии непрерывно.

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

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

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

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


Кроме нюансов в области формата данных, камеры также могут отличаться в области количества выводов синхронизации. У большинства (по моему мнению) сенсоров в наличии имеются специальные выводы строчной и кадровой синхронизации; но есть камеры, имеющие только лишь вывод строба пикселя, а о начале новой строки/кадра они дают знать с помощью специальных передаваемых кодов (к примеру, 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 » - вдруг пригодится кому.