摄像头、显示和音频
1. MIPI-CSI 摄像头
1.1 MIPI 摄像头概述
1.1.1 MIPI CSI-2 接口概述
MIPI(Mobile Industry Processor Interface)是由 ARM、Nokia、ST、TI 等公司于 2003 年成立的联盟制定的接口。其目的是对智能手机等移动设备内部的接口(摄像头、显示接口、RF/基带接口等)进行标准化,从而降低设备设计复杂度并提高设计灵活性。MIPI 联盟下设多个工作组(Work Group),分别负责对应设备的标准定义,包括 Camera 工作组、Display 工作组、高速多点链路工作组等十多个工作组。
MIPI CSI-2 接口是由 MIPI 联盟 Camera 工作组制定的 CSI(Camera Serial Interface)第二版标准。
-
硬件层: 支持最多 4 个虚拟通道(Lane)进行数据传输。在硬件上,一个 Lane 由一对差分信号线实现(参见原理图)。每个 Lane 的最大通信速率为 2.0Gbps。
-
软件层: MIPI CSI-2 协议栈主要由应用层、协议层和物理层组成。其中协议层又可细分为像素/字节打包与解包层、底层协议层以及 Lane 管理层。
1.1.2 硬件接口资源介绍
EASY EAI Nano-TB 开发板配备 2 路 MIPI CSI-2 接口。每路接口引出 4 个 Lane。位置定义如下。

硬件接口资源介绍
EASY EAI Nano-TB 默认搭配 IMX415 单目摄像头使用,并附带一根 0.5mm 间距、40pin 的 FPC 反向线。

硬件接口资源介绍
-
反向线: 两端的蓝色塑料补强板(识别标记)不在同一面。
-
同向线: 两端的蓝色塑料补强板在同一面。

硬件接口资源介绍
1.1.3 接线步骤
-
接线操作必须在断电状态下进行。
-
连接 IMX415 摄像头与 Camera1 接口时,请使用反向线。
💡 注意: 如果线缆类型使用错误,可能会烧毁摄像头或核心板。接线时请务必注意。)
- 连接器锁扣部分与 FPC 线缆的蓝色塑料识别标记必须位于同一侧。详情请参考下图。

接线步骤
1.1.4 确认设备状态
dmesg 命令确认 MIPI-CSI2 接口模块是否正常工作。
dmesg \| grep "csi2-.phy"摄像头正常挂载时如下所示。通过日志可以确认以下内容:
-
dphy0 接口连接了传感器型号为 imx415 的摄像头, 其 MIPI-CSI2 地址为 1-0036。
-
dphy3 接口连接了传感器型号为 imx415 的摄像头, 其 MIPI-CSI2 地址为 4-0036。

确认设备状态
如果没有显示目标摄像头节点,需要检查 FPC 线缆是否正确连接。请按照“1.3 接线步骤”检查线缆连接。
1.1.5 查找可用设备节点
在 Rockchip 平台中,一个 MIPI-CSI 接口会分配 20 个以上的 video 节点(生成数量由设备树定义决定)。请参考下图。

查找可用设备节点
💡 注意: CSI0 不一定对应 video0~24,而是根据设备树的实际情况生成。
另外,MIPI-CSI 摄像头对应的节点会在修改内核设备树时固定。也就是说,在内核中设置 MIPI-CSI 摄像头数量后,无论实际是否连接了 MIPI-CSI 摄像头,video 节点的生成数量都不会根据摄像头连接情况【动态】变化。
因此,用户需要确认这些节点对应的具体设备信息。在 Linux 的 v4l2 框架中,这些节点的描述信息统一汇总在 /sys/class/video4linux/ 目录下。

查找可用设备节点
进入任意目录,例如 video22。

查找可用设备节点
可以使用 cat 命令查看该 name 文件的内容。根据 Rockchip 芯片定义,如果该名称为 mainpath 或 selfpath,则 video22 是可用节点。 在终端任意目录下执行以下命令,即可快速扫描所有节点的 name 描述。
grep "mainpath" /sys/class/video4linux/video\*/name
查找可用设备节点
从扫描结果可知,/dev/video22 和 /dev/video23 是 MIPI-CSI0 的可用节点。
1.2. 快速开始
1.2.1 准备开发环境
在 PC 端 Ubuntu 系统中执行 run 脚本,进入 EASY-EAI 编译环境。详情如下。
cd ~/develop_environment./run.sh 22041.2.2 下载源码并编译示例
首先,在虚拟机后台终端执行以下命令,创建外设示例源码的管理目录:
cd /optmkdir -p EASY-EAI-Nano-TB/demo例如,将示例程序下载到“PC\D:”(没有指定要求,可下载到用户任意位置)。
然后,将下载的示例复制到虚拟机文件系统中。

下载源码并编译示例
最后,进入对应示例目录并执行编译操作。具体命令如下:
cd EASY-EAI-Nano-TB/demo/02_camera./build.sh💡 注意:由于依赖库位于开发板上,交叉编译过程中需要保持 /mnt 挂载。

下载源码并编译示例
1.2.3 运行示例
通过串口调试或 SSH 访问开发板后台,并进入示例所在目录:
cd /userdata
运行示例
运行示例的命令如下:
./test-mipiCam 221.2.4 运行结果
运行结果如下图所示。

运行结果
示例运行完成后,会在 /tmp 目录下生成名为 photo 的原始数据(Raw Video)图像文件。返回虚拟机,新建一个终端窗口,使用 scp 命令将该图像复制到本地。

运行结果
之后,使用 mplayer 命令显示复制得到的 photo 文件。
mplayer -demuxer rawvideo -rawvideo w=1920:h=1080:format=bgr24 photo -loop 0
运行结果
由于示例默认分辨率为 1920x1080,因此 w 和 h 参数分别指定为 1920 和 1080。如果图像无法正常显示,可能是与手头摄像头的分辨率不一致,需要调整 mipicamera_init() 的分辨率(例如 1280x720)。
1.3 MIPI 摄像头测试用例
示例代码路径为 02_camera/test-mipiCam/main.c。MIPI Camera API 测试用例代码的逻辑流程如下图所示。

MIPI 摄像头测试用例
1.3.1 源代码说明
int main(int argc, char **argv) { int ret = 0; if(1 == argc){ printf("\nerr: Missing parameter!\n"); printf("================= [usage] ==================\n"); printf("example:\n"); printf("\t%s <22/30>\n", argv[0]); printf("--------------------------------------------\n"); return 0; }
char *pbuf = NULL; int skip = 0; FILE *fp = NULL;
int cameraIndex = atoi(argv[1]); // 通常是 video22 ret = mipicamera_init(cameraIndex, CAMERA_WIDTH, CAMERA_HEIGHT, 0); if (ret) { printf("error: %s, %d\n", __func__, __LINE__); goto exit3; }
pbuf = (char *)malloc(IMAGE_SIZE); if (!pbuf) { printf("error: %s, %d\n", __func__, __LINE__); ret = -1; goto exit2; }
// 跳过前 10 帧(用于稳定曝光和白平衡) skip = 10; while(skip--) { ret = mipicamera_getframe(cameraIndex, pbuf); if (ret) { printf("error: %s, %d\n", __func__, __LINE__); goto exit1; } }
/* tips: 在 Ubuntu 环境中,可以使用 mplayer 显示采集到的图像 * mplayer -demuxer rawvideo -rawvideo w=1920:h=1080:format=bgr24 photo -loop 0 */ fp = fopen("/tmp/photo", "w"); if (!fp) { printf("error: %s, %d\n", __func__, __LINE__); ret = -1; goto exit2; } fwrite(pbuf, 1, IMAGE_SIZE, fp); fclose(fp);
exit1: free(pbuf); pbuf = NULL;
exit2: mipicamera_exit(cameraIndex);
exit3: return ret;}代码中的 mipicamera_init()、mipicamera_getframe()、mipicamera_exit() 是对 v4l2 接口调用进行简化的封装函数。具体实现在 02_camera/commonApi/mipi_camera.c 中。
2 USB 摄像头
2.1 USB 概述
USB(Universal Serial Bus,通用串行总线)是电子设备之间通信常用的通用标准接口。作为高速串行总线,USB 具有很高的传输速率,能够满足需要高速数据传输的应用环境要求。其优点包括供电方便(支持总线供电)、安装和配置简单(支持即插即用和热插拔)、端口扩展方便(可通过 Hub 扩展最多 127 个外设)、传输方式多样(4 种传输模式)以及兼容性好(产品升级后的向下兼容性)等。
2.1.1 EASY EAI Nano-TB USB 资源介绍
EASY EAI Nano-TB 具有 1 路 USB3.0 HOST,通过 USB Hub 芯片扩展为 2 个 USB3.0 HOST 接口;同时还提供 1 路直接引出的 USB2.0 HOST 接口。

EASY EAI Nano-TB USB 资源介绍
2.1.2 USB 摄像头连接步骤
通常建议只连接一台 USB 摄像头。
如果需要连接多个 USB 摄像头,可以考虑使用 Hub 扩展为多口 USB。理论上可扩展 127 台设备,但实际连接多个 USB 摄像头时,需要考虑 USB 带宽、USB Hub 层级数量(包含 RootHub 的最大层级为 7 层)以及设备 ID 重复问题的处理等。因此,除非必要,否则【不建议】这样连接。
💡 注意:USB 支持热插拔,但在底板未安装保护外壳的情况下插拔时,容易触碰底板上的器件,板卡附近的金属部件也可能导致短路。因此,建议在完全断电状态下插拔外设。
2.1.3 USB 设备连接管理
物理连接 USB 摄像头或 USB Hub 后,需要确认这些 USB 设备是否被系统正确识别。此时使用 lsusb 命令。
lsusb
USB 设备连接管理
另外,在文件系统中,USB 设备的管理目录为 /sys/bus/usb/devices。可在此查看从 root_hub 挂载的 USB 设备与 Hub 设备的拓扑关系。
USB 设备对象命名规则如下:
USB 设备对象命名规则
USB 设备及接口在系统中按照特定规则命名和管理。详细命名规则如下。
设备命名规则
| 设备类型 | 命名规则 | 说明 |
|---|---|---|
| 总线对象 | usb1, usb2 | 处理器内置的 USB 硬件。由于被视为 0 号 Hub 设备,因此其接口对象从 0 开始计数。 |
| 直接连接的 USB 设备 | root_hub-hub_port | 注: 使用连字符 - 分隔。・ root_hub:所连接的总线编号。・ hub_port:表示该总线上第几个设备。0 号设备为总线本身,新接入设备从 1 开始。 |
| 连接到外部 Hub 的设备 | root_hub-hub_port.device | 注: 使用点号 . 分隔。新连接的设备会作为 Hub 的子设备处理,编号从 1 开始。 |
接口命名规则
对于设备对象所拥有的接口,也定义了用于识别配置和接口的命名规则。
| 对象 | 命名规则 | 说明 |
|---|---|---|
| 设备对象的 接口对象 | device_object:config.interface | 注: 使用冒号 : 分隔设备对象与其后续元素。后续元素包括配置(config)和接口,两者使用点号 . 分隔。・ device_object(设备对象):上述总线对象、直接连接设备、通过 Hub 连接设备的统称。・ config(配置描述符):USB 设备的配置描述符。一个 USB 设备能够在不同系统或架构中工作,是因为可使用不同配置描述符。・ interface(接口描述符):USB 设备的接口描述符。例如 USB 收发器中,负责音频播放的接口与负责录音的接口分别存在。 |
2.1.4 查找可用设备节点
在 Rockchip 平台中,一个 MIPI-CSI 接口会分配 20 个以上的 video 节点。另外,MIPI-CSI 摄像头对应的节点会在修改内核设备树时固定。也就是说,在内核中设置 MIPI-CSI 摄像头数量后,无论摄像头是否连接,video 节点状态都不会动态变化。
另一方面,一个 USB 摄像头对应 2 个 video 节点,并且【通常】排在 MIPI-CSI 摄像头节点之后。通过插拔 USB 摄像头并比较、观察 video 节点变化,即可判断哪两个节点是 USB 摄像头的设备节点。

查找可用设备节点
不过,【最准确】的方法是查看这些 video 节点的【描述信息】。在 Linux 的 v4l2 框架中,这些节点的描述信息全部汇总在 /sys/class/video4linux/ 目录下。

查找可用设备节点
进入任意描述目录(例如 video22),使用 cat 命令查看该 name 文件的内容。

查找可用设备节点
cat /sys/class/video4linux/video22/name
查找可用设备节点
因此,需要使用以下命令对【所有 video 节点】进行扫描和【过滤(筛选)】。
grep -i "usb" /sys/class/video4linux/video\*/name
查找可用设备节点
2.2 快速开始
2.2.1 准备开发环境
在 PC 端 Ubuntu 系统中执行 run 脚本,进入 EASY-EAI 编译环境。详情如下。
cd ~/develop_environment./run.sh 22042.2.2 下载源码并编译示例
首先,在虚拟机后台终端执行以下命令,创建外设示例源码的管理目录:
cd /optmkdir -p EASY-EAI-Nano-TB/demo接下来,下载示例程序:
然后,将下载的示例复制到虚拟机文件系统中。

下载源码并编译示例
最后,进入对应示例目录并执行编译操作。具体命令如下:
cd EASY-EAI-Nano-TB/demo/02_camera./build.sh💡 注意:由于依赖库位于开发板上,交叉编译过程中需要保持 /mnt 挂载。

2.2.3 运行示例
通过串口调试或 SSH 访问开发板后台,并进入示例所在目录:
cd /userdata
运行示例
运行示例的命令如下(51 表示 /dev/video51 ):
./test-usbCam-single 512.2.4 运行结果

运行结果
示例运行完成后,会在 /tmp 目录下生成名为 photo 的图像文件。返回虚拟机,新建一个终端窗口,使用 scp 命令将该图像复制到本地。

运行结果
之后,使用 mplayer 命令播放(显示)获取到的 photo 文件。
mplayer -demuxer rawvideo -rawvideo w=1280:h=720:format=bgr24 photo -loop 0由于示例默认分辨率为 1280x720,因此 w 和 h 参数分别指定为 1280 和 720。如果图像无法正常显示,可能是与手头摄像头的分辨率不一致,需要调整 usbcamera_init() 的分辨率(例如 640x480)。

运行结果
2.3. USB 摄像头 API 测试用例
示例代码路径为 02_camera/test-usbCam/single-cam.c。

运行结果
2.3.1 源代码说明
int main(int argc, char **argv){ if(1 == argc){ printf("\nerr: Missing parameter!\n"); printf("================= [usage] ==================\n"); printf("example:\n"); printf("\t%s <51/52>\n", argv[0]); printf("--------------------------------------------\n"); return 0; } int cameraIndex = atoi(argv[1]);
char *pbuf = NULL; int ret = 0; int skip = 0; FILE *fp = NULL;
ret = usbcamera_init(cameraIndex, CAMERA_WIDTH, CAMERA_HEIGHT, 0); if (ret) { printf("error: %s, %d\n", __func__, __LINE__); goto exit3; }
pbuf = (char *)malloc(IMAGE_SIZE); if (!pbuf) { printf("error: %s, %d\n", __func__, __LINE__); ret = -1; goto exit2; }
// 跳过前 10 帧(用于稳定曝光等) skip = 10; while(skip--) { ret = usbcamera_getframe(cameraIndex, pbuf); if (ret) { printf("error: %s, %d\n", __func__, __LINE__); goto exit1; } }
/* tips: 在 Ubuntu 环境中,可以使用 mplayer 显示采集到的图像 * mplayer -demuxer rawvideo -rawvideo w=1280:h=720:format=bgr24 photo -loop 0 */ fp = fopen("/tmp/photo", "w"); if (!fp) { printf("error: %s, %d\n", __func__, __LINE__); ret = -1; goto exit2; } fwrite(pbuf, 1, IMAGE_SIZE, fp); fclose(fp);
exit1: free(pbuf); pbuf = NULL;exit2: usbcamera_exit(cameraIndex);exit3: return ret;}代码中的 usbcamera_init()、usbcamera_getframe()、usbcamera_exit() 是对 v4l2 接口调用进行简化的封装函数。具体实现在 02_camera/commonApi/usb_camera/usb_camera.c 中。
3.MIPI-DSI
3.1. MIPI-DSI 概述
MIPI(Mobile Industry Processor Interface)是由 ARM、Nokia、ST、TI 等公司于 2003 年成立的联盟制定的接口。其目的是对智能手机等移动设备内部的接口(摄像头、显示接口、RF/基带接口等)进行标准化,从而降低移动设备设计复杂度并提高设计灵活性。 MIPI 联盟下设多个工作组(Work Group),分别负责对应设备的标准定义,包括 Camera 工作组、Display 工作组、高速多点链路工作组等十多个工作组。 MIPI DSI 接口是由 MIPI 联盟 Display 工作组制定的 DSI(Display Serial Interface)接口标准。
3.1.1 EASY EAI Nano-TB DSI
DSI 接口不支持热插拔,同时对电源功率有一定要求。因此,使用 DSI 接口前,必须先确认输入电源为 DC12V-3A。否则,电力不足会导致无法驱动 DSI 屏幕,具体表现为在 U-Boot 阶段反复重启。
3.1.2 连接线说明
-
反向线: 两端的蓝色塑料识别标记不在同一面。
-
同向线: 两端的蓝色塑料识别标记在同一面。

连接线说明
3.1.3 硬件接口说明
-
MIPI 接口不支持热插拔。接线操作【必须】在断电状态下进行。
-
屏幕与 MIPI-DSI0 接口连接时,请使用同向线。(注意:如果线缆类型使用错误,可能会烧毁摄像头或核心板。接线时请务必注意。)
-
连接器锁扣部分与 FPC 线缆的蓝色塑料识别标记必须位于同一侧。详情请参考下图。

硬件接口说明
3.2. 驱动支持说明
默认固件中,MIPI-DSI 支持 8 英寸 MIPI 显示屏。如果系统需要支持其他尺寸或型号的显示屏,需要修改并替换内核镜像。包含以下步骤。
3.2.1 升级到最新固件
3.2.2 下载 Ubuntu 系统 SDK 源码
关于 ubuntu_sdk 源码(主要是内核源码)的下载,请参考“嵌入式底层开发/Ubuntu 系统 SDK/获取源码”。
3.2.3 修改内核设备树
按照“嵌入式底层开发/Ubuntu 系统 SDK/kernel”的说明,用户可根据需要构建并管理【内核源码开发仓库】。
然后,按照下图说明修改【内核源码开发仓库】中的设备树文件:

修改内核设备树
- EASY-EAI-Nano-TB 设备树入口点: arch/arm64/boot/dts/rockchip/rv1126b-nano.dts
编译后会生成新的 boot.img。
3.2.4 更新内核镜像
将上一步生成的 boot.img 复制到固件的 rockdev 目录中,替换原有 boot.img,然后将 boot.img 烧写到开发板。
3.3.1 默认显示结果
确认接线无误后,给设备上电,会显示如下图所示的启动 Logo。

默认显示结果
3.3.2 修改启动 Logo
如果需要修改启动 Logo,请参考“应用笔记/启动 Logo 修改方法”文档进行操作。
3.3.3 安装桌面系统
设备上电后,屏幕会一直显示 Logo。如果需要安装桌面系统,请参考“应用笔记/桌面系统安装”进行操作。
3.3.4 UI 应用开发
如果用户需要开发 UI 应用,请参考“EASY-EAI-Solution(开发示例)/QT GUI 示例”进行操作。
4. 音频输出
4.1. 声卡资源概述
EASY-EAI-Nano-TB 仅搭载一个由 RV1126B 主控输出的声卡。
可通过串口调试或 SSH 调试访问开发板终端。执行 aplay 命令查看声卡相关详细信息。详情如下。
aplay -l
声卡资源概述
4.1.1 硬件接口
硬件接口位置如下。

硬件接口
4.2. 声卡控制
在 Linux 系统中,应用层调用声卡时通常使用 ALSA(aplay、arecord、amixer)框架。本文档仅说明与本开发板平台相关的部分。如果需要进一步了解 ALSA 的具体使用方法,请参考开发者社区中的“ALSA 使用概述”。
4.2.1 音频播放
音频播放使用 aplay 命令。
aplay -D plughw:0,0 test.wav \## 音频从 Card0 输出4.2.2 音量调整
Card0 输出音量【值】的【范围】为 0 到 510。
amixer -c 0 cset name='DAC Digital Volume' 0,0 ## 将 Card0 输出音量【值】设置为 0amixer -c 0 cset name='DAC Digital Volume' 192,192 ## 将 Card0 输出音量【值】设置为 192amixer -c 0 cset name='DAC Digital Volume' 255,255 ## 将 Card0 输出音量【值】设置为 255💡 注意:这里的【值】是与增益正相关的变量,但既不是增益本身,也不是音量百分比。调整该值时的音量变化**【不是线性的】**。
-
0〜255: 这是数字增益部分。设置为 255 时,为不产生失真的最大音量。
-
256〜510: 这是模拟增益部分。超过 255 时,音频输出会产生失真。
4.3. 声卡使用注意事项
硬件层使用的 I2S/SAI 控制器本身不支持单声道或混音等使用场景。但用户可以通过 alsa-plug 进行数据转换。具体操作方法是编辑 ~/.asoundrc 文件。
4.3.1 声道转换
由于 I2S/SAI 控制器不支持单声道,当用户尝试播放单声道音频文件时,会出现“2 倍速播放”现象。 因此,用户需要编辑 ~/.asoundrc 文件进行声道转换。请在文件中追加以下内容:
pcm.!default{ type asym playback.pcm "playbackmono" capture.pcm "hw:0,0"}
pcm.playbackmono { type route slave.pcm "hw:0,0" # 输出到声卡设备 hw:0,0 slave.channels 2 ttable { # 声道 route 设置 0.0 0.5 0.1 0.5 1.0 0.5 1.1 0.5 }}之后播放音频文件时,将命令改为如下形式:
aplay -D default test_mono.wav或者
aplay -D playbackmono test_mono.wav5. 音频输入
5.1 声卡资源介绍
EASY-EAI-Nano-TB 仅搭载一个由 RV1126B 主控输出的声卡。
可通过串口调试或 SSH 调试访问开发板终端。执行 aplay 命令查看声卡相关详细信息。如下所示。
arecord -l
声卡资源介绍
5.1.1 硬件接口
硬件接口位置如下。

硬件接口
5.2 声卡控制
在 Linux 系统中,应用层调用声卡时通常使用 ALSA(aplay、arecord、amixer)框架。本文仅说明与本开发板平台相关的部分。如果想进一步了解 ALSA 的使用方法,可以参考开发者社区中的“ALSA 使用概述”。
5.2.1 音频录制
音频录制使用 arecord 命令。
arecord -D plughw:0,0 -c 2 -r 44100 -f S16_LE 1234567.wav使用【Ctrl+C】组合键结束录音。
💡 注意:录制单声道(-c 1)音频时,-f 仅支持【S16_LE】和【S24_LE】两种采样位深。
5.2.2 录音音量调整
Card0 录音音量【值】的【范围】为 0 到 31。
amixer -c 0 cset name='ACodec_LP PGA Gain Volume' 0 ##将 Card0 输出音量【值】设置为 0amixer -c 0 cset name='ACodec_LP PGA Gain Volume' 16 ##将 Card0 输出音量【值】设置为 16amixer -c 0 cset name='ACodec_LP PGA Gain Volume' 31 ##将 Card0 输出音量【值】设置为 31💡 注意:这里的【值】是与增益正相关的变量,但既不是增益本身,也不是音量百分比。调整该值时音量变化是【非线性的】。
5.2.3 录音命令详情
以 cd 格式录音(使用【Ctrl+C】组合键结束录音):
arecord -D hw:0,0 -f cd 1234567.wav指定 cd 录音格式等同于固定通道(立体声/2 通道)、采样率(44100Hz)以及采样位深(S16_LE)。
指定录音时长:
arecord -D hw:0,0 -f cd -d 10 1234567.wav-d 10:表示将录音时间指定为 10 秒,随后自动结束录音。 关于 arecord 更多使用方法,可参考:
arecord -h |