一、摄像头相关资料信息
在LCD上显示当前camera的图像数据,类似我们前面提到的LCD显示图片数据,就是实时将摄像头抓取的视频数据帧(图片)转换成图片字码表,即LCD宽*LCD高像素大小的颜色点阵,然后推送到LCD接口实现刷新显示。
本博文使用的是STM32L496VGT3开发板进行测试,该开发板自带了Camera摄像头,采用DCMI 接口与MCU通信,Camera 是由深圳影领电子有限公司提供的 YL-F170066-0329.采用的是 8bits 数据接口,通过 24Pin BTB 连接器外接一个 Camera 模组。Camera 配置通过 I2C3 接入 MCU,地址为 0x78/0x79。BTB 连接器的接口定义如下表:
即对于Camera 下行指令是采用I2C通信,视频数据推送给MCU采用的是DCMI 通信。Camera 的硬件原理框图如下:
各个引脚相关参数:
二、 基于旧(.ioc)创建新工程
本文基于本专栏前面的LCD显示图片博文上进行创建新工程,并将其源码移植到新工程,然后在此基础上进行Camera相关配置及源码实现。
cubeIDE开发,结合图片取模工具,stm32程序在LCD显示图片_py_free的博客-CSDN博客
【1】在CubeIDE工作空间,创建一个stm32L496VGTx_cam的目录文件,将前面LCD显示图片工程的(stm32L496VGTx_lcm.ioc)拷贝到该目录下,并改名为stm32L496VGTx_cam.ioc.
【2】打开cubeIDE开发工具,选择Create a New STM32 Project from an Existing STM32CubeMX Configuration File(.ioc)新建一个工程
【2】选择刚刚拷贝并修改了名字的(.ioc)文件,在工程配置属性页面中,将工程名称及工程存放路径设置为刚刚新建文件夹的路径,完成工程创建。
【4】创建工程后,在工程目录下新建一个ICore源文件夹,将原来stm32L496VGTx_lcm工程的ICore源码文件目录拷贝覆盖该文件夹。同时,也可将原来工程有过调整的spi.c和main.c文件拷贝覆盖到新工程文件中,同样,禁止syscalls.c编译。设置项目编译输出支持bin和hex格式。
然后点击编译,成功通过编译,妥妥的是上次博文中实现的工程,只是更换了个工程名字而已。
三、配置与Camera相关的MCU引脚
在成功移植LCD显示图片工程后,双击stm32L496VGTx_cam.ioc打开cubeMX编辑界面,按上述的引脚要求配置MCU与Camera相关引脚。
【1】开启I2C3的功能,参数保持默认设置即可
I2C3的引脚相关配置如下:
【2】开启DCMI功能 ,参数设置如下。
添加DCMI的DMA功能支持
并开启DCMI的终端功能支持
DCMI相关引脚配置如下:
【3】开启Camera辅助功能的GPIO引脚接口(PB8、PE13配置,与上面开发板给出的引脚参数资料不同)
PA8引脚配置
完成配置后点击保存输出生成代码。
四、Camera源码设计
在ICore中添加一个cam目录,并在该目录下创建Camera.h和Camera.c源文件。图形编辑配置界面生成代码及手动添加代码结构如下。
1、gc0329 型号芯片数据手册分析
本Camera模组的MCU芯片是gc0329 型号芯片,我在网上查阅该型号的数据手册及逐项分析如下:
【1】Camera模组参数从设备地址0X62/3,非开发板指出的0x78/0x79
gc0329芯片支持I2C接口控制信号设计如下:
PWDN 与 RESET 均是异步设计,生效时不需要 MCLK pin 有时钟提供。PWDN 高有效,Low -> 正常工作,High -> 省电模式;RESET 低有效,Low -> reset 芯片,High -> 正常工作
设帧同步信号 Vsync 低有效,行同步 Hsync 为高有效,而图像输出格式为 YCbCr 或 RGB565。例如我们在LCD显示图像时,图片转换工具了设置16位真彩的RGB565格式一样。
GC0329 采用逐行扫描的方式将阵列产生的信号依次输入到模拟信号处理模块中。最开始的
行为 0 行。在默认寄存器设置下,Sensor 的阵列数据输出顺序为从下到上,从左到右。图像扫描的方式输出设置地址为0X17。
【2】模拟转换地址(analog PWC)0XFC,将寄存器 0xfc[0]置 1,0xf0 写为 0x00。此方式同样降低功耗,输出 pin 高阻,寄存器值保持不变。此时寄存器是可以读写的。要恢复 normal 工作模式,需要将 0xfc[0]置 0,0xf0 写为 0xff。芯片内部寄存器复位默认值有两种方式:1) RESET pin 接入低电平;2) 将寄存器 0xfe[7]置 1。
更多输出使能控制。
VSync output enable 0xf0[0] 控制 VSYNC pin 输出
0 –> VSYNC pin 高阻
1 –> VSYNC pin 正常输出
HSync output enable 0xf0[1] 控制 HSYNC pin 输出
0 –> HSYNC pin 高阻
1 –> HSYNC pin 正常输出
PCLK output enable 0xf0[2] 控制 PCLK pin 输出
0 –> PCLK pin 高阻
1 –> PCLK pin 正常输出
Pixel data[7:0] output enable 0xf0[3] 控制 data pin 输出
0 –> data pin 高阻
1 –> data pin 正常输出
寄存器相关信息,设备ID存储地址为0X00。page 选择由 P0:0xfe[1:0]设置,设为 0x00 表示page0,设为 0x01 表示 page1 。
【3】输出PIN驱动能力,PCLK PIN 、Data PIN、SYNC PIN的设置
【4】时钟预分频,外部 MCLK 时钟输入后,通过 clock divider 模块对 MCLK 进行分频,芯片内部工作频率基于分频后的频率。
【5】同步模式设置,VSYNC 为场同步信号,HSYNC 为行同步信号,PCLK 为输出 data 的同步时钟,可以通过寄存器0X46来控制这三个信号的极性。
【6】GC0329 可以截取任意尺寸(≤VGA)的窗口输出,且有两种模式实现窗口输出,这两种模式输出小于 VGA 窗口时,视角均会变小。
Windowing 模式,用 column start 和 row start 来分别确定要挖窗口起始的 X/Y 坐标,用 window width-8 和 window height-8(注意寄存器设置要比实际输出多 8)来确定所需要窗口的宽度和高度。
Crop window 模式,用 Out window x0 和 Out window y0 来分别确定要挖窗口
起始的 X/Y 坐标,用 Out window width 和 Out window height 来确定所需要窗口的宽度和
高度。
subsample 模式,如果要实现视角不变输出 QVGA/QQVGA/CIF/QCIF等窗口,需要用到 subsample 模式,在 windowing(0x09-0x10 寄存器)确定好图像窗口尺寸后,采用抽点的
方式输出更小尺寸的数据,最终输出图像尺寸的行/列可以为设定窗口行/列的 1/2, 1/3, 1/4,
1/5, 1/6, 1/7(表示行为原始尺寸的 1/7,列也为原始尺寸的 1/7),同时也能实现 3/5,2/3,
4/7 等采样率。例如下图设置示例(P0:0x5a 表示 0x5a 寄存器是 page0 的)。
【7】在 GC0329 中,消除灯管频率导致的 flicker 问题,需要同时配置 exposure step register(P1:0x03,P1;0x04)Hb({P0:0x05,P0:0x06}),Vb({P0:0x07,P0:0x08}),exp_level(P1:0x2b~0x32)来实现。消除 flicker 的原理即是曝光时间是灯管周期的整数倍。
计算方法为: step * row_time = N * T。
可以配置 4 档 Exp_level,用户可以通过修改 P1:0x33[5:4]来设置用哪一档 exp_level(范围为 0-3),设置完成后,AEC 最大曝光时间只能达到 exp_level 所设定的值,从而达到控制帧率的目的。Exp_level 的配置只要用是所计算出来的 step 的整数倍即可,可按照想要的帧率配置,每个 level 需要配两个寄存器,如 0xe4[3:0]为高位,0xe5 为低位。
【8】输出模式设置
YUV/RGB565 输出设置及RawRGB 输出设置:
自动曝光控制(Auto Exposure Control)设置
Function Address Value Description
AEC enable P0:0x4f[0] 1’b0 使能 AEC 模块
Target Y P1:0x13 0x50 AEC 目标亮度值
AEC action period P1:0x11[6:4] 2’b3 AEC 每隔几帧调一次
Exp level P1:0x2b-0x32 -- 配置曝光等级,具体见 4.6.2
Max exp level P1:0x33[5:4] -- 选择曝光等级 0~3 中的一档
Exp min P1:0x34 0x04 设置 AEC 自动调节的最小值
Auto pre gain P0:0x71 -- AEC 使能时,为只读。
Auto post gain P0:0x72 -- AEC 不使能时,可读写。
Pre gain limit P1:0x22 0x40 Auto_pre_gain 的最大值。
Post gain limit P1:0x21 0x80 Auto_post_gain 的最大值。
自动白平衡(Auto White Balance)设置
Function Address Value Description
AWB enable P0:0x42[1] 1’b0 使能 AWB 模块
AWB_R_gain P0:0x77 -- AWB 使能时,为只读。
AWB 不使能时,可读写。
AWB_G_gain P0:0x78 --
AWB_B_gain P0:0x79 --
AWB_R_gain_limit P1:0x67 0x80 AWB 中 R 通道的增益限制
AWB_G_gain_limit P1:0x68 0x80 AWB 中 G 通道的增益限制
AWB_B_gain_limit P1:0x69 0x80 AWB 中 B 通道的增益限制
AWB action period P1: 0x5d[5:4] 1’b0 AWB 每隔几帧调一次。
R_ratio P0:0x7a 0x80 做完 AWB 后 R 通道所乘的增益,0x80 为 1 倍增益。
G_ratio P0:0x7b 0x80 做完 AWB 后 R 通道所乘的增益,0x80 为 1 倍增益。
B_ratio P0:0x7c 0x80 做完 AWB 后 B 通道所乘的增益,0x80 为 1 倍增益。
以及镜头阴影补偿(Lens Shading Compensation)、去坏点(Delete defective points)、去噪(Denoise)、边缘加强(Edge Enhancement)、饱和度、对比度、特效参数、Gamma 调整、色彩矩阵(Color Matrix)等摄像头图像输出相关设置就不逐一列举。
2.程序代码设计
【1】伪代码实现设计
从机地址:0X62/0X63
Init
拉低CAM_PD,拉高CAM_RST
向 0xfe 写入 设置值
向 0xfc 写入 设置值
向 0x00 读取 CHIPID 验证是否==0XC0
进行 摄像头参数设置:
(analog、blk、ISP、DNDD、INTPEE、ASDE、RGB-gamma、Y-gamma、YCP、AEC、ABS、AWB、LSC、flicker、输出等)
进行 LCD显示输出配置(VGA-DCMI)
启动DCMI抓取数据及写入DMA
刷新显示
DCMI回调,调用LCD显示函数进行屏幕刷新显示
【2】依据上述分析及伪代码设计,给出camera.h和camera.c源码
#ifndef __GC0329_H
#define __GC0329_H
#include "main.h"
#include "dcmi.h"
#include "i2c.h"
#include "gpio.h"
#include "../oled/oled.h"
#define GC0329_I2CADDR 0X62
#define GC0329_ID 0xC0
#define GC0329_CHIPID 0x00
/* GC0329 Registers definition */
#define GC0329_SENSOR_PIDH 0x00
#define GC0329_SENSOR_PIDL 0x0B
#define GC0329_SENSOR_COM7 0x12
#define GC0329_SENSOR_TSLB 0x3A
#define GC0329_SENSOR_MTX1 0x4F
#define GC0329_SENSOR_MTX2 0x50
#define GC0329_SENSOR_MTX3 0x51
#define GC0329_SENSOR_MTX4 0x52
#define GC0329_SENSOR_MTX5 0x53
#define GC0329_SENSOR_MTX6 0x54
#define GC0329_SENSOR_BRTN 0x55
#define GC0329_SENSOR_CNST1 0x56
#define GC0329_SENSOR_CNST2 0x57
/**
* @brief GC0329 Features Parameters
*/
#define GC0329_BRIGHTNESS_LEVEL0 0xB0 /* Brightness level -2 */
#define GC0329_BRIGHTNESS_LEVEL1 0x98 /* Brightness level -1 */
#define GC0329_BRIGHTNESS_LEVEL2 0x00 /* Brightness level 0 */
#define GC0329_BRIGHTNESS_LEVEL3 0x18 /* Brightness level +1 */
#define GC0329_BRIGHTNESS_LEVEL4 0x30 /* Brightness level +2 */
#define GC0329_BLACK_WHITE_BW 0xCC000000000000 /* Black and white effect */
#define GC0329_BLACK_WHITE_NEGATIVE 0xEC808000008080 /* Negative effect */
#define GC0329_BLACK_WHITE_BW_NEGATIVE 0xEC000000000000 /* BW and Negative effect */
#define GC0329_BLACK_WHITE_NORMAL 0xCC808000008080 /* Normal effect */
#define GC0329_CONTRAST_LEVEL0 0x30 /* Contrast level -2 */
#define GC0329_CONTRAST_LEVEL1 0x38 /* Contrast level -1 */
#define GC0329_CONTRAST_LEVEL2 0x40 /* Contrast level 0 */
#define GC0329_CONTRAST_LEVEL3 0x50 /* Contrast level +1 */
#define GC0329_CONTRAST_LEVEL4 0x60 /* Contrast level +2 */
#define GC0329_COLOR_EFFECT_NONE 0xCC808000008080 /* No color effect */
#define GC0329_COLOR_EFFECT_ANTIQUE 0xCC000020F00000 /* Antique effect */
#define GC0329_COLOR_EFFECT_BLUE 0xCC000000000060 /* Blue effect */
#define GC0329_COLOR_EFFECT_GREEN 0xCC000000008000 /* Green effect */
#define GC0329_COLOR_EFFECT_RED 0xCC600000000000 /* Red effect */
#define CAMERA_R160x120 0x00 /* QQVGA Resolution */
#define CAMERA_R320x240 0x01 /* QVGA Resolution */
#define CAMERA_R480x272 0x02 /* 480x272 Resolution */
#define CAMERA_R640x480 0x03 /* VGA Resolution */
#define CAMERA_CONTRAST_BRIGHTNESS 0x00 /* Camera contrast brightness features */
#define CAMERA_BLACK_WHITE 0x01 /* Camera black white feature */
#define CAMERA_COLOR_EFFECT 0x03 /* Camera color effect feature */
#define CAMERA_BRIGHTNESS_LEVEL0 0x00 /* Brightness level -2 */
#define CAMERA_BRIGHTNESS_LEVEL1 0x01 /* Brightness level -1 */
#define CAMERA_BRIGHTNESS_LEVEL2 0x02 /* Brightness level 0 */
#define CAMERA_BRIGHTNESS_LEVEL3 0x03 /* Brightness level +1 */
#define CAMERA_BRIGHTNESS_LEVEL4 0x04 /* Brightness level +2 */
#define CAMERA_CONTRAST_LEVEL0 0x05 /* Contrast level -2 */
#define CAMERA_CONTRAST_LEVEL1 0x06 /* Contrast level -1 */
#define CAMERA_CONTRAST_LEVEL2 0x07 /* Contrast level 0 */
#define CAMERA_CONTRAST_LEVEL3 0x08 /* Contrast level +1 */
#define CAMERA_CONTRAST_LEVEL4 0x09 /* Contrast level +2 */
#define CAMERA_BLACK_WHITE_BW 0x00 /* Black and white effect */
#define CAMERA_BLACK_WHITE_NEGATIVE 0x01 /* Negative effect */
#define CAMERA_BLACK_WHITE_BW_NEGATIVE 0x02 /* BW and Negative effect */
#define CAMERA_BLACK_WHITE_NORMAL 0x03 /* Normal effect */
#define CAMERA_COLOR_EFFECT_NONE 0x00 /* No effects */
#define CAMERA_COLOR_EFFECT_BLUE 0x01 /* Blue effect */
#define CAMERA_COLOR_EFFECT_GREEN 0x02 /* Green effect */
#define CAMERA_COLOR_EFFECT_RED 0x03 /* Red effect */
#define CAMERA_COLOR_EFFECT_ANTIQUE 0x04 /* Antique effect */
/**
* @}
*/
typedef struct
{
void (*Init)(uint16_t, uint32_t);
uint16_t (*ReadID)(uint16_t);
void (*Config)(uint16_t, uint32_t, uint32_t, uint32_t);
} CAMERA_DrvTypeDef;
//void gc0329_mclk_onoff(int on);
void gc0329_power_onoff(int on);
void gc0329_Init(uint16_t DeviceAddr, uint32_t resolution);
void gc0329_Config(uint16_t DeviceAddr, uint32_t feature, uint32_t value, uint32_t BR_value);
uint16_t gc0329_ReadID(uint16_t dummy);
uint64_t gc0329_ConvertValue(uint32_t feature, uint32_t value);
/* CAMERA driver structure */
extern CAMERA_DrvTypeDef gc0329_drv;
extern DCMI_HandleTypeDef hdcmi;
void enable_camera_display(uint8_t on);
void CAMERA_Init(uint32_t Resolution);
void CameraDEMO_Init();
void enable_camera_display(uint8_t on);
int CameraHAL_Capture_Start(uint8_t * buf, uint32_t len, void(*notify)());
#endif //__GC0329_H
camera.c
#include "camera.h"
#include <string.h>
#include <stdio.h>
extern I2C_HandleTypeDef hi2c3;
CAMERA_DrvTypeDef *camera_drv;
DCMI_HandleTypeDef *phdcmi;
uint16_t *camera_buff = NULL;
static void(*pCapFunc)() = 0;
CAMERA_DrvTypeDef gc0329_drv =
{
gc0329_Init,
gc0329_ReadID,
gc0329_Config,
};
/* Initialization sequence for VGA resolution (640x480)*/
const unsigned char GC0329_VGA[][2] =
{
{0xfe, 0x80},
{0xfc, 0x16},
{0xfc, 0x16},
{0xfe, 0x00},
{0xfa, 0x00},
{0x70, 0x48},
{0x73, 0x90},
{0x74, 0x80},
{0x75, 0x80},
{0x76, 0x94}, //80 jambo
{0x77, 0x62},
{0x78, 0x47},
{0x79, 0x40},
{0x03, 0x02},
{0x04, 0x40},
analog
{0xfc, 0x16},
{0x09, 0x00},
{0x0a, 0x02},
{0x0b, 0x00},
{0x0c, 0x02},
#ifndef CAMERA_GRAY_MIRROR
{0x17, 0x15}, //0x14
#endif
{0x19, 0x05},
{0x1b, 0x24},
{0x1c, 0x04},
{0x1e, 0x08},
{0x1f, 0x08}, //C8
{0x20, 0x01},
{0x21, 0x48},
{0x22, 0xba},
{0x23, 0x22},
{0x24, 0x16},
blk
added for MID
{0x26, 0xf4}, //BLK
{0x2a, 0x2c},
{0x2b, 0x2c},
{0x2c, 0x2c},
{0x2d, 0x2c},
{0x26, 0xf7}, //BLK
{0x28, 0x7f}, //BLK limit
{0x29, 0x00},
{0x32, 0x00}, //04 darkc
{0x33, 0x20}, //blk ratio
{0x34, 0x20},
{0x35, 0x20},
{0x36, 0x20},
{0x3b, 0x04}, //manual offset
{0x3c, 0x04},
{0x3d, 0x04},
{0x3e, 0x04},
ISP BLOCK ENABLE
{0x40, 0xff},
{0x41, 0x24}, //[5]skin detection
{0x42, 0xfa}, //disable ABS
{0x46, 0x03},
{0x4b, 0xca},
{0x4d, 0x01},
{0x4f, 0x01},
{0x70, 0x48},
DNDD
{0x80, 0x07}, // 0xe7 20140915
{0x81, 0xc2}, // 0x22 20140915
{0x82, 0x90}, //DN auto DNDD DEC DNDD //0e //55 jambo
{0x83, 0x05},
{0x87, 0x40}, // 0x4a 20140915
INTPEE
{0x90, 0x8c}, //ac
{0x92, 0x05},
{0x94, 0x05},
{0x95, 0x45}, //0x44
{0x96, 0x88},
ASDE
{0xfe, 0x01},
{0x18, 0x22},
{0xfe, 0x00},
{0x9c, 0x0a},
{0xa0, 0xaf},
{0xa2, 0xff},
{0xa4, 0x30}, //50 jambo
{0xa5, 0x31},
{0xa7, 0x35},
RGB gamma
{0xfe, 0x00},
{0xbf, 0x0b},
{0xc0, 0x1d},
{0xc1, 0x33},
{0xc2, 0x49},
{0xc3, 0x5d},
{0xc4, 0x6e},
{0xc5, 0x7c},
{0xc6, 0x99},
{0xc7, 0xaf},
{0xc8, 0xc2},
{0xc9, 0xd0},
{0xca, 0xda},
{0xcb, 0xe2},
{0xcc, 0xe7},
{0xcd, 0xf0},
{0xce, 0xf7},
{0xcf, 0xff},
Y gamma
{0xfe, 0x00},
{0x63, 0x00},
{0x64, 0x06},
{0x65, 0x0d},
{0x66, 0x1b},
{0x67, 0x2b},
{0x68, 0x3d},
{0x69, 0x50},
{0x6a, 0x60},
{0x6b, 0x80},
{0x6c, 0xa0},
{0x6d, 0xc0},
{0x6e, 0xe0},
{0x6f, 0xff},
//CC///
#if 1 //main
{0xfe, 0x00},
{0xb3, 0x44},
{0xb4, 0xfd},
{0xb5, 0x02},
{0xb6, 0xfa},
{0xb7, 0x48},
{0xb8, 0xf0},
#else //sub
{0xfe, 0x00},
{0xb3, 0x42}, //40
{0xb4, 0xff}, //00
{0xb5, 0x06}, //06
{0xb6, 0xf0}, //00
{0xb7, 0x44}, //40
{0xb8, 0xf0}, //00
#endif
// crop
{0x50, 0x01},
YCP
{0xfe, 0x00},
{0xd0, 0x40},
{0xd1, 0x28},
{0xd2, 0x28},
{0xd3, 0x40}, //cont 0x40
{0xd5, 0x00},
{0xdd, 0x14},
{0xde, 0x34},
AEC
{0xfe, 0x01},
{0x10, 0x40}, // before Gamma
{0x11, 0x21}, //
{0x12, 0x13}, // center weight *2
{0x13, 0x50}, //4 //4}, // AE Target
#ifdef CAMERA_GRAY_MIRROR
{0x17, 0xa9}, //88, 08, c8, a8
#else
{0x17, 0xa8}, //88, 08, c8, a8
#endif
{0x1a, 0x21},
{0x20, 0x31}, //AEC stop margin
{0x21, 0xc0},
{0x22, 0x60},
{0x3c, 0x50},
{0x3d, 0x40},
{0x3e, 0x45}, //read 3f for status
//main
{0xfe, 0x01},
{0x06, 0x12},
{0x07, 0x06},
{0x08, 0x9c},
{0x09, 0xee},
{0x50, 0xfc},
{0x51, 0x28},
{0x52, 0x10},
{0x53, 0x20},
{0x54, 0x12},
{0x55, 0x16},
{0x56, 0x30},
{0x58, 0x60},
{0x59, 0x08},
{0x5a, 0x02},
{0x5b, 0x63},
{0x5c, 0x35},
{0x5d, 0x72},
{0x5e, 0x11},
{0x5f, 0x40},
{0x60, 0x40},
{0x61, 0xc8},
{0x62, 0xa0},
{0x63, 0x40},
{0x64, 0x50},
{0x65, 0x98},
{0x66, 0xfa},
{0x67, 0x80},
{0x68, 0x60},
{0x69, 0x90},
{0x6a, 0x40},
{0x6b, 0x39},
{0x6c, 0x30},
{0x6d, 0x60},
{0x6e, 0x41},
{0x70, 0x10},
{0x71, 0x00},
{0x72, 0x10},
{0x73, 0x40},
{0x80, 0x60},
{0x81, 0x50},
{0x82, 0x42},
{0x83, 0x40},
{0x84, 0x40},
{0x85, 0x40},
{0x74, 0x40},
{0x75, 0x58},
{0x76, 0x24},
{0x77, 0x40},
{0x78, 0x20},
{0x79, 0x60},
{0x7a, 0x58},
{0x7b, 0x20},
{0x7c, 0x30},
{0x7d, 0x35},
{0x7e, 0x10},
{0x7f, 0x08},
///ABS///
{0x9c, 0x00},
{0x9e, 0xc0},
{0x9f, 0x40},
CC-AWB
{0xd0, 0x00},
{0xd2, 0x2c},
{0xd3, 0x80},
///LSC //
{0xfe, 0x01},
{0xc0, 0x0b},
{0xc1, 0x07},
{0xc2, 0x05},
{0xc6, 0x0b},
{0xc7, 0x07},
{0xc8, 0x05},
{0xba, 0x39},
{0xbb, 0x24},
{0xbc, 0x23},
{0xb4, 0x39},
{0xb5, 0x24},
{0xb6, 0x23},
{0xc3, 0x00},
{0xc4, 0x00},
{0xc5, 0x00},
{0xc9, 0x00},
{0xca, 0x00},
{0xcb, 0x00},
{0xbd, 0x2b},
{0xbe, 0x00},
{0xbf, 0x00},
{0xb7, 0x09},
{0xb8, 0x00},
{0xb9, 0x00},
{0xa8, 0x31},
{0xa9, 0x23},
{0xaa, 0x20},
{0xab, 0x31},
{0xac, 0x23},
{0xad, 0x20},
{0xae, 0x31},
{0xaf, 0x23},
{0xb0, 0x20},
{0xb1, 0x31},
{0xb2, 0x23},
{0xb3, 0x20},
{0xa4, 0x00},
{0xa5, 0x00},
{0xa6, 0x00},
{0xa7, 0x00},
{0xa1, 0x3c},
{0xa2, 0x50},
{0xfe, 0x00},
flicker ///
{0x05, 0x02},
{0x06, 0x2c},
{0x07, 0x00},
{0x08, 0xb8},
{0xfe, 0x01},
{0x29, 0x00}, //anti-flicker step [11:8]
{0x2a, 0x60}, //anti-flicker step [7:0]
{0x2b, 0x02}, //exp level 0 14.28fps
{0x2c, 0xa0},
{0x2d, 0x03}, //exp level 1 12.50fps
{0x2e, 0x00},
{0x2f, 0x03}, //exp level 2 10.00fps
{0x30, 0xc0},
{0x31, 0x05}, //exp level 3 7.14fps
{0x32, 0x40},
{0x33, 0x20},
{0xfe, 0x00},
out ///
#ifdef CAMERA_GRAY_MIRROR
{0x44, 0xB1},
#else
{0x44, 0xA6},
#endif
{0xf0, 0x07},
{0xf1, 0x01},
};
/* Initialization sequence for QVGA resolution (320x240) */
const unsigned char GC0329_QVGA[][2] =
{
{0xff, 0xff},
};
/* Initialization sequence for QQVGA resolution (160x120) */
const char GC0329_QQVGA[][2] =
{
{0xff, 0xff},
};
//void gc0329_mclk_onoff(int on)
//{
// RCC_OscInitTypeDef RCC_OscInitStruct;
//
// /*xiehj add RCC_OSCILLATORTYPE_HSI48*/
// RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI48;
//
// /*xiehj add*/
// if(on)
// RCC_OscInitStruct.HSI48State = RCC_HSI48_ON;
// else
// RCC_OscInitStruct.HSI48State = RCC_HSI48_OFF;
//
// if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
// {
//
// }
//}
void gc0329_power_onoff(int on)
{
if(1 == on)
{
HAL_GPIO_WritePin(CAM_PD_GPIO_Port, CAM_PD_Pin, GPIO_PIN_RESET);
HAL_Delay(10);
HAL_GPIO_WritePin(CAM_RST_GPIO_Port, CAM_RST_Pin, GPIO_PIN_RESET);
HAL_Delay(50);
HAL_GPIO_WritePin(CAM_RST_GPIO_Port, CAM_RST_Pin, GPIO_PIN_SET);
}
else
{
HAL_GPIO_WritePin(CAM_RST_GPIO_Port, CAM_RST_Pin, GPIO_PIN_RESET);
HAL_Delay(50);
HAL_GPIO_WritePin(CAM_PD_GPIO_Port, CAM_PD_Pin, GPIO_PIN_SET);
}
}
uint8_t CAMERA_IO_Read(uint8_t DevAddr, uint8_t Reg)
{
uint8_t read_value = 0;
HAL_StatusTypeDef i2c3_status = 0X00;
// i2c3_status = HAL_I2C_Mem_Read_IT(&hi2c3, DevAddr, Reg, I2C_MEMADD_SIZE_8BIT, (uint8_t *)&read_value, 1);
i2c3_status = HAL_I2C_Mem_Read(&hi2c3, DevAddr, Reg, I2C_MEMADD_SIZE_8BIT, (uint8_t *)&read_value, 1, 1000);
if(HAL_OK != i2c3_status)
{
printf("[1]CAMERA_IO_Read error HAL_StatusTypeDef(0x%02x) ErrorCode(%lu) State(%u) \r\n"
,i2c3_status,hi2c3.ErrorCode,hi2c3.State);
return read_value ;
}
return read_value;
}
uint8_t CAMERA_IO_Write(uint8_t DevAddr, uint8_t Reg, uint8_t Value)
{
HAL_StatusTypeDef i2c3_status = 0X00;
// i2c3_status = HAL_I2C_Mem_Write_IT(&hi2c3, DevAddr, Reg, I2C_MEMADD_SIZE_8BIT, (uint8_t *)&Value, 1);
i2c3_status = HAL_I2C_Mem_Write(&hi2c3, DevAddr, Reg, I2C_MEMADD_SIZE_8BIT, (uint8_t *)&Value, 1, 1000);
if(HAL_OK != i2c3_status)
{
printf("1)CAMERA_IO_Write error HAL_StatusTypeDef(0x%02x) hi2c3.ErrorCode(%lu) hi2c3.State(%u)\r\n"
,i2c3_status,hi2c3.ErrorCode,hi2c3.State);
return HAL_ERROR;
}
return HAL_OK ;
}
uint16_t gc0329_ReadID(uint16_t dummy)
{
/* Get the camera ID */
// CAMERA_IO_Write(0xfe, 0x00);
// CAMERA_IO_Write(GC0329_I2CADDR,0xfe, 0x48);
CAMERA_IO_Write(GC0329_I2CADDR,0xfc, 0x16);
return (CAMERA_IO_Read(GC0329_I2CADDR, GC0329_CHIPID));
}
void gc0329_Init(uint16_t DeviceAddr, uint32_t resolution)
{
uint32_t index;
/* Initialize GC0329 */
switch (resolution)
{
case CAMERA_R160x120:
{
for(index = 0; index < (sizeof(GC0329_QQVGA) / 2); index++)
{
CAMERA_IO_Write(DeviceAddr, GC0329_QQVGA[index][0], GC0329_QQVGA[index][1]);
HAL_Delay(2);
}
break;
}
case CAMERA_R320x240:
{
for(index = 0; index < (sizeof(GC0329_QVGA) / 2); index++)
{
CAMERA_IO_Write(DeviceAddr, GC0329_QVGA[index][0], GC0329_QVGA[index][1]);
HAL_Delay(2);
}
break;
}
case CAMERA_R480x272:
{
/* Not supported resolution */
break;
}
case CAMERA_R640x480:
{
for(index = 0; index < (sizeof(GC0329_VGA) / 2); index++)
{
CAMERA_IO_Write(DeviceAddr, GC0329_VGA[index][0], GC0329_VGA[index][1]);
HAL_Delay(2);
}
break;
}
default:
{
break;
}
}
}
void gc0329_Config(uint16_t DeviceAddr, uint32_t feature, uint32_t value, uint32_t brightness_value)
{
/* Convert the input value into gc0329 parameters */
gc0329_ConvertValue(feature, value);
gc0329_ConvertValue(CAMERA_CONTRAST_BRIGHTNESS, brightness_value);
}
uint64_t gc0329_ConvertValue(uint32_t feature, uint32_t value)
{
// uint64_t ret = 0;
return 0;
#if 0
switch(feature)
{
case CAMERA_BLACK_WHITE:
{
switch(value)
{
case CAMERA_BLACK_WHITE_BW:
{
ret = GC0329_BLACK_WHITE_BW;
break;
}
case CAMERA_BLACK_WHITE_NEGATIVE:
{
ret = GC0329_BLACK_WHITE_NEGATIVE;
break;
}
case CAMERA_BLACK_WHITE_BW_NEGATIVE:
{
ret = GC0329_BLACK_WHITE_BW_NEGATIVE;
break;
}
case CAMERA_BLACK_WHITE_NORMAL:
{
ret = GC0329_BLACK_WHITE_NORMAL;
break;
}
default:
{
ret = GC0329_BLACK_WHITE_NORMAL;
break;
}
}
break;
}
case CAMERA_CONTRAST_BRIGHTNESS:
{
switch(value)
{
case CAMERA_BRIGHTNESS_LEVEL0:
{
ret = GC0329_BRIGHTNESS_LEVEL0;
break;
}
case CAMERA_BRIGHTNESS_LEVEL1:
{
ret = GC0329_BRIGHTNESS_LEVEL1;
break;
}
case CAMERA_BRIGHTNESS_LEVEL2:
{
ret = GC0329_BRIGHTNESS_LEVEL2;
break;
}
case CAMERA_BRIGHTNESS_LEVEL3:
{
ret = GC0329_BRIGHTNESS_LEVEL3;
break;
}
case CAMERA_BRIGHTNESS_LEVEL4:
{
ret = GC0329_BRIGHTNESS_LEVEL4;
break;
}
case CAMERA_CONTRAST_LEVEL0:
{
ret = GC0329_CONTRAST_LEVEL0;
break;
}
case CAMERA_CONTRAST_LEVEL1:
{
ret = GC0329_CONTRAST_LEVEL1;
break;
}
case CAMERA_CONTRAST_LEVEL2:
{
ret = GC0329_CONTRAST_LEVEL2;
break;
}
case CAMERA_CONTRAST_LEVEL3:
{
ret = GC0329_CONTRAST_LEVEL3;
break;
}
case CAMERA_CONTRAST_LEVEL4:
{
ret = GC0329_CONTRAST_LEVEL4;
break;
}
default:
{
ret = GC0329_CONTRAST_LEVEL0;
break;
}
}
break;
}
case CAMERA_COLOR_EFFECT:
{
switch(value)
{
case CAMERA_COLOR_EFFECT_ANTIQUE:
{
ret = GC0329_COLOR_EFFECT_ANTIQUE;
break;
}
case CAMERA_COLOR_EFFECT_BLUE:
{
ret = GC0329_COLOR_EFFECT_BLUE;
break;
}
case CAMERA_COLOR_EFFECT_GREEN:
{
ret = GC0329_COLOR_EFFECT_GREEN;
break;
}
case CAMERA_COLOR_EFFECT_RED:
{
ret = GC0329_COLOR_EFFECT_RED;
break;
}
case CAMERA_COLOR_EFFECT_NONE:
default:
{
ret = GC0329_COLOR_EFFECT_NONE;
break;
}
}
break;
default:
{
ret = 0;
break;
}
}
}
#endif
// return ret;
}
uint8_t camera_dis_on = 0;
void enable_camera_display(uint8_t on)
{
camera_dis_on = on;
}
void CAMERA_Init(uint32_t Resolution)
{
camera_drv = &gc0329_drv;
phdcmi = &hdcmi;
/* Camera Module Initialization via I2C to the wanted 'Resolution' */
if (Resolution == CAMERA_R640x480)
{
/* For 240x240 resolution, the OV9655 sensor is set to QVGA resolution
* as OV9655 doesn't supports 240x240 resolution,
* then DCMI is configured to output a 240x240 cropped window */
camera_drv->Init(GC0329_I2CADDR, CAMERA_R640x480);
HAL_DCMI_ConfigCROP(phdcmi,
150, /* Crop in the middle of the VGA picture */
120, /* Same height (same number of lines: no need to crop vertically) */
(WIDTH * 2) - 1, /* 2 pixels clock needed to capture one pixel */
(HEIGHT * 1) - 1); /* All 240 lines are captured */
HAL_DCMI_EnableCROP(phdcmi);
}
}
void CameraDEMO_Init()
{
uint16_t buff[WIDTH * HEIGHT] = {LCD_DISP_BLUE};
camera_buff = buff;
// camera_dispaly(camera_buff, (WIDTH * HEIGHT));
gc0329_power_onoff(1);
uint8_t sensor_id = gc0329_ReadID(0);
printf("camera sensor_id = 0x%02X\r\n", sensor_id);
/* gc0329_power_onoff(0); */
if (sensor_id == GC0329_ID)
{
CAMERA_Init(CAMERA_R640x480);
/* Wait 1s to let auto-loops in the camera module converge and lead to correct exposure */
/* aos_msleep(1000); */
HAL_Delay(200);
/*##-4- Camera Continuous capture start in QVGA resolution ############################*/
/* Disable unwanted HSYNC (IT_LINE)/VSYNC interrupts */
__HAL_DCMI_DISABLE_IT(phdcmi, DCMI_IT_LINE | DCMI_IT_VSYNC);
/* LCD size is 240 x 240 and format is RGB565 i.e. 16 bpp or 2 bytes/pixel.
The LCD frame size is therefore 240 * 240 half-words of (240*240)/2 32-bit long words .
Since the DMA associated to DCMI IP is configured in BSP_CAMERA_MspInit() of stm32l496g_discovery_camera.c file
with words alignment, the last parameter of HAL_DCMI_Start_DMA is set to:
(ST7789H2_LCD_PIXEL_WIDTH*ST7789H2_LCD_PIXEL_HEIGHT)/2, that is 240 * 240 / 2
*/
HAL_DCMI_Start_DMA(phdcmi, DCMI_MODE_CONTINUOUS, (uint32_t)camera_buff, (WIDTH * HEIGHT) / 2);
/* OnError_Handler(hal_status != HAL_OK); */
}
}
void GC0329_CAMERA_FrameEventCallback(void)
{
if (pCapFunc)
{
pCapFunc();
return;
}
if (camera_dis_on)
{
HAL_DCMI_Suspend(phdcmi);
camera_dispaly(camera_buff, (WIDTH * HEIGHT));
HAL_DCMI_Start_DMA(phdcmi, DCMI_MODE_CONTINUOUS, (uint32_t)camera_buff, (WIDTH * HEIGHT) / 2);
HAL_DCMI_Resume(phdcmi);
}
}
/**
* @brief Frame event callback
* @param hdcmi: pointer to the DCMI handle
* @retval None
*/
void HAL_DCMI_FrameEventCallback(DCMI_HandleTypeDef *hdcmi)
{
GC0329_CAMERA_FrameEventCallback();
}
int CameraHAL_Capture_Config(uint16_t w, uint16_t h)
{
HAL_StatusTypeDef hal_status = HAL_OK;
if (w > 640 || h > 480) return 0;
camera_drv = &gc0329_drv;
phdcmi = &hdcmi;
gc0329_power_onoff(1);
camera_drv->Init(GC0329_I2CADDR, CAMERA_R640x480);
HAL_DCMI_ConfigCROP(phdcmi,
(640 - w) >> 1, /* Crop in the middle of the VGA picture */
(480 - h) >> 1, /* Same height (same number of lines: no need to crop vertically) */
(w * 1) - 1, /* 2 pixels clock needed to capture one pixel */
(w * 1) - 1); /* All 240 lines are captured */
HAL_DCMI_EnableCROP(phdcmi);
HAL_Delay(1000);
__HAL_DCMI_DISABLE_IT(phdcmi, DCMI_IT_LINE | DCMI_IT_VSYNC);
return hal_status == HAL_OK;
}
int CameraHAL_Capture_Start(uint8_t * buf, uint32_t len, void(*notify)())
{
HAL_StatusTypeDef hal_status = HAL_OK;
pCapFunc = notify;
if (pCapFunc)
{
hal_status = HAL_DCMI_Start_DMA(phdcmi, DCMI_MODE_SNAPSHOT, (uint32_t)buf, len);
}
return hal_status == HAL_OK;
}
五、编辑及测试
【1】在main.c转函数文件中,加入camera.h的引用
/* USER CODE BEGIN Includes */
#include "../../ICore/key/key.h"
#include "../../ICore/led/led.h"
#include "../../ICore/print/print.h"
#include "../../ICore/usart/usart.h"
#include "../../ICore/oled/oled.h"
#include "../../ICore/cam/camera.h"
/* USER CODE END Includes */
在主函数内调用camera的初始函数
/* USER CODE BEGIN 2 */
ResetPrintInit(&hlpuart1);
HAL_UART_Receive_IT(&hlpuart1,(uint8_t *)&HLPUSART_NewData, 1); //再开启接收中断
HLPUSART_RX_STA = 0;
//
OLED_init();
//
CameraDEMO_Init();
uint8_t flag = 0x00;
uint8_t bkflag = 0X00;
/* USER CODE END 2 */
【2】在主函数循环体内,设置通过按键2开启或关闭摄像头抓取数据推送LCD屏幕显示的功能
/* USER CODE BEGIN WHILE */
while (1)
{
if(HLPUSART_RX_STA&0xC000){//溢出或换行,重新开始
//设置OLED蓝色背景显示
if(!bkflag){
BSP_LCD_Clear_DMA(LCD_DISP_BLUE);
bkflag = 0x01;
}
// printf("OLED_Clear_DMA\r\n");
//printf("%.*s\r\n",HLPUSART_RX_STA&0X0FFF, HLPUSART_RX_BUF);
OLED_printf(10,10,"%.*s",HLPUSART_RX_STA&0X0FFF, HLPUSART_RX_BUF);
HLPUSART_RX_STA=0;//接收错误,重新开始
HAL_Delay(100);//等待
}
if(KEY_0())
{
BSP_LCD_login(24,108);
bkflag = 0x00;
}
if(KEY_1())
{
BSP_LCD_img_DMA();
bkflag = 0x00;
}
if(KEY_2())
{
flag = !flag;
enable_camera_display(flag);
bkflag = 0x00;
}
/* USER CODE END WHILE */
【3】在运行按钮下拉选择,进入运行配置界面,新建运行配置,默认配置。
【4】编译及下载
【5】 按键2,测试视频显示输出