13 KiB
13 KiB
Avaota F1 GC2083 摄像头驱动开发完整指南
版本:v1.0
日期:2025-11-28
主机环境:Ubuntu 24.04 LTS
目标平台:Avaota F1 (全志 V821 / 32-bit RISC-V)
摄像头:GC2083 (MIPI-CSI2, 1920x1080 @20fps)
第一部分:环境准备与SDK研究
1. 查找 GC2083 驱动
在 Tina Linux SDK 中定位相关文件:
cd ~/ProgramFiles/avaota_sdk/tina-v821-release
find . -name "*gc2083*"
关键文件位置:
- 驱动代码:
lichee/linux-5.4/drivers/media/platform/sunxi-vin/modules/sensor/gc2083_mipi.c - 设备树配置:
lichee/linux-5.4/arch/riscv/boot/dts/sunxi/board-v821.dtsi - 示例程序:
openwrt/package/allwinner/tina_multimedia/libcedarx/demo/sample_smartIPC_demo/
2. 研究 Allwinner MPP 框架
查找 MPP (Media Processing Platform) 示例:
find . -name "*sample*" | grep -E "(camera|vi|isp|venc)"
核心组件:
- VI (Video Input):视频输入,负责从摄像头获取原始数据
- ISP (Image Signal Processor):图像信号处理器
- VENC (Video Encoder):视频编码器,支持 H.264/JPEG
3. 分析官方示例
关键示例程序:
sample_smartIPC_demo:智能IPC示例(包含完整的 VI→ISP→VENC 流程)sample_virvi2venc:VI直接到编码示例
通过研究示例代码,理解了:
- MPP框架的初始化顺序:
SYS → VI → ISP → VENC - VI/ISP/VENC 的绑定关系
- 配置文件格式(
.conf)
第二部分:C++ 摄像头类设计与实现
1. 项目结构
src/
├── camera/
│ ├── camera.h # 摄像头类头文件
│ └── camera.cpp # 摄像头类实现
├── test_camera.cpp # 测试程序
├── Makefile # 构建配置
└── sample_smartIPC_demo.conf # MPP配置文件
2. 关键设计决策
初始化顺序
经过多次调试,确定正确的初始化顺序:
bool Camera::init() {
// 1. 初始化 MPP 系统
AW_MPI_SYS_Init();
// 2. 创建并配置 VI 设备
AW_MPI_VI_CreateVipp(m_vi_dev);
AW_MPI_VI_SetVippAttr(m_vi_dev, &vipp_attr);
// 3. 启动 ISP
AW_MPI_ISP_Run(m_isp_dev);
// 4. 创建 VI 虚拟通道
AW_MPI_VI_CreateVirChn(m_vi_dev, m_vi_chn, &vi_chn_attr);
AW_MPI_VI_EnableVirChn(m_vi_dev, m_vi_chn);
// 5. 创建并配置 VENC
AW_MPI_VENC_CreateChn(m_venc_chn, &venc_attr);
// 6. 绑定 VI-VENC
AW_MPI_SYS_Bind(&vi_chn, &venc_chn);
return true;
}
缓冲区配置优化
经过测试,找到最优配置:
#define VI_BUFFER_NUM 5 // VI缓冲区数量(增加到5以提供更多缓存)
#define VBV_BUFFER_SIZE (4*1024) // VBV缓冲区4MB(之前1350KB太小)
#define DEFAULT_QUALITY 80 // JPEG质量(降低以减少VBV压力)
3. 关键实现细节
帧捕获与重试机制
bool Camera::capture_frame(uint8_t** jpeg_data, size_t* jpeg_size) {
VENC_STREAM_S stream;
VENC_PACK_S pack;
// 增加超时时间到10秒,并添加重试机制
const int max_retries = 3;
const int timeout_ms = 10000;
ERRORTYPE ret = ERR_VENC_BUF_EMPTY;
for (int retry = 0; retry < max_retries && ret != SUCCESS; retry++) {
if (retry > 0) {
LOGW("Retry %d/%d getting stream...", retry, max_retries);
usleep(100000); // 100ms延迟再重试
}
ret = AW_MPI_VENC_GetStream(m_venc_chn, &stream, timeout_ms);
}
if (ret != SUCCESS) {
LOGE("AW_MPI_VENC_GetStream failed after %d retries: 0x%x", max_retries, ret);
return false;
}
// ... 拷贝JPEG数据
}
第三部分:Makefile 配置(关键)
1. 工具链配置
使用 32 位 RISC-V 交叉编译器:
CROSS_COMPILE := /path/to/riscv32-unknown-linux-
CXX := $(CROSS_COMPILE)g++
CC := $(CROSS_COMPILE)gcc
AR := $(CROSS_COMPILE)ar
STRIP := $(CROSS_COMPILE)strip
2. 头文件路径
必须包含所有 MPP 相关头文件:
CXXFLAGS := -Wall -O2 -std=c++11
CXXFLAGS += -I$(SDK_PATH)/openwrt/staging_dir/target/usr/include
CXXFLAGS += -I$(SDK_PATH)/openwrt/staging_dir/target/usr/include/allwinner
CXXFLAGS += -I$(SDK_PATH)/openwrt/staging_dir/target/usr/include/allwinner/include
CXXFLAGS += -I$(SDK_PATH)/lichee/linux-5.4/drivers/media/platform/sunxi-vin/vin-isp/isp_server/include
CXXFLAGS += -DAWCHIP=AW_V821
3. 链接库配置(最复杂)
经过多次迭代,找到完整的库依赖:
# MPP核心库
LDFLAGS += $(SDK_PATH)/.../libaw_mpp.a
LDFLAGS += $(SDK_PATH)/.../libmedia_utils.a
# ISP相关
LDFLAGS += $(SDK_PATH)/.../libisp.a
LDFLAGS += $(SDK_PATH)/.../libisp_ini.a
LDFLAGS += $(SDK_PATH)/.../libisp_ae.a
LDFLAGS += $(SDK_PATH)/.../libisp_af.a
LDFLAGS += $(SDK_PATH)/.../libisp_afs.a
LDFLAGS += $(SDK_PATH)/.../libisp_awb.a
LDFLAGS += $(SDK_PATH)/.../libisp_md.a
LDFLAGS += $(SDK_PATH)/.../libisp_iso.a
LDFLAGS += $(SDK_PATH)/.../libisp_gtm.a
LDFLAGS += $(SDK_PATH)/.../libisp_pltm.a
LDFLAGS += $(SDK_PATH)/.../libisp_rolloff.a
# VENC相关
LDFLAGS += $(SDK_PATH)/.../libvencoder.a
LDFLAGS += $(SDK_PATH)/.../libvenc_codec.a
LDFLAGS += $(SDK_PATH)/.../libvenc_base.a
LDFLAGS += $(SDK_PATH)/.../libVE.a
LDFLAGS += $(SDK_PATH)/.../libMemAdapter.a
LDFLAGS += $(SDK_PATH)/.../libvenc_h264.a
# CedarX多媒体框架
LDFLAGS += $(SDK_PATH)/.../libcdc_base.a
LDFLAGS += $(SDK_PATH)/.../libcdx_base.a
LDFLAGS += $(SDK_PATH)/.../libcdx_common.a
LDFLAGS += $(SDK_PATH)/.../libcdx_stream.a
LDFLAGS += $(SDK_PATH)/.../libcdx_parser.a
LDFLAGS += $(SDK_PATH)/.../libcdx_muxer.a
# 音频相关(MPP依赖)
LDFLAGS += $(SDK_PATH)/.../libadecoder.a
LDFLAGS += $(SDK_PATH)/.../libcedarx_aencoder.a
LDFLAGS += $(SDK_PATH)/.../libaacenc.a
LDFLAGS += $(SDK_PATH)/.../libAgc.a
LDFLAGS += $(SDK_PATH)/.../libAec.a
LDFLAGS += $(SDK_PATH)/.../libAns.a
# 系统动态库
LDFLAGS += -lpthread -lrt -ldl -lstdc++ -lm
LDFLAGS += -lglog -lexpat -lasound -llog
关键经验:
- 库的链接顺序很重要(依赖库要放在后面)
- 必须包含所有 ISP 子模块的静态库
- 音频库虽然不直接使用,但 MPP 框架依赖它们
第四部分:调试过程与问题解决
问题 1:初始化失败 - ISP 无法初始化传感器
错误日志:
E0101 00:05:31.337224 isp_dev.c:476] [ISP_ERR]unable to initialize sensor subdev.
E0101 00:05:31.339653 isp_dev.c:487] [ISP_ERR]unable to open isp device[0]
原因:初始化顺序错误,先调用了 AW_MPI_ISP_Run() 再创建 VI 设备。
解决:调整顺序为 VI创建 → ISP启动 → VENC创建。
问题 2:VBV FULL - 视频缓冲区溢出
错误日志:
WARNING: jpegenc <JpegEncMainFrame:1288>: BitStreamFreeBufferSize 831936 is too small, total[1350]KB
W0101 00:08:07.503864 VideoEnc_Component.c:9615] Be careful! vencChn[0] encode [7]frames, fail BsFull
原因:
- VBV缓冲区太小(1350KB)
- VI缓冲区不够(3个)
- JPEG质量过高(90)导致数据量大
解决方案:
// 优化前
#define VI_BUFFER_NUM 3
#define DEFAULT_QUALITY 90
vbv_buf_size = pic_size / 10 + vbv_thresh_size; // ~1350KB
// 优化后
#define VI_BUFFER_NUM 5 // +67%
#define VBV_BUFFER_SIZE (4*1024) // 4MB (+203%)
#define DEFAULT_QUALITY 80 // 降低质量减少数据量
问题 3:帧大小不匹配
错误日志:
W0101 00:08:08.700727 VideoEnc_Component.c:9134] fatal error! enc src_size[1280x720]!= frameSize[0x0]
原因:VBV满后,编码器无法正常处理新帧,导致内部状态异常。
解决:通过增大缓冲区和降低质量,避免VBV满的情况。
问题 4:VI超时
错误日志:
W0101 00:08:09.505139 videoInputHw.c:5672] Be careful! vipp fds select timeout[2000]ms, setNum:0!
分析:这是正常的,因为测试程序每秒只捕获一帧,VI在等待过程中超时。
优化:增加帧捕获间隔到1秒,给缓冲区更多恢复时间。
第五部分:测试程序与验证
1. 测试程序设计
int main() {
Camera camera;
// 1. 初始化摄像头
if (!camera.init()) {
printf("ERROR: Camera initialization failed!\n");
return -1;
}
// 2. 设置JPEG质量
camera.set_quality(80);
// 3. 等待ISP稳定
sleep(2);
// 4. 连续捕获10张照片
for (int i = 0; i < 10; i++) {
uint8_t* jpeg_data = nullptr;
size_t jpeg_size = 0;
if (!camera.capture_frame(&jpeg_data, &jpeg_size)) {
printf("ERROR: Failed to capture frame %d\n", i+1);
continue;
}
// 保存到SD卡
char filename[256];
snprintf(filename, sizeof(filename), "/mnt/extsd/pic_%03d.jpg", i);
FILE* fp = fopen(filename, "wb");
fwrite(jpeg_data, 1, jpeg_size, fp);
fclose(fp);
camera.release_frame(jpeg_data);
// 1秒间隔
usleep(1000000);
}
return 0;
}
2. 编译与部署
# 编译
cd ~/ProgramFiles/AvaotaF1/avaota_app_demo/src
make clean
make test_camera
# 复制到SD卡
cp test_camera /media/user/SD_CARD/
# 在板子上运行
mount /dev/mmcblk0p1 /mnt/extsd
cp /mnt/extsd/test_camera /tmp/
chmod +x /tmp/test_camera
/tmp/test_camera
3. 测试结果
优化前(失败案例):
Total captured: 6/10
Success rate: 60.0%
Final FPS: 2.0
优化后(成功!):
Total captured: 10/10
Success rate: 100.0%
Final FPS: 1.0
Pictures saved to: /mnt/extsd/
优化效果对比:
| 指标 | 优化前 | 优化后 | 改进 |
|---|---|---|---|
| 成功率 | 60% | 100% | +40% |
| VI缓冲区 | 3 | 5 | +67% |
| VBV缓冲区 | 1350KB | 4096KB | +203% |
| JPEG质量 | 90 | 80 | 优化 |
| 文件大小 | 58-145KB | 34-88KB | -40% |
| 严重错误 | 大量 | 无 | ✓ |
第六部分:经验总结与最佳实践
1. MPP框架使用要点
✅ 必须遵守的初始化顺序:
SYS_Init → VI_Create → VI_SetAttr → ISP_Run → VI_CreateVirChn → VENC_Create → SYS_Bind
✅ 缓冲区配置原则:
- VI缓冲区:至少5个,提供足够的帧缓存
- VBV缓冲区:根据分辨率和质量设置,建议4MB起步
- 质量设置:JPEG质量80-85为最佳平衡点
✅ 错误处理:
- 所有MPP API调用都要检查返回值
- 重要的初始化步骤要添加详细日志
- 捕获帧时添加重试机制(3次,100ms间隔)
2. 编译链接要点
❌ 常见错误:
- 缺少某个 ISP 子模块静态库
- 库的链接顺序不对
- 忘记定义
AWCHIP宏
✅ 解决方案:
- 参考 SDK 中
tina.mk的配置 - 使用
nm工具检查未定义符号 - 按依赖关系排列库的顺序
3. 调试技巧
📊 日志分析:
- 关注
[ISP_ERR]和fatal error - VBV FULL 警告是性能瓶颈的信号
- VI timeout 可能是正常现象(低帧率捕获时)
🔍 性能分析:
- 通过日志中的
vfmt.bufs查看实际VI缓冲区数 - 通过
BitStreamFreeBufferSize监控VBV使用情况 - FPS统计帮助判断系统负载
🏆 最终验证通过的开发流程
现在你已经拥有了一套经过实战验证的GC2083摄像头驱动开发流程:
- SDK研究:定位驱动和示例代码,理解MPP框架
- 类设计:封装VI/ISP/VENC,提供简洁的API
- Makefile配置:正确配置头文件路径和链接库
- 初始化调试:确保正确的初始化顺序
- 缓冲区优化:根据实际情况调整缓冲区大小
- 测试验证:通过测试程序验证功能
- 性能优化:分析日志,调整参数达到最佳性能
关键配置文件清单
- ✅
camera/camera.h- 摄像头类头文件 - ✅
camera/camera.cpp- 摄像头类实现 - ✅
test_camera.cpp- 测试程序 - ✅
Makefile- 完整的编译配置 - ✅
sample_smartIPC_demo.conf- MPP配置文件
最终测试结果
==========================================
Test Complete!
==========================================
Total captured: 10/10
Success rate: 100.0%
Final FPS: 1.0
Pictures saved to: /mnt/extsd/
==========================================
🎉 GC2083 摄像头驱动开发成功! 🎉