企业🤖AI智能体构建引擎,智能编排和调试,一键部署,支持私有化部署方案 广告
以前没有写Lcd驱动程序,现在开始做项目了,才发现Lcd驱动程序必须认真学习。还是老规矩先上代码。 内核版本:linux-3.4.2                   lcd:4.3 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 longlcdcon1; unsigned longlcdcon2; unsigned longlcdcon3; unsigned longlcdcon4; unsigned longlcdcon5;     unsigned longlcdsaddr1;     unsigned longlcdsaddr2;     unsigned longlcdsaddr3;     unsigned longredlut;     unsigned longgreenlut;     unsigned longbluelut;     unsigned longreserved[9];     unsigned longdithmode;     unsigned longtpal;     unsigned longlcdintpnd;     unsigned longlcdsrcpnd;     unsigned longlcdintmsk;     unsigned longlpcsel; }; 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*272*16/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  = (480*16/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; } ~~~