在基于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这个版本来解码.

用到的函数主要有:

  1. 初始化jpeg解压对象:
      /* init jpeg decompress object error handler */
      cinfo.err = jpeg_std_error(&jerr);
      jpeg_create_decompress(&cinfo);
  2. 绑定jpeg解压对象到输入流:
    	/* bind jpeg decompress object to infile */
    #if READ_FILE	// 从jpeg文件读入
    	jpeg_stdio_src(&cinfo, infile);
    #elif READ_MEM	// 从内存读入jpeg格式
    	jpeg_mem_src(&cinfo, fdmem, st.st_size);
    #endif
  3. 读取jpeg头部信息:
    	/* read jpeg header */
    	jpeg_read_header(&cinfo, TRUE);
  4. 解压过程:
    	/* decompress process */
    	jpeg_start_decompress(&cinfo);

调用这个函数之后,可以得到jpeg图像的下面几个参数:

  1. output_width // 图像的宽度
  2. output_height // 图像的高度
  3. output_components // 每个像素占用的字节数

我们采用每扫描一行像素就输出到屏幕的方法的话,根据以上参数可以确定分配一行信息需要的缓冲区:

	buffer = (unsigned char *)malloc(cinfo.output_width *
			cinfo.output_components);

总共需要扫描output_height行.

  1. 读取一行扫描数据并输出到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++;
    	}
  2. 结束jpeg解码:
    	/* finish decompress, destroy decompress object */
    	jpeg_finish_decompress(&cinfo);
    	jpeg_destroy_decompress(&cinfo);
  3. 释放缓冲区:
    	/* release memory buffer */
    	free(buffer);

2. 输出位图到LCD屏

通过framebuffer直接写屏的主要步骤有:

  1. 打开framebuffer设备:
    	/* open framebuffer device */
    	fbdev = fb_open("/dev/fb0");
  2. 获取framebuffer设备参数:
    	/* get status of framebuffer device */
    	fb_stat(fbdev, &fb_width, &fb_height, &fb_depth);
  3. 映射framebuffer设备到共享内存:
    	screensize = fb_width * fb_height * fb_depth / 8;
    	fbmem = fb_mmap(fbdev, screensize);
  4. 直接对映射到那片内存进行写操作,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;
                }
            }
            y++;    // next scanline
    	}
  5. 卸载映射framebuffer的那部分内存:
    	/* unmap framebuffer's shared memory */
    	fb_munmap(fbmem, screensize);
  6. 关闭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 failed\n", 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 display\n");
#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, "%s\n", msg);
    printf("Usage: fv some-jpeg-file.jpg\n");
}

/* 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,%d\n", 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 %d\n", __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 %d\n", __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 %d\n", __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%x\n", 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 %d\n", __FUNCTION__, __LINE__);
	//debug_lcd = 1;
	register_framebuffer(s3c_fb_info);
    printk("%s %d\n", __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去,就实现了采集图像本地显示了.

嵌入式Linux基于framebuffer的jpeg格式本地LCD屏显示的更多相关文章

  1. 痞子衡嵌入式:记录i.MXRT1060驱动LCD屏显示横向渐变色有亮点问题解决全过程(提问篇)

    大家好,我是痞子衡,是正经搞技术的痞子.今天痞子衡给大家分享的是i.MXRT1060上LCD横向渐变色显示出亮点问题的分析解决经验. 痞子衡前段时间在支持一个i.MXRT1060客户项目时遇到了LCD ...

  2. linux下将jpg,jpeg格式转为PDF

    1.安装imagemagick(用其中的convert)和gthumb     sudo apt-get install imagemagick gthumb 2.将tiff图片转换为png或jpeg ...

  3. Linux 命令 - watch: 反复执行命令,全屏显示输出

    watch 命令周期性地执行命令,全屏显示输出.可以通过 watch 命令反复执行某一程序来监视它的输出变化. 命令格式 watch [-dhvt] [-n <seconds>] [--d ...

  4. 嵌入式Linux驱动学习之路(十八)LCD驱动

    驱动代码: /************************************************************************* > File Name: lcd ...

  5. 痞子衡嵌入式:基于恩智浦i.MXRT1060的MP4视频播放器(RT-Mp4Player)设计

    大家好,我是痞子衡,是正经搞技术的痞子.今天痞子衡给大家介绍的是基于i.MXRT1062的MP4播放器参考设计. i.MXRT1062是恩智浦i.MXRT四位数系列的中端型号,外设搭配上很均衡,辅以6 ...

  6. 基于bootsplash的嵌入式linux启动画面定制

    来源: ChinaUnix博客 作者: ChinaUnix博客 发布时间:2007-01-01 16:29:00 摘 要:在基于linux的嵌入式仿真平台研发中,利用开源工具bootsplash能够定 ...

  7. 基于ARM9和嵌入式Linux系统的多功能综合通信控制系统的框架

    基于ARM9硬件平台和嵌入式Linux系统的多功能综合通信控制系统的框架设计及各模块的功能.系统采用符合POSIX.1标准的C语言编写,实现了对下位机传送数据帧的采集.分析和存储,并能根据上位机的配置 ...

  8. 7、基于嵌入式Linux的视频采集系统---UVC驱动模型介绍

    UVC 即 usb video class.USB协议中,除了通用的软硬件电气接口规范等,还包含了各种各样的Class协议,用来为不同的功能定义各自的标准接口和具体的总线上的数据交互格式和内容.这些C ...

  9. CH02基于ZYNQ的嵌入式LINUX移植

    CH02基于ZYNQ的嵌入式LINUX移植 1.1概述 实验环境: Windows 10 专业版 Vmware workstation 14.1.1 Ubuntu 16.04.3 Xilinx SDx ...

随机推荐

  1. null undefiend NaN

    console.log(typeof NaN) console.log(typeof undefined) console.log(typeof null)

  2. es随想二

    一.es运行状态的监控 es长时间批量入库时,需要对入库的性能进行监控,否则可能导致es重启,入库任务失败. 可以编写shell脚本,每分钟用cat命令监控pending的数量,数量逐渐增大时就需要注 ...

  3. POJ Ikki's Story IV - Panda's Trick [2-SAT]

    题意: 圆上n个点,m对点之间连边,连在园内或园外,所有边不相交是否可行 发现两对点连线都在内相交则都在外也相交,那么只有一个在内一个在外啦,转化为$2-SAT$问题 #include <ios ...

  4. BZOJ 2142: 礼物 [Lucas定理]

    2142: 礼物 Time Limit: 10 Sec  Memory Limit: 259 MBSubmit: 1294  Solved: 534[Submit][Status][Discuss] ...

  5. 用js实现2048小游戏

    用js实现2048小游戏 笔记仓库:https://github.com/nnngu/LearningNotes 1.游戏简介 2048是一款休闲益智类的数字叠加小游戏.(文末给出源代码和演示地址) ...

  6. 微信小程序项目踩过的几个坑

    一.前言 近期,开始了一段辛酸的还未开始就已经结束的"创业"(参见我的第二次创业,以梦为马,莫负韶华).大体上是开发了一款微信小程序,关于创业这件事情就不细说了,本文主要介绍一下开 ...

  7. cmd 与 bash 基础命令入门

    身为一个程序员会用命令行来进行一些简单的操作,不是显得很装逼嘛!?嘿嘿~ ヾ(>∀<) cmd 与 bash 基础命令入门       简介       CMD 基础命令          ...

  8. Code::Blocks 配置

    需要另外下载安装GCC编译器 配置主题皮肤: 先替换' default.conf ' (需要关闭code::blocks) Settings>Editor>(左栏)Syntax highl ...

  9. 二维码开源库ZBar-windows下编译和使用

    源码 下载最新Zbar源码(http://zbar.sourceforge.net/),网站的WIKI是空白的,所以只能在源码包里找使用说明了,很遗憾Windows下怎么编译没说明,只是说明了Wind ...

  10. golang fmt.printf()

      package main import "fmt" import "os" type point struct { x, y int } func main ...