2017年9月14日 星期四

技術筆記CAMERA

印表玩具
   STM32技術

一般EMBEDDED SYSTEM和CAMERA溝通方式為SPI,缺點是速度太慢。STM32F4之後,提供DCMI加速機制,讀取影像更方便,F7之後,還號稱能同時處理兩個以上CAMERA影像,如果我想用兩顆鏡頭抓空間深度,這會有很幫助。

網路上搭配STM32開發板的,幾乎都是使用OV。OV有眾多型號,我購買的OV7670只有三十萬畫素,價格僅二百左右,首次Tabbao買的不能用,後來向taiwaniot購買。缺晶振,但由外部XCLK提供也還堪用。

好歹在相機組裝廠工作過。先來研究一下OV。OV美商OmniVision豪威(台灣有分公司,曾是感光元件巨頭),而OV7670不是想像中僅有sensor,已包含完整影像系統。也就是說,可直接取出經過3A的JPEG、RAW(雖然畫質很糟)。目前使用上就是以RAW為主。


比較貴的模組,會附上FIFO,就是下圖中那顆CHIP。不帶FIFO的攝像頭和帶FIFO攝像頭的區別:帶FIFO的攝像頭比不帶的多了個3M的緩存,可將採集的數據暫存在這個緩存中,使用時讀取緩存中的圖像數據即可,因此可減少對單片機採集圖像數據時對MCU速度的要求。OV7670 FIFO攝像頭,將圖像傳感器和圖像緩衝器件AL422B有機結合,解決了低端單片機圖像採集的速度瓶頸問題。搭配STM32用不到。另外,圖中還多了個晶振。若無,由STM32從外部XCLK提供也還堪用。


從影像模組來看其他專案,例如PrintSnap用的是很簡易配Arduino使用的JPEG模組,使用TTL溝通,取回的內容是JPEG要另行Decode,不實用。

手上的OpenMV也有幾種版本差異

Raspberry Pi使用特殊的CSI(Camera Serial Interface)也有兩種相機
V1 OV5647 5百萬像素固定焦點 支持1080p、720p60和VGA90
V2 Sony IMX219PQ CMOS 八百萬像素固定焦點 支持1080p、720p60和VGA90

另外,小體積的FPV鏡頭也很。

回到STM32對OV取得影像部分,目前使用指令並不多,只有寬高、拍攝、取得畫面。依架構可分成三種。

一堆杜邦線亂纏繞,眼睛都快脫窗( 原本有牛角母啦, 麻煩 ),看到它起動時高興的不得了,覺得前進一大步.

方式一,DCMI資料進入一般記憶體,處理後再送LCD


架構簡單也是後續開發基礎。主要參考位置是 範例, 文件 底部有使用的CODE範例,程式流程

STM_mode = 0;
btn_pressed = false;
frame_flag = false;
// Infinite program loop
while(1){
if (btn_pressed == true){
if (STM_mode == 0){
// MODE 1 - RUN
STM_mode = 1;
開攝影鏡頭
}
else{
if(frame_flag == false){
// MODE 2 - STOP
STM_mode = 0;
停止攝影鏡頭
}
}
btn_pressed = false;
}
if( frame_flag == true){
// Show camera image
通知LCD顯示畫面
frame_flag = false;
畫面沒有更新,不需要再重覆顯示
}
}

當相機收到訊號後,就會送DMA完成的訊號
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);
frame_flag = true;
也就是說 相機會一直送訊號進來,畫面更新的快 就會漏掉一些畫面
但是如果相機更新比較慢,畫面會等相機
}
}

按下按鈕
命令取得影像 DCMI_CaptureCmd(ENABLE);
系統由CAMERA取得影像後觸發 DMA2_Stream1_IRQHandler
  影像會進volatile uint16_t frame_buffer[IMG_ROWS*IMG_COLUMNS];
  因為設定過
  DMA_InitStructure.DMA_Memory0BaseAddr = (uint32_t)frame_buffer;
  DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralToMemory;
  DMA_InitStructure.DMA_BufferSize = IMG_ROWS*IMG_COLUMNS*2/4;

畫出影像LCD_ILI9341_DisplayImage((uint16_t*) frame_buffer);  ( 這和CAMERA無關,只式記錄 )
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;
}

OV7670_control.h 最重要參數是
// Image settings
#define IMG_ROWS   320
#define IMG_COLUMNS   240
決定了影像大小

方式二,CAMERA內容透過DMA直接映射到LCD記憶體區域

這種狀況下,DMA更新LCD畫面也立刻更新( CPU不介入 ),好寫也單純,但不能處理影像,不考慮使用。 OV7725:KitSprout  CMOS VGA 640X480  範例

流程( KitSprout 407 )
  GPIO設定
  RCC_MCO1Config
    啟動 OV7725_Config()
         RCC
         GPIO PIN腳對應
         DCMI_Init
         NVIC_Init
         DMA_Init
             #define DCMI_DR_ADDRESS        0x50050028
             #define FSMC_LCD_ADDRESS       0x60020000
             DMA_InitStructure.DMA_PeripheralBaseAddr = DCMI_DR_ADDRESS;
             DMA_InitStructure.DMA_Memory0BaseAddr  = FSMC_LCD_ADDRESS;
             這裡把CAMERA的內容和LCD連結起來
             在LCD #define LCD_RAM *(vu16*)((u32)0x60020000)

  OV7725_Init();
透過OV_WriteReg SCCB_Write 寫入很多REG設定 OV_reg (在H檔案中)
Camera_Start
  LCD_SetWindow(0, 0, 320 - 1, 240 - 1);
  DMA_Cmd(DMA2_Stream1, ENABLE);
  DCMI_Cmd(ENABLE);
  DCMI_CaptureCmd(ENABLE);

因為是使用DCMI 每當DCMI收到結果就會發送callback
void DCMI_IRQHandler( void )
{
  if(DCMI_GetITStatus(DCMI_IT_FRAME)!= RESET) {
    LCD_SetWindow(0, 0, 320 - 1, 240 - 1);
    DCMI_ClearITPendingBit(DCMI_IT_FRAME);
    將DMA結果對應到LCD記憶體,刷新畫面
  }
}
Handler是固定寫法,放在xxx_it中

方式三,CAMERA內容透過DMA直接映射到 SD RAM記憶體(不是RAM)


和方式一類似,差別在記憶體使用上,方式一是原生記憶體,方式二是SD RAM。

PIN對應和範例相同
SDRAM_Init
影像和處理和顯示特殊處理方式
在DCMI回覆時
    LCD_SetWindow(FRAME_W, FRAME_H, FRAME_W + FRAME_W - 1, FRAME_H + FRAME_W - 1);
    for(uint32_t i = 0; i < FRAME_W * FRAME_H; i++) {
      pixel = SDRAM_DATA(uint16_t, (i << 1));
      LCD_RAM = pixel & (~(RED | GREEN)); //將SDRAM每個元素顏色計算,寫回LCD_RAM
    }
//讀出影像
OV的資料對應到DMA 不是LCD
  DMA_InitStruct.DMA_PeripheralBaseAddr = DCMI_DR_ADDRESS;
  DMA_InitStruct.DMA_Memory0BaseAddr    = (uint32_t)SDRAM_BANK_ADDR;//SDRAM_BANK_ADDR;//FSMC_LCD_ADDRESS;
  DMA_InitStruct.DMA_DIR                = DMA_DIR_PeripheralToMemory;
  DMA_InitStruct.DMA_BufferSize         = FRAME_W * FRAME_H >> 1;
#define SDRAM_BANK_ADDR               ((uint32_t)0xD0000000)
#define SDRAM_DATA(__TYPE, __INDEX)   *(__IO __TYPE *)(SDRAM_BANK_ADDR + (__INDEX))
因為OV市直接設定到SDRAM_BANK_ADDR,只要使用SDRAM_DATA就可以讀出畫面結果
//寫螢幕
#define LCD_RAM     *(__IO uint16_t *)((uint32_t)0x60020000)
用這個就可以將資料逐漸寫入
#define FSMC_LCD_ADDRESS  0x60020000
DMA_InitStruct.DMA_Memory0BaseAddr    = 0x60020000;

特殊點
1.429有加入晶振功能,但407無?
2.加SDRAM但HEAP沒改變? (比對XXX.s)
3.影像大小設定
#ifdef RESOLUTION_VGA
#define FRAME_W 640
#define FRAME_H 480
#else
#define FRAME_W 320
#define FRAME_H 240
#endif
DMA_InitStruct.DMA_BufferSize = FRAME_W * FRAME_H >> 1;
static const uint8_t OV_reg[OV_REG_NUM][2] = {
#ifdef RESOLUTION_VGA
  {0x12,0x06},// VGA RGB565, 640*480
#else
  {0x12,0x46},// QVGA RGB565, 320*240
#endif
#ifdef RESOLUTION_VGA
  {0x29,0xA0},  // VGA
  {0x2C,0xF0},  // VGA
#else
  {0x29,0x50},  // QVGA
  {0x2C,0x78},  // QVGA
#endif

可能的範例如下 範例  範例  範例  範例 範例

DCMI串接多台Camera


若要計算立體視覺,需要綜合多台Camera所傳入影像,DCMI可以處理兩組影像嗎?目前查到的資料如下

文字描述 STM32F7 DCMI
The cameras additionally need to be set up through the I2C bus before the images can be transfered via DCMI. An example of two camera setups is available in the Board Support Package driver files.
Other camera types need different setup that users have to extract from their documentation.

有發出詢問,等待回應。

沒有留言:

張貼留言