嘉楠科技
直播中

jackhui

13年用户 1166经验值
私信 关注
[问答]

如何在K230上移植mipi sensor,然后读取mipi接口的raw数据?

问题描述
我想在K230上移植一个 1通道 的 mipi 摄像头,这个摄像头输出的不是帧图像数据,而是一个事件流,所以需要通过 mipi 读取它的 RAW 数据自己解码使用,如何实现这个过程?有人知道吗?
期待结果
给出移植的教程,CanMV、linux平台、RT-Smart或者Linux+RT-Smart的都可以
软硬件版本信息
CanMV-K230-LP4-V3.0

回帖(1)

凌章致

2025-6-20 17:40:17

针对K230移植单通道MIPI事件流摄像头并读取RAW数据的实现方案,以下是详细步骤(以Linux平台为基础,结合底层驱动和用户空间程序):




核心步骤


1. 硬件准备与确认



  • 摄像头接口:确认MIPI CSI连接器是否兼容传感器(电气特性、物理接口)。

  • 时钟匹配:传感器输出时钟(MIPI CLK)需在K230 CSI控制器支持的范围内(查阅K230 TRM)。

  • 电源与I2C:确保摄像头供电和I2C控制引脚正确连接(通常通过K230扩展板实现)。




2. 内核驱动层移植


2.1 修改设备树 (DTS)


  • 路径:arch/riscv/boot/dts/canaan/ 或相似路径下找到目标板DTS文件。


  • 关键内容:


    &csi {
      status = "okay";
      port {
          csi_in: endpoint {
              remote-endpoint = <&sensor_out>;
              data-lanes = <1>;       // 单通道配置为1
              clock-lanes = <0>;       // 时钟通道号
              clock-noncontinuous;      // 部分传感器需要
          };
      };
    };

    &i2c0 { // 根据实际连接的I2C总线号修改
      status = "okay";
      camera_sensor: sensor@3c { // 替换为实际I2C地址
          compatible = "custom,event-sensor"; // 关键:需匹配驱动
          reg = <0x3c>;
          reset-gpios = <&gpio 12 GPIO_ACTIVE_LOW>; // 复位引脚(可选)
          powerdown-gpios = <&gpio 13 GPIO_ACTIVE_LOW>; // 下电引脚(可选)

          port {
              sensor_out: endpoint {
                  remote-endpoint = <&csi_in>;
                  data-lanes = <1>;
                  clock-lanes = <0>;
              };
          };
      };
    };



2.2 编写/修改摄像头驱动


  • 驱动框架:基于V4L2 Subdevice框架实现(参考drivers/media/i2c/sensors/下的同类驱动)。


  • 关键修改点


    // 1. 在probe函数中设置CSI参数
    static int sensor_probe(struct i2c_client *client) {
      // ... 初始化代码
      struct v4l2_subdev *sd;
      // 配置MIPI参数
      struct v4l2_mbus_config mbus_cfg = {
          .type = V4L2_MBUS_CSI2_DPHY, // 明确指定CSI-2 D-PHY
          .flags = V4L2_MBUS_CSI2_1_LANE | // 单通道模式
                   V4L2_MBUS_CSI2_CONTINUOUS_CLOCK, // 或自定义标志
      };
      sensor_write_reg(sd, REG_CTRL_MODE, EVENT_STREAM_MODE); // 设置事件流模式
      // ... 注册subdev
    }

    // 2. 实现事件流格式定义
    static int sensor_enum_mbus_code(struct v4l2_subdev *sd,
                                   struct v4l2_subdev_state *sd_state,
                                   struct v4l2_subdev_mbus_code_enum *code) {
      switch (code->index) {
          case 0:
              code->code = MEDIA_BUS_FMT_EVENT_STREAM; // 自定义或使用RAW格式
              return 0;
          default:
              return -EINVAL;
      }
    }



2.3 CSI控制器配置


  • 确保K230的CSI驱动(如drivers/media/platform/canaan) 支持RAW数据直通模式。修改CSI控制器代码关闭ISP处理:
    // 在CSI接收路径(例如csi_rx_start函数)
    if (is_event_stream) {
      csi_reg_write(CSI_CTRL_REG, CSI_CTRL_BYPASS_ISP); // 关键:绕过ISP
      csi_reg_write(CSI_FMT_REG, RAW_DATA_FORMAT);      // 设置原始数据格式
    }




3. 用户空间数据读取


3.1 使用V4L2工具验证

# 列出摄像头设备
v4l2-ctl --list-devices

# 设置RAW格式并捕获数据(假设设备为/dev/video0)
v4l2-ctl -d /dev/video0 --set-fmt-video=width=640,height=480,pixelformat=GREY
v4l2-ctl --stream-mmap --stream-count=10 --stream-to=event_data.raw

3.2 Python示例(使用OpenCV/PyV4L2)

import cv2

# 如果V4L2支持,直接用VideoCapture读RAW
cap = cv2.VideoCapture("/dev/video0")
cap.set(cv2.CAP_PROP_FOURCC, cv2.VideoWriter_fourcc('Y','1','0',' '))  # 设置GREY格式

while True:
    ret, frame = cap.read()
    if ret:
        # frame即为RAW数据(Numpy数组)
        process_event_data(frame)  # 自定义事件解析函数

3.3 C语言高效读取(推荐)

#include 
#include
#include

int main() {
    int fd = open("/dev/video0", O_RDWR);
    struct v4l2_format fmt = {
        .type = V4L2_BUF_TYPE_VIDEO_CAPTURE,
        .fmt.pix = {
            .width = 640,
            .height = 480,
            .pixelformat = V4L2_PIX_FMT_GREY, // 或自定义格式
        }
    };
    ioctl(fd, VIDIOC_S_FMT, &fmt); // 设置格式

    // 申请DMA缓冲区
    struct v4l2_requestbuffers req = {0};
    req.count = 4;
    req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    req.memory = V4L2_MEMORY_MMAP;
    ioctl(fd, VIDIOC_REQBUFS, &req);

    // 映射缓冲区并启动采集
    for (int i = 0; i < req.count; i++) {
        struct v4l2_buffer buf = {0};
        buf.type = req.type;
        buf.memory = V4L2_MEMORY_MMAP;
        buf.index = i;
        ioctl(fd, VIDIOC_QUERYBUF, &buf);
        void *mem = mmap(NULL, buf.length, PROT_READ, MAP_SHARED, fd, buf.m.offset);
        // 加入队列
        ioctl(fd, VIDIOC_QBUF, &buf);
    }
    ioctl(fd, VIDIOC_STREAMON, &req.type);

    while (1) {
        struct v4l2_buffer buf = {0};
        buf.type = req.type;
        buf.memory = V4L2_MEMORY_MMAP;
        ioctl(fd, VIDIOC_DQBUF, &buf); // 出队
        parse_event_stream(mem, buf.bytesused);  // 解析事件流
        ioctl(fd, VIDIOC_QBUF, &buf);  // 重新入队
    }
}



4. 事件流解析关键点



  • 数据格式:根据传感器手册解析事件包(通常包含(x, y, timestamp, polarity))。

  • 同步机制:若使用非标准同步信号,需通过GPIO/SPI捕获时间戳。

  • 去噪处理:事件数据常需滤波(如背景活动抑制)。




调试建议



  1. 逻辑分析仪:抓取MIPI CLK/DATA信号确认物理层通信。

  2. I2C探测i2cdetect -y 0检查设备地址是否正确。

  3. 内核日志dmesg | grep csi 查看CSI控制器初始化状态。

  4. 寄存器读写:用devmem2工具直接读写传感器寄存器调试。



注意:若使用 RT-Smart,需在内核启用CONFIG_MEDIA_SUPPORT并重新编译BSP,用户空间改用RT-Thread的V4L2 API或定制驱动接口。



通过以上步骤,您可在K230上完整实现事件流摄像头的RAW数据传输与解析。

举报

更多回帖

发帖
×
20
完善资料,
赚取积分