海思芯片开发的内容搜集整理

海思芯片开发

搜集整理的一些海思芯片开发资料

一、关于内存的调整

调整uboot参数设置内存的大小

setenv bootargs 'mem=512M console=ttyAMA0,115200 rw root=/dev/mtdblock2 rootfstype=jffs2 mtdparts=hi_sfc:1M(boot),4M(kernel),27M(rootfs)'
saveenv

内存的起始地址修改
通过修改mpp/ko目录下load3521d脚本中的mmz模块参数,来修改其起始地址和总大小

#DDR start:0x80000000, kernel start:0x80000000, OS(512M); MMZ
start:0x88000000
mem_total=1024 # 512M, total mem
mem_start=0x80000000 # phy mem start
os_mem_size=512# 512M, os mem
mmz_start=0xA0000000; # mmz start addr
mmz_size=512; # 512M, mmz size

地址空间的分配与使用

DDR内存管理说明
1)所有DDR内存中,一部分由操作系统管理,称为OS内存;另一部分由MMZ模块管理,供媒体业务单独使用,称为MMZ内存。
2)OS内存起始地址为0x40000000,内存大小可通过bootargs进行配置,例如第三章中的setenv bootargs 'mem=64M … ',表示分配给操作系统内存为64M,您可以根据实际情况进行调整。
3)MMZ内存由MMZ内核模块管理(mpp/ko_hi35xx目录下的mmz.ko),加载mmz模块时,通过模块参数指定其起始地址及大小,load脚本中根据osmem大小来进行计算,例如:
./load3521d -a -ad 6134 -osmem 64
您也可以通过修改mpp/ko目录下load3521d脚本中的mmz模块参数,来修改其起始地址和总大小。
4)请注意MMZ内存地址范围不能与OS内存重叠

二、Flash的划分调整

设置bootargs

setenv bootargs 'root=/dev/mtdblock2 rootfstype=jffs2 mtdparts=hi_sfc:1M(boot),4M(kernel),27M(rootfs)'
saveenv

三、GPIO使用

GPIO的设置一般为三步:
1 设置gpio端口复用、
2 设置GPIO口的方向、
3 读取或者写入GPIO值;
第一步不是每个GPIO口都是要配置的,如果你设置的GPIO端口有复用功能,那么你需要对GPIO对应复用寄存器进行配置,例如:文档中GPIO5_5是有复用功能的,0:PWM_OUT0 ; 1:GPIO5_5,所以把复用寄存器设置为1就是表示为GPIO5_5;但有些GPIO端口是没有复用功能的,例如GPIO0_0,这个就不需要复用寄存器的设置;

1、设置gpio端口复用

所谓复用,就是指一个端口能同时干很多事,但是具体你要求他干啥,是需要设置的,这就是管脚复用寄存器的作用。
举个例子,如果我们想设置GPIO5_5,需要首先找到对应的管脚复用寄存器地址;通过手册查询,如果你想让这个管脚作为io口使用,需要给复用控制寄存器muxctrl_reg54这个寄存器里写0x01;管脚复用寄存器的基地址为0x120f_0000,muxctrl_reg54的偏移地址0xD8,所以这个寄存器的地址为0x120f00D8;

通过HIS自带的himm命令输入以下即可:

himm 0x120F00D8 0x01;

2 设置GPIO口的方向

所谓方向,就是你是把io做输入用还是输出用。比如如果你的板子外接了个电池模块,你希望电池模块在没电的时候,能够给你的板子一个信号,告诉你没电了,这时你就需要将io口作为输入使用。反过来,如果你需要告诉外接的什么设备什么事情,那就要将io口做输出,设置GPIO口的方向,实际上就是写 GPIO_DIR寄存器。同样的,首先需要找地址,对于GPIO5的基地址为:0x121A_0000;GPIO_DIR的偏移地址为0x400,得到GPIO5_5的寄存器地址为0x121A_0400;然后GPIO_DIR寄存器里有8位,每一位对应一个GPIO的方向,如果你想把GPIO5_5的方向设为输出,则需要把BIT5置1;

输入命令:

himm  0x121A0400  0x20(二进制 0010 0000)

3 读取或者写入GPIO值

GPIO_DATA为GPIO数据寄存器,用来对输入或输出数据进行缓存;当配置GPIO_DIR中对应位为输出时,写入GPIO_DATA 寄存器的值将会输出到相应的管脚(注意需要配置正确的管脚复用);如果配置为输入时,将会读取相应输入管脚的值。

注意:当GPIO_DIR相应的比特配置为输入时,有效读取的结果将返回管脚的值;当配置为输出的时候,有效读取的结果将返回写入的值。GPIO_DATA 寄存器利用PADDR[9:2]实现了读写寄存器比特的屏蔽操作。该寄存器对应256个地址空间。PADDR[9:2]分别对应GPIO_DATA[7:0],当相应的bit 为高时,则可以对相应的位进行读写操作;反之,若对应bit 为低则不能进行操作。

例如:
若地址为0x3FC(0b11_1111_1100),则对GPIO_DATA[7:0]这8bit 操作全部有效。
若地址为0x200(0b10_0000_0000),则仅对GPIO_DATA[7]的操作有效。

同样的,首先需要找地址,对于GPIO5的基地址为:0x121A_0000;GPIO_DATA 的偏移地址为0x3FC,得到GPIO5_5的寄存器地址为0x121A_03FC,如果你想把GPIO5_5写入值为1时,则需要把BIT5置1;

输入命令:

himm 0x121A03FC 0x20(二进制 0010 0000)


四、解码操作

H264解码实例走的是HARD DISK->VDEC->VPSS->VO->显示器的流程,这个流程一定要熟悉牢记,代码实现都是围绕这条主线来编写的
H.264视频解码实例走的是HARDDISK->VDEC->VPSS->VO->显示器的处理流程,这个过程可以细分为八大步骤,这八大步骤在其他类型的音视频编解码样例也类似,可以说这八大步骤是使用海思HIMPP API的灵魂。下面简单介绍这个八大步骤的内容:

Step1:初始化HIMPP SYS和通用VB缓冲,包括设置缓冲区的大小,缓冲区块的数目。需要注意的是,在设置通用VB参数之前,必须确保HIMPP系统已经退出,否则设置失败。

Step2:设置通用缓冲区的公共缓冲池属性。

Step3:配置解码器,包括指定解码类型,这里是H.264解码样例,当然选PT_H264啦,然后指定视频大小、解码优先级等等。然后创建解码通道,并是能加收解码流。

Step4:配置VPSS参数,VPSS是对VDEC解码后的流进行处理,如裁剪、降噪等,MPEG解码实例从简单应用出发,仅仅按默认的方式配置VPSS。

Step5:配置VO参数,这一步也很关键,因为它指定了画面输出,包括常见的HDMI和VGA,主要是配置输出显示,图层属性设置、输出位置等信息。

Step6:绑定VDEC与VPSS,实现H264解码流程。

Step7:绑定VPSS与VO,实现H264解码流程。

Step8:推送视频流数据,这一步需要文件读写配合使用,对于H264一般做法是先从NALU层中找到视频I帧,然后将I帧推流至VDEC,紧接下来就按帧推送就好了,该注意的是H264解码必须先推送I帧,否则会视频会花屏。

代码:

/*
**函数功能:HIMPP系统初始化及配置
**HARDDISK->VDEC->VPSS->VO->显示器的处理流程
*/
HARDDISK->VDEC->VPSS->VO->显示器的处理流程
HI_S32 SAMPLE_VDEC_VdhH264(char *filename)
{
    VB_CONF_S stVbConf, stModVbConf;
    HI_S32 i, s32Ret = HI_SUCCESS;
	VdecThreadParam pstVdecSend;
    SIZE_S stSize;
    VO_PUB_ATTR_S stVoPubAttr;
    VO_VIDEO_LAYER_ATTR_S stVoLayerAttr;	
    stSize.u32Width = HD_WIDTH;
    stSize.u32Height = HD_HEIGHT;
	
    /************************************************
    step1:  HIMPP系统初始化以及通用视频缓冲池配置 
    *************************************************/
	MPP_SYS_CONF_S stSysConf = {0};
 
	memset(&stVbConf,0,sizeof(stVbConf));
	stSize.u32Width = HD_WIDTH; //指定宽度
    stSize.u32Height = HD_HEIGHT; //指定高度
	stVbConf.u32MaxPoolCnt = 1; //指定最大缓冲池数量,我们只创建一路H264解码
	stVbConf.astCommPool[0].u32BlkSize = (stSize.u32Width * stSize.u32Height* 4) >> 1; //指定缓冲池大小,经验值一般为分辨率的1.5倍左右,这里取2倍
	stVbConf.astCommPool[0].u32BlkCnt	 = 3;
	memset(stVbConf.astCommPool[0].acMmzName,0,sizeof(stVbConf.astCommPool[0].acMmzName));
	
	HI_MPI_SYS_Exit(); //设置前先去初始换HIMPP调用
    
    for(i=0;i<22;i++)
    {
         HI_MPI_VB_ExitModCommPool(i);
    }
    for(i=0; i<256; i++)
    {
         HI_MPI_VB_DestroyPool(i);
    }
    HI_MPI_VB_Exit();
	
	s32Ret = HI_MPI_VB_SetConf(&stVbConf);//配置缓冲池
	if (HI_SUCCESS != s32Ret)
	{
		hidebug("HI_MPI_VB_SetConf failed!\n");
		return HI_FAILURE;
	}
	s32Ret = HI_MPI_VB_Init();//缓冲池初始化
	if (HI_SUCCESS != s32Ret)
	{
		hidebug("HI_MPI_VB_Init failed!\n");
		return HI_FAILURE;
	}
	stSysConf.u32AlignWidth = 16;
	/*set config of mpp system*/
	s32Ret = HI_MPI_SYS_SetConf(&stSysConf);//HIMPP配置
	if (HI_SUCCESS != s32Ret)
	{
		hidebug("HI_MPI_SYS_SetConf failed!\n");
		return HI_FAILURE;
	}
	s32Ret = HI_MPI_SYS_Init();	//HIMPP系统初始化	
	if (HI_SUCCESS != s32Ret)
	{
		hidebug("HI_MPI_SYS_Init failed!\n");
		return HI_FAILURE;
	}
    /************************************************
    step2:  系统缓冲池模块初始化配置
    *************************************************/
 	memset(&stModVbConf, 0, sizeof(VB_CONF_S));
	stModVbConf.u32MaxPoolCnt = 2;
	stModVbConf.astCommPool[0].u32BlkSize = (stSize.u32Width * stSize.u32Height* 4) >> 1;;
	stModVbConf.astCommPool[0].u32BlkCnt  = 5;
	
	stModVbConf.astCommPool[1].u32BlkSize = (stSize.u32Width * stSize.u32Height* 4) >> 1;;
	stModVbConf.astCommPool[1].u32BlkCnt  = 5;
 
	HI_MPI_VB_ExitModCommPool(VB_UID_VDEC);
	HI_MPI_VB_SetModPoolConf(VB_UID_VDEC, &stModVbConf);
	HI_MPI_VB_InitModCommPool(VB_UID_VDEC);
	
 
    /************************************************
    step3:  解码器配置及初始化
    *************************************************/
	VDEC_CHN_ATTR_S stVdecChnAttr;
 
    stVdecChnAttr.enType       = PT_H264; //创建H264类型的解码器
    stVdecChnAttr.u32BufSize   = 3 * stSize.u32Width * stSize.u32Height;//指定解码缓冲区大小
    stVdecChnAttr.u32Priority  = 5; //设置解码优先级
    stVdecChnAttr.u32PicWidth  = stSize.u32Width; //解码宽高
    stVdecChnAttr.u32PicHeight =stSize.u32Height;
 
	stVdecChnAttr.stVdecVideoAttr.enMode = VIDEO_MODE_FRAME; //帧式解码模式
    stVdecChnAttr.stVdecVideoAttr.u32RefFrameNum = 2;
	stVdecChnAttr.stVdecVideoAttr.bTemporalMvpEnable = 0;
 
	HI_MPI_VDEC_SetChnVBCnt(0, 10);
	HI_MPI_VDEC_CreateChn(0, &stVdecChnAttr);
	HI_MPI_VDEC_StartRecvStream(0);
 
    /************************************************
    step4:  VPSS配置及初始化
    *************************************************/
	VPSS_GRP_PARAM_S stVpssParam = {0};
	VPSS_CHN_ATTR_S stChnAttr = {0};
	VPSS_GRP_ATTR_S stVpssGrpAttr;
	
	stVpssGrpAttr.enDieMode = VPSS_DIE_MODE_NODIE;
    stVpssGrpAttr.bIeEn     = HI_FALSE;
    stVpssGrpAttr.bDciEn    = HI_TRUE;
    stVpssGrpAttr.bNrEn     = HI_FALSE;
    stVpssGrpAttr.bHistEn   = HI_FALSE;
	stVpssGrpAttr.bEsEn	  = HI_FALSE;
    stVpssGrpAttr.enPixFmt  = PIXEL_FORMAT_YUV_SEMIPLANAR_420;//解码像素格式YUV420
    stVpssGrpAttr.u32MaxW   = stSize.u32Width;
    stVpssGrpAttr.u32MaxH   = stSize.u32Height;
 
	/*** create vpss group ***/
	s32Ret = HI_MPI_VPSS_CreateGrp(0, &stVpssGrpAttr);//创建VPSS Group,在HI3536平台一个Group有4个VPSS Channel
	if (s32Ret != HI_SUCCESS)
	{
	   	hidebug("HI_MPI_VPSS_CreateGrp failed!\n");
		return HI_FAILURE;
	}
	/*** set vpss param ***/
	s32Ret = HI_MPI_VPSS_GetGrpParam(0, &stVpssParam);//设置VPSS Group属性
	if(s32Ret != HI_SUCCESS)
	{
	   	hidebug("HI_MPI_VPSS_GetGrpParam failed!\n");
		return HI_FAILURE;
	}
	stVpssParam.u32IeStrength = 0;
	s32Ret = HI_MPI_VPSS_SetGrpParam(0, &stVpssParam);
	if(s32Ret != HI_SUCCESS)
	{
	   	hidebug("HI_MPI_VPSS_GetGrpParam failed!\n");
		return HI_FAILURE;
	}
	/*** enable vpss chn, with frame ***/
	/* Set Vpss Chn attr */
    stChnAttr.bSpEn = HI_FALSE;
    stChnAttr.bUVInvert = HI_FALSE;
    stChnAttr.bBorderEn = HI_TRUE;
    stChnAttr.stBorder.u32Color = 0xffffff;//背景色为黑色
    stChnAttr.stBorder.u32LeftWidth = 2;
    stChnAttr.stBorder.u32RightWidth = 2;
    stChnAttr.stBorder.u32TopWidth = 2;
    stChnAttr.stBorder.u32BottomWidth = 2;
 
	s32Ret = HI_MPI_VPSS_SetChnAttr(0, 0, &stChnAttr);
	if(s32Ret != HI_SUCCESS)
	{
	   	hidebug("HI_MPI_VPSS_SetChnAttr failed!\n");
		return HI_FAILURE;
	}
	s32Ret = HI_MPI_VPSS_EnableChn(0, 0);//由于只有一路H264解码,故只用Group0 及 Channel 0
	if(s32Ret != HI_SUCCESS)
	{
	   	hidebug("HI_MPI_VPSS_EnableChn failed!\n");
		return HI_FAILURE;
	}
	/*** start vpss group ***/
	s32Ret = HI_MPI_VPSS_StartGrp(0);
	if(s32Ret != HI_SUCCESS)
	{
	   	hidebug("HI_MPI_VPSS_StartGrp failed!\n");
		return HI_FAILURE;
	}
	
    /************************************************
    step5:  配置VO及初始化VO
    *************************************************/
	VO_CHN_ATTR_S stChnAttr1;
 
	stVoPubAttr.enIntfSync = VO_OUTPUT_1080P60;//VO输出模式为1080p 60帧,普通显示器的输出
    stVoPubAttr.enIntfType = VO_INTF_VGA | VO_INTF_HDMI;//启用VGA及HDMI输出
	s32Ret = HI_MPI_VO_SetPubAttr(0, &stVoPubAttr);
	if(s32Ret != HI_SUCCESS)
	{
	   	hidebug("HI_MPI_VO_SetPubAttr failed!\n");
		return HI_FAILURE;
	}
	s32Ret = HI_MPI_VO_Enable(0);
	if (s32Ret != HI_SUCCESS)
	{
	   	hidebug("HI_MPI_VO_Enable failed!\n");
		return HI_FAILURE;
	}
	//设置VO Layer显示配置,如显示位置,大小,像素类型
	stVoLayerAttr.u32DispFrmRt = 60;
	stVoLayerAttr.stDispRect.u32Width = 1920;
	stVoLayerAttr.stDispRect.u32Height = 1080;
	stVoLayerAttr.stImageSize.u32Width = stVoLayerAttr.stDispRect.u32Width;
    stVoLayerAttr.stImageSize.u32Height = stVoLayerAttr.stDispRect.u32Height;
   	stVoLayerAttr.bClusterMode = HI_FALSE;
   	stVoLayerAttr.bDoubleFrame = HI_FALSE;
    stVoLayerAttr.enPixFormat = PIXEL_FORMAT_YUV_SEMIPLANAR_420;    
	s32Ret = HI_MPI_VO_SetVideoLayerAttr(0, &stVoLayerAttr);
	if(s32Ret != HI_SUCCESS)
	{
	   	hidebug("HI_MPI_VO_SetVideoLayerAttr failed!\n");
		return HI_FAILURE;
	}
	s32Ret = HI_MPI_VO_EnableVideoLayer(0);
	if (s32Ret != HI_SUCCESS)
	{
	   	hidebug("HI_MPI_VO_EnableVideoLayer failed!\n");
		return HI_FAILURE;
	}
	/*
	if (HI_SUCCESS != SAMPLE_COMM_VO_HdmiStart(stVoPubAttr.enIntfSync))
    {
        hidebug("Start SAMPLE_COMM_VO_HdmiStart failed!\n");
    }
	*/
	//设置VO Channel显示位置,大小
	stChnAttr1.stRect.s32X		= 0;
	stChnAttr1.stRect.s32Y		= 0;
	stChnAttr1.stRect.u32Width	= 1920;
	stChnAttr1.stRect.u32Height	= 1080;
	stChnAttr1.u32Priority		= 0;
	stChnAttr1.bDeflicker		= HI_FALSE;
 
	s32Ret = HI_MPI_VO_SetChnAttr(0, 0, &stChnAttr1);
	if (s32Ret != HI_SUCCESS)
	{
		hidebug("failed with %#x!\n", s32Ret);	
	}
	s32Ret = HI_MPI_VO_EnableChn(0,0);
	if (s32Ret != HI_SUCCESS)
	{
		hidebug("failed with %#x!\n", s32Ret);
		 
	}
    /************************************************
    step6:  解码器绑定VPSS
    *************************************************/
    MPP_CHN_S stSrcChn;
   	MPP_CHN_S stDestChn;
 
  	stSrcChn.enModId = HI_ID_VDEC;
   	stSrcChn.s32DevId = 0;
    stSrcChn.s32ChnId = 0;
 
    stDestChn.enModId = HI_ID_VPSS;
    stDestChn.s32DevId = 0;
    stDestChn.s32ChnId = 0;
	s32Ret = HI_MPI_SYS_Bind(&stSrcChn, &stDestChn);	 
	if(s32Ret != HI_SUCCESS)
	{
	   	hidebug("HI_MPI_SYS_Bind failed!\n");
		return HI_FAILURE;
	}
		
    /************************************************
    step7:  VPSS绑定VO
    *************************************************/
	stSrcChn.enModId = HI_ID_VPSS;
    stSrcChn.s32DevId = 0;
    stSrcChn.s32ChnId = 0;
 
    stDestChn.enModId = HI_ID_VOU;
   	stDestChn.s32DevId = 0;
   	stDestChn.s32ChnId = 0;
	s32Ret = HI_MPI_SYS_Bind(&stSrcChn, &stDestChn);	 
	if(s32Ret != HI_SUCCESS)
	{
	    hidebug("HI_MPI_SYS_Bind failed!\n");
		return HI_FAILURE;
	}
 
    /************************************************
    step8:  发送解码流,推送H264流至VDEC
    *************************************************/    
    sprintf(pstVdecSend.cFileName,filename);
    pstVdecSend.s32MilliSec     = 0;
    pstVdecSend.s32ChnId        = 0;
    pstVdecSend.s32IntervalTime = 1;
    pstVdecSend.u64PtsInit      = 0;
    pstVdecSend.u64PtsIncrease  = 0;
    pstVdecSend.eCtrlSinal      = VDEC_CTRL_START;
    pstVdecSend.bLoopSend       = HI_TRUE;
    pstVdecSend.bManuSend       = HI_FALSE;
    pstVdecSend.enType          = PT_H264;
    pstVdecSend.s32MinBufSize   = (stVdecChnAttr.u32PicWidth *stVdecChnAttr.u32PicHeight * 3)>>1;
	pstVdecSend.s32StreamMode = VIDEO_MODE_FRAME;
	SAMPLE_COMM_VDEC_H264_SendStream(&pstVdecSend);
 
    return	s32Ret;
}
 
 
int main(int argc, char *argv[])
{ 
	if(argc != 2)
	{
		printf("Usage: h264 filename\n");
		exit(0);
	}	
    signal(SIGINT, SAMPLE_VDEC_HandleSig);
    signal(SIGTERM, SAMPLE_VDEC_HandleSig);
    SAMPLE_VDEC_VdhH264(argv[1]);
 
    return 0;
}