Novinka:Přenos obrazových dat z kamery OV7670 pomocí STM32F429 DISCO do PC
(Kategorie: Konstrukce)
Napsal Zbyna
14.02.2017 00:00

Chtěl bych popsat, jak jsem postupoval při získávání obrazových dat do počítače a jejich zobrazení na monitoru a pro jejich další libovolné zpracování.
Jako základní zadání jsem si určil získat obraz v rozumné kvalitě zobrazený na monitoru počítače nebo ukládaný na nějaké paměťové médium ve formátu, se kterým je snadné dále pracovat. Přenos dat by měl probíhat, pokud možno bezdrátově a data by měla být získávána s co nejvyšší frekvencí (alespoň několik snímků za vteřinu při rozlišení VGA).

Použitý hardware.
Rozhodl jsem se použít kit STM32F429I-DISCO a to z několika důvodů, především byl k dispozici, disponuje integrovaným displejem, a hlavně má dostatek externí paměti pro ukládání obrazových dat a pro případnou manipulaci s nimi a disponuje rozhraním DCMI pro komunikaci s kamerou. Kameru jsem zvolil OV7670 vzhledem k dostatečnému počtu příkladů na internetu a pro bezdrátový přenos dat oblíbený modul pro Bluetooth HC-05 a pro přenos pomocí Wi-Fi rovněž levný modul ESP 8266 a některé další.

Použitý software.
Pro vývoj programového kódu a další podpůrné funkce jsem použil vývojové prostředí Keil uVision5 (Lite verze, max. velikost kódu 32kB), EMBitz 1.0, Visual Studio 2013 Express Edition C++ , knihovny pro podporu práce s USB LibUsb a pro práci s grafikou SDL, TeraTerm, dále STM CDC serial driver a další.

1.
Připojíme kameru OV7670 ke kitu STM32F429I-DISCO a zprovozníme komunikaci pomocí sběrnice SCCB, načteme data konfiguračních registrů kamery (např. ID kamery) a provedeme zápis dat do registrů kamery a zašleme si informaci o výsledku přes sériovou linku do PC.


Potřebný hardware
Kameru OV7670 nabízejí například zde [22], datasheet je ke stažení například zde [7]. Kit STM429I-DISCO je dostupný například zde [23] a datasheet je ke stažení například zde [6]. Převodník RS232 TTL na USB je dostupný například zde [22].

Stručný popis kamery OmniVision OV7670
Kamera je vybavena CMOS senzorem s maximálním rozlišením 640×480 pixelů (VGA, 0.3 Mpx) při obnovovací frekvenci do 30 fps a dalšími integrovanými periferiemi, které zajištují předzpracování obrazu a komunikaci s okolím (podrobněji viz datasheet [7]).
Kamera zaznamenává obraz v maximálním rozlišení VGA. Na základě nastavení kamery je obraz zpracován pomocí interního DSP a teprve pak je odeslán přes DCMI sběrnici do připojeného externího zařízení.
Konfigurace kamery je prováděna pomocí SCCB sběrnice. Kamera na základě nastavení podporuje rozlišení, jako již zmíněné VGA nebo CIF, případně další různé alternativy, dále také různé formáty obrazových dat jako je například RGB565/555 nebo YUV/YCbCr(4:2:2) a další.
Režimy kamery a její další nastavení je tedy možné nastavit a měnit pomocí sady konfiguračních registrů, popsaných v datasheetu [7].

Stručný popis STM32F429I-DISCO kitu
Vývojová deska je osazena výkonným 32bitovým MCU architektury ARM Cortex-M4 s označením STM32F429ZIT6. Tento MCU disponuje značným množstvím periférií, jako jsou komunikační sběrnice I2C, SPI, USART, USB, DCMI, dále desítkami procesorových instrukcí atd. Z hlediska vybavení vývojové desky zde můžeme nalézt periferie, jako tříosý MEMS pohybový senzor, 3,2“ RGB TFT LCD displej s rozlišením QVGA, paměť SDRAM 64MBit nebo standardní ovládací mikrotlačítka USER a RESET a několik LED diod. Podrobnější popis viz třeba zde [2], [6].


Stručný popis převodníku RS232 TTL na USB
Libovolný typ zakoupený například zde[22].

Úvod
Zapojení se skládá z STM32F429I-DISCO kitu, kamery OV7670 a převodníku RS232TLL-USB a počítače. Cílem je zobrazit data snímaná kamerou na monitoru počítače a na displeji kitu.

Postupně popíšu, jak propojíme a zkonfigurujeme obvod pro generování hodinového signálu XCLK, sériovou linku USART1 pro vysílání a příjem dat z/do PC, sběrnici I2C pro emulaci SCCB a konfiguraci kamery OV7670, rozhraní DCMI pro přenos obrazových dat z kamery OV7670 a rozhraní SPI pro komunikaci s integrovaným displejem na kitu.
Dále se rovněž budeme zabývat konfigurací řadiče FMC a paměti SDRAM umístěné na kitu, kterou použijeme pro uložení obrazových dat. Poté se zaměříme na možnosti přenosu dat do PC a to zprovozněním komunikace přes USB a i s bezdrátovým přenosem dat pomocí Wi-Fi a Bluetoothu.
Dále si popíšeme naprogramování jednoduché aplikace pro PC, která umožní přijmout data z kitu přes sériovou linku, USB, Wi-Fi, atd. a zobrazit je v samostatné okně na obrazovce monitoru, případně uložit na pevný disk počítače.

Pro prvotní oživení celého systému si zvolíme následující parametry, které jsou výhodné tím, že je podporuje přímo displej integrovaný v kitu a okamžitě tedy uvidíme kamerou snímaný obraz na displeji kitu.
Rozlišení QVGA 320x240pixelů, formát obrazových dat RGB 565 a obnovovací frekvenci 7,5 fps, obraz z kamery zobrazíme pro kontrolu na displeji kitu. Interní pamět MCU má velikost 256kB, takže se nám do ní obrázek bez problémů vejde (QVGA 320x240x2 = 154kB).


Popis konektoru kamery OV7670

Použitá kamera OV7670 má konektor s 9x2 piny s následujícím významem dle [32].




Napájení napětí pro kameru
Kamera je napájena přímo z kitu z pinů 3V a GND.


Generování hodinového signálu XCLK pro kameru OV7670

Zapojení XCLK

Použitá kamera vyžaduje přivedení hodinového signálu o frekvenci 10 - 40MHz na pin XCLK. Ten zajistíme zkonfigurováním pinu PA8 jako výstupu MCO následující programovou sekvencí.

void MCO1Init(void)
{
        GPIO_InitTypeDef GPIO_InitStructure;
        RCC_ClockSecuritySystemCmd(ENABLE);
        RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);

        // GPIO config
        GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8;               //PA8 - XCLK
        GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
        GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
        GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
        GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;  
        GPIO_Init(GPIOA, &GPIO_InitStructure);
       
        // GPIO AF config
        GPIO_PinAFConfig(GPIOA, GPIO_PinSource8, GPIO_AF_MCO);
       
        // MCO clock source
        RCC_MCO1Config(RCC_MCO1Source_PLLCLK, RCC_MCO1Div_4);   //_2,4
}
 

Po přivedení hodinového signálu na pin XCLK začne použitá kamera vytvářet synchronizační signály PCLK, HREF a VSYNC a datové signály D0 – D7.

Přenos dat a stavových zpráv do PC pomocí rozhraní RS232 TTL
Konfigurace sériového rozhraní

Pro přenos obrazových dat a stavových zpráv z kitu do počítače použijeme pro jednoduchost standardní sériovou linku. Její konfigurace je velmi jednoduchá jak na straně kitu, tak i na straně PC. Nevýhodou je rychlost přenosu, která bude dosahovat cca 90kByte/sec při nastavené rychlosti sériové linky 921 600Baudů. V rozlišení QVGA 320x240px. a datovém formátu obrazu RGB 565 je nutné přenést 154kB (320x240x2Byte) na jeden obrázek, takže přenos jednoho snímku bude trvat asi dvě vteřiny.
Nejdříve si zprovozníme zasílání stavových zpráv na terminál PC.
Pomocí funkce UART4Init(115200); zkonfigurujeme I/O piny pro UART4, nastavíme rozhraní, dále nastavíme funkci obsluhy přerušení void UART4_IRQHandler(void), přesměrujeme standartní výstup na rozhraní UART4 a celou komunikaci vyzkoušíme. Podrobný popis nastavení sériového rozhraní pro procesory ST32F4x je například zde [1].


Obecně známý programový kód pro přesměrování standartního výstupu pomocí printf na např. USART1(UART4).

/**
* @brief  Retargets the C library printf function to the i.e. USART. Gnu gcc
* @param  None
* @retval None
*/

int _write(int file, char *ptr, int len)
{
  int n;
  int ch;
  uint16_t timeout = 0xffff;

  for (n = 0; n < len; n++)
  {
    /* write a characters to the USART */
    ch = *ptr++ & (uint8_t)0xFF;
    //print2LCD(ch);
    while ((USART_GetFlagStatus(UART4, USART_FLAG_TC) == RESET) && (timeout--));
    USART_SendData(UART4, ch);
  }
  return len;
}
 


/**
* @brief  Retargets the C library printf function to the i.e. USART. Keil uVision
* @param  None
* @retval None
*/

int fputc(int ch, FILE *f)
{
  uint16_t timeout = 0xffff;
  /* write a character to the USART */
  while ((USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET) && (timeout--));
  USART_SendData(USART1, (uint8_t)ch);
  //VCP_DataTx((uint8_t*)&ch,1);
  return ch;
}
 


Otevřeme na PC libovolný terminálový program třeba TeraTerm [20] a zadáme konfigurační parametry sériového rozhraní a pošleme libovolný znak (např. „mezeru“). Terminál by nám měl vypsat „Received > >032“. Nyní můžeme přejít ke konfiguraci rozhraní SSCB (I2C).

Přesměrování pomocí funkce printf na USART4



Konfigurace SCCB (I2C) sběrnice
Dále zapojíme a zkonfigurujeme I2C sběrnici, kterou budeme emulovat rozhraní SCCB.

Zapojení SCCB (I2C) sběrnice

Jedná se o speciální sběrnici, kterou používá firma OmniVision pro konfiguraci interních registrů kamery OV7670. Výrobce však v datasheetu [7] deklaruje kompatibilitu se standartní sběrnicí I2C (Inter-Integrated Circuit) [25]. Můžeme tedy sběrnici SCCB emulovat právě standardem I2C. Použijeme dvouvodičovou verzi pro komunikaci jako master-slave a pokud budou dodrženy specifikace SCCB sběrnice, tak budeme schopni pomocí sběrnice I2C číst a zapisovat do interních registrů kamery.
Po sběrnici musíme zaslat následující informace, ID adresa pro identifikaci zápisu (0x42), pro čtení (0x43), samotnou adresu požadovaného registru, do kterého chceme zapisovat nebo číst a pro zápis i hodnotu registru. V prvním kroku z kamery přečteme její ID pro ověření správné funkce komunikace pomocí sběrnice I2C.
Konfiguraci a inicializace sběrnice SSBC(I2C) provedeme pomocí následující funkce void SCCBInit(void).

Dále naprogramujeme programovou sekvenci pro čtení a zápis na sběrnici I2C což nám bude emulovat sběrnici SSCB a pomocí volání funkcí provedeme zápis/čtení, do/z registrů kamery OV7670.
funkce int i2cSendData(uint8_t reg_addr, uint8_t data) pro zápis dat do registrů kamery
a funkce int i2cReadData(uint8_t reg_addr, uint8_t* data) pro čtení dat z registrů kamery

Obě funkce vrací v případě neúspěchu hodnotu, podle které se dá určit, ve kterém kroku došlo k jejich selhání. Nyní již můžeme provést inicializaci a zápis do registrů kamery voláním, funkce int OV7670Init(void). V této funkci je naprogramována jednoduchá vizualizace zápisu do registrů kamery a vlastní cyklus zápisu. Nejdříve provedeme reset celé kamery zápisem hodnoty 0x80 do registru 0x12, vřadíme zpoždění a pak si přečteme identifikační číslo kamery z vnitřních registrů a pošleme ho k zobrazení na terminál PC. Viz obrázek výše.
Podrobněji o problematice celé komunikace pojednává např. tento článek [32] nebo tento projekt [33].


2.
Zkonfigurujeme interní registry kamery, přeneseme obrazová data z kamery do paměti kitu a zobrazíme je na integrovaném displeji kitu STM32F429I-DISCO.

Dále si popíšeme připojení kamery OV7670 ke kitu pomocí sběrnice DCMI pro přenos obrazových dat.

Propojení Discovery kitu a kamery





Konfigurace DCMI sběrnice.
Po přivedení hodinového signálu na pin XCLK začne použitá kamera vytvářet synchronizační signály PCLK, HREF a VSYNC a datové signály D0 – D7.
Studiem průběhů v datasheetu [7] zjistíme, že signál HREF představuje horizontální synchronizaci snímku, kde úroveň H (logické jedničky) tohoto signálu představuje jeden řádek snímaného obrazu a signál VSYNC představuje vertikální synchronizaci snímku, kde úroveň L (logická nula) představuje příjem jednoho snímku. Pro správnou konfigurace DCMI je nezbytné nastavit tyto signály tak, aby byly invertovány. Celou konfiguraci DCMI provedeme voláním funkce void DCMIDMAInit(void)


Konfigurace DMA pro přenos dat.
Dále je nutné provést konfiguraci DMA kanálu pro přenos obrazových dat. Pro přenos použijeme řadič DMA2, stream 1. Nyní musíme tedy správně nastavit směr přenosu mezi periférií a pamětí, zdrojovou adresu datového toku z datového registru DCMI, adresu v paměti RAM, kam mají být data ukládána, prioritu a další potřebá nastavení. Obrazová data budeme ukládat do vlastní paměti CPU.
Další funkcí DMA řadiče, kterou s výhodou použijeme je automatický převod mezi datovými typy (32bit-Word,16bit-HalfWord, 8bit-Byte). V našem případě budeme převádět z 32b datového registru DCMI na 16b prostor v paměti RAM, což je dáno námi použitým formátem obrazových dat RGB565. Nastavení DMA přenosu je částí již zmiňované funkce void DCMIDMAInit(void).
Ještě napíšeme funkci pro obsluhu přerušení vyvolaného po skončení přenosu dat. V této funkci otestujeme příznak skončení přenosu dat DMA_IT_TCIF1, nastavíme globální proměnnou gFrameReady na hodnotu true. Tato proměnná nám později poslouží k zahájení přenosu dat na integrovaný displej.

/**
* @brief  This function handles DMA2_Stream1 global interrupt request.
* @param  None
* @retval None,
*/

void DMA2_Stream1_IRQHandler(void)
{
  // DMA complete
  if (DMA_GetITStatus(DMA2_Stream1, DMA_IT_TCIF1) != RESET)
  {
    DMA_ClearITPendingBit(DMA2_Stream1, DMA_IT_TCIF1);

    DMA_Cmd(DMA2_Stream1, ENABLE);
    gFrameReady = true;
    gCnt++;
    //dbgPrint("I:DMA >%d %dms\r\n",gCnt,gTime);
  }
}
 


Konfigurace SPI sběrnice pro ovládání displeje 3,2“ QVGA pomocí řadiče ILI9341
Konfigurace a zobrazení na integrovaném displeji kitu STM32F429I-DISCO
Zobrazování získaného obrázku z rozhraní DCMI pro kameru, provedeme na LCD displej pomocí SPI sběrnice, přes kterou MCU komunikuje s řadičem displeje ILI9341.
Samotný postup inicializace displeje vychází z oficiálních knihoven firmy ST a z publikovaných příkladů. Data pro zobrazení jsou zaslána řadiči displeje pomocí sběrnice SPI.

Programový kód pro přenos jednoho snímku vypadá následovně.

void LCD_ILI9341_DisplayImage(uint16_t image[ILI9341_PIXEL])
{
  uint32_t n;
  uint8_t  tmp;
  uint16_t data;
 
  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)
  {
    data = image[n];
    tmp =  data >> 8;                          
    LCD_SPI_Send(ILI9341_SPI, tmp);
    tmp = data;
    LCD_SPI_Send(ILI9341_SPI, tmp);
  }    
  ILI9341_CS_SET;
}
 

Celá knihovna pro řízení displeje je v podstatě převzata z [26], pouze je mírně upravena, a to především z důvodů rychlosti provádění.

Konfigurace kamery OV7670
Konfigurační parametry pro kameru pro rozlišení QVGA jsou převzaty z [32] a [33] a jsou uloženy v dvojrozměrném poli const uint8_t OV7670_regQVGA[OV7670_REG_NUM_QVGA][2]. Jak již bylo uvedeno dříve inicializaci kamery OV7670 provedeme voláním funkce int OV7670Init(void)

Hlavní programová smyčka vypadá nyní takto

  // Infinite program loop
  while (1)
  {
    if (true == gFrameReady)
    {
      STM_EVAL_LEDOn(LED4); //RED
      dbgPrint("I:Start send img to LCD >%dms.\r\n",gTime);
      LCD_ILI9341_DisplayImage((uint16_t*)CPURamBuffer);
      dbgPrint("I:End send img to LCD >%dms.\r\n",gTime);
      gFrameReady = false;
      STM_EVAL_LEDOff(LED4);
    }
  }
 

Dokola se testuje, zda je dokončen přenos obrazu a nastaven globální flag gFrameReady a pokud ano, tak se odešlou obrazová data z paměti přes SPI sběrnici na displej kitu.
Přenos dat je signalizován pomocí červené LED diody na kitu.
Na displeji kitu se zobrazí reálný obraz snímaný kamerou v rozlišení QVGA s obnovovací frekvencí několik fps.


3.
Přeneseme obrazová data z paměti kitu STM32F429I-DISCO do připojeného PC a zobrazíme je na monitoru.

Obrazová data jsou tedy uložena v interní paměti mikroprocesoru, zobrazují se na integrovaném displeji kitu a nyní je přeneseme do PC.
Kit k PC již máme připojený pomocí sériové linky, která byla dosud využita pro přenos stavových hlášení. Nyní její funkci rozšíříme na přenos obrazových dat z kitu do PC.
Na kitu je přidána funkce int VCP_SendImage(uint8_t *image), která má za úkol odeslat data uložená v paměti MCU do PC.

int VCP_SendImage(uint8_t *image)
{
  uint32_t i, timeout,counter = 20000;
  int j;
  uint16_t data,sum = 0;
  uint8_t pole[4] = {0};
  int err = 0;

#define WIDTH        480
#define HEIGHT       640

#define LEN_VCP      384
#define LEN_WIFI        2035
#define LEN_UART        1

#define LEN     LEN_UART

  gPtrJPEGImg = (uint8_t*) image;

  for (j = 0; j < 320*240*2; gPtrJPEGImg += LEN, j += LEN)
  {
    while ((USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET));
    USART_SendData(USART1, *gPtrJPEGImg); // pole[0]
  }
  return err;
}
 

Do obsluhy přerušení pro sériový port přidáme test na příznak startu přenosu a po jeho přijetí nastavíme globální požadavek gStartTransfer na přenos dat do PC. Pokud z PC zašleme znak „!“, kit odešle obrazová data do PC, kde se zobrazí v okně terminálu.


/**
* @brief  This function handles USARTx global interrupt request.
* @param  None
* @retval None
*/


void USART1_IRQHandler(void)
{
  if (USART_GetITStatus(USART1, USART_IT_RXNE) != RESET)
  {
    STM_EVAL_LEDToggle(LED3); //GREEN
    gChar = USART_ReceiveData(USART1);
    dbgPrint("I:USART1 Received >%c >%03d\r\n", gChar, (uint8_t)gChar);
    if (gChar == 33) gStartTransfer = true;   // "!" to start transfer
    if (gChar == 82) NVIC_SystemReset();      // "R" to Reset
  }
}
 


V hlavní smyčce programu je testován požadavek na odeslání dat a v případě požadavku na data z PC, a pokud jsou data připravena, jsou data odeslána do PC. Obrazová data jsou přenášena ve stejném formátu, v jakém jsou získána z kamery, tedy v RGB565.
Aby nedocházelo k přepisu obrazových dat během jejich přenosu do PC, je použita funkce DCMI_CaptureCmd(DISABLE), která zastaví načítání dat z kamery, a po skončení přenosu dat do PC je opět povolen.
Přenos dat není nijak zabezpečen ani kontrolován proti ztrátě dat nebo přerušení spojení.

Hlavní programová smyčka vypadá nyní takto

 // Infinite program loop
  while (1)
  {
    if (true == gFrameReady)
    {
      STM_EVAL_LEDOn(LED4); //RED
      dbgPrint("I:Start send img to LCD >%dms.\r\n",gTime);
      LCD_ILI9341_DisplayImage((uint16_t*)CPURamBuffer);
      dbgPrint("I:End send img to LCD >%dms.\r\n",gTime);

      STM_EVAL_LEDOff(LED4);

      if (true == gStartTransfer)
      {
        DCMI_CaptureCmd(DISABLE);
        VCP_SendImage(CPURamBuffer);
        DCMI_CaptureCmd(ENABLE);
        gStartTransfer = false;
      }
      gFrameReady = false;

    }
  }
 

V této fázi musíme buď použít dva převodníky z USB na RS232 (jeden pro přenos obrazových dat a druhý pro ladící výpisy) anebo použít pouze jeden pro přenos dat a zrušit přesměrování funkce printf na USART.
Celou komunikaci vyzkoušíme pomocí terminálu na PC, kde po odeslání znaku „!“, by měla na terminál dorazit obrazová data z kitu.

Aplikace na straně PC
Aplikace je napsána v C/C++ za pomoci vývojového nástroje firmy Microsoft Visual Studio 2013 Express Edition v OS Windows 7.
Aplikace se sestává ze dvou hlavních funkcí. První funkce DWORD WINAPI readCOM(LPVOID) má za úkol komunikovat s 429I-Disco kitem a druhá funkce drawData () se stará o zobrazení přijatých dat na monitor počítače.
Nejprve vytvoříme grafické okno, kde budeme zobrazovat data a do konzolového okna programu zapisujeme různá stavová hlášení a případné chyby.
Komunikační část programu provede otevření sériového portu pro komunikaci, nastavení parametrů portu, a pokud vše proběhne bez chyb tak zašle požadavek na zahájení přenosu obrazových dat z kitu.
Po přijetí dat se zobrazí prvních 30 bajtů ve znakovém a číselném formátu a pokračuje se druhou částí programu, která se stará o zobrazení dat. Obrazová data jsou po přijmutí, ukládána do statického globálního pole o potřebné velikosti (QVGA RGB565 tzn. 320x240x2Byte = 154kB).
Pro obsluhu sériové linky jsem použil kód publikovaný zde [34]. Z něj použijeme pouze několik funkcí a to


int RS232_OpenComport(int, int, const char *); k otevření příslušného sériového portu.
int RS232_PollComport(int, unsigned char *, int); pro příjem dat z kitu
void RS232_SendByte(int, unsigned char); pro zaslání požadavku na data
void RS232_CloseComport(int); pro uzavření portu při ukončení programu
 


Programový kód pro zápis dat vypadá takto

//RS232 write data
  RS232_SendByte(comPortNr, PC_REQ_DATA);
 


Programový kód pro čtení dat vypadá takto

//RS232 read data
  for (j = 0; j < 320*240*2; j++)    //RGB565 2*
    cnt += RS232_PollComport(comPortNr, &gPole[j], 1);
 


po skončení cyklu je zavolána funkce drawData(uint8_t gPole[]), která vykreslí přijatá data do okna.
Po vykreslení dat se celá smyčka pro příjem dat opakuje.
Pozn. V použité knihovně jsou sériové porty číslovány od nuly na rozdíl od zvyklostí ve Windows.

Zobrazovací část programu po zavolání čte data z globálního pole, provede převod z formátu RGB565 na RGB888 a zobrazí data pomocí funkce Windows SetPixel(hdc, x, y, RGB(r,g,b)); v grafickém okně na monitoru.

Tělo funkce drawData(uint8_t gPole[]) vypadá takto

  for (i = 0; i < (240); i++)   // v2 600msec
  {
    startLoop = (2 * 320* i);
    stopLoop = (2 * 320* i) + (2 * 320);    //RGB565 *2, RGB888 *3

    for (j = startLoop; j < stopLoop;)
    {
      //RGB565 to RGB888
      tmp = (gPole[j+1] << 8 ) + gPole[j];   //MSB LSB in STM32F429i
      r = (uint8_t)(((tmp & 0xF800) >> 8 ));   //8
      g = (uint8_t)(((tmp & 0x07E0) >> 3));   //3
      b = (uint8_t)((tmp & 0x001F) << 3);   //3

      //RGB 888
      //r = gImgRGB[j];
      //g = gImgRGB[j + 1];
      //b = gImgRGB[j + 2];

      //Draw pixels via win32 API
      SetPixel(hdc, x, y, RGB(r,g,b));  //RGB888

      //DrawPixel(gScreen, x, y, r, g, b);     //RGB888 via SDL
      //++x;
      ++x;
      ++j;
      ++j;
      //++j;  //RGB888
    }
    x = 0;
    y++;
  }
 


Potom se předá řízení opět komunikační části programu, která pošle požadavek na data z kitu, a celý cyklus se opakuje.
Řízení komunikace je velmi jednoduché a navazuje na sebe pouze časově, takže se může stát, že program uvázne při čtení dat, protože očekává data od kitu, která se mohla ztratit při přenosu. Obě funkce jsou spouštěny jako samostatná vlákna.
Pokud by docházelo často k výpadkům komunikace, nezbyde nic jiného, než snížit přenosovou rychlost (nejdříve zkrátíme vodiče od kitu k převodníku).
Aby zobrazení na monitoru bylo plynulé, měly by obě části programu probíhat co nejrychleji.
Funkčnost zapojení poznáme podle zobrazení reálného obrazu na displeji kitu a na monitoru počítače, jak je patrné z přiloženého obrázku.

Nyní je možné v PC provést uložení obrázku na disk pro další možné zpracování obrazových dat. V této fázi je asi nejednoduší provést uložení do některého z formátů bez komprimace např. PPM nebo BMP. V případě zvolení formátu PPM a jeho textové verze stačí pouze otevřít soubor pro zápis a poté zapsat následujícím kódem


Vložit hlavičku souboru
P3
# Created by xy
320 240
255
224 136 122 224 136 122 224 136 124 …………………..

fprintf(fout, "%d %d %d ", r, g, b);
 


4.
Zhodnotíme dosažené výsledky, najdeme slabá místa celého řešení a pokusíme se je zlepšit a upravit a zrychlit.

Nyní máme obrazová data z kamery zobrazena na monitoru PC, takže z funkčního hlediska vše funguje tak jak má a hlavního cíle bylo dosaženo.
Pokud však provedeme měření rychlosti obnovení snímku na monitoru pak, zjistíme, že je přibližně 0,4fps (2,5sec/snímek) z čehož načítání dat z kitu trvá cca 1.8sec a jejich vykreslení dalších 0,8sec. Tím jsme identifikovali slabá místa, na která se je potřeba zaměřit při zlepšování rychlosti překreslování obrazu na PC.

Nejdříve se budeme zabývat rychlostí vykreslování na monitoru PC.
Jak jsem pochopil ze studia MSDN, řešení pouze ve WinAPI by bylo poměrně komplikované, a protože jsem se nechtěl příliš dlouho zabývat programováním na straně PC tak jsem zvolil alternativní řešení pomocí grafické knihovny SDL, které má navíc tu výhodu, že je multiplatformní. Stáhneme a naistalujeme starší verzi knihovny SDL 1.2.15. Doplníme cesty k příslušným hlavičkovým souborům a souboru *.lib napíšeme inicializaci okna a smyčku zpráv a zaměníme volání funkce setPixel() za DrawPixel() a vše ostatní zůstane stejné. Pouze se řádově zvýší rychlost vykreslování dat (z cca 800ms na 80ms pro rozlišení VGA viz dále). Program, který jsem napsal pro zobrazování na monitoru počítače pomocí knihovny SDL, je inspirován sérií článků o SDL na serveru root.cz viz [35].
Dalším krokem je přesunutí části programu, která čte data do samostatného vlákna, aby se během vykreslování dat již četla obrazová data z kitu. Je vhodné ve smyčce pro vykreslování obrazových dat odstranit volání funkce memset(), která maže přijatá data po jejich vykreslení.
V programu není přístup k datům mezi vlákny nijak synchronizován záměrně a v této aplikaci to není na závadu. Při tomto způsobu se rovněž při vykreslování na monitor mohou překrývat data ze dvou snímků. Pokud by to bylo na závadu, je asi nejjednodušší cestou použít dvě pole pro data obrázku a přepínat mezi nimi, tzn. do jedno se načítají data z kitu a druhé je vykreslováno. Po načtení dat se toto pole vykreslí a data se mezitím již načítají do druhého pole.
Pokud by byly na aplikaci kladeny vysoké nároky, je možné ještě změnit prioritu vlákna na čtení dat, aby ho souběh ostatních aplikací běžících na PC zbytečně nebrzdil.
Těmito kroky jsme dosáhli toho, že jedno programové vlákno neustále čte data z kitu a zapisuje je do paměti a druhé po jejich načtení nezávisle vykreslí obrazová data na monitoru. Na procesoru s dvěma a více jádry by se operační systém měl postarat o to, aby obě vlákna běžela skutečně paralelně. Nyní můžeme tedy konstatovat, že aplikace na straně PC nám běží v rámci možností maximální rychlostí.

Komunikace mezi STM32F429I-DISCO a aplikací na PC
Dalším úzkým místem je přenos dat do PC. Tuto problematiku můžeme rozdělit na dvě části, kterými se budeme postupně zabývat.
První možností je použít nestandartních rychlostí sériového portu. Touto možností jsem se dále nezabýval.
Elegantnějším řešením bude, komunikaci přes sériovou linku odstranit a použít komunikaci pouze přes rozhraní USB. Jednou ze základních tříd USB komunikace je emulace sériového rozhraní. Na straně PC pouze nainstalujeme ovladač od STM VCP driver [21] a změníme číslo sériového portu v programu.
Na straně kitu toho musíme změnit poněkud více. Přidat musíme inicializaci USB, třídu zařízení a vlastní obsluhu komunikace. Rovněž je nutné snížit frekvenci MCU ze 180MHz na 168MHz kvůli získání frekvence potřebné pro komunikaci přes USB. Kód pro komunikaci přes USB je převzat z příkladu, který dodává firma ST k STM32F429I-DISCO kitu.
Po uvedení komunikace přes VCP do provozu a po vyzkoušení, je rychlost přenosu dat spíše zklamáním, neboť dosahuje přibližně 300 kB/sec.
Další možností je použít „bulk transfer“ pro přenos dat přes USB. Zde bude nutné upravit program jak na straně kitu, tak na straně PC. Je použit a upraven příklad ze seriálu o STM32F4 Discovery publikovaného na mcu.cz. K odesílání dat slouží funkce USBD_VD_SendData(&USB_OTG_dev, data, LEN);
Na straně PC použijeme pro zprovoznění komunikace přes USB, volně šiřitelnou knihovnu libUSB a k ní přidané utility. Kit připojíme k PC pomocí „user“ USB konektoru, spustíme inf-wizard.exe dodaný s balíkem knihoven libUSB, najdeme si naše zařízení detekované programem a necháme si vygenerovat soubory ovladače pro naše zařízení. Tento instalační balíček s ovladači pak použijeme k nainstalování ovladačů pro naše zařízení do PC. Celý postup je poměrně jednoduchý a dokumentují jej následující obrázky. Kontrolu správného nainstalování ovladače provedeme pomocí programu testllibusb.exe.




Poté pomocí dokumentace dodané k libUSB naprogramujeme funkci pro komunikaci přes USB s kitem. Ještě jednou opakuji, že celá část komunikace pomocí „bulk transferu“přes USB je v podstatě převzata ze seriálu o STM32F4 Discovery kitu na mcu.cz [1]. Tímto postupem dosáhneme rychlosti přenosu obrazových dat mezi kitem a PC přes 700kB/s.
Tímto jsou možnosti pro urychlení přenosu obrazových dat asi vyčerpány. Jako nejlepší se tedy jeví možnost použít některou variantu přenosu dat přes USB. S tím jsou však především na straně MCU spojeny zvýšené nároky na programový kód, především na jeho velikost a tak jsem musel v této fázi portovat celý projekt do prostření EM:Bitz s integrovaným kompilátorem gcc, protože výsledná velikost generovaného kódu přesáhla 32kB, což je omezení lite verze KEIL uVision5.
Další možností je použít na straně kitu kompresní algoritmus, který by data komprimoval za běhu a výrazně zmenšil jejich přenášenou velikost, čímž se budu zabývat později.
Můžeme rovněž vypnout zobrazování na interním displeji kitu, nebo ještě lépe využít DMA přenos dat po sběrnici SPI.
Nejlepší variantou pro urychlení je, aby přenos dat z kamery probíhal přes DMA a vlastní procesor využíval veškerý výkon pro komunikaci s PC pomocí sběrnice USB. Z přiloženého obrázku jsou patrné dosažené parametry.




5.
Změníme rozlišení z QVGA na VGA a vyřešíme s tím spojené problémy.

Protože již máme funkční zapojení v rozlišení QVGA, byla provedena základní optimalizace ke zvýšení rychlosti, tak se dále zaměříme na změnu rozlišení kamery z QVGA na VGA (640x480, RGB565) a tím získáme podstatně kvalitnější obraz.

Se zvýšením rozlišení z QVGA na VGA jsou spojeny následující problémy:
- nároky na paměť pro uložení obrazu na straně DISCO kitu a PC
- 4x větší objem dat k přenosu na PC (QVGA RG565 cca 153kB, VGA RGB565 cca 614kB)
- rychlost vykreslování obrazových dat na monitoru PC
- možnost zobrazení dat na displeji kitu

Nyní se zaměříme na problematiku uložení obrazových dat v kitu. Pro rozlišení VGA a formátu dat RGB565 nám již nestačí vnitřní paměť MCU a musíme použít externí paměť SDRAM, která je umístěna na kitu.

Použití a konfigurace SDRAM 64MBit paměti
Pro uložení celého obrazu do paměti v rozlišení VGA 640x480px , formátu RGB 565 je velikost jednoho snímku 614kByte a na jeho uložení nám již nebude stačit paměť RAM mikroprocesoru tak musíme použít paměť 64MBit SDRAM integrovanou na kitu. Použitý mikroprocesor disponuje rozhraním FMC (flexible memory controler), které nám „připojení“ a komunikaci značně zjednoduší.
Počáteční adresa SDRAM paměti je definována jako SDRAM_BANK_ADDR (0xD0000000) a její celková velikost je definován konstantou IS42S16400J_SIZE = 0x400000.
Paměť rozdělíme na několik částí pro uložení obrazových dat už s ohledem na později prováděnou kompresi do formátu JPEG.

Rozdělení paměti SDRAM

// SDRAM  
#define SDRAM_IMG          (uint32_t)(SDRAM_BANK_ADDR)
#define SDRAM_IMG0         (uint32_t)(SDRAM_IMG + 0x25800)
#define SDRAM_IMG1         (uint32_t)(SDRAM_IMG0 + 0x25800)
#define0 SDRAM_IMG2         (uint32_t)(SDRAM_IMG1 + 0x25800)
#define SDRAM_IMG3         (uint32_t)(SDRAM_IMG2 + 0x25800)

#define SDRAM_RGB888       (uint32_t)(SDRAM_IMG3 + 0x25800)
#define SDRAM_JPEG         (uint32_t)(SDRAM_RGB888 + 921600)  /* 3Byte* VGA */
#define SDRAM_ARR          (uint32_t)(SDRAM_JPEG + 133000)

#define IMG_LENGHT         (153600)   /* 153600 230040 0x38298 0x543e4 */
#define IMG_LENGHT_RGB888  (230400)

#define IS42S16400J_SIZE             0x400000
 


Provedeme inicializaci paměti, odstraníme ochranu proti zápisu a poté můžeme paměť používat stejně jako by to byla interní paměť mikroprocesoru. Programový kód je převzat z příkladu, který lze stáhnout na stránkách výrobce kitu [30].

Programový kód pro nastavení SDRAM paměti je následující.
 
SDRAM_Init();         // SDRAM Initialization
 SDRAM_GPIOConfig();   //FMC SDRAM GPIOs Configuration

 FMC_SDRAMWriteProtectionConfig(FMC_Bank2_SDRAM, DISABLE); // Disable write protection
 FMC_SDRAMWriteProtectionConfig(FMC_Bank1_SDRAM, DISABLE);

 eraseSDRAM(0xA5); //Reset the SDRAM memory
 


K datům přistupujeme následovně čtení a zápis.

bool VCP_SendImage(uint16_t *image)
ptr = (uint8_t*)image;
status = USBD_VD_SendData(&USB_OTG_dev, ptr, LEN);  //bulk
 

ukazatel ptr ukazuje na data uložená v paměti SDRAM.

Nastavení rozlišení VGA na kameře OV7670
Nastavení rozlišení VGA na kameře OV7670 není vzhledem k nejednoznačnosti dokumentace triviální. Většina zdrojů na Internetu [32], [33] a jiné používá rozlišení QVGA nebo nižší. Funkční konfigurace pro VGA je tak spíše dílem experimentu než studování dokumentace. Přenos obrazu probíhá po čtyřech částech v řadě za sebou. Provedeme změny v konfiguračním souboru pro kameru OV7670 a to záměnou pole parametrů z QVGA na VGA, tzn. const uint8_t OV7670_regVGA[OV7670_REG_NUM_VGA][2].
SDRAM paměť mám rozdělenou na několik nepřekrývajících se částí, jak bylo uvedeno již dříve. Po signálu řadiče DMA o ukončení přenosu si v obsluze přerušení ve funkci void DMA2_Stream1_IRQHandler(void), zkopírujeme data pomocí fce memcpy32 na příslušné místo v paměti SDRAM a poté okamžitě čteme další část snímku.
Na straně PC pouze změníme velikost konstant, které udávají rozměry obrázku z QVGA na VGA, ve funkcích pro transfer změníme velikost přenášených dat.

Kopírování dat v paměti
O problematice rychlosti kopírování dat v paměti pro procesory ARM se lze dozvědět více na tomto odkazu [31]. Z výsledků vyplývá, že nejrychlejší je kopírování dat pomocí MCU bez použití DMA.
Pro kopírování dat v paměti jsem napsal funkci void* memcpy32(void* dst, void const* src, size_t len), která kopíruje data v paměti po 32bitech a má částečně rozvinutý cyklus.
Funkce není univerzální, velikost kopírovaného bloku musí být zarovnána na 16Bajtů.

void* memcpy32(void* dst, void const* src, size_t len)
{
  uint32_t* ldst = (uint32_t*)dst;
  uint32_t* lsrc = (uint32_t*)src;
  while (len >= 16)
  {
    *ldst++ = *lsrc++;
    *ldst++ = *lsrc++;
    *ldst++ = *lsrc++;
    *ldst++ = *lsrc++;
   
    len -= 16;
  }
  return (dst);
}
 

Celý program nyní probíhá tak, že se postupně a stále dokola v obsluze přerušení řadiče DMA kopírují jednotlivé části obrázku do paměti SDRAM a v případě požadavku na jejich přenos, jsou data odeslána do PC pomocí zvoleného druhu přenosu, a sice přes UART nebo přes USB.
Nyní jsem dosáhl zobrazení obrazu v rozlišení VGA na monitoru počítače. Na interní displej kitu je stále kopírována jedna ze snímaných části obrazu pro vizuální kontrolu funkčnosti kamery.

Takže závěrem této části lze konstatovat, že pro rozlišení VGA je již nezbytné používat k přenosu rozhraní USB a nejlépe „bulk“ transfer, který je schopen zajistit přenos požadovaného objemu dat v čase menším než 1 vteřina.
Rychlost vykreslování dat na monitoru PC pomocí knihoven SDL do okna s rozlišením VGA je na monitoru PC více než dostatečná a není ji třeba dále řešit.
Na displeji kitu je zobrazena jedna část přenášeného snímku z rozhraní DCMI, jehož rozlišení je 320x240px. To umožňuje jednoduchou kontrolu snímaného obrazu.
Uvedeným postupem je možné dosáhnou překreslení obrazu na monitoru PC rychlostí zhruba jeden snímek za vteřinu.


6.
Zkusíme přenos bez drátů. Budeme navíc potřebovat bezdrátový modul Wi-Fi ESP8266, alternativně Bluetooth modul HC-06, a vývojové prostředí ESPlorer, NodeMcu firmware ve verzi SDK 0.9.5 nebo 1.5, NodeMcu Flasher.

Zkusíme to bez drátů
Další možností je odstranění drátového propojení mezi kitem a PC a použít některou variantu bezdrátového připojení.
Pro bezdrátové připojení máme několik možností. Abychom však zůstali, v co nejnižší cenové relaci připadá v úvahu buď přenos pomocí Bluetooth nebo přes Wi-Fi.

Bluetooth modul HC-06
Tento způsob přenosu má nesmírnou výhodu v tom, že není třeba v aplikaci jak pro kit, tak pro PC nic měnit. Zapojíme modul na rozhraní sériového portu na kitu a na PC vyhledáme nové zařízení Bluetooth a nainstalujeme nový COM port a přesměrujeme na něj komunikaci. Hlavní nevýhodou, která činí toto řešení nepoužitelným je přenosová rychlost. Modul lze pomocí AT příkazů překonfigurovat, návod je k dispozici například zde [37]. Pokud bude modul komunikovat rychlostí 115 200Baudů, je to cca 10kB/s to znamená, že přenos jednoho obrázku v rozlišení QVGA (320x240 RGB565) bude trvat cca 15sec. Pokud nastavíme větší rychlost, tak začne docházet ke ztrátě dat při přenosu. Tuto variantu lze tedy použít jako řešení pro ověření funkčnosti bezdrátového připojení.

Wi-Fi modul ESP8266 (ESP-01)
Bezdrátovou komunikaci implementujeme pomocí WiFi modulu ESP 8266. Modul je možné zakoupit třeba zde [22] a další informace je možné najít zde [9] .
Kvůli ověření funkčnosti a jednoduchosti implementace byla pro začátek ke komunikaci mezi moduly použita sériová linka. Sériové rozhraní je možné naprogramovat na rychlost až 921 600Baudů. Připojení ke kitu provedeme tedy opět přes sériové rozhraní. Do modulu ESP8266 použijeme alternativní firmware NodeMcu [11]. Postup pro přehrání firmware je jednoduchý a je popsán například zde [10]. Použitý firmware je verze SDK v0.9.5 nebo novější. Poté je již možné programovat modul pomocí skriptovacího jazyka LUA [12]. Pro vývoj a naprogramování skriptu do WiFi modulu je výhodné použít vývojové prostředí ESPlorer [38]. Skript pro správnou funkci musí obsahovat připojení k bezdrátové síti, spuštění TCP serveru a callback funkce, pro TCP server a UART, které se postarají o přenos dat ze sériového rozhraní přes TCP do vzdálené stanice PC.
Celý skript vypadá například takto.

--s MT funguje 03.07., prijima i vysila, vyzkouset
--12.07 fungule s PC do rychlosti 19200

global_c = nil
SSID = "SSID"
PWD = "Pwd"

wifi.setmode(wifi.STATION)
wifi.sta.config(SSID,PWD)  
wifi.sta.autoconnect (1)
   
-- Hang out until we get a wifi connection
tmr.alarm (1, 800, 1, function ( )
  if wifi.sta.getip ( ) == nil then
     print ("Waiting for Wifi connection")
  else
     tmr.stop (1)
     print ("Config done, IP is " .. wifi.sta.getip ( ))
  end
end)

--uart cfg
uart.setup(0,19200,8,0,1,0)
-- create a server
sv=net.createServer(net.TCP, 300)    -- 300s time out for a inactive client
--server listen on 333
--never use node.input() in console. no effect.
 
sv:listen(333,function(c)
     if global_c~=nil then
          global_c:close()
     end
     global_c=c
     
     c:on("receive", function(c, pl)
          --print(pl) --debug
          --c:send(wifi.sta.getip()) --debug
          --c:send(" Receive>") c:send(pl) --debug
          uart.write(0,pl)
          end)
     c:send(" Listen >")
     c:send(wifi.sta.getip())
end)

uart.on("data",255, function(data)  
    --if global_c~=nil then --0307 remove for better perfomance
          global_c:send(data)
    --end
end,0)
 


Odladění skriptu lze s výhodu provést pomocí MT a Androidem. Stáhneme si aplikaci TCP/UDP Terminal [29], zadáme parametry spojení a komunikaci vyzkoušíme.
Po odladění přejmenujeme skript na init.lua, aby se automaticky spustil po restartu WiFi modulu. Rovněž je vhodné přidat na začátek skriptu zpoždění, abychom mohli program přehrát, protože po spuštění callback funkce pro UART která, již předávání příkazů modulu přes UART není možné.

Program na straně PC bude složitější než v předchozích případech. Musíme naprogramovat inicializaci a otevření TCP soketu, abychom přes něj mohli posílat obousměrně data. Obojí je naprogramováno v souborech tcpClient.cpp a
tcpServer.cpp. PC se chová v celé komunikaci jako TCP client.

Po zprovoznění celé komunikace se ukázalo, že je pomalá, nestabilní a ještě navíc se při přenosu ztrácejí data, přestože jsem použil předávání dat z UARTu na TCP spojení, které je z tohoto hlediska spolehlivé. Ke ztrátě dat pravděpodobně dochází v modulu ESP 8266.
Modul (firmware?) ESP 8266 není dostatečně rychlý, aby přes něj dokázala protéct data z UARTu na TCP bez výpadku v přijímaných a vysílaných datech. Spolehlivě lze komunikovat pouze rychlostí do 19 200Baudů. Pokud nastavíme větší rychlost, dojde ke ztrátě dat při přenosu. Nabízí se zde ještě možnost použití jiného firmware. Je otázkou, zda například při přímém řízení pomocí AT příkazů by rychlosti prospělo nebo naopak.
Na internetu jsem našel projekt „UART_TCP transparent“, který by měl přesně takto fungovat, ale nezkoušel jsem ho.

Přenos dat pomocí Wi-Fi modulu SN8200
Výhodně jsem získal modul DiscoverWiFi osazený čipem SN8200. K modulu se mi podařilo stáhnout příklad, který popisuje navázání bezdrátového spojení přes TCP nebo UDP sokety. Komunikace mezi kitem a modulem SN8200 probíhá pomocí sériové linky a je nastavena na rychlost 921 600Bd, takže rychlost přenosu dat zhruba odpovídá přenosu po sériové lince na této rychlosti.
Na straně discovery kitu jsou potřebné funkce obsaženy v souboru wifi.h a wifi.c a podadresář SN8200 obsahuje programové soubory pro SN8200 s komunikačním protokolem a aplikačním rozhraním.
Připojení provedeme zavoláním funkce int connectWIFI(int8_t* mySock), která nám v případě úspěchu vrátí číslo otevřeného socketu, přes který je možno posílat data ve stejné funkci, jako kdybychom je posílali pře sériové rozhraní.

Na straně PC jsem použil již vytvořený kód pro komunikaci pomocí TCP socketů. V tomto případě se PC chová v celé komunikaci jako TCP server.

Přenos dat pomocí Wi-Fi modulu EMW3165
Výhodně jsem získal development modul EMW3165. V modulu jsem dle příkladu z www stránek výrobce naprogramoval jednoduchý obousměrný TCP <>UART server. Dosažitelná rychlost přenosu bez ztráty přenášených dat je 230 400Bd.

--uart to tcp via WiFi
-- EMW3165
--22.05.2016 Zby
-- 28.07.2016

celkem = 0

print("---transparent uart to wifi (tcp srver 333)---")
--setup a ap
cfg={ssid = 'ssid',pwd = 'Pwd'}
wifi.startsta(cfg)
cfg=nil

--tcp server
skt = net.new(net.TCP,net.SERVER)
print("tcp server started")
uartClt = nil --save the clt socket
net.on(skt,"accept",function(clt,ip,port) uartClt = clt; end)
net.on(skt,"disconnect",function(clt) uartClt=nil end)
net.on(skt,"receive",function(clt,d)
        --print("Receive:"..d)
        uartClt = clt
        --send to uart 1
        uart.send(1,d)
end)

net.start(skt,333)

--uart pin:D8(RX1), D9(TX1)
--setup uart
uart.setup(1,230400,'n','8','1')

function uartReceive(d) 
        celkem = celkem + string.len(d)
        print("pkt>"..string.len(d).."B >"..celkem.."B")
       
        if uartClt ~=nil then
                net.send(uartClt,d)
        else
                print("Wrong UART control")
        end
end
uart.on(1, 'data',uartReceive)
 

Při hledání dalších možností jsem našel modul od TI který slibuje následující parametry:
TI WiFi modul CC3100 cena 7-10USD
• Application Throughput
o UDP: 16 Mbps
o TCP: 13 Mbps
pokud by platily katalogové parametry tak rychlost cca 2MB při UDP a 1,6MB při TCP spojení by jíž byla velice dobrá.

Takže závěrem této části lze konstatovat, že pro bezdrátový přenos se modul ESP8266 ani HC-06 nehodí. Vhodnější je použít další zmíněné moduly, které byly testovány jenom do dosažení funkčního stavu, a již jsem dále nezkoumal výkonové parametry a další možnosti zlepšení.
Žádný z Wi-Fi modulů, které jsem měl dispozici, se tedy k vysokorychlostnímu přenosu dat moc nehodí, nejlépe z hlediska přenosové rychlosti vycházel modul SN8200 (Proti jeho použití se staví poměrně komplikovaný komunikační protokol, cena a zastaralost).
Bezdrátový přenos obrazových dat je tedy možný, je však nutné najít možnosti a hranice použitelnosti pro jednotlivé moduly z praktického hlediska potřeb konkrétní aplikace.

7.
Možnosti komprese obrazových dat.

Možnosti komprese obrazových dat.
Protože značnou nevýhodu na straně kitu je manipulace s velkým množstvím dat zejména při VGA rozlišení obrazu jeví se, jako další možnost použít na obrazová data kompresi, a tak zmenšit jejich velikost, která se musí přenášet do PC.
Lákavou možností je aplikovat přímo v kitu některý z algoritmů pro kompresi obrazových dat. Formát RGB565, který byl doteď používán, můžeme například zkomprimovat některou z běžně používaných metod např. LZA apod. Nicméně lepší je pro tento účel použít některou ze ztrátových kompresí určených přímo pro obrazová data.
Jako nejvíce vhodný se jeví formát JPEG, pro jehož použití mluví především vysoký kompresní poměr při zachování dobré kvality obrázku.
Kompresním algoritmus by měl být velmi rychlý, výpočetně nenáročný a měl by dosahovat výrazného komprese (desítky nebo lépe stovky procent).

Komprese do formátu JPEG
Pro kompresi do formátu JPEG rovněž existuje velice dobře zpracovaná dokumentace včetně příkladů implementace. Nevýhodou je, že komprese do formátu JPEG je velmi náročná na výpočetní výkon. Použitý MCU však disponuje FPU jednotkou což by mohlo zajistit průběh komprese v dobrém čase.
Cílem je, aby komprese do formátu JPEG včetně přenosu dat do PC byla alespoň stejně rychlá jako samotný přenos nekomprimovaných dat.
Provedl jsem implementaci těchto dvou kompresních algoritmů [38], [39] , které se mi podařilo přizpůsobit pro procesor STM32F429
Nejdříve provedeme převod z formátu RGB565 do formátu RGB888, který je možné dále zpracovat. To provedeme přímo v obslužné rutině přerušení při skončení transferu pomocí DMA. Poté na takto převedený formát obrázku aplikujeme kompresi JPEG a odešleme ho stejně jako předtím nekomprimovaná data do PC. Algoritmus pro kompresi a dekompresi formátu JPEG je převzat z těchto zdrojů [38], [39] a je upraven pro mikroprocesor STM32F429. Celý kód jsem ještě profiloval na PC pomocí programu[28] a odstranil jsem některé zbytečné operace. Rovněž jsem převedl některé výpočty na celočíselné.
Při zachování rozumné kvality je kompresní poměr zhruba 12. Výsledná velikost obrázku se pohybuje mezi 40 až 60 kB.
Bohužel i po úpravě algoritmů trvá komprese VGA obrázku do formátu JPEG přibližně dvě vteřiny, takže z časového hlediska nebylo dosaženo žádné úspory.

Pokud použijeme tento postup, snížíme objem přenášených dat (v závislosti na kvalitě obrazu) a v celkovém důsledku snížíme rychlost zobrazování na PC. Komprese do formátu JPEG na kitu trvá cca dvě vteřiny, a proto tímto postupem nijak nezrychlíme celkovou obnovovací frekvenci na PC.
Nicméně i tato možnost je velmi zajímavá, neboť snižuje objem použitých dat pro přenos do PC a snižuje nárok na komunikační linku. Nicméně v případě nasazení do praxe, by musel přenos být zabezpečen proti ztrátě dat.


8.
Jiné
Raspberry Pi
Dle příkladů na serveru builder.cz [36] lze naprogramovat velice snadno TCP klienta pro Linux, kterého spustíme na Raspberry Pi. V RPI2 pak přijmeme obrazová data přes LAN a uložíme je ve formátu JPEG na SD kartu a pokud na RPI2 spustíme web server tak je možné jednoduchým kódem zobrazit obrázek na webové stránce.


9.
Shrnutí dosažených rychlostí



10.
Závěr
Získal jsem obraz v rozlišením VGA (QVGA) zobrazený na monitoru počítače a možnost uložit ho ve formátu *.ppm, *.jpeg na disku počítače. S takto uloženým obrázky je možné dále libovolně pracovat.
Vyzkoušel jsem různé varianty přenosu obrazových dat včetně bezdrátových. Dosažené výsledky shrnuje výše uvedená tabulka.

ToDo
DMA přenos dat SDRAM-> USB rozhraní.
DMA přenos dat SDRAM-> LCD Displej.
Použít na kompresi do formátu JPEG výkonnější algoritmus nebo procesor, cílem je čas komprese < 0,5sec.
Použít kameru, která komprimuje obrázky přímo do formatu JPEG.

Použité zdroje a odkazy:
[1] Seriál o STM32F4 Discovery kitu, odkaz
[2] Články o STM32F429I Disco kitu, odkaz
[3] Stránky projektu, odkaz
[4] Konfigurační soubory OV7670 pro Linux, odkaz
[5] Datové listy firmy ST k použitému MCU STM32F429ZI, odkaz
[6] Datové listy firmy ST k použitému vývojovému kitu, odkaz
[7] Datové listy firmy Omivision ke kameře OV7670, odkaz
[8] Dokumentace k modulu Bluetooth HC-05,06, odkaz
[9] Dokumentace k modulu ESP8266, odkaz
[10] ESP8266 Web Server Tutorial, odkaz
[11] Alternativní firmware NodeMcu modulu ESP8266, odkaz
[12] Skriptovací jazyk LUA, odkaz
[13] Wikipedie - JPEG, odkaz
[14] jpeg.org, odkaz
[15] Vývojové prostředí Keil uVision5 (Lite verze, max. velikost kódu 32kB), odkaz
[16] Vývojové prostředí EMBitz (0.42,1.0,1.1), odkaz
[17] Visual Studio 2013 Express Edition C/C++, odkaz
[18] Knihovny pro podporu práce s USB ve Windows, Lib USB, odkaz
[19] Knihovny pro podporu práce s grafikou SDL v1.2.15 ve Windows, odkaz
[20] Univerzální terminál TeraTerm, odkaz
[21] STM CDC serial driver, odkaz
[22] Nákup komponent, odkaz
[23] Nákup komponent, odkaz
[24] RGB565/555, YUV/YCbCr(4:2:2), odkaz
[25] Sběrnice I2C, odkaz
[26] Libraries and tutorials for STM32Fxxx series by Tilen Majerle, odkaz
[27] SPL pro STM32F4xx dostupné z webu výrobce, použité verze 1.3.0. a 1.7.1, odkaz
[28] Very Sleepy Profiler pro Windows, odkaz
[29] TCP/UDP Terminal pro Android, odkaz
[30] STM32F429 discovery firmware package (UM1662), odkaz
[31] Using Direct Memory Access (DMA) in STM32 projects, odkaz
[32] Hacking the OV7670 camera module (SCCB cheat sheet inside), odkaz
[33] Demonstration of interfacing the Nucleo STM32F446RE with a OV7670 camera module, odkaz
[34] RS-232 for Linux, FreeBSD and windows, odkaz
[35] Seriál SDL: Hry nejen pro Linux, odkaz
[36] TCP klient v MS Windows, odkaz
[37] ESplorer, odkaz
[38] NanoJPEG: a compact JPEG decoder odkaz
[39] JPEG ENCODER & DECODER odkaz

Přílohy a zdrojové kódy
QVGA a USB VCP
camov7670.zip
esp8266.zip
rgb565console.zip
image.zip
https://sourceforge.net/projects/libusb-win32/?source=directory
http://www.libsdl.org/download-1.2.php


Tato novinka je z -MCU-mikroelektronika
( http://mcu.cz/news.php?extend.3988 )