第3章:SDK包解析:海思SDK目录结构、osdrv与drv目录功能、sample与mpp库说明

拿到海思的SDK包,第一件事是什么?

别急着编译。我个人的习惯是,先泡杯茶,把目录结构捋一遍。这就像拿到一张新地图,你得先看懂图例,才知道往哪走。

海思的SDK包,说实话,刚开始看会觉得有点乱。目录多,命名也怪。但只要你摸清了它的套路,后面做裁剪和优化就会顺手很多。今天我就带你把这锅「大杂烩」拆开看看。

3.1 SDK顶层目录结构

解压完SDK后,你会看到类似这样的顶层目录:

Hi3516EV200_SDK_V1.0.0.0/
├── osdrv/          # 操作系统驱动层
├── drv/            # 硬件驱动层
├── mpp/            # 媒体处理平台
├── sample/         # 示例代码
├── tools/          # 工具链与烧录工具
├── doc/            # 文档
└── scripts/        # 编译脚本

嗯,这里要注意:不同芯片型号的SDK,目录名可能略有差异。比如有的叫Hi3516DV300_SDK_V2.0,但核心结构基本一致。

我最早接触海思SDK时,犯过一个低级错误——直接在mpp/目录下改代码,结果发现编译不过。后来才明白,mpp/里的源码是只读的,真正要改的是osdrv/drv/里的东西。这个坑,我先帮你踩了。

3.2 osdrv目录:系统的「骨架」

osdrv/全称是OS Driver,说白了就是操作系统相关的驱动层。它负责把Linux内核和你的硬件板子「粘」在一起。

来看看它的内部结构:

osdrv/
├── kernel/         # Linux内核源码(海思补丁版)
├── uboot/          # U-Boot启动引导
├── rootfs/         # 根文件系统
├── pub/            # 编译产物与工具
└── Makefile        # 一键编译脚本

kernel/:这里放的是海思打过补丁的Linux内核。为什么是补丁版?因为海思芯片有很多私有IP,比如视频编解码模块、ISP图像处理单元,这些在主线内核里是没有的。海思会提供一份.patch文件,你把它打到标准内核上,才能驱动这些硬件。

uboot/:启动引导程序。我建议你重点关注uboot/include/configs/下的头文件,板级配置都在那里。比如DDR大小、Flash分区表、网络参数等。我在项目里就遇到过因为分区表配错,导致系统启动后挂载不了文件系统的尴尬事。

rootfs/:根文件系统。海思默认给的是一个Busybox做的迷你文件系统,大概只有几兆。但说实话,这个默认配置很「糙」——很多你用不到的命令也编译进去了,比如telnetdftpd。做产品时,这些都得砍掉。

我的经验:裁剪系统时,osdrv/rootfs/是重点关照对象。我曾经把一个4MB的根文件系统砍到1.2MB,就靠删掉那些用不上的Busybox命令和库文件。省下来的空间,全给了视频缓存。

3.3 drv目录:硬件的「驱动程序仓库」

drv/目录和osdrv/容易搞混。我简单解释一下:

  • osdrv/:管的是操作系统层面的驱动,比如内核、文件系统、启动引导。
  • drv/:管的是具体硬件外设的驱动,比如GPIO、I2C、SPI、UART、PWM等。

看个例子:

drv/
├── gpio/           # GPIO驱动
├── i2c/            # I2C总线驱动
├── spi/            # SPI总线驱动
├── uart/           # 串口驱动
├── pwm/            # PWM输出驱动
├── watchdog/       # 看门狗驱动
└── Makefile        # 编译脚本

每个子目录里,通常包含一个.ko文件(内核模块)和对应的测试程序。比如gpio/里会有hi_gpio.kotest_gpio

为什么要单独搞个drv/目录?

因为海思芯片的外设驱动,很多是作为内核模块加载的,而不是编译进内核。这样做的好处是灵活——你不需要的驱动就不加载,省内存。坏处是,你得自己写加载脚本。

注意:有些驱动依赖特定的加载顺序。比如I2C驱动必须在GPIO驱动之后加载,因为I2C的引脚复用了GPIO控制器。我见过有人把顺序搞反,结果I2C设备死活枚举不出来。排查了半天,最后发现是加载顺序的问题。

3.4 mpp目录:多媒体处理的核心

mpp/是海思SDK里最值钱的部分。MPP全称是Media Process Platform,它封装了视频编解码、图像处理、音频处理等硬件加速能力。

它的目录结构大致如下:

mpp/
├── include/        # 头文件(API接口)
├── lib/            # 静态库/动态库
├── component/      # 组件源码(如VENC、VDEC、VPSS)
├── sample/         # 示例程序(注意:这里也有sample)
└── Makefile        # 编译脚本

include/:这是你写应用层代码时要包含的头文件。比如mpi_venc.h(视频编码接口)、mpi_vdec.h(视频解码接口)、mpi_vi.h(视频输入接口)。

lib/:编译好的库文件。有静态库(.a)和动态库(.so)两种。做产品时,我建议用静态库。为什么?因为动态库在运行时需要加载,会增加启动时间,而且容易因为库版本不一致出问题。静态库直接编译进可执行文件,省心。

component/:这是MPP的核心源码。每个组件对应一个硬件模块:

组件名 功能 对应硬件
VENC 视频编码(H.264/H.265) 硬件编码器
VDEC 视频解码 硬件解码器
VPSS 视频处理子系统(缩放、裁剪、旋转) 图像处理单元
VI 视频输入(从Sensor采集数据) MIPI/DVP接口
VO 视频输出(显示到屏幕) HDMI/LCD接口
AI/AO 音频输入/输出 I2S接口

每个组件都提供了一套API,你只需要调用这些API,就能控制硬件做视频编解码,完全不用关心底层寄存器怎么配。说白了,MPP就是海思给你搭好的「积木」,你只管拼就行。

3.5 sample目录:最好的学习资料

sample/目录里放着各种示例程序。我强烈建议你,拿到SDK后,先跑一遍sample。

它的结构是这样的:

sample/
├── venc/           # 视频编码示例
├── vdec/           # 视频解码示例
├── vpss/           # 视频处理示例
├── vi/             # 视频输入示例
├── vo/             # 视频输出示例
├── audio/          # 音频示例
├── region/         # 区域叠加示例(OSD)
└── Makefile        # 编译脚本

每个示例都是一个独立的工程,有完整的Makefilemain.c。比如sample/venc/里,你会看到:

sample_venc.c       # 主程序
sample_comm_venc.c  # 公共函数(参数配置等)
Makefile            # 编译脚本

这些示例代码写得挺规范的。我当年入门海思平台时,就是照着sample_venc.c一行一行读下来的。读懂了它,你就知道怎么初始化MPP、怎么创建编码通道、怎么处理数据流。

避坑指南:我曾经直接拿sample代码用到产品里,结果发现内存泄漏。后来排查发现,sample里为了演示方便,有些资源申请了没释放。比如HI_MPI_VENC_CreateChn创建的编码通道,在退出时没有调用HI_MPI_VENC_DestroyChn。所以,参考sample可以,但别直接复制粘贴,一定要检查资源释放。

3.6 MPP库的两种使用方式

在实际项目中,MPP库有两种用法:

方式一:动态库加载

mpp/lib/下的.so文件放到板子的/lib目录下,然后在编译你的应用时链接动态库。优点是库可以单独升级,缺点是启动时多了一步加载过程。

# 编译时链接
arm-himix200-linux-gcc sample_venc.c -I./mpp/include -L./mpp/lib -lmpi -o sample_venc

# 运行时需要把libmpi.so拷贝到板子上
cp ./mpp/lib/libmpi.so /lib/

方式二:静态库链接

直接把.a文件链接进你的可执行程序。优点是部署简单,缺点是程序体积会变大。

# 编译时静态链接
arm-himix200-linux-gcc sample_venc.c -I./mpp/include ./mpp/lib/libmpi.a -o sample_venc

我个人更推荐静态链接。尤其是在做系统裁剪时,能少一个动态库就少一个,省得在文件系统里折腾依赖关系。

3.7 小结

好了,海思SDK的目录结构就讲到这里。总结一下:

  • osdrv/:管操作系统,内核、U-Boot、文件系统都在这里。
  • drv/:管硬件外设,GPIO、I2C这些驱动模块。
  • mpp/:管多媒体,视频编解码、图像处理的核心库。
  • sample/:管学习,最好的入门资料。

下一章,我们会深入osdrv/,手把手教你裁剪Linux内核。到时候,你会看到怎么把一颗「臃肿」的内核,瘦身成适合嵌入式设备的「精干」版本。

嗯,今天就到这里。有问题欢迎交流。