STM32F4-DISCO 学习之 摄像头OV7670(I2C,DMA,DCMI)

学习摄像头之前,有一件事,特别麻烦,如何证明摄像头工作OK呢?比较好的办法,就是摄像头到屏幕,DMA内存到内存.可是,我们每次只能送8位,而摄像头本身可以配置是RGB565模式,16位呢,所以有两个办法,办法1就是换用FSMC驱动的屏幕,16位妥妥的,第二个办法,先内存到RAM的DMA,然后CPU不断翻译那部分RAM,送到屏幕上,第一种实时性很好,但是占用IO过多,虽然摄像头自己也占了一堆IO,第三种,使用位宽不一样的DMA源和目标…

另外根据硬件来看,还涉及到I2C呢,所以这一次要掌握的内容是I2C,DMA和摄像头外设.首先要配置好摄像头外设,然后是DMA通道:

  DMA_InitStructure.DMA_Channel = DMA_Channel_1;
  DMA_InitStructure.DMA_PeripheralBaseAddr = DCMI_DR_ADDRESS;
  DMA_InitStructure.DMA_Memory0BaseAddr = (uint8_t)&SPI1->DR;
  DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralToMemory;
  DMA_InitStructure.DMA_BufferSize = 0xfffe;
  DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
  DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Disable;
  DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Word;
  DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;
  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_MemoryDataSize/DMA_PeripheralDataSize,因为我们是大传小,至于行不行,还不知道,不过可以试一下,另外,DCMI上,非常不幸,占用了我一个串口啊.至于PB6行不通,转战PA9也不行,因为有个大大的电容,另外TFT引脚也有占用,所以 ,为今之计,只能修改IO.修改到哪里,可以自行定义,也可以参考的代码设计[按照国际惯例,这博文后应该有代码提供的]

因为一开始调试时候,没有读OV7670的ID,也就认为他好了,后来读取发现有问题,才发现模块是坏的,只好再买了一个,就好了.这说明,读ID这种容错机制,应该到处都有啊.好吧,废话说了一堆,首先要做的事情是初始化各种IO,这里的I2C跟F0,F1的不一样,F1是残废,F0是身残志坚,F4是还可以了,直接代入速度还不用计算.

void SCCB_GPIO_Config(void)
{
    I2C_InitTypeDef  SCCB_InitStructure;
    GPIO_InitTypeDef  GPIO_InitStructure;
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C2, ENABLE);
    GPIO_PinAFConfig(GPIOB, GPIO_PinSource10, GPIO_AF_I2C2);
    GPIO_PinAFConfig(GPIOB, GPIO_PinSource11, GPIO_AF_I2C2);
    GPIO_InitStructure.GPIO_Pin =  GPIO_Pin_10 | GPIO_Pin_11;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
    GPIO_InitStructure.GPIO_OType = GPIO_OType_OD;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz;
    GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;
    GPIO_Init(GPIOB, &GPIO_InitStructure);
    I2C_DeInit(I2C2);
    SCCB_InitStructure.I2C_Mode = I2C_Mode_I2C;
    SCCB_InitStructure.I2C_DutyCycle = I2C_DutyCycle_2;
    SCCB_InitStructure.I2C_OwnAddress1 = SCCB_SLAVE_ADDRESS7;
    SCCB_InitStructure.I2C_Ack = I2C_Ack_Enable;
    SCCB_InitStructure.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit;
    SCCB_InitStructure.I2C_ClockSpeed = SCCB_SPEED; //速度kHz
    I2C_Cmd(I2C2, ENABLE);
    I2C_Init(I2C2, &SCCB_InitStructure);
    I2C_AcknowledgeConfig(I2C2, ENABLE);
}

而整个I2C的传输,就是不断判断各个标志就可以了,没什么问题的.这就差不多了,因为OV7670寄存器是I2C做的,其实就算很多高速芯片,比如HDMI桥什么的,也是I2C控制的很多哦,主要是CPU一般引脚紧缺,这是很重要的.配置DCMI的IO,之后配置一下DMA,好把数据送上去:

    DMA_InitStructure.DMA_Channel = DMA_Channel_1;
    DMA_InitStructure.DMA_PeripheralBaseAddr = DCMI_DR_ADDRESS;
    DMA_InitStructure.DMA_Memory0BaseAddr = (uint32_t) & (SPI1->DR);
    DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralToMemory;
    DMA_InitStructure.DMA_BufferSize = 0xfffe;
    DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
    DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Disable;
    DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Word;
    DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;
    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;

虽然DCMI是8Bit数据,但是他发送两次后,才能送入外设,也就是16位数据,另外寄存器长度32B,所以数据32B呢.不然我也可以直接DMA过去拉,同样的宽度.不过既然可以目标和源不用长度,也可以试一下这个配置.然后写0x07寄存器最高位0x80复位一下芯片.芯片手册:http://www.voti.nl/docs/OV7670.pdf 然后可以把配置表倒过去.至于这个配置,一来你自己可以查手册,其他如对比度条件,都在手册有写哦.把初始化送过去之后,摄像头就可以开始正常的采样.这时候需要DMA把他送上去,但是屏幕一般要写起始地址和结束地址啊,所以…用这个先把LCD初始化OK.

    /*Set LCD direction*/
    LCD_WriteReg(0x2a);
    LCD_WriteData(0x00);
    LCD_WriteData(0x00);
    LCD_WriteReg(0x2b);
    LCD_WriteData(0x00);
    LCD_WriteData(0x00);
    LCD_WriteReg(0x2C);
    GPIOA->BSRRH = LCD_CE;
    GPIOA->BSRRL = LCD_DC;
    /* Start Image capture and Display on the LCD *****************************/
    /* Enable DMA transfer */
    DMA_Cmd(DMA2_Stream1, ENABLE);

然后,观察LCD,真见鬼了,竟然如此:

QQ截图20150914160048

测量PCLK,竟然达到了20Hz[fps]呢,不行啊,所以,降低了MCO1的输出,修改0x73寄存器,分频到16分频,然后把SPI屏幕调成最高的速度,希望两边一均衡,就能显示了.测试显示,也还是很勉强的,看来,摄像头DMA到FSMC屏幕才是正道啊.[下图,SPI 42MHz TFT + RCC_MCO1Div_5 + PCLK / 16],其中168MHz/5=33MHz,然后再除16是2MHz,换算过去,也有2fps,而SPI其实42MHz下,带宽也就2fps左右.而且各种数据貌似处理不来,手抖一下,全花了.等学会了做BMP再说吧.

QQ截图20150914161031

 

程序:DCMI摄像头DMA实验

发表评论

您的电子邮箱地址不会被公开。 必填项已用*标注