LCD 驅動程序分析和圖像顯示測試代碼

這個代碼相對有點複雜,朋友們一定要仔細分析。驅動程序如下:

lcd.c文件如下:

#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/fb.h>
#include <linux/init.h>
#include <linux/dma-mapping.h>
#include <linux/interrupt.h>
#include <linux/workqueue.h>
#include <linux/wait.h>
#include <linux/platform_device.h>
#include <linux/clk.h>

#include <asm/io.h>
#include <asm/uaccess.h>
#include <asm/div64.h>

#include <asm/mach/map.h>

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];

/* 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 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;

/* 用red,green,blue三原色構造出val */
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)
{
/* 1. 分配一個fb_info */
s3c_lcd = framebuffer_alloc(0, NULL);

/* 2. 設置 /
/
2.1 設置固定的參數 /
strcpy(s3c_lcd->fix.id, “mylcd”);
s3c_lcd->fix.smem_len = 480
27216/8;
s3c_lcd->fix.type     = FB_TYPE_PACKED_PIXELS;
s3c_lcd->fix.visual   = FB_VISUAL_TRUECOLOR; /
TFT /
s3c_lcd->fix.line_length = 480
2;

/* 2.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;

/* RGB:565 */
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;

/* 2.3 設置操作函數 */
s3c_lcd->fbops              = &s3c_lcdfb_ops;

/* 2.4 其他的設置 /
s3c_lcd->pseudo_palette = pseudo_palette;
//s3c_lcd->screen_base  = ;  /
顯存的虛擬地址
s3c_lcd->screen_size   = 480
272*16/8;

/* 3. 硬件相關的操作 /
/
3.1 配置GPIO用於LCD */
gpbcon = ioremap(0x56000010, 8);
gpbdat = gpbcon+1;
gpccon = ioremap(0x56000020, 4);
gpdcon = ioremap(0x56000030, 4);
gpgcon = ioremap(0x56000060, 4);

gpccon  = 0xaaaaaaaa;   / GPIO管腳用於VD[7:0],LCDVF[2:0],VM,VFRAME,VLINE,VCLK,LEND */
gpdcon  = 0xaaaaaaaa;   / GPIO管腳用於VD[23:8] */

gpbcon &= ~(3);  / GPB0設置爲輸出引腳 */
*gpbcon |= 1;
gpbdat &= ~1;     / 輸出低電平 */

gpgcon |= (3<<8); / GPG4用作LCD_PWREN */

/* 3.2 根據LCD手冊設置LCD控制器, 比如VCLK的頻率等 */
lcd_regs = ioremap(0x4D000000, sizeof(struct lcd_regs));

/* bit[17:8]: VCLK = HCLK / [(CLKVAL+1) x 2], LCD手冊P14

  • 10MHz(100ns) = 100MHz / [(CLKVAL+1) x 2]
  • CLKVAL = 4
  • bit[6:5]: 0b11, TFT LCD
  • bit[4:1]: 0b1100, 16 bpp for TFT
  • bit[0]  : 0 = Disable the video output and the LCD control signal.
    */
    lcd_regs->lcdcon1  = (4<<8) | (3<<5) | (0x0c<<1);

#if 1
/* 垂直方向的時間參數

  • bit[31:24]: VBPD, VSYNC之後再過多長時間才能發出第1行數據
  • LCD手冊 T0-T2-T1=4
  • VBPD=3
  • bit[23:14]: 多少行, 320, 所以LINEVAL=320-1=319
  • bit[13:6] : VFPD, 發出最後一行數據之後,再過多長時間才發出VSYNC
  • LCD手冊T2-T5=322-320=2, 所以VFPD=2-1=1
  • bit[5:0]  : VSPW, VSYNC信號的脈衝寬度, LCD手冊T1=1, 所以VSPW=1-1=0
    */
    lcd_regs->lcdcon2  = (1<<24) | (271<<14) | (1<<6) | (9);

/* 水平方向的時間參數

  • bit[25:19]: HBPD, VSYNC之後再過多長時間才能發出第1行數據
  • LCD手冊 T6-T7-T8=17
  • HBPD=16
  • bit[18:8]: 多少列, 240, 所以HOZVAL=240-1=239
  • bit[7:0] : HFPD, 發出最後一行裏最後一個象素數據之後,再過多長時間才發出HSYNC
  • LCD手冊T8-T11=251-240=11, 所以HFPD=11-1=10
    */
    lcd_regs->lcdcon3 = (1<<19) | (479<<8) | (1);

/* 水平方向的同步信號

  • bit[7:0] : HSPW, HSYNC信號的脈衝寬度, LCD手冊T7=5, 所以HSPW=5-1=4
    */
    lcd_regs->lcdcon4 = 40;

#else
lcd_regs->lcdcon2 = S3C2410_LCDCON2_VBPD(5) |
S3C2410_LCDCON2_LINEVAL(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
/* 信號的極性

  • bit[11]: 1=565 format
  • bit[10]: 0 = The video data is fetched at VCLK falling edge
  • bit[9] : 1 = HSYNC信號要反轉,即低電平有效
  • bit[8] : 1 = VSYNC信號要反轉,即低電平有效
  • bit[6] : 0 = VDEN不用反轉
  • bit[3] : 0 = PWREN輸出0
  • bit[1] : 0 = BSWP
  • bit[0] : 1 = HWSWP 2440手冊P413
    */
    lcd_regs->lcdcon5 = (1<<11) | (0<<10) | (1<<9) | (1<<8) | (1<<0);

/* 3.3 分配顯存(framebuffer), 並把地址告訴LCD控制器 */
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  = (48016/16);  / 一行的長度(單位: 2字節) */

//s3c_lcd->fix.smem_start = xxx;  /* 顯存的物理地址 /
/
啓動LCD /
lcd_regs->lcdcon1 |= (1<<0); /
使能LCD控制器 /
lcd_regs->lcdcon5 |= (1<<3); /
使能LCD本身 */
gpbdat |= 1;     / 輸出高電平, 使能背光 */

/* 4. 註冊 */
register_framebuffer(s3c_lcd);

return 0;
}

static void lcd_exit(void)
{
unregister_framebuffer(s3c_lcd);
lcd_regs->lcdcon1 &= ~(1<<0); /* 關閉LCD本身 */
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”);

測試程序怎麼編寫就看你的應用程序了,但是框架是不變的如下:下面是在Lcd上顯示一幅圖片,用到了libjpeg庫,這個地方可以不用關注,主要是看看應用程序怎麼調用驅動程序的,我總結有如下當函數:

static int FBDeviceInit(void)    /* FB初始化函數 */

static int FBShowPixel(int iX, int iY, unsigned int dwColor)  /* 填充像素,具體怎麼填充看如下代碼中 */

static int FBCleanScreen(unsigned int dwBackColor)      /* 清屏函數,把顯示屏初始化爲黑色 */

#include <stdio.h>
#include “jpeglib.h”
#include <setjmp.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <linux/fb.h>
#include <string.h>
#include <stdlib.h>

#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);   /* 打開lcd驅動設備節點 */
if (0 > g_fd)
{
DBG_PRINTF(“can’t open %s\n”, FB_DEVICE_NAME);
}

ret = ioctl(g_fd, FBIOGET_VSCREENINFO, &g_tFBVar);   /* 獲取lcd可變參數 */
if (ret < 0)
{
DBG_PRINTF(“can’t get fb’s var\n”);
return -1;
}

ret = ioctl(g_fd, FBIOGET_FSCREENINFO, &g_tFBFix);   /* 獲取lcd固定參數 */
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;  /* 計算lcd屏幕大小 /
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)  /* 填充像素,dwcolor就是我們要在一個像素顯示的顏色 */
{
unsigned char *pucFB;
unsigned short *pwFB16bpp;
unsigned int pdwFB32bpp;
unsigned short wColor16bpp; /
565 */
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; /
565 */
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++)
{
/* 0xRRGGBB */
dwColor = (pucRGBArray[i]<<16) + (pucRGBArray[i+1]<<8) + (pucRGBArray[i+2]<<0);
i += 3;
FBShowPixel(iX, iY, dwColor);
}
return 0;
}

/*
Allocate and initialize a JPEG decompression object    // 分配和初始化一個decompression結構體
Specify the source of the compressed data (eg, a file) // 指定源文件
Call jpeg_read_header() to obtain image info   // 用jpeg_read_header獲得jpg信息
Set parameters for decompression   // 設置解壓參數,比如放大、縮小
jpeg_start_decompress(…);   // 啓動解壓:jpeg_start_decompress
while (scan lines remain to be read)
jpeg_read_scanlines(…);  // 循環調用jpeg_read_scanlines
jpeg_finish_decompress(…);   // jpeg_finish_decompress
Release the JPEG decompression object   // 釋放decompression結構體
*/

/* Uage: jpg2rgb <jpg_file>
 */

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 <jpg_file>\n", argv[0]);
return -1;
}

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

FBCleanScreen(0);

/* 以下部分是把圖片解壓出來,取出各個像素值放在一個緩衝區中,我們只需要把這些像素填進lcd當中去就行 */

// 分配和初始化一個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;
}
————————————————
版權聲明:本文爲CSDN博主「HeroKern」的原創文章,遵循 CC 4.0 BY-SA 版權協議,轉載請附上原文出處鏈接及本聲明。
原文鏈接:https://blog.csdn.net/qq_21792169/article/details/50427961

發佈了39 篇原創文章 · 獲贊 7 · 訪問量 8418
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章