Obsługa interfejsu DCMI (kamery CCD) – przykład na STM32F2

Obsługa interfejsu DCMI (kamery CCD) – przykład na STM32F2

Interfejs DCMI zastosowany w niektórych mikrokontrolerach STM32 współpracuje z dołączanym z zewnątrz modułem kamery. Na styku fizycznym łączącym interfejs mikrokontrolera z modułem wyprowadzone są wszystkie sygnały niezbędne do sterowania modułu i odczytu danych obrazka (ramki obrazu). Dla uproszczenia konstrukcji dane są zapisywane w buforze w wewnętrznej pamięci RAM mikrokontrolera. Do sterowania płytką interfejsu oraz odczytu danych obrazka wykorzystuje się drugie złącze na które oprócz zasilania podane są sygnały RxD i TxD portu szeregowego mikrokontrolera. Dzięki temu złączu interfejs może współpracować z zewnętrznym komputerem na którym uruchamia się przykładowy program sterujący. W celu przechwycenia danych obrazu oprogramowanie mikrokontrolera wykorzystuje wewnętrzny interfejs DCMI, mechanizm DMA oraz pamięć RAM, która służy do przechowywania odczytanych z modułu danych. Ponadto płytka interfejsu dostarcza do modułu napięcie zasilania oraz steruje jego działaniem za pośrednictwem 2-przewodowej magistrali I2C.   

Na rysunku 1 pokazano schemat blokowy przepływu danych obrazu z wyjścia modułu kamery do buforu w pamięci RAM mikrokontrolera.

 

Rys. 1. Schemat przepływu danych z modułu kamery do pamięci mikrokontrolera STM32  

Rys. 1. Schemat przepływu danych z modułu kamery do pamięci mikrokontrolera STM32

 

Obraz z wyjścia modułu wysyłany jest jako strumień informacji o kolejnych pikselach obrazu pogrupowanych w linie które tworzą kompletną ramkę obrazka. Interfejs DCMI przy pomocy sygnałów synchronizacji generowanych przez moduł wychwytuje dane kolejnych pikseli i tworzy z nich 32-bitowe słowo danych. Dane mogą być zapamiętane w buforze obrazu w pamięci RAM mikrokontrolera. Do zapisu danych najlepiej wykorzystać sprzętowy mechanizm DMA mikrokontrolera ponieważ jest szybki i może działać w tle głównego programu. Dane w buforze zapisane jako jeden uporządkowany blok mogą być wykorzystane np. do stworzenia pliku obrazu w formacie BMP czy JPEG dającym się wyświetlić na ekranie komputera.

Tworzenie programu sterującego powinno składać się z kilku kolejnych etapów:

  • napisania procedury inicjacji portów mikrokontrolera połączonych z wyprowadzeniami modułu kamery,
  • napisania procedury inicjacji wewnętrznych rejestrów modułu kamery,
  • napisania procedury inicjacji rejestrów mikrokontrolera sterujących interfejsem DCMI,
  • napisania procedury inicjacji rejestrów mikrokontrolera sterujących interfejsem DMA,
  • napisania procedury która uruchamia interfejsy DCMI i DMA mikrokontrolera oraz inicjuje wykonanie zdjęcia przez moduł kamery.

W kolejnych krokach przy pomocy przykładów postaram się pokazać jak można to zrobić.

 

Dołączenie wyprowadzeń kamery do portów mikrokontrolera

Najpierw należy fizycznie połączyć wyprowadzenia sygnałów modułu z odpowiednimi portami mikrokontrolera. Na schemacie interfejsu opublikowanym wraz z poprzednim artykułem można zobaczyć jak to zostało zrobione. Wybór niektórych linii może być dowolny jednak w przypadku portów mikrokontrolera związanych z interfejsem DCMI istnieją pewne ograniczenia. Jak to często bywa w przypadku mikrokontrolerów STM32 możliwe konfiguracje zależą od obudowy i ogólnej ilości dostępnych portów. Przy większych obudowach istnieje możliwość przypisania określonej funkcji jednemu z kilku dostępnych portów. Wszystko opisane jest w dokumentacji technicznej zastosowanego typu mikrokontrolera. Należy w niej znaleźć sekcję nazwaną Alternate function mapping. Zawiera ona w formie tabeli zestawienie wszystkich dodatkowych funkcji jakie można przypisać wyprowadzeniu każdego z portów. W tym momencie najbardziej nas interesują funkcje związane z interfejsem DCMI. W tabeli są one poprzedzone przedrostkiem DCMI_ i dotyczą magistrali danych oraz linii sygnałów sterujących. Ponieważ w interfejsie wybrano magistralę o szerokości 8 bitów w tabeli należy odszukać funkcje określone jako  DCMI_D0–DCMI_D7. Jak można się przekonać tą samą funkcję mogą pełnić różne porty. Np. wyprowadzenie interfejsu DCMI_D0 można przypisać jako funkcję alternatywną albo do portu PA9 albo PC6. Dzięki temu można łatwiej dopasować schemat urządzenia do możliwości technicznych mikrokontrolera. Oprócz portów magistrali danych należy wybrać te, które będą współpracowały z sygnałami sterującymi: DCMI_HSYNC, DCMI_VSYNC, DCMI_PIXCK. Dodatkowo należy wybrać porty do obsługi szeregowej magistrali I2C przy pomocy której wysyłane są rozkazy sterujące do modułu. Jeżeli zdecydujemy się na własną programową obsługę protokołu I2C do obsługi magistrali można wybrać dowolne porty. Jeżeli zaś będziemy chcieli skorzystać ze sprzętowego wsparcia protokołu I2C jakie oferują mikrokontrolery STM32F znów trzeba odszukać w sekcji Alternate function mapping informacji o tym, które porty związane są z obsługą portu I2C. Oprócz tego do podłączenia pozostał jeszcze jeden sygnał: sygnał zegara taktującego który trzeba podać do modułu. Zazwyczaj częstotliwość zegara w przypadku różnego typu modułów zawiera się w granicach 6…80 MHz. Można w tym celu wykorzystać jedno z dostępnych wyprowadzeń MCO wewnętrznego sygnału zegarowego mikrokontrolera. Częstotliwość przebiegu prostokątnego na tym wyprowadzeniu może być programowo zmieniana i doskonale nadaje się na sygnał zegarowy dla modułu. Jedynym ograniczeniem jest przypisanie funkcji MCO do konkretnego portu.

 

Rys. 2. Schemat elektryczny ilustrujący sposób dołączenia modułu kamery do DCMI w STM32F2

Rys. 2. Schemat elektryczny ilustrujący sposób dołączenia modułu kamery do DCMI w STM32F2

 

Szukanie w tabeli interesujących nas funkcji portów i unikanie możliwych kolizji gdy wybór jednej funkcji blokuje możliwość wykorzystania innej bywa kłopotliwe. Innym rozwiązaniem jest posłużenie się graficznym programem MicroXplorer. To sygnowany przez firmę ST darmowy program pozwalający łatwiej odszukać w gąszczu alternatywnych funkcji wyprowadzeń mikrokontrolera tą konfigurację, która odpowiada nam najbardziej. Po zainstalowaniu i uruchomieniu programu na początku należy zadeklarować typ  i obudowę mikrokontrolera który będzie użyty w konstruowanym urządzeniu. Następnie należy wskazać typ interfejsu, który nas interesuje np. DCMI. Na rysunku obudowy podświetlone na zielono zostaną wyprowadzenia, interfejsu. Jednocześnie na liście  dostępnych interfejsów mikrokontrolera na czerwono zostaną zaznaczone wszystkie, które w tej konfiguracji staną się nie dostępne np. I2C3. Żółtymi znaczkami ostrzeżeń zostaną oznaczone interfejsy o ograniczonej dla takiej konfiguracji funkcjonalności. Ponieważ interfejsy mają zdublowane funkcje dla różnych wyprowadzeń mikrokontrolera, posługując się dokumentacją i programem MicroXplorer można wszystko odpowiednio skonfigurować. Zamieszczony w poprzednim artykule schemat interfejsu pokazuje jak ostatecznie przypisano funkcje do portów mikrokontrolera.

 

Procedura inicjacji portów mikrokontrolera

Porty mikrokontrolera wykorzystywane przez interfejs DCMI należy zainicjować do pracy w funkcji alternatywnej. Procedurę najłatwiej napisać wykorzystując odpowiednie funkcje biblioteki standardu CMSIS udostępnianej przez firmę ST dla konkretnego typu mikrokontrolera. Funkcje są dobrze udokumentowane i mają opisowe nazwy procedur. Dla tego wszystkie kolejne przykłady będą wykorzystywały funkcje takiej biblioteki.

Na początku należy zadeklarować zmienne symboliczne związane ze wszystkimi liniami wykorzystywanymi przez interfejs DCMI. Deklarację najlepiej umieścić w dołączanym pliku nagłówkowym. Do nazw symbolicznych będą się potem odwoływały wszystkie procedury operujące na liniach interfejsu. Deklaracja może wyglądać tak:

i dalej deklaracja dla kolejnych linii magistrali danych, sygnałów sterujących.

Procedura inicjacji linii interfejsu DCMI może wyglądać następująco:

Jak widać cała procedura składa się z trzech funkcji. Najpierw deklarowane są tabele nazw symbolicznych które odwołują się do wewnętrznych rejestrów mikrokontrolera odpowiedzialnych za konfiguracje portów GPIO. Następnie procedura Inicjacja_linii_DCMI w kolejnych krokach inicjuje linie interfejsu DCMI. Dla większej przejrzystości inicjację linii magistrali danych i linii sygnałów sterujących podzielono na dwa podprogramy KAM_Magistrala_danych_inicjacja i KAM_IO_Linie_Inicjacja. Dodatkowo uruchamiane jest wyprowadzenie MCO i ustawiane parametry sygnału zegarowego dla modułu.

Od tego momentu porty interfejsu DCMI gotowe są do obsługi sygnałów i współpracy z liniami modułu kamery. Na koniec należy jeszcze zainicjować linie szeregowej magistrali sterującej. Jeżeli do sterowania linii wykorzystany zostanie wewnętrzny interfejs I2C mikrokontrolera, linie powinny zostać standardowo zainicjowane do pracy w funkcji alternatywnej. Jeżeli będzie stosowana programowa obsługa protokołu I2C linie powinny zostać zainicjowane jako standardowe porty I/O w trybie otwartego drenu z wewnętrznym podciąganiem do plusa. Przy takiej konfiguracji procedury użytkownika obsługujące transmisję I2C będą miały możliwość zapisu i odczytu poziomu portów.

 

Procedura inicjacji wewnętrznych rejestrów modułu kamery

Następnym krokiem jest inicjacja modułu kamery poprzez zapisanie do jej wewnętrznych rejestrów odpowiednich ustawień. Najczęściej po włączeniu zasilania w rejestrach są już jakieś sensowne wartości. Jednak nawet w takim przypadku użytkownik może chcieć zaprogramować własne początkowe ustawienia np. formatu, wymiarów obrazu, jaskrawości, nasycenia itp. Zapis ewentualnie odczyt rejestrów następuje poprzez szeregową magistralę sterującą. Sposób adresowania rejestrów, ich funkcje, format danych określa dokumentacja konkretnego modelu modułu. Dane do rejestrów przesyłane są magistralą w formacie I2C z wykorzystaniem wewnętrznego interfejsu mikrokontrolera lub przy pomocy własnych procedur użytkownika obsługi protokołu I2C.

 

Procedury inicjacji rejestrów interfejsu DCMI

Sprzętowy interfejs DCMI służy do wyłapywania z ciągłego strumienia pikseli i linii wysyłanego przez moduł kamery danych samego obrazu i formowania ich w logicznie ułożone bloki danych. Interfejs może współpracować z różnymi modułami i dla tego jego parametry są konfigurowalne. Dotyczy to między innymi:

  • szerokości magistrali danych obrazu która może składać się z 8, 10, 12 lub 14 linii,
  • określenia aktywnego zbocza sygnału PIXCLK służącego do synchronizacji kolejnych bajtów danych oraz określenia aktywnego poziomu sygnałów HSYNC, VSYNC. W przypadku tych ostatnich aktywny poziom wskazuje na przerwę pomiędzy kolejnymi liniami obrazu i przerwę pomiędzy kolejnymi ramkami obrazu,
  • określenia trybu rejestracji danych: jako kilku kolejnych pojedynczych obrazków (ramek) czyli trybu Snapshot mode lub rejestracji ciągłego (rejestracja filmu) czyli trybu Continuous grab mode,
  • ustalenie formatu danych obrazu wysyłanych przez moduł: prostego gdy wysyłane są dane kolejnych pikseli kolejnych linii lub kodowanego JPEG format gdy dane stanowią zakodowane bloki.

Oprócz tego interfejs może zostać zaprogramowany do automatycznego przycinania odbieranego rozkazu. Może także pracować bez sygnałów HSYN, VSYNC o ile moduł jest w stanie wysyłać dane z kodami synchronizacji zastępujące wspomniane sygnały.

Na potrzeby tego opisu podany zostanie przykład zaprogramowania interfejsu DCMI do pracy z 8-przewodową magistralą obrazu, sygnałami HSYNC, VSYNC aktywnymi przy niskim poziomie dla rejestracji pojedynczych nie kodowanych ramek obrazu.

Ustawienie rejestrów interfejsu może wyglądać w sposób następujący:

Najpierw podłączony zostaje wewnętrzny zegar mikrokontrolera do obwodów interfejsu DCMI. Następnie wypełnione zostają kolejne pola struktury wartościami konfiguracyjnymi o następującym znaczeniu:

  • DCMI_CaptureMode_SnapShot – interfejs będzie pracował przy obsłudze pojedynczych zdjęć (ramek),
  • DCMI_SynchroMode_Hardware – interfejs będzie współpracował z liniami sterującymi sygnałów HSYNC, VSYNC,
  • DCMI_PCKPolarity_Rising – aktywne będzie zbocze narastające sygnału taktującego dane PIXCL,
  • DCMI_VSPolarity_Low – poziomem aktywnym sygnału VSYNC jest poziom niski (poziom aktywny linia VSYNC przyjmuje pomiędzy kolejnymi ramkami obrazów),
  • DCMI_HSPolarity_Low – poziomem aktywnym sygnału HSYNC jest poziom niski (poziom aktywny linia HSYNC przyjmuje pomiędzy kolejnymi liniami ramki obrazu),
  • DCMI_CaptureRate_All_Frame – interfejs prześle do bufora dane wszystkich kolejnych ramek (parametr istotny w przypadku transmisji z modułu więcej niż 1 obrazu),
  • DCMI_ExtendedDataMode_8b – interfejs będzie współpracował z magistralą danych obrazu o rozmiarze 8 bitów.

Po inicjacji interfejsu w przypadku odczytu danych obrazu w postaci kolejnych linii zostaje wyłączona opcja pracy z danymi kodowanymi.

Najwygodniej kontrolować pracę interfejsu poprzez przerwania generowane przez różne zdarzenia. Najważniejsze DCMI_IT_FRAME oznacza zakończenie przesyłania kolejnej ramki obrazu, DCMI_IT_OVF, DCMI_IT_ERR oznaczają wystąpienie błędów w pracy interfejsu DCMI. Kolejne linie programu włączają poszczególne przerwania. Obsługę przerwań należy umieścić w przeznaczonym do tego pliku stm32f2xx_it.c i może ona wyglądać następująco:

Każde przerwanie może np. ustawiać flagę deklarowaną w programie użytkownika. Stan flagi będzie informował procedurę odbioru ramki obrazu o sukcesie lub błędzie transmisji danych.

Na koniec należy pamiętać o ustawieniu wektora przerwania interfejsu DCMI w kontrolerze NVIC.

 

Procedury inicjacji rejestrów DMA

Mechanizm interfejsu DCMI odpowiada za współpracę pomiędzy modułem kamery a mikrokontrolerem. Odebrane dane powinny zostać przesłane do buforu w pamięci RAM mikrokontrolera. Takie przesłanie najwygodniej powierzyć mechanizmowi DMA mikrokontrolera. Ponieważ jest to wewnętrzny mechanizm sprzętowy w trakcie pracy będzie w minimalny sposób obciążł główny program użytkownika oraz co nie bez znaczenia będzie szybki w działaniu.

Mikrokontroler z rodziny STM32F2xx został wyposażony w dwa interfejsy DMA. Każdy interfejs obsługuje określoną liczbę umownych kanałów i strumieni. Taki podział wynika z faktu, że DMA może obsługiwać wiele różnych źródeł i odbiorców danych. Mogą to być np. porty mikrokontrolera, tajmery/liczniki, przetworniki oraz właśnie interfejs DCMI. Obsługa każdego źródła danych do przesłania, powiązana jest z określonym kanałem i strumieniem. Przypisanie obsługi interfejsu DCMI do konkretnego kanału i strumienia można znaleźć w dokumentacji technicznej mikrokontrolera w tabeli DMA2 request mapping. Po decyzji z którego strumienia DMA chcemy korzystać należy zainicjować DMA wypełniając pola struktury i wywołując funkcję inicjującą.

Na potrzeby tego przykładu poczynione zostały następujące założenia co do obrazu przesyłanego z modułu kamery:

  • wymiar obrazka to 160 x 120 pikseli,
  • dane przesyłane będą w formacie 565RGB bez kodowania co oznacza 2 bajty na piksel,
  • każdorazowo przesyłana i zapisywana do buforu będzie 1 ramka obrazu.

Przyjęcie takich założeń oznacza, że obsługiwana transmisja będzie miała stały rozmiar wynoszący 160*120*2 = 38 400 bajtów. Bufor w pamięci RAM mikrokontrolera do którego DMA będzie zapisywało dane obrazu musi mieć co najmniej taki rozmiar. Procedura inicjacji DMA2 może wyglądać tak:

Dane do przesłania przez DMA pobierane są z rejestru DCMI_DR będącym chwilowym buforem po odczycie danych z magistrali modułu. Zmienna symboliczna wskazuje na fizyczny adres rejestru w przestrzeni adresowej mikrokontrolera.

Znaczenie najważniejszych ustawień wartości inicjujących DMA:

  • DMA_Channel_1 – do transmisji danych obrazu będzie wykorzystany kanał 1 DMA2,
  • DCMI_DR_ADDRESS – zmienna symboliczna określająca adres urządzenia peryferyjnego (w tym przypadku interfejsu DCMI) będącego źródłem danych do przesłania nota1,
  • (uint32_t)bufor_RAM_danych_obrazka – adres odbiorcy danych (w tym przypadku adres początku buforu dla danych obrazu w pamięci RAM mikrokontrolera),
  • DMA_DIR_PeripheralToMemory – kierunek przesyłu danych (w tym przypadku z urządzenia peryferyjnego do buforu w pamięci),
  • rozmiar_przeslania_DMA – ilość danych do przesłania (w tym przypadku rozmiar danych obrazka),
  • DMA_PeripheralInc_Disable -po kolejnym przesłaniu bazowy adres urządzenia peryferyjnego nie będzie automatycznie zwiększany,
  • DMA_MemoryInc_Enable – po kolejnym przesłaniu bazowy adres odbiorcy danych będzie automatycznie zwiększany (i będzie wskazywał na miejsce do zapisu w buforze dla kolejnych danych),
  • DMA_PeripheralDataSize_Word – rozmiar danych pobieranych z interfejsu DCMI nota2,
  • DMA_MemoryDataSize_Byte – rozmiar danych zapisywanych w buforze,
  • DMA_Mode_Normal – tryb pracy DMA normalny, po zakończeniu przesyłania określonej wcześniej liczby danych DMA automatycznie zakończy pracę.
Dane odbierane z magistrali danych modułu zawsze są pakowane do postaci 4-bajtowego słowa.

Tabela pokazuje rozmieszczenie poszczególnych bitów koloru w słowie danych obrazu:

Byte address 31:27 26:21 20:16 15:11 10:5 4:0
0 Red n + 1 Green n + 1 Blue n + 1 Red n Green n Blue n
4 Red n + 3 Green n + 3 Blue n + 3 Red n + 2 Green n + 2 Blue n + 2

Po przesłaniu nowych ustawień inicjujących do rejestrów DMA może on zostać otwarty podobnie jak interfejs DCMI.

 

Wykonanie zdjęcia i odczyt danych ramki obrazu

Jeżeli moduł kamery, interfejs DCMI oraz DMA zostały prawidłowo zainicjowane można przejść do procedury wykonania zdjęcia. Wykonuje się to w kolejnych krokach:

  1. przygotowanie rejestrów modułu oraz w razie konieczności DCMI i DMA
  2. wysłanie do modułu rozkazu wykonania zdjęcia
  3. uruchomienie interfejsu DCMI do odczytu danych 1 ramki obrazu
  4. oczekiwanie na zakończenie przesyłania danych

Krok 1

Jeżeli parametry rejestrowanego obrazu ulegają zmianie (np. zmienia się format koloru lub wymiary) należy skorygować ustawienia wewnętrznych rejestrów modułu oraz w razie potrzeby rejestrów DCMI i DMA.

Krok 2

Zależnie od typu zastosowanego modułu rozkaz wykonania zdjęcia może mieć różną składnię. W przypadku modułu MT9D111/9D131 wysłany rozkaz uruchomi moduł do rejestracji określonej liczby ramek (w tym przypadku jednej) począwszy od następnego aktywnego poziomu linii VSYNC. Moment pojawienia się impulsu, którego zakończenie oznacza start procesu rejestracji danych obrazu można określić odczytując ustawienie wewnętrznego rejestru statusu modułu.

Krok 3 i 4

Procedura startu rejestracji i oczekiwanie na jej zakończenie może wyglądać następująco:

Na początku procedury o ile wcześniej nie zostało to zrobione są zerowane flagi związane z przerwaniami interfejsu DCMI. Następnie zostaje uruchomiony interfejs DCMI do odbioru danych z modułu. Odbiór danych rozpocznie się automatycznie w chwili zakończenia sygnału linii sterującej VSYNC. Włączony wcześniej kanał DMA także w tej chwili rozpocznie automatyczny transfer do buforu kolejnych odbieranych danych. Zarówno interfejs DCMI jak i DMA samoczynnie zawieszają pracę w czasie aktywnego poziomu sygnału linii HSYNC rozdzielającego transmisję danych kolejnych linii obrazu.

Na rysunku 3 przedstawiono schematyczny przebieg sygnałów podczas transmisji danych ramki obrazu.

 

Rys. 3. Przebiegi charakterystyczne dla transmisji ramki danych z modułu kamery

Rys. 3. Przebiegi charakterystyczne dla transmisji ramki danych z modułu kamery

 

Kolejny aktywny poziom sygnału linii VSYNC oznacza zakończenie transmisji. Praca interfejsów DCMI i DMA zostaje zawieszona. Interfejs DCMI może zostać wyłączony. Od tej chwili dane obrazu znajdują się w buforze i są dostępne do wykorzystania przez program użytkownika.

Opisane procedury każdy użytkownik może przykroić do swoich potrzeb Jak zawsze możliwe są różne rozwiązania także lepsze od wcześniej opisanych. Mam jednak nadzieję, że przykładowe procedury pozwolą lepiej zrozumieć sposób działania mechanizmu DCMI, który w przypadku mikrokontrolerów STM32F2XX i STM32F4XX bardzo ułatwia pracę z modułami kamer.

Ryszard Szymaniak

div#stuning-header .dfd-stuning-header-bg-container {background-size: initial;background-position: top center;background-attachment: initial;background-repeat: initial;}#stuning-header div.page-title-inner {min-height: 650px;}