- //初始化lcd
- //该初始化函数可以初始化各种ILI93XX液晶,但是其他函数是基于ILI9320的!!!
- //在其他型号的驱动芯片上没有测试!
- void LCD_Init(void)
- {
- vu32 i=0;
-
- GPIO_InitTypeDef GPIO_InitStructure;
- FSMC_NORSRAMInitTypeDef FSMC_NORSRAMInitStructure;
- FSMC_NORSRAMTimingInitTypeDef readWriteTiming;
- FSMC_NORSRAMTimingInitTypeDef writeTiming;
-
- RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB|RCC_AHB1Periph_GPIOD|RCC_AHB1Periph_GPIOE|RCC_AHB1Periph_GPIOF|RCC_AHB1Periph_GPIOG, ENABLE);//使能PD,PE,PF,PG时钟
- RCC_AHB3PeriphClockCmd(RCC_AHB3Periph_FSMC,ENABLE);//使能FSMC时钟
-
-
- GPIO_InitStructure.GPIO_Pin = GPIO_Pin_15;//PB15 推挽输出,控制背光
- GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;//普通输出模式
- GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;//推挽输出
- GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;//100MHz
- GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;//上拉
- GPIO_Init(GPIOB, &GPIO_InitStructure);//初始化 //PB15 推挽输出,控制背光
-
- GPIO_InitStructure.GPIO_Pin = (3<<0)|(3<<4)|(7<<8)|(3<<14);//PD0,1,4,5,8,9,10,14,15 AF OUT
- GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;//复用输出
- GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;//推挽输出
- GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;//100MHz
- GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;//上拉
- GPIO_Init(GPIOD, &GPIO_InitStructure);//初始化
-
- GPIO_InitStructure.GPIO_Pin = (0X1FF<<7);//PE7~15,AF OUT
- GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;//复用输出
- GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;//推挽输出
- GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;//100MHz
- GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;//上拉
- GPIO_Init(GPIOE, &GPIO_InitStructure);//初始化
- GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12;//PF12,FSMC_A6
- GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;//复用输出
- GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;//推挽输出
- GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;//100MHz
- GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;//上拉
- GPIO_Init(GPIOF, &GPIO_InitStructure);//初始化
- GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12;//PF12,FSMC_A6
- GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;//复用输出
- GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;//推挽输出
- GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;//100MHz
- GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;//上拉
- GPIO_Init(GPIOG, &GPIO_InitStructure);//初始化
- GPIO_PinAFConfig(GPIOD,GPIO_PinSource0,GPIO_AF_FSMC);//PD0,AF12
- GPIO_PinAFConfig(GPIOD,GPIO_PinSource1,GPIO_AF_FSMC);//PD1,AF12
- GPIO_PinAFConfig(GPIOD,GPIO_PinSource4,GPIO_AF_FSMC);
- GPIO_PinAFConfig(GPIOD,GPIO_PinSource5,GPIO_AF_FSMC);
- GPIO_PinAFConfig(GPIOD,GPIO_PinSource8,GPIO_AF_FSMC);
- GPIO_PinAFConfig(GPIOD,GPIO_PinSource9,GPIO_AF_FSMC);
- GPIO_PinAFConfig(GPIOD,GPIO_PinSource10,GPIO_AF_FSMC);
- GPIO_PinAFConfig(GPIOD,GPIO_PinSource14,GPIO_AF_FSMC);
- GPIO_PinAFConfig(GPIOD,GPIO_PinSource15,GPIO_AF_FSMC);//PD15,AF12
-
- GPIO_PinAFConfig(GPIOE,GPIO_PinSource7,GPIO_AF_FSMC);//PE7,AF12
- GPIO_PinAFConfig(GPIOE,GPIO_PinSource8,GPIO_AF_FSMC);
- GPIO_PinAFConfig(GPIOE,GPIO_PinSource9,GPIO_AF_FSMC);
- GPIO_PinAFConfig(GPIOE,GPIO_PinSource10,GPIO_AF_FSMC);
- GPIO_PinAFConfig(GPIOE,GPIO_PinSource11,GPIO_AF_FSMC);
- GPIO_PinAFConfig(GPIOE,GPIO_PinSource12,GPIO_AF_FSMC);
- GPIO_PinAFConfig(GPIOE,GPIO_PinSource13,GPIO_AF_FSMC);
- GPIO_PinAFConfig(GPIOE,GPIO_PinSource14,GPIO_AF_FSMC);
- GPIO_PinAFConfig(GPIOE,GPIO_PinSource15,GPIO_AF_FSMC);//PE15,AF12
-
- GPIO_PinAFConfig(GPIOF,GPIO_PinSource12,GPIO_AF_FSMC);//PF12,AF12
- GPIO_PinAFConfig(GPIOG,GPIO_PinSource12,GPIO_AF_FSMC);
- readWriteTiming.FSMC_AddressSetupTime = 0XF; //地址建立时间(ADDSET)为16个HCLK 1/168M=6ns*16=96ns
- readWriteTiming.FSMC_AddressHoldTime = 0x00; //地址保持时间(ADDHLD)模式A未用到
- readWriteTiming.FSMC_DataSetupTime = 60; //数据保存时间为60个HCLK =6*60=360ns
- readWriteTiming.FSMC_BusTurnAroundDuration = 0x00;
- readWriteTiming.FSMC_CLKDivision = 0x00;
- readWriteTiming.FSMC_DataLatency = 0x00;
- readWriteTiming.FSMC_AccessMode = FSMC_AccessMode_A; //模式A
-
- writeTiming.FSMC_AddressSetupTime =9; //地址建立时间(ADDSET)为9个HCLK =54ns
- writeTiming.FSMC_AddressHoldTime = 0x00; //地址保持时间(A
- writeTiming.FSMC_DataSetupTime = 8; //数据保存时间为6ns*9个HCLK=54ns
- writeTiming.FSMC_BusTurnAroundDuration = 0x00;
- writeTiming.FSMC_CLKDivision = 0x00;
- writeTiming.FSMC_DataLatency = 0x00;
- writeTiming.FSMC_AccessMode = FSMC_AccessMode_A; //模式A
-
- FSMC_NORSRAMInitStructure.FSMC_Bank = FSMC_Bank1_NORSRAM4;// 这里我们使用NE4 ,也就对应BTCR[6],[7]。
- FSMC_NORSRAMInitStructure.FSMC_DataAddressMux = FSMC_DataAddressMux_Disable; // 不复用数据地址
- FSMC_NORSRAMInitStructure.FSMC_MemoryType =FSMC_MemoryType_SRAM;// FSMC_MemoryType_SRAM; //SRAM
- FSMC_NORSRAMInitStructure.FSMC_MemoryDataWidth = FSMC_MemoryDataWidth_16b;//存储器数据宽度为16bit
- FSMC_NORSRAMInitStructure.FSMC_BurstAccessMode =FSMC_BurstAccessMode_Disable;// FSMC_BurstAccessMode_Disable;
- FSMC_NORSRAMInitStructure.FSMC_WaitSignalPolarity = FSMC_WaitSignalPolarity_Low;
- FSMC_NORSRAMInitStructure.FSMC_AsynchronousWait=FSMC_AsynchronousWait_Disable;
- FSMC_NORSRAMInitStructure.FSMC_WrapMode = FSMC_WrapMode_Disable;
- FSMC_NORSRAMInitStructure.FSMC_WaitSignalActive = FSMC_WaitSignalActive_BeforeWaitState;
- FSMC_NORSRAMInitStructure.FSMC_WriteOperation = FSMC_WriteOperation_Enable; // 存储器写使能
- FSMC_NORSRAMInitStructure.FSMC_WaitSignal = FSMC_WaitSignal_Disable;
- FSMC_NORSRAMInitStructure.FSMC_ExtendedMode = FSMC_ExtendedMode_Enable; // 读写使用不同的时序
- FSMC_NORSRAMInitStructure.FSMC_WriteBurst = FSMC_WriteBurst_Disable;
- FSMC_NORSRAMInitStructure.FSMC_ReadWriteTimingStruct = &readWriteTiming; //读写时序
- FSMC_NORSRAMInitStructure.FSMC_WriteTimingStruct = &writeTiming; //写时序
- FSMC_NORSRAMInit(&FSMC_NORSRAMInitStructure); //初始化FSMC配置
- FSMC_NORSRAMCmd(FSMC_Bank1_NORSRAM4, ENABLE); // 使能BANK1
-
- delay_ms(50); // delay 50 ms
- LCD_WriteReg(0x0000,0x0001);
- delay_ms(50); // delay 50 ms
- lcddev.id = LCD_ReadReg(0x0000);
- if(lcddev.id<0XFF||lcddev.id==0XFFFF||lcddev.id==0X9300)//读到ID不正确,新增lcddev.id==0X9300判断,因为9341在未被复位的情况下会被读成9300
- {
- //尝试9341 ID的读取
- LCD_WR_REG(0XD3);
- lcddev.id=LCD_RD_DATA(); //dummy read
- lcddev.id=LCD_RD_DATA(); //读到0X00
- lcddev.id=LCD_RD_DATA(); //读取93
- lcddev.id<<=8;
- lcddev.id|=LCD_RD_DATA(); //读取41
- if(lcddev.id!=0X9341) //非9341,尝试是不是6804
- {
- LCD_WR_REG(0XBF);
- lcddev.id=LCD_RD_DATA(); //dummy read
- lcddev.id=LCD_RD_DATA(); //读回0X01
- lcddev.id=LCD_RD_DATA(); //读回0XD0
- lcddev.id=LCD_RD_DATA(); //这里读回0X68
- lcddev.id<<=8;
- lcddev.id|=LCD_RD_DATA(); //这里读回0X04
- if(lcddev.id!=0X6804) //也不是6804,尝试看看是不是NT35310
- {
- LCD_WR_REG(0XD4);
- lcddev.id=LCD_RD_DATA();//dummy read
- lcddev.id=LCD_RD_DATA();//读回0X01
- lcddev.id=LCD_RD_DATA();//读回0X53
- lcddev.id<<=8;
- lcddev.id|=LCD_RD_DATA(); //这里读回0X10
- if(lcddev.id!=0X5310) //也不是NT35310,尝试看看是不是NT35510
- {
- LCD_WR_REG(0XDA00);
- lcddev.id=LCD_RD_DATA(); //读回0X00
- LCD_WR_REG(0XDB00);
- lcddev.id=LCD_RD_DATA(); //读回0X80
- lcddev.id<<=8;
- LCD_WR_REG(0XDC00);
- lcddev.id|=LCD_RD_DATA(); //读回0X00
- if(lcddev.id==0x8000)lcddev.id=0x5510;//NT35510读回的ID是8000H,为方便区分,我们强制设置为5510
- if(lcddev.id!=0X5510) //也不是NT5510,尝试看看是不是SSD1963
- {
- LCD_WR_REG(0XA1);
- lcddev.id=LCD_RD_DATA();
- lcddev.id=LCD_RD_DATA(); //读回0X57
- lcddev.id<<=8;
- lcddev.id|=LCD_RD_DATA(); //读回0X61
- if(lcddev.id==0X5761)lcddev.id=0X1963;//SSD1963读回的ID是5761H,为方便区分,我们强制设置为1963
- }
- }
- }
- }
- }
- if(lcddev.id==0X9341||lcddev.id==0X5310||lcddev.id==0X5510||lcddev.id==0X1963)//如果是这几个IC,则设置WR时序为最快
- {
- //重新配置写时序控制寄存器的时序
- FSMC_Bank1E->BWTR[6]&=~(0XF<<0);//地址建立时间(ADDSET)清零
- FSMC_Bank1E->BWTR[6]&=~(0XF<<8);//数据保存时间清零
- FSMC_Bank1E->BWTR[6]|=3<<0; //地址建立时间(ADDSET)为3个HCLK =18ns
- FSMC_Bank1E->BWTR[6]|=2<<8; //数据保存时间(DATAST)为6ns*3个HCLK=18ns
- }else if(lcddev.id==0X6804||lcddev.id==0XC505) //6804/C505速度上不去,得降低
- {
- //重新配置写时序控制寄存器的时序
- FSMC_Bank1E->BWTR[6]&=~(0XF<<0);//地址建立时间(ADDSET)清零
- FSMC_Bank1E->BWTR[6]&=~(0XF<<8);//数据保存时间清零
- FSMC_Bank1E->BWTR[6]|=10<<0; //地址建立时间(ADDSET)为10个HCLK =60ns
- FSMC_Bank1E->BWTR[6]|=12<<8; //数据保存时间(DATAST)为6ns*13个HCLK=78ns
- }
- printf(" LCD ID:%x\r\n",lcddev.id); //打印LCD ID
这里参考了正点原子的LCD的初始化,我说贴上的代码没有具体型号的屏幕初始化,因为代码太长,会占用我的篇幅,在我的上面贴的代码最后是打印LCD ID,打印完成后会进行if()语句的判断,判断属于哪个型号的LCD屏幕,再进行初始化,大家有需要可以参考正点原子的初始化。在初始化完成后会对LCD进行清屏。因为使用摄像头会占用大量的内存资源,所有我在板子上贴了一片外部SARM芯片,同样需要对其进行初始化。此过程已放入LCD初始化中。
- LCD_Display_Dir(0); //默认为竖屏
- LCD_LED=1; //点亮背光
- LCD_Clear(WHITE);
exfuns初始化:
在使用摄像头拍照结束后,会将.bmp文件存入内存卡中,所以这里需要初始化exfuns,为fatfs相关变量申请内存 。
- u8 exfuns_init(void)
- {
- u8 i;
- for(i=0;i<_VOLUMES;i++)
- {
- fs[i]=(FATFS*)mymalloc(SRAMIN,sizeof(FATFS)); //为磁盘i工作区申请内存
- if(!fs[i])break;
- }
- file=(FIL*)mymalloc(SRAMIN,sizeof(FIL)); //为file申请内存
- ftemp=(FIL*)mymalloc(SRAMIN,sizeof(FIL)); //为ftemp申请内存
- fatbuf=(u8*)mymalloc(SRAMIN,512); //为fatbuf申请内存
- if(i==_VOLUMES&&file&&ftemp&&fatbuf)return 0; //申请有一个失败,即失败.
- else return 1;
- }
OV7670初始化:
初始化OV7670主要是初始化POWER DOWN控制信号(PG9)和复位控制信号(PG15),以及SCCB的接口,该接口是串行摄像机控制总线协议的英文名简称,相当于一个简易的I2C协议,同时也会初始化QVGA的分辨率。
- //初始化OV7670
- //返回0:成功
- //返回其他值:错误代码
- u8 OV7670_Init(void)
- {
- u16 i=0;
- u16 reg=0;
- u8 temp=0;
- //设置IO
- GPIO_InitTypeDef GPIO_InitStructure;
- RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOG, ENABLE);
- //GPIOG9,15初始化设置
- GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9|GPIO_Pin_15;//PG9,15推挽输出
- GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT; //推挽输出
- GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;//推挽输出
- GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;//100MHz
- GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;//上拉
- GPIO_Init(GPIOG, &GPIO_InitStructure);//初始化
- OV7670_PWDN=0; //POWER ON
- delay_ms(10);
- OV7670_RST=0; //复位OV7670
- delay_ms(10);
- OV7670_RST=1; //结束复位
- SCCB_Init(); //初始化SCCB 的IO口
- SCCB_WR_Reg(0X12, 0x80); //软复位OV7670
- delay_ms(50);
- LED0=0;
- //初始化 OV7670,采用QVGA分辨率(320*240)
- for(i=0;i<sizeof(ov7670_init_reg_tbl)/sizeof(ov7670_init_reg_tbl[0]);i++)
- {
- SCCB_WR_Reg(ov7670_init_reg_tbl[i][0],ov7670_init_reg_tbl[i][1]);
- }
- return 0x00; //ok
- }
- //初始化SCCB接口
- void SCCB_Init(void)
- {
- GPIO_InitTypeDef GPIO_InitStructure;
- RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOD, ENABLE);//使能GPIOD时钟
- //GPIOF9,F10初始化设置
- GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6|GPIO_Pin_7;//PD6,7 推挽输出
- GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT; //PD6,7 推挽输出
- GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;//推挽输出
- GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;//100MHz
- GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;//上拉
- GPIO_Init(GPIOD, &GPIO_InitStructure);//初始化
-
- GPIO_SetBits(GPIOD,GPIO_Pin_6|GPIO_Pin_7);
- SCCB_SDA_OUT();
- }
DCMI配置:
这里是一个重点,因为我的OV7670不带FIFO和晶振,而7670的CMOS芯片的时钟可以高达24M,而单片机的IO速度不够,退而求其次,使用DCMI的接口,勉强让其跑起来,当然,如果是ARM9或者DSP图像处理芯片就另说了,人家内存大,带camera接口,但是价格也感人。
DCMI接口是一个同步并行接口,能够接收外部 8 位、 10 位、 12 位或 14 位 CMOS 摄像头模块发出的高速数据流。可支持不同的数据格式: YCbCr4:2:2/RGB565 逐行视频和压缩数据 (JPEG)。
DCMI可以接收54M的数据流,高达14根数据线和一条像素时钟线PIXCLK,且像素时钟的极性可编程,可以自定义在上升沿还是下降沿捕捉数据。
- //DCMI初始化
- 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);//使能GPIOA B C E 时钟
- RCC_AHB2PeriphClockCmd(RCC_AHB2Periph_DCMI,ENABLE);//使能DCMI时钟
- //PA4/6初始化设置
- GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4|GPIO_Pin_6;//PA4/6 复用功能输出
- GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF; //复用功能输出
- GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;//推挽输出
- GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;//100MHz
- GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;//上拉
- GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化
-
- GPIO_InitStructure.GPIO_Pin = GPIO_Pin_7|GPIO_Pin_6;// PB6/7 复用功能输出
- GPIO_Init(GPIOB, &GPIO_InitStructure);//初始化
-
- GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6|GPIO_Pin_7|GPIO_Pin_8|GPIO_Pin_9|GPIO_Pin_11;//PC6/7/8/9/11 复用功能输出
- GPIO_Init(GPIOC, &GPIO_InitStructure);//初始化
- GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5|GPIO_Pin_6;//PE5/6 复用功能输出
- GPIO_Init(GPIOE, &GPIO_InitStructure);//初始化
- GPIO_PinAFConfig(GPIOA,GPIO_PinSource4,GPIO_AF_DCMI); //PA4,AF13 DCMI_HSYNC
- GPIO_PinAFConfig(GPIOA,GPIO_PinSource6,GPIO_AF_DCMI); //PA6,AF13 DCMI_PCLK
- GPIO_PinAFConfig(GPIOB,GPIO_PinSource7,GPIO_AF_DCMI); //PB7,AF13 DCMI_VSYNC
- GPIO_PinAFConfig(GPIOC,GPIO_PinSource6,GPIO_AF_DCMI); //PC6,AF13 DCMI_D0
- GPIO_PinAFConfig(GPIOC,GPIO_PinSource7,GPIO_AF_DCMI); //PC7,AF13 DCMI_D1
- GPIO_PinAFConfig(GPIOC,GPIO_PinSource8,GPIO_AF_DCMI); //PC8,AF13 DCMI_D2
- GPIO_PinAFConfig(GPIOC,GPIO_PinSource9,GPIO_AF_DCMI); //PC9,AF13 DCMI_D3
- GPIO_PinAFConfig(GPIOC,GPIO_PinSource11,GPIO_AF_DCMI);//PC11,AF13 DCMI_D4
- GPIO_PinAFConfig(GPIOB,GPIO_PinSource6,GPIO_AF_DCMI); //PB6,AF13 DCMI_D5
- GPIO_PinAFConfig(GPIOE,GPIO_PinSource5,GPIO_AF_DCMI); //PE5,AF13 DCMI_D6
- GPIO_PinAFConfig(GPIOE,GPIO_PinSource6,GPIO_AF_DCMI); //PE6,AF13 DCMI_D7
-
- DCMI_DeInit();//清除原来的设置
-
-
- DCMI_InitStructure.DCMI_CaptureMode=DCMI_CaptureMode_Continuous;//连续模式
- DCMI_InitStructure.DCMI_CaptureRate=DCMI_CaptureRate_All_Frame;//全帧捕获
- DCMI_InitStructure.DCMI_ExtendedDataMode= DCMI_ExtendedDataMode_8b;//8位数据格式
- DCMI_InitStructure.DCMI_HSPolarity = DCMI_HSPolarity_Low;//HSYNC 低电平有效
- DCMI_InitStructure.DCMI_PCKPolarity= DCMI_PCKPolarity_Falling;//PCLK 上升沿有效
- DCMI_InitStructure.DCMI_SynchroMode= DCMI_SynchroMode_Hardware;//硬件同步HSYNC,VSYNC
- DCMI_InitStructure.DCMI_VSPolarity=DCMI_VSPolarity_High;//VSYNC 低电平有效
- DCMI_Init(&DCMI_InitStructure);
- printf("start DCMI_IT_FRAME\r\n");
- DCMI_ITConfig(DCMI_IT_FRAME,ENABLE);//开启帧中断
- //DCMI_ITConfig(DCMI_IT_LINE,ENABLE); //开启行中断
- //DCMI_ITConfig(DCMI_IT_VSYNC,ENABLE); //开启场中断
- DCMI_Cmd(ENABLE); //DCMI使能
- printf("ENABLE DCMI_IT_FRAME OK\r\n");
- NVIC_InitStructure.NVIC_IRQChannel = DCMI_IRQn;
- NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=0;//抢占优先级1
- NVIC_InitStructure.NVIC_IRQChannelSubPriority =0; //子优先级3
- NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道使能
- NVIC_Init(&NVIC_InitStructure); //根据指定的参数初始化VIC寄存器、
- printf("My_DCMI_Init OK\r\n");
- }
DCMI的初始化中我们使用连续模式的全帧捕捉,在中断中我们选择帧中断,方便获取一个满屏的图像数据。
DCMI获取到的数据会保存在32位的数据寄存器DCMI_DR中,之后我们便可以通过DMA进行传输,图像的缓冲由DMA进行管理,不是由DCMI接管。
DCMI的DMA配置:
- void DCMI_DMA_Init(u32 DMA_Memory0BaseAddr,u32 DMA_Memory1BaseAddr,u16 DMA_BufferSize,u32 DMA_MemoryDataSize,u32 DMA_MemoryInc)
- {
- DMA_InitTypeDef DMA_InitStructure;
- NVIC_InitTypeDef NVIC_InitStructure;
-
- RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA2,ENABLE);//DMA2时钟使能
- DMA_DeInit(DMA2_Stream1);//等待DMA2_Stream1
- while (DMA_GetCmdStatus(DMA2_Stream1) != DISABLE){}//等待DMA2_Stream1可配置
-
- /* 配置 DMA Stream */
- DMA_InitStructure.DMA_Channel = DMA_Channel_1; //通道1 DCMI通道
- DMA_InitStructure.DMA_PeripheralBaseAddr = (u32)&DCMI->DR;//外设地址为:DCMI->DR
- DMA_InitStructure.DMA_Memory0BaseAddr = DMA_Memory0BaseAddr;//DMA 存储器0地址
- 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;//外设数据长度:32位
- 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; //FIFO模式
- DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_Full;//使用全FIFO
- DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single;//外设突发单次传输
- DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single;//存储器突发单次传输
- DMA_Init(DMA2_Stream1, &DMA_InitStructure);//初始化DMA Stream
-
- if(DMA_Memory1BaseAddr)
- {
- DMA_DoubleBufferModeCmd(DMA2_Stream1,ENABLE);//双缓冲模式
- DMA_MemoryTargetConfig(DMA2_Stream1,DMA_Memory1BaseAddr,DMA_Memory_1);//配置目标地址1
- }
- DMA_ITConfig(DMA2_Stream1,DMA_IT_TC,ENABLE);//开启传输完成中断
-
- NVIC_InitStructure.NVIC_IRQChannel = DMA2_Stream1_IRQn;
- NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=0;//抢占优先级0
- NVIC_InitStructure.NVIC_IRQChannelSubPriority =0; //子优先级0
- NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道使能
- NVIC_Init(&NVIC_InitStructure); //根据指定的参数初始化VIC寄存器、
- }
开始工作:
在启动MCU后需要初始化相关配置。
- LCD_Init(); //LCD初始化
- FSMC_SRAM_Init(); //初始化外部SRAM.
- W25QXX_Init(); //初始化W25Q128
- exfuns_init(); //为fatfs相关变量申请内存
- //
- // KEY_Init(); //按键初始化
- // SD_Init();
- //TIM3_Int_Init(10000-1,8400-1);//10Khz计数,1秒钟中断一次
- TIM1_PWM_Init();
- my_mem_init(SRAMIN); //初始化内部内存池
- my_mem_init(SRAMEX); //初始化内部内存池
- my_mem_init(SRAMCCM); //初始化CCM内存池
- usmart_dev.init(84); //初始化USMART
- POINT_COLOR=RED;//设置字体为红色
- LCD_ShowString(30,130,200,16,16,"OV7670 00089");
- printf("init ov\n");
- while(OV7670_Init())//初始化OV7670
- {
- LCD_ShowString(30,130,240,16,16,"OV7670 ERR");
- delay_ms(200);
- LCD_Fill(30,130,239,170,WHITE);
- delay_ms(200);
- }
- LCD_ShowString(30,130,200,16,16,"OV7670 OK");
- printf("init ov ok\n");
- delay_ms(1500);
- jpeg_buf0=mymalloc(SRAMIN,jpeg_dma_bufsize*4); //为jpeg dma接收申请内存
- jpeg_buf1=mymalloc(SRAMIN,jpeg_dma_bufsize*4); //为jpeg dma接收申请内存
- jpeg_data_buf=mymalloc(SRAMEX,300*1024); //为jpeg文件申请内存(最大300KB)
- pname=mymalloc(SRAMIN,30);//为带路径的文件名分配30个字节的内存
- while(pname==NULL||!jpeg_data_buf) //内存分配出错
- {
- LCD_ShowString(30,190,240,16,16,"内存分配失败!");
- printf("jpeg_data_buf ERROR!!!!!!!!!!!!!!!!!!!!!!! ok\n");
- delay_ms(200);
- LCD_Fill(30,190,240,146,WHITE);//清除显示
- delay_ms(200);
- }
- printf("init mymalloc ok \n");
- My_DCMI_Init(); //DCMI配置
- // DCMI_DMA_Init((u32)&LCD->LCD_RAM,0,10,DMA_MemoryDataSize_HalfWord,DMA_MemoryInc_Disable);//DCMI DMA配置
- // DCMI_Start(); //启动传输
- printf("init My_DCMI_Init ok\n");
- dcmi_rx_callback=jpeg_dcmi_rx_callback;//回调函数
- //DCMI_DMA_Init((u32)&LCD->LCD_RAM,0,10,DMA_MemoryDataSize_HalfWord,DMA_MemoryInc_Disable);//DCMI DMA配置
- DCMI_DMA_Init((u32)jpeg_buf0,(u32)jpeg_buf1,jpeg_dma_bufsize,DMA_MemoryDataSize_HalfWord,DMA_MemoryInc_Enable);//DCMI DMA配置(双缓冲模式)
- printf("init DCMI_Start");
- DCMI_Start(); //启动传输
- OV7670_Window_Set(12,176,240,320);//OV7670设置输出窗口
- // sw_ov2640_mode(); //切换为OV2640模式
- //while(1){
- // if(sd_ok==1){
- printf("start DCMI_Start ok ");
- dcmi=1;
- while(jpeg_data_ok!=1); //等待第一帧图片采集完
- dcmi=0;
- DCMI_Stop(); //停止DMA搬运
- printf("DCMI STOP");
-
- sw_sdcard_mode(); //切换为SD卡模式
- printf("SWICCT SDCARD SUCCESS");
- // if(KEY0==0) //BMP拍照
- // {
- f_mount(fs[0],"0:",1); //挂载SD卡
- res=f_mkdir("0:/PHOTO"); //创建PHOTO文件夹
- if(res!=FR_EXIST&&res!=FR_OK) //发生了错误
- {
- printf("SD CARD ERROR");
- LCD_ShowString(30,190,200,16,16,"内存分配失败!");
- delay_ms(200);
- sd_ok=0;
- }
- printf("SD CARD RIGHT");
- camera_new_pathname(pname,0);//得到文件名
- printf("CREAT PNAME SUCCESS");
- res=bmp_encode(pname,0,0,lcddev.width,lcddev.height,0);
- printf("BMP SAVE SUCCESS");
- delay_ms(200);
此过程皆在main函数中完成,初始化DCMI之前必须先初始化内存并为jpeg dma接收申请和分配内存。
然后启动DMA,启动DCMI,进行图像采集。
- //DCMI,启动传输
- void DCMI_Start(void)
- {
- LCD_Scan_Dir(U2D_L2R); //从上到下,从左到右
- LCD_Set_Window(0,0,240,320); //LCD设置显示窗口,如果改变了分辨率,这里需要更改
- LCD_SetCursor(0,0);
- LCD_WriteRAM_Prepare(); //开始写入GRAM
- DMA_Cmd(DMA2_Stream1, ENABLE);//开启DMA2,Stream1
- DCMI_CaptureCmd(ENABLE);//DCMI捕获使能
- }
设置OV7670输出窗口
- //设置图像输出窗口
- //对QVGA设置。
- void OV7670_Window_Set(u16 sx,u16 sy,u16 width,u16 height)
- {
- u16 endx;
- u16 endy;
- u8 temp;
- endx=sx+width*2; //V
- endy=sy+height*2;
- if(endy>784)endy-=784;
- temp=SCCB_RD_Reg(0X03); //读取Vref之前的值
- temp&=0XF0;
- temp|=((endx&0X03)<<2)|(sx&0X03);
- SCCB_WR_Reg(0X03,temp); //设置Vref的start和end的最低2位
- SCCB_WR_Reg(0X19,sx>>2); //设置Vref的start高8位
- SCCB_WR_Reg(0X1A,endx>>2); //设置Vref的end的高8位
- temp=SCCB_RD_Reg(0X32); //读取Href之前的值
- temp&=0XC0;
- temp|=((endy&0X07)<<3)|(sy&0X07);
- SCCB_WR_Reg(0X32,temp);
- SCCB_WR_Reg(0X17,sy>>3); //设置Href的start高8位
- SCCB_WR_Reg(0X18,endy>>3); //设置Href的end的高8位
- }
此时DCMI启动,会抓拍照片,当捕获到一帧完整数据会触发DCMI的帧中断,进入中断函数之后会进行JPEG的数据处理,在处理JPEG数据时会停止DMA的传输,并将数据保存到pbuf[]数组。
- //DCMI中断服务函数
- void DCMI_IRQHandler(void)
- {
- if(DCMI_GetITStatus(DCMI_IT_FRAME)==SET)//捕获到一帧图像
- {
- //DCMI_Stop(); //停止DMA搬运
- DCMI_ClearITPendingBit(DCMI_IT_FRAME);//清除中断
- if (dcmi == 1)
- //printf("dcmi == 1 \r\n");
- jpeg_data_process();
- //ov_frame++;
-
- }
-
- }
- //处理JPEG数据
- //当采集完一帧JPEG数据后,调用此函数,切换JPEG BUF.开始下一帧采集.
- void jpeg_data_process(void)
- {
- u16 i;
- u16 rlen;//剩余数据长度
- u32 *pbuf;
- if(jpeg_data_ok==0) //jpeg数据还未采集完?
- {
- DMA_Cmd(DMA2_Stream1,DISABLE); //停止当前传输
- while(DMA_GetCmdStatus(DMA2_Stream1) != DISABLE); //等待DMA2_Stream1可配置
- rlen=jpeg_dma_bufsize-DMA_GetCurrDataCounter(DMA2_Stream1);//得到剩余数据长度
- pbuf=jpeg_data_buf+jpeg_data_len;//偏移到有效数据末尾,继续添加
- if(DMA2_Stream1->CR&(1<<19))
- for(i=0;i<rlen;i++)
- pbuf[i]=jpeg_buf1[i];//读取buf1里面的剩余数据
- else
- for(i=0;i<rlen;i++)
- pbuf[i]=jpeg_buf0[i];//读取buf0里面的剩余数据
- jpeg_data_len+=rlen; //加上剩余长度
- jpeg_data_ok=1; //标记JPEG数据采集完按成,等待其他函数处理
- }
- if(jpeg_data_ok==2) //上一次的jpeg数据已经被处理了
- { DMA_SetCurrDataCounter(DMA2_Stream1,jpeg_dma_bufsize);//传输长度为jpeg_buf_size*4字节
- DMA_Cmd(DMA2_Stream1,ENABLE); //重新传输
- jpeg_data_ok=0; //标记数据未采集
- jpeg_data_len=0; //数据重新开始
- }
- printf("jpeg_data_process success \r\n");
- }
当判断一帧数据采集完成,会启动DMA传输,传输完成会触发DMA中断,最后模式切换到SD卡模式,进行图片的保存。
- void DMA2_Stream1_IRQHandler(void)
- {
- if(DMA_GetFlagStatus(DMA2_Stream1,DMA_FLAG_TCIF1)==SET)//DMA2_Steam1,传输完成标志
- {
- DMA_ClearFlag(DMA2_Stream1,DMA_FLAG_TCIF1);//清除传输完成中断
- dcmi_rx_callback(); //执行摄像头接收回调函数,读取数据等操作在这里面处理
-
- }
- }
- //jpeg数据接收回调函数
- void jpeg_dcmi_rx_callback(void)
- {
- u16 i;
- u32 *pbuf;
- pbuf=jpeg_data_buf+jpeg_data_len;//偏移到有效数据末尾
- if(DMA2_Stream1->CR&(1<<19))//buf0已满,正常处理buf1
- {
- for(i=0;i<jpeg_dma_bufsize;i++)pbuf[i]=jpeg_buf0[i];//读取buf0里面的数据
- jpeg_data_len+=jpeg_dma_bufsize;//偏移
- }else //buf1已满,正常处理buf0
- {
- for(i=0;i<jpeg_dma_bufsize;i++)pbuf[i]=jpeg_buf1[i];//读取buf1里面的数据
- jpeg_data_len+=jpeg_dma_bufsize;//偏移
- }
- }
我这里保存的是BMP格式,所以有一个转码处理,这里也贴一下。
- //BMP编码函数
- //将当前LCD屏幕的指定区域截图,存为16位格式的BMP文件 RGB565格式.
- //保存为rgb565则需要掩码,需要利用原来的调色板位置增加掩码.这里我们已经增加了掩码.
- //保存为rgb555格式则需要颜色转换,耗时间比较久,所以保存为565是最快速的办法.
- //filename:存放路径
- //x,y:在屏幕上的起始坐标
- //mode:模式.0,仅仅创建新文件的方式编码;1,如果之前存在文件,则覆盖之前的文件.如果没有,则创建新的文件.
- //返回值:0,成功;其他,错误码.
- u8 bmp_encode(u8 *filename,u16 x,u16 y,u16 width,u16 height,u8 mode)
- {
- u32 i=0,num=0;
- u32* pbuf;
- u16* pbuf_Data;
- FIL* f_bmp;
- u16 bmpheadsize; //bmp头大小
- BITMAPINFO hbmp; //bmp头
- u8 res=0;
- u16 tx,ty; //图像尺寸
- u16 *databuf; //数据缓存区地址
- u16 pixcnt; //像素计数器
- u16 bi4width; //水平像素字节数
- if(width==0||height==0)return PIC_WINDOW_ERR; //区域错误
- if((x+width-1)>lcddev.width)return PIC_WINDOW_ERR; //区域错误
- if((y+height-1)>lcddev.height)return PIC_WINDOW_ERR; //区域错误
-
- #if BMP_USE_MALLOC == 1 //使用malloc
- databuf=(u16*)pic_memalloc(1024); //开辟至少bi4width大小的字节的内存区域 ,对240宽的屏,480个字节就够了.
- if(databuf==NULL)return PIC_MEM_ERR; //内存申请失败.
- f_bmp=(FIL *)pic_memalloc(sizeof(FIL)); //开辟FIL字节的内存区域
- if(f_bmp==NULL) //内存申请失败.
- {
- pic_memfree(databuf);
- return PIC_MEM_ERR;
- }
- #else
- databuf=(u16*)bmpreadbuf;
- f_bmp=&f_bfile;
- #endif
- bmpheadsize=sizeof(hbmp);//得到bmp文件头的大小
- mymemset((u8*)&hbmp,0,sizeof(hbmp));//置零空申请到的内存.
- hbmp.bmiHeader.biSize=sizeof(BITMAPINFOHEADER);//信息头大小
- hbmp.bmiHeader.biWidth=width; //bmp的宽度
- hbmp.bmiHeader.biHeight=height; //bmp的高度
- hbmp.bmiHeader.biPlanes=1; //恒为1
- hbmp.bmiHeader.biBitCount=16; //bmp为16位色bmp
- hbmp.bmiHeader.biCompression=BI_BITFIELDS;//每个象素的比特由指定的掩码决定。
- hbmp.bmiHeader.biSizeImage=hbmp.bmiHeader.biHeight*hbmp.bmiHeader.biWidth*hbmp.bmiHeader.biBitCount/8;//bmp数据区大小
-
- hbmp.bmfHeader.bfType=((u16)'M'<<8)+'B';//BM格式标志
- hbmp.bmfHeader.bfSize=bmpheadsize+hbmp.bmiHeader.biSizeImage;//整个bmp的大小
- hbmp.bmfHeader.bfOffBits=bmpheadsize;//到数据区的偏移
- hbmp.RGB_MASK[0]=0X00F800; //红色掩码
- hbmp.RGB_MASK[1]=0X0007E0; //绿色掩码
- hbmp.RGB_MASK[2]=0X00001F; //蓝色掩码
- if(mode==1)res=f_open(f_bmp,(const TCHAR*)filename,FA_READ|FA_WRITE);//尝试打开之前的文件
- if(mode==0||res==0x04)res=f_open(f_bmp,(const TCHAR*)filename,FA_WRITE|FA_CREATE_NEW);//模式0,或者尝试打开失败,则创建新文件
- if((hbmp.bmiHeader.biWidth*2)%4)//水平像素(字节)不为4的倍数
- {
- bi4width=((hbmp.bmiHeader.biWidth*2)/4+1)*4;//实际要写入的宽度像素,必须为4的倍数.
- }else bi4width=hbmp.bmiHeader.biWidth*2; //刚好为4的倍数
- if(res==FR_OK)//创建成功
- {
- res=f_write(f_bmp,(u8*)&hbmp,bmpheadsize,&bw);//写入BMP首部
- pixcnt=0;
- pbuf=(u32*)jpeg_data_buf;
- // pbuf_Data = (u32*)jpeg_data_buf;
- for (i = 16; i < 32; i++)
- {
- pbuf[i]=0x0;
- // pbuf_Data=pbuf&0x0000FFFF;
- }
- f_write(f_bmp,(u32*)pbuf,jpeg_data_len,&bw);
-
- f_close(f_bmp);
- }
- // if(res==FR_OK)//创建成功
- // {
- // res=f_write(f_bmp,(u8*)&hbmp,bmpheadsize,&bw);//写入BMP首部
- // for(ty=y+height-1;hbmp.bmiHeader.biHeight;ty--){
- //
- // }
- // pixcnt=0;
- // pbuf=(u32*)jpeg_data_buf;
- // f_write(f_bmp,(u32*)pbuf,jpeg_data_len*4,&bw);
- //
- // f_close(f_bmp);
- // }
- #if BMP_USE_MALLOC == 1 //使用malloc
- pic_memfree(databuf);
- pic_memfree(f_bmp);
- #endif
- return res;
- }
因为项目不是现在做的,所以讲起来可能有些乱,这里附上百度网盘的下载链接,因为文件过大,没法上传附件,望谅解。
链接的隐藏是为了获取回复量,有点私心,各位大侠高抬贵手。感激不尽。