嵌入式 Linux 实战:i.MX6ULL 上的 CAN 通信与摄像头采集

CAN 通信与摄像头采集

在嵌入式 Linux 项目中,CAN 通信 和摄像头采集是两个非常典型的外设场景。CAN 常用于工业控制、车载通信和多节点数据上报;摄像头则常用于图像采集、视觉识别和人机交互。本文以 i.MX6ULL 为例,整理一套从硬件、设备树、驱动到应用测试的实战流程。

一、系统架构

整体系统可以简单理解为:

  • 摄像头模块:负责采集图像数据,通常通过 I2C 配置寄存器,通过 CSI/MIPI 接口输出图像。
  • i.MX6ULL 主控:运行嵌入式 Linux,负责摄像头驱动、V4L2 图像采集和 CAN 通信。
  • CAN 总线:用于与其他 MCU 节点通信,例如 STM32、电机控制板、传感器采集板等。
  • 应用层:通过 V4L2 获取图像,通过 SocketCAN 收发 CAN 报文。

Linux 内核中,这两类外设分别对应不同子系统:

外设 内核框架 用户空间接口
CAN SocketCAN can0、candump、cansend
摄像头 V4L2 / Media /dev/videoX、v4l2-ctl

二、CAN 通信部分

2.1 CAN 总线简介

CAN 是一种差分串行总线,具有较好的抗干扰能力和多节点通信能力。常见特点包括:

  • 支持多主通信;
  • 具备错误检测与重传机制;
  • 适合工业现场、车载、机器人等场景;
  • Linux 下通过 SocketCAN 将 CAN 设备抽象为网络接口。

硬件上,CAN 控制器通常集成在 SoC 内部,外部通过 CAN 收发器连接到 CANH、CANL。总线两端一般需要接入 120Ω 终端电阻,否则容易出现通信不稳定。

2.2 设备树配置

以 i.MX6ULL 为例,CAN 节点通常需要在设备树中使能,并配置引脚和时钟。示例如下:

复制代码
&can1 {
    pinctrl-names = "default";
    pinctrl-0 = <&pinctrl_can1>;
    xceiver-supply = <&reg_can1_3v3>;
    status = "okay";
};

引脚配置示例:

复制代码
pinctrl_can1: can1grp {
    fsl,pins = <
        MX6UL_PAD_GPIO1_IO08__FLEXCAN1_TX 0x1b0b0
        MX6UL_PAD_GPIO1_IO09__FLEXCAN1_RX 0x1b0b0
    >;
};

具体引脚需要根据原理图确认,不能直接照搬。

2.3 Linux 端 CAN 调试

启动系统后,可以先查看 CAN 接口:

复制代码
ip link show can0

配置波特率并启动 CAN 接口:

复制代码
sudo ip link set can0 type can bitrate 500000
sudo ip link set can0 up

监听 CAN 报文:

复制代码
candump can0

发送 CAN 报文:

复制代码
cansend can0 123#1122334455667788

其中:

  • 123 是 CAN ID;
  • 1122334455667788 是 8 字节数据。

如果 candump 没有输出,可以依次检查:

  1. CAN 收发器供电是否正常;
  2. CANH、CANL 是否接反;
  3. 总线两端是否有 120Ω 终端电阻;
  4. 波特率是否与对端一致;
  5. 设备树中 CAN 节点是否真正使能;
  6. dmesg | grep -i can 是否有报错。

三、摄像头部分

3.1 摄像头硬件连接

以 OV5640 为例,摄像头通常通过以下接口连接到 i.MX6ULL:

  • I2C 接口:用于配置摄像头寄存器;
  • CSI/MIPI 接口:用于输出图像数据;
  • PWDN/RESET:用于电源控制和复位;
  • MCLK:摄像头主时钟。

调试前建议先确认:

  • I2C 地址是否正确,例如 OV5640 常见地址为 0x3c;
  • 供电电压是否稳定;
  • CSI 数据线和时钟线连接是否正确;
  • PWDN、RESET 引脚极性是否符合模组要求。

可以使用 i2cdetect 初步确认摄像头是否出现在总线上:

复制代码
i2cdetect -y 1

3.2 设备树配置

OV5640 的设备树节点一般放在对应的 I2C 控制器下,例如 &i2c1。示例如下:

复制代码
&i2c1 {
    clock-frequency = <100000>;
    status = "okay";

    ov5640: camera@3c {
        compatible = "ovti,ov5640";
        reg = <0x3c>;
        pinctrl-names = "default";
        pinctrl-0 = <&pinctrl_csi1 &pinctrl_csi_pwn_rst>;
        clocks = <&clks IMX6UL_CLK_CSI>;
        clock-names = "csi_mclk";
        pwn-gpios = <&gpio1 4 GPIO_ACTIVE_LOW>;
        rst-gpios = <&gpio1 2 GPIO_ACTIVE_HIGH>;
        csi_id = <0>;
        mclk = <24000000>;
        mclk_source = <0>;
        status = "okay";

        port {
            ov5640_ep: endpoint {
                remote-endpoint = <&csi1_ep>;
            };
        };
    };
};

CSI 节点也需要使能:

复制代码
&csi {
    status = "okay";

    port {
        csi1_ep: endpoint {
            remote-endpoint = <&ov5640_ep>;
        };
    };
};

注意:引脚、GPIO 编号、I2C 总线号必须和实际硬件一致。不同开发板的 OV5640 引脚可能完全不同,不能盲目复制。

3.3 内核配置

摄像头驱动通常依赖 V4L2 和 CSI 采集接口。内核中需要确认以下配置已开启:

复制代码
Device Drivers --->
    Multimedia support --->
        [*] V4L platform devices
        [*] MXC Video For Linux Video Capture
        [*] OV5640 camera support

如果使用正点原子等第三方 BSP,建议优先参考其提供的 OV5640 驱动和设备树配置,因为原厂内核驱动可能缺少部分板级适配。

3.4 摄像头调试

系统启动后,可以先查看视频设备:

复制代码
ls /dev/video*

查看摄像头支持格式:

复制代码
v4l2-ctl -d /dev/video0 --list-formats-ext

查看完整信息:

复制代码
v4l2-ctl -d /dev/video0 --all

简单采集测试:

复制代码
v4l2-ctl -d /dev/video0 --stream-mmap=4 --stream-count=120 --stream-to=test.yuv

如果采集失败,可以查看内核日志:

复制代码
dmesg | grep -i ov5640
dmesg | grep -i csi

常见问题包括:

  • I2C 通信失败,检查地址和上拉电阻;
  • 无图像,检查 MCLK、PWDN、RESET;
  • 图像花屏,检查 CSI 数据线和时钟;
  • 节点未出现,检查设备树和内核驱动是否匹配。

四、CAN 与摄像头联动示例

一个比较典型的联动场景是:

  1. 摄像头采集图像;
  2. 应用层对图像做简单处理或特征提取;
  3. 通过 CAN 总线将结果发送给 MCU;
  4. MCU 根据结果控制电机、指示灯或其他执行机构。

例如,Linux 端可以通过 SocketCAN 发送一帧检测结果:

复制代码
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <net/if.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <linux/can.h>
#include <linux/can/raw.h>

int main(void)
{
    int s;
    struct sockaddr_can addr;
    struct ifreq ifr;
    struct can_frame frame;

    s = socket(PF_CAN, SOCK_RAW, CAN_RAW);
    if (s < 0) {
        perror("socket");
        return -1;
    }

    strcpy(ifr.ifr_name, "can0");
    ioctl(s, SIOCGIFINDEX, &ifr);

    memset(&addr, 0, sizeof(addr));
    addr.can_family = AF_CAN;
    addr.can_ifindex = ifr.ifr_ifindex;

    if (bind(s, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
        perror("bind");
        close(s);
        return -1;
    }

    frame.can_id = 0x200;
    frame.can_dlc = 8;
    memset(frame.data, 0, sizeof(frame.data));
    frame.data[0] = 0x01;  // 示例:检测结果
    frame.data[1] = 0x02;  // 示例:目标数量

    if (write(s, &frame, sizeof(frame)) != sizeof(frame)) {
        perror("write");
        close(s);
        return -1;
    }

    printf("CAN frame sent, id=0x%03X\n", frame.can_id);
    close(s);
    return 0;
}

交叉编译后放到开发板运行:

复制代码
arm-linux-gnueabihf-gcc can_send.c -o can_send

运行前确保 CAN 接口已经启动:

复制代码
sudo ip link set can0 type can bitrate 500000
sudo ip link set can0 up
./can_send

对端 MCU 如果正常接收,就能看到对应 CAN 报文。

五、调试顺序建议

实际开发中,建议按下面顺序推进:

  1. 先调通 CAN 物理层

    用示波器或逻辑分析仪确认 CANH、CANL 波形,确认终端电阻和波特率。

  2. 再调通 Linux CAN 接口

    使用 candump、cansend 验证收发是否正常。

  3. 再调通摄像头 I2C

    使用 i2cdetect 确认摄像头地址。

  4. 再调通摄像头驱动

    确认 /dev/videoX 出现,并能采集图像。

  5. 最后做联动应用

    把图像结果通过 CAN 发送出去,或者根据 CAN 指令控制摄像头工作模式。

六、踩坑总结

  • CAN 不通:优先检查 120Ω 终端电阻、波特率、CANH/CANL 是否接反。
  • 摄像头 I2C 扫描不到:检查供电、上拉电阻、I2C 地址和引脚复用。
  • 设备树节点不生效:确认修改的 dts 是否真正被编译,U-Boot 加载的 dtb 是否与编译结果一致。
  • 摄像头有节点但无图像:检查 MCLK、PWDN、RESET、CSI 数据线和驱动匹配。
  • CAN 能发不能收:检查对端是否也在发送,ID 过滤是否开启,总线是否只接了一个节点。

七、小结

CAN 通信和摄像头采集看似属于两个方向,但在嵌入式项目中经常需要协同工作。CAN 负责可靠、实时的控制与数据交互,摄像头负责环境感知和图像处理。把两者结合起来,可以做出智能小车、工业视觉终端、机器人感知节点等完整项目。

后续可以继续扩展到 Qt 界面显示、OpenCV 图像处理、ROS2 通信,或者将摄像头结果通过 CAN 发送给 STM32 执行运动控制。


相关推荐
xinruoqianqiu1 小时前
记录Linux
linux
牛马鸡niumasi1 小时前
【Linux】进程替换
linux
RFID科技的魅力2 小时前
2026 年 RFID 资产标签批量制作怎么做?RFID 标签打印机实操指南
运维·人工智能·嵌入式硬件·物联网
灯澜忆梦2 小时前
【Linux】#10 | Linux 终端实用快捷键 + 安装应用
linux·运维·服务器
码士集团小青2 小时前
面霸篇:高频 Java 基础问题(核心卷一)
linux·服务器·数据库·redis·缓存·1024程序员节
水饺编程3 小时前
第2章:Linux 基础,切换用户
linux·c语言·汇编
Lsetea4 小时前
curl指定--tlsv1.2仍连接TLS1.3:最低版本与tls-max排查
linux·https·ssl证书·curl·tls
Lsetea4 小时前
curl访问HTTPS报47 Too many redirects:重定向循环与max-redirs排查
linux·http·https·ssl证书·curl