Mini2440 linux之LCD驱动 移植、以及一些遇到的问题

13 篇文章 12 订阅
4 篇文章 0 订阅

首先编译环境配置

linux 2.6.32源码

arm-linux-gcc  交叉编译器

有mini2440的根文件系统

这里默认环境都配好了

 

打开linux源码文件夹,进入后,找到对应开发板的配置文件

将其复制一份重命名为.config

cp config_mini2440_n35 .config

然后执行内核配置,更加自己需要增减模块后退出

make menuconfig

 将默认的LCD配置去掉,换上我们自己的驱动  完成后 exit退出

找到Device Drivers ->Graphics support->support for frame buffer devices->

去掉默认的s3c2440 lcd frame buffer support

勾上我们自己添加的mylcd lcd frame buffer support

 

 

 

 这里讲讲怎么添加自己的选项

打开目录  linux2.6.32/drivers/video

修改里面的Kconfig文件和makefile文件

在Kconfig中找到config FB_S3C2410附近的行添加

config FB_MYLCD
	tristate "MY LCD framebuffer support"
	depends on FB && ARCH_S3C2410
	select FB_CFB_FILLRECT
	select FB_CFB_COPYAREA
	select FB_CFB_IMAGEBLIT
	---help---
	  Frame buffer driver for the built-in LCD controller in the Samsung
	  S3C2410 processor.

	  This driver is also available as a module ( = code which can be
	  inserted and removed from the running kernel whenever you want). The
	  module will be called s3c2410fb. If you want to compile it as a module,
	  say M here and read <file:Documentation/kbuild/modules.txt>.

	  If unsure, say N.

 添加后是这样

打开makefile

在最下面添加,这里的mylcd2是我写的驱动文件,也是在此目录下的

obj-$(CONFIG_FB_MYLCD)	  += mylcd2.o

 添加后是这样(obj-m 表示模块编译,obj-y 表示编译进内核)

 

接下来下lcd的驱动,在linux2.6.32/drivers/video目录下,创建mylcd2.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>

#include <mach/regs-lcd.h>
#include <mach/regs-gpio.h>
#include <mach/fb.h>

 
/*LCD :  240*320     */
#define   LCD_xres     240        //LCD 行分辨率
#define   LCD_yres     320          //LCD列分辨率


/*
根据像素时钟频率计算行频(HSF)和场频(VSF):
一行刷新率  HSF=VCLK÷[(HSPW+1)+(HSPD+1)+(HFPD+1)+(HOZVAL+1)]
帧率       VSF=HSF÷[(VSPW+1)+(VBPD+1)+(VFPD+1)+(LINEVAL+1)]
*/



//LCDCON1
#define LCD_CLKVAL    8         //[17:8]   5.6MHZ的像素时钟频率=8
#define LCD_PNRMODE   3         //[6:5]  =0b11  tft
#define LCD_BPPMODE   12         //[4:1] =0b1100 16bpp

//LCDCON2
#define LCD_VBPD     (1-1)     //[31:24] 这个值越大 显示的图像就越往下偏,根据屏幕的典型值配置即可
#define LCD_LINEVAL  (LCD_yres-1)     //[23:14]
#define LCD_VFPD     (5-1)     //[13:6]  
#define LCD_VSPW     (1-1)     //[5:0]

//LCDCON3
#define LCD_HBPD     (36-1)     //[25:19]  这个值越大 显示的图像就越往右偏
#define LCD_HOZVAL   (LCD_xres-1)    //[18:8]
#define LCD_HFPD     (19-1)//(2-1)    //[7:0] 

//LCDCON4
#define LCD_HSPW     (5-1)     //[7:0]

//LCDCON5 
#define LCD_FRM565     1     //[11]  =0,5:5:5:1; =1,5:6:5
#define LCD_INVVCLK    1    //[10]
#define LCD_INVLINE    1    //[9]
#define LCD_INVVFRAME   1    //[8]
#define LCD_BSWP       0   //[1]
#define LCD_HWSWP      1   //[0]




/* GPIO prot   */
static unsigned long  *GPBcon;
static unsigned long  *GPCcon;
static unsigned long  *GPDcon;
static unsigned long  *GPGcon;  //GPG4:控制LCD信号 
static unsigned long  *GPBdat;   //GPB0: 控制背光


/* LCD control */ 
 struct  lcd_reg{  
      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       tconsel;  
};

static struct lcd_reg  *lcd_reg;
 
static struct fb_info *my_lcd;    //定义一个全局变量
static u32 pseudo_palette[16];   //调色板数组,被fb_info->pseudo_palette调用

static inline unsigned int chan_to_field(unsigned int chan, struct fb_bitfield *bf)
{
/*内核中的单色都是16位,默认从左到右排列,比如G颜色[0x1f],那么chan就等于0XF800*/
       chan       &= 0xffff;
       chan       >>= 16 - bf->length;    //右移,将数据靠到位0上
       return chan << bf->offset;    //左移一定偏移值,放入16色数据中对应的位置
}

static int my_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)        //调色板数组不能大于15
      return 1;

       /* 用red,green,blue三个颜色值构造出16色数据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;     //放到调色板数组中
       return 0;
}


static struct fb_ops my_lcdfb_ops = {
      .owner           = THIS_MODULE,
      .fb_setcolreg  = my_lcdfb_setcolreg,//调用my_lcdfb_setcolreg()函数,来设置调色板fb_info-> pseudo_palette
      .fb_fillrect       = cfb_fillrect,     //填充矩形
      .fb_copyarea   = cfb_copyarea,     //复制数据
      .fb_imageblit  = cfb_imageblit,    //绘画图形,
};

static int lcd_init(void)
{
  /*1.申请一个fb_info结构体*/
  my_lcd= framebuffer_alloc(0,0); 

  /*2.设置fb_info*/
 
  /* 2.1设置固定的参数fb_info-> fix */
  /*my_lcd->fix.smem_start    物理地址后面注册MDA缓存区设置*/ 
  strcpy(my_lcd->fix.id, "mylcd");       //名字
  my_lcd->fix.smem_len =LCD_xres*LCD_yres*2;     //地址长
  my_lcd->fix.type        =FB_TYPE_PACKED_PIXELS;
  my_lcd->fix.visual    =FB_VISUAL_TRUECOLOR;    //真彩色
  my_lcd->fix.line_length      =LCD_xres*2;       //LCD 一行的字节

  /* 2.2 设置可变的参数fb_info-> var  */
  my_lcd->var.xres        =LCD_xres;          //可见屏X 分辨率
  my_lcd->var.yres        =LCD_yres;          //可见屏y 分辨率
  my_lcd->var.xres_virtual     =LCD_xres;           //虚拟屏x分辨率
  my_lcd->var.yres_virtual     =LCD_yres;           //虚拟屏y分辨率
  my_lcd->var.xoffset           = 0;     //虚拟到可见屏幕之间的行偏移
  my_lcd->var.yoffset           = 0;      //虚拟到可见屏幕之间的行偏移

  my_lcd->var.bits_per_pixel  =   16;        //像素为16BPP
  my_lcd->var.grayscale       =   0;    //灰色比例
 
  my_lcd->var.red.offset      =     11;
  my_lcd->var.red.length      =     5;
  my_lcd->var.green.offset  =       5;
  my_lcd->var.green.length  =       6;
  my_lcd->var.blue.offset     =     0;
  my_lcd->var.blue.length     =     5;

/* 2.3 设置操作函数fb_info-> fbops  */
  my_lcd->fbops         = &my_lcdfb_ops;


  /* 2.4 设置fb_info 其它的成员  */ 
 /*my_lcd->screen_base    虚拟地址在后面注册MDA缓存区设置*/
  my_lcd->pseudo_palette   =pseudo_palette;    //保存调色板数组
  my_lcd->screen_size          =LCD_xres * LCD_yres *2;     //虚拟地址长

  /*3   设置硬件相关的操作*/
  /*3.1 配置LCD引脚*/
  GPBcon = ioremap(0x56000010, 8);
  GPBdat = GPBcon+1;
  GPCcon = ioremap(0x56000020, 4);
  GPDcon = ioremap(0x56000030, 4);
  GPGcon = ioremap(0x56000060, 4);

  *GPBcon    &=~(0x03<<(1*2));
  *GPBcon    |= (0x01<<(1*2));     //PGB1背光
  *GPBdat    &=~(0X1<<1);           //关背光

  *GPCcon    =0xaaaaaaaa;
  *GPDcon    =0xaaaaaaaa;
  *GPGcon    |=(0x03<<(4*2));    //GPG4:LCD信号

  /*3.2 根据LCD手册设置LCD控制器,参考之前的裸机驱动*/
  lcd_reg=ioremap(0X4D000000, sizeof( lcd_reg) );

	//240*320
	/*
     	TFT LCD的TTL信号
     	VSYNC                        垂直同步信号
     	HSYNC                        水平同步信号
     	HCLK                           像素时钟信号
     	VD[23:0]                       数据信号
     	LEND                           行结束信号(非必须)                   
     	PWREN                       电源开关信号

	HSYNC表示“是跳到最左边的时候了”,VSYNC表示“是跳到最上边的时候了”
	VSYNC可以理解为一帧图像有效地信号,在它低电平有效的时候,要连续发出(LINEVAL+1)
	个行有效数据HSYNC信号;而在HSYNC有效地时候,要连续发出(HOZVAL+1)个像素有效数据,
	这时像素数据的频率是由VCLK控制,VCLK是作为时序图的基准信号。我们开发板上用的屏是
	240*320的3.5寸触摸屏,那么LINEVAL+1=240,HOZVAL+1=320。

	lcd时钟信号频率,是像素时钟
	VCLK=HCLK/[(CLKVAL+1)*2]
	TFT:VCLK = 101.25M / [(CLKVAL + 1) × 2] = 6.4M CLKVAL = 7
	bit[17:8] CLKVAL = 7
	bit[6:5] PNRMODE=0b11 (tft lcd)
	bit[4:1] BPPMODE=0b1100 (tft 16bpp)
	bit[0] ENDIV=0 lcd使能信号,一开始先关闭使能
	*/
	lcd_reg->lcdcon1 = (LCD_CLKVAL<<8) | (3<<5) | (12<<1);
	/*
	水平信号参数
	(T0-T2-T1=VBPD+1)
	bit[31:24] VBPD=1  (upper_margin)VBPD表示VSYNC信号脉冲之后,还要经过VBPD+1个HSYNC信号周期,有效行才出现。
	bit[23:14] LINEVAL=319   y-1
	bit[13:6] VFPD=1   (T5-T2=vfpd+1)(lower_margin)VFPD表示在连续发出LINEVAL+1(lineecal=y-1)行有效数据后,还要经过VFPD+1个无效行,之后完整的一帧结束。
	bit[5:0] VSPW=0	  VSPW表示VSYNC信号的脉冲宽度为(VSPW+1)个,1帧图像周期
 	
	*/
	//lcd_reg->lcdcon2 = (1<<24) | (319<<14) | (1<<6) | (1<<0);
	lcd_reg->lcdcon2 = (LCD_VBPD<<24) | (LCD_LINEVAL<<14) | (LCD_VFPD<<6) | (LCD_VSPW<<0);
	/*
	hoz信号参数
	T6-T7-T8
	bit[25:19] HBPD=19  HBPD表示在HSYNC信号脉冲之后,还要经过HBPD+1个VCLK信号周期,有效像素才能够出现。
	bit[18:8] HOZVAL=239
	bit[7:0] HFPD==9    (T8-T11)HFPD表示在连续发出HOZVAL+1个像素的有效数据之后,还要发出(HFPD+1)个无效像素,完整的一行结束。
	
	*/
	//lcd_reg->lcdcon3 = (19<<19) | (239<<8) | (9<<0);
	lcd_reg->lcdcon3 = (LCD_HBPD<<19) | (LCD_HOZVAL<<8) | (LCD_HFPD<<0);
	/*
	垂直信号参数
	bit[7:0] HSPW=9   HSPW表示HSYNC信号脉冲宽度为(HSPW+1)个VCLK信号周期,即HSPW+1个像素是无效的。
	*/
	lcd_reg->lcdcon4 = (LCD_HSPW<<0);
	/*
	引脚的极性设置
	bit[11] FRM565=1
	bit[10] INVVCLK=1 上升沿取数据
	bit[9] INVVLINE=1 水平同步信号反相
	bit[8] INVVFRAME=1 垂直同步信号反相
	bit[7] INVVD=0 数据信号不需要反相
	bit[6] INVVDEN=0 数据使能信号不需要反相
	bit[5] INVPWREN=0 电源使能信号不需要反相
	bit[3] PWREN=0 先不使用电源使能信号
	bit[1:0] BSWP=0 HWSWP=1 设置字节排列的顺序

	lcd_reg->lcdcon5 = (LCD_FRM565<<11) | (LCD_INVVCLK<<10) | (LCD_INVLINE<<9) | (LCD_INVVFRAME<<8) | (0<<7) | (0<<6) | (0<<5) | (0<<3) | (LCD_BSWP<<1) | (LCD_HWSWP<<0);

   	lcd_reg->lcdcon1     &=~(1<<0);      // 关闭PWREN信号输出
   	lcd_reg->lcdcon5     &=~(1<<3);      //禁止PWREN信号

 /* 3.3  分配显存(framebuffer),把地址告诉LCD控制器和fb_info*/
   my_lcd->screen_base=dma_alloc_writecombine(NULL,my_lcd->fix.smem_len,  &my_lcd->fix.smem_start, GFP_KERNEL);

   /*lcd控制器的地址必须是物理地址*/
	lcd_reg->lcdsaddr1  = my_lcd->fix.smem_start >> 1;
	lcd_reg->lcdsaddr2  = my_lcd->fix.smem_start;
	lcd_reg->lcdsaddr2 += LCD_yres*LCD_xres*2;
	lcd_reg->lcdsaddr2 >>= 1;

	lcd_reg->lcdsaddr3 = (0<<11)|(LCD_xres<<0);  //一行的长度,单位是半字
    //PAGEWIDTH [10:0]:保存LCD一行占的宽度(半字数为单位)

	printk(KERN_EMERG"ADDRstart = 0x%08lx\n", my_lcd->fix.smem_start);
	printk(KERN_EMERG"ADDRend = 0x%08lx\n", my_lcd->fix.smem_start+LCD_yres*LCD_xres*2);
	printk(KERN_EMERG"LCDSADDR1 = 0x%08lx\n", lcd_reg->lcdsaddr1);
	printk(KERN_EMERG"LCDSADDR2 = 0x%08lx\n", lcd_reg->lcdsaddr2);
	printk(KERN_EMERG"LCDSADDR3 = 0x%08lx\n", lcd_reg->lcdsaddr3);



	lcd_reg->tconsel &=~(1<<0); 
                                                                                                                         

   /*4开启LCD,并注册fb_info: register_framebuffer()*/
   /*4.1 直接在init函数中开启LCD(后面讲到电源管理,再来优化)*/
  lcd_reg->lcdcon1      |=1<<0;         //输出PWREN信号
  lcd_reg->lcdcon5      |=1<<3;        //允许PWREN信号
  *GPBdat           |=(0X1<<1);           //开背光

   /*4.2 注册fb_info*/
   register_framebuffer(my_lcd);
   return 0;
}

static int lcd_exit(void)
{
   /* 1卸载内核中的fb_info*/
     unregister_framebuffer(my_lcd);

  /*2 控制LCDCON1关闭PWREN信号,关背光,iounmap注销地址*/
   lcd_reg->lcdcon1 &=~(1<<0);      // 关闭PWREN信号输出
   lcd_reg->lcdcon5 &=~(1<<3);    //禁止PWREN信号
   *GPBdat    &=~(0X1<<1);           //关背光

   iounmap(GPBcon);
   iounmap(GPCcon);
   iounmap(GPDcon);
   iounmap(GPGcon);

  /*3.释放DMA缓存地址dma_free_writecombine()*/
  dma_free_writecombine(0,my_lcd->screen_size,my_lcd->screen_base,my_lcd->fix.smem_start);

   /*4.释放注册的fb_info*/
   framebuffer_release(my_lcd);

   return 0;
}

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

回到linux2.6.32目录

执行命令,编译内核

make zImage -j4

 编译模块

make modules -j4

编译好内核后,到目录 linux2.6.32/arch/arm/boot

找到zImage

复制出来,通过supervivi写入内核到flash

开发板norflash启动

选择 k 命令

到DWN工具上传内核

开机即可看到linux的小企鹅图标出来了

这里可以直接对fb文件进行操作

输入以下命令可以看到屏幕上打印出hello

echo HELLO >> /dev/tty1

 输入以下命令是乱码

cat /dev/urandom >> /dev/fb0

 

 

遇到的问题:

1.关于内核启动不了

首先linux中的路径linux2.6.32/arch/arm/mach-mini2440.c文件中的分区确保与supervivi一致

static struct mtd_partition friendly_arm_default_nand_part[] = {
	[0] = {
		.name	= "vivi",
		.size	= 0x00040000,
		.offset	= 0,
	},
	[1] = {
		.name	= "param",
		.offset = 0x00040000,
		.size	= 0x00010000,
	},
	[2] = {
		.name	= "kernel",
		.offset = 0x00050000,
		.size	= 0x002b0000,
	},
	[3] = {
		.name	= "root",
		.offset = 0x00300000,
		.size	= 0x03d00000, //
	},
	[4] = {
		.name	= "nand",
		.offset = 0x00000000,
		.size	= 0x04000000, //
	}


};

在supervivi中确保分区的正确性(其中大小根据自己实际调整也行)

查询分区

##### FriendlyARM BIOS for 2440 #####
[x] bon part 0 320k 2368k
[v] Download vivi 
[k] Download linux kernel 
[y] Download root_yaffs image 
[c] Download root_cramfs image 
[a] Absolute User Application
[n] Download Nboot 
[e] Download Eboot 
[i] Download WinCE NK.nb0 
[w] Download WinCE NK.bin 
[d] Download & Run 
[z] Download zImage into RAM 
[g] Boot linux from RAM 
[f] Format the nand flash 
[p] Partition for Linux 
[b] Boot the system 
[s] Set the boot parameters 
[t] Print the TOC struct of wince 
[u] Backup NAND Flash to HOST through USB(upload) 
[r] Restore NAND Flash from HOST through USB 
[q] Goto shell of vivi 
Enter your selection: q
Supervivi> 
Supervivi> 
Supervivi> 
Supervivi> 
Supervivi> part show


正确的分区
name            :       offset          size            flag
------------------------------------------------------------
vivi            :       0x00000000      0x00040000      0
param           :       0x00040000      0x0010000      0
kernel          :       0x00050000      0x00250000      0
root            :       0x00300000      0x03d00000      0

启动参数确保正确

##### FriendlyARM BIOS for 2440 #####
[x] bon part 0 320k 2368k
[v] Download vivi 
[k] Download linux kernel 
[y] Download root_yaffs image 
[c] Download root_cramfs image 
[a] Absolute User Application
[n] Download Nboot 
[e] Download Eboot 
[i] Download WinCE NK.nb0 
[w] Download WinCE NK.bin 
[d] Download & Run 
[z] Download zImage into RAM 
[g] Boot linux from RAM 
[f] Format the nand flash 
[p] Partition for Linux 
[b] Boot the system 
[s] Set the boot parameters 
[t] Print the TOC struct of wince 
[u] Backup NAND Flash to HOST through USB(upload) 
[r] Restore NAND Flash from HOST through USB 
[q] Goto shell of vivi 
Enter your selection: s

##### Parameter Menu #####
[r] Reset parameter table to default table 
[s] Set parameter 
[v] View the parameter table  
[w] Write the parameter table to flash memeory 
[q] Quit 
Enter your selection: v
Number of parameters: 9
name                    :          hex             integer
-------------------------------------------------------------
mach_type               :       000007cf                 1999
media_type              :       00000003                    3
boot_mem_base           :       30000000            805306368
baudrate                :       0001c200               115200
xmodem                  :       00000001                    1
xmodem_one_nak          :       00000000                    0
xmodem_initial_timeout  :       000493e0               300000
xmodem_timeout          :       000f4240              1000000
boot_delay              :       01000000             16777216
Linux command line: noinitrd root=/dev/mtdblock3 init=/linuxrc console=ttySAC0,115200

这里的root=/dev/mtdblokck3,要看自己的分区实际是几号0/1/2/3,本分区root刚好排在第4个所以是3,如果填错了是找不到linuxrc文件的,就是没法运行控制台

2.LCD图像显示偏移,有黑边,这里是lcd控制器的参数没设置对,如mylcd2.c文件中最开头宏定义注释

所说,按lcd手册值设置即可,也可手动微调下

#define LCD_VBPD     (1-1)     //[31:24] 这个值越大 显示的图像就越往下偏,根据屏幕的典型值配置即可
#define LCD_HBPD     (36-1)     //[25:19]  这个值越大 显示的图像就越往右偏

3.图像下半部分显示到上面,左边有部分显示到右边

这个是因为在把显存地址给lcd控制器时,值给错误导致的

lcdsaddr1、lcdsaddr2、lcdsaddr3,检查这三个寄存器所给的值是否正确

 

 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值