linuxlcd驱动实验(代码片段)

行稳方能走远 行稳方能走远     2023-02-01     475

关键词:


LCD 是很常用的一个外设,在裸机篇中我们讲解了如何编写LCD 裸机驱动,在Linux 下LCD 的使用更加广泛,在搭配QT 这样的GUI 库下可以制作出非常精美的UI 界面。本章我们就来学习一下如何在Linux 下驱动LCD 屏幕。

Linux 下LCD 驱动简析

1 Framebuffer 设备

先来回顾一下裸机的时候LCD 驱动是怎么编写的,裸机LCD 驱动编写流程如下:
①、初始化I.MX6U 的eLCDIF 控制器,重点是LCD 屏幕宽(width)、高(height)、hspw、hbp、hfp、vspw、vbp 和vfp 等信息。
②、初始化LCD 像素时钟。
③、设置RGBLCD 显存。
④、应用程序直接通过操作显存来操作LCD,实现在LCD 上显示字符、图片等信息。

在Linux 中应用程序最终也是通过操作RGB LCD 的显存来实现在LCD 上显示字符、图片等信息。在裸机中我们可以随意的分配显存,但是在Linux 系统中内存的管理很严格,显存是需要申请的,不是你想用就能用的。而且因为虚拟内存的存在,驱动程序设置的显存和应用程序访问的显存要是同一片物理内存。

为了解决上述问题,Framebuffer 诞生了,Framebuffer 翻译过来就是帧缓冲,简称fb,因此大家在以后的Linux 学习中见到“Framebuffer”或者“fb”的话第一反应应该想到RGBLCD或者显示设备。fb 是一种机制,将系统中所有跟显示有关的硬件以及软件集合起来,虚拟出一个fb 设备,当我们编写好LCD 驱动以后会生成一个名为/dev/fbX(X=0~n)的设备,应用程序通过访问/dev/fbX 这个设备就可以访问LCD。NXP 官方的Linux 内核默认已经开启了LCD 驱动,因此我们是可以看到/dev/fb0 这样一个设备,如图59.1.1.1 所示:

图59.1.1.1 中的/dev/fb0 就是LCD 对应的设备文件,/dev/fb0 是个字符设备,因此肯定有file_operations 操作集,fb 的file_operations 操作集定义在drivers/video/fbdev/core/fbmem.c 文件中,如下所示:

1495 static const struct file_operations fb_fops = 
1496 .owner = THIS_MODULE,
1497 .read = fb_read,
1498 .write = fb_write,
1499 .unlocked_ioctl = fb_ioctl,
1500 #ifdef CONFIG_COMPAT
1501 .compat_ioctl = fb_compat_ioctl,
1502 #endif
1503 .mmap = fb_mmap,
1504 .open = fb_open,
1505 .release = fb_release,
1506 #ifdef HAVE_ARCH_FB_UNMAPPED_AREA
1507 .get_unmapped_area = get_fb_unmapped_area,
1508 #endif
1509 #ifdef CONFIG_FB_DEFERRED_IO
1510 .fsync = fb_deferred_io_fsync,
1511 #endif
1512 .llseek = default_llseek,
1513 ;

关于fb 的详细处理过程就不去深究了,本章我们的重点是驱动起来ALPHA 开发板上的LCD。

LCD 驱动简析

LCD 裸机例程主要分两部分:
①、获取LCD 的屏幕参数。
②、根据屏幕参数信息来初始化eLCDIF 接口控制器。

不同分辨率的LCD 屏幕其eLCDIF 控制器驱动代码都是一样的,只需要修改好对应的屏幕参数即可。屏幕参数信息属于屏幕设备信息内容,这些肯定是要放到设备树中的,因此我们本章实验的主要工作就是修改设备树,NXP 官方的设备树已经添加了LCD 设备节点,只是此节点的LCD 屏幕信息是针对NXP 官方EVK 开发板所使用的4.3 寸480*272 编写的,我们需要将其改为我们所使用的屏幕参数。

我们简单看一下NXP 官方编写的Linux 下的LCD 驱动,打开imx6ull.dtsi,然后找到lcdif节点内容,如下所示:

1 lcdif: lcdif@021c8000 
2 compatible = "fsl,imx6ul-lcdif", "fsl,imx28-lcdif";
3 reg = <0x021c8000 0x4000>;
4 interrupts = <GIC_SPI 5 IRQ_TYPE_LEVEL_HIGH>;
5 clocks = <&clks IMX6UL_CLK_LCDIF_PIX>,
6 <&clks IMX6UL_CLK_LCDIF_APB>,
7 <&clks IMX6UL_CLK_DUMMY>;
8 clock-names = "pix", "axi", "disp_axi";
9 status = "disabled";
10 ;

示例代码59.1.2.1 中的lcdif 节点信息是所有使用I.MX6ULL 芯片的板子所共有的,并不是完整的lcdif 节点信息。像屏幕参数这些需要根据不同的硬件平台去添加,比如向imx6ull-alientek-emmc.dts 中的lcdif 节点添加其他的属性信息。从示例代码59.1.2.1 可以看出lcdif 节点的compatible 属性值为“fsl,imx6ul-lcdif”和“fsl,imx28-lcdif”,因此在Linux 源码中搜索这两个
字符串即可找到I.MX6ULL 的LCD 驱动文件,这个文件为drivers/video/fbdev/mxsfb.c,mxsfb.c就是I.MX6ULL 的LCD 驱动文件,在此文件中找到如下内容:

1362 static const struct of_device_id mxsfb_dt_ids[] = 
1363  .compatible = "fsl,imx23-lcdif", .data = &mxsfb_devtype[0], ,
1364  .compatible = "fsl,imx28-lcdif", .data = &mxsfb_devtype[1], ,
1365  /* sentinel */ 
1366 ;
......
1625 static struct platform_driver mxsfb_driver = 
1626 .probe = mxsfb_probe,
1627 .remove = mxsfb_remove,
1628 .shutdown = mxsfb_shutdown,
1629 .id_table = mxsfb_devtype,
1630 .driver = 
1631 .name = DRIVER_NAME,
1632 .of_match_table = mxsfb_dt_ids,
1633 .pm = &mxsfb_pm_ops,
1634 ,
1635 ;
1636
1637 module_platform_driver(mxsfb_driver);

从示例代码59.1.2.2 可以看出,这是一个标准的platform 驱动,当驱动和设备匹配以后mxsfb_probe 函数就会执行。在看mxsfb_probe 函数之前我们先简单了解一下Linux 下Framebuffer 驱动的编写流程,Linux 内核将所有的Framebuffer 抽象为一个叫做fb_info 的结构体,fb_info 结构体包含了Framebuffer 设备的完整属性和操作集合,因此每一个Framebuffer 设
备都必须有一个fb_info。换言之就是,LCD 的驱动就是构建fb_info,并且向系统注册fb_info的过程。fb_info 结构体定义在include/linux/fb.h 文件里面,内容如下(省略掉条件编译):

448 struct fb_info 
449 atomic_t count;
450 int node;
451 int flags;
452 struct mutex lock; /* 互斥锁*/
453 struct mutex mm_lock; /* 互斥锁,用于fb_mmap和smem_*域*/
454 struct fb_var_screeninfo var; /* 当前可变参数*/
455 struct fb_fix_screeninfo fix; /* 当前固定参数*/
456 struct fb_monspecs monspecs; /* 当前显示器特性*/
457 struct work_struct queue; /* 帧缓冲事件队列*/
458 struct fb_pixmap pixmap; /* 图像硬件映射*/
459 struct fb_pixmap sprite; /* 光标硬件映射*/
460 struct fb_cmap cmap; /* 当前调色板*/
461 struct list_head modelist; /* 当前模式列表*/
462 struct fb_videomode *mode; /* 当前视频模式*/
463
464 #ifdef CONFIG_FB_BACKLIGHT /* 如果LCD支持背光的话*/
465 /* assigned backlight device */
466 /* set before framebuffer registration,
467 remove after unregister */
468 struct backlight_device *bl_dev; /* 背光设备*/
469
470 /* Backlight level curve */
471 struct mutex bl_curve_mutex;
472 u8 bl_curve[FB_BACKLIGHT_LEVELS];
473 #endif
......
479 struct fb_ops *fbops; /* 帧缓冲操作函数集*/
480 struct device *device; /* 父设备*/
481 struct device *dev; /* 当前fb设备*/
482 int class_flag; /* 私有sysfs标志*/
......
486 char __iomem *screen_base; /* 虚拟内存基地址(屏幕显存) */
487 unsigned long screen_size; /* 虚拟内存大小(屏幕显存大小) */
488 void *pseudo_palette; /* 伪16位调色板*/
......
507 ;

fb_info 结构体的成员变量很多,我们重点关注var、fix、fbops、screen_base、screen_size和pseudo_palette。mxsfb_probe 函数的主要工作内容为:
①、申请fb_info。
②、初始化fb_info 结构体中的各个成员变量。
③、初始化eLCDIF 控制器。
④、使用register_framebuffer 函数向Linux 内核注册初始化好的fb_info。register_framebuffer函数原型如下:

int register_framebuffer(struct fb_info *fb_info)

函数参数和返回值含义如下:
fb_info:需要上报的fb_info。
返回值:0,成功;负值,失败。
接下来我们简单看一下mxsfb_probe 函数,函数内容如下(有缩减):

1369 static int mxsfb_probe(struct platform_device *pdev)
1370 
1371 const struct of_device_id *of_id =
1372 of_match_device(mxsfb_dt_ids, &pdev->dev);
1373 struct resource *res;
1374 struct mxsfb_info *host;
1375 struct fb_info *fb_info;
1376 struct pinctrl *pinctrl;
1377 int irq = platform_get_irq(pdev, 0);
1378 int gpio, ret;
1379
......
1394
1395 res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
1396 if (!res) 
1397 dev_err(&pdev->dev, "Cannot get memory IO resource\\n");
1398 return -ENODEV;
1399 

第1374 行,host 结构体指针变量,表示I.MX6ULL 的LCD 的主控接口,mxsfb_info 结构体是NXP 定义的针对I.MX 系列SOC 的Framebuffer 设备结构体。也就是我们前面一直说的设备结构体,此结构体包含了I.MX 系列SOC 的Framebuffer 设备详细信息,比如时钟、eLCDIF控制器寄存器基地址、fb_info 等。
第1395 行,从设备树中获取eLCDIF 接口控制器的寄存器首地址,设备树中lcdif 节点已经设置了eLCDIF 寄存器首地址为0X021C8000,因此res=0X021C8000。
第1401 行,给host 申请内存,host 为mxsfb_info 类型结构体指针。
第1407 行,给fb_info 申请内存,也就是申请fb_info。
第1413~1414 行,设置host 的fb_info 成员变量为fb_info,设置fb_info 的par 成员变量为host。通过这一步就将前面申请的host 和fb_info 联系在了一起。
第1416 行,申请中断,中断服务函数为mxsfb_irq_handler。
第1425 行,对从设备树中获取到的寄存器首地址(res)进行内存映射,得到虚拟地址,并保存到host 的base 成员变量。因此通过访问host 的base 成员即可访问I.MX6ULL 的整个eLCDIF寄存器。其实在mxsfb.c 中已经定义了eLCDIF 各个寄存器相比于基地址的偏移值,如下所示:

67 #define LCDC_CTRL 0x00
68 #define LCDC_CTRL1 0x10
69 #define LCDC_V4_CTRL2 0x20
70 #define LCDC_V3_TRANSFER_COUNT 0x20
71 #define LCDC_V4_TRANSFER_COUNT 0x30
......
89 #define LCDC_V4_DEBUG0 0x1d0
90 #define LCDC_V3_DEBUG0 0x1f0

大家可以对比着《I.MX6ULL 参考手册》中的eLCDIF 章节检查一下示例代码59.1.2.4 中的这些寄存器有没有错误。

继续回到示例代码59.1.2.5 中的mxsfb_probe 函数,第1462 行,给fb_info 中的pseudo_palette申请内存。

第1473 行,调用mxsfb_init_fbinfo 函数初始化fb_info,重点是fb_info 的var、fix、fbops,screen_base 和screen_size。其中fbops 是Framebuffer 设备的操作集,NXP 提供的fbops 为mxsfb_ops,内容如下:

987 static struct fb_ops mxsfb_ops = 
988 .owner = THIS_MODULE,
989 .fb_check_var = mxsfb_check_var,
990 .fb_set_par = mxsfb_set_par,
991 .fb_setcolreg = mxsfb_setcolreg,
992 .fb_ioctl = mxsfb_ioctl,
993 .fb_blank = mxsfb_blank,
994 .fb_pan_display = mxsfb_pan_display,
995 .fb_mmap = mxsfb_mmap,
996 .fb_fillrect = cfb_fillrect,
997 .fb_copyarea = cfb_copyarea,
998 .fb_imageblit = cfb_imageblit,
999 ;

关于mxsfb_ops 里面的各个操作函数这里就不去详解的介绍了。mxsfb_init_fbinfo 函数通过调用mxsfb_init_fbinfo_dt 函数从设备树中获取到LCD 的各个参数信息。最后,mxsfb_init_fbinfo函数会调用mxsfb_map_videomem 函数申请LCD 的帧缓冲内存(也就是显存)。

第1489~1490 行,设置eLCDIF 控制器的相应寄存器。

第1494 行,最后调用register_framebuffer 函数向Linux 内核注册fb_info。
mxsfb.c 文件很大,还有一些其他的重要函数,比如mxsfb_remove、mxsfb_shutdown 等,这里我们就简单的介绍了一下mxsfb_probe 函数,至于其他的函数大家自行查阅。

硬件原理图分析

本章实验硬件原理图参考24.2 小节即可。

LCD 驱动程序编写

前面已经说了,6ULL 的eLCDIF 接口驱动程序NXP 已经编写好了,因此LCD 驱动部分我们不需要去修改。我们需要做的就是按照所使用的LCD 来修改设备树。重点要注意三个地方:
①、LCD 所使用的IO 配置。
②、LCD 屏幕节点修改,修改相应的属性值,换成我们所使用的LCD 屏幕参数。
③、LCD 背光节点信息修改,要根据实际所使用的背光IO 来修改相应的设备节点信息。
接下来我们依次来看一下上面这两个节点改如何去修改:

1、LCD 屏幕IO 配置
首先要检查一下设备树中LCD 所使用的IO 配置,这个其实NXP 都已经给我们写好了,不需要修改,不过我们还是要看一下。打开imx6ull-alientek-emmc.dts 文件,在iomuxc 节点中找到如下内容:

1 pinctrl_lcdif_dat: lcdifdatgrp 
2 fsl,pins = <
3 MX6UL_PAD_LCD_DATA00__LCDIF_DATA00 0x79
4 MX6UL_PAD_LCD_DATA01__LCDIF_DATA01 0x79
5 MX6UL_PAD_LCD_DATA02__LCDIF_DATA02 0x79
6 MX6UL_PAD_LCD_DATA03__LCDIF_DATA03 0x79
7 MX6UL_PAD_LCD_DATA04__LCDIF_DATA04 0x79
8 MX6UL_PAD_LCD_DATA05__LCDIF_DATA05 0x79
9 MX6UL_PAD_LCD_DATA06__LCDIF_DATA06 0x79
10 MX6UL_PAD_LCD_DATA07__LCDIF_DATA07 0x79
11 MX6UL_PAD_LCD_DATA08__LCDIF_DATA08 0x79
12 MX6UL_PAD_LCD_DATA09__LCDIF_DATA09 0x79
13 MX6UL_PAD_LCD_DATA10__LCDIF_DATA10 0x79
14 MX6UL_PAD_LCD_DATA11__LCDIF_DATA11 0x79
15 MX6UL_PAD_LCD_DATA12__LCDIF_DATA12 0x79
16 MX6UL_PAD_LCD_DATA13__LCDIF_DATA13 0x79
17 MX6UL_PAD_LCD_DATA14__LCDIF_DATA14 0x79
18 MX6UL_PAD_LCD_DATA15__LCDIF_DATA15 0x79
19 MX6UL_PAD_LCD_DATA16__LCDIF_DATA16 0x79
20 MX6UL_PAD_LCD_DATA17__LCDIF_DATA17 0x79
21 MX6UL_PAD_LCD_DATA18__LCDIF_DATA18 0x79
22 MX6UL_PAD_LCD_DATA19__LCDIF_DATA19 0x79
23 MX6UL_PAD_LCD_DATA20__LCDIF_DATA20 0x79
24 MX6UL_PAD_LCD_DATA21__LCDIF_DATA21 0x79
25 MX6UL_PAD_LCD_DATA22__LCDIF_DATA22 0x79
26 MX6UL_PAD_LCD_DATA23__LCDIF_DATA23 0x79
27 >;
28 ;
29
30 pinctrl_lcdif_ctrl: lcdifctrlgrp 
31 fsl,pins = <
32 MX6UL_PAD_LCD_CLK__LCDIF_CLK 0x79
33 MX6UL_PAD_LCD_ENABLE__LCDIF_ENABLE 0x79
34 MX6UL_PAD_LCD_HSYNC__LCDIF_HSYNC 0x79
35 MX6UL_PAD_LCD_VSYNC__LCDIF_VSYNC 0x79
36 >;
37 pinctrl_pwm1: pwm1grp 
38 fsl,pins = <
39 MX6UL_PAD_GPIO1_IO08__PWM1_OUT 0x110b0
40 >;
41 ;

第2~27 行,子节点pinctrl_lcdif_dat,为RGB LCD 的24 根数据线配置项。
第30~36 行,子节点pinctrl_lcdif_ctrl,RGB LCD 的4 根控制线配置项,包括CLK、ENABLE、VSYNC 和HSYNC。
第37~40 行,子节点pinctrl_pwm1,LCD 背光PWM 引脚配置项。这个引脚要根据实际情况设置,这里我们建议大家在以后的学习或工作中,LCD 的背光IO 尽量和半导体厂商的官方开发板一致。

注意示例代码59.3.1 中默认将LCD 的电气属性都设置为0X79,这里将其都改为0X49,也就是将LCD 相关IO 的驱动能力改为R0/1,也就是降低LCD 相关IO 的驱动能力。因为前面已经说了,正点原子的ALPHA 开发板上的LCD 接口用了三个SGM3157 模拟开关,为了防止模拟开关影响到网络,因此这里需要降低LCD 数据线的驱动能力,如果你所使用的板子没有用到模拟开关那么就不需要将0X79 改为0X49。
2、LCD 屏幕参数节点信息修改
继续在imx6ull-alientek-emmc.dts 文件中找到lcdif 节点,节点内容如下所示:

1 &lcdif 
2 pinctrl-names = "default";
3 pinctrl-0 = <&pinctrl_lcdif_dat /* 使用到的IO */
4 &pinctrl_lcdif_ctrl
5 &pinctrl_lcdif_reset>;
6 display = <&display0>;
7 status = "okay";
8
9 display0: display  /* LCD属性信息*/
10 bits-per-pixel = <16>; /* 一个像素占用几个bit */
11 bus-width = <24>; /* 总线宽度*/
12
13 display-timings 
14 native-mode = <&timing0>; /* 时序信息*/
15 timing0: timing0 
16 clock-frequency = <9200000>; /* LCD像素时钟,单位Hz */
17 hactive = <480>; /* LCD X轴像素个数*/
18 vactive = <272>; /* LCD Y轴像素个数*/
19 hfront-porch = <8>; /* LCD hfp参数*/
20 hback-porch = <4>; /* LCD hbp参数*/
21 hsync-len = <41>; /* LCD hspw参数*/
22 vback-porch = <2>; /* LCD vbp参数*/
23 vfront-porch = <4>; /* LCD vfp参数*/
24 vsync-len = <10>; /* LCD vspw参数*/
25
26 hsync-active = <0>; /* hsync数据线极性*/
27 vsync-active = <0>; /* vsync数据线极性*/
28 de-active = <1>; /* de数据线极性*/
29 pixelclk-active = <0>; /* clk数据线先极性*/
30 ;
31 ;
32 ;
33 ;

示例代码59.3.2 就是向imx6ull.dtsi 文件中的lcdif 节点追加的内容,我们依次来看一下示例代码59.3.2 中的这些属性都是写什么含义。

第3 行,pinctrl-0 属性,LCD 所使用的IO 信息,这里用到了pinctrl_lcdif_dat、pinctrl_lcdif_ctrl和pinctrl_lcdif_reset 这三个IO 相关的节点,前两个在示例代码59.3.1 中已经讲解了。
pinctrl_lcdif_reset 是LCD 复位IO 信息节点,正点原子的I.MX6U-ALPHA 开发板的LCD 没有用到复位IO,因此pinctrl_lcdif_reset 可以删除掉。

第6 行,display 属性,指定LCD 属性信息所在的子节点,这里为display0,下面就是display0子节点内容。

第9~32 行,display0 子节点,描述LCD 的参数信息,第10 行的bits-per-pixel 属性用于指明一个像素占用的bit 数,默认为16bit。本教程我们将LCD 配置为RGB888 模式,因此一个像素点占用24bit,bits-per-pixel 属性要改为24。第11 行的bus-width 属性用于设置数据线宽度,因为要配置为RGB888 模式,因此bus-width 也要设置为24。

第13~30 行,这几行非常重要!因为这几行设置了LCD 的时序参数信息,NXP 官方的EVK开发板使用了一个4.3 寸的480*272 屏幕,因此这里默认是按照NXP 官方的那个屏幕参数设置的。每一个属性的含义后面的注释已经写的很详细了,大家自己去看就行了,这些时序参数就是我们重点要修改的,需要根据自己所使用的屏幕去修改。

这里以正点原子的ATK7016(7 寸1024*600)屏幕为例,将imx6ull-alientek-emmc.dts 文件中的lcdif 节点改为如下内容:

1 &lcdif 
2 pinctrl-names = "default";
3 pinctrl-0 = <&pinctrl_lcdif_dat /* 使用到的IO */
4 &pinctrl_lcdif_ctrl>;
5 display = <&display0>;
6 status = "okay";
7
8 display0: display  /* LCD属性信息*/
9 bits-per-pixel = <24>; /* 一个像素占用24bit */
10 bus-width = <24>; /* 总线宽度*/
11
12 display-timings 
13 native-mode = <&timing0>; /* 时序信息*/
14 timing0: timing0 
15 clock-frequency = <51200000>; /* LCD像素时钟,单位Hz */
16 hactive = <1024>; /* LCD X轴像素个数*/
17 vactive = <600>; /* LCD Y轴像素个数*/
18 hfront-porch = <160>; /* LCD hfp参数*/
19 hback-porch = <140>; /* LCD hbp参数*/
20 hsync-len = <20>; /* LCD hspw参数*/
21 vback-porch = <20>; /* LCD vbp参数*/
22 vfront-porch = <12>; /* LCD vfp参数*/
23 vsync-len = <3>; /* LCD vspw参数*/
24
25 hsync-active = <0>; /* hsync数据线极性*/
26 vsync-active = <0>; /* vsync数据线极性*/
27 de-active = <1>; /* de数据线极性*/
28 pixelclk-active = <0>; /* clk数据线先极性*/
29 ;
30 ;
31 ;
32 ;

第3 行,设置LCD 屏幕所使用的IO,删除掉原来的pinctrl_lcdif_reset,因为没有用到屏幕复位IO,其他的IO 不变。
第9 行,使用RGB888 模式,所以一个像素点是24bit。
第15~23 行,ATK7016 屏幕时序参数,根据自己所使用的屏幕修改即可。

3、LCD 屏幕背光节点信息
正点原子的LCD 接口背光控制IO 连接到了I.MX6U 的GPIO1_IO08 引脚上,GPIO1_IO08复用为PWM1_OUT,通过PWM 信号来控制LCD 屏幕背光的亮度,这个我们已经在第二十九章详细的讲解过了。正点原子I.MX6U-ALPHA 开发板的LCD 背光引脚和NXP 官方EVK 开发板的背光引脚一样,因此背光的设备树节点是不需要修改的,但是考虑到其他同学可能使用别的开发板或者屏幕,LCD 背光引脚和NXP 官方EVK 开发板可能不同,因此我们还是来看一下如何在设备树中添加背光节点信息。

首先是GPIO1_IO08 这个IO 的配置,在imx6ull-alientek-emmc.dts 中找到如下内容:

1 pinctrl_pwm1: pwm1grp 
2 fsl,pins = <
3 MX6UL_PAD_GPIO1_IO08__PWM1_OUT 0x110b0
4 >;
5 ;

pinctrl_pwm1 节点就是GPIO1_IO08 的配置节点,从第3 行可以看出,设置GPIO1_IO08这个IO 复用为PWM1_OUT,并且设置电气属性值为0x110b0。

LCD 背光要用到PWM1,因此也要设置PWM1 节点,在imx6ull.dtsi 文件中找到如下内容:

1 pwm1: pwm@02080000 
2 compatible = "fsl,imx6ul-pwm", "fsl,imx27-pwm";
3 reg = <0x02080000 0x4000>;
4 interrupts = <GIC_SPI 83 IRQ_TYPE_LEVEL_HIGH>;
5 clocks = <&clks IMX6UL_CLK_PWM1>,
6 <&clks IMX6UL_CLK_PWM1>;
7 clock-names = "ipg", "per";
8 #pwm-cells = <2>;
9 ;

imx6ull.dtsi 文件中的pwm1 节点信息大家不要修改,如果要修改pwm1 节点内容的话请在imx6ull-alientek-emmc.dts 文件中修改。在整个Linux 源码文件中搜索compatible 属性的这两个值即可找到imx6ull 的pwm 驱动文件,imx6ull 的PWM 驱动文件为drivers/pwm/pwm-imx.c,这里我们就不详细的去分析这个文件了。继续在imx6ull-alientek-emmc.dts 文件中找到向pwm1
追加的内容,如下所示:

1 &pwm1 
2 pinctrl-names = "default";
3 pinctrl-0 = <&pinctrl_pwm1>;
4 status = "okay";
5 ;

第3 行,设置pwm1 所使用的IO 为pinctrl_pwm1,也就是示例代码59.3.4 所定义的GPIO1_IO08 这个IO。
第4 行,将status 设置为okay。

如果背光用的其他pwm 通道,比如pwm2,那么就需要仿照示例代码59.3.6 的内容,向pwm2 节点追加相应的内容。pwm 和相关的IO 已经准备好了,但是Linux 系统怎么知道PWM1_OUT 就是控制LCD 背光的呢?因此我们还需要一个节点来将LCD 背光和PWM1_OUT连接起来。这个节点就是backlight ,backlight 节点描述可以参考
Documentation/devicetree/indings/video/backlight/pwm-backlight.txt 这个文档,此文档详细讲解了backlight 节点该如何去创建,这里大概总结一下:

linuxmisc驱动实验(代码片段)

目录MISC设备驱动简介硬件原理图分析实验程序编写修改设备树beep驱动程序编写编写测试APP运行测试编译驱动程序和测试APP运行测试misc的意思是混合、杂项的,因此MISC驱动也叫做杂项驱动,也就是当我们板子上的某些外... 查看详情

2018-2019-1201652272016522820165237实验四外设驱动程序设计(代码片段)

2018-2019-1201652272016522820165237实验四外设驱动程序设计实验目的编写最简单的字符驱动程序实现简单的读写功能,并列出常用格式的Makefile以及驱动的加载和卸载脚本熟悉字符设备驱动的整个编写流程实验内容及步骤任务一实验要... 查看详情

2018-2019-1201652272016522820165237实验四外设驱动程序设计(代码片段)

2018-2019-1201652272016522820165237实验四外设驱动程序设计实验目的编写最简单的字符驱动程序实现简单的读写功能,并列出常用格式的Makefile以及驱动的加载和卸载脚本熟悉字符设备驱动的整个编写流程实验内容及步骤任务一实验要... 查看详情

七linuxlcd驱动代分析

LCD驱动分析原文地址:http://blog.csdn.net/woshidahuaidan2011/article/details/520547951、对LCD驱动添加设备信息对lcd驱动程序,跟之前分析的方式一样,还是先看设备信息,其定义在Mach-smdk2440.c(arch\\arm\\mach-s3c24xx)文件中,在该文... 查看详情

设备树下的led驱动实验(代码片段)

目录设备树LED驱动原理硬件原理图分析实验程序编写修改设备树文件LED灯驱动程序编写编写测试APP运行测试编译驱动程序和测试APP运行测试上一章我们详细的讲解了设备树语法以及在驱动开发中常用的OF函数,本章我们就开... 查看详情

新字符设备驱动实验(代码片段)

目录新字符设备驱动原理分配和释放设备号新的字符设备注册方法自动创建设备节点mdev机制创建和删除类创建设备参考示例设置文件私有数据硬件原理图分析实验程序编写LED灯驱动程序编写编写测试APP运行测试编译驱动程序和... 查看详情

linuxled驱动开发实验(代码片段)

目录Linux下LED灯驱动原理地址映射I/O内存访问函数硬件原理图分析实验程序编写LED灯驱动程序编写编写测试APP运行测试编译驱动程序和测试APP运行测试上一章我们详细的讲解了字符设备驱动开发步骤,并且用一个虚拟的chrdevba... 查看详情

platform设备驱动实验(代码片段)

目录Linux驱动的分离与分层驱动的分隔与分离驱动的分层platform平台驱动模型简介platform总线platform驱动platform设备硬件原理图分析试验程序编写platform设备与驱动程序编写测试APP编写54.5运行测试编译驱动程序和测试APP运行测试我... 查看详情

linuxrtc驱动实验(代码片段)

目录Linux内核RTC驱动简介I.MX6U内部RTC驱动分析RTC时间查看与设置RTC也就是实时时钟,用于记录当前系统时间,对于Linux系统而言时间是非常重要的,就和我们使用Windows电脑或手机查看时间一样,我们在使用Linux设备... 查看详情

linuxinput子系统实验(代码片段)

目录input子系统input子系统简input驱动编写流程input_event结构体硬件原理图分析实验程序编写修改设备树文件按键input驱动程序编写编写测试APP运行测试编译驱动程序和测试APP运行测试Linux自带按键驱动程序的使用自带按键驱动程序... 查看详情

linux自带的led灯驱动实验(代码片段)

目录Linux内核自带LED驱动使能Linux内核自带LED驱动简介LED灯驱动框架分析module_platform_driver函数简析gpio_led_probe函数简析设备树节点编写运行测试前面我们都是自己编写LED灯驱动,其实像LED灯这样非常基础的设备驱动,Linux... 查看详情

linuxcan驱动实验(代码片段)

CAN简介CAN全称(ControllerAreaNetwork)控制局域网络。最早是由德国BOSCH(博世)开发,是当前应用最广泛的现场总线之一。应用领域汽车电子、工业自动化、医疗、工业和船舶等。CAN特点:1)多主控制在... 查看详情

2018-2019-120165211实验四外设驱动程序设计(代码片段)

2018-2019-120165211实验四外设驱动程序设计任务一1.实验要求学习资源中全课中的“hqyj.嵌入式Linux应用程序开发标准教程.pdf”中的第十一章提交康奈尔笔记的照片(可以多张)2.任务完成任务二1.实验要求在Ubuntu完成资源中全课中的... 查看详情

linuxi2c驱动实验(代码片段)

目录LinuxI2C驱动框架简介I2C总线驱动I2C设备驱动I2C设备和驱动匹配过程I.MX6U的I2C适配器驱动分析I2C设备驱动编写流程I2C设备信息描述I2C设备数据收发处理流程硬件原理图分析实验程序编写修改设备树AP3216C驱动编写编写测试APP运行... 查看详情

层级中断控制器驱动程序上机实验(代码片段)

层级中断控制器驱动程序上机实验1.确定中断号n1.1IMX6ULL1.2STM32MP1572.怎么触发中断3.上机实验3.1设置工具链1.STM32MP1572.IMX6ULL3.2编译、替换设备树1.STM32MP1572.IMX6ULL3.3编译、安装驱动程序参考资料:linuxkernel的中断子系统之(七... 查看详情

linux蜂鸣器实验(代码片段)

目录蜂鸣器驱动原理硬件原理图分析实验程序编写修改设备树文件蜂鸣器驱动程序编写编写测试APP运行测试编译驱动程序和测试APP运行测试上一章实验中我们借助pinctrl和gpio子系统编写了LED灯驱动,I.MX6U-ALPHA开发板上还有一... 查看详情

proteus仿真arduinouno驱动直流有刷电机转动实验(代码片段)

【Proteus仿真】ArduinoUNO驱动直流有刷电机转动实验Proteus仿真在Proteus软件里面使用L273D驱动芯片替代带MX1508驱动芯片进行仿真。MX1508双路马达驱动模块接脚图mx1508原理图示例代码/*MX1508四通道双路有刷直流马达驱动模块简易控制电... 查看详情

异步通知实验(代码片段)

目录异步通知异步通知简介驱动中的信号处理应用程序对异步通知的处理硬件原理图分析实验程序编写修改设备树文件程序编写编写测试APP运行测试编译驱动程序和测试APP运行测试在前面使用阻塞或者非阻塞的方式来读取驱动中... 查看详情