硬件说明

STM32F4开发板

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

我手上的这块张这个样子 (图片来源:某宝)
在这里插入图片描述
在这里插入图片描述

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

这位博主大大已经很详细地介绍了DCMI接口的具体使用方法和原理,更重要的是里面还有接口的配置函数,虽然不完全一样,但是非常具有参考意义!
图3

TFT-LCD屏幕

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

长这个样子(图片来源:原子哥):
在这里插入图片描述
对应资料链接:https://leoeinstein.lanzous.com/iApMql9yzkj

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

摄像头OV7670

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

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

摄像头模块长这个样子(图片来源:某宝):
在这里插入图片描述
OV7670中文资料手册:
https://wenku.baidu.com/view/e60f19ea81c758f5f61f67df.html

硬件连接说明

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

我直接用杜邦线连的,有“亿点点”乱哈。
在这里插入图片描述

LCD与STM32

LCDMCULCDMCU
CSPG12RSPF12
WR/CLKPD5RDPD4
RSTRSTD0D0
D1D1D2D3
D3D3D4D4
D5D5D6D6
D7D7D8D8
D9D9D10D10
D11D11D12D12
D13D13D14D14
D15D15GNDGND
BLPB15VCC3.33.3V
VCC3.33.3VGNDGND
GNDGNDBL_VDD5V
MISOMIMOSIMO
T_PENPB1MO-------
T_CSPC13CLKPB0

在这里插入图片描述

OV7670与STM32

可以参考这篇博文:https://blog.csdn.net/LiangWF22/article/details/112676289
在这里插入图片描述

编程实现

编程要点、思路

一、摄像头配置
无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日 星期三

Logo

旨在为数千万中国开发者提供一个无缝且高效的云端环境,以支持学习、使用和贡献开源项目。

更多推荐