Individální projekty MPOA

Mikroprocesory s architekturou ARM

Uživatelské nástroje

Nástroje pro tento web


2014:cam-ov7670

Kamera OV7670 se STM periferií DCMI

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.

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.

Obr. 2:  Fotografie kamery OV7670

Tato kamera standardně disponuje kvalitním CMOS senzorem schopným zaznamenávat rozlišení až 640×480 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.

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.

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í 320×240 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.

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á uživatelská knihovna OV7670 nebo fórum vývojové komunity 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 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.

 Obr. 6: Získaný snímek pro výchozí konfiguraci kamery

 Obr. 7: Získaný snímek pro kompletní konfiguraci (mimo AGC, AEC a AWB)

 Obr. 8: Získaný snímek pro kompletní konfiguraci (mimo registru 0xb0)

 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.

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 MPOA Keil projekt.

2014/cam-ov7670.txt · Poslední úprava: 2015/01/18 14:14 autor: Petr Machala