STM32F4 DCMI & OV9655 Kamera Uygulaması

Başlatan Mucit23, 17 Mart 2014, 12:18:29

Mucit23

Hazır kamera ile uğraşıyorken kütüphanemizi sabitleyelim.

Yaptığımız uygulama STM32F4 içerisinde bulunan DCMI modülünün kullanımına dair bir örnek uygulamadır. DCMI + DMA + FSMC donanımları sayesinde neredeyse sıfır işlem gücüyle kameradan alınan görüntüyü LCD ye taşıyoruz.

Kullandığım kamera modülü WaveSharenin yaptığı bir modül.
http://www.wvshare.com/product/OV9655-Camera-Board.htm
Modülün üzerinde OV9655 Cmos Sensör bulunmakta. Max. çözünürlüğü 1.3 Megapixel. Bu çözünürlükte SXGA boyutunda yani 1280×1024 boyutlu bir görüntü elde edilebiliyor.

Kameranın diğer desteklediği boyutlar aşağıdaki gibidir.

VGA = 640×480
QVGA = 320×240
QQVGA = 160×120
CIF = 40×30

Kamera hakkında fazla ayrıntıya girmeyeceğim. İnternette birçok bilgi mevcut.

İlk önce STM32F4-Discovery Board ile kamera arasında aşağıdaki bağlantıları yapın.

D0 = PC6
D1 = PC7
D2 = PE0
D3 = PE1
D4 = PE4
D5 = PB6
D6 = PE5
D7 = PE6
HSYNC = PA4
VSYNC = PB7
XCLK = PC9
PXCLK = PA6
SIO_C= PB8
SIO_D= PB9


FSMC LCD Bağlantıları

LCD_D0 = PD14
LCD_D1 = PD15
LCD_D2 = PD0
LCD_D3 = PD1
LCD_D4 = PE7
LCD_D5 = PE8
LCD_D6 = PE9
LCD_D7 = PE10
LCD_D8 = PE11
LCD_D9 = PE12
LCD_D10 = PE13
LCD_D11 = PE14
LCD_D12 = PE15
LCD_D13 = PD8
LCD_D14 = PD9
LCD_D15 = PD10
LCD_CS = PD7
LCD_RS = PD11
LCD_RD = PD4
LCD_WR = PD5


Beslemeleri bağlamayı unutmayın. 

Bu uygulamada kamera çözünürlüğünü 320x240(QVGA) olacak şekilde ayarlı. Ayrıca RGB565 formatında çıkış veriyor. Bu yüzden LCD de RGB565 formatında data alacak şekilde ayarlanmalı.

OV9655 çalışmak için 10 ile 48Mhz arasında bir clock kaynağına ihtiyaç duyar. İdealde 24Mhz uygulanmalı diyor datasheet.
Kamera modülünün üzerinde ov9655'in bu ihtiyacını karşılayacak bir clock kaynağı yok. Bu işi kullanıcıya bırakmışlar. Böyle olması daha iyi çünkü uygulayacağımız frekans eğeri doğrudan kamera karakteristiğini ve FPS değerini değiştiriyor. Farklı FPS değerleri için farklı frekanslar uygulanmalı.

Biz RGB565 formatında data aldığımız için her bir pixelin RGB verisi 2 adet XCLK cloğunda gönderilir. Dolayısıyla 24Mhz XCLK frekansı uyguladığımızda 12Mhz PXCLK frekansı görürüz. 12Mhz PXCLK frekansında 15FPS elde edilebiliyor.

Resimde daha net anlaşılacaktır durum.


Şimdi bağlantıları yapıp doğruluğunu test ettikten sonra geriye OV9655.c, OV9655.h kütüphanelerini projenize eklemek kalıyor.  ( OV9655.c, OV9655.h dosyaları ektedir.)

Görüntüyü ekrana göndermeden önce ekranda 320x240 boyutunda bir pencere açarız. Sonrasında kameraya start veririz. Artık LCD ye gönderilecek datalar sırayla bu pencere içerisine yazılır. Yukarıdaki kodlarda ben DCMI Frame Kesmesini aktif etmişim. Yani her bir görüntü yüklendiğinde kesme oluşur. Bunu yapma sebebim ise bendeki SSD1289 çipsetli LCD'ye beyaz renk datası geldiğinde bazende olsa ekran karışabiliyor. Bunu önlemek için her bir görüntü yüklendiğinde kesmeye gidip yeniden ekrana pencere açıyorum.

Kesmenin düşebilmesi için stm32f4xx_it.c içerisine DCMI interrup alt programını ekleyin.


void DCMI_IRQHandler(void)
{  	   
	if (DCMI_GetITStatus(DCMI_IT_VSYNC) != RESET) 
	{	 		   	
		DCMI_ClearITPendingBit(DCMI_IT_VSYNC);	  
	}
	
	if (DCMI_GetITStatus(DCMI_IT_LINE) != RESET) 
	{
		DCMI_ClearITPendingBit(DCMI_IT_LINE); 			  
	}
	
	if (DCMI_GetITStatus(DCMI_IT_FRAME) != RESET) 
	{
		LCD_SetDisplayWindow(0, 0, 319, 239);
	  LCD_WriteRAM_Prepare();
		DCMI_ClearITPendingBit(DCMI_IT_FRAME);
	}
	if (DCMI_GetITStatus(DCMI_IT_ERR) != RESET) 
	{
		DCMI_ClearITPendingBit(DCMI_IT_ERR);
	}
}


Bu sayede yeni bir görüntü geldiğinde ekran toparlanmış oluyor. Bu her zaman olmuyor. Çok nadiren yaşıyorum. Bu yüzden eğer kullandığınız lcd de bu tür problemler yok ise kesme kullanmanıza da gerek yoktur.

Unutmadan kamerayı init etmek için I2C kullanılıyor. Epeyce bir uğraşmama rağmen kamerayı Hardware I2C ile init edemedim. Bu yüzden Software I2C Kullandım. Kamera I2C konusunda hata kabul etmiyor. Sırf kamera ile iletişim kurabilmek 1 haftamı aldı.

Aşağıdaki kodları da projenize eklemeniz gerekiyor.


I2C.c
/**
  ******************************************************************************
  * @file    I2C.c
  * @author  Ferhat YOL
  * @version V0.0.3
  * @date    ///
  * @brief   Software I2C Program
  *          
  ******************************************************************************/
	/* Includes ------------------------------------------------------------------*/
  #include "stm32f4xx.h"
	#include "I2C.h"
	#include "main.h"
/* Private typedef -----------------------------------------------------------*/
/* Private define ------------------------------------------------------------*/
	 #define I2C_Port GPIOB
	 #define I2C_SDA  GPIO_Pin_9
	 #define I2C_SCL  GPIO_Pin_8
/* Bits definitions ----------------------------------------------------------*/
/* Private macro -------------------------------------------------------------*/
   #define SDAH GPIO_SetBits(I2C_Port,I2C_SDA)
	 #define SDAL GPIO_ResetBits(I2C_Port,I2C_SDA)
	 #define SCLH GPIO_SetBits(I2C_Port,I2C_SCL)
	 #define SCLL GPIO_ResetBits(I2C_Port,I2C_SCL)
   #define SDAR GPIO_ReadInputDataBit(I2C_Port,I2C_SDA)//GPIOB->IDR & I2C_SDA
   #define SCLR GPIO_ReadInputDataBit(I2C_Port,I2C_SCL)//GPIOB->IDR & I2C_SDA
/* Private variables ---------------------------------------------------------*/

/* Private function prototypes -----------------------------------------------*/
/* Private functions ---------------------------------------------------------*/

void Soft_I2C_Configuration(void){
  GPIO_InitTypeDef GPIO_InitStructure;
	/* Enable GPIOB clocks */
	RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE);
	RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOD, ENABLE);

	/*SDA Pin Configuration*/
  GPIO_InitStructure.GPIO_Pin = I2C_SDA; 
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
  GPIO_InitStructure.GPIO_OType = GPIO_OType_OD;
  GPIO_InitStructure.GPIO_PuPd  = GPIO_PuPd_UP;
  GPIO_Init(I2C_Port, &GPIO_InitStructure);
	/*SCL Pin Configuration*/
	GPIO_InitStructure.GPIO_Pin = I2C_SCL; 
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
  GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
  GPIO_InitStructure.GPIO_PuPd  = GPIO_PuPd_NOPULL;
  GPIO_Init(I2C_Port, &GPIO_InitStructure);
}


void I2C_Delay(void)
{
   unsigned int i=I2C_Delay_Time;
   while(i)
   {
     i--;
   }
}

uint8_t I2C_Start(void)
{
  SDAH;
	SCLH;
	I2C_Delay();
  SDAL;
	I2C_Delay();
	SCLL;
	I2C_Delay();
 return (0x00);
}

void I2C_Stop(void)
{
 SDAL;
 I2C_Delay();
 SCLH;
 I2C_Delay();
 SDAH;
 I2C_Delay();
}

void I2C_Ack(void)
{
 SCLL;
 I2C_Delay();
 SDAL;
 I2C_Delay();
 SCLH;
 I2C_Delay();
 SCLL;
 I2C_Delay();
}

void I2C_NoAck(void)
{
 SCLL;
 I2C_Delay();
 SDAH;
 I2C_Delay();
 SCLH;
 I2C_Delay();
 SCLL;
 I2C_Delay();
}
 
uint8_t I2C_WaitAck(void)  
{
	SDAH;
  I2C_Delay();
	SCLH;I2C_Delay();  //Clock Uygulaniyor
	if(SDAR) return (0xFF);
	;SCLL;I2C_Delay();
	return(0x00);
}

void I2C_SendByte(unsigned char SendByte)
{
    unsigned char i=0;
    for(i=0;i<8;i++)
		{
      if ((SendByte << i) & 0x80)
			 {
				 SDAH;
       }
			 else
			 {
         SDAL;
       }
			 I2C_Delay();
       SCLH;
			 I2C_Delay();
       SCLL;
			 I2C_Delay();
    }
}
 
unsigned char I2C_ReceiveByte(void)  
{
    unsigned char i=8;
    unsigned char ReceiveByte=0;
 
    SDAH;    
    while(i--)
    {
      ReceiveByte<<=1;      
      SCLL;
      I2C_Delay();
      SCLH;
      I2C_Delay();
      if(SDAR)
      {
        ReceiveByte|=0x01;
      }
    }
    SCLL;
    return ReceiveByte;
}

unsigned char I2C_ReadByte( unsigned char DeviceAddress,unsigned int ReadAddress)
{  
    unsigned char temp;
    if(!I2C_Start())return (0xFF);    
 
    I2C_SendByte((DeviceAddress & 0xFF));
    if(!I2C_WaitAck()){I2C_Stop(); return 0xFF;}
   
    I2C_SendByte((unsigned char)((ReadAddress>>8) & 0xFF));    
    I2C_WaitAck();
    I2C_SendByte((unsigned char)((ReadAddress) & 0xFF));        
    I2C_WaitAck();
    I2C_Start();
    I2C_SendByte((DeviceAddress & 0xFE)|0x01);    
    I2C_WaitAck();
   
    temp = I2C_ReceiveByte();
   
    I2C_NoAck();
     
    I2C_Stop();
    return temp;
}


I2C.h
/********************************************************************************
  * @file    I2C.h
  * @author  Ferhat YOL
  * @version V0.0.3
  * @date    10/15/2010
  * @brief   Software I2C Program
  *          
  ******************************************************************************/
#ifndef I2C_H
#define I2C_H
/* Includes ------------------------------------------------------------------*/
   #include "stm32f4xx.h"
	 
/* Private typedef -----------------------------------------------------------*/
/* Private define ------------------------------------------------------------*/ 
	 #define I2C_Delay_Time 500
/* Bits definitions ----------------------------------------------------------*/
/* Private macro -------------------------------------------------------------*/
/* Private variables ---------------------------------------------------------*/
/* Private function prototypes -----------------------------------------------*/
/* Private functions ---------------------------------------------------------*/
void Soft_I2C_Configuration(void);
void I2C_Delay(void);
uint8_t I2C_Start(void);
void I2C_Stop(void);
void I2C_Ack(void);
void I2C_NoAck(void);
uint8_t I2C_WaitAck(void);
void I2C_SendByte(unsigned char SendByte);
unsigned char I2C_ReceiveByte(void);
unsigned char I2C_ReadByte(unsigned char DeviceAddress,unsigned int ReadAddress);

#endif


Tüm kodları ekledikten sonra mainde aşağıdaki işlemleri yapın.

int main()
{
	SystemInit();
	if (SysTick_Config(SystemCoreClock / 3119))//1ms 
  { 
    /* Capture error */ 
    while (1);
  }	

        STM3240F_LCD_Init();  	
	LCD_Clear(Black);
	LCD_SetColors(Green,Black);
	LCD_SetFont(&Font8x8);

    if(OV9655_Configuration()==0xFF) 
		{
	     LCD_DisplayStringLine(0,0,"Camera Failed");
    }else{
       LCD_DisplayStringLine(0,0,"Camera OK!");
    }
		/* Start Image capture */ 

	LCD_SetDisplayWindow(0, 0, 319, 239);
  LCD_WriteRAM_Prepare();
  DCMI_CaptureCmd(ENABLE); 
  /* Enable DMA transfer */
  DMA_Cmd(DMA2_Stream1, ENABLE);
    /* Enable DCMI interface */
  DCMI_Cmd(ENABLE); 
    /* Start Image capture */ 
  DCMI_CaptureCmd(ENABLE);   

	while(1)  //User Code
	{ 	
		 
 	}
}


Yapılacak işlemler bunlardan ibaret.

Kameranın setup ayarlarıyla ben epeyce oynadım. Yani günlük kullanımda en ideal görüntüyü verecek şekilde ayarlamaya çalıştım. Şuan kamera stabil bir şekilde çalışıyor.

Test Videosu

STM32F4 Discovery OV9655 Camera Test (Final)

İyi çalışmalar.

xekseni

Merhabalar,
Çalışmanız çok güzel tebrik ederim. Uygulamanızı indirip aynı çalışmayı bende yaptım fakat UART4 ü konfigure edip bilgi almaya çalıştığımda anlamsız ifadeler geliyor. Aynı UART4 konfigurasyonunu başka uygulamada yapıyorum çalışıyor. Nedeni sizce ne olabilir?
Yol gösterirseniz sevinirim.
Teşekkurler.

Mucit23

Uygulamada UART kullanılmıyor. Farklı bir iş içinmi kullanıyorsunuz?

Elektroemre

Uygulamayı yeni gördüm, eline sağlık Mucit23.

Mucit23


xekseni

Evet bu uygulamaya ilave olarak çektiğim resmi uarttan almak istedim. Nedenini tam olarak anlayamadım ama sonradan baştan dosyaları kendim oluşturarak-düzenleme yaparak yaptım. Çok uzun surdu ama sorun Düzeldi. LCD deki Ram i piksel piksel okuyup uarttan bilgisayar a gonderdim. Tekrar teşekkur ederim. İyi çalışmalar.

XX_CİHAN_XX

Mucit hocam elinize sağlık çok güzel bir çalışma olmuş. Birşeyi merak ediyorum hiç denediniz mi bu kameradan 8bit raw RGB datası almaya kalktığımızda 3x8bitlik RGB datası mı alıyoruz yoksa daha farklı birşey mi geliyor. Merak ettiğim konu her bir piksel için aynı paintteki gibi 3x8bitlik renk datası bu kamera üzerinden almak mümkün müdür?
Yirmi yaşındaki bir insan, dünyayı değiştirmek ister . Yetmiş yaşına gelince , yine dünyayı değiştirmek ister, ama yapamayacağını bilir.

Mucit23

Hocam açıkçası benimde fazla bir bilgim yok. OV9655'in Datasheeti bu konuda çok kıt.

OV7670'de Data bacakları 8 bitten doluşuyor. RAW RGB data geldiği zaman 888 olarak 3 PXCLK darbesinde Pixele ait RGB bilgisini gönderiyor. Fakat OV9655'te data bacakları 10 bit. Normalde Biz 8Bit ile haberleşirken ilk iki biti kullanmayıp sadece D[2]-D[9]Arasını kullanıyoruz.

OV9655'in Datasheetinde SXGA Frame Timing için şöyle bir şekil verilmiş.

Bu şekle göre haberleşmede 10 bitin hepsi kullanılıyor fakat dizilim nasıl olur Kaç PXCLK darbesinde RGB gelir bu konuda bir bilgiye rastlayamadım.

tekosis

elinize sağlık çok güzel bir paylaşım olmuş.
İlim ilim bilmektir, ilim kendin bilmektir, sen kendin bilmezsin, bu nice okumaktır.

Gökhan BEKEN

Özel mesaj okumuyorum, lütfen göndermeyin.

namso0632

Hocam bu kit ile birden fazla kamerayı kontrol etmemiz mümkün müdür acaba ? Örneğin 2 farklı kamerayı bu kitle kontrol edip , fotograf alıp dataları bir hafıza kartına yazabilir miyiz ? Eğer mümkünse kullanılabilecek kamera sınırı nedir ?

Mucit23

DCMI normalde tek bir kamera için. Birden fazla kamera bağlanırmı, Bağlanırsa eğer nasıl bağlanır bir fikrim yok malesef.

okg

Hocam öncelikle elinize sağlık güzel bir çalışma olmuş,
ov7670 ile deniyorum da pxclk darbesini nasıl ayarlamam gerekiyor acaba?
KTU Elektronik Haberleşme - YTÜ Haberşelme YL - GTU Haberleşme YL

Mucit23

pxclk frekansı kameranın çalışma hızına göre ayarlanır. Datasheette sayfa 6 da clock frekansı belirtilmiş. 15FPS için 24Mhz, 30FPS için 48Mhz uygulanması gerekiyor.

kadirstn.25

Merhabalar,
öncelikler verdiğiniz bilgilerden dolayı teşekkür ederim. ben de ov9655 kamera ve tft lcd ekran ile görüntü almaya çalışıyorum stm32f4 ile ama bir türlü olmadı. hatta son olarak sizin dosyaları denedim yine çalışmadı. acaba kamera mı bozuk bilemedim. yardımcı olur musunuz ?