基于STM32F407的摄像头(不带FIFO的OV7670)图像采集及LCD显示实验-笔记整理

硬件说明

STM32F4开发板

stm32f4系列的芯片处理的能力可以说非常强大了的,而且内部还自带有数字摄像头接口(DCMI),可以说stm32f407的核心板和不带FIFO的摄像头模块OV7670简直就是绝配!

我手上的这块张这个样子 (图片来源:某宝)
基于STM32F407的摄像头(不带FIFO的OV7670)图像采集及LCD显示实验-笔记整理_第1张图片
基于STM32F407的摄像头(不带FIFO的OV7670)图像采集及LCD显示实验-笔记整理_第2张图片

F4系列的DCMI接口内部结构框图如下图3所示,具体的配置使用方式可以参考以下这篇博文:《stm32f4的数字摄像头接口(DCMI)使用》

这位博主大大已经很详细地介绍了DCMI接口的具体使用方法和原理,更重要的是里面还有接口的配置函数,虽然不完全一样,但是非常具有参考意义!
基于STM32F407的摄像头(不带FIFO的OV7670)图像采集及LCD显示实验-笔记整理_第3张图片

TFT-LCD屏幕

LCD屏幕用的是原子哥的TFT-LCD 屏幕,去年学STM32F103的时候随便买的一块液晶屏幕,分辨率就是320*240,驱动IC是ILI9341(ID9341)

长这个样子(图片来源:原子哥):
基于STM32F407的摄像头(不带FIFO的OV7670)图像采集及LCD显示实验-笔记整理_第4张图片
对应资料链接:https://leoeinstein.lanzous.com/iApMql9yzkj

资料有点大哈,因为这个资料有可能是最全的LCD驱动IC资料包了。(这句话很小米)

摄像头OV7670

OV7670摄像头模块的体积小、工作电压低,提供单片VGA摄像头和影像处理器的所有功能,因为我这个不带FIFO芯片,所有还特便宜,但是像素不算太高。(这个价钱也还是可以了)

通过SCCB总线控制,可以输出整帧、子采样、取窗口等方式的各种分辨率8位影响数据。该产品VGA图像最高达到30帧/秒。用户可以完全控制图像质量、数据格式和传输方式。

摄像头模块长这个样子(图片来源:某宝):
基于STM32F407的摄像头(不带FIFO的OV7670)图像采集及LCD显示实验-笔记整理_第5张图片
OV7670中文资料手册:
https://wenku.baidu.com/view/e60f19ea81c758f5f61f67df.html

硬件连接说明

开发板上有一个TFT-LCD的接口,你如果手上有这样的屏幕就可以直接插上去使用了,如果用的的原子哥的屏幕的话,就需要稍微外接一些线了。

我直接用杜邦线连的,有“亿点点”乱哈。

LCD与STM32

LCD MCU LCD MCU
CS PG12 RS PF12
WR/CLK PD5 RD PD4
RST RST D0 D0
D1 D1 D2 D3
D3 D3 D4 D4
D5 D5 D6 D6
D7 D7 D8 D8
D9 D9 D10 D10
D11 D11 D12 D12
D13 D13 D14 D14
D15 D15 GND GND
BL PB15 VCC3.3 3.3V
VCC3.3 3.3V GND GND
GND GND BL_VDD 5V
MISO MI MOSI MO
T_PEN PB1 MO -------
T_CS PC13 CLK PB0

基于STM32F407的摄像头(不带FIFO的OV7670)图像采集及LCD显示实验-笔记整理_第6张图片

OV7670与STM32

可以参考这篇博文:https://blog.csdn.net/LiangWF22/article/details/112676289
基于STM32F407的摄像头(不带FIFO的OV7670)图像采集及LCD显示实验-笔记整理_第7张图片

编程实现

编程要点、思路

一、摄像头配置
无FIFO的摄像头需要注意 时钟配置和窗口配置,否则不能出图像;

二、DCMI配置
DCMI需要配置成 DMA传输模式和使用行场中断进行图像数据采集,同时需要注意DCMI硬件同步的行场信号以及像素信号的上升沿和有效电平。

可以参考上面那篇《stm32f4的数字摄像头接口(DCMI)使用》博文。

具体代码

dcmi.c

#include "sys.h"
#include "dcmi.h" 

u8 ov_frame=0;  						//帧率
u32 datanum=0;
u32 HSYNC=0;
u32 VSYNC=0;
DCMI_InitTypeDef DCMI_InitStructure;

//DCMI 初始化
void DCMI_DMA_Init(void)
{ 
	DMA_InitTypeDef  DMA_InitStructure;
	NVIC_InitTypeDef NVIC_InitStructure;
	
	RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA2,ENABLE);//DMA2时钟使能 
	DMA_DeInit(DMA2_Stream1);
	while (DMA_GetCmdStatus(DMA2_Stream1) != DISABLE){}//等待DMA2_Stream1可配置 
	
  
	DMA_InitStructure.DMA_Channel = DMA_Channel_1;  //通道1 DCMI通道 
	DMA_InitStructure.DMA_PeripheralBaseAddr = (u32)&DCMI->DR;//外设地址为:DCMI->DR  摄像头
	DMA_InitStructure.DMA_Memory0BaseAddr = (u32)&LCD->LCD_RAM;//DMA 存储器0地址 lcd
	DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralToMemory;//外设到存储器模式 搬运数据:摄像头->lcd
	DMA_InitStructure.DMA_BufferSize = 10;//数据传输量 
	DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;//外设非增量模式
	DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Disable;//存储器增量模式 关闭
	DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Word;//外设数据长度:32位
	DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord ;//存储器数据长度 16bit 
	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
		
	DMA_ITConfig(DMA2_Stream1,DMA_IT_TC,ENABLE);
	NVIC_InitStructure.NVIC_IRQChannel=	DMA2_Stream1_IRQn;
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=0;
	NVIC_InitStructure.NVIC_IRQChannelSubPriority=0;
	NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE;
	NVIC_Init(&NVIC_InitStructure);	//根据指定的参数初始化VIC寄存器、
	
} 

void DMA2_Stream1_IRQHandler(void)
{        
	if(DMA_GetFlagStatus(DMA2_Stream1,DMA_FLAG_TCIF1)==SET)//DMA2_Steam1,传输完成标志
	{  
		 DMA_ClearFlag(DMA2_Stream1,DMA_FLAG_TCIF1);//清除传输完成中断
			datanum++;
	}    											 
}  
//DCMI初始化
void DCMI_Initxx(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;//PC6/7/8/9 复用功能输出
	GPIO_Init(GPIOC, &GPIO_InitStructure);//初始化	

	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4|GPIO_Pin_5|GPIO_Pin_6;//PE 4/5/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(GPIOE,GPIO_PinSource4,GPIO_AF_DCMI);//PE4,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);

	DCMI_ITConfig(DCMI_IT_FRAME,ENABLE);//开启帧中断 
	DCMI_ITConfig(DCMI_IT_LINE,ENABLE); //开启行中断
	DCMI_ITConfig(DCMI_IT_VSYNC,ENABLE); //开启场中断	
	DCMI_Cmd(ENABLE);	//DCMI使能

	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寄存器、
} 
//DCMI,启动传输
void DCMI_Start(void)
{ 
	LCD_Scan_Dir(U2D_L2R);	//U2D_L2R	   //扫描方式从上到下,从左到右,一般模式	
	LCD_Set_Window(0,0,320,240); //LCD设置显示窗口,如果改变了分辨率,这里需要更改
	LCD_SetCursor(0,0); //设置光标 
	LCD_WriteRAM_Prepare();	//开始写入GRAM
	DMA_Cmd(DMA2_Stream1, ENABLE);//开启DMA2,Stream1 
	DCMI_CaptureCmd(ENABLE);//DCMI捕获使能  
}
//DCMI,关闭传输
void DCMI_Stop(void)
{ 
	DCMI_CaptureCmd(DISABLE);//DCMI捕获使关闭	
	while(DCMI->CR&0X01);		//等待传输结束  	
	DMA_Cmd(DMA2_Stream1,DISABLE);//关闭DMA2,Stream1
} 
//DCMI中断服务函数
void DCMI_IRQHandler(void)
{
	if(DCMI_GetITStatus(DCMI_IT_LINE)==SET)//捕获到行
	{
		DCMI_ClearITPendingBit(DCMI_IT_LINE);//清除中断	
		ov_frame++;
	}
	
} 

//以下两个函数,供usmart调用,用于调试代码

//DCMI设置显示窗口
//sx,sy;LCD的起始坐标
//width,height:LCD显示范围.
void DCMI_Set_Window(u16 sx,u16 sy,u16 width,u16 height)
{
	DCMI_Stop(); 
	LCD_Clear(WHITE);
	LCD_Set_Window(sx,sy,width,height);
	//	OV7670_OutSize_Set(width,height);
	LCD_SetCursor(0,0);  
	LCD_WriteRAM_Prepare();		//开始写入GRAM
	
	DMA_Cmd(DMA2_Stream1,ENABLE);	//开启DMA2,Stream1 
	
	DCMI_CaptureCmd(ENABLE);//DCMI捕获使能 
	
}


摄像头初始化函数如下:

u8 OV7670_Init(void)
{
	u16 i=0;
	
	//设置IO     	   
	GPIO_InitTypeDef  GPIO_InitStructure;

	RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOG, ENABLE);
	//GPIOG9,15初始化设置pow和reset
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9|GPIO_Pin_8;//PG9,8推挽输出
	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(100);
	OV7670_RST=0;	//复位OV7670
	delay_ms(100);
	OV7670_RST=1;	//结束复位 
	SCCB_Init();        		//初始化SCCB 的IO口
	
 	SCCB_WR_Reg(0X12, 0x80);	//软复位OV7670
	delay_ms(50); 
	

 	//初始化 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]);
 	} 
	
	//OV7670_Window_Set(PIC_START_X,PIC_START_Y,PIC_WIDTH,PIC_HEIGHT);
	
	OV7670_Light_Mode(0);
	OV7670_Color_Saturation(2);
	OV7670_Brightness(1);
	OV7670_Contrast(2);

	//My_DCMI_Init();
	//DCMI_DMA_Init((uint32_t)&camera_buffer+54,(sizeof(camera_buffer)-54)/4,DMA_MemoryDataSize_HalfWord,DMA_MemoryInc_Enable);//DCMI DMA 

  	return 0x00; 	//ok
} 

实现效果

这仅仅是显示RGB数据,如果要上云的话,需要将RGB数据转化为BMP数据,然后再上传到物联网平台上,在后面的博文里将会介绍到。

工程链接

链接1:https://download.csdn.net/download/LiangWF22/15020550

链接2:https://leoeinstein.lanzoum.com/icY0x0b627tg
2021年2月3日 星期三

你可能感兴趣的:(STM32,LCD,stm32,嵌入式,单片机)