49. DCMI 您所在的位置:网站首页 模拟摄像头通用吗 49. DCMI

49. DCMI

2024-07-09 11:20| 来源: 网络整理| 查看: 265

49.5.2. 软件设计¶

为了使工程更加有条理,我们把摄像头控制相关的代码独立分开存储,方便以后移植。在“LTDC—液晶显示”工程的基础上新建“bsp_ov5640.c”及“bsp_ov5640.h”文件, 这些文件也可根据您的喜好命名,它们不属于STM32标准库的内容,是由我们自己根据应用需要编写的。

49.5.2.1. 编程要点¶

初始化DCMI时钟,I2C时钟;

使用I2C接口向OV5640写入寄存器配置;

初始化DCMI工作模式;

初始化DMA,用于搬运DCMI的数据到显存空间进行显示;

编写测试程序,控制采集图像数据并显示到液晶屏。

49.5.2.2. 代码分析¶

摄像头硬件相关宏定义

我们把摄像头控制硬件相关的配置都以宏的形式定义到 “bsp_ov5640.h”文件中,其中包括I2C及DCMI接口的,见 代码清单:OV5640-2 。

代码清单:OV5640-2 摄像头硬件配置相关的宏(省略了部分数据线)¶ 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47/*摄像头接口 */ //IIC SCCB #define CAMERA_I2C I2C1 #define CAMERA_I2C_CLK RCC_APB1Periph_I2C1 #define CAMERA_I2C_SCL_PIN GPIO_Pin_6 #define CAMERA_I2C_SCL_GPIO_PORT GPIOB #define CAMERA_I2C_SCL_GPIO_CLK RCC_AHB1Periph_GPIOB #define CAMERA_I2C_SCL_SOURCE GPIO_PinSource6 #define CAMERA_I2C_SCL_AF GPIO_AF_I2C1 #define CAMERA_I2C_SDA_PIN GPIO_Pin_7 #define CAMERA_I2C_SDA_GPIO_PORT GPIOB #define CAMERA_I2C_SDA_GPIO_CLK RCC_AHB1Periph_GPIOB #define CAMERA_I2C_SDA_SOURCE GPIO_PinSource7 #define CAMERA_I2C_SDA_AF GPIO_AF_I2C1 //VSYNC #define DCMI_VSYNC_GPIO_PORT GPIOI #define DCMI_VSYNC_GPIO_CLK RCC_AHB1Periph_GPIOI #define DCMI_VSYNC_GPIO_PIN GPIO_Pin_5 #define DCMI_VSYNC_PINSOURCE GPIO_PinSource5 #define DCMI_VSYNC_AF GPIO_AF_DCMI // HSYNC #define DCMI_HSYNC_GPIO_PORT GPIOA #define DCMI_HSYNC_GPIO_CLK RCC_AHB1Periph_GPIOA #define DCMI_HSYNC_GPIO_PIN GPIO_Pin_4 #define DCMI_HSYNC_PINSOURCE GPIO_PinSource4 #define DCMI_HSYNC_AF GPIO_AF_DCMI //PIXCLK #define DCMI_PIXCLK_GPIO_PORT GPIOA #define DCMI_PIXCLK_GPIO_CLK RCC_AHB1Periph_GPIOA #define DCMI_PIXCLK_GPIO_PIN GPIO_Pin_6 #define DCMI_PIXCLK_PINSOURCE GPIO_PinSource6 #define DCMI_PIXCLK_AF GPIO_AF_DCMI //PWDN #define DCMI_PWDN_GPIO_PORT GPIOG #define DCMI_PWDN_GPIO_CLK RCC_AHB1Periph_GPIOG #define DCMI_PWDN_GPIO_PIN GPIO_Pin_3 //数据信号线 #define DCMI_D0_GPIO_PORT GPIOH #define DCMI_D0_GPIO_CLK RCC_AHB1Periph_GPIOH #define DCMI_D0_GPIO_PIN GPIO_Pin_9 #define DCMI_D0_PINSOURCE GPIO_PinSource9 #define DCMI_D0_AF GPIO_AF_DCMI /*....省略部分数据线*/

以上代码根据硬件的连接,把与DCMI、I2C接口与摄像头通讯使用的引脚号、引脚源以及复用功能映射都以宏封装起来。

初始化DCMI的 GPIO及I2C

利用上面的宏,初始化DCMI的GPIO引脚及I2C,见 代码清单:OV5640-3 。

代码清单:OV5640-3 初始化DCMI的GPIO及I2C(省略了部分数据线)¶ 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111/** * @brief 初始化控制摄像头使用的GPIO(I2C/DCMI) * @param None * @retval None */ void OV5640_HW_Init(void) { GPIO_InitTypeDef GPIO_InitStructure; I2C_InitTypeDef I2C_InitStruct; /***DCMI引脚配置***/ /* 使能DCMI时钟 */ RCC_AHB1PeriphClockCmd(DCMI_PWDN_GPIO_CLK|DCMI_RST_GPIO_CLK|DCMI_VS YNC_GPIO_CLK | DCMI_HSYNC_GPIO_CLK | DCMI_PIXCLK_GPIO_CLK| DCMI_D0_GPIO_CLK| DCMI_D1_GPIO_CLK| DCMI_D2_GPIO_CLK| DCMI_D3_GPIO_CLK| DCMI_D4_GPIO_CLK| DCMI_D5_GPIO_CLK| DCMI_D6_GPIO_CLK| DCMI_D7_GPIO_CLK, ENABLE); /*控制/同步信号线*/ GPIO_InitStructure.GPIO_Pin = DCMI_VSYNC_GPIO_PIN; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz; GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP ; GPIO_Init(DCMI_VSYNC_GPIO_PORT, &GPIO_InitStructure); GPIO_PinAFConfig(DCMI_VSYNC_GPIO_PORT, DCMI_VSYNC_PINSOURCE, DCMI_VSYNC_AF); GPIO_InitStructure.GPIO_Pin = DCMI_HSYNC_GPIO_PIN ; GPIO_Init(DCMI_HSYNC_GPIO_PORT, &GPIO_InitStructure); GPIO_PinAFConfig(DCMI_HSYNC_GPIO_PORT, DCMI_HSYNC_PINSOURCE, DCMI_HSYNC_AF); GPIO_InitStructure.GPIO_Pin = DCMI_PIXCLK_GPIO_PIN ; GPIO_Init(DCMI_PIXCLK_GPIO_PORT, &GPIO_InitStructure); GPIO_PinAFConfig(DCMI_PIXCLK_GPIO_PORT, DCMI_PIXCLK_PINSOURCE, DCMI_PIXCLK_AF); /*数据信号*/ GPIO_InitStructure.GPIO_Pin = DCMI_D0_GPIO_PIN ; GPIO_Init(DCMI_D0_GPIO_PORT, &GPIO_InitStructure); GPIO_PinAFConfig(DCMI_D0_GPIO_PORT, DCMI_D0_PINSOURCE, DCMI_D0_AF); /*...省略部分数据信号线*/ GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz; GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP; GPIO_InitStructure.GPIO_Pin = DCMI_PWDN_GPIO_PIN ; GPIO_Init(DCMI_PWDN_GPIO_PORT, &GPIO_InitStructure); GPIO_InitStructure.GPIO_Pin = DCMI_RST_GPIO_PIN ; GPIO_Init(DCMI_RST_GPIO_PORT, &GPIO_InitStructure); /*PWDN引脚,高电平关闭电源,低电平供电*/ GPIO_ResetBits(DCMI_RST_GPIO_PORT,DCMI_RST_GPIO_PIN); GPIO_SetBits(DCMI_PWDN_GPIO_PORT,DCMI_PWDN_GPIO_PIN); Delay(10);//延时10ms GPIO_ResetBits(DCMI_PWDN_GPIO_PORT,DCMI_PWDN_GPIO_PIN); Delay(10);//延时10ms GPIO_SetBits(DCMI_RST_GPIO_PORT,DCMI_RST_GPIO_PIN); /****** 配置I2C,使用I2C与摄像头的SCCB接口通讯*****/ /* 使能I2C时钟 */ RCC_APB1PeriphClockCmd(CAMERA_I2C_CLK, ENABLE); /* 使能I2C使用的GPIO时钟 */ RCC_AHB1PeriphClockCmd(CAMERA_I2C_SCL_GPIO_CLK|CAMERA_I2C_SDA_GPIO_ CLK, ENABLE); /* 配置引脚源 */ GPIO_PinAFConfig(CAMERA_I2C_SCL_GPIO_PORT, CAMERA_I2C_SCL_SOURCE, CAMERA_I2C_SCL_AF); GPIO_PinAFConfig(CAMERA_I2C_SDA_GPIO_PORT, CAMERA_I2C_SDA_SOURCE, CAMERA_I2C_SDA_AF); /* 初始化GPIO */ GPIO_InitStructure.GPIO_Pin = CAMERA_I2C_SCL_PIN ; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz; GPIO_InitStructure.GPIO_OType = GPIO_OType_OD; GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL; GPIO_Init(CAMERA_I2C_SCL_GPIO_PORT, &GPIO_InitStructure); GPIO_PinAFConfig(CAMERA_I2C_SCL_GPIO_PORT, CAMERA_I2C_SCL_SOURCE, CAMERA_I2C_SCL_AF); GPIO_InitStructure.GPIO_Pin = CAMERA_I2C_SDA_PIN ; GPIO_Init(CAMERA_I2C_SDA_GPIO_PORT, &GPIO_InitStructure); /*初始化I2C模式 */ I2C_DeInit(CAMERA_I2C); I2C_InitStruct.I2C_Mode = I2C_Mode_I2C; I2C_InitStruct.I2C_DutyCycle = I2C_DutyCycle_2; I2C_InitStruct.I2C_OwnAddress1 = 0xFE; I2C_InitStruct.I2C_Ack = I2C_Ack_Enable; I2C_InitStruct.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7b it; I2C_InitStruct.I2C_ClockSpeed = 400000; /* 写入配置 */ I2C_Init(CAMERA_I2C, &I2C_InitStruct); /* 使能I2C */ I2C_Cmd(CAMERA_I2C, ENABLE); Delay(50);//延时50ms }

与所有使用到GPIO的外设一样,都要先把使用到的GPIO引脚模式初始化,以上代码把DCMI接口的信号线全都初始化为DCMI复用功能。

注意

OV5640的上电时序比较特殊,我们初始化PWDN则被和RST应该特别小心,先初始化成普通的推挽输出模式, 并且在初始化完毕后直接控制它RST为低电平,PWDN为高电平,使能给摄像头供电处于待机模式,延时10ms后控制PWDN为低电平, 再延时10ms后控制RST为高电平,OV5640模组启动。

函数中还包含了I2C的初始化配置,使用I2C与OV2640OV5640的SCCB接口通讯,这里的I2C模式配置与标准的I2C无异。

警告

I2C初始化完必须延时50ms,再进行对OV5640寄存器的读写操作。

配置DCMI的模式

接下来需要配置DCMI的工作模式,我们通过编写OV5640_Init函数完成该功能,见 代码清单:OV5640-4 。

代码清单:OV5640-4 配置DCMI的模式(bsp_ov5640.c文件)¶ 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41#define FSMC_LCD_ADDRESS FSMC_Addr_ILI9806G_DATA /** * @brief 配置 DCMI/DMA 以捕获摄像头数据 * @param None * @retval None */ void OV5640_Init(void) { DCMI_InitTypeDef DCMI_InitStructure; NVIC_InitTypeDef NVIC_InitStructure; /*** 配置DCMI接口 ***/ /* 使能DCMI时钟 */ RCC_AHB2PeriphClockCmd(RCC_AHB2Periph_DCMI, ENABLE); /* DCMI 配置*/ DCMI_InitStructure.DCMI_CaptureMode = DCMI_CaptureMode_Continuous; DCMI_InitStructure.DCMI_SynchroMode = DCMI_SynchroMode_Hardware; DCMI_InitStructure.DCMI_PCKPolarity = DCMI_PCKPolarity_Rising; DCMI_InitStructure.DCMI_VSPolarity = DCMI_VSPolarity_Low; DCMI_InitStructure.DCMI_HSPolarity = DCMI_HSPolarity_Low; DCMI_InitStructure.DCMI_CaptureRate = DCMI_CaptureRate_All_Frame; DCMI_InitStructure.DCMI_ExtendedDataMode = DCMI_ExtendedDataMode_8b; DCMI_Init(&DCMI_InitStructure); //配置DMA传输,直接配置循环传输即可 OV5640_DMA_Config(FSMC_LCD_ADDRESS,1); /* 配置中断 */ NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1); /* 配置帧中断,接收到帧同步信号就进入中断 */ NVIC_InitStructure.NVIC_IRQChannel = DCMI_IRQn ; //帧中断 NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority =0; NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1; NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; NVIC_Init(&NVIC_InitStructure); DCMI_ITConfig (DCMI_IT_FRAME,ENABLE); }

该函数的执行流程如下:

(1) 使能DCMI外设的时钟, 它是挂载在AHB2总线上的;

(2) 根据摄像头的时序和硬件连接的要求,配置DCMI工作模式为:使用硬件同步, 连续采集所有帧数据,采集时使用8根数据线,PIXCLK被设置为上升沿有效,VSYNC被设置成高电平有效, HSYNC被设置成低电平有效;

(3) 调用OV5640_DMA_Config函数开始DMA数据传输,每传输完一行数据需要调用一次, 它包含本次传输的目的首地址及传输的数据量,后面我们再详细解释 ;

(4) 配置DMA中断,DMA每次传输完毕会引起中断, 以便我们在中断服务函数配置DMA传输下一行数据;

(5) 配置DCMI的帧传输中断,为了防止有时DMA出现传输错误或传输速度跟不上导致数据错位、 偏移等问题,每次DCMI接收到摄像头的一帧数据,得到新的帧同步信号后(VSYNC),就进入中断,复位DMA,使它重新开始一帧的数据传输。

配置DMA数据传输

上面的DCMI配置函数中调用了OV5640_DMA_Config函数开始了DMA传输,该函数的定义见 代码清单:OV5640-5 。

代码清单:OV5640-5 配置DMA数据传输(bsp_ov5640.c文件)¶ 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46/** * @brief 配置 DCMI/DMA 以捕获摄像头数据 * @param DMA_Memory0BaseAddr:本次传输的目的首地址 * @param DMA_BufferSize:本次传输的数据量(单位为字, 即4字节) */ void OV5640_DMA_Config(uint32_t DMA_Memory0BaseAddr,uint16_t DMA_BufferSize) { DMA_InitTypeDef DMA_InitStructure; /* 配置DMA从DCMI中获取数据*/ /* 使能DMA*/ RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA2, ENABLE); DMA_Cmd(DMA2_Stream1,DISABLE); while (DMA_GetCmdStatus(DMA2_Stream1) != DISABLE) {} DMA_InitStructure.DMA_Channel = DMA_Channel_1; DMA_InitStructure.DMA_PeripheralBaseAddr = DCMI_DR_ADDRESS; //DCMI数据寄存器地址 DMA_InitStructure.DMA_Memory0BaseAddr = DMA_Memory0BaseAddr; //DMA传输的目的地址(传入的参数) 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_Enable; //寄存器地址自增 DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_W ord; DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord; 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_INC8; DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single; /*DMA中断配置 */ DMA_Init(DMA2_Stream1, &DMA_InitStructure); DMA_Cmd(DMA2_Stream1,ENABLE); while (DMA_GetCmdStatus(DMA2_Stream1) != ENABLE) {} }

该函数跟普通的DMA配置无异,它把DCMI接收到的数据从它的数据寄存器搬运到SDRAM显存中,从而直接使用液晶屏显示摄像头采集得的图像。 它包含2个输入参数DMA_Memory0BaseAddr和DMA_BufferSize,其中DMA_Memory0BaseAddr用于设置本次DMA传输的目的首地址, 该参数会被赋值到结构体成员DMA_InitStructure.DMA_Memory0BaseAddr中。DMA_BufferSize则用于指示本次DMA传输的数据量, 它会被赋值到结构体成员DMA_InitStructure.DMA_BufferSize中,要注意它的单位是一个字,即4字节,如我们要传输60字节的数据时, 它应配置为15。在前面的OV5640_Init函数中,对这个函数有如下调用:

#define FSMC_LCD_ADDRESS ((uint32_t)0xD0000000) /*液晶屏的分辨率,用来计算地址偏移*/ uint16_t lcd_width=800, lcd_height=480; /*摄像头采集图像的大小,改变这两个值可以改变数据量, 但不会加快采集速度,要加快采集速度需要改成SVGA*/ uint16_t img_width=800, img_height=480; //dma_memory 以16位数据为单位, dma_bufsize以32位数据为单位(即像素个数/2) OV5640_DMA_Config(FSMC_LCD_ADDRESS,img_width*2/4);

其中的lcd_width和lcd_height是液晶屏的分辨率,img_width和img_heigh表示摄像头输出的图像的分辨率,FSMC_LCD_ADDRESS是液晶层的首个显存地址。 另外,本工程中显示摄像头数据的这个液晶层采用RGB565的像素格式,每个像素点占据2个字节。

所以在上面的函数调用中,第一个输入参数:

【FSMC_LCD_ADDRESS】

它表示的是液晶屏第一行的第一个像素的地址。

而第二个输入参数:

【img_width*2/4】

它表示表示摄像头一行图像的数据量,单位为字,即用一行图像数据的像素个数除以2即可。注意这里使用的变量是“img_width”而不是的“lcd_width”。

由于这里配置的是第一次DMA传输,它把DCMI接收到的第一行摄像头数据传输至液晶屏的第一行,见图 DMA传输过程 , 再配合在后面分析的中断函数里的多次DMA配置,摄像头输出的数据会一行一行地“由上至下”显示到液晶屏上。

DMA传输完成中断及帧中断

OV5640_Init函数初始化了DCMI,使能了帧中断、DMA传输完成中断,并使能了第一次DMA传输,当这一行数据传输完成时,会进入DMA中断服务函数, 见 代码清单:OV5640-6 中的DMA2_Stream1_IRQHandler。

代码清单:OV5640-6 DMA传输完成中断与帧中断(stm32f4xx_it.c文件)¶ 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34extern uint16_t lcd_width, lcd_height; extern uint16_t img_width, img_height; //记录传输了多少行 static uint16_t line_num =0; //DMA传输完成中断服务函数 void DMA2_Stream1_IRQHandler(void) { if ( DMA_GetITStatus(DMA2_Stream1,DMA_IT_TCIF1) == SET ) { /*行计数*/ line_num++; if (line_num==img_height) { /*传输完一帧,计数复位*/ line_num=0; } /*DMA 一行一行传输*/ OV5640_DMA_Config(FSMC_LCD_ADDRESS+(lcd_width*2*line_num)),img_width*2/4); DMA_ClearITPendingBit(DMA2_Stream1,DMA_IT_TCIF1); } } //帧中断服务函数,使用帧中断重置line_num,可防止有时掉数据的时候DMA传送行数出现偏移 void DCMI_IRQHandler(void) { if ( DCMI_GetITStatus (DCMI_IT_FRAME) == SET ) { /*传输完一帧,计数复位*/ line_num=0; DCMI_ClearITPendingBit(DCMI_IT_FRAME); } }

DMA中断服务函数中主要是使用了一个静态变量line_num来记录已传输了多少行数据,每进一次DMA中断时自加1,由于进入一次中断就代表传输完一行数据, 所以line_num的值等于lcd_height时(摄像头输出的数据行数),表示传输完一帧图像,line_num复位为0,开始另一帧数据的传输。 line_num计数完毕后利用前面定义的OV5640_DMA_Config函数配置新的一行DMA数据传输,它利用line_num变量计算显存地址的行偏移, 控制DCMI数据被传送到正确的位置,每次传输的都是一行像素的数据量。

当DCMI接口检测到摄像头传输的帧同步信号时,会进入DCMI_IRQHandler中断服务函数,在这个函数中不管line_num原来的值是什么, 它都把line_num直接复位为0,这样下次再进入DMA中断服务函数的时候,它会开始新一帧数据的传输。这样可以利用DCMI的硬件同步信号, 而不只是依靠DMA自己的传输计数,这样可以避免有时STM32内部DMA传输受到阻塞而跟不上外部摄像头信号导致的数据错误。

使能DCMI采集

以上是我们使用DCMI的传输配置,但它还没有使能DCMI采集,在实际使用中还需要调用下面两个库函数开始采集数据。

//使能DCMI采集数据 DCMI_Cmd(ENABLE); DCMI_CaptureCmd(ENABLE);

读取OV5640芯片ID

配置完了STM32的DCMI,还需要控制摄像头,它有很多寄存器用于配置工作模式。利用STM32的I2C接口,可向OV5640的寄存器写入控制参数, 我们先写个读取芯片ID的函数测试一下,见 代码清单:OV5640-7 。

代码清单:OV5640-7 读取OV5640的芯片ID(bsp_ov5640.c文件)¶ 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122//存储摄像头ID的结构体 typedef struct { uint8_t PIDH; uint8_t PIDL; } OV5640_IDTypeDef; #define OV5640_SENSOR_PIDH 0x300A #define OV5640_SENSOR_PIDL 0x300B /** * @brief 读取摄像头的ID. * @param OV5640ID: 存储ID的结构体 * @retval None */ void OV5640_ReadID(OV5640_IDTypeDef *OV5640ID) { /*读取寄存芯片ID*/ OV5640ID->PIDH = OV5640_ReadReg(OV5640_SENSOR_PIDH); OV5640ID->PIDL = OV5640_ReadReg(OV5640_SENSOR_PIDL); } /** * @brief 从OV5640寄存器中读取一个字节的数据 * @param Addr: 寄存器地址 * @retval 返回读取得的数据 */ uint8_t OV5640_ReadReg(uint16_t Addr) { uint32_t timeout = DCMI_TIMEOUT_MAX; uint8_t Data = 0; /* Generate the Start Condition */ I2C_GenerateSTART(CAMERA_I2C, ENABLE); /* Test on CAMERA_I2C EV5 and clear it */ timeout = DCMI_TIMEOUT_MAX; /* Initialize timeout value */ while (!I2C_CheckEvent(CAMERA_I2C, I2C_EVENT_MASTER_MODE_SELECT)) { /* If the timeout delay is exeeded, exit with error code */ if ((timeout--) == 0) return 0xFF; } /* Send DCMI selcted device slave Address for write */ I2C_Send7bitAddress(CAMERA_I2C, OV5640_DEVICE_ADDRESS, I2C_Direction_Transmitter); /* Test on I2C1 EV6 and clear it */ timeout = DCMI_TIMEOUT_MAX; /* Initialize timeout value */ while (!I2C_CheckEvent(CAMERA_I2C, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED)) { /* If the timeout delay is exeeded, exit with error code */ if ((timeout--) == 0) return 0xFF; } /* Send I2C1 location address MSB */ I2C_SendData( CAMERA_I2C, (uint8_t)((Addr>>8) & 0xFF) ); /* Test on I2C1 EV8 and clear it */ timeout = DCMI_TIMEOUT_MAX; /* Initialize timeout value */ while (!I2C_CheckEvent(CAMERA_I2C, I2C_EVENT_MASTER_BYTE_TRANSMITTED)) { /* If the timeout delay is exeeded, exit with error code */ if ((timeout--) == 0) return 0xFF; } /* Clear AF flag if arised */ CAMERA_I2C->SR1 |= (uint16_t)0x0400; //-------------------------------------------------------- /* Send I2C1 location address LSB */ I2C_SendData( CAMERA_I2C, (uint8_t)(Addr & 0xFF) ); /* Test on I2C1 EV8 and clear it */ timeout = DCMI_TIMEOUT_MAX; /* Initialize timeout value */ while (!I2C_CheckEvent(CAMERA_I2C, I2C_EVENT_MASTER_BYTE_TRANSMITTED)) { /* If the timeout delay is exeeded, exit with error code */ if ((timeout--) == 0) return 0xFF; } /* Clear AF flag if arised */ CAMERA_I2C->SR1 |= (uint16_t)0x0400; //-------------------------------------------------------- /* Generate the Start Condition */ I2C_GenerateSTART(CAMERA_I2C, ENABLE); /* Test on I2C1 EV6 and clear it */ timeout = DCMI_TIMEOUT_MAX; /* Initialize timeout value */ while (!I2C_CheckEvent(CAMERA_I2C, I2C_EVENT_MASTER_MODE_SELECT)) { /* If the timeout delay is exeeded, exit with error code */ if ((timeout--) == 0) return 0xFF; } /* Send DCMI selcted device slave Address for write */ I2C_Send7bitAddress(CAMERA_I2C, OV5640_DEVICE_ADDRESS, I2C_Direction_Receiver); /* Test on I2C1 EV6 and clear it */ timeout = DCMI_TIMEOUT_MAX; /* Initialize timeout value */ while (!I2C_CheckEvent(CAMERA_I2C, I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED)) { /* If the timeout delay is exeeded, exit with error code */ if ((timeout--) == 0) return 0xFF; } /* Prepare an NACK for the next data received */ I2C_AcknowledgeConfig(CAMERA_I2C, DISABLE); /* Test on I2C1 EV7 and clear it */ timeout = DCMI_TIMEOUT_MAX; /* Initialize timeout value */ while (!I2C_CheckEvent(CAMERA_I2C, I2C_EVENT_MASTER_BYTE_RECEIVED)) { /* If the timeout delay is exeeded, exit with error code */ if ((timeout--) == 0) return 0xFF; } /* Prepare Stop after receiving data */ I2C_GenerateSTOP(CAMERA_I2C, ENABLE); /* Receive the Data */ Data = I2C_ReceiveData(CAMERA_I2C); /* return the read data */ return Data; }

在OV5640的PIDH及PIDL寄存器存储了产品ID,PIDH的默认值为0x56,PIDL的默认值为0x40。在代码中我们定义了一个结构体OV5640_IDTypeDef专门存储这些读取得的ID信息。

OV5640_ReadID函数中使用的OV5640_ReadReg函数是使用STM32的I2C外设向某寄存器读写单个字节数据的底层函数, 它与我们前面章节中用到的I2C函数差异是OV5640的寄存器地址是16位的。程序中是先发高8位地址接着发低8位地址,再读取寄存器的值。

向OV5640写入寄存器配置

检测到OV5640的存在后,向它写入配置参数,见 代码清单:OV5640-8 。

代码清单:OV5640-8向OV5640写入寄存器配置¶ 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131/** * @brief Configures the OV5640 camera in BMP mode. * @param BMP ImageSize: BMP image size * @retval None */ void OV5640_RGB565Config(void) { uint32_t i; /*摄像头复位*/ OV5640_Reset(); /* 写入寄存器配置 */ /* Initialize OV5640 Set to output RGB565 */ for (i=0; i


【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有