stm32f4系列的芯片处理的能力可以说非常强大了的,而且内部还自带有数字摄像头接口(DCMI),可以说stm32f407的核心板和不带FIFO的摄像头模块OV7670简直就是绝配!
F4系列的DCMI接口内部结构框图如下图3所示,具体的配置使用方式可以参考以下这篇博文:《stm32f4的数字摄像头接口(DCMI)使用》
这位博主大大已经很详细地介绍了DCMI接口的具体使用方法和原理,更重要的是里面还有接口的配置函数,虽然不完全一样,但是非常具有参考意义!
LCD屏幕用的是原子哥的TFT-LCD 屏幕,去年学STM32F103的时候随便买的一块液晶屏幕,分辨率就是320*240,驱动IC是ILI9341(ID9341)。
长这个样子(图片来源:原子哥):
对应资料链接:https://leoeinstein.lanzous.com/iApMql9yzkj
资料有点大哈,因为这个资料有可能是最全的LCD驱动IC资料包了。(这句话很小米)
OV7670摄像头模块的体积小、工作电压低,提供单片VGA摄像头和影像处理器的所有功能,因为我这个不带FIFO芯片,所有还特便宜,但是像素不算太高。(这个价钱也还是可以了)
通过SCCB总线控制,可以输出整帧、子采样、取窗口等方式的各种分辨率8位影响数据。该产品VGA图像最高达到30帧/秒。用户可以完全控制图像质量、数据格式和传输方式。
摄像头模块长这个样子(图片来源:某宝):
OV7670中文资料手册:
https://wenku.baidu.com/view/e60f19ea81c758f5f61f67df.html
开发板上有一个TFT-LCD的接口,你如果手上有这样的屏幕就可以直接插上去使用了,如果用的的原子哥的屏幕的话,就需要稍微外接一些线了。
我直接用杜邦线连的,有“亿点点”乱哈。
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 |
可以参考这篇博文: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日 星期三