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.
You must check the clock is worming correctly using a logic analyzer or a oscilloscope before move to next step.
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...