LCD框架介绍

鸿蒙OpenHarmony【LCD】 子系统 LCD(Liquid Crystal Display)驱动编程,通过对显示器上电、初始化显示器驱动IC(Integrated Circuit)内部寄存器等操作,使其可以正常工作。 阅读详情

Revision History

Date Issue Description Author

<04/15/2010> <0.1> LCD架构分析 Wylhistory

目录

1. 摘要 3

2. 介绍 3

3. 思路 3

4. 初始化 4

4.1 设备和驱动注册 4

4.2 PROBE函数的执行 6

5. 用户使用流程 8

5.1 上层软件使用流程 8

5.2 驱动处理逻辑 9

6. 总结 12

7. 未讨论 12


 

1. 摘要

分析一下在pxa310平台,LCD驱动的架构;

2. 介绍

每次换LCD的屏都会经历一个痛苦的过程,所以想理一下它的架构,以便在以后的集成过程中,能有一个简单的指导;

硬件平台:pxa310

软件平台:android 2.1

内核:2.6.29

3. 思路

对于驱动的集成,我的一个原始的思路是这样的,驱动包含两方面的内容,

A) 硬件管理;

B) 提供对上接口,也就是提供给应用层接口;

从硬件管理的角度来说,任何一个模块想要工作,比如wifi,蓝牙, LCD等都必须要和我们的CPU相连,以实现通讯,所以要让这些模块工作,从上面一句话里面可以得出的结论就是这样:

(模块正常工作)==(模块本身工作正常)+(模块控制器工作正常)+(接口工作正常);

也就是说,想让一个驱动工作,就必须要考虑这三方面的内容,对应于我们要讨论的LCD的驱动,也就意味着:

A) LCD屏本身需要正确的初始化,以进入正常工作的状态(这个目前的lcd屏都不提供反馈信号);

B) LCD controller本身要正确的初始化,以进入正常的工作状态(这个可以通过寄存器的打印验证);

C) 接口需要正确配置,以实现LCD controller和LCD屏之间的通讯,接口通常都包含命令接口和数据接口,我们的命令接口是SPI的(通过gpio口模拟SPI协议),所以需要对 SPI接口做相应的初始化,数据接口一般分为RGB接口和CPU接口,rgb一般是16根线,有时候需要扩展以适应屏的pin脚,而cpu通过是8根,我们这边也需要扩展,比如高位扩展比如我们这边传递5位的red,那么我们的m4——m0位接到对方的p7——p3,而对方的p0——p2就接到我们的m4 ——m2(这个可以通过示波器验证);

而从对上的接口来说,各个驱动通常都有统一的接口,比如:

Open,close,ioctl,write,read,mmap,当然不同的驱动有可能只选择某些来实现,对于 LCD驱动来说,最重要的当然是Open,ioctl,以及mmap,我们后面可以看看它的代码逻辑;

下面会从初始化和上层使用角度来具体分析:

4. 初始化

4.1 设备和驱动注册

 

图 4.1设备和驱动的注册


有的驱动设备的注册和驱动的注册是放在不同的地方的,不过LCD的驱动是放在同一个函数里面的,就在pxafb.c里面的pxafb_init里面;

按照惯例,是先注册设备,再注册驱动,我们可以看到,设备的注册最后到了函数pxa_register_device 里面,看看它的代码:

void pxa_register_device(struct platform_device *dev, void *data)

{

int ret;


dev->dev.platform_data = data;


ret = platform_device_register(dev);

if (ret)

dev_err(&dev->dev, "unable to register device: %d/n", ret);

}

这里最重要的就是上面蓝色这句话,也就是保存了传入的这个info参数到platform_data里面,后面可以看到,很多操作都是需要这个数据结构的参与的,甚至可以说,这个数据结构对于 LCD的驱动来说是至关重要的,那么不看看它,就不够人情了,如下:

struct pxafb_mach_info lin2008_lcdpar = {

        .modes                  = lin2008_modes,

        .num_modes              = 1,

.lccr0 = LCCR0_Act ,

.lccr3 = LCCR3_STALL | LCCR3_OutEnH,

};

这里先看看这个lccr0的值,注释如下:

#define LCCR0_Act       (LCCR0_PAS*1)   /*  Active display (TFT)           */

简单说也就是定义这个屏的类型;

Lccr3的值定义如下:

#define LCCR3_STALL (1 << 28) /* Stall pixel clock if underrun occurs */

#define LCCR3_OutEnH    (LCCR3_OEP*0)   /*  Output Enable active High      */

翻译一下就是说,如果发生没有数据传输的情况,那么就停止像素时钟;

第二行,使能输出,并且是高有效;

这里我最关心的还是lin2008_modes,我们看看它的内容:

static struct pxafb_mode_info lin2008_modes[] = {

        [0] = {

             //   .pixclock       = 68296,  // consider for 812*492*36 frame

// .pixclock = 48344,    // consider for 812*492*60 frame

.pixclock = 40977,    // consider for 504*807*60

// .pixclock = 68296,          // consider for 504*807*36

                .xres           = 480,

                .yres           = 800,

                .bpp            = 16,

                .hsync_len      = 7,

                .left_margin    = 8,

                .right_margin   = 16,

.vsync_len = 1,

                .upper_margin   = 4,

                .lower_margin   = 3,

                .sync           = 0,

        },

};

这里面的信息都很重要,一个个来说:

Pixclock:这里是指时钟频率的倒数,否则怎么算都不对的,而且这里没有计算行同步和帧同步,40997== (1<<12)/(480+8+16)×(800+4+3)×60;也就是说这个值等于x的分辨率加上前廊+后廊以后与y的分辨率加上上廊加下廊后的值的乘积;

OK,现在我们回到先前设备注册的地方,也就是函数 pxa_register_device里面的dev成员,如下:

struct platform_device pxa_device_fb = {

.name = "pxa2xx-fb",

.id = -1,

.dev = {

.dma_mask = &fb_dma_mask,

.coherent_dma_mask = 0xffffffff,

},

.num_resources = ARRAY_SIZE(pxafb_resources),

.resource = pxafb_resources,

};

这里面唯一需要关注的就是这个资源,如下:

static struct resource pxafb_resources[] = {

[0] = {

.start = 0x44000000,

.end = 0x4400ffff,

.flags = IORESOURCE_MEM,

},

[1] = {

.start = IRQ_LCD,

.end = IRQ_LCD,

.flags = IORESOURCE_IRQ,

},

};

也就是说与LCD controller相关的寄存器的地址范围就在0x44000000——0x4400ffff之间;

既然设备以及注册了,我们再看看驱动的注册,

platform_driver_register(&pxafb_driver),这里最重要的还是pxafb_driver,下面看看它的值:

static struct platform_driver pxafb_driver = {

.probe = pxafb_probe,

#ifdef CONFIG_PM

.suspend = pxafb_suspend,

.resume = pxafb_resume,

#endif

.driver = {

.name = "pxa2xx-fb",

},

};

4.2 Probe函数的执行

根据platform设备的配备原则,只要名字相同,就可以结婚了,正所谓“金风玉露一相逢,便调用probe函数”

于是这里的probe函数也就是 pxafb_probe开始被调用了,这个函数挺长的,总结一下,至少干了以下几件事:

1, 调用lcdchip_init 对LCD屏本身进行初始化;

2, 取出inf = dev->dev.platform_data,这是先前我们在设备注册的时候赋值的,它是原始数据的来源,后面的很多操作很它有关;

3, 一堆的参数检查,不讨论;

4, 构建framebuffer信息,对于pxa310的架构,有三个层,所以不同的层拥有不同的framebuffer信息,但是这些信息的初始都来自于先前的 platform_data,可以把这一步的动作理解为我前面总结的是为了提供对上接口,而做的准备,这里会对我们在应用层看到的var 信息和fix信息做填充;需要单独说明的是这两句代码:fbi->fb.fbops = &pxafb_ops;……; INIT_WORK(&fbi->task, pxafb_task);前一个设置函数操作集,后面一句话初始化一个work pxafb_task后面会用到;

5, 通过pxafb_map_video_memory函数分配 framebuffer空间,空间的长度为xres*yres*bpp/2(如果是android系统还会再乘以2,因为是双缓冲)+PAGESIZE, 这个pagesize,通常是用来存放DMA描述符的,如果要调整上层看到的framebuffer的大小,这里需要修改,当然返回给上层的 smem_len也必须要修改;

6, 设置中断处理函数,通常也就用在调试的时候;

7, 通过pxafb_set_par设置DMA描述符的地址,以及描述符里面的内容,如果有某个相关的寄存器有改变,那么就通过 pxafb_schedule_work(fbi, C_REENABLE)来启动在第4步里面设置的work也就是pxafb_task来运行,注意不是立即运行;

8, 通过register_framebuffer(&fbi->fb)来注册这个framebuffer信息,本质上就是让registered_fb【index】指向先前我们分配的这个framebuffer信息,以便在后面使用的时候可以取出来;

9, 调用set_ctrlr_state(fbi, C_ENABLE)再通过pxafb_enable_controller 来enable controller,这里会把先前在第7步里面赋好的值放入具体的寄存器;

好现在先看看第4步里面的pxafb_ops:

static struct fb_ops pxafb_ops = {

.owner = THIS_MODULE,

.fb_check_var = pxafb_check_var,

.fb_set_par = pxafb_set_par,

.fb_setcolreg = pxafb_setcolreg,

.fb_fillrect = cfb_fillrect,

.fb_copyarea = cfb_copyarea,

.fb_imageblit = cfb_imageblit,

.fb_blank = pxafb_blank,

.fb_mmap = pxafb_mmap,

.fb_pan_display = pxafb_pan_display,

#ifdef CONFIG_FB_PXA_MINILCD

.fb_ioctl       = pxafb_minilcd_ioctl,

#else

.fb_ioctl = pxafb_ioctl,

#endif

};

再看看这个pxafb_task:

static void pxafb_task(struct work_struct *work)

{

struct pxafb_info *fbi =

container_of(work, struct pxafb_info, task);

u_int state = xchg(&fbi->task_state, -1);


set_ctrlr_state(fbi, state);

}

这里面先取得 fbi->task_state放到state里面,估计是为了实现互斥的访问,所以搞了一个xchg操作;然后通过 set_ctrlr_state来实现真正的LCD controller的寄存器的操作;

总结下来就是说,参数的原始设置在static struct pxafb_mode_info lin2008_modes里面,必须要正确填写,估计不同的屏这个地方都需要修改,然后就是controller的enable,这个是需要到 set_ctrlr_state的时候,才会被传递到寄存器的,那么有就是说,在这之前,必须要保证相关的值已经被正确的赋给了framebuffer info,需要注意的就是那个描述符的内容的填写,结构如下:

struct pxafb_dma_descriptor {

unsigned int fdadr;

unsigned int fsadr;

unsigned int fidr;

unsigned int ldcmd;

};

fdadr:下一个描述符的地址,注意这个是DMA的地址,在我们的平台上就是物理地址,通常可以让它指向这个结构本身的地址,这样就可以形成一个环,从而实现一直刷新;

fsadr:这个是数据的地址,也就是说应用层在这个地方开始写的数据,对于android平台,这个地址有可能是 framebuffer的起始地址,也有可能是起始地址+xres*yres*bpp/2;

fidr,这个值没怎么使用到,我们都填的是0;

ldcm,也就是这个数据的长度,如果要刷一屏的话,对于 800X480的分辨率16位色,那么就是800*480*2;


5. 用户使用流程

5.1 上层软件使用流程

代码如下:

static int fb_post(struct framebuffer_device_t* dev, buffer_handle_t buffer)

{

    if (private_handle_t::validate(buffer) < 0)

        return -EINVAL;


    fb_context_t* ctx = (fb_context_t*)dev;


    private_handle_t const* hnd = reinterpret_cast<private_handle_t const*>(buffer);

    private_module_t* m = reinterpret_cast<private_module_t*>(

            dev->common.module);

   

    if (m->currentBuffer) {

        m->base.unlock(&m->base, m->currentBuffer);

        m->currentBuffer = 0;

    }


    if (hnd->flags & private_handle_t::PRIV_FLAGS_FRAMEBUFFER) {


        m->base.lock(&m->base, buffer,

                private_module_t::PRIV_USAGE_LOCKED_FOR_POST,

                0, 0, m->info.xres, m->info.yres, NULL);


        const size_t offset = hnd->base - m->framebuffer->base;

        m->info.activate = FB_ACTIVATE_VBL;

        m->info.yoffset = offset / m->finfo.line_length;

        if (ioctl(m->framebuffer->fd, FBIOPUT_VSCREENINFO, &m->info) == -1) {

            LOGE("FBIOPUT_VSCREENINFO failed");

            m->base.unlock(&m->base, buffer);

            return -errno;

        }

        m->currentBuffer = buffer;

       

    } else {

        // If we can't do the page_flip, just copy the buffer to the front

        // FIXME: use copybit HAL instead of memcpy

       

        void* fb_vaddr;

        void* buffer_vaddr;

       

        m->base.lock(&m->base, m->framebuffer,

                GRALLOC_USAGE_SW_WRITE_RARELY,

                0, 0, m->info.xres, m->info.yres,

                &fb_vaddr);


        m->base.lock(&m->base, buffer,

                GRALLOC_USAGE_SW_READ_RARELY,

                0, 0, m->info.xres, m->info.yres,

                &buffer_vaddr);


        memcpy(fb_vaddr, buffer_vaddr, m->finfo.line_length * m->info.yres);

       

        m->base.unlock(&m->base, buffer);

        m->base.unlock(&m->base, m->framebuffer);

    }

   

    return 0;

}

这个函数会在surfaceflinger层的postframebuffer到opengl层到 nativewindow层的queueBuffer里面会调用fb->post(fb, handle)触发这个函数的执行;

从驱动的角度来说,它最关心的还是FBIOPUT_VSCREENINFO这个ioctl,下面看看驱动对应的处理逻辑;

5.2 驱动处理逻辑

如下图所示:

 

上层的ioctl会触发这个fb_ioctl的执行,在里面首先从registerd_fb[fbidx]取出先前我们注册的framebuffer信息(对于fb0来说就是在probe函数里面通过 register_framebuffer来实现注册的);然后通过fb_set_var把这个信息传递下去,var代表了上层传下来的数据信息,这个函数也是相当的长,不过我还是贴出来了,去掉了一写错误处理:

int

fb_set_var(struct fb_info *info, struct fb_var_screeninfo *var)

{

int flags = info->flags;

int ret = 0;

……………………….

…………………….....


if ((var->activate & FB_ACTIVATE_FORCE) ||

   memcmp(&info->var, var, sizeof(struct fb_var_screeninfo))) {

u32 activate = var->activate;

if (!info->fbops->fb_check_var) {

*var = info->var;

goto done;

}


ret = info->fbops->fb_check_var(var, info);

if (ret)

goto done;


if ((var->activate & FB_ACTIVATE_MASK) == FB_ACTIVATE_NOW) {

struct fb_videomode mode;

if (info->fbops->fb_get_caps) {

ret = fb_check_caps(info, var, activate);


if (ret)

goto done;

}


info->var = *var;


if (info->fbops->fb_set_par)

info->fbops->fb_set_par(info);


fb_pan_display(info, &info->var);

fb_set_cmap(&info->cmap, info);


fb_var_to_videomode(&mode, &info->var);


if (info->modelist.prev && info->modelist.next &&

   !list_empty(&info->modelist))

ret = fb_add_videomode(&mode, &info->modelist);



if (!ret && (flags & FBINFO_MISC_USEREVENT)) {

struct fb_event event;

int evnt = (activate & FB_ACTIVATE_ALL) ?

FB_EVENT_MODE_CHANGE_ALL :

FB_EVENT_MODE_CHANGE;


info->flags &= ~FBINFO_MISC_USEREVENT;

event.info = info;

event.data = &mode;

fb_notifier_call_chain(evnt, &event);

}

}

}


 done:

  return ret;

}

A) 先通过 pxafb_check_var检查传入的参数的有效性;

B) 调用 info->fbops->fb_set_par(info)也就是pxafb_set_par计算新的DMA传输相关的参数,通过调用 pxafb_activate_var来把新的包括行同步,帧同步,前廊后廊,上廊下廊等也包括DMA描述符内容的更新保存在fbi->reg*成员里面,然后触发pxafb_task的调度,注意这时候还没有被执行;

C) fb_pan_display-> info->fbops->fb_pan_display(var, info),最后就调用到了pxafb_pan_display,这个函数最主要的作用就是pxafb_schedule_work(fbi, C_CHANGE_DMA_BASE);注意这个状态因为是后设置的,也就是说前面在fb_set_par的时候设置的状态将会被这个状态覆盖,那么最后在执行pxafb_task的时候取得的状态将会是这个C_CHANGE_DMA_BASE的状态;


等这个pxafb_task被调度执行的时候,将会走到C_CHANGE_DMA_BASE的分支,如下:

static void pxafb_task(struct work_struct *work)

{

struct pxafb_info *fbi =

container_of(work, struct pxafb_info, task);

u_int state = xchg(&fbi->task_state, -1);


set_ctrlr_state(fbi, state);

}

void set_ctrlr_state(struct pxafb_info *fbi, u_int state)

{

……

case C_CHANGE_DMA_BASE:

fbi->dmadesc_fbhigh_cpu->fsadr = fbi->screen_dma +

(fbi->fb.var.xres*fbi->fb.var.yoffset*/

fbi->fb.var.bits_per_pixel/8);

break;

……

}

可以看到基本上就是修改fsadr的地址,它的值,我们前面有看到就是实际的数据的起始地址,通过这样的方式,那么下一次DMA数据传递的时候,数据将会从这个地址取得,从而实现了android平台下的双缓冲机制;

6. 总结

A)对于分辨率的修改,也就是说,如果屏本身支持这个新的分辨率,那么只需要对 static struct pxafb_mode_info lin2008_modes这个结构体修改就可以了,它这里面的xres,yres是后面许多数据结构里面的值的根源,如果需要在只支持800X480的屏上模拟320X480的话,还需要修改前廊后廊以扩展到相应的pixelclock就应该可以了;

B)真正对于 controller的设置都是在set_ctrlr_state里面做的,而这些值的设置都是在pxafb_activate_var里面做的,也就是说,如果要欺骗controller也就是说为了实现实际的值和上层看到的值的差异性,我想可以在这里做hack;

C)对于屏不亮的问题,我想首先要考虑的是电压问题,如果电压正常,那么就考虑另外四根线,CLOCK,hsync,vsync,以及 de线,如果这个都正常,再看看数据线是否正常,这些都可以通过示波器看出来,当然正确的对屏本身的初始化是前提,而屏无法提供反馈信号,这也是调试 LCD驱动最痛苦的地方,因为你无法判断初始化是否已经正确;

D)可以通过打开中断来判断,每帧数据的发送情况,当然,里面的打印信息不是完全有效,但是可以提供一定的参考,而且如果它在一直打印,至少可以说明,数据在不断的刷新;

E)显示结果和应该的结果如果有差异,可以通过抓出framebuffer的内容转化层图片来比较;当然也可以通过cat一幅图片到 framebuffer来判断驱动是否已经工作,如果开始的时候cat工作,过了一定时间cat又不工作,说明在这个过程中某些硬件的配置已经被改变了,这个需要看看是不是别的模块和你的设置有冲突;


7. 未讨论

1, controller寄存器的详细设置没有分析,这个我觉得需要对照spec详细看;

2, lcd屏本身的初始化没有仔细看,这个我觉得和屏本身有关;

3, 其它;

备注:

作者:wylhistory

联系方式: wylhistory@gmail.com

原文地址:http://blog.chinaunix.net/u2/67984/showart_2237233.html

LCD驱动框架 LCD驱动框架;应用调用的驱动入口 立即下载

相关推荐

开发LCD,TV,TW88系列代码

TW88的代码,用于开发显示器和TV,DVD车载产品

市占率最高的显示器件:LCD器件整体结构与工作方式 Part TWO

接着上话,探讨完毕LCD器件的结构与显示原理,本章讨论内容主要分为两个方面:LCD的整体结构以及LCD的工作方式;LCD(Liquid Crystal Display,液晶显示器)的整体结构主要包含以下几个关键部分:玻璃基板(Glass Substrate):LCD由两片非常薄的玻璃基板组成,分别作为顶层和底层,上面涂有透明导电层,用以形成电极。这些基板通常是由钙钠玻璃经过精细抛光和镀膜处理制成,以保证透光性和耐用性。:两片玻璃基板之间夹着一层液晶材料。

littlemingg的博客 5363

前面板LCD菜单显示框架

介绍如何搭建LCD菜单显示框架,利用函数指针和状态机操作机制,能灵活、快速地增删功能子菜单

常用的像素操作算法:Resize、Flip、Rotate

Resize图像缩放是把原图像按照目标尺寸放大或者缩小,是图像处理的一种。图像缩放有多种算法。最为简单的是最临近插值算法,它是根据原图像和目标图像的尺寸,计算缩放的比例,然后根据缩放比例计算目标像素所依据的原像素,过程中自然会产生小数,这时就采用四舍五入,取与这个点最相近的点。除此之外,还有双线性插值算法。双线性插值,又称为双线性内插。在数学上,双线性插值是有两个变量的插值函数的线性插值扩展,其核

Java与Android技术栈 1347

windows10系统上安装最新版mysql 8.0详细步骤

windows10系统上安装最新版mysql 8.0详细步骤

glei20的博客 6715

FRM(Financial Risk Manager,金融风险管理师)知识点总结归纳

FRM知识点总结(150字) FRM考试分为两级:一级侧重基础理论和工具,包含风险管理基础、定量分析、金融市场产品、估值与风险建模四大模块,涵盖风险管理框架、统计学方法、衍生品定价及VaR计算等核心内容。二级聚焦实际应用,分为市场风险、信用风险、操作风险、流动性风险、投资管理和市场前沿六大模块,深入探讨高级风险度量技术(如极值理论)、信用风险模型、巴塞尔协议及新兴风险议题。考试体系贯穿"风险识别→度量→管理→监管"逻辑主线,强调定量分析与实务案例的结合,覆盖传统金融风险和区块链、ESG等

极客神殿 1440

驱动模型的底层架构

Simple-Beauty的博客 272

Unity3D 简单水面shader的实现

实现思路 今天来实现一个简单的水面效果。水面的话主要是以下几个效果叠加 1.水面的动态波浪效果,我们这里使用最简单的正弦波加上法线贴图来模拟。如果想要更精确的波动效果可以考虑使用Gerstner波 2.不同水深颜色不同的效果,使用该顶点深度和顶点坐标的差值来计算深度,然后映射到颜色 3.浪涌效果,还是根据顶点深度和顶点坐标的差值来计算深度,然后叠加上一层浪涌 4.折射效果,用grabpass,采样之后进行偏移,最后叠加上去。 代码实现 首先是顶点位移函数,使用正弦函数代入顶点的x坐标和z坐标来算,因为这里写

o83290102o5的博客 1万+

高通-LCD驱动框架简述

LCD驱动框架简述 目录 一 LCD硬件结构框架 二 不同的硬件结构LCD的驱动框架简述 三 RGB接口LCD驱动简述(以户外手机(Android5.1)为例) 3.1 硬件线路框架 3.2 软件驱动框架 3.2.1 第一层:核心层 3.2.2 第二层:FB驱动层 3.2.3 第三层:具体LCD驱动 ///////////////////////////...

奶牛养殖场小马 7086

基于Codex平台与AI Agent技能开发,实现抖音爆款视频分析与脚本自动生成

大家好,我是专注于AI应用开发的技术博主。最近在探索如何利用AI工具自动化内容创作时,我发现了一个非常有趣的组合:通过Codex平台创建自定义的Skill(技能),来批量分析抖音平台的爆款视频,并基于分析结果自动生成新的带货视频脚本。这听起来像是内容团队的“黑科技”,但实现起来其实有清晰的路径可循。本文将为你完整拆解这个实战项目,从环境搭建、Skill编写、数据抓取到视频脚本生成,手把手教你打造自己的“AI内容助理”。无论你是想了解AI Agent的开发,还是对短视频自动化创作感兴趣,都能从本文中获得可直接

weixin_34110749的博客 644

瑞萨RZN2L工业以太网开发避坑指南:EtherCAT从入门到通信调试全流程

本文详细解析了瑞萨RZN2L MPU在工业以太网开发中的EtherCAT从站配置与通信优化全流程。通过硬件设计关键点、协议栈选型、PDO映射策略及DC同步精度优化等实战技巧,帮助开发者快速搭建高效通信框架并解决调试中的常见问题,特别适合工业自动化领域的实时通信需求。

aitie1479的博客 458

显示驱动原理(一):TFT LCD架构

TFT LCD 架构

HZY2020的博客 3184

SAP Event Mesh 简介

Get Started with Messaging for Decoupled Communication Step 1: What is the SAP Event Mesh service? SAP Event Mesh 是一种完全托管的服务,用于连接应用程序、服务和系统,以便它们可以通过消息和事件相互交互。 它通过引入中央消息代理取代了传统的点对点通信。 虽然点对点通信适用于在有限数量的发送方和接收方之间共享数据,但可扩展性很快就会成为一个问题。 SAP Event Mesh 解决了这个问题,并确保

2007 年 ~ 2025 年,深耕 SAP 技术 18 年 1240

Linux驱动之LCD框架介绍

在裸机编程的时候,曾经编写过LCD的测试程序,对这一部分不是很了解的可以先去看一下,我们今天要做的是基于Linux内核框架下的LCD驱动程序,内核版本,Linux-2.6.22.6 内核中已经集成了LCD的驱动框架,在driver/video/fbmem.c,打开这个文件,发现它是符合字符设备驱动的编写流程,下面是它的入口函数 注册了一个字符设备驱动,主设备号FB_MAJOR是29,要注意的是在入口函数里注册了类,但是并没有在类下注册设备,先来看看它的操作函数集合中的函数 上面的是open函数的详情,

weixin_41791581的博客 750

芒果YOLOv12改进90:检测头篇之ShareSepHead:即插即用|原创新颖共享Sep检测头,更省参数量,更高效,打造新型YOLOv8检测器,精度高效涨点

芒果YOLOv12改进90:检测头篇之ShareSepHead:即插即用|原创新颖共享Sep检测头,更省参数量,更高效,打造新型YOLOv8检测器,精度高效涨点

包括YOLOv5、YOLOv7、YOLOv8等模型改进 343

《四》:LBE,VR 大空间系列——Pico的空间扫描方式

(这里有个选项叫高精度扫描模式,这个功能在下方详细补充)系统会根据绘制的顶点,将区域分割成若干个4X4米的小方格,我们只需要根据提示,首先进入一个方格子空间内部,然后依次凝视每个区域上下左右前后六个面的小方格组,每一面4个,直至全部消失, 消失后地面会出现一个方形行走路径,我们沿着边缘正或反走一圈即可,完成后此块区域即自动消失。不过由于pico有一个高精度扫描圈,如图加粗的圆环,这个圆环的意思是在在内部的区域扫描精度较高,越远离原点的区域误差越大,所以为了更有效的利用,建议原点设置在场地中心区域。

unitygroup的博客 2880

Linux Framebuffer 驱动框架之一概念介绍及LCD硬件原理

一、基本概念     帧缓冲(Framebuffer)是Linux系统为显示设备提供的一个接口,它将显示缓冲区抽象,屏蔽图像硬件的底层差异,允许上层应用程序在图形模式下直接对显示缓冲区进行读写操作。用户不必关心物理显示缓冲区的具体位置及存放方式,这些都是由帧缓冲设备驱动本身来完成。对于帧缓冲设备而言,只要在显示缓冲区与显示点对应的区域写入颜色值,对应的颜色会自动在屏幕上显示,下一小节重点讲解各种

GQB的专栏 2万+

TMS_WebGMaps_1.9.8.0_Full_Source

TMS_WebGMaps_1.9.8.0_Full_Source 全源码 delphi cbuilder

Linux 帧缓冲子系统详解:LCD介绍、framebuffer驱动框架、LCD驱动源码分析

LCD显示屏基础知识介绍 请看博客:《嵌入式开发(S5PV210)——LCD显示器》;

weixin_42031299的博客 2583

最新的sdio 4.0 spec 协议标准 带书签 高清完整PDF版

Part-E1-SDIO-Specification-Ver4.00-Final-120220.pdf:最新的sdio 4.0 spec 协议标准 带书签 高清完整PDF版,包括SDIO 接口的外设,SDIO 接口官方协议等。

老版QQ原生表情gif动态

这是老版的QQ表情,不过全是动态的的,Windows照片查看器是不会动的,可以右击用浏览器打开查看,再说一遍是老版的,只是分享给宁愿用老版也要动态的人。

Linux Framebuffer 驱动框架之一概念介绍及LCD硬件原理【转】

本文转载自:http://blog.youkuaiyun.com/liuxd3000/article/details/17464779 一、基本概念 帧缓冲(Framebuffer)是Linux系统为显示设备提供的一个接口,它将显示缓冲区抽象,屏蔽图像硬件的底层差异,允许上层应用程序在图形模式下直接对显示缓冲区进行读写操作。用户不必关心物理显示缓冲区的具体位置及存放方式,这些都是由帧缓冲...

116

假人有限元模型

假人模型,车辆碰撞假人有限元模型,适合做乘员安全的研究者利用

分享一个LCD驱动框架

我始终认为对代码最好的解释就是代码本身,理解代码很多时候都是只可意会不可言传,所以后续我会将LCD框架这部分代码上传到gitte上,希望能给大家带来点收获,同时热切希望大家提出自己宝贵的想法,一起交流完善!!!一蓑烟雨任平生&wf。

dsgdauigfs的博客 186
上一篇: ubuntu 10.04 wine的字体美化
下一篇: bluetooth的profile
evilcode
博客等级 码龄20年 307粉丝 16原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值