#110_SCH3000 平台图传系统配置指南:从 DVP 摄像头到 UVC/WiFi 双通道传输

一、系统架构总览

1. 硬件平台

本系统基于 Beken BK7258 双核 Wi-Fi SoC,具备以下图传相关硬件能力:

硬件模块 功能 备注
DVP 接口 并行摄像头输入 支持 8 位数据总线
JPEG 硬件编码器 将 YUV 编码为 MJPEG bk_jpeg_enc 驱动
H.264 硬件编码器 将 YUV 编码为 H.264 bk_h264 驱动
USB OTG USB 视频类(UVC)主机 支持 H.264/JPEG/YUV 流
PSRAM 帧缓冲存储 独立编码/YUV 堆
DMA 编码器 FIFO → PSRAM 搬运 8 通道固定分配
LCD RGB/I80 本地显示 最大 864×480

2. 软件分层

整个系统从底到顶分为五层:

复制代码
┌─────────────────────────────────────────────────────┐
│ 应用层: doorbell_asr / uvc_example / dvp_example    │
├─────────────────────────────────────────────────────┤
│ 服务层: media_service / wifi_transfer               │
├─────────────────────────────────────────────────────┤
│ 管线层: uvc_pipeline / h264_encode_pipeline /       │
│         jpeg_decode_pipeline                        │
├─────────────────────────────────────────────────────┤
│ 抽象层: bk_camera_ctlr (统一摄像头控制器接口)        │
├─────────────────────────────────────────────────────┤
│ 驱动层: bk_dvp / bk_uvc / frame_buffer              │
└─────────────────────────────────────────────────────┘

3. 数据流路径

系统支持两条独立的图传链路:

链路 A:DVP 本地采集链路(发送端)

复制代码
DVP 传感器 → YUV BUF → JPEG/H.264 硬件编码器 → DMA → PSRAM 帧缓冲
    → 帧队列 → WiFi 传输 / LCD 显示

链路 B:UVC USB 接收链路(接收端)

复制代码
USB 主机口 ← UVC 摄像头 → URB 包接收 → 包解析 → 帧缓冲
    → JPEG 解码管线 → YUV → H.264 编码管线 / LCD 旋转显示

二、环境准备

1. 工程目录结构

bash 复制代码
cd ~/Project_Chen/SCH3000_4302644_4.3_Inch

# 核心源码目录
ap/include/components/    # 公开头文件
ap/components/bk_camera/  # 摄像头控制器抽象层
ap/components/bk_dvp/     # DVP 底层驱动
ap/components/bk_uvc/     # UVC 底层协议栈
ap/components/multimedia/ # 多媒体管线
ap/components/wifi_transfer/ # WiFi 帧传输

# 示例工程目录
projects/dvp_example/     # DVP 摄像头示例
projects/uvc_example/     # UVC 单流示例
projects/uvc_dualstream_example/ # UVC 双流示例
projects/video_pipeline_example/ # 视频管线示例
projects/doorbell_asr/    # 可视门铃综合应用

2. 关键依赖关系

复制代码
doorbell_asr
    ├── media_service (服务框架)
    ├── wifi_transfer (WiFi 传输)
    ├── multimedia/pipeline (视频管线)
    │       ├── uvc_pipeline_act
    │       ├── h264_encode_pipeline
    │       └── jpeg_decode_pipeline
    ├── bk_camera (摄像头控制器)
    │       ├── bk_camera_dvp_ctlr
    │       └── bk_camera_uvc_ctlr
    ├── bk_dvp (DVP 驱动)
    ├── bk_uvc (UVC 协议栈)
    └── frame_buffer (帧缓冲管理)

三、帧缓冲管理

1. 内存分配原理

帧缓冲是所有图像数据的载体。系统在 PSRAM 中划分了两个独立堆:

堆类型 用途 分配函数
PSRAM_HEAP_YUV YUV 原始帧 frame_buffer_display_malloc()
PSRAM_HEAP_ENCODE 编码后帧(JPEG/H.264) frame_buffer_encode_malloc()

核心实现位于 ap/components/multimedia/src/frame_buffer.c

c 复制代码
frame_buffer_t *frame_buffer_encode_malloc(uint32_t size)
{
    // 分配 frame_buffer_t 头 + 数据区 + 32 字节对齐余量
    frame_buffer_t *frame = bk_psram_frame_buffer_malloc(
        PSRAM_HEAP_ENCODE, 
        size + sizeof(frame_buffer_t) + (1 << ALIGN_BITS)
    );

    if (frame == NULL) return NULL;

    os_memset(frame, 0, sizeof(frame_buffer_t));
    // 数据指针 32 字节对齐
    frame->frame = (uint8_t *)((((uint32_t)(frame + 1) >> ALIGN_BITS) + 1) << ALIGN_BITS);
    frame->size = size;
    frame->flag = FB_ALLOCATED_PATTERN;       // 0x8338 已分配标记
    frame->frame_crc = hnd_crc8((uint8_t *)frame, 6, 0xFF); // CRC 防重复释放

    return frame;
}

原理说明

  • frame_buffer_t 结构体头部紧挨数据区,一次性从 PSRAM 分配
  • flag 字段标记分配状态,frame_crc 校验头部完整性,防止 double-free
  • 数据指针强制 32 字节对齐,满足 DMA 要求

2. 帧队列管理

DVP 示例中的 dvp_frame_list.c 实现了生产者-消费者模型:

c 复制代码
typedef struct {
    uint8_t count;
    image_format_t format;
    beken_queue_t free_que;   // 空闲帧队列
    beken_queue_t ready_que;  // 就绪帧队列
} frame_queue_t;

工作流程

复制代码
① 摄像头中断 → dvp_frame_malloc() → 从 free_que 取消息 → 分配 frame_buffer
② 编码完成 → dvp_frame_complete() → 将 frame 推入 ready_que
③ 消费任务 → dvp_frame_queue_get_frame() → 从 ready_que 取 frame
④ 消费完成 → dvp_frame_queue_free() → 释放 frame → 消息回 free_que

每种格式预分配帧数

格式 帧数
MJPEG 4
H.264 6
YUV 3

四、摄像头控制器抽象层

1. 统一接口设计

bk_camera_ctlr.h 定义了所有摄像头的统一操作接口,采用函数指针表实现多态:

c 复制代码
typedef struct bk_camera_ctlr *bk_camera_ctlr_handle_t;

struct bk_camera_ctlr {
    avdk_err_t (*open)(bk_camera_ctlr_t *controller);
    avdk_err_t (*close)(bk_camera_ctlr_t *controller);
    avdk_err_t (*suspend)(bk_camera_ctlr_t *controller);
    avdk_err_t (*resume)(bk_camera_ctlr_t *controller);
    avdk_err_t (*del)(bk_camera_ctlr_t *controller);
    avdk_err_t (*ioctlr)(bk_camera_ctlr_t *controller, uint32_t cmd, void *arg);
};

上层应用只依赖这六个接口,不关心底层是 DVP 还是 UVC。

2. DVP 控制器实现

bk_camera_dvp_ctlr_new() 在创建时绑定 DVP 专用的操作函数:

c 复制代码
avdk_err_t bk_camera_dvp_ctlr_new(bk_camera_ctlr_handle_t *handle, 
                                   bk_dvp_ctlr_config_t *config)
{
    private_camera_dvp_ctlr_t *controller = os_malloc(sizeof(...));
    
    // 根据输出格式预分配编码缓冲区
    if (config->config.img_format & IMAGE_H264)
        controller->encode_buffer = os_malloc(config->config.width * 32 * 2);
    else if (config->config.img_format & IMAGE_MJPEG)
        controller->encode_buffer = os_malloc(config->config.width * 16 * 2);

    // 绑定操作函数
    controller->ops.open = dvp_camera_ctlr_open;
    controller->ops.close = dvp_camera_ctlr_close;
    controller->ops.del = dvp_camera_ctlr_delete;
    controller->ops.suspend = dvp_camera_ctlr_suspend;
    controller->ops.resume = dvp_camera_ctlr_resume;
    controller->ops.ioctlr = dvp_camera_ctlr_ioctlr;

    *handle = &(controller->ops);  // 返回接口指针
    controller->state = CAMERA_STATE_INIT;
    return AVDK_ERR_OK;
}

3. UVC 控制器实现

bk_camera_uvc_ctlr_new() 同样绑定 UVC 操作函数,open 内部调用 bk_uvc_power_on() + bk_uvc_open()

c 复制代码
static avdk_err_t uvc_camera_ctlr_open(bk_camera_ctlr_t *controller)
{
    private_camera_uvc_ctlr_t *uvc_controller = 
        __containerof(controller, private_camera_uvc_ctlr_t, ops);

    // 先上电等待设备连接
    avdk_err_t ret = bk_uvc_power_on(uvc_controller->config.config.img_format, 40000);
    if (ret == AVDK_ERR_OK) {
        // 再打开流
        ret = bk_uvc_open(&uvc_controller->handle, 
                          &uvc_controller->config.config, 
                          uvc_controller->config.cbs);
        if (ret == AVDK_ERR_OK)
            uvc_controller->state = CAMERA_STATE_OPENED;
    }
    return ret;
}

五、DVP 摄像头配置

1. 配置结构体

bk_dvp_config_t 包含摄像头完整的硬件配置:

c 复制代码
typedef struct {
    bk_dvp_i2c_config_t i2c_config;  // I2C 配置(传感器寄存器读写)
    uint8_t reset_pin;               // 复位引脚,0xFF 表示无效
    uint8_t pwdn_pin;                // 电源控制引脚,0xFF 表示无效
    bk_dvp_io_config_t io_config;    // 数据/同步 IO 引脚
    mclk_freq_t clk_source;          // 主时钟频率
    uint16_t img_format;             // 输出格式
    uint16_t width;                  // 输出宽度
    uint16_t height;                 // 输出高度
    uint32_t fps;                    // 帧率
    void *user_data;                 // 用户数据
} bk_dvp_config_t;

2. 预定义配置

dvp_camera_types.h 提供了默认配置宏:

c 复制代码
#define BK_DVP_864X480_30FPS_MJPEG_CONFIG()    \
{    \
    .i2c_config = {    \
        .id = 2,           /* I2C 总线 2 */    \
        .scl_pin = 0,      /* SCL: GPIO0 */    \
        .sda_pin = 1,      /* SDA: GPIO1 */    \
        .baud_rate = 100000, /* 100KHz */      \
        .flags = 0,    \
    },    \
    .reset_pin = 0xFF,     /* 无复位引脚 */    \
    .pwdn_pin = 0xFF,      /* 无电源引脚 */    \
    .io_config = {    \
        .data_width = SENSOR_BITS_WIDTH_8BIT,  /* 8 位数据总线 */  \
        .data_pin = {32, 33, 34, 35, 36, 37, 38, 39},  /* D0-D7 */ \
        .vsync_pin = 31,   /* VSYNC: GPIO31 */    \
        .hsync_pin = 30,   /* HSYNC: GPIO30 */    \
        .xclk_pin = 27,    /* XCLK: GPIO27 */     \
        .pclk_pin = 29,    /* PCLK: GPIO29 */     \
    },    \
    .clk_source = MCLK_24M, /* 24MHz 主时钟 */   \
    .width = 864,    \
    .height = 480,   \
    .fps = 30,       \
    .img_format = IMAGE_MJPEG, \
    .user_data = NULL, \
}

3. 回调函数

摄像头每采集完成一帧,通过回调通知上层:

c 复制代码
typedef struct {
    frame_buffer_t *(*malloc)(image_format_t format, uint32_t size);
    void (*complete)(image_format_t format, frame_buffer_t *frame, int result);
} bk_dvp_callback_t;

注意:回调函数运行在中断上下文中,不允许阻塞操作。源码中通过独立工作线程将中断回调转发到任务上下文:

c 复制代码
static void dvp_work_thread(void *param)
{
    // 从消息队列接收 EOF 事件
    // 调用 handle->callback->complete() 在任务上下文通知上层
}

4. 三种工作模式

bk_dvp.c 中根据 img_format 初始化不同硬件:

模式 硬件初始化 中断注册 说明
IMAGE_YUV dvp_camera_yuv_mode() YUV_BUF_YUV_ARV 直接输出 YUV422
IMAGE_MJPEG dvp_camera_jpeg_mode() JPEG_EOF JPEG 硬件编码
IMAGE_H264 dvp_camera_h264_mode() H264_FINAL_OUT H.264 硬件编码

组合模式:IMAGE_MJPEG | IMAGE_YUV 同时输出 JPEG 和 YUV。


六、UVC 摄像头配置

1. 配置结构体

c 复制代码
typedef struct {
    uvc_stream_type_t type;    // 单流/双流
    uint8_t port;              // 端口号(1 开始)
    uint8_t drop_num;          // 丢弃帧数
    image_format_t img_format; // 输出格式
    uint16_t width;
    uint16_t height;
    uint32_t fps;
    void *user_data;
} bk_cam_uvc_config_t;

2. 预定义配置

c 复制代码
// MJPEG 单流
#define BK_UVC_864X480_30FPS_MJPEG_CONFIG()  \
{                                            \
    .type = UVC_SINGLE_STREAM,               \
    .port = 1,                               \
    .drop_num = 0,                           \
    .img_format = IMAGE_MJPEG,               \
    .width = 864, .height = 480, .fps = 30,  \
    .user_data = NULL,                       \
}

// H.264 单流
#define BK_UVC_1920X1080_30FPS_H26X_CONFIG() \
{                                            \
    .type = UVC_SINGLE_STREAM,               \
    .port = 1, .drop_num = 0,                \
    .img_format = IMAGE_H264,                \
    .width = 1920, .height = 1080, .fps = 15,\
    .user_data = NULL,                       \
}

3. UVC 协议栈核心

bk_uvc.c 实现了完整的 UVC 视频类主机栈:

(1) 设备枚举 :通过 bk_usbh_hub_port_register_connect_callback() 注册连接回调,设备插入时自动触发。

(2) 格式协商uvc_camera_stream_check_config() 遍历设备支持的格式/分辨率/帧率,与用户配置匹配。

(3) URB 管理 :使用 uvc_camera_urb_malloc() / uvc_camera_urb_push() 管理 USB 请求块。

(4) 包解析uvc_camera_stream_packet_process() 处理 UVC 载荷头(12 字节头),提取 FID/EOF 标志,重组帧数据。

(5) EOF 处理uvc_camera_stream_eof_handle() 校验帧完整性(JPEG SOF/EOF 标记、H.264 起始码),通过 complete 回调通知上层。

4. 回调函数

c 复制代码
typedef struct {
    frame_buffer_t *(*malloc)(image_format_t format, uint32_t size);
    void (*complete)(uint8_t port, image_format_t format, 
                     frame_buffer_t *frame, int result);
    void (*uvc_event_callback)(bk_usb_hub_port_info *port_info, 
                               void *arg, uvc_error_code_t code);
} bk_uvc_callback_t;

七、视频管线配置

1. 管线初始化

uvc_pipeline_act.c 中的 uvc_pipeline_init() 按顺序初始化各子管线:

c 复制代码
bk_err_t uvc_pipeline_init(void)
{
    bk_jdec_pipeline_init();      // ① JPEG 解码管线
#if SUPPORTED_IMAGE_MAX_720P
    bk_scale_pipeline_init();     // ② 缩放管线(可选)
#endif
    bk_rotate_pipeline_init();    // ③ 旋转管线
#ifdef CONFIG_H264
    bk_h264_pipeline_init();      // ④ H.264 编码管线
#endif
    pipeline_init = true;
}

2. H.264 编码管线

h264_encode_pipeline.c 实现了从 JPEG 解码输出到 H.264 编码的完整链路:

(1) 任务模型 :独立任务 h264_encode_main(),通过消息队列处理编码请求。

(2) 消息类型

消息 功能
H264_ENCODE_START 开始编码一帧
H264_ENCODE_LINE_DONE 一行编码完成
H264_ENCODE_LINE_CONTINUE 继续编码下一行
H264_ENCODE_FINISH 一帧编码完成
H264_ENCODE_REGENERATE_IDR 强制生成 IDR 帧
H264_ENCODE_RESET 硬件复位
H264_ENCODE_STOP 停止编码

(3) 双缓冲机制

编码器在 YUV_BUF 中采用 ping-pang 缓冲,每 16 行触发一次行完成中断,将解码后的 YUV 数据送入编码器。

(4) DMA 搬运

c 复制代码
static bk_err_t h264_encode_dma_config(void)
{
    // 从 H.264 编码器 FIFO 到 PSRAM 帧缓冲
    dma_config.src.dev = DMA_DEV_H264;
    dma_config.src.start_addr = encode_fifo_addr;
    dma_config.dst.start_addr = (uint32_t)h264_encode_config->h264_frame->frame;
    dma_config.dst.end_addr = h264_encode_config->h264_frame->frame + size;
    // 重复模式,10KB 传输长度
    dma_config.mode = DMA_WORK_MODE_REPEAT;
    bk_dma_set_transfer_len(channel, H264_DMA_LEN); // 10240 字节
}

(5) 帧完成回调

c 复制代码
static void h264_encode_finish_handle(void)
{
    // 校验 DMA 长度与实际编码长度一致
    // 校验 H.264 起始码 00 00 00 01
    // 设置 I/P 帧类型标志
    // 调用 cb->complete() 通知上层
}

3. JPEG 解码管线

jpeg_decode_pipeline.c 实现 JPEG → YUV 的解码,作为 H.264 编码的数据源:

(1) 解码模式自动切换

条件 模式
JPEG 内部为 YUV422 硬件解码(按行)
JPEG 内部为 YUV420 软件解码(按帧)
分辨率 ≥ 1280×720 软件解码不支持,报错

(2) 多路复用缓冲

解码输出可以同时供 H.264 编码、旋转、缩放三个模块使用,每个模块通过 MUX_BUFFER_PRESET/SHAREED/IDLE 状态管理。

(3) 模块注册机制

c 复制代码
void bk_jdec_buffer_request_register(pipeline_module_t module, 
                                      mux_request_callback_t cb, 
                                      mux_reset_callback_t reset_cb)
{
    jdec_config->module[module].enable = true;
    jdec_config->cb[module] = cb;
    jdec_config->reset_cb[module] = reset_cb;
}

八、WiFi 传输配置

1. 传输层接口

c 复制代码
typedef struct {
    media_transfer_send_cb send;       // 发送回调
    media_transfer_prepare_cb prepare; // 预处理回调
    media_transfer_drop_check_cb drop_check; // 丢帧检查
    media_transfer_get_tx_buf_cb get_tx_buf; // 获取发送缓冲
    media_transfer_get_tx_size_cb get_tx_size; // 获取缓冲大小
    frame_buffer_t *(*read)(image_format_t format, uint32_t timeout); // 读取帧
    void (*free)(image_format_t format, frame_buffer_t *frame); // 释放帧
} media_transfer_cb_t;

2. 分包发送

wifi_transfer.c 将一帧数据按 pkt_size 分包,添加 transfer_data_t 头部:

c 复制代码
typedef struct {
    uint8_t id;    // 帧 ID(sequence 低 8 位)
    uint8_t eof;   // 帧结束标志
    uint8_t cnt;   // 包序号
    uint8_t size;  // 总包数
    uint8_t data[]; // 载荷
} transfer_data_t;

发送流程

复制代码
① 读取帧 → wifi_transfer_send_frame()
② 计算分包数: count = length / pkt_size, tail = length % pkt_size
③ 逐包填充 transfer_data_t 头 + 数据
④ 调用 cfg->cb->send() 发送
⑤ 最后一包设置 eof = 1
⑥ 释放帧

3. 传输任务

c 复制代码
static void wifi_transfer_task_entry(beken_thread_arg_t data)
{
    while (cfg->enable) {
        // 从帧队列读取帧(50ms 超时)
        frame = cfg->cb->read(cfg->img_format, 50);
        if (frame == NULL) continue;
        
        // 分包发送
        wifi_transfer_send_frame(cfg, frame);
        
        // 释放帧
        cfg->cb->free(cfg->img_format, frame);
    }
}

4. 打开/关闭

c 复制代码
bk_err_t bk_wifi_transfer_frame_open(const media_transfer_cb_t *cb, 
                                      uint16_t img_format)
{
    // 初始化发送缓冲
    wifi_transfer_buffer_init(cfg);
    // 创建传输任务
    rtos_create_thread(&cfg->transfer_thread, ..., "trs_task", ...);
    // 设置 WiFi 媒体模式
    bk_wifi_set_wifi_media_mode(true);
    bk_wifi_set_video_quality(WIFI_VIDEO_QUALITY_SD);
}

bk_err_t bk_wifi_transfer_frame_close(void)
{
    cfg->enable = 0;
    // 等待任务退出
    rtos_get_semaphore(&cfg->sem, BEKEN_NEVER_TIMEOUT);
    // 恢复 WiFi 模式
    bk_wifi_set_wifi_media_mode(false);
    bk_wifi_set_video_quality(WIFI_VIDEO_QUALITY_HD);
}

九、应用层集成

1. 门铃应用入口

doorbell_asr/ap/ap_main.c

c 复制代码
int main(void)
{
    bk_init();           // ① 系统初始化
    media_service_init(); // ② 媒体服务初始化

#if CONFIG_INTEGRATION_DOORBELL
    bk_smart_config_init(); // ③ WiFi 配网
    doorbell_core_init();   // ④ 门铃核心服务
#endif
    return 0;
}

2. 摄像头开关流程

doorbell_devices.c 中的 doorbell_camera_turn_on() 展示了完整的调用链:

c 复制代码
int doorbell_camera_turn_on(camera_parameters_t *parameters)
{
    // ① 根据设备 ID 判断摄像头类型
    if (parameters->id == UVC_DEVICE_ID)  // 0xFDF6
        curr_cam_type = UVC_CAMERA;
    else
        curr_cam_type = DVP_CAMERA;

    // ② 根据传输格式配置编码
    if (parameters->format == 0) {  // WiFi 传输 MJPEG
        uvc_config.img_format = IMAGE_MJPEG;
        dvp_config.img_format = IMAGE_YUV | IMAGE_MJPEG;
    } else {  // WiFi 传输 H.264
        dvp_config.img_format = IMAGE_YUV | IMAGE_H264;
        uvc_config.img_format = IMAGE_MJPEG;  // UVC 输出 MJPEG
        db_device_info->pipeline_enable = true; // 启用 JPEG→H264 管线
    }

    // ③ 组装摄像头配置
    bk_camera_config_t camera_config = {0};
    camera_config.camera_type = curr_cam_type;
    if (curr_cam_type == DVP_CAMERA)
        camera_config.dvp_cfg.dvp_config = &dvp_config;
    else
        camera_config.uvc_cfg.uvc_config = &uvc_config;

    // ④ 打开摄像头
    ret = media_app_camera_open(&db_device_info->video_handle, &camera_config);

    // ⑤ 如果启用管线,打开 H.264 编码
    if (db_device_info->pipeline_enable)
        media_app_pipeline_h264_encode_open(NULL, NULL, NULL, NULL);

    // ⑥ 如果 LCD 已打开,打开 JPEG 解码
    if (check_lcd_task_is_open())
        media_app_jpeg_decode_open(&jpeg_dec_config, NULL, NULL);
}

3. WiFi 传输开关

c 复制代码
int doorbell_video_transfer_turn_on(void)
{
    if (db_device_info->h264_transfer)
        bk_wifi_transfer_frame_open(db_device_info->camera_transfer_cb, IMAGE_H264);
    else
        bk_wifi_transfer_frame_open(db_device_info->camera_transfer_cb, IMAGE_MJPEG);
}

int doorbell_video_transfer_turn_off(void)
{
    bk_wifi_transfer_frame_close();
}

十、实际操作步骤

1. DVP 摄像头示例

bash 复制代码
cd projects/dvp_example
# 编译
make bk7258

# 烧录后通过串口 CLI 操作
# 打开 MJPEG 摄像头
dvp open 864 480 mjpeg

# 打开 H.264 摄像头
dvp open 864 480 h264

# 打开 YUV 输出
dvp open 864 480 yuv

# 转储帧数据
dvp dump

# 关闭摄像头
dvp close

2. UVC 单流示例

bash 复制代码
cd projects/uvc_example
make bk7258

# 打开 UVC MJPEG 流
uvc open 1 864 480 mjpeg

# 打开 UVC H.264 流
uvc open 1 1920 1080 h264

# 关闭
uvc close 1

3. UVC 双流示例

bash 复制代码
cd projects/uvc_dualstream_example
make bk7258

# 打开双流:H.264 主码流 + MJPEG 子码流
uvc open_dual 1 H264 1280 720 MJPEG 640 480

# 关闭双流
uvc close 1 all

4. 视频管线示例

bash 复制代码
cd projects/video_pipeline_example
make bk7258

# 创建管线
video_pipeline new

# 打开 H.264 编码管线(JPEG 解码 → H.264 编码)
video_pipeline open_h264e

# 打开旋转管线(JPEG 解码 → 旋转 → LCD)
video_pipeline open_rotate

# 关闭管线
video_pipeline close_h264e
video_pipeline delete

5. 门铃应用

bash 复制代码
cd projects/doorbell_asr
make bk7258
# 烧录后通过 APP 或 CLI 控制摄像头和传输

十一、调试与验证

1. 关键日志

源码中留有关键调试日志:

日志 TAG 位置 内容
dvp_drv bk_dvp.c 打开成功:dvp open success 864 X 480, 30, 0x4
uvc_stream bk_uvc.c 连接:device_index, port
h264_pipline h264_encode_pipeline.c 编码:I/p frame type
jdec_pip jpeg_decode_pipeline.c 解码模式切换
wifi_trs wifi_transfer.c 分包发送

2. 性能统计

media_debug_t 结构提供实时统计:

c 复制代码
typedef struct {
    uint16_t isr_h264;      // H.264 帧计数
    uint16_t isr_decoder;   // JPEG 解码帧计数
    uint16_t fps_wifi;      // WiFi 传输帧率
    uint32_t h264_kbps;     // H.264 码率
    uint32_t wifi_kbps;     // WiFi 传输码率
} media_debug_t;

3. 常见问题排查

现象 原因 排查方法
DVP 打不开 传感器未检测到 检查 I2C 地址、GPIO 引脚、传感器供电
UVC 连接超时 USB 设备未枚举 检查 USB 线缆、设备供电、VID/PID
帧数据错误 分辨率不匹配 检查 uvc_camera_stream_check_config() 日志
H.264 编码失败 管线未正确初始化 检查 uvc_pipeline_init() 返回值
WiFi 丢帧 发送缓冲不足 检查 drop_check 回调逻辑
帧缓冲泄漏 double-free 或忘记释放 检查 frame_crc 校验日志
  1. 统一抽象bk_camera_ctlr 将 DVP 和 UVC 统一为六个接口,上层无感知
  2. 回调分离:中断回调通过工作线程转发,避免中断上下文阻塞
  3. 双队列管理:free/ready 队列实现零拷贝帧传递
  4. 管线可组合:JPEG 解码 → H.264 编码 / 旋转 / 缩放按需注册
  5. WiFi 分包:固定头部 + 载荷,支持接收端重组

掌握这些要点后,即可灵活配置出 DVP 直出、UVC 接收、JPEG→H.264 转码等多种图传方案。

相关推荐
caimouse2 小时前
ReactOS 窗口系统分析(20):光标与图标 — cursoricon.c
c语言·开发语言
淡淡的香烟2 小时前
Android中的MQTT通信原理解析
android·物联网
H_oRIZoN_2 小时前
Linux入门DAY23(C语言树形结构)
linux·c语言·数据结构
千秋岁rr3 小时前
5.0 TIM
单片机·嵌入式硬件
小僧景贤4 小时前
STM32 startup.s 启动文件与 .map 映射文件深度技术解析(md/hd 版本差异化对比)
stm32·内存映射·startup.s·map 文件·单片机启动流程
悟天特斯5 小时前
智慧楼宇边缘计算实战:从端侧自治到云边协同的算力下沉架构
人工智能·物联网·架构·边缘计算
caimouse5 小时前
ReactOS 窗口系统分析(25):标题栏显示与系统按钮 — nonclient.c 标题栏专题
c语言·开发语言
2601_966799046 小时前
CH398 技术解析:USB 3.0转千兆以太网单芯片方案与内置电源设计要点
单片机·嵌入式硬件
caimouse6 小时前
ReactOS 窗口系统分析(14):计时器/属性/加速键/热键 — timer.c + prop.c + accelerator.c + hotkey.c
c语言·开发语言·reactos