Monday, April 28, 2014

STM32F4 Discovery Board + OV7660 or OV7670 Working Code

Hello all,

This post is about on how to get data from  OV7660/OV7670 camera module by connecting to STM32F4 Discovery Board. first of all I would like to introduce you following posts on connecting OV7670 camera module to a micro controller which were really helpful for me to become success.

  • http://embeddedprogrammer.blogspot.com/2012/07/hacking-ov7670-camera-module-sccb-cheat.html
  • http://embeddedprogrammer.blogspot.com/2013/01/demo-stm32f4-ov7670-qserialterm.html
  • forum
I must thankful for the above posts which were really useful. 

Okay, Lets get to work!

Before you are going to do this project I recommend you to read the data sheets and reference manuals of OV7660, STM32F4 data sheet and STM32F4 Reference manual. It is not a good practice for a person who is willing to gain his knowledge on embedded systems to copy and pasting a code in internet and make it to work. What is really valuable is practicing to read data sheets and understanding how they work.

Above mentioned blog posts have clearly mentioned how camera module works. The difference between OV7660 and OV7670 is ,OV7670 has more options like scaling the image as user wanted,register values  etc... . The behavior of working is same for both the camera modules.

I will explain how I became success step by step

STEP 1

Before doing all the things you must give a clock to camera module in order to work. Without giving a clock it is a dead module. I was able to connect this camera module to both STM32F103 and STM32f4 Discovery board. I shift to discovery board because it is unable to get data at higher speeds since no DCMI interface is available on that microcontroller.
STM32F4 is more powerful than F103. It is having DCMI and DMA modules. So the speed is really good.
To get a clock out from your microcontroller,just search on microcontroller's data sheet as MCO.
You can see what is the MCO pin in your microcontroller. In discovery board I used PA8 to get clock out. Get the data sheet of STM32F4 Discovery board and search on PA8. You will see what PA8 can do. We must set that pin as MCO in configuration code. Following is the code for it.


1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
void MCO1_Init(void)
{
 GPIO_InitTypeDef GPIO_InitStructure;

 RCC_ClockSecuritySystemCmd(ENABLE);

 /* Enable GPIOs clocks */
 RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);
 
 GPIO_PinAFConfig(GPIOA, GPIO_PinSource8, GPIO_AF_MCO);
  
 /* Configure MCO (PA8) */
 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8;
 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);

 RCC_MCO1Config(RCC_MCO1Source_PLLCLK, RCC_MCO1Div_4);
}


You must check the clock is worming correctly using a logic analyzer or a oscilloscope before move to next step.

STEP 2

Next is to configure the camera as we want. Omnivision has used SCCB interface to configure the camera. Seems like SCCB is almost same as I2C. But I couldn't get to work with I2C. By changing some commands I was able to communicate with the camera. following is the code for SCCB interface, It is worked well for me. If anything need to be changed, please let me know. Your ideas are warmly welcome!


1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
#include "stm32f4xx.h"
#define ONE_BYTE_REG_ADDR 0x01
#define TWO_BYTE_REG_ADDR 0x02

void Hardware_InitI2C(void) 
{
 
 GPIO_InitTypeDef GPIO_InitStructure; // this is for the GPIO pins used as I2C1SDA and I2C1SCL
 I2C_InitTypeDef I2C_InitStructure; // this is for the I2C1 initilization
 
 /* enable APB1 peripheral clock for I2C1*/
 RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C1, ENABLE);
 
 /* enable the peripheral clock for the pins used by
  PB6 for I2C SCL and PB9 for I2C1_SDL*/
        RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE); 
 
 /* This sequence sets up the I2C1SDA and I2C1SCL pins
  * so they work correctly with the I2C1 peripheral
  */
 GPIO_StructInit(&GPIO_InitStructure);
 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8 | GPIO_Pin_9; // Pins 10(I2C1_SCL) and 11(I2C1_SDA)
 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF; // the pins are configured as alternate function so the USART peripheral has access to them
 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz;// this defines the IO speed and has nothing to do with the baudrate!
 GPIO_InitStructure.GPIO_OType = GPIO_OType_OD;// this defines the output type as open drain
 GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;// this activates the pullup resistors on the IO pins
 GPIO_Init(GPIOB, &GPIO_InitStructure);// now all the values are passed to the GPIO_Init() 
 
 
 /* The I2C1_SCL and I2C1_SDA pins are now connected to their AF
  * so that the I2C1 can take over control of the
  * pins
  */
 GPIO_PinAFConfig(GPIOB, GPIO_PinSource8, GPIO_AF_I2C1); //
 GPIO_PinAFConfig(GPIOB, GPIO_PinSource9, GPIO_AF_I2C1);
 
  /* Configure I2C1 */
  I2C_StructInit(&I2C_InitStructure);
  I2C_DeInit(I2C1);
 
  /* Enable the I2C peripheral */
  I2C_Cmd(I2C1, ENABLE);
 
  /* Set the I2C structure parameters */
  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 Peripheral Enable */
I2C_ITConfig(I2C1, I2C_IT_ERR, ENABLE);
  /* Initialize the I2C peripheral w/ selected parameters */
  I2C_Init(I2C1,&I2C_InitStructure);
 I2C_Cmd(I2C1, ENABLE);
     

}

int i2c_send_data(u8 slave_addr, u16 reg_addr, u8* data, u8 addr_len)
{
  int timeout = 0x7FFFFF;
  int ret = 0;
         /* send i2c*/
  I2C_GenerateSTART(I2C1, ENABLE); 
  while( !I2C_CheckEvent(I2C1,I2C_EVENT_MASTER_MODE_SELECT));  
  {
   if ((timeout--) == 0) 
   {
    ret = 1;
    goto exit;
   }
  }
  I2C_Send7bitAddress(I2C1, slave_addr, I2C_Direction_Transmitter);  
  while(!(I2C1->SR1 & (1 << 1))); // check ADDR bit
  {
   if ((timeout--) == 0) 
   {
    ret = 2;
    goto exit;
   }
  }
  while(!(I2C1->SR2 & (1 << 2)));   // check TRA bit
  {
   if ((timeout--) == 0) 
   {
    ret = 3;
    goto exit;
   }
  }
 
  /* 2 byte reg address */
  if(addr_len == TWO_BYTE_REG_ADDR)
  {
   // MSB
   I2C_SendData(I2C1, (0xFF & (reg_addr >> 8)) );   
   while(!(I2C1->SR1 & (1 << 7))); 
   {
   if ((timeout--) == 0) 
   {
    ret = 4;
    goto exit;
   }
   }
     
   // LSB
   I2C_SendData(I2C1, (0xFF & reg_addr));   
   while(!(I2C1->SR1 & (1 << 7)));
   {
   if ((timeout--) == 0) 
   {
    ret = 5;
    goto exit;
   }
   }
  
  }
  /* 1 byte reg address */
  else 
  {
   I2C_SendData(I2C1, (0xFF & reg_addr));   
   while(!(I2C1->SR1 & (1 << 7)));
   {
   if ((timeout--) == 0) 
   {
    ret = 6;
    goto exit;
   }
   } 
  }

  I2C_SendData(I2C1, *data);  
  while(!I2C_CheckEvent(I2C1,I2C_EVENT_MASTER_BYTE_TRANSMITTED));
  {
//   if ((timeout--) == 0) 
   {
    ret = 7;
    goto exit;
   }
  } 

                exit:  
  I2C_GenerateSTOP(I2C1, ENABLE);  
  return ret;
}

int i2c_receive_data(u8 slave_addr, u16 reg_addr, u8* data, u8 addr_len)
{
  int timeout = 0x7FFFFF;
  int ret = 0;
   /* send i2c*/
  while(I2C_GetFlagStatus(I2C1, I2C_FLAG_BUSY));
  I2C_GenerateSTART(I2C1, ENABLE); 
  while(!I2C_CheckEvent(I2C1,I2C_EVENT_MASTER_MODE_SELECT));
  {
   if ((timeout--) == 0) 
   {
    ret = 1;
    goto exit;
   }
  }
  
  I2C_Send7bitAddress(I2C1, slave_addr, I2C_Direction_Transmitter);  
  while(!(I2C1->SR1 & (1 << 1))); // check ADDR bit
  {
   if ((timeout--) == 0)    
   {
    ret = 2;
    goto exit;
   }
  }
  
  while(!(I2C1->SR2 & (1 << 2)));   // check TRA bit
  {
   if ((timeout--) == 0) 
   {
    ret = 3;
    goto exit;
   }
  }
  
  /* 2 byte reg address */
  if(addr_len == TWO_BYTE_REG_ADDR)
  {
   // MSB
   I2C_SendData(I2C1, (0xFF & (reg_addr >> 8)) );   
   while(!(I2C1->SR1 & (1 << 7))); 
   {
   if ((timeout--) == 0) 
   {
    ret = 4;
    goto exit;
   }
  }
     
   // LSB
   I2C_SendData(I2C1, (0xFF & reg_addr));   
   while(!(I2C1->SR1 & (1 << 7)));
   {
   if ((timeout--) == 0) 
   {
    ret = 5;
    goto exit;
   }
  }
  }
  
  /* 1 byte reg address */
  else 
  {
   I2C_SendData(I2C1, (0xFF & reg_addr));
   while(!(I2C1->SR1 & (1 << 7)));
   {
   if ((timeout--) == 0) 
   {
    ret = 6;
    goto exit;
   }
  }
  }

  I2C_GenerateSTOP(I2C1, ENABLE);
  I2C_GenerateSTART(I2C1, ENABLE);
  while(!I2C_CheckEvent(I2C1,I2C_EVENT_MASTER_MODE_SELECT));
  {
   if ((timeout--) == 0) 
   {
    ret = 7;
    goto exit;
   }
  }
  I2C_Send7bitAddress(I2C1, slave_addr, I2C_Direction_Receiver);
  while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED)); // check ADDR bit
  {
   if ((timeout--) == 0) 
   {
    ret = 8;
    goto exit;
   }
  }
    
  
  I2C_AcknowledgeConfig(I2C1, DISABLE);
                /* Send STOP Condition */
                I2C_GenerateSTOP(I2C1, ENABLE);
  
  while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_RECEIVED));
  {
   if ((timeout--) == 0) 
   {
    ret = 10;
    goto exit;
   }
  }
  
  *data = I2C_ReceiveData(I2C1);
  I2C_AcknowledgeConfig(I2C1, ENABLE);
  return ret;
  
exit:
  I2C_GenerateSTOP(I2C1, ENABLE);
  return ret;
}

To write to and read from  the camera i defined two functions...




1
2
3
4
5
6
7
8
9
int camera_read_reg(u8 reg, u8* data)
{ 
 return i2c_receive_data(0x42, (u16) reg, data, ONE_BYTE_REG_ADDR);
}
/*******************************************************************************/
int camera_write_reg(u8 reg, u8* data)
{ 
 return i2c_send_data(0x43, (u16) reg, data, ONE_BYTE_REG_ADDR);
}

STEP 3

You have to configure the camera module as your desire. Following are the register configurations for OV7660. If you are having OV7670, please read the data sheet of OV7670 for configuration registers.


1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
int camera_config()
{
 int ret = 0;
 u8 data = 0;
 sprintf(&tempBuff[0],"CAMERA CONFIGURING\r\n");send(&tempBuff[0]); 
 
 
 ret = camera_read_reg(0x12, &data); //Reset all registers
  if(ret < 0)
  return ret;
 Delay(0xFFFF);
 data = 0x80;
 ret = camera_write_reg(0x12, &data);  
  if(ret < 0)
  return ret;
 Delay(0xFFFF);
 ret = camera_read_reg(0x12, &data); //output format to qcif
  if(ret < 0)
  return ret;
 Delay(0xFFFF);
 data = 0xA;               //yuv enabled
 ret = camera_write_reg(0x12, &data);  
  if(ret < 0)
  return ret;
 Delay(0xFFFF);
 ret = camera_read_reg(0x11, &data); //Set PCLK
  if(ret < 0)
  return ret;
 Delay(0xFFFF);
 data = 0x8;
 ret = camera_write_reg(0x11, &data);  
  if(ret < 0)
  return ret;
 Delay(0xFFFF);
 ret = camera_read_reg(0x04, &data); //Set qqcif
  if(ret < 0)
  return ret;
 Delay(0xFFFF);
 data = 0x28;
 ret = camera_write_reg(0x04, &data);  
  if(ret < 0)
  return ret;
 Delay(0xFFFF);
 
 ret = camera_read_reg(0x3A, &data); //Set manual UV values
  if(ret < 0)
  return ret;
 Delay(0xFFFF);
 data = 0x1C;
 ret = camera_write_reg(0x3A, &data);  
  if(ret < 0)
  return ret;
 Delay(0xFFFF);
 
 ret = camera_read_reg(0x67, &data); //Set manual U value
  if(ret < 0)
  return ret;
 Delay(0xFFFF);
 data = 0xFF;
 ret = camera_write_reg(0x67, &data);  
  if(ret < 0)
  return ret;
 Delay(0xFFFF);
 
 ret = camera_read_reg(0x68, &data); //Set manual V value
  if(ret < 0)
  return ret;
 Delay(0xFFFF);
 data = 0x0;
 ret = camera_write_reg(0x68, &data);  
  if(ret < 0)
  return ret;
 Delay(0xFFFF);
//      ret = camera_read_reg(0x40, &data); //Set out put data range
//   if(ret < 0)
//   return ret;
//  Delay(10);
//  data = 0xC0;
//  ret = camera_write_reg(0x40, &data);  
//   if(ret < 0)
//   return ret;
//  Delay(10);
//  
//  ret = camera_read_reg(0x17, &data); //Set HSTART 0
//   if(ret < 0)
//   return ret;
//  Delay(10);
//  data = 0x14;
//  ret = camera_write_reg(0x17, &data);  
//   if(ret < 0)
//   return ret;
//  Delay(10);
//  ret = camera_read_reg(0x18, &data); //Set HSTOP 40
//   if(ret < 0)
//   return ret;
//  Delay(10);
//  data = 0x3C;
//  ret = camera_write_reg(0x18, &data);  
//   if(ret < 0)
//   return ret;
//  Delay(10);
// //  
//  ret = camera_read_reg(0x19, &data); //Set VSTART 20  We cxan set number of raws
//   if(ret < 0)
//   return ret;
//  Delay(10);
//  data = 0x14;
//  ret = camera_write_reg(0x19, &data);  
//   if(ret < 0)
//   return ret;
//  Delay(10);
//  ret = camera_read_reg(0x1A, &data); //Set VSTOP 40
//   if(ret < 0)
//   return ret;
//  Delay(10);
//  data = 0x28;
//  ret = camera_write_reg(0x1A, &data);  
//   if(ret < 0)
//   return ret;
//  Delay(10);
 
sprintf(&tempBuff[0],"CAMERA CONFIGURING DONE\r\n");send(&tempBuff[0]);
return ret;  
}

 STEP 4

Now you have done all the necessary settings on camera side. F4 Discovery board has a DCMI interface & DMA module which can read data from camera module and save the data in memory automatically. Within that time processor can do any other work as processing the image. It is really useful to read the refference manual of the micro controller on how DCMI and DMA works before get into do anything. If you know all the theories behind ,then it is really easy to correct the errors while reading the data.

Note :- Some DCMI pins of STM32f4 discovery board has connected to other devices such as accelerometer and DAC. Try to get free pins, If there are no free pins use the existing one. Following are the pins I used.

    /* B7: VSYNC*/
    /* A4: HSYNC*/
    /* A6: PCLK*/
    /* C6: data0*/
    /* C7: data1*/
    /* C8: data2*/
    /* C9: data3*/
    /* E4: data4*/
    /* B6: data5*/
    /* E5: data6*/
    /* E6: data7*/

I'm sure above are the best pin selection from all the available DCMI pins. PA6 is connected to accelerometer. Although it is an input to the microcontroller it wont be a problem. For the safety I removed the path to accelerometer. All the other pins are working perfect.

Following is the code for GPIO,DCMI and DMA settings.


1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
#define RAW 72
#define COLUMNS 80  //I got only 80 columns instead of 88 columns
#define BYTESPERPIX 2
void DCMI_Configure(void)
{
  DCMI_InitTypeDef DCMI_InitStructure;
  DMA_InitTypeDef  DMA_InitStructure; 
NVIC_InitTypeDef NVIC_InitStructure;
  /* GPIOD Periph clock enable */
 RCC_AHB2PeriphClockCmd(RCC_AHB2Periph_DCMI, ENABLE);
        RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);
 RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE);
 RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOC, ENABLE);
 RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOD, ENABLE);
 RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOE, ENABLE);
 
        RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA2, ENABLE);
 
  /* Configure PD12, PD13, PD14 and PD15 in output pushpull mode */
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12 | GPIO_Pin_13| GPIO_Pin_14| GPIO_Pin_15;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;
  GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
  GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
  GPIO_Init(GPIOD, &GPIO_InitStructure);
 
   /* B7: VSYNC*/
    GPIO_PinAFConfig(GPIOB, GPIO_PinSource7, GPIO_AF_DCMI);
    /* A4: HSYNC*/
    GPIO_PinAFConfig(GPIOA, GPIO_PinSource4, GPIO_AF_DCMI);
    /* A6: PCLK*/
    GPIO_PinAFConfig(GPIOA, GPIO_PinSource6, GPIO_AF_DCMI);
    /* C6: data0*/
    GPIO_PinAFConfig(GPIOC, GPIO_PinSource6, GPIO_AF_DCMI);
    /* C7: data1*/
    GPIO_PinAFConfig(GPIOC, GPIO_PinSource7, GPIO_AF_DCMI);
    /* C8: data2*/  
    GPIO_PinAFConfig(GPIOC, GPIO_PinSource8, GPIO_AF_DCMI);
    /* C9: data3*/ 
    GPIO_PinAFConfig(GPIOC, GPIO_PinSource9, GPIO_AF_DCMI);
    /* E4: data4*/
    GPIO_PinAFConfig(GPIOE, GPIO_PinSource4, GPIO_AF_DCMI);
    /* B6: data5*/
    GPIO_PinAFConfig(GPIOB, GPIO_PinSource6, GPIO_AF_DCMI);
    /* E5: data6*/
    GPIO_PinAFConfig(GPIOE, GPIO_PinSource5, GPIO_AF_DCMI);
    /* E6: data7*/
    GPIO_PinAFConfig(GPIOE, GPIO_PinSource6, GPIO_AF_DCMI);
 /* DCMI GPIO configuration */

    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
    GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
    GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP ;
    GPIO_Init(GPIOA, &GPIO_InitStructure);
  
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
    GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
    GPIO_Init(GPIOA, &GPIO_InitStructure);
 
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
    GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
    GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP ;
    GPIO_Init(GPIOB, &GPIO_InitStructure);
 
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7 | GPIO_Pin_8 | GPIO_Pin_9;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
    GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
    GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP ;
    GPIO_Init(GPIOC, &GPIO_InitStructure);
 
    GPIO_InitStructure.GPIO_Pin =  GPIO_Pin_4 | GPIO_Pin_5 | GPIO_Pin_6;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
    GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
    GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP ;
    GPIO_Init(GPIOE, &GPIO_InitStructure);
 

      /* DCMI configuration */
   
      DCMI_InitStructure.DCMI_CaptureMode = DCMI_CaptureMode_SnapShot;  
      DCMI_InitStructure.DCMI_ExtendedDataMode = DCMI_ExtendedDataMode_8b; 
      DCMI_InitStructure.DCMI_CaptureRate = DCMI_CaptureRate_All_Frame;   
      DCMI_InitStructure.DCMI_VSPolarity = DCMI_VSPolarity_High;    
      DCMI_InitStructure.DCMI_HSPolarity = DCMI_HSPolarity_Low;           
      DCMI_InitStructure.DCMI_PCKPolarity = DCMI_PCKPolarity_Rising;       
      DCMI_InitStructure.DCMI_SynchroMode = DCMI_SynchroMode_Hardware;     
      
     
                                                                             
      DCMI_Init(&DCMI_InitStructure);
 
     
     //DCMI_ITConfig(DCMI_IT_VSYNC, ENABLE);
     //DCMI_ITConfig(DCMI_IT_LINE, ENABLE);
     //DCMI_ITConfig(DCMI_IT_FRAME, ENABLE);
     //DCMI_ITConfig(DCMI_IT_OVF, ENABLE);
     //DCMI_ITConfig(DCMI_IT_ERR, ENABLE);
   
 
 
      /* Configures the DMA2 to transfer Data from DCMI */

      /* DMA2 Stream1 Configuration */
      DMA_DeInit(DMA2_Stream1);
  
      DMA_StructInit(&DMA_InitStructure);
  
      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 = ROW*COLUMNS*BYTESPERPIX/4;/* size of image in bytes/4 */     
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_Word;
      DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;
      DMA_InitStructure.DMA_Priority = DMA_Priority_VeryHigh;
      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_ITConfig(DMA2_Stream1, DMA_IT_TC, ENABLE);
      DMA_ITConfig(DMA2_Stream1, DMA_IT_TE, ENABLE);
   
      /* DMA2 IRQ channel Configuration */
      
NVIC_InitStructure.NVIC_IRQChannel = DMA2_Stream1_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
   
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);
NVIC_InitStructure.NVIC_IRQChannel = DCMI_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
   
DMA_Cmd(DMA2_Stream1, ENABLE);
   
DCMI_Cmd(ENABLE);
DCMI_CaptureCmd(ENABLE);
   
   
 
 

}

STEP 5

You can use interrupts of DCMI and DMA for get to know whether the frame has been received or not or if anything is wrong in the settings. There are many interrupts in DCMI and DMA module.

In DMA we can see following interrupts
  *   DMA_IT_TCIFx:Streamx transfer complete interrupt
  *   DMA_IT_HTIFx:  Streamx half transfer complete interrupt
  *   DMA_IT_TEIFx:  Streamx transfer error interrupt
  *   DMA_IT_DMEIFx: Streamx direct mode error interrupt
  *   DMA_IT_FEIFx:  Streamx FIFO error interrupt

Most useful interrupt is " transfer complete interrupt". Using this ,we can identify a frame has been successfully transferred by DMA to the memory.

Following is the code for interrupt.


1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
void DMA2_Stream1_IRQHandler(void)
{
 static  int K;
  //Test on DMA2 Channel1 Transfer Complete interrupt
  if(DMA_GetITStatus(DMA2_Stream1,DMA_IT_TCIF1) ==  SET)
  {
  frame_flag = 1;//when frame_flag =1,all the data will be send through serial port in main function while loop
    DMA_ClearITPendingBit(DMA2_Stream1,DMA_IT_TCIF1);
  }
  if(DMA_GetITStatus(DMA2_Stream1,DMA_IT_TEIF1) ==  SET)
  {
    sprintf(&tempBuff[0]," Dma error \r\n");send(&tempBuff[0]);  
    DMA_ClearITPendingBit(DMA2_Stream1,DMA_IT_TEIF1);
  } 
}

By using following DCMI interrupts we can know a frame has been received or not. A overflow has been occured or not. An overflow occurs when DMA did not transfer data from Data register of DCMI module to destination memory. So you must set DMA Mode as Circular.


1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
void DCMI_IRQHandler(void)
{
 static volatile int line,col,i,j = 0;
    if(DCMI_GetFlagStatus(DCMI_FLAG_FRAMERI) == SET)
    {
   sprintf(&tempBuff[0],"Frame got\r\n");send(&tempBuff[0]);
   DCMI_ClearFlag(DCMI_FLAG_FRAMERI);   
    }
   if(DCMI_GetFlagStatus(DCMI_FLAG_OVFRI) == SET)
    {
   sprintf(&tempBuff[0],"overflow\r\n");send(&tempBuff[0]);
   DCMI_ClearFlag(DCMI_FLAG_OVFRI);   
    }
 
}

If you have declare the DMA buffer size well, both the DMa transfer complete interrupt and DCMI frame received flag should occur same time(small delay).

I suggest you to first read a snap shot by configuring as,
"DCMI_InitStructure.DCMI_CaptureMode = DCMI_CaptureMode_SnapShot;"
Once you get snap shot, then move to Continuous mode.

Following is the code in main function, Once transfer complete interrupt occur in DMA module, following code will send data through serial port.


1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
while (1)
  {
  if( frame_flag == 1)
  {
    show_image();
    frame_flag = 0;
  }   
  }
}


  
void show_image(void)
  {
   for( K =0;K< WIDTH*HEIGHT*BYTES_PER_PIXEL/4;K++)
   { 
    {
    sprintf(&tempBuff[0],"%d \n",frame_buffer[4*K+1]);send_no_new_line(&tempBuff[0]);
    sprintf(&tempBuff[0],"%d \n",frame_buffer[4*K+3]);send_no_new_line(&tempBuff[0]);
    }
    if ((K+1)%40 == 0 )
    {
     sprintf(&tempBuff[0],";\r\n");send(&tempBuff[0]);
    }
   }
  }

I used QQCIF format to get image for my application. I need to compute the optical flow for stabalize  quad copter in horizontal plane. so QQCIF format is almost enough for me. Following is the image I took from the camera.
 I used matlab to plot the image from byte array which is coming from serial port at 921600 baud rate. I will post how to read data from serial port and show image in next post.


6 comments:

  1. where is the initial assignment to tempBuff ?

    ReplyDelete
  2. I2c not working in my code please send complete project file

    ReplyDelete

  3. I want to do this project with stm32f103. Can you help me?

    ReplyDelete
  4. Hello. I read your interesting article and I want to know what tool you used to program your project, if you used STM32CubeMX or some other tool. Thanks in advance.

    Jaime Olvera

    ReplyDelete
  5. Dear Sir,
    its a very good post, can you please post the sequence of code in main().
    thanks in advance

    ReplyDelete