Linux framebuffer的框架非常簡單, 對於應用程序就是操作一塊內存(俗稱幀緩存), 當然也有可能是雙緩存, 一般用於高幀率場景, 一塊幀在填充數據時, 另一塊在顯示, 接着對調過來,
那通過設置哪里告知驅動層讀取哪塊幀數據呢? 答案是用vinfo.xoffset, vinfo.yoffset
需要注意的是, 無論用write()、還是mmap()后直接操作內存都只是填充內存而已, 並不代表能夠立馬顯示, 這得看驅動, 如果驅動實現了自刷新(不斷從幀緩存拿數據刷到LCD上), 那填充數據到幀緩存就會立馬顯示出來,
如果驅動沒有實現,那應用程序需要主動的調用 ioctl(fp, FBIOPAN_DISPLAY, &vinfo);, 告知驅動可以刷數據了, 如果這都沒顯示出來, 估計驅動沒實現FBIOPAN_DISPLAY功能。
示例代碼:(驅動實現自刷新, 應用依次顯示黃、藍、紅,最后畫線)
#include <unistd.h> #include <stdio.h> #include <fcntl.h> #include <linux/fb.h> #include <sys/mman.h> #include <stdlib.h> #include <string.h> #define RED 0xF800 #define YELLOW 0xFFE0 #define BLUE 0x001F #define WHITE 0xFFFF #define BLACK 0x0000 void fill_color16(short *fb_addr, short bit_map, int psize) { int i; for(i=0; i<psize; i++) { *fb_addr = bit_map; fb_addr++; } } int main () { int fp=0; struct fb_var_screeninfo vinfo; struct fb_fix_screeninfo finfo; long screensize=0; char *fbp = NULL, *test_fbp=NULL; int x = 0, y = 0; long location = 0; int i; int num = 5; int pix_size=0; fp = open("/dev/graphics/fb0", O_RDWR); if(fp < 0) { printf("Error : Can not open framebuffer device/n"); exit(1); } if(ioctl(fp, FBIOGET_FSCREENINFO, &finfo)){ printf("Error reading fixed information/n"); exit(2); } if(ioctl(fp, FBIOGET_VSCREENINFO, &vinfo)){ printf("Error reading variable information/n"); exit(3); } screensize = vinfo.xres * vinfo.yres * vinfo.bits_per_pixel / 8; printf("The phy mem = 0x%x, total size = %d(byte)\n", finfo.smem_start, finfo.smem_len); printf("xres = %d, yres = %d, bits_per_pixel = %d\n", vinfo.xres, vinfo.yres, vinfo.bits_per_pixel); printf("So the screensize = %d(byte), using %d frame\n", screensize, finfo.smem_len/screensize); printf("vinfo.xoffset = %d, vinfo.yoffset = %d\n", vinfo.xoffset, vinfo.yoffset); printf("vinfo.vmode is :%d\n", vinfo.vmode); printf("finfo.ypanstep is :%d\n", finfo.ypanstep); printf("vinfo.red.offset=0x%x\n", vinfo.red.offset); printf("vinfo.red.length=0x%x\n", vinfo.red.length); printf("vinfo.green.offset=0x%x\n", vinfo.green.offset); printf("vinfo.green.length=0x%x\n", vinfo.green.length); printf("vinfo.blue.offset=0x%x\n", vinfo.blue.offset); printf("vinfo.blue.length=0x%x\n", vinfo.blue.length); printf("vinfo.transp.offset=0x%x\n", vinfo.transp.offset); printf("vinfo.transp.length=0x%x\n", vinfo.transp.length); fbp =(char *)mmap(0, screensize, PROT_READ | PROT_WRITE, MAP_SHARED, fp,0); if ((int)fbp == -1) { printf ("Error: failed to map framebuffer device to memory./n"); exit (4); } printf("Get virt mem = %p\n", fbp); pix_size = vinfo.xres * vinfo.yres; /* using first frame, for FBIOPAN_DISPLAY * 當刷新需要調用FBIOPAN_DISPLAY, 要告知驅動刷哪塊幀, 用到下面兩個參數 * 如果使用第二幀buffer -> vinfo.xoffset = 0; vinfo.yoffset = vinfo.yres; */ vinfo.xoffset = 0; vinfo.yoffset = 0; /* show color loop */ while(num--) { printf("\ndrawing YELLOW......\n"); fill_color16((short *)fbp, YELLOW, pix_size); //ioctl(fp, FBIOPAN_DISPLAY, &vinfo); sleep(3); printf("\ndrawing BLUE......\n"); fill_color16((short *)fbp, BLUE, pix_size); //ioctl(fp, FBIOPAN_DISPLAY, &vinfo); sleep(3); printf("\ndrawing RED......\n"); fill_color16((short *)fbp, RED, pix_size); //ioctl(fp, FBIOPAN_DISPLAY, &vinfo); sleep(3); } #if 1 /*這是你想畫的點的位置坐標,(0,0)點在屏幕左上角*/ x = 10; y = 10; location = x * (vinfo.bits_per_pixel / 8) + y * finfo.line_length; test_fbp = fbp + location; printf("draw line.......\n"); for(i = 0; i < (vinfo.xres - x); i++) *test_fbp++ = i+30; //ioctl(fp, FBIOPAN_DISPLAY, &vinfo); #endif munmap(fbp, screensize); /*解除映射*/ close (fp); return 0; }
當然用read()/write(), 也可以, 就是效率非常低, 太多系統調用導致系統在用戶態和kernel態切換, 而且每次還傳輸一個字節, 但作為例子可以參考一下:
#include <unistd.h> #include <stdio.h> #include <fcntl.h> #include <linux/fb.h> #include <sys/mman.h> #include <stdlib.h> #include <string.h> #define RED 0xF800 #define YELLOW 0xFFE0 #define BLUE 0x001F #define WHITE 0xFFFF #define BLACK 0x0000 int main () { int fp=0; struct fb_var_screeninfo vinfo; int i; int pix_size=0; unsigned char color1, color2; fp = open("/dev/graphics/fb0", O_RDWR); if(fp < 0) { printf("Error : Can not open framebuffer device/n"); exit(1); } if(ioctl(fp, FBIOGET_VSCREENINFO, &vinfo)){ printf("Error reading variable information/n"); exit(3); } pix_size = vinfo.xres * vinfo.yres; color1 = 0; color2 = 0xf8; for(i=0; i<pix_size; i++) { write(fp, &color1, 1); write(fp, &color2, 1); } close (fp); return 0; }
