1. 程式人生 > >linux驅動由淺入深系列:顯示子系統之一(通過FrameBuffer在螢幕上畫出圖形)

linux驅動由淺入深系列:顯示子系統之一(通過FrameBuffer在螢幕上畫出圖形)

顯示子系統對應用層提供的介面叫做framebuffer,一般位於/dev/fb0(下文示例運行於adroid的平臺位於/dev/graphics/fb0,不過它們都是一樣的),它為上層提供了統一的對顯示卡的描述。首先要明確的是lcd顯示子系統雖然複雜,但其任然是基本的字元裝置,fb0就是其裝置節點,主裝置號29。不同之處在於,可以通過mmap(mmap將一個檔案或者其它物件對映進記憶體。)對其進行地址對映,將核心中的視訊記憶體空間直接對映到使用者空間,這樣使用者空間填入需要顯示的資料就能直接顯示在lcd上。其餘的引數查詢與設定通過ioctl都可以完成。

我們先從第一個應用層測試開始講起:

1,  保持螢幕上有一幀畫面,進入fb0對應的目錄(/dev/ 或/dev/graphics/)執行:

cat fb0 > fb_test

這樣就可以對framebuffer的一幀原始資料進行暫存

2,  切換一幀螢幕畫面,執行

cat fb_test > fb0

可以看到之前暫存的一幀畫面重新出現了。adroid有自身的重新整理頻率,所以我們刷入的一幀畫面會很快被覆蓋,看到的現象可能是一閃即逝。

從上面的測試我們理解了fb0的基本作用,下面我們寫一個測試程式畫一個自己的圖形:

#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <fcntl.h>
#include <linux/fb.h>
#include <sys/mman.h>

int main()
{
   int fbfd=0;//framebuffer 檔案控制代碼
   struct fb_var_screeninfo vinfo;
   unsigned long screen_size=0;
   char *fbp=0;//framebuffer 在使用者空間對映的虛擬地址指標
   int x=0,y=0,i=0;

   fbfd=open("/dev/graphics/fb0",O_RDWR);  //開啟framebuffer 檔案節點
   if(!fbfd){
		  printf("error\n");
		  exit(1);
   }

   if(ioctl(fbfd,FBIOGET_VSCREENINFO,&vinfo)){  //獲取螢幕可變引數
		  printf("error\n");
		  exit(1);
   }

   //列印螢幕可變引數:x軸畫素個數,y軸畫素個數,每個畫素點數
   printf("%dx%d,%dbpp\n",vinfo.xres,vinfo.yres,vinfo.bits_per_pixel);
   screen_size=vinfo.xres*vinfo.yres*12;  //framebuffer大小
   fbp=(char *)mmap(0,screen_size,PROT_READ|PROT_WRITE,MAP_SHARED,fbfd,0);//對映
   if((int)fbp==-1){
		  printf("error\n");
		  exit(4);
   }

   for(i = 0; i < screen_size/2; i++)
   {
	   unsigned short rgb;
	   rgb=(31<<11)|(0<<5)|0;
	   *((unsigned short *)(fbp+i*2))=rgb;
   }

   munmap(fbp,screen_size);
   close(fbfd);
   return 0;
}


分析:

上述程式比較簡潔,先打開了fb0節點,獲取了相關引數,然後進行framebuffer地址對映。之後直接向framebuffer中寫入資料,畫面就會出現在螢幕上。

其中framebuffer的大小是從驅動程式中獲知的,下一篇文章將會就驅動層展開分析。

framebuffer中資料格式有rgb565、rgb888,上面示例為rgb565。執行後全屏為紅色。