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...
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); } |
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.
where is the initial assignment to tempBuff ?
ReplyDeleteI2c not working in my code please send complete project file
ReplyDeletethis code doesn't worked
ReplyDelete
ReplyDeleteI want to do this project with stm32f103. Can you help me?
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.
ReplyDeleteJaime Olvera
Dear Sir,
ReplyDeleteits a very good post, can you please post the sequence of code in main().
thanks in advance
Great and that i have a tremendous provide: Whole House Renovation Checklist Pdf split level kitchen remodel
ReplyDelete