miniGUI 3.0.2 在Hi3535平台上的移植
miniGUI是一个*软件项目。其目标是提供一个快速、稳定、跨操作系统的图形用户界面(GUI)支持系统,尤其是基于 Linux/uClinux、eCos 以及其他传统 RTOS(如 VxWorks、ThreadX、uC/OS-II、Nucleus 等)的实时嵌入式操作系统。最近在Hi3535的平台上移植miniGUI3.0,并完成Helloworld的用例测试。这里将移植的过程记录如下。
准备工作
Hi3535SDK和开发板一套,并构建Hi3535的嵌入式开发环境(包括linux编译主机、安装交叉编译工具以及在宿主机上安装NFS调试环境等)。
从官网下载miniGUI源码以及资源包和示例包。点击打开链接
熟悉海思Hi3535的Framebuffer模块。包括海思Hi3535提供Framebuffer的标准接口以及Hi3535提供的扩展接口(包括设置colorkey,Alpha等),Hi3535的Framebuffer提供的几种工作模式(None Buffer,One Buffer,Double Buffer等)。此外要仔细研读海思Hi3535的SDK中提供关于Framebufer的指导文档以及参考代码。熟悉Hi3535初始化流程以及Framebuffer的使用流程(包括初始化以及绘制Framebuffer)。
初次编译
解压下载的源代码后,先在源代码的根路径下创建_install目录用于保存编译生成文件。运行以下命令进行配置:
./configure --prefix=[mg_path]/libminigui-3.0.12-linux/_install CC=arm-hisiv100nptl-linux-gcc --host=arm-hisiv100nptl-linux --disable-pcxvfb --disable-screensaver --disable-splash --disable-jpgsupport --enable-videoqvfb=no --enable-rtosxvfb=no --enable-pcxvfb=no
注意:[mg_path]为miniGUI 3.0源代码的存储路径,在具体运行时根据实际保存的源代码路径重新写入。
运行以下命令进行编译:
make
make install
编译成功后可在_install目录下看到编译生成的文件。包括四个文件夹,简要说明如下:
-
etc 文件夹下保存运行配置文件miniGUI.cfg,在实际调试过程中需要修改其内容,详见后续章节描述;
-
include 集成miniGUI库时需要使用到的头文件;
-
lib 包括链接库和运行库;
- share 暂未使用;
加入Hi3535的framebuffer接口文件
miniGUI中Framebuffer部分的接口主要需要完成两个结构体的初始化,即VideoBootStrap和GAL_VideoDevice。可查到上述两个结构体的定义如下:
typedef struct VideoBootStrap {
const char *name;
const char *desc;
int (*available)(void);
GAL_VideoDevice *(*create)(int devindex);
} VideoBootStrap;
结构体成员简要说明如下:
-
name为字符串变量,改字符串用于区分运行的平台,因此尽量保持唯一性。这里使用hi3535作为name。此字符串需要跟运行配置文件MiniGUI.cfg中的名称保持一致。
-
desc 描述,目前没有查到有什么限制,本例使用HI3535 Framebuffer Console。
-
available为需要填充的函数,检查framebuffer是否可用。即尝试打开设备,如果打开成功即验证可用。
-
create为需要填充的函数,函数主要完成GAL_VideoDevice变量的初始化。
GAL_VideoDevice的定义详见src/newgal/sysvideo.h文件。
主要说两点:
-
name仍然保持和VideoBootStrap中定义的一致;
- hidden 成员变量,主要保存Hi3535在后续Framebuffer中操作需要的信息,可根据需要进行定制。本例中暂定义为:
struct GAL_PrivateVideoData
{
Uint32 fd;
HIFB_BUFFER_S stCanvasBuf;
Uint32 memLen;
};
其中:
-
fd为open Framebuffer设备后的句柄;
-
stCanvasBuf为framebuffer的画布信息(请参考海思文档);
- memLen为Framebuffer分配的内存长度;
-
fd为open Framebuffer设备后的句柄;
其他为需要填充的函数。
miniGUI3.0.2中的src/newgal中包含hisi文件夹,目前有实现Hi3510/Hi3560/Hi3560a等芯片的接口。本例中添加Hi3535芯片的接口,将海思hi3535的接口文件命名为:hi3535_fbvideo.c和hi3535_fbvideo.h,完成上述两个结构体变量的初始化。首先可使用空函数完成代码。接下来修改配置文件(包括configure和makefile)来编译新增加的Hi3535的接口文件。
修改configure.in文件,增加编译Hi3535
增加如下代码:
dnl Check HI3535 video driver
CheckHI3535Video()
{
AC_ARG_ENABLE(videohi3535,
[ --enable-videohi3535 include Hi3535 Video NEWGAL engine <default=no>],
enable_video_hi3535=$enableval)
if test "x$enable_video_hi3535" = "xyes"; then
AC_DEFINE(_MGGAL_HI3535, 1,
[Define if include Hi3535 Video NEWGAL engine])
VIDEO_SUBDIRS="$VIDEO_SUBDIRS hisi"
VIDEO_DRIVERS="$VIDEO_DRIVERS hisi/libvideo_hisifbcon.la"
fi
}
增加调用上述代码:
dnl Checks NewGAL Engine.
{
CheckDummyVideo
CheckFBCON
CheckQVFB
CheckXVFB
CheckWVFB
CheckCOMMLCD
CheckShadowVideo
CheckMLShadowVideo
CheckEM85xxOSD
CheckEM85xxYUV
CheckEM86GFX
CheckSvpxxosdVideo
CheckBF533Video
CheckMB93493Video
CheckUTPMCVideo
CheckDirectFBVideo
CheckSTGFBVideo
CheckHI35XXVideo
CheckHI3560AVideo
CheckHI3535Video
CheckGDLVideo
CheckNexusVideo
CheckS3C6410Video
CheckCustomVideo
CheckSigmaVideo
CheckMStarVideo
}
修改makefile相关文件
修改src/newgal/hisi目录下的make.in文件
am__objects_1 = hi3510_fbvideo.lo gal_hi3560a.lo hi3560_fbvideo.lo hi3535_fbvideo.lo \
pix_array.lo tde.lo
上述属于修改,找到am__objects_1的变量,增加hi3535_fbvideo.lo
SRC_FILES = hi3510_fbvideo.c gal_hi3560a.c hi3560_fbvideo.c hi3535_fbvideo.c pix_array.c tde.c
HDR_FILES = hi3510_fb.h hi3560_fb.h hi3510_fbvideo.h hi3560_fbvideo.h hi3535_fbvideo.h \
hi_tde.h tde_reg.h gal_hi3560a.h
上述属于修改,找到SRC_FILES和HDR_FILES变量,分别添加hi3535_fbvideo.c和hi3535_fbvideo.h
@aaa@qq.com@aaa@qq.com @aaa@qq.com/$(DEPDIR)/aaa@qq.comaaa@qq.com
上述属于增加的行,参考现有海思芯片的类型进行添加。
修改src/newgal/hisi目录下的makefile.am
SRC_FILES = hi3510_fbvideo.c gal_hi3560a.c hi3560_fbvideo.c hi3535_fbvideo.c pix_array.c tde.c
HDR_FILES = hi3510_fb.h hi3560_fb.h hi3510_fbvideo.h hi3560_fbvideo.h hi3535_fbvideo.h \
hi_tde.h tde_reg.h gal_hi3560a.h
完成四个基础函数的填充。
static int HI3535_Available(void)
{
const char *GAL_fbdev;
int fd;
GAL_fbdev = getenv("FRAMEBUFFER");
if ( GAL_fbdev == NULL )
{
GAL_fbdev = "/dev/fb0";
}
fd = open(GAL_fbdev, O_RDWR, 0);
if ( fd < 0 )
{
fprintf(stderr, "failed to open file :%s!\n", GAL_fbdev);
return 0;
}
else
{
close(fd);
return 1;
}
}
Hi3535默认加载Framebuffer,因此可以在/dev目录下找到fb0的设备节点文件。上述函数只是尝试打开/dev/fb0文件,如果能够成功打开并获取句柄,则返回成功,否则,返回失败。
static GAL_VideoDevice *HI3535_CreateDevice (int devindex)
{
GAL_VideoDevice *this;
/* Initialize all variables that we clean on shutdown */
this = (GAL_VideoDevice *)malloc(sizeof(GAL_VideoDevice));
if ( this )
{
memset(this, 0, (sizeof *this));
this->hidden = (struct GAL_PrivateVideoData *)malloc(sizeof (struct GAL_PrivateVideoData));
this->name = "hi3535";
}
if ( (this == NULL) || (this->hidden == NULL) )
{
GAL_OutOfMemory();
if ( this )
{
free(this);
}
return(0);
}
memset(this->hidden, 0, (sizeof (struct GAL_PrivateVideoData)));
/* Set the function pointers */
this->VideoInit = HI3535_VideoInit;
this->ListModes = HI3535_ListModes;
this->SetVideoMode = HI3535_SetVideoMode;
this->ToggleFullScreen = HI3535_ToggleFullScreen;
this->SetColors = HI3535_SetColors;
this->UpdateRects = HI3535_UpdateRects;
this->VideoQuit = HI3535_VideoQuit;
#ifndef _MGRM_THREADS
this->RequestHWSurface = HI3535_RequestHWSurface;
#endif
this->AllocHWSurface = HI3535_AllocHWSurface;
this->CheckHWBlit = HI3535_CheckHWBlit;
this->FillHWRect = HI3535_FillHWRect;
this->SetHWColorKey = HI3535_SetHWColorKey;
this->SetHWAlpha = HI3535_SetHWAlpha;
this->FreeHWSurface = HI3535_FreeHWSurface;
this->free = HI3535_FBFree;
this->DeleteSurface = HI3535_DeleteSurface;
this->SetSurfaceColors = HI3535_SetSurfaceColors;
this->GetFBInfo = HI3535_GetFBInfo;
this->UpdateSurfaceRects = HI3535_UpdateSurfaceRects;
return this;
}
上述函数主要用于创建GAL_VideoDevice结构体变量,初始化并返回。并且分配hidden成员变量的空间。
extern int hi3535_board_init(void);
外部初始化函数,用于实现初始化Hi3535平台。可参考海思SDK中的sample示例。因为miniGUI运行时,并不会去完成平台初始化的工作,因此在miniGUI调用其库完成framebuffer的接口初始化之前,要借助此外部函数完成平台初始化,以确保framebuffer的初始化及相关操作不会出错。
static int HI3535_VideoInit(GAL_VideoDevice *this, GAL_PixelFormat *vformat)
{
struct fb_fix_screeninfo finfo;
struct fb_var_screeninfo vinfo;
int i;
const char *GAL_fbdev;
GAL_VideoInfo* paccel_info = &this->info;
HIFB_COLORKEY_S stColorKey;
HIFB_POINT_S stPoint;
HIFB_LAYER_INFO_S stLayerInfo = {0};
HI_BOOL bShow;
hi3535_board_init();
/* 1. open Framebuffer device overlay 0 */
GAL_fbdev = getenv("FRAMEBUFFER");
if ( GAL_fbdev == NULL )
{
GAL_fbdev = "/dev/fb0";
}
this->hidden->fd = open(GAL_fbdev, O_RDWR, 0);
if ( this->hidden->fd < 0 )
{
GAL_SetError("NEWGAL>HI3535: Unable to open %s\n", GAL_fbdev);
return -1;
}
/*2. all layer surport colorkey*/
stColorKey.bKeyEnable = HI_TRUE;
stColorKey.u32Key = 0x0;
if (ioctl(this->hidden->fd, FBIOPUT_COLORKEY_HIFB, &stColorKey) < 0)
{
GAL_SetError("NEWGAL>HI3535:FBIOPUT_COLORKEY_HIFB!\n");
return -2;
}
/* 3. set the screen original position */
stPoint.s32XPos = 0;
stPoint.s32YPos = 0;
if (ioctl(this->hidden->fd, FBIOPUT_SCREEN_ORIGIN_HIFB, &stPoint) < 0)
{
GAL_SetError("NEWGAL>HI3535:set screen original show position failed!\n");
return -3;
}
/* 4. get the variable screen info */
if(ioctl(this->hidden->fd, FBIOGET_VSCREENINFO, &vinfo) < 0)
{
GAL_SetError("NEWGAL>HI3535:GET_VSCREENINFO failed!\n");
return -4;
}
/* 5. config the default parameters of the variable screen. */
vinfo.xres_virtual = DEFAULT_SCREEN_WIDTH;
vinfo.yres_virtual = DEFAULT_SCREEN_HEIGHT;
vinfo.xres = DEFAULT_SCREEN_WIDTH;
vinfo.yres = DEFAULT_SCREEN_HEIGHT;
vinfo.transp = s_a16;
vinfo.red = s_r16;
vinfo.green = s_g16;
vinfo.blue = s_b16;
vinfo.bits_per_pixel = 16;
vinfo.activate = FB_ACTIVATE_NOW;
if (ioctl(this->hidden->fd, FBIOPUT_VSCREENINFO, &vinfo) < 0)
{
GAL_SetError("NEWGAL>HI3535: Put variable screen info failed!\n");
return -5;
}
/* 6. set one buffer mode. */
stLayerInfo.BufMode = HIFB_LAYER_BUF_ONE;
stLayerInfo.u32Mask = HIFB_LAYERMASK_BUFMODE;
if(ioctl(this->hidden->fd, FBIOPUT_LAYER_INFO, &stLayerInfo) < 0)
{
GAL_SetError("NEWGAL>HI3535: PUT_LAYER_INFO failed!\n");
return -6;
}
bShow = HI_TRUE;
if (ioctl(this->hidden->fd, FBIOPUT_SHOW_HIFB, &bShow) < 0)
{
GAL_SetError("NEWGAL>HI3535:FBIOPUT_SHOW_HIFB failed!\n");
return -7;
}
/* 7. Get the type of video hardware */
if ( ioctl(this->hidden->fd, FBIOGET_FSCREENINFO, &finfo) < 0 )
{
GAL_SetError("NEWGAL>HI3535: Couldn't get console hardware info\n");
HI3535_VideoQuit(this);
return -6;
}
/* Memory map the device, compensating for buggy PPC mmap() */
this->hidden->stCanvasBuf.stCanvas.u32PhyAddr = mmap(NULL, finfo.smem_len,PROT_READ|PROT_WRITE, MAP_SHARED, this->hidden->fd, 0);
this->hidden->stCanvasBuf.stCanvas.u32Width = vinfo.xres;
this->hidden->stCanvasBuf.stCanvas.u32Height = vinfo.yres;
this->hidden->stCanvasBuf.stCanvas.u32Pitch = vinfo.xres*2;
this->hidden->stCanvasBuf.stCanvas.enFmt = HIFB_FMT_ARGB1555;
memset((Uint16*)(this->hidden->stCanvasBuf.stCanvas.u32PhyAddr), 0x00, this->hidden->stCanvasBuf.stCanvas.u32Pitch*this->hidden->stCanvasBuf.stCanvas.u32Height);
this->hidden->memLen = finfo.smem_len;
vformat->BitsPerPixel = vinfo.bits_per_pixel;
for ( i=0; i<vinfo.red.length; ++i )
{
vformat->Rmask <<= 1;
vformat->Rmask |= (0x00000001<<vinfo.red.offset);
}
for ( i=0; i<vinfo.green.length; ++i )
{
vformat->Gmask <<= 1;
vformat->Gmask |= (0x00000001<<vinfo.green.offset);
}
for ( i=0; i<vinfo.blue.length; ++i )
{
vformat->Bmask <<= 1;
vformat->Bmask |= (0x00000001<<vinfo.blue.offset);
}
for ( i=0; i<vinfo.transp.length; ++i )
{
vformat->Amask <<= 1;
vformat->Amask |= (0x00000001<<vinfo.transp.offset);
}
paccel_info->hw_available = 1;
paccel_info->video_mem = finfo.smem_len/1024;
paccel_info->blit_fill = 1;
paccel_info->blit_hw = 1;
paccel_info->blit_hw_A = 1;
paccel_info->blit_hw_CC = 1;
return(0);
}
参考sample_hifb.c中的初始化过程实现。但在内存映射上做了修改。
static GAL_Surface *HI3535_SetVideoMode(GAL_VideoDevice *this, GAL_Surface *current, int width, int height, int bpp, Uint32 flags)
{
struct fb_fix_screeninfo finfo;
struct fb_var_screeninfo vinfo;
int i;
Uint32 Rmask;
Uint32 Gmask;
Uint32 Bmask;
Uint32 Amask;
char *surfaces_mem;
int surfaces_len;
/* Set the video mode and get the final screen format */
if ( ioctl(this->hidden->fd, FBIOGET_VSCREENINFO, &vinfo) < 0 )
{
fprintf (stderr, "Couldn't get console screen info");
return(NULL);
}
if ((vinfo.xres != width) || (vinfo.yres != height) ||(vinfo.bits_per_pixel != bpp))
{
vinfo.activate = FB_ACTIVATE_NOW;
vinfo.accel_flags = 0;
vinfo.bits_per_pixel = bpp;
vinfo.xres = width;
vinfo.xres_virtual = width;
vinfo.yres = height;
vinfo.yres_virtual = height;
vinfo.xoffset = 0;
vinfo.yoffset = 0;
vinfo.red.length = vinfo.red.offset = 0;
vinfo.green.length = vinfo.green.offset = 0;
vinfo.blue.length = vinfo.blue.offset = 0;
vinfo.transp.length = vinfo.transp.offset = 0;
if ( ioctl(this->hidden->fd, FBIOPUT_VSCREENINFO, &vinfo) < 0 )
{
GAL_SetError("NEWGAL>HI3535: Couldn't set console screen info\n");
vinfo.yres_virtual = height;
if ( ioctl(this->hidden->fd, FBIOPUT_VSCREENINFO, &vinfo) < 0 )
{
GAL_SetError("NEWGAL>HI3535: Couldn't set console screen info[2]\n");
return(NULL);
}
}
}
else
{
int maxheight;
/* Figure out how much video memory is available */
maxheight = height;
if ( vinfo.yres_virtual > maxheight )
{
vinfo.yres_virtual = maxheight;
}
}
Rmask = 0;
for ( i=0; i<vinfo.red.length; ++i )
{
Rmask <<= 1;
Rmask |= (0x00000001<<vinfo.red.offset);
}
Gmask = 0;
for ( i=0; i<vinfo.green.length; ++i )
{
Gmask <<= 1;
Gmask |= (0x00000001<<vinfo.green.offset);
}
Bmask = 0;
for ( i=0; i<vinfo.blue.length; ++i )
{
Bmask <<= 1;
Bmask |= (0x00000001<<vinfo.blue.offset);
}
Amask = 0;
for ( i=0; i<vinfo.transp.length; ++i )
{
Amask <<= 1;
Amask |= (0x00000001<<vinfo.transp.offset);
}
if (!GAL_ReallocFormat(current, vinfo.bits_per_pixel,Rmask, Gmask, Bmask, Amask) )
{
return(NULL);
}
if ( vinfo.bits_per_pixel < 8 )
{
current->format->MSBLeft = !(vinfo.red.msb_right);
}
/* Set up the new mode framebuffer */
current->flags = GAL_FULLSCREEN;
current->w = vinfo.xres;
current->h = vinfo.yres;
current->pitch = vinfo.xres*2;
current->pixels = this->hidden->stCanvasBuf.stCanvas.u32PhyAddr;
return(current);
}
最后在hi3535_fbvideo.c中包含hi3535SDK中与Framebuffer有关的头文件:hi_type.h和hifb.h,并将这两个头文件拷贝到src/newgal/hisi目录下。
完成后重新配置编译,在配置之前先运行make distclean清除之前的配置
./configure --prefix=/home/zkliu/miniGUI/libminigui-3.0.12-linux/_install CC=arm-hisiv100nptl-linux-gcc --host=arm-hisiv100nptl-linux --disable-pcxvfb --disable-screensaver --disable-splash --disable-jpgsupport --enable-videoqvfb=no --enable-rtosxvfb=no --enable-pcxvfb=no --enable-videohi3535=yes
注意增加了对hi3535的支持
然后编译全套代码:
make
make install
测试
准备资源文件
解压miniGUI的资源文件,在资源文件根目录下建立_install目录。运行一下命令配置编译:
./configure --host=arm-hisiv100nptl-linux --prefix=[mg_res]/ minigui-res-be-3.0.12 /_install
make
make install
成功编译完成后在_install目录下可得到需要的资源文件。
建立helloworld sample
在hi3535SDK中的sample目录下建立一个单独的目录,比如miniGUI。拷贝helloworld.c(该文件来自于miniGUI网站中的sample实例)文件到目录下。拷贝_install下的include目录到miniGUI目录下。拷贝_install/lib/ libminigui_ths.a文件到miniGUI/lib目录下。新建board.c文件,实现int hi3535_board_init(void);包括sys模块、VO、HDMI等跟输出相关模块的初始化。修改miniGUI目录下的makefile如下:
# Hisilicon Hi3535 sample Makefile
include ../Makefile.param
# target source
SRC := $(wildcard *.c)
OBJ := $(SRC:%.c=%.o)
TARGET := helloworld
.PHONY : clean all
all: $(TARGET)
MPI_LIBS := $(REL_LIB)/libmpi.a
MPI_LIBS += $(REL_LIB)/libhdmi.a
CFLAGS += -I./include
$(TARGET):$(OBJ)
$(CC) $(CFLAGS) -lpthread -lm -o aaa@qq.com $^ $(MPI_LIBS) $(AUDIO_LIBA) -L./lib -lminigui_ths
%.o:%.c
$(CC) -c $(CFLAGS) $^ -o aaa@qq.com
clean:
@rm -f $(TARGET)
@rm -f $(OBJ)
在sample文件夹下的makefile中增加对miniGUI文件夹的编译:
mg:
@cd miniGUI; make
mg_clean:
@cd miniGUI; make clean
至此,可对helloworld例子进行编译。
建立调试目录
将资源文件_install/share/minigui目录拷贝的nfs挂载目录,将miniGUI动态库和软连接文件拷贝到nfs挂载目录中的miniGUILibs目录(如果没有就新建)。软连接文件如果失效,使用ln –sf命令重新建立软连接。拷贝示例helloworld到nfs挂载目录。修改miniGUI.cfg文件如下:
[system]
# GAL engine and default options
gal_engine=hi3535
defaultmode=800x600-16bpp
# IAL engine
ial_engine=dummy
mdev=/dev/input/mice
mtype=IMPS2
[fbcon]
defaultmode=1024x768-16bpp
[hi3535]
defaultmode=1920x1080-16bpp
第3行修改gal_engine:
第14行增加hi3535 gal_engine定义,设置缺省分辨率。
修改鼠标资源文件定向,找到以下位置修改:
cursorpath=./minigui/res/cursor/
修改图片资源文件定向,找到以下位置修改:
respath=./minigui/res/
完成上述修改后将miniGUI.cfg拷贝到nfs挂载目录下。
修改Hi3535单板上/etc/profile文件,将miniGUI的动态库文件所在路径包含到LD_LIBRARY_PATH变量中:
LD_LIBRARY_PATH="/usr/local/lib:/usr/lib:/mnt/miniGUILibs"
运行示例:./helloworld
可在HDMI接口看到欢迎窗口如图:
参考文献
- Minigui3在海思Hi3520D/Hi3531平台上运行 https://blog.csdn.net/jhting/article/details/51059484
- HiFBAPI参考.pdf
- HiFB开发指南.pdf
源代码资源已上传,下载地址点击打开链接。