摄像头应用¶
1. 文档概述¶
1.1 文档目的¶
本文档旨在详细说明模组外接camera 的硬件连接、软件接口及使用的注意事项。功能覆盖拍照/预览/图片压缩/扫码/数据存储/数据传输等场景,如智能扫码设备、智能水表/电表/气表等
| 功能 | 状态 | 备注 |
|---|---|---|
| 拍照 | 已实现 | 支持YUV和ONLY Y格式 |
| 预览 | 开发中 | |
| 图片压缩(JPEG) | 已实现 | 支持YUV/ONLY Y数据编码成jpeg |
| 扫码 (ZBAR) | 已实现 | 支持一维码(CODE128 EAN-13)、 二维码(QR CODE)识别 |
| 扫码 (QY-ZXING) | 已实现 | 商用扫码库,支持多种码制识别 |
1.2 开发板框图¶

2. 引脚定义¶
摄像头接口¶
| 引脚号 | 信号名称 | 对应模块管脚号 | 说明 |
|---|---|---|---|
| 1 | GND | 地 | |
| 2 | CAM_VDD | 摄像头电源 | |
| 3 | CAM_VDDIO | 24 | 摄像头数字电源 |
| 4 | CAM_I2C_SCL | 57 | 摄像头I2C时钟信号 |
| 5 | CAM_I2C_SDA | 58 | 摄像头I2C数据信号 |
| 6 | GND | 地 | |
| 7 | CAM_SPI_CLK | 80 | 摄像头SPI时钟信号 |
| 8 | GND | 地 | |
| 9 | CAM_MCLK | 54 | 摄像头主时钟信号 |
| 10 | CAM_SPI_D0 | 55 | 摄像头SPI数据信号0 |
| 11 | CAM_SPI_D1 | 56 | 摄像头SPI数据信号1 |
| 12 | CAM_PWDN | 81 | 摄像头POWER DOWN控制信号 |
| 13 | PIN39 | 39 | 预留功能 |
| 14 | PIN40 | 40 | 预留功能 |
| 15 | VBAT | 42 | 闪光灯电源 |
3. 软件环境¶
下载yopen代码¶
git clone https://gitee.com/yuge-info/yopen.git
关键代码¶
- 围绕 Camera 功能搭建多组件协同的软件架构,各组件功能及关联接口如下:
| 组件/头文件 | 功能 | 相关接口定义 |
|---|---|---|
| components\camera | camera组件、提供拍照、预览(开发中),支持 YUYV、ONLY Y、RGB565 数据格式输出 | cameraDev.h |
| components\lcd | lcd组件、支持 Camera 数据预览显示(开发中) | lcdDrv.h |
| components\zbar | zbar开源组件、默认支持指定一维 / 二维码识别,可配置编码格式 | zbar.h、zbar_config.h |
| components\qy_zxing | 擎亚商用扫码库、支持多种码制识别,需license授权 | qy_zxing.h |
| components\jpeg | jpeg组件、支持 YUYV/ONLY Y 格式转 JPEG | yopen_jpeg.h |
| components\ftp | ftp组件、实现压缩后图片向服务器终端设备的上传 | ftpClient.h |
| yopen_fs.h | 文件系统接口、用于存储压缩后的图片 | |
| yopen_uart.h | 串口接口、用于传输数据在PC显示 | |
| yopen_power.h | pwrkey接口、通过pwrkey按键触发拍照功能 |
- 文档提供 3 个核心 Demo,覆盖 Camera 核心场景的实现逻辑:
- demo_camera.c:pwrkey 按键触发拍照 → JPEG 压缩 → FTP 上传压缩数据 + 本地文件系统同步保存。
- demo_zbar.c:拍照采集条码数据 → 传入 zbar 解码接口 → 解析并输出编码类型、编码内容。
- demo_qy_zxing.c:拍照采集条码数据 → 传入擎亚扫码库解码 → 获取解码结果并通过串口输出。
demo_camera.c 介绍¶
- 打开camera, 采集yuyv数据
cameraDrvFunc_t* drv = CamOpen("gc032a_2ddr", NULL, false);
- 拍照,获取一帧数据
char* pic = camTakePicture(drv);
- jpeg编码, 保存到文件系统中
使用 yopen_jpeg 硬件编码器进行JPEG编码,支持YUYV格式输入:
#define CONFGI_JPG_PATH "output.jpg"
jpeg_encode(pic, CONFGI_JPG_PATH);
static bool jpeg_encode(char *src, char *output_path)
{
void *hJpeg = NULL;
YOPEN_JPEG_ENC_PARAM enc_param;
YOPEN_JPEG_IMAGE_BUF img_buf;
unsigned int suggested_buf_len = 0;
unsigned int actual_buf_len = 0;
unsigned char *jpeg_buf = NULL;
QFILE fd;
int ret;
uint32_t start_tick, end_tick, encode_time_ms;
hJpeg = yopen_jpegE_create();
if (hJpeg == NULL)
{
DEMO_CAMERA_TRACE("yopen_jpegE_create fail");
return false;
}
memset(&enc_param, 0, sizeof(YOPEN_JPEG_ENC_PARAM));
enc_param.eFmt = YOPEN_JPEG_COLOR_FMT_YUYV;
enc_param.uWidth = IMG_WIDTH;
enc_param.uHeight = IMG_HEIGHT;
enc_param.uQuality = IMG_ENCODE_QUALITY;
ret = yopen_jpegE_set_param(hJpeg, &enc_param, &suggested_buf_len);
if (ret != 0)
{
DEMO_CAMERA_TRACE("yopen_jpegE_set_param fail ret(%d)", ret);
yopen_jpegE_destroy(hJpeg);
return false;
}
jpeg_buf = (unsigned char *)malloc(suggested_buf_len);
if (jpeg_buf == NULL)
{
DEMO_CAMERA_TRACE("malloc jpeg buf fail");
yopen_jpegE_destroy(hJpeg);
return false;
}
memset(&img_buf, 0, sizeof(YOPEN_JPEG_IMAGE_BUF));
img_buf.eFmt = YOPEN_JPEG_COLOR_FMT_YUYV;
img_buf.uWidth = IMG_WIDTH;
img_buf.uHeight = IMG_HEIGHT;
img_buf.pData[0] = (void *)src;
actual_buf_len = suggested_buf_len;
start_tick = yopen_rtos_get_system_tick();
ret = yopen_jpegE_encode(hJpeg, &img_buf, jpeg_buf, &actual_buf_len);
end_tick = yopen_rtos_get_system_tick();
encode_time_ms = end_tick - start_tick;
if (ret != 0)
{
DEMO_CAMERA_TRACE("yopen_jpegE_encode fail ret(%d)", ret);
free(jpeg_buf);
yopen_jpegE_destroy(hJpeg);
return false;
}
DEMO_CAMERA_TRACE("jpeg encode done: %d bytes in %d ms", actual_buf_len, encode_time_ms);
fd = yopen_fopen(output_path, "wb+");
if (fd < YOPEN_FS_OK)
{
DEMO_CAMERA_TRACE("open output file fail fd(%d)", fd);
free(jpeg_buf);
yopen_jpegE_destroy(hJpeg);
return false;
}
ret = yopen_fwrite(jpeg_buf, 1, actual_buf_len, fd);
yopen_fclose(fd);
free(jpeg_buf);
yopen_jpegE_destroy(hJpeg);
return true;
}
yopen_jpeg 编码接口说明:
| 接口函数 | 功能描述 | 参数说明 | 返回值 |
|---|---|---|---|
| yopen_jpegE_create | 创建JPEG编码器句柄 | 无 | 成功返回句柄指针,失败返回NULL |
| yopen_jpegE_set_param | 设置编码参数 | hJpeg:编码器句柄; pParam:编码参数结构; pSuggBufLen:建议缓冲区大小(输出) | 0成功,<0失败 |
| yopen_jpegE_encode | 执行JPEG编码 | hJpeg:编码器句柄; pIbuf:图像缓冲区; pBuf:输出缓冲区; pBufLen:缓冲区大小/实际输出长度 | 0成功,<0失败 |
| yopen_jpegE_destroy | 销毁JPEG编码器句柄 | hJpeg:编码器句柄 | 无 |
YOPEN_JPEG_ENC_PARAM 参数说明:
| 字段 | 类型 | 说明 |
|---|---|---|
| eFmt | YOPEN_JPEG_COLOR_FMT | 输入图像格式:YOPEN_JPEG_COLOR_FMT_YUYV/YOPEN_JPEG_COLOR_FMT_Y等 |
| uWidth | unsigned int | 图像宽度(像素) |
| uHeight | unsigned int | 图像高度(像素) |
| uQuality | unsigned int | 编码质量,1~100,值越大质量越高 |
- ftp 上传
ftp_upload(CONFGI_JPG_PATH);
void ftp_upload(char *filename)
{
static NetBuf_t* ftpClientNetBuf = NULL;
yopen_nw_reg_status_info_s nw_status;
FtpClient* ftpClient = getFtpClient();
yopen_nw_get_reg_status(0, &nw_status);
if(nw_status.data_reg.state != YOPEN_NW_REG_STATE_HOME_NETWORK &&
nw_status.data_reg.state != YOPEN_NW_REG_STATE_ROAMING)
{
yopen_trace("=== ftp upload network not ready ===");
return;
}
yopen_trace("=== ftp upload ftpClient %x ===", ftpClient);
int connect = ftpClient->ftpClientConnect(CONFIG_FTP_SERVER, CONFIG_FTP_PORT, &ftpClientNetBuf);
yopen_trace("=== ftp upload connect %d ===", connect);
int login = ftpClient->ftpClientLogin(CONFIG_FTP_USER, CONFIG_FTP_PASSWD, ftpClientNetBuf);
yopen_trace("=== ftp upload login %d ===", login);
int put = ftpClient->ftpClientPut(filename, filename, FTP_CLIENT_BINARY, ftpClientNetBuf);
yopen_trace("=== ftp upload put %d ===", put);
ftpClient->ftpClientQuit(ftpClientNetBuf);
yopen_trace("=== ftp upload quit %d ===");
}
demo_zbar.c介绍¶
- 打开camera, 采集ONLY Y数据
cameraDrvFunc_t* drv = CamOpen("gc032a_2ddr", NULL, true);
- 拍照, 获取一维码、二维码数据
char* pic = camTakePicture(drv);
- 解析,输出编码格式和内容
prvZbarScannerRun(CAMERA_WIDTH, CAMERA_HEIGHT, CAMERA_WIDTH*CAMERA_HEIGHT, (unsigned char *)pic);
static void prvZbarScannerRun(int width, int height, int size, unsigned char *dataInput)
{
int len;
char *data;
int handle = zbar_scanner_open(width, height, size, (unsigned char *)dataInput);
if (handle)
{
do
{
char *temp = malloc(4096);
data = zbar_get_data(handle, &len);
data[len] = 0;
yopen_trace("zbar_scan: %s,%d,%s", zbar_get_type(handle), len, data);
sprintf(temp, "type:%s,len=%d,data:\r\n%s\r\n", zbar_get_type(handle), len, data);
uart_write((char *)temp, strlen(temp));
free(temp);
}while(zbar_find_nextData(handle) > 0);
zbar_scanner_close((int *)handle);
}
else
{
yopen_trace("zbar_scan: no data");
}
}
zbar 解码接口说明:
| 接口函数 | 功能描述 | 参数说明 | 返回值 |
|---|---|---|---|
| zbar_create | 创建zbar解码器 | 无 | 成功返回zbar_t指针,失败返回NULL |
| zbar_run | 执行条码/二维码解码 | zbar:解码器指针; width:图像宽度; height:图像高度; data:图像数据(灰度) | >0成功,<=0失败 |
| zbar_get_data_ptr | 获取解码结果数据指针 | zbar:解码器指针; len:结果长度(输出) | 返回数据指针 |
| zbar_symbol_get_type | 获取条码类型 | symbol:条码符号指针 | 返回zbar_symbol_type_t类型 |
| zbar_get_symbol_name | 获取条码类型名称 | typ:条码类型 | 返回类型名称字符串 |
| zbar_find_next_data | 查找下一个条码数据 | zbar:解码器指针 | >0成功找到,<=0未找到 |
| zbar_destory | 销毁zbar解码器 | zbar:解码器指针 | 无 |
demo_qy_zxing.c介绍(商用扫码库)¶
- 简介
demo_qy_zxing.c 演示了使用擎亚(QY)商用扫码库进行条码/二维码识别。该库基于ZXing算法优化,支持多种码制,识别率高,性能优异。
支持码制: - 一维码:CODE-128 - 二维码:QR Code
授权方式:
该库为商用授权版本,使用前需要获取license授权。支持两种授权方式:
1. HTTP在线授权:通过 am_http_get_license() 接口在线获取license
2. NVM离线授权:通过 am_nvmWriteLicense() 接口写入预先获取的license
- 打开camera, 采集ONLY Y数据
cameraDrvFunc_t* drv = CamOpen("gc032a_2ddr", NULL, true);
- 获取license授权(HTTP方式)
if(!am_http_get_license("6dcb09a23ef54d929b2a85371c6df1bb"))
{
DEMO_QYZXING_TRACE("am_http_get_license failed");
goto exit;
}
- 初始化解码器
if(!Initial_Decoder())
{
DEMO_QYZXING_TRACE("Initial_Decoder failed");
goto exit;
}
- 拍照并解码
char* pic = camTakePicture(drv);
if (pic)
{
ret = Decoding_Image((unsigned char*)pic, CAMERA_WIDTH, CAMERA_HEIGHT);
unsigned int result_len = GetResultLength();
if(ret && result_len > 0)
{
char *result = malloc(result_len+1);
memset(result, 0, result_len+1);
GetDecoderResult((unsigned char*)result);
uart_write(result, result_len);
free(result);
}
}
qy_zxing 解码接口说明:
| 接口函数 | 功能描述 | 参数说明 | 返回值 |
|---|---|---|---|
| am_http_get_license | 通过HTTP获取license授权(在线方式) | license_key:授权密钥 | true成功,false失败 |
| am_nvmWriteLicense | 写入license授权(离线方式) | data:license数据 | 0成功,<0失败 |
| Initial_Decoder | 初始化解码器,只需调用一次 | 无 | 1成功,0失败 |
| Decoding_Image | 解码图像数据 | img_buffer:灰度图像数据指针; width:图像宽度; height:图像高度 | 1成功,0失败 |
| GetResultLength | 获取解码结果长度 | 无 | 成功返回结果长度,0表示失败或无数据 |
| GetDecoderResult | 获取解码结果内容 | result:存储结果的缓冲区(需外部申请足够空间) | 1成功,0失败 |
注意事项:
-
图像格式:输入图像必须为灰度图像(ONLY Y格式),通过
CamOpen的第三个参数true指定:cameraDrvFunc_t* drv = CamOpen("bf30a2_1sdr", NULL, true); -
网络要求:使用HTTP授权方式时,需要确保网络已注册成功(HOME_NETWORK或ROAMING状态)。
-
内存管理:解码结果缓冲区需要调用者自行分配和释放。
工程编译¶
# 命令行下执行 demo描述:按键触发拍照 → JPEG 压缩 → FTP 上传压缩数据 + 本地文件系统同步保存。
build.bat YM310_X09S.U62 camera
# 命令行下执行 demo描述:拍照采集条码数据 → 传入 zbar 解码接口 → 解析并输出编码类型、编码内容。
build.bat YM310_X09S.U62 zbar
# 命令行下执行 demo描述:拍照采集条码数据 → 传入擎亚扫码库解码 → 获取解码结果并通过串口输出。
build.bat YM310_X09S.U62 qy_zxing