📄 skyeye硬件模拟平台,第三部分 硬件仿真实现之五.htm
字号:
<P><A name=IDAIDQTB><B>图 0-2 SkyEye中基于ep7312的LCD模拟流程图</B></A><BR><IMG
height=325 alt="图 0-2 SkyEye中基于ep7312的LCD模拟流程图"
src="SkyEye硬件模拟平台,第三部分 硬件仿真实现之五.files/image005.gif" width=559 border=0
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"></P>
<P>LCD模拟模块的实现先后采用了两种方案,在第一种方案中,在SkyEye的内存模拟模块中,在每一次的写内存操作之后判断其地址是否属于LCD显示内存的地址范围,如果在该范围之内则调用LCD模拟模块中的GTK+画点函数gdk_draw_point(),根据由像素值查找彩色查找表CLUT得到的RGB值(对于真彩色,颜色深度为16,24,32时,RGB值可以直接由像素值得到),在模拟屏幕窗口的相应位置画一个相应灰度或颜色的点。</P>
<P>该方案的优点在于实现起来简单,且模拟了真实的LCD最基本的画点动作,对于图像随时间流逝而只有小范围变化的情况具有一定的优势,因为对显存有写操作时才有画点操作,但是也有两个方面的缺点,其一,与SkyEye模拟器的内存模拟模块耦合紧密,破坏了模块间的独立性;其二,对于图像随时间流逝而大范围变化的情况,本方案效率低下,在LCD驱动程序连续的每两次写显存操作中,都要经历一个单位延迟时间,其长度等于一次地址范围的判断,一次CLUT查找及一次GTK+画点函数的调用所耗费的时间,对于一次全屏操作,以320x240x8为例,若以字节为单位写显存,则额外的时间延迟将320x240x8/8=76800倍于单位延迟时间。</P>
<P>而第二种方案则直接定时(时间间隔可调,例如设置成200ms)
调用GTK+的绘图函数gdk_draw_rgb_image()将显存中的数据一次性绘制到窗口中。该方案模拟了DMA的定时扫描方式,与真实的DMA方式不同的是,在真实的硬件上,DMA方式无须CPU参与,可与CPU并行工作,而用软件模拟的硬件无法做到这一点,只能串行地定时扫描显示内存,其时间延迟不可避免的比真实硬件大。</P>
<P>第二方案降低了LCD模拟模块与内存模拟模块之间的耦合度,其缺点是不能实时地反映显存的快速变化。如果将定时间隔设置得过大,则增大了窗口内容刷新时的闪烁;如果定时间隔设置得过小,定时扫描过于频繁地发生,对系统资源是一种浪费。</P>
<P><A name=IDAZDQTB><SPAN class=atitle3>2.
SkyEye中的LCD模拟分析</SPAN></A><BR>SkyEye的lcd仿真首先是在模拟ep7312时实现的,所以在本文分析lcd仿真时是以ep7312的lcd模块为例,对于模拟其它的开发板时添加lcd模块的方法是一样的。在SkyEye源码中的clps7110.h文件中有如下定义:</P><A
name=IDA5DQTB><B></B></A><BR>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc
border=1><TBODY>
<TR>
<TD><PRE><CODE>
//lcd控制寄存器地址
#define LCDCON 0x02c0 /* LCD Control register */
#define VBUFSIZ 0x00001fff /* Video buffer size (bits/128-1) */
#define LINELEN 0x0007e000 /* Line length (pix/16-1) */
#define LINELEN_SHIFT 13
#define PIXPSC 0x01f80000 /* Pixel prescale (526628/pixels-1) */
#define PIXPSC_SHIFT 19
#define ACPSC 0x3e000000 /* AC prescale */
#define ACPSC_SHIFT 25
//下面两个控制lcd是单色、4级灰度或16级灰度(每个像素点有几位决定灰度级)
#define GSEN 0x40000000 /* Grayscale enable (0: monochrome) */
#define GSMD 0x80000000 /* Grayscale mode (0: 2 bit, 1: 4 bit) */
//SYSCON寄存器的一位,控制lcd是否enable(SYSCON就是state->io.syscon)
#define LCDEN 0x00001000 /* LCD enable */
</CODE></PRE></TD></TR></TBODY></TABLE>
<P>在SkyEye源码中的skyeye_lcd. c文件中有如下定义:</P>
<P><CODE>#define LCD_BASE 0xC0000000 // lcd显示内存起始地址</CODE></P>
<P>在SkyEye源码中的skyeye_mach_ep7312.c文件中有:</P><A
name=IDAMEQTB><B></B></A><BR>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc
border=1><TBODY>
<TR>
<TD><PRE><CODE>
state->mach_io.lcdcon =(ARMword *)&io.lcdcon; // lcd控制寄存器
state->mach_io.lcd_is_enable =(ARMword *)&io.lcd_is_enable; // 是否打开lcd
state->mach_io.lcd_addr_begin =(ARMword *)&io.lcd_addr_begin; // lcd显示内存起始地址
state->mach_io.lcd_addr_end =(ARMword *)&io.lcd_addr_end; // lcd显示内存结束地址
</CODE></PRE></TD></TR></TBODY></TABLE>
<P>在ep7312_io_do_cycle函数中也就是每个时钟后会调用:</P>
<P>skyeye_config.mach->mach_io_do_cycle(state);<BR
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">SkyEye模拟ep7312时,会在skyeye_mach_ep7312.c中的函数ep7312_mach_init注册<BR
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">this_mach->mach_io_do_cycle
= ep7312_io_do_cycle;<BR
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">在ep7312_io_do_cycle中调用lcd_cycle,这里<BR
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">lcd_cycle(state)=gtk_main_iteration_do(FALSE);<BR
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">检查gtk窗口是否有事件需要处理,没有则立即返回。</P>
<P>ep7312_io_read_word函数中:<BR
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> 如果读LCDCON寄存器的地址<BR
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> 返回 data =
state->io.lcdcon;</P><A name=IDAGFQTB><B></B></A><BR>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc
border=1><TBODY>
<TR>
<TD><PRE><CODE>
switch (addr - 0x80000000) {
//如果用户写系统控制寄存器,让lcd的状态从关闭变为打开,则重新初始化lcd。
case SYSCON:
tmp = io.syscon;
io.syscon = data;
//chy 2004-03-11
if ((tmp & LCDEN) != (data & LCDEN)) {
ep7312_update_lcd(state);
}
break;
… …
//如果用户改写lcd控制寄存器,改变lcd的控制参数,则重新初始化lcd
case LCDCON:
tmp = io.lcdcon;
io.lcdcon = data;
//chy 2004-03-11 tmp compare with data
if ((tmp & (VBUFSIZ|LINELEN|GSEN|GSMD)) != (data & (VBUFSIZ|LINELEN|GSEN|GSMD))) {
ep7312_update_lcd(state);
}
break;
… …
}
//在应用程序改写SYSCON或LCDCON时,重新初始化LCD时被调用
static void ep7312_update_lcd(ARMul_State *state)
{
ep7312_lcd_disable(state);
if (io.syscon & LCDEN) {
ARMword lcdcon = io.lcdcon;
ARMword vbufsiz = lcdcon & VBUFSIZ;
ARMword linelen = (lcdcon & LINELEN) >> LINELEN_SHIFT;
int width, height, depth;
switch (lcdcon & (GSEN|GSMD)) {
case GSEN:
depth = 2;
break;
case GSEN|GSMD:
depth = 4;
break;
default:
depth = 1;
break;
}
width = (linelen + 1) * 16;
height = (vbufsiz + 1) * 128 / depth / width;
//以上是取得lcd的高,宽,像素位数(都从LCDCON寄存器中来)
//使用以上参数重新初始化lcd
ep7312_lcd_enable(state, width, height, depth);
}
}
</CODE></PRE></TD></TR></TBODY></TABLE>
<P><A name=IDANFQTB><SPAN class=atitle3>3.
LCD相关函数分析</SPAN></A><BR>skyeye_lcd.c中的函数:</P><A
name=IDATFQTB><B></B></A><BR>
<TABLE cellSpacing=0 cellPadding=5 width="100%" bgColor=#cccccc
border=1><TBODY>
<TR>
<TD><PRE><CODE>
//当LCDCON寄存器被改写时,调用lcd_enable重新初始化lcd仿真屏幕窗口
void lcd_enable(ARMul_State *state, int width, int height, int depth)
{
int i;
static int once = 0;
GdkColor tmpColor;
char * title;
char mode[100];
if(skyeye_config.no_lcd){
return;
}//如果不使用lcd,则返回
if (!once) {
once++;
gtk_init(&global_argc, &global_argv);
}//只在第一次运行时初始化一个gtk模拟出的lcd屏幕窗口
lcd_width = width; //lcd仿真屏幕宽度
lcd_height = height; //lcd仿真屏幕高度
lcd_depth = depth; //表示一个象素所用的bit数(决定颜色深度)
*(state->mach_io.lcd_is_enable)=1;
//根据显示模式计算lcd显示内存的结束地址
*(state->mach_io.lcd_addr_end) = *(state->mach_io.lcd_addr_begin) + (width * height * depth / 8);
printf("SKYEYE: lcd_addr_begin 0x%x,lcd_addr_end 0x%x, width %d, height %d, depth %d\n",*(state->mach_io.lcd_addr_end),
*(state->mach_io.lcd_addr_begin),width, height,depth);
//建立顶层窗口
window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
//初始化gtk窗口,包括窗口标题,窗口大小等
title="SkyEye_LcdScreen_TouchScreen ";
sprintf(mode,"%s%dx%dx%d",title,lcd_width,lcd_height,lcd_depth);
gtk_window_set_title(window, mode);
gtk_widget_set_usize(window, width, height);
//把"expose_event"和顶层窗口的信号处理器联系起来
gtk_signal_connect(GTK_OBJECT(window), "expose_event",
(GtkSignalFunc)expose_event, NULL);
gtk_widget_set_events(window, GDK_EXPOSURE_MASK);
//建立触摸屏仿真事件盒容器,这在skyeye的触摸屏仿真中会用到,此处不进行详细分析
TouchScreen = gtk_event_box_new ( );
gtk_container_add (GTK_CONTAINER (window), TouchScreen);
gtk_widget_set_events(GTK_OBJECT(TouchScreen), GDK_ENTER_NOTIFY_MASK
| GDK_LEAVE_NOTIFY_MASK
| GDK_BUTTON_PRESS_MASK
| GDK_BUTTON_RELEASE_MASK
| GDK_POINTER_MOTION_HINT_MASK);
gtk_signal_connect (GTK_OBJECT(TouchScreen), "button-press-event",
GTK_SIGNAL_FUNC (callback_button_press), NULL);
gtk_signal_connect (GTK_OBJECT(TouchScreen), "button-release-event",
GTK_SIGNAL_FUNC (callback_button_release), NULL);
gtk_signal_connect (GTK_OBJECT(TouchScreen), "motion-notify-event",
GTK_SIGNAL_FUNC (callback_motion_notify), NULL);
gtk_widget_show (TouchScreen);
gtk_widget_realize (TouchScreen);
gdk_window_set_cursor (TouchScreen->window,gdk_cursor_new (GDK_HAND2));
//建立LCD屏幕仿真绘图窗口
LCD = gtk_drawing_area_new ();
gtk_container_add (GTK_CONTAINER (TouchScreen), LCD);
//显示LCD屏幕仿真绘图窗口
gtk_widget_show (LCD);
//显示顶层窗口
gtk_widget_show(window);
colormap = gdk_window_get_colormap(LCD->window);
/*单色,4色,16色,256色都需要调色板调出RGB颜色*/
switch (lcd_depth) {
case 1:
for (i = 0; i < 2; i++){
tmpColor= color2[i];
gdk_color_alloc (colormap, &tmpColor);
gc[i] = gdk_gc_new(LCD->window);
gdk_gc_set_foreground(gc[i], &tmpColor);
}
break;
case 2:
for (i = 0; i < 4; i++){
tmpColor= color4[i];
gdk_color_alloc (colormap, &tmpColor);
gc[i] = gdk_gc_new(LCD->window);
gdk_gc_set_foreground(gc[i], &tmpColor);
}
break;
case 4:
for (i = 0; i < 16; i++){
tmpColor= color16[i];
gdk_color_alloc (colormap, &tmpColor);
gc[i] = gdk_gc_new(LCD->window);
gdk_gc_set_foreground(gc[i], &tmpColor);
}
break;
case 8:
for (i = 0; i < 256; i++){
tmpColor= color256[i];
gdk_color_alloc (colormap, &tmpColor);
gc[i] = gdk_gc_new(LCD->window);
gdk_gc_set_foreground(gc[i], &tmpColor);
}
break;
default:
break;
}
}
//对lcd屏幕仿真窗口重新初始化时,要先调用lcd_disable释放资源并关闭原来的窗口
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -