原创 在boot loader中加入启动画面及插值算法

2009-4-16 16:29 2336 5 5 分类: MCU/ 嵌入式

http://omale.blogcn.com/diary,11439067.shtml


 


最近有个朋友希望我帮忙给他的一块板子加一个启动画面。板子是用烂了的三星2410处理器。 不同之处就在于他的板子是640 x 480 VGA输出,而不是通常的240 x 320的LCD。

在Windows CE中,通常加入启动画面的方法很土,因为OS没有启动,所以没有什么BitBlt,StretchBlt等方法可以贴图,唯一的方法就是在启动的过程中,直接往显卡的Framebuffer里面写数据。这个工作一般在Boot Loader里面做,如果在OS启动的时候一般就太晚了,如果不加入人为的延时,启动画面刚一显示,就到了OS画面了。做法一般是这样,在Boot Loader里面:

BOOL OEMPlatformInit()
{
…………………………
    
    
    // Init the Display
    InitDisplay();

…………………………
}

然后是InitDisplay函数一般如下所示:
static void InitDisplay()
{
    int i = 0;
 int j = 0;
    volatile IOPreg *s2410IOP;
    volatile LCDreg *s2410LCD;

    s2410IOP = (IOPreg *)IOP_BASE;
    s2410LCD = (LCDreg *)LCD_BASE;


    // LCD port initialize.
    s2410IOP->rGPCUP  = 0xFFFFFFFF;
    s2410IOP->rGPCCON = 0xAAAAAAAA;
    s2410IOP->rGPCCON = 0xAAAAAAAA;


    s2410IOP->rGPDUP  = 0xFFFFFFFF;
    s2410IOP->rGPDCON = 0xAAAAAAAA;


    s2410IOP->rGPGCON &= ~(3 << 8);                 /* Set LCD_PWREN as output                          */
    s2410IOP->rGPGCON |=  (1 << 8);


    s2410IOP->rGPGDAT |=  (1 << 4);                 /* Backlight ON                                     */



 s2410LCD->rLCDCON1=(1<<8)|(MVAL_USED<<7)|(3<<5)|(12<<1)|0;
        // TFT LCD panel,16bpp TFT,ENVID=off
 s2410LCD->rLCDCON2=(VBPD<<24)|(LINEVAL_TFT<<14)|(VFPD<<6)|(VSPW);
 s2410LCD->rLCDCON3=(HBPD<<19)|(HOZVAL_TFT<<8)|(HFPD);
 s2410LCD->rLCDCON4=(MVAL<<8)|(HSPW);
 s2410LCD->rLCDCON5=(1<<11)|(1<<9)|(1<<8)|(1<<3)|(1<<0); //FRM5:6:5,HSYNC and VSYNC are inverted
 s2410LCD->rLCDSADDR1=((FRAMEBUF_DMA_BASE>>22)<<21)|M5D(FRAMEBUF_DMA_BASE>>1);
 s2410LCD->rLCDSADDR2=M5D( (FRAMEBUF_DMA_BASE+(LCD_XSIZE_TFT*LCD_YSIZE_TFT*2))>>1 );
 s2410LCD->rLCDSADDR3=(((LCD_XSIZE_TFT-LCD_XSIZE_TFT)/1)<<11)|(LCD_XSIZE_TFT/1);
 //s2410LCD->rLCDINTMSK|=(3); // MASK LCD Sub Interrupt
 s2410LCD->rLPCSEL&=(~7); // Disable LPC3600
 s2410LCD->rTPAL=0; // Disable Temp Palette
 
 s2410LCD->rLCDCON1 |= 1;

 
 // Display a bitmap image on the LCD...
    //memcpy((void *)FRAMEBUF_BASE, ScreenBitmap, ARRAY_SIZE_TFT_16BIT);


 // Jason : Interpolation


 EdbgOutputDebugString("+Interpolation\r\n");


 for(i = 0; i < 320 * 240; i++)
 {
  PWORD pWord = (PWORD)ScreenBitmap;
  PWORD pFrmBuf = (PWORD)FRAMEBUF_BASE;


  pFrmBuf[4 * i - 2 * (i % 320)] = pWord;
  pFrmBuf[4 * i - 2 * (i % 320) + 1] = pWord;
  pFrmBuf[4 * i - 2 * (i % 320) + 320 * 2] = pWord;
  pFrmBuf[4 * i - 2 * (i % 320) + 1 + 320 * 2] = pWord;



 }

前面初始化硬件的代码没什么好看的。
注意到memcpy((void *)FRAMEBUF_BASE, ScreenBitmap, ARRAY_SIZE_TFT_16BIT);这一句,这就是显示启动画面的关键了,直接调用memcpy函数,把一个大数组,复制到了FRAMEBUF_BASE这个地址。这个其实就是直接往显卡缓冲区里面填东西了。至于这个大数组,可以用Image2LCD等类似软件生成,也就是直接把一张位图转成C语言数组的软件,这东西在游戏开发中用的比较多。

一般情况下,这种方法都是OK的,但是现在遇到什么问题呢?

因为目标设备的显示分辨率是640 x 480 x 16位的,所以这个大数组需要多大的存储空间是可以算出来的:640 * 480 * 2 = 600KB。但是对于一般的boot loader而言,板子上不会划分这么大的空间给boot loader用,一般boot loader的空间只有256KB。(其实可以人为改,只不过这个板子用linux的loader 引导CE的loader,才产生这么多问题)。所以解决方法只能有下面两条:

1. 压缩,然后在运行时候解压缩,然后再memcpy。可以找一些最基本的通用开源压缩算法来完成。
2. 插值,弄一张小图片,把它插值到640 x 480。

两者权衡,偶还是决定采用插值的办法,这样的工作量还是小一些的。其实也就是自己把StretchBlt函数实现了。

哎,本科的时候没有开设图形学,所以也不知道插值有没有具体的公式,只能自己推导了。好在这个不是一个通用的算法,只是放大4倍的这样一个特例。一个点变四个点,还是很容易的。自己拿着纸笔比划了一阵子,终于得出个公式来,公式不好帖,直接帖代码就是下面这样:


 for(i = 0; i < 320 * 240; i++)
 {
  PWORD pWord = (PWORD)ScreenBitmap;
  PWORD pFrmBuf = (PWORD)FRAMEBUF_BASE;


  pFrmBuf[4 * i - 2 * (i % 320)] = pWord;
  pFrmBuf[4 * i - 2 * (i % 320) + 1] = pWord;
  pFrmBuf[4 * i - 2 * (i % 320) + 320 * 2] = pWord;
  pFrmBuf[4 * i - 2 * (i % 320) + 1 + 320 * 2] = pWord;
 }

只不过这次准备的是一张320 x 240的照片,然后采用一重的循环,把它写到framebuffer相应的位置去。

最终的效果还是不错的,不知道插值有没有更好的公式。如果有,也请告诉偶。呵呵。

文章评论0条评论)

登录后参与讨论
我要评论
0
5
关闭 站长推荐上一条 /2 下一条