在裸机篇我们已经学习过了如何使用 I.MX6ULL 的 PWM 外设来实现 LCD 的背光调节,其实在 Linux 的 LCD 驱动实验我们也提到过 I.MX6ULL 的 PWM 背光调节,但是并没有专门的去讲解 PWM 部分,本章我们就来学习一下 Linux 下的 PWM 驱动开发。
PWM 驱动简析
关于 PWM 原理以及 I.MX6ULL 的 PWM 外设已经在裸机篇进行了详细的讲解,这里就不再赘述了,我们重点来看一下 NXP 原厂提供的 Linux 内核自带的 PWM 驱动。
设备树下的 PWM 控制器节点
I.MX6ULL 有 8 路 PWM 输出,因此对应 8 个 PWM 控制器,所有在设备树下就有 8 个PWM 控制器节点。这 8 路 PWM 都属于 I.MX6ULL 的 AIPS-1 域,但是在设备树 imx6ull.dtsi 中分为了两部分,PWM1~PWM4 在一起,PWM5~PWM8 在一起,这 8 路 PWM 并没有全部放到一起,这一点一定要注意,不要以为 imx6ull.dtsi 没有写完整。这 8 路 PWM 的设备树节点内容都是一样的,除了 reg 属性不同(毕竟不同的控制器,其地址范围不同)。本章实验我们使用GPIO1_IO04 这个引脚来完成 PWM 实验,而 GPIO1_IO04 就是 PWM3 的输出引脚,所以这里我们就以 PWM3 为例进行讲解,imx6ull.dtsi 文件中的 pwm3 节点信息如下:
第 2 行,compatible 属性值有两个“fsl,imx6ul-pwm”和“fsl,imx27-pwm”,所以在整个 Linux源码里面搜索这两个字符窜即可找到 I.MX6ULL 的 PWM 驱动文件,这个文件就是drivers/pwm/pwm-imx.c。
关 于 I.MX6ULL 的 PWM 节 点 更 为 详 细 的 信 息 请 参 考 对 应 的 绑 定 文 档 :Documentation/devicetree/bindings/pwm/ imx-pwm.txt,这里就不去分析了。
PWM 子系统
Linux 内核提供了个 PWM 子系统框架,编写 PWM 驱动的时候一定要符合这个框架。PWM子系统的核心是 pwm_chip 结构体,定义在文件 include/linux/pwm.h 中,定义如下:
第 4 行,pwm_ops 结构体就是 PWM 外设的各种操作函数集合,编写 PWM 外设驱动的时候需要开发人员实现。pwm_ops 结构体也定义在 pwm.h 头文件中,定义如下:
pwm_ops 中的这些函数不一定全部实现,但是像 config、enable 和 disable 这些肯定是需要实现的,否则的话打开/关闭 PWM,设置 PWM 的占空比这些就没操作了。
PWM 子系统驱动的核心就是初始化 pwm_chip 结构体各成员变量,然后向内核注册初始化完成以后的 pwm_chip。这里就要用到 pwmchip_add 函数,此函数定义在 drivers/pwm/core.c 文件中,函数原型如下:
int pwmchip_add(struct pwm_chip *chip)
函数参数和返回值含义如下:
chip:要向内核注册的 pwm_chip。
返回值:0 成功;负数 失败。
卸载 PWM 驱动的时候需要将前面注册的 pwm_chip 从内核移除掉,这里要用到pwmchip_remove 函数,函数原型如下:
int pwmchip_remove(struct pwm_chip *chip)
函数参数和返回值含义如下:
chip:要移除的 pwm_chip。
返回值:0 成功;负数 失败。
PWM 驱动源码分析
我们简单分析一下 Linux 内核自带的 I.MX6ULL PWM 驱动,驱动文件前面都说了,是 pwm-imx.c 这个文件。打开这个文件,可以看到,这是一个标准的平台设备驱动文件,如下所示:
第 3 行,当设备树 PWM 节点的 compatible 属性值为“fsl,imx27-pwm”的话就会匹配此驱动,注意后面的.data 为 imx_pwm_data_v2,这是一个 imx_pwm_data 类型的结构体变量,内容如下:
imx_pwm_config_v2 函数就是最终操作 I.MX6ULL 的 PWM 外设寄存器,进行实际配置的函数。imx_pwm_set_enable_v2 就是具体使能 PWM 的函数。
第 14 行,当设备树节点和驱动匹配以后 imx_pwm_probe 函数就会执行。
imx_pwm_probe 函数具体参考正点原子驱动开发手册。
此处暂略。
PWM 驱动编写
修改设备树
PWM 驱动就不需要我们再编写了,NXP 已经写好了,前面我们也已经详细的分析过这个驱动源码了。我们在实际使用的时候只需要修改设备树即可,ALPHA 开发板上的 JP2 排针引出了 GPIO1_IO04 这个引脚,如图 73.2.1.1 所示:
GPIO1_IO04 可以作为 PWM3 的输出引脚,所以我们需要在设备树里面添加 GPIO1_IO04的引脚信息以及 PWM3 控制器对应的节点信息。
1、添加 GPIO1_IO04 引脚信息
打开 imx6ull-alientek-emmc.dts 文件,在 iomuxc 节点下添加 GPIO1_IO04 的引脚信息,如下所示:
2、向 pwm3 节点追加信息
前面已经讲过了,imx6ull.dtsi 文件中已经有了“pwm3”节点,但是还不能直接使用,需要在 imx6ull-alientek-emmc.dts 文件中向 pwm3 节点追加一些内容,在 imx6ull-alientek-emmc.dts 文件中加入如下所示内容:
第 3 行,pinctrl-0 属性指定 PWM3 所使用的输出引脚对应的 pinctrl 节点,这里设置为示例代码 73.2.1 中的 pinctrl_pwm3。
第 4 和 5 行,设置时钟,第 4 行设置 ipg 时钟,第 5 行设置 per 时钟。有些 pwm 节点默认时钟源是 IMX6UL_CLK_DUMMY,这里我们需要将其改为对应的时钟,比如这里设置为IMX6UL_CLK_PWM3。PWM1~PWM8 分别对应 IMX6UL_CLK_PWM1~ IMX6UL_CLK_PWM8。
3、屏蔽掉其他复用的 IO
检查一下设备树中有没有其他外设用到 GPIO1_IO04,如果有的话需要屏蔽掉!注意,不能只屏蔽掉 GPIO1_IO04 的 pinctrl 配置信息,也要搜索一下“gpio1 4”,看看有没有哪里用到,用到的话也要屏蔽掉。
设备树修改完成以后重新编译设备树,然后使用新的设备树启动系统。
使能 PWM 驱动
NXP 官方的 Linux 内核已经默认使能了 PWM 驱动,所以不需要我们修改,但是为了学习,我们还是需要知道怎么使能。打开 Linux 内核配置界面,按照如下路径找到配置项:
PWM 驱动测试
使用新的设备树启动系统,然后将开发板 JP2 排针上的 GPIO_4(GPIO1_IO04)引脚连接到示波器上,通过示波器来查看 PWM 波形图。
直接参考:
Linux-PWM应用编程-CSDN博客
更多待补充。