Framebuffer 是用一個視頻輸出設備從包含完整的幀數據的一個內存緩沖區中來驅動一個視頻顯示設備,本文我們來看看嵌入式Linux基于framebuffer的jpeg格式在本地LCD屏顯示.
在基于Linux的視頻監控采集系統中,攝像頭采集到的一幀視頻圖像數據一般都是經過硬件自動壓縮成jpeg格式的,然后再保存到攝像頭設備的緩沖區.如果要把采集到的jpeg格式顯示在本地LCD屏上,由于我們的Linux系統沒有移植任何GUI系統,就要考慮以下方面:
1. 將jpeg格式解壓縮為位圖格式,也就是jpeg解碼.
2. 將解碼出來的位圖格式輸出到本地的LCD屏上. 在Linux系統下是通過寫入幀緩沖(framebuffer)來實現的.
3. framebuffer相當于為LCD設備提供一個統一的接口,對framebuffer的操控會反映到LCD顯示設備上去. 如果配置Linux內核時沒有找到支持本地lcd屏這種型號的驅動,那我們要自己寫lcd屏驅動,然后選擇靜態加入內核或以模塊的形式加入內核動態加載. 針對以上三點,我們逐一解決:
1.jpeg解碼
先了解一下jpeg標準的編碼過程:原始的一幀未壓縮過的圖像可以看成是RGB(紅綠藍)色彩空間上的一組向量集合,但在RGB空間是不利于數據壓縮的,因此為了壓縮先要把圖像映射到利于壓縮的YUV空間上(原因是因為人類的眼睛對于亮度差異的敏感度高于色彩變化,而YUV空間的一個基向量Y就是亮度), 這一步叫色彩空間轉換.
下一步可以在YUV空間上減少U(色調)和V(飽和度)的成分,也就是在亮度信息不減少的情況下移除部分色彩信息,誰叫人的眼睛對亮度的敏感優于對色彩的敏感呢.這一步叫縮減取樣.下一步是將圖像從色彩空間映射到頻率空間,可以采用的變換方法有:離散余弦變換,傅氏變換,正弦變換等. 其中應用最廣的是離散余弦變換(DCT).這一步是無損的,目的是為了在下一步稱之為量化的過程中可以經過四舍五入刪除高頻量得到壓縮后的矩陣.量化之后就是對這個矩陣的編碼問題了.針對這個矩陣的分布特點,采用"Z"字形的順序掃描編排,然后進行RLE行程編碼,把大量連續重復的數據壓縮.最后再用范式Huffman編碼.要了解詳細的過程,可以查看JPEG標準. 而解碼就是以上編碼的逆過程了.除非想要自己實現jpeg的編碼和解碼函數,我們可以不必細究這些過程,而是直接使用別人已經實現的jpeg編碼解碼庫.在Linux平臺下,有libjpeg庫,它是完全用C語言編寫的, 依照它的許可協議,可自由使用,不是GPL協議,它可以用于商業目的. libjpeg的6b版本有個問題,就是解碼接口,它只接受文件源.打開源的函數jpeg_stdio_src(j_decompress_ptr cinfo, FILE *infile)要求解碼源infile是文件.而我們希望解碼的是直接來自映射內存中的數據.要解碼內存流的話就要修改libjpeg的源碼了,可以參考這里:http://my.unix-center.net/~Simon_fu/?p=565 目前libjpeg的最新版8c已經解決了這個接口不好的問題了,它增加了對內存流解碼的支持.通過調用函數
jpeg_mem_src(&cinfo, fdmem, st.st_size);
就可以將保存在內存的jpeg格式數據作為源輸入了.因此我們就用libjpeg 8c這個版本來解碼,用到的函數主要有:
初始化jpeg解壓對象:
- /* init jpeg decompress object error handler */
- cinfo.err = jpeg_std_error(&jerr);
- jpeg_create_decompress(&cinfo);
綁定jpeg解壓對象到輸入流:
- /* bind jpeg decompress object to infile */
- READ_FILE // 從jpeg文件讀入
- jpeg_stdio_src(&cinfo, infile);
- f READ_MEM // 從內存讀入jpeg格式
- jpeg_mem_src(&cinfo, fdmem, st.st_size);
- if
讀取jpeg頭部信息:
- /* read jpeg header */
- jpeg_read_header(&cinfo, TRUE);
解壓過程:
- /* decompress process */
- jpeg_start_decompress(&cinfo);
調用這個函數之后,可以得到jpeg圖像的下面幾個參數:
- output_width // 圖像的寬度
- output_height // 圖像的高度
- output_components // 每個像素占用的字節數
我們采用每掃描一行像素就輸出到屏幕的方法的話,根據以上參數可以確定分配一行信息需要的緩沖區:
- buffer = (unsigned char *)malloc(cinfo.output_width *
- cinfo.output_components);
總共需要掃描output_height行,讀取一行掃描數據并輸出到LCD屏幕:
- y = 0;
- while (cinfo.output_scanline < cinfo.output_height) {
- jpeg_read_scanlines(&cinfo, &buffer, 1);
- if (fb_depth == 16) { // 如果顯示設備色深是16位
- ...
- } else if (fb_depth == 24) { // 如果顯示設備色深是24位
- ...
- } else if (fb_depth == 32) { // 如果顯示設備色深是32位
- ...
- }
- y++;
- }
結束jpeg解碼:
- /* finish decompress, destroy decompress object */
- jpeg_finish_decompress(&cinfo);
- jpeg_destroy_decompress(&cinfo);
釋放緩沖區:
- /* release memory buffer */
- free(buffer);
2.輸出位圖到LCD屏
通過framebuffer直接寫屏的主要步驟有:
打開framebuffer設備:
- /* open framebuffer device */
- fbdev = fb_open("/dev/fb0");
獲取framebuffer設備參數:
- /* get status of framebuffer device */
- fb_stat(fbdev, &fb_width, &fb_height, &fb_depth);
映射framebuffer設備到共享內存:
- screensize = fb_width * fb_height * fb_depth / 8;
- fbmem = fb_mmap(fbdev, screensize);
直接對映射到那片內存進行寫操作,LCD屏刷新刷新時就會反應到屏幕上去了.
- y = 0;
- while (cinfo.output_scanline < cinfo.output_height) {
- jpeg_read_scanlines(&cinfo, &buffer, 1);
- if (fb_depth == 16) {
- unsigned short color;
- for (x = 0; x < cinfo.output_width; x++) {
- color =
- RGB888toRGB565(buffer[x * 3],
- buffer[x * 3 + 1], buffer[x * 3 + 2]);
- fb_pixel(fbmem, fb_width, fb_height, x, y, color);
- }
- } else if (fb_depth == 24) {
- memcpy((unsigned char *) fbmem + y * fb_width * 3,
- buffer, cinfo.output_width * cinfo.output_components);
- } else if (fb_depth == 32) {
- // memcpy((unsigned char *) fbmem + y * fb_width * 4,
- // buffer, cinfo.output_width * cinfo.output_components);
- for (x = 0; x < cinfo.output_width; x++) {
- *(fbmem + y * fb_width * 4 + x * 4) = (unsigned char) buffer[x * 3 + 2];
- *(fbmem + y * fb_width * 4 + x * 4 + 1) = (unsigned char) buffer[x * 3 + 1];
- *(fbmem + y * fb_width * 4 + x * 4 + 2) = (unsigned char) buffer[x * 3 + 0];
- *(fbmem + y * fb_width * 4 + x * 4 + 3) = (unsigned char) 0;
- } //Vevb.com
- }
- y++; // next scanline
- }
卸載映射framebuffer的那部分內存:
- /* unmap framebuffer's shared memory */
- fb_munmap(fbmem, screensize);
- 關閉framebuffer設備:
- close(fbdev);
根據以上兩點,可以寫一個測試程序,在不開X-window圖形系統的情況下,將本地的jpeg文件直接顯示到屏幕上.
- #include <stdio.h>
- #include <string.h>
- #include <stdlib.h>
- #include <unistd.h>
- #include <sys/ioctl.h>
- #include <sys/types.h>
- #include <sys/stat.h>
- #include <errno.h>
- #include <fcntl.h>
- #include <sys/mman.h>
- #include <linux/fb.h>
- #include "jpeglib.h"
- #include "jerror.h"
- #define FB_DEV "/dev/fb0"
- #define __fnc__ __FUNCTION__
- #define debug 0
- #define debug_printf 0
- #define BYREAD 0
- #define BYMEM 1
- /* function deciaration */
- void usage(char *msg);
- unsigned short RGB888toRGB565(unsigned char red,
- unsigned char green, unsigned char blue);
- int fb_open(char *fb_device);
- int fb_close(int fd);
- int fb_stat(int fd, unsigned int *width, unsigned int *height, unsigned int * depth);
- void *fb_mmap(int fd, unsigned int screensize);
- void *fd_mmap(int fd, unsigned int filesize);
- int fb_munmap(void *start, size_t length);
- int fb_pixel(void *fbmem, int width, int height,
- int x, int y, unsigned short color);
- #if(debug)
- void draw(unsigned char *fbp,
- struct fb_var_screeninfo vinfo,
- struct fb_fix_screeninfo finfo);
- #endif
- /* function implementation */
- int main(int argc, char **argv)
- {
- struct jpeg_decompress_struct cinfo;
- struct jpeg_error_mgr jerr;
- #if(BYREAD)
- FILE *infile;
- #endif
- int fd;
- unsigned char *buffer;
- struct stat st;
- int fbdev;
- char *fb_device;
- unsigned char *fbmem;
- unsigned char *fdmem;
- unsigned int screensize;
- unsigned int fb_width;
- unsigned int fb_height;
- unsigned int fb_depth;
- register unsigned int x;
- register unsigned int y;
- /* check auguments */
- if (argc != 2) {
- usage("insuffient auguments");
- exit(-1);
- }
- /* open framebuffer device */
- if ((fb_device = getenv("FRAMEBUFFER")) == NULL)
- fb_device = FB_DEV;
- fbdev = fb_open(fb_device);
- /* get status of framebuffer device */
- fb_stat(fbdev, &fb_width, &fb_height, &fb_depth);
- /* map framebuffer device to shared memory */
- screensize = fb_width * fb_height * fb_depth / 8;
- fbmem = fb_mmap(fbdev, screensize);
- #if (BYREAD)
- /* open input jpeg file */
- if ((infile = fopen(argv[1], "rb")) == NULL) {
- fprintf(stderr, "open %s failedn", argv[1]);
- exit(-1);
- }
- #endif
- if ((fd = open(argv[1], O_RDONLY)) < 0) {
- perror("open");
- exit(-1);
- }
- if (fstat(fd, &st) < 0) {
- perror("fstat");
- exit(-1);
- }
- fdmem = fd_mmap(fd, st.st_size);
- /* init jpeg decompress object error handler */
- cinfo.err = jpeg_std_error(&jerr);
- jpeg_create_decompress(&cinfo);
- /* bind jpeg decompress object to infile */
- #if (BYREAD)
- jpeg_stdio_src(&cinfo, infile);
- #endif
- #if (BYMEM)
- jpeg_mem_src(&cinfo, fdmem, st.st_size);
- #endif
- /* read jpeg header */
- jpeg_read_header(&cinfo, TRUE);
- /* decompress process */
- jpeg_start_decompress(&cinfo);
- if ((cinfo.output_width > fb_width) ||
- (cinfo.output_height > fb_height)) {
- printf("too large jpeg file, can't displayn");
- #if (0)
- return -1;
- #endif
- }
- buffer = (unsigned char *) malloc(cinfo.output_width *
- cinfo.output_components);
- struct fb_fix_screeninfo fb_finfo;
- struct fb_var_screeninfo fb_vinfo;
- if (ioctl(fbdev, FBIOGET_FSCREENINFO, &fb_finfo)) {
- perror(__fnc__);
- return -1;
- }
- if (ioctl(fbdev, FBIOGET_VSCREENINFO, &fb_vinfo)) {
- perror(__fnc__);
- return -1;
- }
- #if(debug)
- draw(fbmem, fb_vinfo, fb_finfo);
- #endif
- y = 0;
- while (cinfo.output_scanline < cinfo.output_height) {
- jpeg_read_scanlines(&cinfo, &buffer, 1);
- if (fb_depth == 16) {
- unsigned short color;
- for (x = 0; x < cinfo.output_width; x++) {
- color =
- RGB888toRGB565(buffer[x * 3],
- buffer[x * 3 + 1], buffer[x * 3 + 2]);
- fb_pixel(fbmem, fb_width, fb_height, x, y, color);
- }
- } else if (fb_depth == 24) {
- memcpy((unsigned char *) fbmem + y * fb_width * 3,
- buffer, cinfo.output_width * cinfo.output_components);
- } else if (fb_depth == 32) {
- // memcpy((unsigned char *) fbmem + y * fb_width * 4,
- // buffer, cinfo.output_width * cinfo.output_components);
- for (x = 0; x < cinfo.output_width; x++) {
- * (fbmem + y * fb_width * 4 + x * 4) = (unsigned char) buffer[x * 3 + 2];
- * (fbmem + y * fb_width * 4 + x * 4 + 1) = (unsigned char) buffer[x * 3 + 1];
- * (fbmem + y * fb_width * 4 + x * 4 + 2) = (unsigned char) buffer[x * 3 + 0];
- * (fbmem + y * fb_width * 4 + x * 4 + 3) = (unsigned char) 0;
- }
- }
- y++; // next scanline
- }
- /* finish decompress, destroy decompress object */
- jpeg_finish_decompress(&cinfo);
- jpeg_destroy_decompress(&cinfo);
- /* release memory buffer */
- free(buffer);
- #if (BYREAD)
- /* close jpeg inputing file */
- fclose(infile);
- #endif
- /* unmap framebuffer's shared memory */
- fb_munmap(fbmem, screensize);
- #if (BYMEM)
- munmap(fdmem, (size_t) st.st_size);
- close(fd);
- #endif
- /* close framebuffer device */
- fb_close(fbdev);
- return 0;
- }
- void usage(char *msg)
- {
- fprintf(stderr, "%sn", msg);
- printf("Usage: fv some-jpeg-file.jpgn");
- }
- /* open framebuffer device.
- * return positive file descriptor if success,
- * else return -1
- */
- int fb_open(char *fb_device)
- {
- int fd;
- if ((fd = open(fb_device, O_RDWR)) < 0) {
- perror(__fnc__);
- return -1;
- }
- return fd;
- }
- int fb_close(int fd)
- {
- return (close(fd));
- }
- /* get framebuffer's width, height, and depth.
- * return 0 if success, else return -1.
- */
- int fb_stat(int fd, unsigned int *width, unsigned int *height, unsigned int * depth)
- {
- struct fb_fix_screeninfo fb_finfo;
- struct fb_var_screeninfo fb_vinfo;
- if (ioctl(fd, FBIOGET_FSCREENINFO, &fb_finfo)) {
- perror(__fnc__);
- return -1;
- }
- if (ioctl(fd, FBIOGET_VSCREENINFO, &fb_vinfo)) {
- perror(__fnc__);
- return -1;
- }
- *width = fb_vinfo.xres;
- *height = fb_vinfo.yres;
- *depth = fb_vinfo.bits_per_pixel;
- return 0;
- }
- /* map shared memory to framebuffer device.
- * return maped memory if success
- * else return -1, as mmap dose
- */
- void *fb_mmap(int fd, unsigned int screensize)
- {
- caddr_t fbmem;
- if ((fbmem = mmap(0, screensize, PROT_READ | PROT_WRITE,
- MAP_SHARED, fd, 0)) == MAP_FAILED) {
- perror(__func__);
- return (void *) (-1);
- }
- return fbmem;
- }
- /* map shared memmory to a opened file */
- void *fd_mmap(int fd, unsigned int filesize)
- {
- caddr_t fdmem;
- if ((fdmem = mmap(0, filesize, PROT_READ,
- MAP_SHARED, fd, 0)) == MAP_FAILED) {
- perror(__func__);
- return (void *) (-1);
- }
- return fdmem;
- }
- /* unmap map memory for framebuffer device */
- int fb_munmap(void *start, size_t length)
- {
- return (munmap(start, length));
- }
- /* convert 24bit RGB888 to 16bit RGB565 color format */
- unsigned short RGB888toRGB565(unsigned char red,
- unsigned char green, unsigned char blue)
- {
- unsigned short B = (blue >> 3) & 0x001F;
- unsigned short G = ((green >> 2) << 5) & 0x07E0;
- unsigned short R = ((red >> 3) << 11) & 0xF800;
- return (unsigned short) (R | G | B);
- }
- /* display a pixel on the framebuffer device.
- * fbmem is the starting memory of framebuffer,
- * width and height are dimension of framebuffer,
- * width and height are dimension of framebuffer,
- * x and y are the coordinates to display,
- * color is the pixel's color value.
- * return 0 if success, otherwise return -1.
- */
- int fb_pixel(void *fbmem, int width, int height,
- int x, int y, unsigned short color)
- {
- if ((x > width) || (y > height))
- return -1;
- unsigned short *dst = ((unsigned short *) fbmem + y * width + x);
- *dst = color;
- return 0;
- }
3.LCD驅動
我們用到的是一塊東華3.5寸數字屏,型號為WXCAT35-TG3.下面的驅動程序是韋東山老師課堂上現場寫的,如下:
- #include <linux/module.h>
- #include <linux/kernel.h>
- #include <linux/errno.h>
- #include <linux/string.h>
- #include <linux/mm.h>
- #include <linux/slab.h>
- #include <linux/delay.h>
- #include <linux/interrupt.h>
- #include <linux/fb.h>
- #include <linux/init.h>
- #include <linux/ioport.h>
- #include <linux/dma-mapping.h>
- #include <asm/uaccess.h>
- #include <asm/system.h>
- #include <asm/irq.h>
- #include <asm/setup.h>
- /* WXCAT35-TG3 */
- struct s3c_lcd_regs {
- unsigned long lcdcon1;
- unsigned long lcdcon2;
- unsigned long lcdcon3;
- unsigned long lcdcon4;
- unsigned long lcdcon5;
- unsigned long lcdsaddr1;
- unsigned long lcdsaddr2;
- unsigned long lcdsaddr3;
- unsigned long redlut;
- unsigned long greenlut;
- unsigned long bluelut;
- unsigned long reserved[9];
- unsigned long dithmode;
- unsigned long tpal;
- unsigned long lcdintpnd;
- unsigned long lcdsrcpnd;
- unsigned long lcdintmsk;
- unsigned long lpcsel;
- };
- static u32 colregs[16];
- static struct fb_info *s3c_fb_info;
- static dma_addr_t s3c_fb_handle;
- static unsigned long fb_va;
- /* from pxafb.c */
- static inline unsigned int chan_to_field(unsigned int chan, struct fb_bitfield *bf)
- {
- chan &= 0xffff;
- chan >>= 16 - bf->length;
- return chan << bf->offset;
- }
- static int s3cfb_setcolreg(unsigned regno,
- unsigned red, unsigned green, unsigned blue,
- unsigned transp, struct fb_info *info)
- {
- unsigned int val;
- /* dprintk("setcol: regno=%d, rgb=%d,%d,%dn", regno, red, green, blue); */
- /* true-colour, use pseuo-palette */
- if (regno < 16) {
- u32 *pal = s3c_fb_info->pseudo_palette;
- val = chan_to_field(red, &s3c_fb_info->var.red);
- val |= chan_to_field(green, &s3c_fb_info->var.green);
- val |= chan_to_field(blue, &s3c_fb_info->var.blue);
- pal[regno] = val;
- }
- return 0;
- }
- static struct fb_ops s3cfb_ops = {
- .owner = THIS_MODULE,
- // .fb_check_var = clps7111fb_check_var,
- // .fb_set_par = clps7111fb_set_par,
- // .fb_setcolreg = clps7111fb_setcolreg,
- // .fb_blank = clps7111fb_blank,
- .fb_setcolreg = s3cfb_setcolreg,
- .fb_fillrect = cfb_fillrect,
- .fb_copyarea = cfb_copyarea,
- .fb_imageblit = cfb_imageblit,
- };
- struct s3c_lcd_regs *s3c_lcd_regs;
- static volatile unsigned long *gpccon;
- static volatile unsigned long *gpdcon;
- static volatile unsigned long *gpgcon;
- int s3c_lcd_init(void)
- {
- extern int debug_lcd;
- /* 1. 分配一個fb_info結構體 */
- s3c_fb_info = framebuffer_alloc(0, NULL);
- printk("%s %dn", __FUNCTION__, __LINE__);
- /* 2. 設置fb_info結構體 */
- /*
- 2.1 設置固定的信息
- 2.2 設置可變的信息
- 2.3 設置操作函數
- */
- /* 24BPP(bits per pixel), 會用到4字節, 其中浪費1字節 */
- strcpy(s3c_fb_info->fix.id, "WXCAT35-TG3");
- // s3c_fb_info->fix.smem_start // frame buffer's physical address
- s3c_fb_info->fix.smem_len = 320*240*32/8;
- s3c_fb_info->fix.type = FB_TYPE_PACKED_PIXELS;
- s3c_fb_info->fix.visual = FB_VISUAL_TRUECOLOR;
- s3c_fb_info->fix.line_length = 320 * 4;
- s3c_fb_info->var.xres = 320;
- s3c_fb_info->var.yres = 240;
- s3c_fb_info->var.xres_virtual = 320;
- s3c_fb_info->var.yres_virtual = 240;
- s3c_fb_info->var.bits_per_pixel = 32;
- s3c_fb_info->var.red.offset = 16;
- s3c_fb_info->var.red.length = 8;
- s3c_fb_info->var.green.offset = 8;
- s3c_fb_info->var.green.length = 8;
- s3c_fb_info->var.blue.offset = 0;
- s3c_fb_info->var.blue.length = 8;
- //s3c_fb_info->var.activate = FB_ACTIVATE;
- s3c_fb_info->fbops = &s3cfb_ops;
- s3c_fb_info->pseudo_palette = colregs;
- /* 3. 硬件相關的操作 */
- /* 配置GPIO */
- gpccon = ioremap(0x56000020, 4);
- gpdcon = ioremap(0x56000030, 4);
- gpgcon = ioremap(0x56000060, 4);
- *gpccon = 0xaaaaaaaa;
- *gpdcon = 0xaaaaaaaa;
- *gpgcon |= (3<<8); /* GPG4 use as lcd_pwren */
- printk("%s %dn", __FUNCTION__, __LINE__);
- s3c_lcd_regs = ioremap(0X4D000000, sizeof(struct s3c_lcd_regs));
- /*
- * VCLK = HCLK / [(CLKVAL+1)x2] = 100M/[(CLKVAL+1)x2] = 6.4
- * CLKVAL = 6.8 = 7
- * TFT LCD panel
- * 24bpp
- */
- s3c_lcd_regs->lcdcon1 = (7<<8)|(0<<7)|(3<<5)|(0x0d<<1)|(0<<0);
- printk("%s %dn", __FUNCTION__, __LINE__);
- /* VBPD: 電子槍收到VSYNC信號后,"多長時間"才能跳回第1行
- * VBPD=14, LCD: tvb=15
- * LINEVAL=239, LCD: 有240行
- * VFPD=11, LCD: tvf=12 // 發出最后一行數據后,再過多長時間才發出VSYNC
- * VSPW=2, LCD: tvp=3 // VSYNC的寬度
- */
- s3c_lcd_regs->lcdcon2 = (14<<24)|(239<<14)|(11<<6)|(2<<0);
- /* HBPD: 電子槍收到HSYNC信號后,"多長時間"才能跳回第1列
- * HBPD=37, LCD: thb=38
- * HORVAL=319, LCD: 有320行
- * HFPD=19, LCD: thf=20 // 發出最后一象素數據后,再過多長時間才發出HSYNC
- * HSPW=29, LCD: thp=30 // VSYNC的寬度
- */
- s3c_lcd_regs->lcdcon3 = (37<<19)|(319<<8)|(19<<0);
- s3c_lcd_regs->lcdcon4 = 29;
- /* bit10: 在VCLK上升沿取數據
- * bit9 : VSYNC低電平有效
- * bit8 : HSYNC低電平有效
- * bit5 : PWREN低電平有效
- */
- s3c_lcd_regs->lcdcon5 = (1<<10)|(1<<9)|(1<<8)|(1<<5)|(0<<3);
- /* 分配frame buffer */
- fb_va = (unsigned long)dma_alloc_writecombine(NULL, s3c_fb_info->fix.smem_len, &s3c_fb_handle, GFP_KERNEL);
- printk("fb_va = 0x%x, pa = 0x%xn", fb_va, s3c_fb_handle);
- s3c_fb_info->fix.smem_start = s3c_fb_handle;
- s3c_fb_info->screen_base = fb_va;
- /* 把framebuffer的地址告訴LCD控制器 */
- s3c_lcd_regs->lcdsaddr1 = (s3c_fb_info->fix.smem_start >> 1);
- s3c_lcd_regs->lcdsaddr2 = ((s3c_fb_info->fix.smem_start+320*240*4) >> 1) & 0x1fffff;
- s3c_lcd_regs->lcdsaddr3 = 320*4/2;
- /* 使能LCD */
- s3c_lcd_regs->lcdcon1 |= (1<<0);
- /* 4. register_framebuffer */
- printk("%s %dn", __FUNCTION__, __LINE__);
- //debug_lcd = 1;
- register_framebuffer(s3c_fb_info);
- printk("%s %dn", __FUNCTION__, __LINE__);
- return 0;
- }
- void s3c_lcd_exit(void)
- {
- unregister_framebuffer(s3c_fb_info);
- dma_free_writecombine(NULL, s3c_fb_info->fix.smem_len, fb_va, s3c_fb_handle);
- iounmap(s3c_lcd_regs);
- iounmap(gpccon);
- iounmap(gpdcon);
- iounmap(gpgcon);
- framebuffer_release(s3c_fb_info);
- }
- module_init(s3c_lcd_init);
- module_exit(s3c_lcd_exit);
- MODULE_LICENSE("GPL");
然后把它加入到內核,以靜態加載的模式啟動.最后,可以把讀取內存jpeg格式數據輸出到LCD屏的這部分整合到mjpg-stream或servfox去,就實現了采集圖像本地顯示了.
新聞熱點
疑難解答