Interfejs DCMI w STM32: jak dołączyć kamerę CCD do mikrokontrolerów STM32F2 i STM32F4
Konfiguracja kamery
Do komunikacji z kamerą po magistrali I 2 C wykorzystywane są cztery funkcje:
// Odczyt rejestru pod adresem regAddr ushort MT9D111_ReadReg(uchar regAddr); // Zapis do rejestru pod adresem regAddr wartosci regVal uchar MT9D111_WriteReg(uchar regAddr, ushort regVal); // Odczyt wartosci zmiennej znajdujacej sie w sterowniku driverID, przesunietej // o wartosc varOffset od poczatku struktury i rozmiarze varSize bajtow ushort MT9D111_ReadVar(uchar driverID, uchar varOffset, uchar varSize); // Podobnnie jak wyzej, ale z zapisem uchar MT9D111_WriteVar(uchar driverID, uchar varOffset, uchar varSize, ushort varData);
Konfigurację parametrów obrazów wyjściowych i sposobu w jaki są one wykonywane można dokonać na wiele sposobów wykorzystując sterowniki obecne w SOC (co jest łatwiejsze, ale nie pozwala poznać głębiej funkcjonowania kamery) lub też operując bezpośrednio na rejestrach. Tutaj zostały przedstawione podstawowe wiadomości na ten temat niezbędne do uzyskania żądanego obrazu drugą metodą. Aby była możliwa swobodna zmiana wartości rejestrów należy w pierwszej kolejności wyłączyć mikrokontroler przez wprowadzenie go w stan resetu (w nim są zaimplementowane sterowniki różnych funkcji jak np. autoekspozycja):
R0xC3:1[0] := (zapisanie jedynki do tego bitu wprowadza mikrokontroler w stan reset)
Matryca elementów światłoczułych (pixel array) znajdująca się w bloku jądra czujnika (sensor core) jest zbudowana z 1688 kolumn i 1256 wierszy (rysunek 8). Pierwsze 52 i ostatnie 4 kolumny oraz pierwsze i ostatnie 20 wierszy matrycy pikseli nie reagują na padające na nie światło (zawsze są optycznie interpretowane jako czarne) – wykorzystuje się je do automatycznej regulacji poziomu czerni.
Rys. 8. Matryca pikseli CCD
W konfiguracji domyślnej generowany obraz ma rozdzielczość 1600 x 1200 pikseli (UXGA – Ultra Extended Graphics Array) i jest pobierany z matrycy począwszy od 28 wiersza i 60 kolumny. Zmianę pozycji i wymiarów ramki obrazu można dokonać za pomocą zapisu odpowiednich wartości do następujących rejestrów znajdujących się w banku 0 czujnika (Page 0 – Sensor Core):
R0x01:0 := 328 (numer pierwszego wiersza obrazu) R0x02:0 := 444 (numer pierwszej kolumny obrazu) R0x03:0 := 600 (liczba wierszy/wysokość obrazu) R0x04:0 := 800 (liczba kolumn/szerokość obrazu)
Przy wypełnianiu tych rejestrów należy pamiętać, aby nie wchodzić w obszar czarnych pikseli oraz to, że minimalna wysokość obrazu wynosi 2 wiersze, a szerokość 9 (przy wykorzystaniu jednego ADC) oraz 17 (w trybie z dwoma ADC) kolumn.
Na matrycę czujnika jest nałożona siatka filtrów Bayera w wyniku obecności której każdy piksel jest czuły tylko na pewne spektrum światła i na wyjściu otrzymuje się intensywność jednego z trzech podstawowych kolorów (rysunek 9). Wiersze parzyste matrycy zawierają zielone i czerwone piksele, a nieparzyste – zielone i niebieskie. Podobnie jest dla kolumn, parzyste – zielone i niebieskie piksele, nieparzyste – zielone i czerwone piksele. Dlatego przy podawaniu numerów pierwszego wiersza i/lub kolumny obrazu należy się kierować zasadą „co drugi element”, czyli podawanie parzystych (lub nieparzystych w zależności od konfiguracji) numerów wierszy/kolumn. Istnieje również inny sposób polegający na przełączeniu bitów kolejności kolorowych pikseli:
R0x08:1[0] := 0 (najpierw idą wiersze z niebieskim pikselami) := 1 (najpierw idą wiersze z czerwonymi pikselami) R0x08:1[1] := 0 (najpierw idą kolumny z zielonymi pikselami) := 1 (najpierw idą kolumny z czerwonymi/niebieskimi pikselami)
Rys. 9. Układ pikseli RGB w matrycy CCD
Wyjściowe dane z bloku Color Pipeline mogą mieć format YUV lub RGB, a za wybór odpowiada 5 bit rejestru R0x08:1 (ustawiony – RGB, wyzerowany – YUV). Oprócz tego istnieje możliwość ustawienia czasu przetwarzania pikseli lub przesłony elektronicznej (Shutter Width). Im jej wartość jest większa, tym obraz będzie jaśniejszy, ale generowany z mniejszą częstotliwością:
R0x9:0 := 700 (wartość przesłony)
Ponieważ wyświetlacz LCD może przedstawić obraz o maksymalnych wymiarach 320×240 pikseli, więc wyjściowy obraz z kamery również powinien mieć taką rozdzielczość. Można to zrobić modyfikują wyżej opisane rejestry, jednak wtedy pole widzenia będzie niewielkie. Alternatywny sposób polega na wykorzystaniu decymatora, czyli funkcji skalującej obraz do żądanych rozmiarów. Za jej włączenie odpowiada 8 bit rejestru R0x08:1, a parametry decymacji zapisuje się zgodnie z poniższym:
R0x11:0 := 0 (położenie X pierwszego punktu decymacji na obrazie wejściowy) R0x12:0 := 0 (położenie Y pierwszego punktu decymacji na obrazie wejściowy) R0x13:0 := 800 (szerokość obrazu wejściowego) R0x14:0 := 600 (wysokość obrazu wejściowego) R0x16:0 := 820 (waga dla horyzontalnej decymacji) R0x17:0 := 820 (waga dla wertykalnej decymacji)
Waga decymacji jest wyznaczana ze wzoru:
Dodatkowo czujnik kamery udostępnia funkcje binningu oraz pomijania wierszy i kolumn. Pierwszy sposób zmniejszenia rozdzielczości polega na wyznaczeniu średniej wartości dla 4 pikseli o tym samym kolorze pobranych z kwadratowego obszaru i powinno się stosować raczej tą metodę, gdyż pomijanie wprowadza efekt aliasingu. Jednocześnie można wybrać tryb pracy z dwoma przetwornikami ADC w celu nie tylko skrócenia czasów przetwarzania obrazu z matrycy, ale również zwiększenia częstotliwości wysyłanych ramek o 2 razy:
R0x21:0 :=0 (binning wył., tryb 2xADC, brak pomijania wierszy/kolumn)
Standardowo po włączeniu zasilania sygnał PIXCLK posiada częstotliwość dwa razy mniejszą niż wejściowy sygnał XCLK (pracuje tylko jeden przetwornik ADC). W przypadku gdy koniecznym jest zwiększenie lub zmniejszenie tej częstotliwości można wykorzystać podblok PLL wbudowany w blok jądra czujnika. Należy jednak pamiętać by zachowane były pewne warunki (tablica 4).
Tab. 4. Warunki stawiane sygnałom taktującym w systemie kamery
Również moduł DCMI mikrokontrolera wprowadza ograniczenie maksymalnej częstotliwości taktowania pikseli do 54 MHz. Uwzględniając powyższe informacje konfiguracja bloku PLL wygląda następująco:
//*************************** // Fin = 56 MHz // Fpfd = 8 MHz // Fvco = 232 MHz // Fout = 37.3MHz //*************************** R0x65:0 := 0xA000 //Clock: <15> PLL BYPASS = 1 --- omin bloku PLL R0x65:0 := 0xE000 //Clock: <14> PLL OFF = 1 --- wylacz blok PLL R0x66:0 := 0x1C06 //PLL Control 1: <15:8> M = 28, <5:0> N = 6 R0x67:0 := 0x0502 //PLL Control 2: <6:0> P = 2 R0x65:0 := 0xA000 //Clock: <14> PLL OFF = 0 --- wlacz blok PLL Delay(10); //Odczekaj 10 ms R0x65:0 := 0x2000 //Clock: <15> PLL BYPASS = 0 --- wybierz sygnal z bloku PLL //jako glowny zegar systemu
Ostatecznie wszystkie powyższe ustawienia zostały zapisane w tablicy struktur wykorzystywanej w funkcji inicjalizującej MT9D111_Init():
struct MT9D111_REG_STRUCT MT9D111_InitStructure[] = { //************************ // Konfiguracja bloku PLL //************************ //*************************** // Fin = 56 MHz // Fpfd = 8 MHz // Fvco = 232 MHz // Fout = 37.3MHz //*************************** {0, 0x65, 0xA000}, //Clock: <15> PLL BYPASS = 1 --- make sure that PLL is bypassed {0, 0x65, 0xE000}, //Clock: <14> PLL OFF = 1 --- make sure that PLL is powered-down {0, 0x66, 0x1C06}, //PLL Control 1: <15:8> M = 28, <5:0> N = 6 {0, 0x67, 0x0502}, //PLL Control 2: <6:0> P = 2 {0, 0x65, 0xA000}, //Clock: <14> PLL OFF = 0 --- PLL is powered-up {MT9D111_DELAY,0,10},//Delay 10 ms {0, 0x65, 0x2000}, //Clock: <15> PLL BYPASS = 0 --- enable PLL as master clock //**************************************** // Konfiguracja pozycji i wymiarow obrazu //**************************************** {0, 0x01, 0x0148}, //Row Start = 328 {0, 0x02, 0x01BC}, //Column Start = 444 {0, 0x03, 0x0258}, //Row Width = 600 {0, 0x04, 0x0320}, //Column Width = 800 {1, 0x08, 0x01F8}, //Decimator enabled {1, 0x12, 0x0320}, //X1 Coordinate for Crop Window +1; width = 800 {1, 0x14, 0x0258}, //Y1 Coordinate for Crop Window +1; height = 600 {1, 0x16, 0x0334}, //Weight for H Decimation = 820 {1, 0x17, 0x0334}, //Weight for V Decimation = 820 //****************************************** // Konfiguracja parametrow wykonania obrazu //****************************************** {0, 0x09, 0x02BC}, //Shutter Width = 700 --- Ustawienie wartosci przeslony {0, 0x20, 0x0300}, //Read Mode (B): binning disabled, use both ADCs, normal UXGA size, column/row skip disabled {0, 0x21, 0x0000}, //Read Mode (A): binning disabled, use both ADCs, column/row skip disabled //********************************************** // Konfiguracja formatu obrazu wyjsciowego //********************************************** {1, 0x09, 0x0009}, //Factory bypass: Data from SOC going to Dout pads {1, 0x97, 0x0022}, //Output Format Config: RGB output, swap odd/even bytes {MT9D111_TERM,0,0} };