有没有人做网站,哪家公司网站做得好,做网站怎么切psd图,微信小程序申请场所码一.Framebuffer设备LCD 显示器都是由一个一个的像素点组成#xff0c;像素点就类似一个灯(在 OLED 显示器中#xff0c;像素点就是一个小灯)#xff0c;这个小灯是 RGB 灯#xff0c;也就是由 R(红色)、G(绿色)和 B(蓝色)这三种颜色组成的#xff0c;而 RGB 就是光的三原色…一.Framebuffer设备 LCD 显示器都是由一个一个的像素点组成像素点就类似一个灯(在 OLED 显示器中像素点就是一个小灯)这个小灯是 RGB 灯也就是由 R(红色)、G(绿色)和 B(蓝色)这三种颜色组成的而 RGB 就是光的三原色。1080P 的意思就是一个 LCD 屏幕上的像素数量是1920*1080 个也就是这个屏幕一列 1080 个像素点一共 1920 列。 通过控制 R、G、B 这三种颜色的亮度就可以显示出各种各样的色彩。那该如何控制 R、G、B 这三种颜色的显示亮度呢一般一个 R、G、B 这三部分分别使用 8bit 的数据那么一个像素点就是 8bit*324bit也就是说一个像素点3 个字节这种像素格式称为 RGB888。如果再加入 8bit 的 Alpha(透明)通道的话一个像素点就是 32bit也就是 4 个字节这种像素格式称为 ARGB8888。 如果采用 ARGB8888 格式的话一个像素需要 4 个字节的内存来存放像素数据那么 1024*600 分辨率就需要 1024*600*42457600B≈2.4MB 内存。但是 RGB LCD 内部是没有内存的所以就需要在开发板上的 DDR3 中分出一段内存作为 RGBLCD 屏幕的显存我们如果要在屏幕上显示什么图像的话直接操作这部分显存即可。1.裸机编写LCD驱动 a.初始化 I.MX6U 的 eLCDIF 控制器重点是 LCD 屏幕宽(width)、高(height)、hspw、
hbp、hfp、vspw、vbp 和 vfp 等信息。 b.初始化 LCD 像素时钟。 c.设置 RGBLCD 显存。 d.应用程序直接通过操作显存来操作 LCD实现在 LCD 上显示字符、图片等信息。2. LInux下的Framebuffer设备 在 Linux 中应用程序最终也是通过操作 RGB LCD 的显存来实现在 LCD 上显示字符、图片
等信息。在裸机中我们可以随意的分配显存但是在 Linux 系统中内存的管理很严格显存是
需要申请的不是你想用就能用的。而且因为虚拟内存的存在驱动程序设置的显存和应用程
序访问的显存要是同一片物理内存。 为了解决上述问题Framebuffer 诞生了 Framebuffer 翻译过来就是帧缓冲简称 fb因
此大家在以后的 Linux 学习中见到“Framebuffer”或者“fb”的话第一反应应该想到 RGBLCD或者显示设备。fb 是一种机制将系统中所有跟显示有关的硬件以及软件集合起来虚拟出一个 fb 设备当我们编写好 LCD 驱动以后会生成一个名为/dev/fbX(X0~n)的设备应用程序通过访问/dev/fbX 这个设备就可以访问 LCD。 在linux系统中/dev/fb0 就是 LCD 对应的设备文件/dev/fb0 是个字符设备fb 的file_operations 操作集定义在 drivers/video/fbdev/core/fbmem.c 文件中操作集如下所示
static const struct file_operations fb_fops {.owner THIS_MODULE,.read fb_read,.write fb_write,.unlocked_ioctl fb_ioctl,
#ifdef CONFIG_COMPAT.compat_ioctl fb_compat_ioctl,
#endif.mmap fb_mmap,.open fb_open,.release fb_release,
#ifdef HAVE_ARCH_FB_UNMAPPED_AREA.get_unmapped_area get_fb_unmapped_area,
#endif
#ifdef CONFIG_FB_DEFERRED_IO.fsync fb_deferred_io_fsync,
#endif.llseek default_llseek,
};
二.LCD驱动 不同分辨率的 LCD 屏幕其 eLCDIF 控制器驱动代码都是一样的只需要修改好对应的屏幕参数即可。屏幕参数信息属于屏幕设备信息内容这些肯定是要放到设备树中的我们需要在设备树中将屏幕信息修改为我们所使用的屏幕参数。1.内核驱动 NXP 官方编写的 Linux 下的 LCD 驱动打开 imx6ull.dtsilcdif节点内容如下所示;lcdif: lcdif021c8000 {compatible fsl,imx6ul-lcdif, fsl,imx28-lcdif;reg 0x021c8000 0x4000;interrupts GIC_SPI 5 IRQ_TYPE_LEVEL_HIGH;clocks clks IMX6UL_CLK_LCDIF_PIX,clks IMX6UL_CLK_LCDIF_APB,clks IMX6UL_CLK_DUMMY;clock-names pix, axi, disp_axi;status disabled;}; lcdif 节点信息是所有使用 I.MX6ULL 芯片的板子所共有的,是lcd控制器相关的信息后续需要根据使用LCD屏幕规格参数的不同在此节点后面续上特定的信息。根据上述lcdif 节点中的信息得到compatible 属性值为“fsl,imx6ul-lcdif”和“fsl,imx28-lcdif”搜索这两个字符串可以找到内核中LCD的驱动文件probe函数的内容如下所示,省略部分内容
static int mxsfb_probe(struct platform_device *pdev)
{const struct of_device_id *of_id of_match_device(mxsfb_dt_ids, pdev-dev);struct resource *res;
//host 结构体指针变量表示 I.MX6ULL 的 LCD 的主控接口mxsfb_info 结构
//体是 NXP 定义的针对 I.MX 系列 SOC 的 Framebuffer 设备结构体。也就是我们前面一直说的设
//备结构体此结构体包含了 I.MX 系列 SOC 的 Framebuffer 设备详细信息比如时钟、eLCDIF
//控制器寄存器基地址、fb_info 等。struct mxsfb_info *host;struct fb_info *fb_info;struct pinctrl *pinctrl;int irq platform_get_irq(pdev, 0);int gpio, ret;if (of_id)pdev-id_entry of_id-data;gpio of_get_named_gpio(pdev-dev.of_node, enable-gpio, 0);if (gpio -EPROBE_DEFER)return -EPROBE_DEFER;if (gpio_is_valid(gpio)) {ret devm_gpio_request_one(pdev-dev, gpio, GPIOF_OUT_INIT_LOW, lcd_pwr_en);if (ret) {dev_err(pdev-dev, faild to request gpio %d, ret %d\n, gpio, ret);return ret;}}
//从设备树中获取 eLCDIF 接口控制器的寄存器首地址设备树中 lcdif 节点已
//经设置了 eLCDIF 寄存器首地址为 0X021C8000因此 res0X021C8000。res platform_get_resource(pdev, IORESOURCE_MEM, 0);if (!res) {dev_err(pdev-dev, Cannot get memory IO resource\n);return -ENODEV;}
//给 host 申请内存host 为 mxsfb_info 类型结构体指针。host devm_kzalloc(pdev-dev, sizeof(struct mxsfb_info), GFP_KERNEL);if (!host) {dev_err(pdev-dev, Failed to allocate IO resource\n);return -ENOMEM;}
//给 fb_info 申请内存也就是申请 fb_info。fb_info framebuffer_alloc(sizeof(struct fb_info), pdev-dev);if (!fb_info) {dev_err(pdev-dev, Failed to allocate fbdev\n);devm_kfree(pdev-dev, host);return -ENOMEM;}
//设置 host 的 fb_info 成员变量为 fb_info设置 fb_info 的 par 成员变量为
//host。通过这一步就将前面申请的 host 和 fb_info 联系在了一起。host-fb_info fb_info;fb_info-par host;
//申请中断中断服务函数为 mxsfb_irq_handler。ret devm_request_irq(pdev-dev, irq, mxsfb_irq_handler, 0,dev_name(pdev-dev), host);if (ret) {dev_err(pdev-dev, request_irq (%d) failed with error %d\n,irq, ret);ret -ENODEV;goto fb_release;}
//从设备树中获取到的寄存器首地址(res)进行内存映射得到虚拟地址并保
//存到 host 的 base 成员变量。因此通过访问 host 的 base 成员即可访问 I.MX6ULL 的整个 eLCDIF
//寄存器。其实在 mxsfb.c 中已经定义了 eLCDIF 各个寄存器相比于基地址的偏移值host-base devm_ioremap_resource(pdev-dev, res);if (IS_ERR(host-base)) {dev_err(pdev-dev, ioremap failed\n);ret PTR_ERR(host-base);goto fb_release;}host-pdev pdev;platform_set_drvdata(pdev, host);host-devdata mxsfb_devdata[pdev-id_entry-driver_data];host-clk_pix devm_clk_get(host-pdev-dev, pix);if (IS_ERR(host-clk_pix)) {host-clk_pix NULL;ret PTR_ERR(host-clk_pix);goto fb_release;}//........host-reg_lcd devm_regulator_get(pdev-dev, lcd);if (IS_ERR(host-reg_lcd))host-reg_lcd NULL;
//给 fb_info 中的 pseudo_palette申请内存。fb_info-pseudo_palette devm_kzalloc(pdev-dev, sizeof(u32) * 16,GFP_KERNEL);if (!fb_info-pseudo_palette) {ret -ENOMEM;goto fb_release;}INIT_LIST_HEAD(fb_info-modelist);pm_runtime_enable(host-pdev-dev);
//调用 mxsfb_init_fbinfo 函数初始化 fb_info重点是 fb_info 的 var、 fix、 fbops
//screen_base 和 screen_size。其中 fbops 是 Framebuffer 设备的操作集
//mxsfb_init_fbinfo 函数通过
//调用 mxsfb_init_fbinfo_dt 函数从设备树中获取到 LCD 的各个参数信息。
//最后mxsfb_init_fbinfo函数会调用 mxsfb_map_videomem 函数申请 LCD 的帧缓冲内存(也就是县存)。ret mxsfb_init_fbinfo(host);if (ret ! 0)goto fb_pm_runtime_disable;mxsfb_dispdrv_init(pdev, fb_info);if (!host-dispdrv) {pinctrl devm_pinctrl_get_select_default(pdev-dev);if (IS_ERR(pinctrl)) {ret PTR_ERR(pinctrl);goto fb_pm_runtime_disable;}}if (!host-enabled) {writel(0, host-base LCDC_CTRL);mxsfb_set_par(fb_info);mxsfb_enable_controller(fb_info);pm_runtime_get_sync(host-pdev-dev);}
//向 Linux 内核注册 fb_inforet register_framebuffer(fb_info);if (ret ! 0) {dev_err(pdev-dev, Failed to register framebuffer\n);goto fb_destroy;}console_lock();ret fb_blank(fb_info, FB_BLANK_UNBLANK);console_unlock();if (ret 0) {dev_err(pdev-dev, Failed to unblank framebuffer\n);goto fb_unregister;}dev_info(pdev-dev, initialized\n);return 0;//................return ret;
} 根据上述probe函数中的信息可以得到Linux 内核将所有的 Framebuffer 抽象为一个叫做 fb_info 的结构体 fb_info 结构体包含了 Framebuffer 设备的完整属性和操作集合因此每一个 Framebuffer 设备都必须有一个 fb_info。LCD 的驱动就是构建 fb_info并且向系统注册 fb_info
的过程。fb_info结构体的内容如下所示
struct fb_info {atomic_t count;int node;int flags;struct mutex lock; /* 互斥锁 */struct mutex mm_lock; /* 互斥锁用于 fb_mmap 和 smem_*域*/struct fb_var_screeninfo var; /* 当前可变参数 */struct fb_fix_screeninfo fix; /* 当前固定参数 */struct fb_monspecs monspecs; /* 当前显示器特性 */struct work_struct queue; /* 帧缓冲事件队列 */struct fb_pixmap pixmap; /* 图像硬件映射 */struct fb_pixmap sprite; /* 光标硬件映射 */struct fb_cmap cmap; /* 当前调色板 */struct list_head modelist; /* 当前模式列表 */struct fb_videomode *mode; /* 当前视频模式 */#ifdef CONFIG_FB_BACKLIGHT /* 如果 LCD 支持背光的话 *//* assigned backlight device *//* set before framebuffer registration, remove after unregister */struct backlight_device *bl_dev; /* 背光设备 *//* Backlight level curve */struct mutex bl_curve_mutex; u8 bl_curve[FB_BACKLIGHT_LEVELS];
#endif.........struct fb_ops *fbops;/* 帧缓冲操作函数集 */struct device *device; /* 父设备 */struct device *dev; /* 当前 fb 设备 */int class_flag; /* 私有 sysfs 标志 */.........char __iomem *screen_base; /* 虚拟内存基地址(屏幕显存) */unsigned long screen_size; /* 虚拟内存大小(屏幕显存大小) */void *pseudo_palette; /* 伪 16 位调色板 */..........
};
2.LCD驱动修改 LCD 驱动部分芯片厂家提供的内核中已有不需要去修改。我们需要做的就是按照所使用的 LCD 屏幕规格来修改设备树。主要有三个地方需要注意①LCD 所使用的 IO 配置。②LCD 屏幕节点修改修改相应的属性值换成我们所使用的 LCD 屏幕参数。③LCD 背光节点信息修改要根据实际所使用的背光 IO 来修改相应的设备节点信息。 a.LCD 屏幕 IO 配置 设备树中 LCD 所使用的 IO 配置在文件imx6ull-alientek-emmc.dts中iomuxc节点中相关的配置有
//子节点 pinctrl_lcdif_dat为 RGB LCD 的 24 根数据线配置项。pinctrl_lcdif_dat: lcdifdatgrp {fsl,pins MX6UL_PAD_LCD_DATA00__LCDIF_DATA00 0x49MX6UL_PAD_LCD_DATA01__LCDIF_DATA01 0x49MX6UL_PAD_LCD_DATA02__LCDIF_DATA02 0x49MX6UL_PAD_LCD_DATA03__LCDIF_DATA03 0x49MX6UL_PAD_LCD_DATA04__LCDIF_DATA04 0x49MX6UL_PAD_LCD_DATA05__LCDIF_DATA05 0x49MX6UL_PAD_LCD_DATA06__LCDIF_DATA06 0x49MX6UL_PAD_LCD_DATA07__LCDIF_DATA07 0x49MX6UL_PAD_LCD_DATA08__LCDIF_DATA08 0x49MX6UL_PAD_LCD_DATA09__LCDIF_DATA09 0x49MX6UL_PAD_LCD_DATA10__LCDIF_DATA10 0x49MX6UL_PAD_LCD_DATA11__LCDIF_DATA11 0x49MX6UL_PAD_LCD_DATA12__LCDIF_DATA12 0x49MX6UL_PAD_LCD_DATA13__LCDIF_DATA13 0x49MX6UL_PAD_LCD_DATA14__LCDIF_DATA14 0x49MX6UL_PAD_LCD_DATA15__LCDIF_DATA15 0x49MX6UL_PAD_LCD_DATA16__LCDIF_DATA16 0x49MX6UL_PAD_LCD_DATA17__LCDIF_DATA17 0x49MX6UL_PAD_LCD_DATA18__LCDIF_DATA18 0x49MX6UL_PAD_LCD_DATA19__LCDIF_DATA19 0x49MX6UL_PAD_LCD_DATA20__LCDIF_DATA20 0x49MX6UL_PAD_LCD_DATA21__LCDIF_DATA21 0x49MX6UL_PAD_LCD_DATA22__LCDIF_DATA22 0x49MX6UL_PAD_LCD_DATA23__LCDIF_DATA23 0x49;};
//子节点 pinctrl_lcdif_ctrlRGB LCD 的 4 根控制线配置项包括 CLK、ENABLE、VSYNC 和 HSYNC。pinctrl_lcdif_ctrl: lcdifctrlgrp {fsl,pins MX6UL_PAD_LCD_CLK__LCDIF_CLK 0x49MX6UL_PAD_LCD_ENABLE__LCDIF_ENABLE 0x49MX6UL_PAD_LCD_HSYNC__LCDIF_HSYNC 0x49MX6UL_PAD_LCD_VSYNC__LCDIF_VSYNC 0x49;}; b.LCD 屏幕参数节点信息修改 在 imx6ull-alientek-emmc.dts 文件中找到 lcdif 节点节点内容如下所示
lcdif {pinctrl-names default;pinctrl-0 pinctrl_lcdif_dat //使用到的iopinctrl_lcdif_ctrl;display display0;status okay;display0: display { /* LCD 属性信息 */bits-per-pixel 24; /* 一个像素占用 24bit */bus-width 24; /* 总线宽度 */display-timings {native-mode timing0; /* 时序信息 */timing0: timing0 {clock-frequency 51200000; /* LCD 像素时钟单位 Hz */hactive 1024; /* LCD X 轴像素个数 */vactive 600; /* LCD Y 轴像素个数 */hfront-porch 160; /* LCD hfp 参数 */hback-porch 140; /* LCD hbp 参数 */hsync-len 20; /* LCD hspw 参数 */vback-porch 20; /* LCD vbp 参数 */vfront-porch 12; /* LCD vfp 参数 */vsync-len 3; /* LCD vspw 参数 */hsync-active 0; /* hsync 数据线极性 */vsync-active 0; /* vsync 数据线极性 */de-active 1; /* de 数据线极性 *//* rgb to hdmi: pixelclk-ative should be set to 1 */pixelclk-active 0; /* clk 数据线先极性 */};};};
}; c.LCD 屏幕背光节点信息 LCD 接口背光控制 IO 连接到了 I.MX6U 的 GPIO1_IO08 引脚上 GPIO1_IO08复用为 PWM1_OUT通过 PWM 信号来控制 LCD 屏幕背光的亮度。GPIO1_IO08 这个 IO 的配置在 imx6ull-alientek-emmc.dts 中可以找到pinctrl_pwm1: pwm1grp {fsl,pins MX6UL_PAD_GPIO1_IO08__PWM1_OUT 0x110b0;}; LCD 背光要用到 PWM1因此也要设置 PWM1 节点在 imx6ull.dtsi 文件中找到如下内
容 pwm1: pwm02080000 {compatible fsl,imx6ul-pwm, fsl,imx27-pwm;reg 0x02080000 0x4000;interrupts GIC_SPI 83 IRQ_TYPE_LEVEL_HIGH;clocks clks IMX6UL_CLK_PWM1,clks IMX6UL_CLK_PWM1;clock-names ipg, per;#pwm-cells 2;}; 在 imx6ull-alientek-emmc.dts 文件中找到向 pwm1追加的内容设置 pwm1 所使用的 IO 为 pinctrl_pwm1GPIO1_IO08如下所示
pwm1 {pinctrl-names default;pinctrl-0 pinctrl_pwm1;status okay;
}; 还需要一个节点来将 LCD 背光和 PWM1_OUT连 接 起 来 。 这 个 节 点 就 是 backlightbacklight {compatible pwm-backlight;pwms pwm1 0 5000000; //描述背光所使用的 PWM 以及 PWM 频率brightness-levels 0 4 8 16 32 64 128 255;default-brightness-level 6;status okay;};