====== Kamera OV7670 se STM periferií DCMI ====== --- //[[xmacha49@stud.feec.vutbr.cz|Petr Machala]], 18.1.2015// ===== Zadání ===== Navrhněte základní koncepci využívající kameru OV7670 a vývojovou desku 32F429IDISCOVERY, použijte periferii DCMI. Na vývojové desce demonstrujte propojení kamery s mikrokontrolérem. ---- ===== Úvod ===== Tématem tohoto individuálního projektu je problematika týkající se propojení vývojové desky 32F429IDISCOVERY firmy //STMicroelectronics//\\ s mikrokontrolérem ARM Cortex-M4 a modulu kamery OV7670 firmy //OmniVision//. Samotné propojení těchto dílčích částí je realizováno využitím serial camera control bus (SCCB) sběrnice, která slouží pro konfiguraci modulu kamery a digital camera interface (DCMI) sběrnice pro získání požadovaných obrazových dat. Tyto data jsou následně zobrazována na displej na vývojové desky. Pro vývoj programového kódu a další podpůrné funkce bylo použito vývojového prostředí //Keil MDK-ARM uVision5//. ===== Hardware ===== ==== Vývojová deska 32F429IDISCOVERY ==== Základem nastíněného individuálního projektu je vývojová deska 32F429IDISCOVERY firmy //STMicroelectronics//, kterou lze vidět na Obr. 1 níže. {{:2014:cam-ov7670:vyvojova_deska_foto.png?500|Obr. 1: Fotografie vývojové desky 32F429IDISCOVERY}} Tato použitá vývojová deska je osazena 32 bitovým mikrokontrolérem (MCU) architektury ARM Cortex-M4 s označením STM32F429ZIT6. Tento MCU je součástí tzv. STM32 F4 série, která disponuje, oproti sérii předchozí, značným množstvím nových periférií, desítkami nových procesorových instrukcí atd. Obecně to tedy znamená zvýšení výkonu, efektivnější využití procesorového času a snížení spotřeby. Z hlediska vybavení vývojové desky zde můžeme nalézt periferie, jako např. tříosý MEMS pohybový senzor, QVGA RGB TFT LCD displej, SDRAM paměť nebo standardní ovládací mikrotlačítka. Tato skutečnost činí tuto vývojovou desku jako velmi univerzální, což představuje velkou výhodu v praxi. ==== Kamera OV7670 ==== Druhou komponentou individuálního projektu je zmíněný modul kamery OV7670 firmy //OmniVision//, kterou lze vidět na Obr. 2 níže. {{:2014:cam-ov7670:kamera_foto.png?450|Obr. 2: Fotografie kamery OV7670}} Tato kamera standardně disponuje kvalitním CMOS senzorem schopným zaznamenávat rozlišení až 640x480 px (VGA neboli 0.3 Mpx)\\ při obnovovací frekvenci až 30 fps společně s dalšími periferiemi zajišťující obrazové předzpracování a externí konektivitu. Kamera obecně\\ vždy zaznamenává obraz ve výše zmíněném rozlišení a obnovovací frekvenci, který je poté na základě nastavení kamery předzpracováván digitálním signálovým procesorem (DSP) a poté teprve odesílán pomocí DCMI sběrnice do koncového zařízení. Tato konfigurace je prováděna právě pomocí zmíněné SCCB sběrnice. Kamera tedy na základě nastavení podporuje rozlišení jako zmíněné VGA nebo CIF a jejich alternativy, datové formáty jako RGB565/555 nebo YUV/YCbCr(4:2:2) atd. ==== Zapojení přípravku==== Obecně lze tedy říct, že výše popsaný přípravek se skládá pouze ze dvou dílčích částí a to z použité vývojové desky 32F429IDISCOVERY\\ a jejich periférií a modulu použité kamery OV7670. Tento fakt je dán skutečností, že právě vývojová deska a MCU obecně jsou velmi univerzální záležitostí, která nabízí komplexní podporu v libovolné aplikaci. Zjednodušené blokové schéma přípravku s použitými periferiemi lze přehledně vidět na Obr. 3 níže. {{:2014:cam-ov7670:blokovy_digram_hw2.png?500|Obr. 3: Blokové schéma zapojení přípravku}} Zapojení jednotlivých použitých sběrnic přípravku bylo zvoleno podle obecné dostupnosti general-purpose input/output (GPIO) pinů\\ a na základě HW zapojení vývojové desky. Výsledné zapojení jednotlivých GPIO pinů tedy vypadá následovně: ^ DCMI sběrnice ^^^^ | //PB7// | VSYNC | //PB6// | D5 | | //PA4// | HREF | //PE4// | D4 | | //PA6// | PCLK | //PC9// | D3 | | //PA8// | XCLK | //PC8// | D2 | | //PE6// | D7 | //PC7// | D1 | | //PE5// | D6 | //PC6// | D0 | ^ SCCB sběrnice ^^ | //PB8// | SCL (SIOC) | | //PB9// | SDA (SIOD) | ^ SPI sběrnice ^^ | //PF7// | SCK | | //PF8// | MISO | | //PF9// | MOSI | Modul kamery je napájen přímo z použité vývojové desky, kterou je možné napájet standardně z universal serial bus (USB) portu PC nebo např. pomocí baterie v rozsahu 3-5V na pinech VDD a GND. Reálné zapojení přípravku bez napájení je možné vidět na Obr. 4 níže. {{:2014:cam-ov7670:prototyp_photo.png?600|Obr. 4: Fotografie reálného zapojení přípravku}} ===== Firmware ===== ==== Ovládací jádro ==== Samotné jádro firmwaru (FW) je koncipováno jako jednoduchá nekonečná smyčka s blokujícím programovým obsahem společně s obsluhou použitých přerušení. Blokující obsah představuje např. standardní obsluhu FW nebo zobrazování na displej. Jako přerušení jsou použity interrupt request (IRQ) kanály pro čítače/časovače (TIM), které zajišťují obsluhu uživatelského tlačítka a odpočítávání a DMA řadič pro indikaci kompletního přenosu mezi DCMI sběrnicí a pamětí. Jako obrazový formát snímků byl zvolen barevný model RGB565 a rozlišení 320x240 px (QVGA), který je podporován kamerou i použitým displejem. Zjednodušenou funkci FW přípravku ilustruje vývojový diagram na Obr. 5 níže. {{:2014:cam-ov7670:vyvojovy_diagram_sw.png?650|Obr. 5: Vývojový diagram jádra obslužného FW}} ==== DCMI sběrnice a DMA přenos ==== Nyní se zaměříme na konfiguraci DCMI sběrnice s DMA přenosem. V úvodu je dobré zmínit, že v případě DCMI sběrnice se jedná o paralelní rozhraní, takže můžeme předvídat použití většího množství synchronizačních signálů. Po přivedení hodinového signálu na pin XCLK začne použitá kamera ihned generovat zmínené synchronizační signály PCLK, HREF a VSYNC a datové signály D0 - D7. Studiem sběrnice zjistíme, že signál HREF představuje horizontální synchronizaci snímku, kde úroveň logické jedničky (H) tohoto signálu představuje právě jeden řádek snímaného obrazu a signál VSYNC představuje vertikální synchronizaci snímku, kde úroveň logické nuly (L) představuje příjem právě jednoho snímku. Nutné ovšem zmínit, že konfigurace DCMI z hlediska programového kódu vyžaduje nastavení těchto signálů v invertované podobě. Výsledný programový kód pro konfiguraci DCMI sběrnici poté může vypadat následovně: // DCMI config DCMI_DeInit(); DCMI_InitStructure.DCMI_CaptureMode = DCMI_CaptureMode_Continuous; DCMI_InitStructure.DCMI_ExtendedDataMode = DCMI_ExtendedDataMode_8b; DCMI_InitStructure.DCMI_CaptureRate = DCMI_CaptureRate_All_Frame; DCMI_InitStructure.DCMI_PCKPolarity = DCMI_PCKPolarity_Rising; DCMI_InitStructure.DCMI_HSPolarity = DCMI_HSPolarity_Low; DCMI_InitStructure.DCMI_VSPolarity = DCMI_VSPolarity_High; DCMI_InitStructure.DCMI_SynchroMode = DCMI_SynchroMode_Hardware; DCMI_Init(&DCMI_InitStructure); DCMI_Cmd(ENABLE); Nyní můžeme přejít ke konfiguraci DMA kanálu. Pro DCMI sběrnici byl vybrán řadič DMA2, tok stream 1 a kanál 1. Hlavními parametry této konfigurace bude tedy zvolení správného DMA kanálu, nastavení přenosu mezi periférií a pamětí, zdrojová adresa datového toku (datový registr DCMI), koncová adresa paměti kam mají být data ukládána aj. Užitečnou funkcí DMA řadiče je automatizovaný převod mezi datovými typy (Word, HalfWord, Byte). V našem případě budeme převádět z 32b datového registru DCMI na 16b prostor v paměti (podle zvoleného formátu RGB565). Výsledný programový kód pro DMA přenos poté může vypadat následovně: // DMA config DMA_DeInit(DMA2_Stream1); DMA_InitStructure.DMA_Channel = DMA_Channel_1; DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)(&DCMI->DR); DMA_InitStructure.DMA_Memory0BaseAddr = (uint32_t)frame_buffer; DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralToMemory; DMA_InitStructure.DMA_BufferSize = (IMG_ROWS*IMG_COLUMNS)/2; DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Word; DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord; DMA_InitStructure.DMA_Mode = DMA_Mode_Circular; DMA_InitStructure.DMA_Priority = DMA_Priority_High; DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Enable; DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_Full; DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single; DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single; DMA_Init(DMA2_Stream1, &DMA_InitStructure); DMA_Cmd(DMA2_Stream1, ENABLE); ==== SCCB sběrnice ==== Dále se seznámíme s problematikou SCCB sběrnice. Jedná se o speciální sběrnici používanou právě firmou //OmniVision// pro konfiguraci interních registrů použité kamery, což znamená, že ve výchozím stavu není tato sběrnice MCU podporována. Nicméně pokud se blíže podíváme do specifikací sběrnice SCCB, tak zjistíme, že tato sběrnice se velmi podobá standardně používané inter-integrated circuit sběrnici (I2C). Obecně tedy můžeme říct, že sběrnici SCCB bude možné emulovat právě standardem I2C. Důležitým poznatkem je, že budeme používat tzv. dvouvodičovou verzi pro komunikaci jako master-slave a pokud budou dodrženy specifikace SCCB sběrnice, tak dosáhneme úpravy požadovaného registru. Základním požadavkem je dodržení tří (pro čtení pouze první dvě) komunikačních fází sběrnice a to odeslání ID adresy pro identifikaci zápisu (0x42) nebo čtení (0x43), samotnou adresu požadovaného registru, který chceme modifikovat a hodnotu registru (pouze pro zápis). Výsledný programový kód pro konfiguraci I2C poté může vypadat následovně: // I2C config I2C_DeInit(I2C1); I2C_InitStructure.I2C_Mode = I2C_Mode_I2C; I2C_InitStructure.I2C_DutyCycle = I2C_DutyCycle_2; I2C_InitStructure.I2C_OwnAddress1 = 0x00; I2C_InitStructure.I2C_Ack = I2C_Ack_Enable; I2C_InitStructure.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit; I2C_InitStructure.I2C_ClockSpeed = 100000; I2C_ITConfig(I2C1, I2C_IT_ERR, ENABLE); I2C_Init(I2C1,&I2C_InitStructure); I2C_Cmd(I2C1, ENABLE); Následně již přímo emulovaná sběrnice SCCB může vypadat takto: bool SCCB_write_reg(uint8_t reg_addr, uint8_t* data){ uint32_t timeout = 0x7FFFFF; while(I2C_GetFlagStatus(I2C1, I2C_FLAG_BUSY)){ if ((timeout--) == 0){ return true; } } // Send start bit I2C_GenerateSTART(I2C1, ENABLE); while( !I2C_CheckEvent(I2C1,I2C_EVENT_MASTER_MODE_SELECT)){ if ((timeout--) == 0){ return true; } } // Send slave address (camera write address) I2C_Send7bitAddress(I2C1, OV7670_WRITE_ADDR, I2C_Direction_Transmitter); while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED)){ if ((timeout--) == 0){ return true; } } // Send register address I2C_SendData(I2C1, reg_addr); while(!I2C_CheckEvent(I2C1,I2C_EVENT_MASTER_BYTE_TRANSMITTED)){ if ((timeout--) == 0){ return true; } } // Send new register value I2C_SendData(I2C1, *data); while(!I2C_CheckEvent(I2C1,I2C_EVENT_MASTER_BYTE_TRANSMITTED)){ if ((timeout--) == 0){ return true; } } // Send stop bit I2C_GenerateSTOP(I2C1, ENABLE); return false; } ==== Zobrazení na displej ==== Samotné zobrazování na displej, jak již bylo naznačeno, je provedeno pomocí SPI sběrnice, která komunikuje s řadičem ILI9341 displeje vývojové desky. Samotná inicializace displeje je mírně obtížnější, nicméně lze říct, že se jedná o jednoduchou modifikaci oficiálních knihoven //STMicroelectronics//, které lze nalézt společně se specifikacemi použité vývojové desky. Obecně tedy programový kód pro konfiguraci SPI\\ poté může vypadat následovně: //SPI config SPI_StructInit(&SPI_InitStruct); SPI_InitStruct.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_2; SPI_InitStruct.SPI_DataSize = SPI_DataSize_8b; SPI_InitStruct.SPI_Direction = SPI_Direction_2Lines_FullDuplex; SPI_InitStruct.SPI_FirstBit = SPI_FirstBit_MSB; SPI_InitStruct.SPI_Mode = SPI_Mode_Master; SPI_InitStruct.SPI_CPOL = SPI_CPOL_Low; SPI_InitStruct.SPI_CPHA = SPI_CPHA_1Edge; SPI_InitStruct.SPI_NSS = SPI_NSS_Soft; SPI_Init(SPI5, &SPI_InitStruct); SPI_Cmd(SPI5, ENABLE); V případě korektní inicializace displeje poté můžeme standardně nechat vypisovat text, kreslit tvary apod., nicméně v našem případě\\ je nejdůležitější vykreslení získaného snímku z paměti. Tato funkce z hlediska programového kódu poté může vypadat následovně: void LCD_ILI9341_DisplayImage(uint16_t image[ILI9341_PIXEL]) { uint32_t n, i, j; LCD_ILI9341_SetCursorPosition(0, 0, ILI9341_Opts.width - 1, ILI9341_Opts.height - 1); LCD_ILI9341_SendCommand(ILI9341_GRAM); ILI9341_WRX_SET; ILI9341_CS_RESET; for (n = 0; n < ILI9341_PIXEL; n++) { i = image[n] >> 8; j = image[n] & 0xFF; LCD_SPI_Send(ILI9341_SPI, i); LCD_SPI_Send(ILI9341_SPI, j); } ILI9341_CS_SET; } Zde můžeme vidět, že samotné zobrazení je provedeno jako blokující, tedy že data snímku jsou postupně odesílána pixel po pixelu pomocí SPI sběrnice na displej. Konkrétně tedy bajt po bajtu, kde 2 B představují jeden pixel snímku. Takto dosáhneme obnovovací frekvence cca 15fps. ===== Realizace a testování ===== ==== Konfigurace kamery ==== Před demonstrací výsledků je nutné se zaměřit na problematiku konfigurace interních registrů modulu kamery pro dosažení požadované funkce. Komplikací této konfigurace je fakt, že kvalita oficiální dokumentace je velmi neuspokojivá a často lze zde nalézt i informace nepravdivé. Obecně lze tedy říct, že požadovanou funkci je možné pouze experimentálně otestovat. Nicméně dobrým základem může být volně dostupná [[http://lxr.free-electrons.com/source/drivers/media/i2c/ov7670.c|uživatelská knihovna OV7670]] nebo fórum vývojové komunity [[http://developer.mbed.org/|ARM mbed]]. Příklad základní úpravy registrů může poté vypadat následovně: {0x12, 0x80}, //Reset registers // Image format {0x12, 0x14}, //QVGA size, RGB mode {0x40, 0xd0}, //RGB565 {0xb0, 0x84}, //Color mode // Hardware window {0x11, 0x01}, //PCLK settings, 15fps {0x32, 0x80}, //HREF {0x17, 0x17}, //HSTART {0x18, 0x05}, //HSTOP {0x03, 0x0a}, //VREF {0x19, 0x02}, //VSTART {0x1a, 0x7a}, //VSTOP // Scalling numbers {0x70, 0x3a}, //X_SCALING {0x71, 0x35}, //Y_SCALING {0x72, 0x11}, //DCW_SCALING {0x73, 0xf0}, //PCLK_DIV_SCALING {0xa2, 0x02}, //PCLK_DELAY_SCALING Zde můžeme vidět např. resetování všech registrů do výchozího stavu nebo nastavení synchronizačních signálů pro zajištění korektní synchronizace na základě zvoleného formátu (QVGA, RGB565) jednotlivých snímků. Kompletní nastavení kamery obsahuje úpravu celkem\\ 122 konfiguračních registrů, které je možné nalézt zde {{:2014:cam-ov7670:ov7670_settings.zip| Kompletní konfigurace kamery}}. ==== Dosažené výsledky ==== Nyní můžeme přejít přímo k demonstraci dosažených výsledků, tedy zobrazení získaných snímků. Pro názornost zde můžeme nalézt více nastavení modulu kamery pro demonstraci vlivů jednotlivých nastavení na kvalitu výsledného snímku. Jedná se o čistá obrazová data, která byla získána pomocí komunikace vývojové desky ve virtual COM port (VCP) módu se softwarem (SW) //MATLAB// na PC. Tato komunikace\\ je opět rozsáhlejší problematikou a je nad rámec tohoto projektu, takže je záměrně vynechána. Nicméně lze říct, že je opět založena\\ na oficiální předloze USB VCP. Získané obrazové výsledky lze tedy vidět na Obr. 6 až Obr. 9 níže. {{:2014:cam-ov7670:image_default.png | Obr. 6: Získaný snímek pro výchozí konfiguraci kamery}} {{:2014:cam-ov7670:image_no_AWB_AGC_AEC.png| Obr. 7: Získaný snímek pro kompletní konfiguraci (mimo AGC, AEC a AWB)}} {{:2014:cam-ov7670:image_no_0xb0.png | Obr. 8: Získaný snímek pro kompletní konfiguraci (mimo registru 0xb0)}} {{:2014:cam-ov7670:image_full.png| Obr. 9: Získaný snímek pro kompletní konfiguraci}} Video demonstrující funkci přípravku v kontinuálním režimu kamery lze vidět níže, nicméně se jedná o velmi špatnou nahrávku, kde zářící displej vývojového kitu činí obraz velmi nekvalitním. Proto je lepší kvalitu jednotlivých snímků posuzovat podle obrázků uvedených výše. {{youtube>REiaTWDaZpU?large}} ===== Závěr ===== Na této stránce byla postupně nastíněna problematika týkající propojení vývojové desky 32F429IDISCOVERY a modulu kamery OV7670 pomocí DCMI sběrnice. Modul kamery byl nakonfigurován do požadovaného stavu pomocí SCCB sběrnice a následně byla získávána obrazová data pomocí výše zmíněné DCMI sběrnice v kontinuálním nebo jedno-snímkovém režimu. Tyto obrazová data byla poté pomocí DMA řadiče přesouvána do paměti MCU, odkud následně byla jednoduše vizualizována na displej vývojové desky pomocí SPI sběrnice. Tímto způsobem byla úspěšně otestována a demonstrována funkčnost propojení použitého modulu kamery a vývojové desky. Tento popis tedy představuje solidní základovou platformu pro další vývoj programového kódu a realizaci libovolné aplikace v praxi. Celý programový kód včetně projektu\\ v prostředí //Keil MDK-ARM uVision5// lze nalézt zde {{:2014:cam-ov7670:mpoa_keil_projekt.zip| MPOA Keil projekt}}.