最近學習STM32做了OV7620的相關調試工作,剛開始是很困難的,慢慢的搞清楚原理和配置之后就開始熟悉了,目前主要可以采用三種采集模式進行攝像頭數據采集,基本過程差不多,分別是配置IO口,配置SCCB,配置采集控制方式(場、行、像素時鐘),送顯(可以采用兩種送顯方式,一是圖像數據采集完送顯,二是邊采集邊送顯)。
1.GPIO直接采集,就是使用GPIO口直接采集攝像頭的數據,這種方式需要高速的IO翻轉速度,如果單片機速度不夠很難實現(采用STM32F103c8t6完成實現)。具體過程:(1)將A0-A7接攝像頭Y0-Y7,VSYNC、HREF、PCLK分別接B0-B2,SCL和SDA接B10和B11,RST接地,采用5V供電;(2)配置A0-A7為懸浮輸入,攝像頭數據就在GPIOA_ODR寄存器讀取,配置SCCB,配置OV7620的寄存器(可以輸出不同的分辨率和窗口,可以只輸出亮度信息或彩色YUV輸出),配置B0-B2為外部中斷觸發,分別控制場中斷和行中斷比較好實現,注意中斷HREF是上升沿采集數據,場中斷VSYNC是下降沿采集,配置中斷優先級,場優先級要高于行,一場采集結束后給一個標志位,送顯,或者每采集完一行就直接送顯。
2.采用定時器捕獲像素信號PCLK,用DMA傳輸,與GPIO采集不同的是,多了一個PCLK像素采集,這里可以將PCLK連接TIM_x設置成捕獲模式,捕獲到數據時觸發DMA傳輸,依然需要控制場中斷和行中斷,以及送顯。
3.采用DCMI接口進行采集,將Y0-Y7接STM32F4的DCMI接口D0-D7,VSYNC、HREF、PCLK接對應的DCMI_x,SCL和SDA使用GPIO模式,這里重點是DCMI的配置和DMA的配置,DMA的設備地址是DCMI,首地址是圖像buffer的首地址或者是LCD_RAM(直接送顯),采用硬件同步模式,VSYNC高電平有效(下降沿),HREF低電平有效(上升沿),PCLK上升沿觸發,必須配置好采集的時鐘控制才能出圖像。
可以多參考原子和野火的例程,原子是OV2640,重點弄清楚攝像頭的采集原理和時序,其他的代碼都差不多。
部分代碼如下,有用的可以參考:
//輸入中斷配置
void EXTI_Config(void)
{
GPIO_InitTypeDef GPIO_InitStruct;
EXTI_InitTypeDef EXTI_InitStruct;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1|GPIO_Pin_2;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_Init(GPIOB, &GPIO_InitStruct);
GPIO_EXTILineConfig(GPIO_PortSourceGPIOB, GPIO_PinSource1);
EXTI_InitStruct.EXTI_Line = EXTI_Line1;
EXTI_InitStruct.EXTI_Mode = EXTI_Mode_Interrupt;
EXTI_InitStruct.EXTI_Trigger = EXTI_Trigger_Rising;
EXTI_InitStruct.EXTI_LineCmd = ENABLE;
EXTI_Init(&EXTI_InitStruct);
EXTI_GenerateSWInterrupt(EXTI_Line1);
GPIO_EXTILineConfig(GPIO_PortSourceGPIOB, GPIO_PinSource0);
EXTI_InitStruct.EXTI_Line = EXTI_Line0;
EXTI_InitStruct.EXTI_Mode = EXTI_Mode_Interrupt;
EXTI_InitStruct.EXTI_Trigger = EXTI_Trigger_Falling;
EXTI_InitStruct.EXTI_LineCmd = ENABLE;
EXTI_Init(&EXTI_InitStruct);
EXTI_GenerateSWInterrupt(EXTI_Line0);
}
void NVIC_Config(void)
{
NVIC_InitTypeDef NVIC_InitStruct;
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);
// NVIC_InitStruct.NVIC_IRQChannel = EXTI2_IRQn;
// NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 1;
// NVIC_InitStruct.NVIC_IRQChannelSubPriority =
// NVIC_InitStruct.NVIC_IRQChannelCmd = DISABL
// NVIC_Init(&NVIC_InitStruct
NVIC_InitStruct.NVIC_IRQChannel = EXTI1_IRQn;
NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 1;
NVIC_InitStruct.NVIC_IRQChannelSubPriority = 1;
NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStruct);
NVIC_InitStruct.NVIC_IRQChannel = EXTI0_IRQn;
NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 0;
NVIC_InitStruct.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStruct);
}
//DCMI
void DCMI_DMA_Init(u32 DMA_Memory0BaseAddr,u16 DMA_BufferSize,u32 DMA_MemoryDataSize,u32 DMA_MemoryInc)
{
DMA_InitTypeDef DMA_InitStructure;
// NVIC_InitTypeDef NVIC_InitStructur
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA2,ENABLE);
DMA_DeInit(DMA2_Stream1);
while (DMA_GetCmdStatus(DMA2_Stream1) != DISABLE){}
DMA_InitStructure.DMA_Channel = DMA_Channel_1;
DMA_InitStructure.DMA_PeripheralBaseAddr =(u32)&DCMI->DR;
DMA_InitStructure.DMA_Memory0BaseAddr = DMA_Memory0BaseAddr;
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralToMemory;
DMA_InitStructure.DMA_BufferSize = DMA_BufferSize;
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc;
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Word;
DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize;
DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;
DMA_InitStructure.DMA_Priority = DMA_Priority_High;
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);
}
//DCMI3?ê?
void My_DCMI_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA|RCC_AHB1Periph_GPIOB|RCC_AHB1Periph_GPIOC|RCC_AHB1Periph_GPIOE, ENABLE);
RCC_AHB2PeriphClockCmd(RCC_AHB2Periph_DCMI,ENABLE);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4|GPIO_Pin_6;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;
GPIO_Init(GPIOA, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_7|GPIO_Pin_6;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
GPIO_Init(GPIOB, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6|GPIO_Pin_7|GPIO_Pin_8|GPIO_Pin_9|GPIO_Pin_11;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
GPIO_Init(GPIOC, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin =GPIO_Pin_5|GPIO_Pin_6;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
GPIO_Init(GPIOE, &GPIO_InitStructure);
GPIO_PinAFConfig(GPIOA,GPIO_PinSource4,GPIO_AF_DCMI);
GPIO_PinAFConfig(GPIOA,GPIO_PinSource6,GPIO_AF_DCMI);
GPIO_PinAFConfig(GPIOB,GPIO_PinSource7,GPIO_AF_DCMI);
GPIO_PinAFConfig(GPIOC,GPIO_PinSource6,GPIO_AF_DCMI);
GPIO_PinAFConfig(GPIOC,GPIO_PinSource7,GPIO_AF_DCMI);
GPIO_PinAFConfig(GPIOC,GPIO_PinSource8,GPIO_AF_DCMI);
GPIO_PinAFConfig(GPIOC,GPIO_PinSource9,GPIO_AF_DCMI);
GPIO_PinAFConfig(GPIOC,GPIO_PinSource11,GPIO_AF_DCMI);
GPIO_PinAFConfig(GPIOB,GPIO_PinSource6,GPIO_AF_DCMI);
GPIO_PinAFConfig(GPIOE,GPIO_PinSource5,GPIO_AF_DCMI);
GPIO_PinAFConfig(GPIOE,GPIO_PinSource6,GPIO_AF_DCMI);
DCMI_DeInit();
DCMI_InitStructure.DCMI_CaptureMode=DCMI_CaptureMode_Continuous;
DCMI_InitStructure.DCMI_CaptureRate=DCMI_CaptureRate_All_Frame;
DCMI_InitStructure.DCMI_ExtendedDataMode= DCMI_ExtendedDataMode_8b;
DCMI_InitStructure.DCMI_PCKPolarity= DCMI_PCKPolarity_Rising;
DCMI_InitStructure.DCMI_VSPolarity=DCMI_VSPolarity_High;
DCMI_InitStructure.DCMI_HSPolarity= DCMI_HSPolarity_Low;
DCMI_InitStructure.DCMI_SynchroMode= DCMI_SynchroMode_Hardware;
DCMI_Init(&DCMI_InitStructure);
// DCMI_ITConfig(DCMI_IT_FRAME,ENABLE
DCMI_ITConfig(DCMI_IT_VSYNC,ENABLE);
DCMI_ITConfig(DCMI_IT_LINE,DISABLE);
// DCMI_ITConfig(DCMI_IT_ERR, DISABLE
// DCMI_ITConfig(DCMI_IT_OVF, DISABLE
DCMI_Cmd(ENABLE);
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);
NVIC_InitStructure.NVIC_IRQChannel = DCMI_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=1;
NVIC_InitStructure.NVIC_IRQChannelSubPriority =2;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
}
//DCMI,???ˉ′?
void DCMI_Start(void)
{
LCD_SetCursor(0,0);
LCD_WriteRAM_Prepare();
DMA_Cmd(DMA2_Stream1, ENABLE);
DCMI_CaptureCmd(ENABLE);
}
//DCMI,1?±?′?
void DCMI_Stop(void)
{
DCMI_CaptureCmd(DISABLE);
while(DCMI->CR&0X01);
DMA_Cmd(DMA2_Stream1,DISABLE);
LCD_SetCursor(0,0);
LCD_WriteRAM_Prepare();
}
void DCMI_IRQHandler(void)
{
if (DCMI->MISR & DCMI_IT_VSYNC)
{
DCMI_ClearITPendingBit(DCMI_IT_VSYNC);
// LED1=!LED
Flag=1;
// DCMI_Stop()
// DMA2_Stream1->M0AR = (uint32_t)&(Image[0])
DCMI_ITConfig(DCMI_IT_LINE,ENABLE);
ImgControl.lines = 0;
ImgControl.ImgOk = false;
ov_frame++;
}
if (DCMI->MISR & DCMI_IT_LINE)
{
DCMI->ICR = DCMI_IT_LINE;
if(ImgControl.ImgOk == false)
{
if(ImgControl.lines<camera_H)
{
ImgControl.lines++;
DMA2_Stream1->CR &= (uint32_t)~DMA_SxCR_EN;
DMA2_Stream1->M0AR = (uint32_t)&(Image[ImgControl.lines]);
DMA2_Stream1->CR |= DMA_SxCR_EN;
DCMI->CR |= DCMI_CR_CAPTURE;
}
if(ImgControl.lines ==camera_H)
{
DCMI_ITConfig(DCMI_IT_LINE,DISABLE);
ImgControl.ImgOk = true;
DMA2_Stream1->CR &= (uint32_t)~DMA_SxCR_EN;
}
}
}
if (DCMI->MISR & DCMI_IT_FRAME)
{
DCMI->ICR = DCMI_IT_FRAME;
}
if (DCMI->MISR &DCMI_IT_ERR)
{
DCMI->ICR = DCMI_IT_ERR;
}
}
//8位亮度轉RGB565
uint16_t ChangeYtoRGB(uint8_t Y)
{
uint16_t RGB = 0;
uint8_t R = 0,G = 0,B = 0;
R = Y >> 3;
G = Y >> 2;
B = Y >> 3;
RGB = (R<<6) | G;
RGB = (RGB << 5) | B;
return RGB;
}
//圖像拉伸處理(雙線性插值
int is_in_array(short x,short y, int height, int width)
{
if (x >= 0 && x < width && y >= 0 && y <height)
return 1;
else
return 0;
}
//void bilinera_interpolation(u8 in_array[240][320],u8 out_array[240][240])
//{
// u32 height=240;
// u32 width=32
// u32 out_height=240
// u32 out_width=24
// float h_times = out_height / height
// float w_times = out_width / widt
// int x, y
// int x1, y1, x2, y2, f11, f12, f21, f2
// int i,
// for ( i = 0; i < out_height; i++
// for (j = 0; j < out_width; j++
// x = j / w_times*10
// y = i / h_times*10
// x1 = (int)(x - 100
// x2 = (int)(x + 100
// y1 = (int)(y + 100
// y2 = (int)(y- 100
// f11 = is_in_array(x1, y1, height, width) ? in_array[y1][x1] :
// f12 = is_in_array(x1, y2, height, width) ? in_array[y2][x1] :
// f21 = is_in_array(x2, y1, height, width) ? in_array[y1][x2] :
// f22 = is_in_array(x2, y2, height, width) ? in_array[y2][x2] :
// out_array[i][j] = (u8)(((f11 * (x2 - x) * (y2 - y))
// (f21 * (x - x1) * (y2 - y))
// (f12 * (x2 - x) * (y - y1))
// (f22 * (x - x1) * (y - y1))) / ((x2 - x1) * (y2 - y1))
//
//
/
//sccb
/ SCCB_Write_Reg(0x14,0x04);
// SCCB_Write_Reg(0x14,0x04)
// SCCB_Write_Reg(0x11,0x02);
// SCCB_Write_Reg(0x17,0x57)
// SCCB_Write_Reg(0x18,0xA7);
// SCCB_Write_Reg(0x19,0x06)
// SCCB_Write_Reg(0x1A,0xF6
// SCCB_Write_Reg(0x6A,0x42);
// SCCB_Write_Reg(0x12,0x34)
// SCCB_Write_Reg(0x15,0x01);
// SCCB_Write_Reg(0x05,0x60);
// SCCB_Write_Reg(0x06,0x80);
// SCCB_Write_Reg(0x6A,0x42);
// SCCB_Write_Reg(0x28,0x20)
//YUV轉rgb888再轉RGB5
// R0=(u16)((1164*(Image[i][j+1]-16)+1596*(Image[i][j]-128))/1000);
// B0=(u16)((1164*(Image[i][j+1]-16)+2018*(Image[i][j+2]-128))/1000
// G0=(u16)((1164*(Image[i][j+1]-16)-813*(Image[i][j]-128)-391*(Image[i][j+2]-128))/1000);
// R0=R0>255?255:(R0<1?1:R0);
// G0=G0>255?255:(G0<1?1:G0);
// B0=B0>255?255:(B0<1?1:B0);
// RGB0=((B0<<8)&0xF800)|((G0<<3)&0x7E0)|(R0>>3
// LCD->LCD_RAM=RGB0
// R1=(u16)((1164*(Image[i][j+3]-16)+1596*(Image[i][j]-128))/1000);
// B1=(u16)((1164*(Image[i][j+3]-16)+2018*(Image[i][j+2]-128))/1000
// G1=(u16)((1164*(Image[i][j+3]-16)-813*(Image[i][j]-128)-391*(Image[i][j+2]-128))/1000);
// R1=R1>255?255:(R1<1?1:R1);
// G1=G1>255?255:(G1<1?1:G1);
// B1=B1>255?255:(B1<1?1:B1)
// RGB1=((B1<<8)&0xF800)|((G1<<3)&0x7E0)|(R1>>3);
// LCD->LCD_RAM=RGB1;
//中值濾波
gray=ChangeYtoRGB(Image[i][j]);
// LCD->LCD_RAM=gra
// LCD->LCD_RAM=0XFFE0;
// if(Image[i][j]>=12
//
// LCD->LCD_RAM=65535;
//
// el
//
// LCD->LCD_RAM=
// if(i==
// {
// img=Image[0][j
// gray=ChangeYtoRGB(img
// LCD->LCD_RAM=gra
//
// if(i==23
//
// img=Image[239][j
// gray=ChangeYtoRGB(img
// LCD->LCD_RAM=gra
//
// if(j==
//
// img=Image[i][0
// gray=ChangeYtoRGB(img
// LCD->LCD_RAM=gray
//
// if(j==319
// img=Image[i][319
// gray=ChangeYtoRGB(img
// LCD->LCD_RAM=gray;
// el
//
// img=(u8)((Image[i-1][j-1]+Image[i-1][j]+Image[i-1][j+1]+Image[i][j-1]+Image[i][j]+Image[i][j+1]+Image[i+1][j-1]+Image[i+1][j]+Image[i+1][j+1])/9);
// gray=ChangeYtoRGB(img
// LCD->LCD_RAM=gray
//
//邊緣檢測
for(i=0;i<240;i++)
// {
// left_flag1=
// right_flag1=
// left=
// right=
// LCD_SetCursor(0,i
// LCD_WriteRAM_Prepare()
// for(j=0;j<320;j+
//
// img1[j]=Image[i][j]
//// img[i][j]=Image[i]
//// if(Image[i][j]>=
//// Image[i][j]=0xff;
////
//// Image[i][j]=0;
//
//
// for(j=3;j<320;j++)
// {
// if(Image[i][j-1]-Image[i][j]>Threshold)
//
//// left
// Image[i][j-1]=COLO
// Image[i][j-2]=COLOR;
//// if(Image[i-1][j]-Image[i][j]>Threshold1
//// Image[i][j]=CO
//// if(Image[i+1][j]-Image[i][j]>Threshold1
//// Image[i][j]=CO
//
// else
//
// Image[i][j-1]=
// Image[i][j-2]=
//
// if(Image[i][j+1]-Image[i][j]>Threshold)
//
//// righ
// Image[i][j+1]=COLO
// Image[i][j+2]=COLOR;
//// if(Image[i-1][j]-Image[i][j]>Threshold1
//// Image[i][j]=CO
//// if(Image[i+1][j]-Image[i][j]>Threshold1
//// Image[i][j]=CO
//
// else
//
// Image[i][j+1]=
// Image[i][j+2]=
//
//
//// if((left_flag1==1) && (right_flag1==1))
//
//// if((right-left>=BLACK_LINE_MIN0)&&(right-left<=BLACK_LINE_MAX0))
//// {Line_Center[i]=(right+left)/2
///
//
//// Image[i][(right+left)/2]=0x
//// Image[i][(right+left)/2+1]=0x
//// Image[i][(right+left)/2-1]=0x
/
// for(j=0;j<320;j+
//
/
// gray=ChangeYtoRGB(img1[j]
//// LCD->LCD_RAM=gr
// LCD->LCD_RAM=(u16)(Image[i][j]<<8)+Image[i][j]+gray;
//// if(Image[i][j]>=
//// LCD->LCD_RAM=65535;
////
//// LCD->LCD_RA
//// gray=ChangeYtoRGB(Image[i][j]
//// LCD->LCD_RAM=gr
//
//
//
// DCMI_Start(
// ImgControl.ImgDealOk= true;
/
//
//徑向畸變矯正
// float normalizedX
// float normalizedY
// float distance
// float radialDistor
/
/
// int CAM_U0=269;
// int CAM_V0=24
// float CAM_FX=1674.5
// float CAM_FY=1675.3
// float CAM_K1=-0.2
// float CAM_K2=-0.7
/
/
// uchar sourceV
// int distorPixelPositionX
// int distorPixelPositionY;
/
/
// for(int h = 0; h < 240; h+
//
// for(int w = 0; w < 320; w+
//
// normalizedX = ((float)w - CAM_U0) / CAM_F
// normalizedY = ((float)h - CAM_V0) / CAM_F
// distance = normalizedX * normalizedX + normalizedY * normalized
// radialDistor = CAM_K1 * distance + CAM_K2 * pow(distance , 2)
// distorPixelPositionX = w+((float)w-CAM_U0)*radialDisto
// distorPixelPositionY = h+((float)h-CAM_V0)*radialDisto
// if(distorPixelPositionX < 0 || distorPixelPositionX >=24
//
// distorPixelPositionX =
//
// if(distorPixelPositionY < 0 || distorPixelPositionY >=32
//
// distorPixelPositionY =
//
// sourceV = Image(distorPixelPositionY,distorPixelPositionX)
// compensatedImg.at<uchar>(h,w)=source
/
//
//