ARM embedded LCD driver display picture

Publisher:钱老李Latest update time:2018-04-13 Source: eefocusKeywords:ARM Reading articles on mobile phones Scan QR code
Read articles on your mobile phone anytime, anywhere

Kernel version: linux-3.4.2 lcd: 4.3


Before I go into the code, I have to explain some basic knowledge points. For LCD driver, we only need to write the code for the hardware. The following three functions have been written by the kernel for us. We only need to call them. These functions implement the data transmission between the kernel layer and the application. Friends who are interested can analyze the source code. After the driver is written, we can choose to dynamically load the driver or directly compile it into the kernel statically. The difference is explained very clearly in the previous article. I hope friends will take a look at it. Detailed explanation of static and dynamic compilation methods of Linux drivers


This code is relatively complicated, so friends must analyze it carefully. The driver program is as follows:

The lcd.c file is as follows:

#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include

#include
#include
#include

#include

static int s3c_lcdfb_setcolreg(unsigned int regno, unsigned int red,
    unsigned int green, unsigned int blue,
    unsigned int transp, struct fb_info *info);

struct 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 struct fb_ops s3c_lcdfb_ops = {
.owner  = THIS_MODULE,
.fb_setcolreg  = s3c_lcdfb_setcolreg,
.fb_fillrect  = cfb_fillrect,          
.fb_copyarea  = cfb_copyarea,
.fb_imageblit  = cfb_imageblit,
};

static struct fb_info *s3c_lcd;
static volatile unsigned long *gpbcon;
static volatile unsigned long *gpbdat;
static volatile unsigned long *gpccon;
static volatile unsigned long *gpdcon;
static volatile unsigned long *gpgcon;
static volatile struct lcd_regs* lcd_regs;
static u32 pseudo_palette[16];


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 s3c_lcdfb_setcolreg(unsigned int regno, unsigned int red,
    unsigned int green, unsigned int blue,
    unsigned int transp, struct fb_info *info)
{
unsigned int val;

if (regno > 16)
return 1;


val  = chan_to_field(red,&info->var.red);
val |= chan_to_field(green, &info->var.green);
val |= chan_to_field(blue,&info->var.blue);

//((u32 *)(info->pseudo_palette))[regno] = val;
pseudo_palette[regno] = val;
return 0;
}

static int lcd_init(void)
{

s3c_lcd = framebuffer_alloc(0, NULL);



strcpy(s3c_lcd->fix.id, "mylcd");
s3c_lcd->fix.smem_len = 480*272*16/8;
s3c_lcd->fix.type     = FB_TYPE_PACKED_PIXELS;
s3c_lcd->fix.visual   = FB_VISUAL_TRUECOLOR;
s3c_lcd->fix.line_length = 480*2;


s3c_lcd->var.xres           = 480;
s3c_lcd->var.yres           = 272;
s3c_lcd->var.xres_virtual   = 480;
s3c_lcd->var.yres_virtual   = 272;
s3c_lcd->var.bits_per_pixel = 16;


s3c_lcd->var.red.offset     = 11;
s3c_lcd->var.red.length     = 5;

s3c_lcd->var.green.offset   = 5;
s3c_lcd->var.green.length   = 6;

s3c_lcd->var.blue.offset    = 0;
s3c_lcd->var.blue.length    = 5;

s3c_lcd->var.activate       = FB_ACTIVATE_NOW;


s3c_lcd->fbops              = &s3c_lcdfb_ops;


s3c_lcd->pseudo_palette = pseudo_palette;
//s3c_lcd->screen_base  = ;   
s3c_lcd->screen_size   = 480*272*16/8;



gpbcon = ioremap(0x56000010, 8);
gpbdat = gpbcon+1;
gpccon = ioremap(0x56000020, 4);
gpdcon = ioremap(0x56000030, 4);
gpgcon = ioremap(0x56000060, 4);

    *gpccon  = 0xaaaaaaaa;  
*gpdcon  = 0xaaaaaaaa;  

*gpbcon &= ~(3);  
*gpbcon |= 1;
*gpbdat &= ~1;    

*gpgcon |= (3<<8);


lcd_regs = ioremap(0x4D000000, sizeof(struct lcd_regs));


lcd_regs->lcdcon1  = (4<<8) | (3<<5) | (0x0c<<1);

#if 1

lcd_regs->lcdcon2  = (1<<24) | (271<<14) | (1<<6) | (9);


lcd_regs->lcdcon3 = (1<<19) | (479<<8) | (1);


lcd_regs->lcdcon4 = 40;

#else
lcd_regs->lcdcon2 = S3C2410_LCDCON2_VBPD(5) | \
S3C2410_LCDCON2_LINeval_r(319) | \
S3C2410_LCDCON2_VFPD(3) | \
S3C2410_LCDCON2_VSPW(1);


lcd_regs->lcdcon3 = S3C2410_LCDCON3_HBPD(10) | \
S3C2410_LCDCON3_HOZVAL(239) | \
S3C2410_LCDCON3_HFPD(1);


lcd_regs->lcdcon4 = S3C2410_LCDCON4_MVAL(13) | \
S3C2410_LCDCON4_HSPW(0);

#endif

lcd_regs->lcdcon5 = (1<<11) | (0<<10) | (1<<9) | (1<<8) | (1<<0);


s3c_lcd->screen_base = dma_alloc_writecombine(NULL, s3c_lcd->fix.smem_len, &s3c_lcd->fix.smem_start, GFP_KERNEL);

lcd_regs->lcdsaddr1  = (s3c_lcd->fix.smem_start >> 1) & ~(3<<30);
lcd_regs->lcdsaddr2  = ((s3c_lcd->fix.smem_start + s3c_lcd->fix.smem_len) >> 1) & 0x1fffff;
lcd_regs->lcdsaddr3  = (480*16/16);  

//s3c_lcd->fix.smem_start = xxx;  

lcd_regs->lcdcon1 |= (1<<0);
lcd_regs->lcdcon5 |= (1<<3);
*gpbdat |= 1;    


register_framebuffer(s3c_lcd);

return 0;
}

static void lcd_exit(void)
{
unregister_framebuffer(s3c_lcd);
lcd_regs->lcdcon1 &= ~(1<<0);
*gpbdat &= ~1;    
dma_free_writecombine(NULL, s3c_lcd->fix.smem_len, s3c_lcd->screen_base, s3c_lcd->fix.smem_start);
iounmap(lcd_regs);
iounmap(gpbcon);
iounmap(gpccon);
iounmap(gpdcon);
iounmap(gpgcon);
framebuffer_release(s3c_lcd);
}

module_init(lcd_init);
module_exit(lcd_exit);
MODULE_LICENSE("GPL");


How to write the test program depends on your application, but the framework remains unchanged as follows: The following is to display a picture on the LCD, using the libjpeg library. You don't need to pay attention to this part. The main thing is to see how the application calls the driver. I have summarized the following functions:

static int FBDeviceInit(void)    

static int FBShowPixel(int iX, int iY, unsigned int dwColor)  

static int FBCleanScreen(unsigned int dwBackColor)      

#include
#include "jpeglib.h"
#include
#include
#include
#include
#include
#include
#include
#include
#include

#define FB_DEVICE_NAME "/dev/fb0"
#define DBG_PRINTF printf

static int g_fd;
static struct fb_var_screeninfo g_tFBVar;
static struct fb_fix_screeninfo g_tFBFix;
static unsigned char *g_pucFBMem;
static unsigned int g_dwScreenSize;

static unsigned int g_dwLineWidth;
static unsigned int g_dwPixelWidth;

static int FBDeviceInit(void)
{
int ret;

g_fd = open(FB_DEVICE_NAME, O_RDWR);  
if (0 > g_fd)
{
DBG_PRINTF("can't open %s\n", FB_DEVICE_NAME);
}

ret = ioctl(g_fd, FBIOGET_VSCREENINFO, &g_tFBVar);  
if (ret < 0)
{
DBG_PRINTF("can't get fb's var\n");
return -1;
}


ret = ioctl(g_fd, FBIOGET_FSCREENINFO, &g_tFBFix);   
if (ret < 0)
{
DBG_PRINTF("can't get fb's fix\n");
return -1;
}

g_dwScreenSize = g_tFBVar.xres * g_tFBVar.yres * g_tFBVar.bits_per_pixel / 8;  
g_pucFBMem = (unsigned char *)mmap(NULL , g_dwScreenSize, PROT_READ | PROT_WRITE, MAP_SHARED, g_fd, 0);                                          
if (0 > g_pucFBMem)
{
DBG_PRINTF("can't mmap\n");
return -1;
}

g_dwLineWidth  = g_tFBVar.xres * g_tFBVar.bits_per_pixel / 8;
g_dwPixelWidth = g_tFBVar.bits_per_pixel / 8;

return 0;
}

static int FBShowPixel(int iX, int iY, unsigned int dwColor)  
{
unsigned char *pucFB;
unsigned short *pwFB16bpp;
unsigned int *pdwFB32bpp;
unsigned short wColor16bpp;
int iRed;
int iGreen;
int iBlue;

if ((iX >= g_tFBVar.xres) || (iY >= g_tFBVar.yres))
{
DBG_PRINTF("out of region\n");
return -1;
}

pucFB      = g_pucFBMem + g_dwLineWidth * iY + g_dwPixelWidth * iX;
pwFB16bpp  = (unsigned short *)pucFB;
pdwFB32bpp = (unsigned int *)pucFB;

switch (g_tFBVar.bits_per_pixel)
{
case 8:
{
*pucFB = (unsigned char)dwColor;
break;
}
case 16:
{
iRed   = (dwColor >> (16+3)) & 0x1f;
iGreen = (dwColor >> (8+2)) & 0x3f;
iBlue  = (dwColor >> 3) & 0x1f;
wColor16bpp = (iRed << 11) | (iGreen << 5) | iBlue;
*pwFB16bpp  = wColor16bpp;
break;
}
case 32:
{
*pdwFB32bpp = dwColor;
break;
}
default :
{
DBG_PRINTF("can't support %d bpp\n", g_tFBVar.bits_per_pixel);
return -1;
}
}

return 0;
}

static int FBCleanScreen(unsigned int dwBackColor)
{
unsigned char *pucFB;
unsigned short *pwFB16bpp;
unsigned int *pdwFB32bpp;
unsigned short wColor16bpp;
int iRed;
int iGreen;
int iBlue;
int i = 0;

pucFB      = g_pucFBMem;
pwFB16bpp  = (unsigned short *)pucFB;
pdwFB32bpp = (unsigned int *)pucFB;


switch (g_tFBVar.bits_per_pixel)
{
case 8:
{
memset(g_pucFBMem, dwBackColor, g_dwScreenSize);
break;
}
case 16:
{
iRed   = (dwBackColor >> (16+3)) & 0x1f;
iGreen = (dwBackColor >> (8+2)) & 0x3f;
iBlue  = (dwBackColor >> 3) & 0x1f;
wColor16bpp = (iRed << 11) | (iGreen << 5) | iBlue;
while (i < g_dwScreenSize)
{
*pwFB16bpp  = wColor16bpp;
pwFB16bpp++;
i += 2;
}
break;
}
case 32:
{
while (i < g_dwScreenSize)
{
*pdwFB32bpp  = dwBackColor;
pdwFB32bpp++;
i += 4;
}
break;
}
default :
{
DBG_PRINTF("can't support %d bpp\n", g_tFBVar.bits_per_pixel);
return -1;
}
}


return 0;
}


static int FBShowLine(int iXStart, int iXEnd, int iY, unsigned char *pucRGBArray)
{
int i = iXStart * 3;
int iX;
unsigned int dwColor;


if (iY >= g_tFBVar.yres)
return -1;


if (iXStart >= g_tFBVar.xres)
return -1;


if (iXEnd >= g_tFBVar.xres)
{
iXEnd = g_tFBVar.xres;
}

for (iX = iXStart; iX < iXEnd; iX++)
{

dwColor = (pucRGBArray[i]<<16) + (pucRGBArray[i+1]<<8) + (pucRGBArray[i+2]<<0);
i += 3;
FBShowPixel(iX, iY, dwColor);
}
return 0;
}



int main(int argc, char **argv)
{
struct jpeg_decompress_struct cinfo;
struct jpeg_error_mgr jerr;
FILE * infile;
int row_stride;
unsigned char *buffer;

if (argc != 2)
{
printf("Usage: \n");
printf("%s \n", argv[0]);
return -1;
}

if (FBDeviceInit())
{
return -1;
}

FBCleanScreen(0);



// 分配和初始化一个decompression结构体
cinfo.err = jpeg_std_error(&jerr);
jpeg_create_decompress(&cinfo);

// 指定源文件
if ((infile = fopen(argv[1], "rb")) == NULL) {
fprintf(stderr, "can't open %s\n", argv[1]);
return -1;
}
jpeg_stdio_src(&cinfo, infile);

// 用jpeg_read_header获得jpg信息
jpeg_read_header(&cinfo, TRUE);

printf("image_width = %d\n", cinfo.image_width);
printf("image_height = %d\n", cinfo.image_height);
printf("num_components = %d\n", cinfo.num_components);

// 设置解压参数,比如放大、缩小
printf("enter scale M/N:\n");
scanf("%d/%d", &cinfo.scale_num, &cinfo.scale_denom);
printf("scale to : %d/%d\n", cinfo.scale_num, cinfo.scale_denom);

// 启动解压:jpeg_start_decompress
jpeg_start_decompress(&cinfo);


printf("output_width = %d\n", cinfo.output_width);
printf("output_height = %d\n", cinfo.output_height);
printf("output_components = %d\n", cinfo.output_components);

// 一行的数据长度
row_stride = cinfo.output_width * cinfo.output_components;
buffer = malloc(row_stride);

// 循环调用jpeg_read_scanlines来一行一行地获得解压的数据
while (cinfo.output_scanline < cinfo.output_height) 
{
(void) jpeg_read_scanlines(&cinfo, &buffer, 1);

// 写到LCD去
FBShowLine(0, cinfo.output_width, cinfo.output_scanline, buffer);
}

free(buffer);
jpeg_finish_decompress(&cinfo);
jpeg_destroy_decompress(&cinfo);

return 0;
}


Keywords:ARM Reference address:ARM embedded LCD driver display picture

Previous article:ARM embedded application debugging input simulator writing test simulation function
Next article:ARM筆記:应用调试之自制系统调用、编写进程查看器(二)

Latest Microcontroller Articles
Change More Related Popular Components

EEWorld
subscription
account

EEWorld
service
account

Automotive
development
circle

About Us Customer Service Contact Information Datasheet Sitemap LatestNews


Room 1530, 15th Floor, Building B, No.18 Zhongguancun Street, Haidian District, Beijing, Postal Code: 100190 China Telephone: 008610 8235 0740

Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved 京ICP证060456号 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号