1.概述 采用无数字口的单片机采集摄像头数据,通过模拟摄像头的采集时序进行图像采集,采集速度完全取决于单片机IO口的翻转速度和CPU处理速度; 硬件:stm32f401/411/405,M4内核,带DSP/FPU单精度浮点运算单元,当然F103也能用,但不建议用F103,因为RAM资源和内核,主频等等都跟不上,性能太差没有实际意义; 摄像头以OV7670无FIFO为例,最大像素640x480; 2.采集原理 (1)摄像头时序: PCLK :像素时钟,一个像素时钟出一个8bit数据 XCLK:摄像头外部时钟,用于给摄像头提供时钟信号; HSYNC:行同步信号,代表一行数据的起始,触发这个信号就开始一行图像的采集; VSYNC:场同步信号,代表一场数据的起始,触发这个信号就开始一帧图像采集; DATA 7-0:数据位bit7-bit0,一个RGB565的数据由两个data像素组成,从高到低依次传输 SCL&SDA:摄像头控制接口,寄存器配置,符合I2C/SCCB时序 RST :摄像头硬件复位信号,默认拉高 PWDN:上电时序相关,默认拉低 摄像头时钟24MHz 30帧/s,12MHz,15帧/s,可通过设置时钟分频和内部倍频机制合理控制时钟;
(2)IO配置 两种采集方式: 第一种采用全部普通GPIO模式,通过GPIO模拟摄像头时序,然后手动读取D0-D7的数据,尽量全部采集寄存器处理和指针处理方式会更快; 第二种采用定时器输入捕获+中断处理+DMA传输方式采集,控制行场信号,然后使用DMA搬运每行的数据; (3)摄像头采集流程 第一种:GPIO直接采集
int liencnt=0;
int pixcnt=0;
unsigned char* pimage=NULL;
pimage=mymalloc(SRAMIN,cam_length);
mymemset(pimage,0,cam_length);
while(1)
{
unsigned char* pimg_temp=pimage;
while(OV_VSYNC==1);
while(OV_VSYNC==0)
{
for (linecnt=0;linecnt<cam_height;linecnt++)
{
while(OV_HSYNC==0);
for(pixcnt=0;pixcnt<cam_width*2;pixcnt++)
{
while(OV_PCLK==0);
*pimg_temp=GPIOC->IDR&0XFF;
while(OV_PCLK==1);
pimg_temp++;
}
}
}
}
第二种:中断+DMA采集
场中断:低电平有效 行中断:定时器输入捕获
static void VSYNC_Exit_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_7;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_Init(GPIOB, &GPIO_InitStructure);
EXTI_InitTypeDef EXTI_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);
GPIO_EXTILineConfig(GPIO_PortSourceGPIOB,GPIO_PinSource7);
EXTI_ClearITPendingBit(EXTI_Line7);
EXTI_InitStructure.EXTI_Line = EXTI_Line7;
EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rasing;
EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
EXTI_InitStructure.EXTI_LineCmd = ENABLE;
EXTI_Init(&EXTI_InitStructure);
NVIC_InitTypeDef NVIC_InitStructure ;
NVIC_InitStructure.NVIC_IRQChannel = EXTI9_5_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
}
void TIM4_Cap_Init(u16 arr,u16 psc)
{
GPIO_InitTypeDef GPIO_InitStructure;
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
NVIC_InitTypeDef NVIC_InitStructure;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPD;
GPIO_Init(GPIOB, &GPIO_InitStructure);
GPIO_ResetBits(GPIOB,GPIO_Pin_6);
TIM_TimeBaseStructure.TIM_Period = arr;
TIM_TimeBaseStructure.TIM_Prescaler =psc;
TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInit(TIM4, &TIM_TimeBaseStructure);
TIM4_ICInitStructure.TIM_Channel = TIM_Channel_1;
TIM4_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Rising;
TIM4_ICInitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI;
TIM4_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1;
TIM4_ICInitStructure.TIM_ICFilter = 0x00;
TIM_ICInit(TIM4, &TIM4_ICInitStructure);
NVIC_InitStructure.NVIC_IRQChannel = TIM4_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
TIM_ITConfig(TIM4,TIM_IT_Update|TIM_IT_CC1,ENABLE);
TIM_Cmd(TIM4,ENABLE );
}
void TIM4_DMA_Config(DMA_Channel_TypeDef* DMA_CHx,u32 cpaddr,u32 cmaddr,u32 cndtr)
{
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);
DMA_DeInit(DMA_CHx);
DMA_InitStructure.DMA_Channel=DMA_Channel_2;
DMA_InitStructure.DMA_PeripheralBaseAddr = cpaddr;
DMA_InitStructure.DMA_MemoryBaseAddr = cmaddr;
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;
DMA_InitStructure.DMA_BufferSize = cndtr;
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;
DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;
DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;
DMA_InitStructure.DMA_Priority = DMA_Priority_High;
DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
DMA_Init(DMA_CHx, &DMA_InitStructure);
}
void capture_start(void)
{
EXIT_ITConfig(EXIT_Line7,ENABLE);
TIM_ITConfig(TIM4,TIM_IT_Update|TIM_IT_CC1,ENABLE);
TIM_DMACmd(TIM4, TIM_DMA_CC1, ENABLE);
}
void capture_stop(void)
{
EXIT_ITConfig(EXIT_Line7,DISABLE);
TIM_ITConfig(TIM4,TIM_IT_Update|TIM_IT_CC1,DISABLE);
TIM_DMACmd(TIM4, TIM_DMA_CC1,DISABLE);
}
int linecnt=0;
void EXIT9_5_IRQHandler(void)
{
if(EXTI_GetITStatus(EXIT_Line7)==SET)
{
EXIT_ClearITPendingBit(EXIT_Line7);
capture_stop();
read_ok=1;
frame++;
linecnt=0;
}
}
void TIM4_IRQHandler(void)
{
if(TIM_GetITStatus(TIM4,TIM_IT_CC1)!=RESET)
{
TIM_ClearITPendingBit(TIM4, TIM_IT_CC1|TIM_IT_Update);
TIM_SetCounter(TIM4,0);
if(linecnt <=cam_height)
{
while(DMA_GetFlagStatus(DMA1_FLAG_TC2)==RESET);
DMA_ClearFlag(DMA1_FLAG_TC2);
DMA1_Stream0->CR &= (uint32_t)~DMA_SxCR_EN;
DMA1_Stream0->M0AR = (uint32_t)&(Image[linecnt++]);
DMA1_Stream0->CR |= DMA_SxCR_EN;
}
if(linecnt >cam_height)
{
DMA1_Stream0->CR &= (uint32_t)~DMA_SxCR_EN;
DMA_Cmd(DMA1_Stream0, DISABLE);
TIM_ITConfig(TIM4,TIM_IT_Update|TIM_IT_CC1,DISABLE);
}
}
}
```
|