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

465 lines
13 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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 中定位相关文件:
```bash
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) 示例:
```bash
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. 关键设计决策
#### 初始化顺序
经过多次调试,确定正确的初始化顺序:
```cpp
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;
}
```
#### 缓冲区配置优化
经过测试,找到最优配置:
```cpp
#define VI_BUFFER_NUM 5 // VI缓冲区数量增加到5以提供更多缓存
#define VBV_BUFFER_SIZE (4*1024) // VBV缓冲区4MB之前1350KB太小
#define DEFAULT_QUALITY 80 // JPEG质量降低以减少VBV压力
```
### 3. 关键实现细节
#### 帧捕获与重试机制
```cpp
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 交叉编译器:
```makefile
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 相关头文件:
```makefile
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. 链接库配置(最复杂)
经过多次迭代,找到完整的库依赖:
```makefile
# 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导致数据量大
**解决方案**
```cpp
// 优化前
#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. 测试程序设计
```cpp
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. 编译与部署
```bash
# 编译
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 摄像头驱动开发成功!** 🎉