历史上的今天
返回首页

历史上的今天

今天是:2024年10月19日(星期六)

正在发生

2021年10月19日 | S3C2440 (4.3寸)LCD驱动程序之层次分析(十六)

2021-10-19 来源:eefocus

在上一节LCD层次分析时,得出写个LCD驱动入口函数,需要以下4步:


1)分配一个fb_info结构体:framebuffer_alloc();


2)设置fb_info;


3)设置硬件相关的操作;


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


本节需要用到的函数:


函数dma_alloc_writecombine():(分配显存)


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)


它和dm_alloc_coherent()函数相似,不过dma_alloc_writecombine()函数是分配出来的内存会禁止cache缓存以及禁止写入缓冲区


---------------------------------------------------------------------------------------


函数dma_free_writecombine():(释放显存)


void dma_free_writecombine(struct device *dev, size_t size, void *cpu_addr, dma_addr_t handle);

//cpu_addr:虚拟地址

//handle:物理地址

释放DMA缓冲区,dev和size参数和上面的函数一样


---------------------------------------------------------------------------------------


函数frambuffer_alloc():(申请fb_info结构体)


struct fb_info *framebuffer_alloc(size_t size, struct device *dev)//申请一个fb_info结构体

//size:额外的内存(里面放私有数据private)

//*dev:指针,这里填写NULL,表示这个申请的结构体没有内容

---------------------------------------------------------------------------------------


函数register_frambuffer()和函数frambuffer_release():


int register_framebuffer(struct fb_info *fb_info)

//向内核中注册fb_info结构体,若内存不够,注册失败会返回负数

void framebuffer_release(struct fb_info *info)

//注销内核中fb_info结构体

---------------------------------------------------------------------------------------


本节需要用到的结构体:


fb_info结构体如下:


struct fb_info {

int node;

int flags;

struct fb_var_screeninfo var; //可变的参数

struct fb_fix_screeninfo fix; //固定的参数

… …

struct fb_ops *fbops; //操作函数

… …

char __iomem *screen_base; //显存虚拟起始地址

unsigned long screen_size; //显存虚拟地址长度

void *pseudo_palette; /* Fake palette of 16 colors */ 

//假的16色调色板,里面存放了16色的数据,可以通过8bpp数据来找到调色板里面的16色颜色索引值,模拟出16色颜色来,节省内存,不需要的话就指向一个不用的数组即可

};

其中操作函数fb_info->fbops结构体写法如下:


static struct fb_ops s3c_lcdfb_ops = {

.owner = THIS_MODULE,

 

//可以参考driversvideoAtmel_lcdfb.c

.fb_setcolreg = s3c_lcdfb_setcolreg, //设置调色板fb_info->pseudo_palette,自己构造该函数

//下面三个函数是对显存的操作

.fb_fillrect = cfb_fillrect, //填充一个矩形,用/driver/video/cfbfillrect.c里的函数即可

.fb_copyarea = cfb_copyarea, //拷贝一个区域,用/driver/video/cfbcopyarea.c里的函数即可

.fb_imageblit = cfb_imageblit, //图形填充,用/driver/video/imageblit.c里的函数即可

};

固定的参数fb_info->fix 结构体如下:


 


struct fb_fix_screeninfo {

char id[16]; //id名字

unsigned long 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:黑色,1:白色

// FB_VISUAL_TRUECOLOR 2 真彩(TFT:真彩)

// FB_VISUAL_PSEUDOCOLOR 3 伪彩

// FB_VISUAL_DIRECTCOLOR 4 直彩

 

 

__u16 xpanstep; /* 如果没有硬件panning就赋值为0 */

__u16 ypanstep; /* 如果没有硬件panning就赋值为0 */

__u16 ywrapstep; /* 如果没有硬件panning就赋值为0 */

 

__u32 line_length; /* 一行的字节数,例:(RGB565)240*320,那么这里就等于240*16/8 */

/* 以下成员都可以不需要 */

unsigned long mmio_start; /*内存映射IO的起始地址,用于应用层直接访问寄存器,可以不需要 */

__u32 mmio_len; /* 内存映射IO的长度,可以不需要 */

__u32 accel; /* Indicate to driver which */

/*  specific chip/card we have */

__u16 reserved[3]; /* Reserved for future compatibility */

};

可变的参数fb_info->var结构体如下:


struct fb_var_screeninfo {

__u32 xres; /* 可见屏幕一行有多少个像素点 */

__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即可 */

 

__u32 nonstd; /* !=0表示非标准像素格式 */

 

__u32 activate; /* 设为0即可 */

 

__u32 height; /* 外设高度(单位mm),一般不需要填 */

__u32 width; /* 外设宽度(单位mm),一般不需要填 */

 

__u32 accel_flags; /* 过时的参数,不需要填 */

 

/* 除了pixclock本身外,其它的都以像素时钟为单位 */

__u32 pixclock; /* 像素时钟(皮秒) */

__u32 left_margin; /* 行切换,从同步到绘图之间的延迟 */

__u32 right_margin; /* 行切换,从绘图到同步之间的延迟 */

__u32 upper_margin; /* 帧切换,从同步到绘图之间的延迟 */

__u32 lower_margin; /* 帧切换,从绘图到同步之间的延迟 */

__u32 hsync_len; /* 水平同步的长度 */

__u32 vsync_len; /* 垂直同步的长度 */

__u32 sync; /* see FB_SYNC_* */

__u32 vmode; /* see FB_VMODE_* */

__u32 rotate; /* angle we rotate counter clockwise */

__u32 reserved[5]; /* 保留 */

};

1、写驱动程序:

(写驱动可以参考自带的LCD平台驱动drivers/video/s3c2410fb.c)

(LCD控制寄存器设置:参考之前的LCD裸机驱动:(硬件设置)https://blog.csdn.net/xiaodingqq/article/details/80724190)


 


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_inifo


1.2具体的代码如下:


#include

#include

#include

#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]; //保留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]; //假的调色板,调色板数组,被fb_info->pseudo_palette调用

 

/* 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) //regno哪一个调色板

return 1;

 

/* 用read,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 */

//第一个参数大小:size表示分配额外的空间(里面放私有数据private)

s3c_lcd = framebuffer_alloc(0, NULL); //不分配额外的空间

/* 2.  设置 */

/* 2.1 设备固定的参数fix */

strcpy(s3c_lcd->fix.id, "mylcd");//名字id

s3c_lcd->fix.smem_len    = 480*272*16/8; //显存的长度(需要看LCD手册)分辨率resolution:480*272,RGB(Bit):565,一个像素16位

s3c_lcd->fix.type        = FB_TYPE_PACKED_PIXELS; //在fb.h,使用默认值(值为0)

s3c_lcd->fix.visual      = FB_VISUAL_TRUECOLOR; /* visual视觉的 TFTLCD:真彩色 */

s3c_lcd->fix.line_length = 480*2; //一行480个像素,一个像素16位(2个字节)

/* 2.2 设置可变的参数var */

s3c_lcd->var.xres           = 480; //x方向的分辨率

s3c_lcd->var.yres           = 272; //y方向的分辨率

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; //红色的偏移值从bit11开始

s3c_lcd->var.red.length     = 5; //红色的长度为5位

 

s3c_lcd->var.green.offset   = 5; //绿色的偏移值从bit5开始

s3c_lcd->var.green.length   = 6; //绿色的长度为6位

 

s3c_lcd->var.blue.offset    = 0; //蓝色的偏移值从bit0开始

s3c_lcd->var.blue.length    = 5; //蓝色的长度为5位

 

s3c_lcd->var.activate       = FB_ACTIVATE_NOW; //使用默认值

 

/* 2.3 设置操作函数fbops       */

推荐阅读

史海拾趣

Avery Dennison公司的发展小趣事

1935年,Stan Avery在洛杉矶市中心创立了Avery Adhesives,以发明的不干胶模切贴标机为起点,开启了不干胶标签产业的先河。这一创新不仅改变了标签行业的面貌,也为Avery Adhesives日后的成功奠定了坚实的基础。随着公司业务的拓展,Avery在1948年在加利福尼亚州蒙罗维亚建立了第一家自有工厂,标志着公司规模的不断扩大和生产能力的提升。

Amplitronix LLC公司的发展小趣事

随着国内市场的饱和,Amplitronix LLC开始寻求国际合作,以拓展海外市场。他们与多家国际知名企业建立了战略合作关系,共同研发新产品,开拓新市场。通过国际合作,公司不仅提升了品牌影响力,还获得了更多的技术资源和市场渠道。同时,公司也积极参与国际行业交流活动,展示了其技术实力和市场竞争力。

台湾稳态公司的发展小趣事

在电子行业中,技术创新是企业保持竞争力的关键。台湾稳态公司始终坚持以创新为核心的发展理念,不断投入研发资源,推动产品创新和技术升级。通过持续的创新努力,稳态公司成功开发出了多款具有行业领先水平的新产品,引领了行业潮流,也为公司的持续发展注入了新的动力。

Advanced Electronic Packaging公司的发展小趣事

随着国内市场的逐渐饱和,Advanced Electronic Packaging公司开始将目光投向海外市场。公司积极参与国际电子展会和技术交流活动,展示其先进的封装技术和优质产品。同时,公司还加大了对海外市场的宣传力度,通过线上线下相结合的方式提升品牌知名度和影响力。在不懈的努力下,公司的产品和服务逐渐赢得了国际市场的认可和青睐,海外市场份额逐年攀升。

Fenghua (HK) Electronics Ltd公司的发展小趣事

在技术创新的基础上,Fenghua (HK) Electronics Ltd积极拓展国内外市场。公司参加了多个国际电子展,与全球各地的客户建立了良好的合作关系。同时,公司注重品牌建设,通过广告宣传、赞助活动等方式,提高了品牌知名度和美誉度。这些努力使得公司的产品在国内外市场上占据了越来越大的份额。

DUBILIER公司的发展小趣事

DUBILIER公司非常重视人才的培养和发展。公司建立了完善的人才培训体系,为员工提供各种培训和发展机会。通过内部培训和外部引进相结合的方式,DUBILIER公司培养了一支高素质、专业化的技术和管理团队。这些人才不仅为公司的发展提供了强大的支持,还为公司赢得了更多的商业机会和合作伙伴。

问答坊 | AI 解惑

充分利用计算机内部资源的虚拟示波器(checked)

本帖最后由 辛昕 于 2018-5-3 16:35 编辑 其实从后来的经历来看。 此内容由EEWORLD论坛网友辛昕原创,如需转载或用于商业用途需征得作者同意并注明出处 这件事,除非是自己去做,否则没多大意义。 当然了,花几百块买一台100Mhz以上带宽的 ...…

查看全部问答>

求《一种大电压落差DC-DC高频链开关电源的设计》的电路图具体数据

求《一种大电压落差DC-DC高频链开关电源的设计》的电路图具体数据…

查看全部问答>

lm3s811到底要怎么写程序啊!!要什么.h文件,怎么初始化!我真心蛋疼了

lm3s811到底要怎么写程序啊!!要什么.h文件,怎么初始化!我真心蛋疼了 整了一晚上加一天,一个程序都没运行成功! 求指教! 求批评! 求鞭策!…

查看全部问答>

求推荐一款DSP+FPGA开发板

马上年底了,又要花钱了。 想买一款DSP+FPGA的开发板,价格3K以内都可以,要求质量好就可以。 小伙伴们有没有好的企业可以推荐?…

查看全部问答>

通讯读取MSP430F135 FLASH 0x1090--0x109F 读取不了

比如:发送80 14 04 04 90 10 10 00   MSP430F135 读取不了 ?为什么 BSL通讯读取MSP430F1101、MSP430F1232  FLASH 0x1090--0x109F 可以直接读取                 &n ...…

查看全部问答>

有没有人做过像MP3那种文件系统

如题,用12C5A驱动SD卡的······可以像MP3那样把文件呈列表形式显示到12864上的,三按键的······跪求资源共享啊····…

查看全部问答>

CC2640的PWM问题

我在研究CC2640,想产生PWM波,使用官方的驱动,就像I2C和SPI一样,但是发现里面的配置缺少在哪个IO口输出PWM波,不知道应该在哪里配置,在PWMTimerTiva_HWAttrs PWMTimerTiva_HWAttrs1[CC2650_PWMCOUNT]这个结构体中只有Timer的基地址和TimerA和B ...…

查看全部问答>

msp430单片机 外设模块使用手记

ADC内核完成将模拟信号转换成12位数据并存入转换存储寄存器中,输入模拟电压的最终结果满足公式:         本论文要求有pH值信号和温度信号两路模拟信号进行A/D转换,为了减少误差,可采用多次取值然后取平均值的方 ...…

查看全部问答>

异形pcb制版时面积怎么算

求助坛友,我想问下像上图这样的异形PCB的面积应该怎么算啊? …

查看全部问答>