Files
Docs/DevLogs/Day5.md
2025-12-31 16:18:28 +08:00

13 KiB
Raw Blame History

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_virvi2vencVI直接到编码示例

通过研究示例代码,理解了:

  • 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创建

问题 2VBV 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满的情况。

问题 4VI超时

错误日志

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摄像头驱动开发流程

  1. SDK研究定位驱动和示例代码理解MPP框架
  2. 类设计封装VI/ISP/VENC提供简洁的API
  3. Makefile配置:正确配置头文件路径和链接库
  4. 初始化调试:确保正确的初始化顺序
  5. 缓冲区优化:根据实际情况调整缓冲区大小
  6. 测试验证:通过测试程序验证功能
  7. 性能优化:分析日志,调整参数达到最佳性能

关键配置文件清单

  • 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 摄像头驱动开发成功! 🎉