本文主要是介绍韦东山嵌入式Liunx驱动大全二,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
文章目录
- 一、LCD
- 1-1 不同接口的LCD硬件操作原理
- 1-2 LCD驱动程序框架
- 1-3 结合APP分析LCD驱动程序框架
- 1-4 LCD硬件时序图
- 1-5 分析内核自带的LCD驱动程序
- 1-6 编程LCD驱动程序框架_使用设备树
- 1-7 LCD驱动程序框架_引脚配置
- 1-8 LCD驱动程序框架_时钟配置
- 1-9 LCD驱动程序框架_LCD控制器配置
- 1-10 LCD驱动程序框架_寄存器操作
本人学习完韦老师的视频,因此来复习巩固,写以笔记记之。
韦老师的课比较难,第一遍不知道在说什么,但是坚持看完一遍,再来复习,基本上就水到渠成了。
看完视频复习的同学观看最佳!
基于 IMX6ULL-PRO
一、LCD
1-1 不同接口的LCD硬件操作原理
bpp:bits per pixel,每个像素用多少位来表示
假设每个像素的颜色用16位来表示,那么一个LCD的所有像素点假设有xresy res个,需要的内存为:xresyres*16 / 8,也就是要设置所有像素的颜色,需要这么大小的内存。这块内存就被称为framebuffer:
- Framebuffer中每块数据对应一个像素
- 每块数据的大小可能是16位、32位,这跟LCD上像素的颜色格式有关
- 设置好LCD硬件后,只需要把颜色数据写入Framebuffer即可
统一的LCD硬件模型
MIPI表示Mobile Industry Processor Interface
,即移动产业处理器接口。是MIPI联盟发起的为移动应用处理器制定的开放标准和一个规范。
MIPI接口可以分为3类:MIPI-DBI (Display Bus Interface) ,既能发送数据,也能发送命令,常用的8080接口就属于DBI接口;MIPI-DPI (Display Pixel Interface) ,Pixel(像素),强调的是操作单个像素,在MPU上的LCD控制器就是这种接口
1-2 LCD驱动程序框架
字符设备驱动程序框架
① 驱动主设备号 ② 构造file_operations结构体,填充open/read/write等成员函数
③ 注册驱动:register_chrdev(major, name, &fops) ④ 入口函数 ⑤出口函数
Framebuffer驱动程序框架
分为上下两层:
(1) fbmem.c:承上启下
实现、注册file_operations结构体;把APP的调用向下转发到具体的硬件驱动程序
(2) xxx_fb.c:硬件相关的驱动程序
实现、注册fb_info结构体;实现硬件操作
编写硬件程序如下
分配fb_info:framebuffer_alloc
设置fb_info:var、fbops、硬件相关操作
注册fb_info:register_framebuffer
1-3 结合APP分析LCD驱动程序框架
参考:04_fb_test文件夹
open函数分析:Linux根据主设备号找到驱动程序,根据次设备号来确定驱动程序中的那一个设备。此时会调用fbmen.c文件中fb_open函数,从registered_fb[i]数组中去获取fb_info。底层驱动在此前已经注册设备驱动,并registered_fb[i] = 定义的fb_info结构体。
/*设备驱动程序的注册信息*/
static struct fb_info *myfb_info;
register_framebuffer(myfb_info);
registered_fb[i] = fb_info;
ioctl函数分析:ioctl对应于底层fb_ioctl函数再调用到do_fb_ioctl
1-4 LCD硬件时序图
HSD:水平方向同步信号
VSD:垂直方向同步信号
thd:每行像素个数
LCD控制器时序图
1-5 分析内核自带的LCD驱动程序
(1) 打开mxsfb.c文件,首先,platform_driver结构体中的属性与内核中设备树进行匹配。
ubuntu系统中,通过grep查找(第一个属性的值没找到),第二个属性找到内核的imx6ull.dtsi文件的第1017行,并打开。
Dtsi:可以理解为dts的公共部分,添加、变更非常灵活。Dtsi包含在dts中。
grep "fsl,imx28-lcdif" * -nr
设备树通过compatible属性来匹配驱动程序
vi imx6ull.dtsi +1017
而单板的设备树需要访问100ask_imx6ull-14x14.dts设备树文件,里面有硬件的配置。
(2) 匹配成功,则调用probe函数。并在里面分配、设置、注册fb_info
分配
设置
注册
(3) 硬件操作
我们只需要针对IMX6ULL的编写硬件相关的代码,涉及3部分:
①GPIO设置
- LCD引脚
- 背光引脚
- GPIO设置使用设备树,在设备树中设置pinctrl。
②时钟设置
- 确定LCD控制器的时钟
- 根据LCD的DCLK计算相关时钟
③LCD控制器本身的设置
- 比如设置Framebuffer的地址
- 设置Framebuffer中数据格式、LCD数据格式
- 设置时序
1-6 编程LCD驱动程序框架_使用设备树
将03文件的驱动程序进行改进
使用platform_driver注册,在probe函数里分配fb_info、设置fb_info、注册fb_info、硬件相关的设置。设备树中需要有对应的节点
驱动框架
static int mylcd_probe(struct platform_device *pdev)
{/*简要代码*//* 1.1 分配fb_info */myfb_info = framebuffer_alloc(0, NULL);/* 1.2 设置fb_info *//* a. var : LCD分辨率、颜色格式 */myfb_info->var.xres_virtual = myfb_info->var.xres = 500;myfb_info->var.yres_virtual = myfb_info->var.yres = 300/* 1.3 注册fb_info */register_framebuffer(myfb_info);/* 1.4 硬件操作 */mylcd_regs = ioremap(0x021C8000, sizeof(struct lcd_regs));mylcd_regs->fb_base_phys = phy_addr;mylcd_regs->fb_xres = 500;mylcd_regs->fb_yres = 300;mylcd_regs->fb_bpp = 16;return 0;
}static int mylcd_remove(struct platform_device *pdev)
{/* 反过来操作 *//* 2.1 反注册fb_info */unregister_framebuffer(myfb_info);/* 2.2 释放fb_info */framebuffer_release(myfb_info);iounmap(mylcd_regs);return 0;
}static const struct of_device_id mylcd_of_match[] = {{ .compatible = "100ask,lcd_drv", },{ },
};
MODULE_DEVICE_TABLE(of, simplefb_of_match);static struct platform_driver mylcd_driver = {.driver = {.name = "mylcd",.of_match_table = mylcd_of_match,},.probe = mylcd_probe,.remove = mylcd_remove,
};static int __init lcd_drv_init(void)
{int ret;struct device_node *np;ret = platform_driver_register(&mylcd_driver);if (ret)return ret;return 0;
}/* 2. 出口 */
static void __exit lcd_drv_exit(void)
{platform_driver_unregister(&mylcd_driver);
}
设备树信息
framebuffer-mylcd {compatible = "100ask,lcd_drv"; /*未待完续*/
};
1-7 LCD驱动程序框架_引脚配置
GPIO设置:配置LCD引脚和背光引脚GPIO,在设备树中设置pinctrl。
根据原理图确定需要配置那些引脚,打开引脚配置工具/设备树生成工具,把引脚配置成LCD引脚和背光引脚。
设备树节点信息和pinctrl信息(省略,如上图所示)
framebuffer-mylcd {compatible = "100ask,lcd_drv";pinctrl-names = "default";pinctrl-0 = <&mylcd_pinctrl>;backlight-gpios = <&gpio1 8 GPIO_ACTIVE_HIGH>;
};
/*probe函数获取GPIO*//* get gpio from device tree */bl_gpio = gpiod_get(&pdev->dev, "backlight", 0);/* config bl_gpio as output */gpiod_direction_output(bl_gpio, 1);/* set val: gpiod_set_value(bl_gpio, status); */
1-8 LCD驱动程序框架_时钟配置
时钟设置:确定LCD控制器的时钟和根据LCD的DCLK计算相关时钟
翻阅芯片手册Chapter 34 Enhanced LCD Interface (eLCDIF),查看LCD时钟
设备树添加时钟属性
framebuffer-mylcd {compatible = "100ask,lcd_drv";pinctrl-names = "default";pinctrl-0 = <&mylcd_pinctrl>;backlight-gpios = <&gpio1 8 GPIO_ACTIVE_HIGH>;clocks = <&clks IMX6UL_CLK_LCDIF_PIX>,<&clks IMX6UL_CLK_LCDIF_APB>,clock-names = "pix", "axi";};
驱动代码添加设置时钟
/*probe函数中设置CLK*//*get clk form device tree*/clk_pix = devm_clk_get(&pdev->dev,"pix");clk_axi = devm_clk_get(&pdev->dev,"axi");/*set clk rate*//*clk_axi系统启动后自动设置*50Mhz以后会由设备树来设置*/clk_set_rate(clk_pix, 50000000);/*enable clk*/clk_prepare_enable(clk_pix);clk_prepare_enable(clk_axi);
1-9 LCD驱动程序框架_LCD控制器配置
LCD控制器本身的设置:设置Framebuffer的地址、设置Framebuffer中数据格式、LCD数据格式、设置时序
设备树
display = <&display0>;display0: display {bits-per-pixel = <24>;bus-width = <24>;display-timings {native-mode = <&timing0>;timing0: timing0_1024x600 {clock-frequency = <50000000>;hactive = <1024>;vactive = <600>;hfront-porch = <160>;hback-porch = <140>;hsync-len = <20>;vback-porch = <20>;vfront-porch = <12>;vsync-len = <3>;hsync-active = <0>;vsync-active = <0>;de-active = <1>;pixelclk-active = <0>;};};
};
驱动程序
struct device_node *display_np;int ret;int bits_per_pixel;struct display_timings *timings = NULL;display_np = of_parse_phandle(pdev->dev.of_node, "display", 0);/*get common info*/ret = of_property_read_u32(display_np, "bus-width", &width);ret = of_property_read_u32(display_np, "bits-per-pixel",&bits_per_pixel);/*get timming*/timings = of_get_display_timings(display_np);
时序参数、引脚极性等信息,都被保存在一个display_timing结构体里:
1-10 LCD驱动程序框架_寄存器操作
设备树添加lcd物理地址
reg = <0x021c8000 0x4000>;
驱动程序在设备树中获取地址信息
/* 1.4 硬件操作 *///lcdif = ioremap(0x021C8000, sizeof(*lcdif));res = platform_get_resource(pdev, IORESOURCE_MEM, 0);lcdif = devm_ioremap_resource(&pdev->dev, res);
这篇关于韦东山嵌入式Liunx驱动大全二的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!