学习摄像头之前,有一件事,特别麻烦,如何证明摄像头工作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,真见鬼了,竟然如此:
测量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再说吧.
程序:DCMI摄像头DMA实验