日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > linux >内容正文

linux

linux 修改分辨率lcd_16.Linux-LCD驱动(详解)

發布時間:2025/4/5 linux 30 豆豆
生活随笔 收集整理的這篇文章主要介紹了 linux 修改分辨率lcd_16.Linux-LCD驱动(详解) 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

在上一節

1) 分配一個fb_info結構體: framebuffer_alloc();

2) 設置fb_info

3) 設置硬件相關的操作

4) 使能LCD,并注冊fb_info: register_framebuffer()

本節需要用到的函數:

void *dma_alloc_writecombine(struct device *dev, size_t size, dma_addr_t *handle, gfp_t gfp); //分配DMA緩存區給顯存//返回值為:申請到的DMA緩沖區的虛擬地址,若為NULL,表示分配失敗,則需要使用dma_free_writecombine()釋放內存,避免內存泄漏//參數如下://*dev:指針,這里填0,表示這個申請的緩沖區里沒有內容//size:分配的地址大小(字節單位)//*handle:申請到的物理起始地址//gfp:分配出來的內存參數,標志定義在,常用標志如下://GFP_ATOMIC 用來從中斷處理和進程上下文之外的其他代碼中分配內存. 從不睡眠.//GFP_KERNEL 內核內存的正常分配. 可能睡眠.//GFP_USER 用來為用戶空間頁來分配內存; 它可能睡眠.

分配一段DMA緩存區,分配出來的內存會禁止cache緩存(因為DMA傳輸不需要CPU)

它和?dma_alloc_coherent?()函數相似,不過?dma_alloc_coherent?()函數是分配出來的內存會禁止cache緩存以及禁止寫入緩沖區

dma_free_writecombine(dev,size,cpu_addr,handle); //釋放緩存//cpu_addr:虛擬地址,//handle:物理地址

釋放DMA緩沖區, dev和size參數和上面的一樣

struct fb_info *framebuffer_alloc(size_t size, struct device *dev);//申請一個fb_info結構體,//size:額外的內存,//*dev:指針, 這里填0,表示這個申請的結構體里沒有內容

int register_framebuffer(struct fb_info *fb_info);//向內核中注冊fb_info結構體,若內存不夠,注冊失敗會返回負數

int unregister_framebuffer(struct fb_info *fb_info) ;//注銷內核中fb_info結構體

本節需要用到的結構體:

fb_info結構體如下:

structfb_info {

... ...struct fb_var_screeninfo var; //可變的參數

struct fb_fix_screeninfo fix; //固定的參數... ...struct fb_ops *fbops; //操作函數... ...char __iomem *screen_base; //顯存虛擬起始地址unsignedlong screen_size; //顯存虛擬地址長度

void *pseudo_palette;//假的16色調色板,里面存放了16色的數據,可以通過8bpp數據來找到調色板里面的16色顏色索引值,模擬出16色顏色來,節省內存,不需要的話就指向一個不用的數組即可... ...

};

其中操作函數fb_info-> fbops 結構體寫法如下:

static struct fb_ops s3c_lcdfb_ops ={

.owner=THIS_MODULE,

.fb_setcolreg= my_lcdfb_setcolreg,//設置調色板fb_info-> pseudo_palette,自己構造該函數

.fb_fillrect= cfb_fillrect, //填充矩形,用/drivers/video/ cfbfillrect.c里的函數即可

.fb_copyarea= cfb_copyarea, //復制數據, 用/drivers/video/cfbcopyarea.c里的函數即可

.fb_imageblit= cfb_imageblit, //繪畫圖形, 用/drivers/video/imageblit.c里的函數即可};

固定的參數fb_info-> fix 結構體如下:

structfb_fix_screeninfo {char id[16]; //id名字unsignedlong smem_start; //framebuffer物理起始地址__u32 smem_len;//framebuffer長度,字節為單位__u32 type;//lcd類型,默認值0即可__u32 type_aux;//附加類型,為0__u32 visual;//畫面設置,常用參數如下//FB_VISUAL_MONO01 0   單色,0:白色,1:黑色//FB_VISUAL_MONO10 1   單色,1:白色,0:黑色//FB_VISUAL_TRUECOLOR 2 真彩(TFT:真彩)//FB_VISUAL_PSEUDOCOLOR     3 偽彩//FB_VISUAL_DIRECTCOLOR 4 直彩

__u16 xpanstep;/*如果沒有硬件panning就賦值為0*/__u16 ypanstep;/*如果沒有硬件panning就賦值為0*/__u16 ywrapstep;/*如果沒有硬件ywrap就賦值為0*/__u32 line_length;/*一行的字節數 ,例:(RGB565)240*320,那么這里就等于240*16/8*//*以下成員都可以不需要*/

unsignedlong mmio_start; /*內存映射IO的起始地址,用于應用層直接訪問寄存器,可以不需要*/__u32 mmio_len;/*內存映射IO的長度,可以不需要*/__u32 accel;

__u16 reserved[3];

};

可變的參數fb_info-> var 結構體如下:

structfb_var_screeninfo{

__u32xres;/*可見屏幕一行有多少個像素點*/__u32 yres;/*可見屏幕一列有多少個像素點*/__u32 xres_virtual;/*虛擬屏幕一行有多少個像素點*/__u32 yres_virtual;/*虛擬屏幕一列有多少個像素點*/__u32 xoffset;/*虛擬到可見屏幕之間的行偏移,若可見和虛擬的分辨率一樣,就直接設為0*/__u32 yoffset;/*虛擬到可見屏幕之間的列偏移*/__u32 bits_per_pixel;/*每個像素的位數即BPP,比如:RGB565則填入16*/__u32 grayscale;/*非0時,指的是灰度,真彩直接填0即可*/

struct fb_bitfield red; //fb緩存的R位域, fb_bitfield結構體成員如下://__u32 offset; 區域偏移值,比如RGB565中的R,就在第11位//__u32 length; 區域長度,比如RGB565的R,共有5位//__u32 msb_right; msb_right ==0,表示數據左邊最大, msb_right!=0,表示數據右邊最大

struct fb_bitfield green; /*fb緩存的G位域*/

struct fb_bitfield blue; /*fb緩存的B位域*/   /*以下參數都可以不填,默認為0*/

struct fb_bitfield transp; /*透明度,不需要填0即可*/__u32nonstd;/*!= 0表示非標準像素格式*/__u32 activate;/*設為0即可*/__u32height;/*外設高度(單位mm),一般不需要填*/__u32width;/*外設寬度(單位mm),一般不需要填*/__u32 accel_flags;/*過時的參數,不需要填*/

/*除了pixclock本身外,其他的都以像素時鐘為 單位*/__u32pixclock;/*像素時鐘(皮秒)*/__u32 left_margin;/*行切換,從同步到繪圖之間的延遲*/__u32right_margin;/*行切換,從繪圖到同步之間的延遲*/__u32upper_margin;/*幀切換,從同步到繪圖之間的延遲*/__u32lower_margin;/*幀切換,從繪圖到同步之間的延遲*/__u32hsync_len;/*水平同步的長度*/__u32 vsync_len;/*垂直同步的長度*/__u32 sync;

__u32 vmode;

__u32 rotate;

__u32reserved[5]; /*保留*/}

1.寫驅動程序:

(驅動設置:參考自帶的LCD平臺驅動drivers/video/s3c2410fb.c )

1.1 步驟如下:

在驅動init入口函數中:

1)分配一個fb_info結構體

2)設置fb_info

2.1)設置固定的參數fb_info-> fix

2.2) 設置可變的參數fb_info-> var

2.3) 設置操作函數fb_info-> fbops

2.4) 設置fb_info 其它的成員

3)設置硬件相關的操作

3.1)配置LCD引腳

3.2)根據LCD手冊設置LCD控制器

3.3)分配顯存(framebuffer),把地址告訴LCD控制器和fb_info

4)開啟LCD,并注冊fb_info: register_framebuffer()

4.1) 直接在init函數中開啟LCD(后面講到電源管理,再來優化)

控制LCDCON5允許PWREN信號,

然后控制LCDCON1輸出PWREN信號,

輸出GPB0高電平來開背光,

4.2) 注冊fb_info

在驅動exit出口函數中:

1)卸載內核中的fb_info

2) 控制LCDCON1關閉PWREN信號,關背光,iounmap注銷地址

3)釋放DMA緩存地址dma_free_writecombine()

4)釋放注冊的fb_info

1.2 具體代碼如下:

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

/*LCD : 480*272*/

#define LCD_xres 480 //LCD 行分辨率

#define LCD_yres 272 //LCD列分辨率

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

structlcd_reg{

unsignedlonglcdcon1;

unsignedlonglcdcon2;

unsignedlonglcdcon3;

unsignedlonglcdcon4;

unsignedlonglcdcon5;

unsignedlonglcdsaddr1;

unsignedlonglcdsaddr2;

unsignedlonglcdsaddr3 ;

unsignedlongredlut;

unsignedlonggreenlut;

unsignedlongbluelut;

unsignedlong reserved[9];

unsignedlongdithmode;

unsignedlongtpal ;

unsignedlonglcdintpnd;

unsignedlonglcdsrcpnd;

unsignedlonglcdintmsk;

unsignedlongtconsel;

};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) //設置調色板函數,供內核調用{

unsignedintval;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; //像素為16BPPmy_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<

*GPBdat &=~(0X1<<0); //關背光

*GPCcon =0xaaaaaaaa;*GPDcon =0xaaaaaaaa;*GPGcon |=(0x03<

/*3.2 根據LCD手冊設置LCD控制器,參考之前的裸機驅動*/lcd_reg=ioremap(0X4D000000, sizeof( lcd_reg) );/*HCLK:100Mhz*/lcd_reg->lcdcon1 = (4<<8) | (0X3<<5) | (0x0C<<1) ;

lcd_reg->lcdcon2 = ((3)<<24) | (271<<14) | ((1)<<6) |((0)<<0);

lcd_reg->lcdcon3 = ((16)<<19) | (479<<8) | ((10));

lcd_reg->lcdcon4 = (4);

lcd_reg->lcdcon5 = (1<<11) | (1<<9) | (1<<8) |(1<<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(0,my_lcd->fix.smem_len, &my_lcd->fix.smem_start, GFP_KERNEL);/*lcd控制器的地址必須是物理地址*/lcd_reg->lcdsaddr1 =(my_lcd->fix.smem_start>>1)&0X3FFFFFFF;//保存緩沖起始地址A[30:1]lcd_reg->lcdsaddr2 =((my_lcd->fix.smem_start+my_lcd->screen_size)>>1)&0X1FFFFF; //保存存緩沖結束地址A[21:1]lcd_reg->lcdsaddr3 =LCD_xres& 0x3ff;//OFFSIZE[21:11]:保存LCD上一行結尾和下一行開頭的地址之間的差//PAGEWIDTH [10:0]:保存LCD一行占的寬度(半字數為單位)

/*4開啟LCD,并注冊fb_info: register_framebuffer()*/

/*4.1 直接在init函數中開啟LCD(后面講到電源管理,再來優化)*/lcd_reg->lcdcon1 |=1<<0; //輸出PWREN信號lcd_reg->lcdcon5 |=1<<3; //允許PWREN信號

*GPBdat |=(0X1<<0); //開背光

/*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<<4); //關背光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");

2.重新編譯內核,去掉默認的LCD

make menuconfig ,進入menu菜單重新設置內核參數:

進入Device Drivers->Graphics support: S3C2410 LCD framebuffer support //將自帶的LCD驅動設為模塊, 不編進內核中

然后make uImage 編譯內核

make modules 編譯模塊

為什么要編譯模塊?

因為LCD驅動相關的文件也沒有編進內核,而fb_ops里的成員fb_fillrect(), fb_copyarea(), fb_imageblit()用的都是drivers/video下面的3個文件,所以需要這3個的.ko模塊,如下圖所示:

3.掛載驅動

將編譯好的LCD驅動模塊 和drivers/video里的3個.ko模塊 放入nfs文件系統目錄中

然后燒寫內核, 先裝載3個/drivers/video下編譯好的模塊,再來裝載LCD驅動模塊

掛載LCD驅動后, 如下圖,可以通過?ls -l /dev/fb*???命令查看已掛載的LCD設備節點:

4.測試運行

測試有兩種:

echo hello> /dev/tty1 ? ??// LCD上便顯示hello字段

cat Makefile>/dev/tty1 ? ?// LCD上便顯示Makeflie文件的內容

4.1使用上節的鍵盤驅動在LCD終端打印命令行

vi /etc/inittab //修改inittab, inittab:配置文件,用于啟動init進程時,讀取inittab

添加->tty1::askfirst:-/bin/sh //將sh進程(命令行)輸出到tty1里,也就是使LCD輸出信息

然后重啟,insmod裝載3個/drivers/video下編譯好的模塊,再來insmod裝載LCD驅動模塊,tty1設備便有了,就能看到提示信息:

如下圖,我們insmod上一節的鍵盤驅動后,按下enter鍵,便能在LCD終端上操作linux了

從上圖可以看到按下enter鍵,它就啟動了一個進程號772的-sh進程,如下圖發現這個-sh的描述符都指向了tty1:

下章學習:

總結

以上是生活随笔為你收集整理的linux 修改分辨率lcd_16.Linux-LCD驱动(详解)的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。