Linux 编译 SDK 自动配置脚本
OS: Ubuntu 14.04 x64
首先参考官方文档中 Hi3516EV200/Hi3516EV300/Hi3518EV300 Linux SDK 安装以及升级使用说明.pdf
找到 SDK 开发包 Hi3516EV200_SDK_V1.0.1.0.tgz
提取开发包中 Hi3516EV200_SDK_V1.0.1.0/package
目录下 osdrv.tgz
包
参考 osdrv.tgz
包中 osdrv/readme_cn.txt
下载编译所需的工具源码包
将 SDK 开发包、工具源码包放置在同一目录下下,并在目录中建立 Hi3516EV200_SDK_V1.0.1.0_installer.sh
脚本,写入如下内容
#!/bin/bash
# 替换 Shell
dpkg-reconfigure dash
# 修改开发包权限及属主
chmod 755 -R ../Hi3516EV200_SDK_V1.0.1.0_installer
chown root:root -R ../Hi3516EV200_SDK_V1.0.1.0_installer
# 安装依赖库
apt install -y libncurses5-dev make libc6 lib32z1 lib32stdc++6 zlib1g-dev \
ncurses-term libncursesw5-dev g++ u-boot-tools texinfo texlive gawk curl \
upx pngquant liblzo2-dev uuid-dev pkg-config \
gperf bison
touch /etc/ld.so.preload
echo "" > /etc/ld.so.preload
# 解压交叉编译工具包并安装
tar zxvf arm-himix100-linux.tgz
chmod +x arm-himix100-linux/arm-himix100-linux.install
cd arm-himix100-linux && ./arm-himix100-linux.install && cd ..
# 配置环境变量
echo "export PATH=/opt/hisi-linux/x86-arm/arm-himix100-linux/bin:\$PATH" >> $HOME/.profile
echo "export PKG_CONFIG_PATH=\$PKG_CONFIG_PATH:/usr/lib/x86_64-linuxgnu/pkgconfig" >> $HOME/.profile
# 解压 SDK 开发包并展开
tar zxvf Hi3516EV200_SDK_V1.0.1.0.tgz
cd Hi3516EV200_SDK_V1.0.1.0 && ./sdk.unpack
# 将工具源码包移动至指定目录(参考 osdrv 下的 readme)
cp ../linux-4.9.37.tar.gz osdrv/opensource/kernel
cp ../yaffs2utils-0.2.9.tar.gz osdrv/tools/pc/mkyaffs2image
cp ../util-linux-2.31.tar.gz osdrv/tools/pc/cramfs_tool
cp ../gdb-7.9.1.tar.gz osdrv/tools/board/gdb
cp ../ncurses-6.0.tar.gz osdrv/tools/board/gdb
echo "[INFO] Use 'source ~/.bashrc' to confirm PATH changes."
完成以上步骤后目录结构应如下所示
Hi3516EV200_SDK_V1.0.1.0_installer
├─ arm-himix100-linux.tgz
├─ gdb-7.9.1.tar.gz
├─ Hi3516EV200_SDK_V1.0.1.0.tgz //开发包
├─ Hi3516EV200_SDK_V1.0.1.0_installer.sh //配置脚本
├─ linux-4.9.37.tar.gz
├─ ncurses-6.0.tar.gz
├─ util-linux-2.31.tar.gz
└─ yaffs2utils-0.2.9.tar.gz
将以上文件夹拷贝至 Ubuntu 中,并赋予脚本执行权限
chmod +x Hi3516EV200_SDK_V1.0.1.0_installer.sh
执脚本执行时会弹框询问是否替换执行脚本的 shell,选择
No
或否
即可
脚本执行成功后需要手动执行source ~/.bashrc
命令应用环境变量的更改
进入开发包中 osdrv
目录下,开始编译
cd osdrv
make all
编译使用 gcc-5
g++-5
需要在顶层 Makefile 中添加 export LANG=C
Makefile 中使用了大量
>/dev/null 2>&1
重定向标准及错误输出,导致编译或配置过程出现错误时无法定位错误位置或解决方案,如果出现编译失败的情况请找到距离报错处最近的一条编译命令并手动编译,定位错误原因
根据 U-Boot 启动输出选择相应 SPI Nor Flash 块大小的 rootfs
首次启动会输出一个随机 MAC 地址,记录下来作为固定 MAC
串口调试
在开发板 Hi3516 ERNCV200 芯片临近板边缘的一侧 预留了两个过孔,其中 靠近晶振一侧 的过孔为 TX,接 USB-TTL 板 RX;另一侧为 RX,接 USB-TTL 的 TX,地线可以直接接在 开发板四脚的过孔 上
安装 CP210x
驱动后即可通过串口进入开发板终端
Hi3516EV200 烧录系统并配置板端环境
按照以上步骤编译好 SDK 后目录结构应如下所示
Hi3516EV200_SDK_V1.0.1.0
├─ drv
├─ mpp
├─ osal
├─ osdrv
│ ├─ Makefile
│ ├─ opensource
│ ├─ pub
│ ├─ readme_cn.txt
│ ├─ readme_en.txt
│ ├─ rootfs_scripts
│ └─ tools
├─ package
├─ scripts
├─ sdk.cleanup
└─ sdk.unpack
其中 mpp
目录用于存放初始化板端环境的脚本以及运行库
osdrv
目录用于存放编译好的分区镜像及相关工具,结构如下
osdrv
├─ Makefile
├─ opensource
├─ pub
│ ├─ bin
│ ├─ hi3516ev200_spi_image_uclibc
│ │ ├─ rootfs_hi3516ev200_128k.jffs2
│ │ ├─ rootfs_hi3516ev200_256k.jffs2
│ │ ├─ rootfs_hi3516ev200_2k_128k_32M.ubifs
│ │ ├─ rootfs_hi3516ev200_2k_24bit.yaffs2
│ │ ├─ rootfs_hi3516ev200_2k_4bit.yaffs2
│ │ ├─ rootfs_hi3516ev200_4k_24bit.yaffs2
│ │ ├─ rootfs_hi3516ev200_4k_256k_50M.ubifs
│ │ ├─ rootfs_hi3516ev200_4k_4bit.yaffs2
│ │ ├─ rootfs_hi3516ev200_64k.jffs2
│ │ ├─ u-boot-hi3516ev200.bin
│ │ └─ uImage_hi3516ev200
│ └─ rootfs_uclibc.tgz
├─ readme_cn.txt
├─ readme_en.txt
├─ rootfs_scripts
└─ tools
osdrv/pub
目录下以开发板型号命名的文件夹即为相应的系统分区镜像
烧写系统
打开 HiTool 工具,通过串口连接开发板,将 传输方式 更改为串口
,使用按分区烧写
,按照如下分区配置烧写镜像
串口烧写需要进入 boot,所以开始烧写前需要将开发板下电,点击烧写按钮开始烧写后在 15 秒内上电
分区名 | 偏移 | 长度 | 文件名 | 器件类型 | 文件系统 |
---|---|---|---|---|---|
fastboot | 0 (0M) | 80000 (1M) | u-boot-hi3516ev200.bin | spi nor | none |
kernel | 100000 (1M) | 400000 (4M) | uImage_hi3516ev200 | spi nor | none |
rootfs | 500000 (5M) | b00000 (11M) | rootfs_hi3516ev200_64k.jffs2 | spi nor | none |
配置启动参数
在 HiTool 中打开终端工具,使用串口连接开发板
分区镜像烧写完成后会自动执行 reset
命令从 u-boot 重启系统,但由于没有配置启动参数导致系统只能进入 u-boot,无法进入系统
u-boot 模式下终端格式为
hisilicon #
,进入系统后格式为路径 #
在 u-boot 终端下输入如下命令,参数根据烧写的镜像以及实际需求进行配置
开发板的内存分为两种,一种是 OS Memory,一种是 MMZ Memory;开发板内存的管理机制为 从总内存中减去 OS Memory,剩下的分配给 MMZ,而开发板推送视频流等服务需要占用一定 MMZ Memory。
使用setenv
命令设置系统变量后需要在重启前使用saveenv
保存,否则所有更改在重启后都会丢失
启动参数中的文件系统类型、分区名以及分区长度需要与 HiTool 中烧写时设置一致,否则在启动时会找不到分区
Linux 系统中重启的命令为reboot
,但在 u-boot 中需要使用reset
重启
setenv bootargs 'mem=32M console=ttyAMA0,115200 root=/dev/mtdblock2 rootfstype=jffs2 rw mtdparts=hi_sfc:1M(boot),4M(kernel),11M(rootfs)'
setenv bootcmd 'sf probe 0;sf read 0x42000000 0x100000 0x400000;bootm 0x42000000'
saveenv
reset
配置 NFS 服务端
由于开发板内存不足以支持开发工作,并且无法支持板端编译,需要在服务器上开启 NFS 服务,将服务器上的指定文件夹作为网络硬盘挂载在开发板上
以下步骤需要在服务器上执行
安装 NFS Server
apt install nfs-kernel-server
添加 NFS 配置到 /etc/exports
并重启 NFS 服务
/nfs/server/path
是服务器上的 NFS 路径,后面指定允许访问的地址,可以使用*
指定地址段,括号内是文件夹操作权限
echo "/nfs/server/path 192.168.*(rw,sync,no_root_squash,no_subtree_check)" >> /etc/exports
/etc/init.d/nfs-kernel-server restart
配置完成后需要将编译好的 mpp
目录复制到 NFS 目录中,用于板端加载 sensor 驱动
开发包中 PQTools
目录下 Hi3516EV200_PQ_V1.0.1.0.tgz
解压后移动到 NFS 目录中,用于启动板端业务
配置网络
由于新版 SDK 中未包含 DHCP 服务,并且启动时会使用随机 MAC 地址,可能对路由表造成影响,这里在进入系统后手动修改网络配置
/etc/init.d/rcS
会在每次启动时被调用
以下设置中eth0
为网卡名
ifconfig eth0 down
和ifconfig eth0 up
用于重启网卡
ifconfig eth0 hw ether
设置 MAC 地址,建议查找开发板启动日志,使用任意一个随机生成的地址,避免冲突
ifconfig eth0 192.168.199.145 netmask 255.255.255.0
设置了开发板的 IP 地址和子网掩码,建议在局域网内的电脑上执行arp -a
命令或查看路由器,设置一个未被分配但在同一网段(即地址的前三组与上位机相同)的 IP 地址;无特殊情况子网掩码设置255.255.255.0
即可
route add default gw
命令用于设置默认路由,一般为路由器管理地址,特殊情况请查看路由器或交换机配置
echo "" >> /etc/init.d/rcS
echo "ifconfig eth0 down" >> /etc/init.d/rcS
echo "ifconfig eth0 up" >> /etc/init.d/rcS
echo "ifconfig eth0 hw ether E0:62:90:ED:AB:29" >> /etc/init.d/rcS
echo "ifconfig eth0 192.168.199.145 netmask 255.255.255.0" >> /etc/init.d/rcS
echo "route add default gw 192.168.199.1" >> /etc/init.d/rcS
重启开发板即可使用设置好的配置访问网络
挂载 NFS 并加载驱动
进入系统后输入以下命令挂载 NFS 并设置开机自动加载 sensor 驱动并开启 PQTool 、视频流及 telnet 服务
/nfs/server/path
是服务器上的 NFS 路径,后面是挂载在开发板上的路径,通常位于/mnt
目录下
mpp/ko/load3516ev200
脚本中-osmem
参数中配置的内存需要与系统启动参数配置一致,原理相同
Hi3516EV200_PQ_V1.0.1.0/HiIspTool.sh
脚本需要根据输入的 sensor 型号来选择配置文件,但在实际测试中发现 imx307 型号需要调用imx307_2l
配置文件才能正常调起 vi,使用时需要注意
echo "mount -t nfs -o nolock -o tcp -o rsize=32768,wsize=32768 192.168.199.244:/nfs/server/path /mnt" \
>> /etc/init.d/rcS
echo "cd /mnt/mpp/ko && ./load3516ev200 -i -sensor0 imx307" >> /etc/init.d/rcS
echo "cd /mnt/Hi3516EV200_PQ_V1.0.1.0 && ./HiIspTool.sh -a imx307_2l" >> /etc/init.d/rcS
echo "telnetd&" >> /etc/init.d/rcS
配置板端环境变量
echo "" >> /root/.profile
echo "export LD_LIBRARY_PATH=/mnt/mpp/lib:\$LD_LIBRARY_PATH" >> /root/.profile
echo "export LD_LIBRARY_PATH=/mnt/Hi3516EV200_PQ_V1.0.1.0/libs:\$LD_LIBRARY_PATH" >> /root/.profile
重启后即可使用 PQTool 以及 ITTP_Stream 进行调试
运行原理
查看设备状态
# cat /proc/umap/vpss
[VPSS] Version: [Hi3516EV200_MPP_V1.0.1.0 B050 Release], Build Time[May 9 2019, 22:51:50]
-------------------------------MODULE PARAM----------------------------------------------------------
u32VpssVbSource bOneBufferforLowDelay u32VpssSplitNodeNum bHdrSupport bNrQuickStart
0 0 3 0 0
-------------------------------VPSS GRP ATTR---------------------------------------------------------
GrpID MaxW MaxH PixFmt DRange SrcFRate DstFRate bUserCtrl NrEn NrType RefCmp MotionMode
0 1920 1080 YVU-SP420 SDR8 -1 -1 Y Y VIDEO Y Normal
-------------------------------VPSS CHN ATTR---------------------------------------------------------
GrpID PhyChnID Enable Mode Width Height SrcFRate DstFRate Depth Align MirrorEn FlipEn bBufferShare ProcMode
0 0 Y USER 1920 1080 -1 -1 0 0 N N N VIDEO
0 1 Y USER 640 360 -1 -1 1 0 N N N VIDEO
-------------------------------VPSS EXT-CHN ATTR--------------------------------------------------
GrpID ExtChnID Enable SrcChn Width Height SrcFRate DstFRate Depth Align ProcMode
-------------------------------VPSS GRP CROP INFO-----------------------------------------------------
GrpID CropEn CoorType CoorX CoorY Width Height OriW OriH TrimX TrimY TrimWid TrimHgt
0 N RAT 0 0 0 0
-------------------------------VPSS CHN CROP INFO-----------------------------------------------------
GrpID ChnID CropEn CoorType CoorX CoorY Width Height TrimX TrimY TrimWid TrimHgt
0 0 N RAT 0 0 0 0
0 1 N RAT 0 0 0 0
-------------------------------VPSS GRP PIC QUEUE----------------------------------------------------
GrpID FreeLen0 BusyLen0 Delay Backup
0 9 0 0 0
-------------------------------VPSS GRP PIC INFO---------------------------------------------------
GrpID Width Height Pixfmt Videofmt DRange Compress
0 1920 1080 YVU-SP420 LINEAR SDR8 N
-------------------------------VPSS GRP WORK STATUS---------------------------------------------------
GrpID RecvPic0 ViLost0 VdecLost0 NewDo OldDo NewUnDo OldUnDo StartFl bStart CostTm MaxCostTm
0 0 0 0 0 0 0 0 0 Y 0 0
-------------------------------VPSS CHN OUTPUT RESOLUTION--------------------------------------------
GrpID ChnID Enable Width Height Pixfmt Videofmt DRange Compress SendOk FrameRate
0 0 Y 1920 1080 YVU-SP420 LINEAR SDR8 N 2075695 30
0 1 Y 640 360 YVU-SP420 LINEAR SDR8 N 0 30
-------------------------------VPSS 3DNR X PARAM------------------------------------------------------
-------------------------------VPSS GPR0 3DNR PARAM-------------------------------------
GrpID Intf Version OptMode ISO Ref
0 NR_X VER_3 MANUAL 110 1
NRyEn_0 NRyEn_1 NRyEn_2 NRyEn_3
1 1 1 1
SFS2_0 SFS2_1 SFS2_2 SFS2_3
20 20 20 20
SFS4_0 SFS4_1 SFS4_2 SFS4_3
30 30 30 30
TFS_0 TFS0_1 TFS1_1 TFS_2
12 12 12 12
MATH0_1 MATH1_1 MATH_2 MATH_3
100 100 100 100
-------------------------------VPSS CHN LBA ATTR---------------------------------------------------------
GrpID ChnID Aspect videoX videoY videoW videoH BgColor
-------------------------------VPSS CHN ROTATE ATTR---------------------------------------------------
GrpID ChnID Rotate
-------------------------------VPSS CHN LDCV3 ATTR----------------------------------------------------
GrpID ChnID Enable viewType XOffset YOffset DistortionRatio MinRatio
-------------------------------VPSS CHN LOWDELAY ATTR-------------------------------------------------
GrpID ChnID Enable LineCnt OneBufEnable OneBufAddr
0 0 Y 1 N 0x41e26000
-------------------------------VPSS CHN BUF WRAP ATTR------------------------------------------------
GrpID ChnID Enable BufLine WrapBufSize
0 0 Y 192 552960
-------------------------------FRAME INTERRUPT ATTR---------------------------------------------------
GrpID IntType EarlyLine
0 EARLY_END 128
-------------------------------VPSS GRP PIC PTS---------------------------------------------------------
GrpID FirstPicPTS CurPicPTS
0 7597459 69258932864
-------------------------------DRV WORK STATUS--------------------------------------------------------
StartSuc0 StartSuc1 LinkInt StartErr0 NodeIdErr0 StartErr1 NodeIdErr1 BusErr
0 0 0 0 0 0 0 0
-------------------------------DRV NODE QUEUE---------------------------------------------------------
FreeNum WaitNum Busy00 Busy01 Sel0 Busy10 Busy11 Sel1 Proc
9 0 0 0 0 0 0 0 0
-------------------------------INT WORK STATUS--------------------------------------------------------
CntPerSec MaxCntPerSec CostTm MostCostTm CostTmPerSec MCostTmPerSec
0 0 0 0 0 0
从 VPSS 中获取图片
获取/设置 VPSS 通道属性
HI_S32 HI_MPI_VPSS_GetChnAttr(VPSS_GRP VpssGrp, VPSS_CHN VpssChn, VPSS_CHN_ATTR_S *pstChnAttr);
HI_S32 HI_MPI_VPSS_SetChnAttr(VPSS_GRP VpssGrp, VPSS_CHN VpssChn, const VPSS_CHN_ATTR_S *pstChnAttr);
获取/设置 VPSS 通道扩展属性
HI_S32 HI_MPI_VPSS_GetExtChnAttr(VPSS_GRP VpssGrp, VPSS_CHN VpssChn, VPSS_EXT_CHN_ATTR_S *pstExtChnAttr);
HI_S32 HI_MPI_VPSS_SetExtChnAttr(VPSS_GRP VpssGrp, VPSS_CHN VpssChn, const VPSS_EXT_CHN_ATTR_S *pstExtChnAttr);
重载 VPSS
HI_S32 VPSS_Restore(VPSS_GRP VpssGrp, VPSS_CHN VpssChn);
从 VPSS 通道获取图片
HI_S32 HI_MPI_VPSS_GetChnFrame(VPSS_GRP VpssGrp, VPSS_CHN VpssChn,
VIDEO_FRAME_INFO_S *pstVideoFrame, HI_S32 s32MilliSec);
读图片
申请内存空间
SAMPLE_COMM_IVE_CreateImage(IVE_IMAGE_S* pstImg, IVE_IMAGE_TYPE_E enType, HI_U32 u32Width, HI_U32 u32Height);
读取图片
HI_S32 SAMPLE_COMM_IVE_ReadFile(IVE_IMAGE_S* pstImg, FILE* pFp);
保存图片
申请内存空间
SAMPLE_COMM_IVE_CreateImage(IVE_IMAGE_S* pstImg, IVE_IMAGE_TYPE_E enType, HI_U32 u32Width, HI_U32 u32Height);
保存图片
HI_S32 SAMPLE_COMM_IVE_WriteFile(IVE_IMAGE_S* pstImg, FILE* pFp);
判断函数执行结果
HI_S32 s32Ret;
s32Ret = function();
SAMPLE_CHECK_EXPR_RET(HI_SUCCESS != s32Ret,s32Ret,"Error(%#x),function failed!\n",s32Ret);
边缘检测
判断任务是否完成
HI_S32 s32Ret = HI_SUCCESS;
HI_BOOL bBlock = HI_TRUE;
HI_BOOL bFinish = HI_FALSE;
IVE_HANDLE hIveHandle;
s32Ret = HI_MPI_IVE_LKOpticalFlowPyr(&hIveHandle,
pstStLk->astPrevPyr, pstStLk->astNextPyr,
&pstStLk->stPrevPts, &pstStLk->stNextPts,
&pstStLk->stStatus, &pstStLk->stErr,
&pstStLk->stLkPyrCtrl,HI_TRUE);
SAMPLE_CHECK_EXPR_RET(HI_SUCCESS != s32Ret,s32Ret,"Error(%#x),HI_MPI_IVE_LKOpticalFlowPyr failed!\n",s32Ret);
s32Ret = HI_MPI_IVE_Query(hIveHandle, &bFinish, bBlock);
while (HI_ERR_IVE_QUERY_TIMEOUT == s32Ret)
{
usleep(100);
s32Ret = HI_MPI_IVE_Query(hIveHandle, &bFinish, bBlock);
}
SAMPLE_CHECK_EXPR_RET(HI_SUCCESS != s32Ret,s32Ret,"Error(%#x),HI_MPI_IVE_Query failed!\n",s32Ret);
调试光流示例
代码调试
光流部分的代码位于板端 /mnt/mpp/sample/ive/sample/sample_ive_st_and_lc.c
文件中
编译时进入 /mnt/mmpp/sample/ive
目录下编译整个 ive 的示例,运行相应模块即可
./sample_ive_main 5
执行流程
static HI_S32 SAMPLE_IVE_St_Lk_Init(SAMPLE_IVE_ST_LK_S* pstStLk, HI_CHAR* pchSrcFileName,
HI_U32 u32Width, HI_U32 u32Height, HI_U32 u32PyrWidth, HI_U32 u32PyrHeight, HI_U8 u8MaxLevel)
// 初始化运行光流需要的结构体
在 YUV 中绘制点、线
HI_VOID swap(int *swapX, int *swapY)
{
int swapTemp = *swapX;
*swapX = *swapY;
*swapY = swapTemp;
}
HI_VOID drawPoint(unsigned char *picData,
int pointX, int pointY,
int picWidth, int picHeight,
int pointColorY)
{
int pointOffset = (pointY - 1) * picWidth + (pointX - 1);
int i, j;
for (i = ( pointY < 2 ? 0 : -1 ); i < ( pointY > (picHeight - 1) ? 1 : 2 ); i++)
{
for (j = ( pointX < 2 ? 0 : -1 ); j < ( pointX > (picWidth - 1) ? 1 : 2 ); j++)
{
picData[pointOffset + picWidth * i + j] = pointColorY;
}
}
}
HI_VOID drawLine(unsigned char *picData,
int pointStartX, int pointStartY,
int pointEndX, int pointEndY,
int picWidth, int picHeight,
int pointColorY, int pointColorU, int pointColorV)
{
if (pointStartX < 0 || pointStartX >= picWidth ||
pointStartY < 0 || pointStartY >= picHeight ||
pointEndX < 0 || pointEndX >= picWidth ||
pointEndY < 0 || pointEndY >= picHeight)
{
return;
}
int dx, dy, deltax, deltay, error, pointStartYTemp, x;
dx = abs(pointEndX - pointStartX);
dy = abs(pointEndY - pointStartY);
if (dy>dx?1:0)
{
swap(&pointStartX, &pointStartY);
swap(&pointEndX, &pointEndY);
}
if (pointStartX > pointEndX)
{
swap(&pointStartX, &pointEndX);
swap(&pointStartY, &pointEndY);
}
deltax = pointEndX - pointStartX;
deltay = abs(pointEndY - pointStartY);
error = deltax / 2;
pointStartYTemp = pointStartY;
for (x = pointStartX; x < pointEndX; x++)
{
if (dy>dx?1:0)
{
picData[x*picWidth + pointStartYTemp] = pointColorY;
picData[picWidth*picHeight + x/2*picWidth/2 + pointStartYTemp/2] = pointColorU;
picData[picWidth*picHeight + picWidth*picHeight/4 + x/2*picWidth/2 + pointStartYTemp/2] = pointColorV;
} else {
picData[pointStartYTemp*picWidth + x] = pointColorY;
picData[picWidth*picHeight + pointStartYTemp/2*picWidth/2 + x/2] = pointColorU;
picData[picWidth*picHeight + picWidth*picHeight/4 + pointStartYTemp/2*picWidth/2 + x/2] = pointColorV;
}
error -= deltay;
if (error < 0)
{
pointStartYTemp += ( pointStartY < pointEndY ? 1 : -1 );
error += deltax;
}
}
}
static HI_S32 SAMPLE_IVE_St_LkProc(SAMPLE_IVE_ST_LK_S* pstStLk)
{
HI_U32 u32FrameNum = 4989;
HI_U32 i, k;
HI_U16 u16RectNum;
HI_S32 s32Ret = HI_SUCCESS;
HI_BOOL bBlock = HI_TRUE;
HI_BOOL bFinish = HI_FALSE;
IVE_HANDLE hIveHandle;
IVE_DMA_CTRL_S stDmaCtrl;
IVE_ST_CORNER_INFO_S* pstCornerInfo = SAMPLE_COMM_IVE_CONVERT_64BIT_ADDR(IVE_ST_CORNER_INFO_S, pstStLk->stCorner.u64VirAddr);
IVE_POINT_S25Q7_S *psts25q7NextPts = SAMPLE_COMM_IVE_CONVERT_64BIT_ADDR(IVE_POINT_S25Q7_S, pstStLk->stNextPts.u64VirAddr);
IVE_POINT_S25Q7_S lkPrevPtsArray[512] = {0};
IVE_POINT_S25Q7_S lkNextPtsArray[512] = {0};
HI_U8 lkStatusArray[512] = {0};
HI_U9Q7 lkErrArray[512] = {0};
memset(&stDmaCtrl,0,sizeof(stDmaCtrl));
stDmaCtrl.enMode = IVE_DMA_MODE_DIRECT_COPY;
char *LKOutImgPath = "./data/output/stlk/stlk_out_image.yuv";
FILE *LKOutImg = fopen(LKOutImgPath, "w");
LKOutImg = fopen(LKOutImgPath, "ab+");
for (i = 0; i < u32FrameNum; i++)
{
SAMPLE_PRT("Proc frame %d\n", i);
s32Ret = SAMPLE_COMM_IVE_ReadFile(&pstStLk->stSrcYuv, pstStLk->pFpSrc);
SAMPLE_CHECK_EXPR_RET(HI_SUCCESS != s32Ret,s32Ret,"Error(%#x),Read src file failed!\n",s32Ret);
// 初始化: 坐标 跟踪状态 误差
memset(lkPrevPtsArray, 0, sizeof(IVE_POINT_S25Q7_S)*512);
memset(lkNextPtsArray, 0, sizeof(IVE_POINT_S25Q7_S)*512);
memset(lkStatusArray, 0, sizeof(HI_U8)*512);
memset(lkErrArray, 0, sizeof(HI_U9Q7)*512);
int picWidth = pstStLk->stSrcYuv.u32Width;
int picHeight = pstStLk->stSrcYuv.u32Height;
s32Ret = SAMPLE_IVE_St_Lk_DMA(&hIveHandle,&pstStLk->stSrcYuv,&pstStLk->astNextPyr[0],&stDmaCtrl,HI_FALSE);
SAMPLE_CHECK_EXPR_RET(HI_SUCCESS != s32Ret,s32Ret,"Error(%#x),SAMPLE_IVE_St_Lk_DMA failed!\n",s32Ret);
for (k = 1; k <= pstStLk->stLkPyrCtrl.u8MaxLevel; k++)
{
s32Ret = SAMPLE_IVE_St_Lk_PyrDown(pstStLk, &pstStLk->astNextPyr[k - 1], &pstStLk->astNextPyr[k]);
SAMPLE_CHECK_EXPR_RET(HI_SUCCESS != s32Ret,s32Ret,"Error(%#x),SAMPLE_IVE_St_Lk_PyrDown level %d failed!\n",s32Ret,k);
}
// 第 0 帧获取角点,每 3 帧重新获取角点
if (i==0 || i%3==0)
// if (1)
{
s32Ret = HI_MPI_IVE_STCandiCorner(&hIveHandle, &pstStLk->astNextPyr[0], &pstStLk->stStDst,
&pstStLk->stStCandiCornerCtrl, HI_TRUE);
SAMPLE_CHECK_EXPR_RET(HI_SUCCESS != s32Ret,s32Ret,"Error(%#x),HI_MPI_IVE_STCandiCorner failed!\n",s32Ret);
s32Ret = HI_MPI_IVE_Query(hIveHandle, &bFinish, bBlock);
while (HI_ERR_IVE_QUERY_TIMEOUT == s32Ret)
{
usleep(100);
s32Ret = HI_MPI_IVE_Query(hIveHandle, &bFinish, bBlock);
}
SAMPLE_CHECK_EXPR_RET(HI_SUCCESS != s32Ret,s32Ret,"Error(%#x),HI_MPI_IVE_Query failed!\n",s32Ret);
s32Ret = HI_MPI_IVE_STCorner(&pstStLk->stStDst, &pstStLk->stCorner, &pstStLk->stStCornerCtrl);
SAMPLE_CHECK_EXPR_RET(HI_SUCCESS != s32Ret,s32Ret,"Error(%#x),HI_MPI_IVE_STCorner failed!\n",s32Ret);
pstStLk->stLkPyrCtrl.u16PtsNum = pstCornerInfo->u16CornerNum;
for (k = 0; k < pstStLk->stLkPyrCtrl.u16PtsNum; k++)
{
psts25q7NextPts[k].s25q7X = (HI_S32)(pstCornerInfo->astCorner[k].u16X << 7);
psts25q7NextPts[k].s25q7Y = (HI_S32)(pstCornerInfo->astCorner[k].u16Y << 7);
printf("DEBUG =>=>=>=>=>=>=>=>=>=> frame: [%d]; point: [%d]; psts25q7NextPts[%d].s25q7X: [%d]; psts25q7NextPts[%d].s25q7Y: [%d]\n",
i, k, k, psts25q7NextPts[k].s25q7X >> 7, k, psts25q7NextPts[k].s25q7Y >> 7);
}
}
else
{
s32Ret = HI_MPI_IVE_LKOpticalFlowPyr(&hIveHandle,
pstStLk->astPrevPyr, pstStLk->astNextPyr,
&pstStLk->stPrevPts, &pstStLk->stNextPts,
&pstStLk->stStatus, &pstStLk->stErr,
&pstStLk->stLkPyrCtrl,HI_TRUE);
SAMPLE_CHECK_EXPR_RET(HI_SUCCESS != s32Ret,s32Ret,"Error(%#x),HI_MPI_IVE_LKOpticalFlowPyr failed!\n",s32Ret);
s32Ret = HI_MPI_IVE_Query(hIveHandle, &bFinish, bBlock);
while (HI_ERR_IVE_QUERY_TIMEOUT == s32Ret)
{
usleep(100);
s32Ret = HI_MPI_IVE_Query(hIveHandle, &bFinish, bBlock);
}
SAMPLE_CHECK_EXPR_RET(HI_SUCCESS != s32Ret,s32Ret,"Error(%#x),HI_MPI_IVE_Query failed!\n",s32Ret);
unsigned char *pic = (unsigned char *)malloc(picWidth * picHeight * 1.5);
memcpy(pic, SAMPLE_COMM_IVE_CONVERT_64BIT_ADDR(HI_VOID, pstStLk->stSrcYuv.au64VirAddr[0]), picWidth * picHeight * 1.5);
int prevPointsX, prevPointsY, nextPointsX, nextPointsY;
int pointNum;
int pointStatus, pointErr;
memcpy(lkPrevPtsArray, SAMPLE_COMM_IVE_CONVERT_64BIT_ADDR(HI_VOID, pstStLk->stPrevPts.u64VirAddr), pstStLk->stPrevPts.u32Size);
memcpy(lkNextPtsArray, SAMPLE_COMM_IVE_CONVERT_64BIT_ADDR(HI_VOID, pstStLk->stNextPts.u64VirAddr), pstStLk->stNextPts.u32Size);
memcpy(lkStatusArray, SAMPLE_COMM_IVE_CONVERT_64BIT_ADDR(HI_VOID, pstStLk->stStatus.u64VirAddr), pstStLk->stStatus.u32Size);
memcpy(lkErrArray, SAMPLE_COMM_IVE_CONVERT_64BIT_ADDR(HI_VOID, pstStLk->stErr.u64VirAddr), pstStLk->stErr.u32Size);
for (pointNum = 0; pointNum < MAX_POINT_NUM; pointNum++)
{
pointStatus = lkStatusArray[pointNum];
pointErr = lkErrArray[pointNum] >> 7;
prevPointsX = lkPrevPtsArray[pointNum].s25q7X >> 7;
prevPointsY = lkPrevPtsArray[pointNum].s25q7Y >> 7;
nextPointsX = lkNextPtsArray[pointNum].s25q7X >> 7;
nextPointsY = lkNextPtsArray[pointNum].s25q7Y >> 7;
if (pointStatus > 1 || prevPointsX < 1 || prevPointsY < 1 || nextPointsX < 1 || nextPointsY < 1)
break;
printf("DEBUG =>=>=>=>=>=>=>=>=>=> frame: [%d]; point: [%d]; stStatus: [%d]; stErr: [%d]; stPrevPts: [%d], [%d]; stNextPts: [%d], [%d]\n",
i, pointNum, pointStatus, pointErr,
prevPointsX, prevPointsY, nextPointsX, nextPointsY);
// if (pointStatus == 1 && pointErr != 0)
if(pointStatus == 1)
{
// drawPoint(pic, nextPointsX, nextPointsY, picWidth, picHeight, pointErr==0?0:100);
drawPoint(pic, nextPointsX, nextPointsY, picWidth, picHeight, 0);
// drawLine(pic, prevPointsX, prevPointsY, nextPointsX, nextPointsY, picWidth, picHeight, 0, 0, 0);
drawLine(pic, nextPointsX, nextPointsY, nextPointsX + 2, nextPointsY + 2, picWidth, picHeight, 0, 0, 0);
}
}
fwrite(pic, 1, picWidth * picHeight * 1.5, LKOutImg);
free(pic);
u16RectNum = 0;
for (k = 0; k < pstStLk->stLkPyrCtrl.u16PtsNum; k++)
{
if(! (SAMPLE_COMM_IVE_CONVERT_64BIT_ADDR(HI_U8,pstStLk->stStatus.u64VirAddr))[k])
{
continue;
}
psts25q7NextPts[u16RectNum].s25q7X = psts25q7NextPts[k].s25q7X;
psts25q7NextPts[u16RectNum].s25q7Y = psts25q7NextPts[k].s25q7Y;
u16RectNum++;
}
pstStLk->stLkPyrCtrl.u16PtsNum = u16RectNum;
}
memcpy(SAMPLE_COMM_IVE_CONVERT_64BIT_ADDR(HI_VOID,pstStLk->stPrevPts.u64VirAddr),
SAMPLE_COMM_IVE_CONVERT_64BIT_ADDR(HI_VOID,pstStLk->stNextPts.u64VirAddr),
sizeof(IVE_POINT_S25Q7_S) * pstStLk->stLkPyrCtrl.u16PtsNum);
SAMPLE_IVE_St_Lk_CopyPyr(pstStLk->astNextPyr, pstStLk->astPrevPyr, pstStLk->stLkPyrCtrl.u8MaxLevel);
}
return s32Ret;
}
测试结果
图片中黑色角点:HI_MPI_IVE_LKOpticalFlowPyr
输出 pstStatus
为 1
(跟踪成功)并且输出 pstErr
为 0
(相似度误差为 0
)
灰色角点:pstStatus
为 1
并且输出 pstErr
大于 0
绿色直线:角点从上一帧图片跟踪到当前帧的路径
补充(光流部分)
IVE 中的光流主要分为三部分
HI_MPI_IVE_STCandiCorner
检测图片中所有角点,此时将所有角点绘制在 1 张图片上,数量不确定HI_MPI_IVE_STCorner
将上一步图像中所有角点读出,按照规则进行过滤(两角点间的距离),此时角点数量 <= 500HI_MPI_IVE_LKOpticalFlowPyr
将筛选后的角点作为光流中 下一帧图像角点 的初值,开始跟踪
在该流程中,只使用第 1 帧(或指定帧)图像检测角点,然后将过滤后的角点应用于第 2 帧的光流中。
- 在实际测试中发现获取角点会“消耗”掉 1 帧图像,原因可能是此时的角点并非完全准确(可能包含被判断为跟踪失败的无效角点)。
- 测试[1]方法:将流程修改为在每 1 帧图像上都检测角点再跑光流
- 测试[1]结果:每一帧的角点都与上一帧相差较大,并且多为无意义的角点
- 测试[1]序列:
stlk_out_imag_all_every.yuv
- 测试[2]方法:将流程修改为在第 1 帧和每 3 帧图像上获取角点再跑光流,其他帧只运行光流
- 测试[2]结果:相较测试[1]情况有所好转,但无意义角点依然居多,每 3 帧的角点相差仍然很大
- 测试[2]序列:
stlk_out_image_all_3f.yuv
- 进行对照测试发现检测的角点比较稳定,但抽帧后帧率需要调整
- 测试[3]方法:流程修改为将第 1 帧和每 3 帧用于获取角点,其余帧用于运行光流
- 测试[3]结果:大多数角点跟踪正常,极少部分出现不稳定的情况
- 测试[3]序列:
stlk_out_image_3f.yuv
- 调整帧率:例如原视频
从 VPSS 取视频帧
static HI_S32 VPSS_Restore(VPSS_GRP VpssGrp, VPSS_CHN VpssChn)
{
HI_S32 s32Ret = HI_FAILURE;
VPSS_CHN_ATTR_S stChnAttr;
VPSS_EXT_CHN_ATTR_S stExtChnAttr;
if (VB_INVALID_POOLID != stFrame.u32PoolId)
{
s32Ret = HI_MPI_VPSS_ReleaseChnFrame(VpssGrp, VpssChn, &stFrame);
if (HI_SUCCESS != s32Ret)
{
printf("Release Chn Frame error\n");
}
stFrame.u32PoolId = VB_INVALID_POOLID;
}
if (-1 != hHandle)
{
HI_MPI_VGS_CancelJob(hHandle);
hHandle = -1;
}
if (HI_NULL != stMem.pVirAddr)
{
HI_MPI_SYS_Munmap((HI_VOID *)stMem.pVirAddr, u32BlkSize);
stMem.u64PhyAddr = HI_NULL;
}
if (VB_INVALID_POOLID != stMem.hPool)
{
HI_MPI_VB_ReleaseBlock(stMem.hBlock);
stMem.hPool = VB_INVALID_POOLID;
}
if (VB_INVALID_POOLID != hPool)
{
HI_MPI_VB_DestroyPool(hPool);
hPool = VB_INVALID_POOLID;
}
if (HI_NULL != pUserPageAddr[0])
{
HI_MPI_SYS_Munmap(pUserPageAddr[0], u32Size);
pUserPageAddr[0] = HI_NULL;
}
if (u32VpssDepthFlag)
{
if (VpssChn >= VPSS_CHN3)
{
s32Ret = HI_MPI_VPSS_GetExtChnAttr(VpssGrp, VpssChn, &stExtChnAttr);
}
else
{
s32Ret = HI_MPI_VPSS_GetChnAttr(VpssGrp, VpssChn, &stChnAttr);
}
if (s32Ret != HI_SUCCESS)
{
printf("get chn attr error\n");
}
if (VpssChn >= VPSS_CHN3)
{
stExtChnAttr.u32Depth = u32OrigDepth;
s32Ret = HI_MPI_VPSS_SetExtChnAttr(VpssGrp, VpssChn, &stExtChnAttr);
}
else
{
stChnAttr.u32Depth = u32OrigDepth;
s32Ret = HI_MPI_VPSS_SetChnAttr(VpssGrp, VpssChn, &stChnAttr);
}
if (s32Ret != HI_SUCCESS)
{
printf("set depth error\n");
}
u32VpssDepthFlag = 0;
}
return HI_SUCCESS;
}
HI_VOID STREAM_TEST()
{
HI_U32 u32Depth = 2;
HI_S32 s32MilliSec = -1;
HI_S32 s32Times = 10;
HI_S32 s32Ret;
VPSS_CHN_ATTR_S stChnAttr;
VPSS_EXT_CHN_ATTR_S stExtChnAttr;
HI_U32 u32Cnt = 0;
VPSS_GRP Grp = 0;
VPSS_CHN Chn = 1;
SAMPLE_COMM_IVE_CreateImage(&frameImg, IVE_IMAGE_TYPE_YUV420SP, u32FrameWidth, u32FrameHeight);
fopen("./data/output/stream/stream_output.yuv", "wb");
outFrameFile = fopen("./data/output/stream/stream_output.yuv", "ab+");
if (Chn >= VPSS_CHN3)
{
s32Ret = HI_MPI_VPSS_GetExtChnAttr(Grp, Chn, &stExtChnAttr);
u32OrigDepth = stExtChnAttr.u32Depth;
}
else
{
s32Ret = HI_MPI_VPSS_GetChnAttr(Grp, Chn, &stChnAttr);
u32OrigDepth = stChnAttr.u32Depth;
}
if (s32Ret != HI_SUCCESS)
{
printf("get chn attr error\n");
return;
}
if (Chn >= VPSS_CHN3)
{
stExtChnAttr.u32Depth = u32Depth;
stExtChnAttr.u32Width = u32FrameWidth;
stExtChnAttr.u32Height = u32FrameHeight;
stExtChnAttr.enPixelFormat = PIXEL_FORMAT_YVU_SEMIPLANAR_420;
s32Ret = HI_MPI_VPSS_SetExtChnAttr(Grp, Chn, &stExtChnAttr);
}
else
{
stChnAttr.u32Depth = u32Depth;
stChnAttr.u32Width = u32FrameWidth;
stChnAttr.u32Height = u32FrameHeight;
stChnAttr.enPixelFormat = PIXEL_FORMAT_YVU_SEMIPLANAR_420;
s32Ret = HI_MPI_VPSS_SetChnAttr(Grp, Chn, &stChnAttr);
}
if (s32Ret != HI_SUCCESS)
{
printf("set depth error\n");
VPSS_Restore(Grp, Chn);
return;
}
u32VpssDepthFlag = 1;
memset(&stFrame, 0, sizeof(stFrame));
stFrame.u32PoolId = VB_INVALID_POOLID;
stFrame.stVFrame.u32Width = u32FrameWidth;
stFrame.stVFrame.u32Height = u32FrameHeight;
while (HI_MPI_VPSS_GetChnFrame(Grp, Chn, &stFrame, s32MilliSec) != HI_SUCCESS)
{
s32Times--;
if (0 >= s32Times)
{
s32Ret = HI_MPI_VPSS_GetChnFrame(Grp, Chn, &stFrame, s32MilliSec);
SAMPLE_PRT("HI_MPI_VPSS_GetChnFrame Error(%#x)\n", s32Ret);
printf("get frame error for 10 times,now exit\n");
VPSS_Restore(Grp, Chn);
return;
}
usleep(40000);
}
if (VIDEO_FORMAT_LINEAR != stFrame.stVFrame.enVideoFormat)
{
printf("only support linear frame dump\n");
HI_MPI_VPSS_ReleaseChnFrame(Grp, Chn, &stFrame);
stFrame.u32PoolId = VB_INVALID_POOLID;
return;
}
fflush(stdout);
s32Ret = HI_MPI_VPSS_ReleaseChnFrame(Grp, Chn, &stFrame);
if (HI_SUCCESS != s32Ret)
{
printf("Release frame error ,now exit\n");
VPSS_Restore(Grp, Chn);
return;
}
stFrame.u32PoolId = VB_INVALID_POOLID;
while (u32Cnt < 100)
// while (1)
{
u32Cnt++;
if (HI_MPI_VPSS_GetChnFrame(Grp, Chn, &stFrame, s32MilliSec) != HI_SUCCESS)
{
printf("Get frame fail\n");
usleep(1000);
continue;
}
if ((COMPRESS_MODE_NONE != stFrame.stVFrame.enCompressMode))
{
printf("Do not support decompress\n");
HI_MPI_VPSS_ReleaseChnFrame(Grp, Chn, &stFrame);
VPSS_Restore(Grp, Chn);
return;
}
if (DYNAMIC_RANGE_SDR8 == stFrame.stVFrame.enDynamicRange)
{
sample_yuv_8bit_dump(&stFrame.stVFrame);
printf("DEBUG =>=>=>=>=>=>=>=> 8bit Width: [%d]; Height: [%d]\n", stFrame.stVFrame.u32Width, stFrame.stVFrame.u32Height);
if (u32Cnt != 1)
{
drawLine((void *)(long)frameImg.au64VirAddr[0], u32Cnt*4, u32Cnt*4, 1920 - u32Cnt*4, 1080 - u32Cnt*4, u32FrameWidth, u32FrameHeight, 0, 0, 0);
SAMPLE_COMM_IVE_WriteFile(&frameImg, outFrameFile);
}
}
printf("Get frame %d\n", u32Cnt);
s32Ret = HI_MPI_VPSS_ReleaseChnFrame(Grp, Chn, &stFrame);
if (HI_SUCCESS != s32Ret)
{
printf("Release frame error ,now exit\n");
VPSS_Restore(Grp, Chn);
return;
}
stFrame.u32PoolId = VB_INVALID_POOLID;
}
fclose(outFrameFile);
VPSS_Restore(Grp, Chn);
return;
}
内存管理
测试条件: 开机自动加载 mpp 相关库,不开启推流服务
OS Memory
Mem: 15140K used, 10648K free
实际测试中发现在程序中创建的 OS 内存空间不会随着程序结束自动释放
运行一段包含创建 OS 内存空间的示例后等待其执行完成,通过 top 观察到程序执行前后 OS Memory 均为如下数值
Mem: 19204K used, 6584K free
使用 fopen
打开文件占用 OS Memory
MMZ Memory
测试方法:
使用 SAMPLE_COMM_IVE_CreateImage
函数创建一张 1920*1080 YUV420SP 图片
测试结果:
MMZ 内存在程序结束后自动释放,或调用 IVE_MMZ_FREE
函数手动释放
创建图片过程中调用了 HI_MPI_SYS_MmzAlloc
函数,原型如下
HI_S32 HI_MPI_SYS_MmzAlloc(HI_U64 *pu64PhyAddr, void **ppVirAddr, const HI_CHAR *strMmb, const HI_CHAR *strZone, HI_U32 u32Len)
四个参数分别为: 内存物理地址指针输出,指向内存虚拟地址指针的指针输出,MMB 名称的字符串指针输入,MMZ zone 名称的字符串指针输入,要申请的内存块大小输入
该函数根据内存长度申请用户态 MMZ 内存并返回内存地址,具体现象可通过如下命令查看
watch -n 1 cat /proc/media-mem
watch -n 1 [command]
代表每 1 秒执行一次命令
创建图片后可以看到输出中增加了一行信息
可以调用
usleep
函数暂停程序,方便观察
|-MMB: phys(0x42226000, 0x4251DFFF), kvirt=0x00000000, flags=0x00000000, length=3040KB, name=""
其中 phys
表示内存的起始和结束地址,length
表示内存长度,name
为该块内存的名称
这里创建 1920 \times 1080 YUV420SP
格式图片,实际需要的空间为 1920 \times 1080 \times 1.5 = 3037.5~\text{KB},由于板端要求内存 4K 对齐,实际占用 3040 KB
内存空间
更正
该部分对开发板中某一模块进行了针对性的测试,得出了与预计不符的结论或验证了可能性,记录测试方法、过程与结果
内存部分
Hi3516EV200
开发板中共有 64M DDR 内存可供使用,主要分为两部分
- OS Memory
- 由 Linux 操作系统管理
- MMZ Memory
- 由 osal 模块管理,供 MPP(媒体软件处理平台) 单独使用
|---------|----------------| 0x40000000
| 32M | Linux OS |
| ------- | -------------- | 0x42000000
| 32M | MMZ |
| ------- | -------------- | 0x48000000
MMZ Memory 起始地址的计算方式为
0x40000000 + (HEX)(os_mem_size * 1024 * 1024)
例如为 OS 分配了 32M 内存后的 MMZ Memory 起始地址为0x40000000 + (HEX)(32 * 1024 * 1024) = 0x42000000
0x40000000
为 Kernel 的起始地址
其中 OS Memory 需要通过 bootargs 进行设置,并且需要修改 bootcmd 中的 MMZ 内存起始地址
在系统引导阶段按任意键进入 boot 命令行,boot 命令行中提示符为 hisilicon #
printenv
# 显示已配置的变量信息,后面的配置基于该命令输出进行修改
setenv bootargs "mem=32M console=ttyAMA0,115200 root=/dev/mtdblock2 rootfstype=jffs2 rw mtdparts=hi_sfc:1M(boot),4M(kernel),11M(rootfs)"
# 设置 bootargs 变量,mem 为 OS Memory
setenv bootcmd "sf probe 0;sf read 0x42000000 0x100000 0x400000;bootm 0x42000000"
# 修改 sf read 后面的地址以及 bootm 地址为 MMZ 起始地址
saveenv
# 将修改后的设置保存到系统中
printenv
# 确认更改生效
reset
# 重启系统
MMZ Memory 由 mpp/ko/hi_osal.ko
管理,在 mpp/ko/load3516ev200
脚本中第 89
行有如下命令:
insmod hi_osal.ko anony=1 mmz_allocator=hisi mmz=anonymous,0,$mmz_start,$mmz_size || report_error
insmod
命令用于向 Kernel 中载入模块,后面的参数是在加载模块时输入的参数,其中 调用了 mmz_start
和 mmz_size
两个变量用于设置,相关变量的定义位于第 17
行
# DDR start:0x40000000, kernel start:0x40000000, OS(32M); MMZ start:0x42000000
mem_total=64 # 64M, total mem
mem_start=0x40000000 # phy mem start
os_mem_size=32 # 32M, os mem
mmz_start=0x42000000; # mmz start addr
mmz_size=32M; # 32M, mmz size
在以上定义中 mem_total
mem_start
以及 os_mem_size
仅用于打印日志,实际配置时按照 bootargs 和 bootcmd 进行设置即可
OS Memory 与 MMZ Memory 之和需要小于或等于 mem_total
注意:以下部分内容来自 HiMPP V4.0 媒体处理软件 FAQ.pdf
1.2.1 OS 保留内存和线程栈大小调整
,在实际测试中发现并未达到预期效果(或测试方法不正确)
在 /etc/profile
中加入下面两行命令即可修改保留内存为 4M
echo 2 >/proc/sys/kernel/randomize_va_space
echo 4096 >/proc/sys/vm/min_free_kbytes
randomize_va_space
用于设置 ASLR(地址随机化) 的状态
0 - 关闭
1 - 将 mmap 的基址,stack 和 vdso 的内存页随机化
2 - 在 1 的基础上增加 heap(栈) 的随机化
min_free_kbytes
用于调整系统保留的最低空闲内存
该变量会在系统初始化时根据内存大小计算一个默认值,计算规则如下
\text{min\_free\_kbytes} = \sqrt{\text{lowmem\_kbytes} \times 16}
其中lowmem_kbytes
可以认为是系统内存大小
注意:以上部分内容来自 HiMPP V4.0 媒体处理软件 FAQ.pdf
1.2.1 OS 保留内存和线程栈大小调整
,在实际测试中发现并未达到预期效果(或测试方法不正确)
硬件部分
输出模式: VO -> 8bit LCD
Hi3516EV200 SoC 手册中说明了芯片本身支持 LCD 输出,并且提供了 LCD_CLK
LCD_DE
LCD_HS
LCD_VS
以及 LCD_DATA0 - LCD_DATA7
12 个引脚用于 LCD 输出,但实际在板端并未设计相应的硬件接口,导致无法外接屏幕
推流部分
由于板端 VO 没有接入硬件设备,在该开发板中只能使用 RTSP 协议进行推流查看实时图像,启动网络视频流需要建立 web server,该项目位于 Hi3516EV200_PQ_V1.0.1.0
目录下,使用 ./ittb_stream imx307_2l
或 ./HiIspTool.sh -s imx307_2l
即可打开 web server 并建立一个小型推流服务器
按照预先配置的 32M OS Memory,其中系统本身及加载 sensor 驱动需要占用 12M 内存,启动该服务需要占用大概 5M 内存,系统保留内存约 8M (可调整,此时剩余可用内存大约 5M 左右,根据系统运行状态上下浮动约 1M