腾讯cdc用wordpress,网站内容页优化,沈阳网站开发久,济宁门户网站建设Linux版本号4.1.15 芯片I.MX6ULL 大叔学Linux 品人间百味 思文短情长 本章的思维导图如下#xff1a; 一、PWM驱动简析
1、设备树下的PWM控制节点 8 路 PWM 都属于 I.MX6ULL 的 AIPS-1 域#xff0c;分为了两部分#xff0c; PWM1~P…Linux版本号4.1.15 芯片I.MX6ULL 大叔学Linux 品人间百味 思文短情长 本章的思维导图如下 一、PWM驱动简析
1、设备树下的PWM控制节点 8 路 PWM 都属于 I.MX6ULL 的 AIPS-1 域分为了两部分 PWM1~PWM4 在一起 PWM5~PWM8 在一起。 本章实验我们使用GPIO1_IO04 这个引脚来完成 PWM 实验而 GPIO1_IO04 就是 PWM3 的输出引脚。
1 pwm3: pwm02088000 {
2 compatible fsl,imx6ul-pwm, fsl,imx27-pwm;/* compatible 属性值有两个“fsl,imx6ul-pwm”和“fsl,imx27-pwm”所以在整个 Linux
源码里面搜索这两个字符窜即可找到 I.MX6ULL 的 PWM 驱动文件这个文件就是
drivers/pwm/pwm-imx.c。*/
3 reg 0x02088000 0x4000;
4 interrupts GIC_SPI 85 IRQ_TYPE_LEVEL_HIGH;
5 clocks clks IMX6UL_CLK_PWM3,
6 clks IMX6UL_CLK_PWM3;
7 clock-names ipg, per;
8 #pwm-cells 2;
9 };
2、PWM子系统 框架 PWM子系统的核心是 pwm_chip 结构体定义在文件 include/linux/pwm.h 中定义如下
1 struct pwm_chip {
2 struct device *dev;
3 struct list_head list;
4 const struct pwm_ops *ops;/*第 4 行 pwm_ops 结构体就是 PWM 外设的各种操作函数集合编写 PWM 外设驱动的时
候需要开发人员实现。 */
5 int base;
6 unsigned int npwm;
7 struct pwm_device *pwms;
8 struct pwm_device * (*of_xlate)(struct pwm_chip *pc,
9 const struct of_phandle_args *args);
10 unsigned int of_pwm_n_cells;
11 bool can_sleep;
12 }; pwm_ops 结构体也定义在 pwm.h 头文件中定义如下
/*
pwm_ops 中的这些函数不一定全部实现但是像 config、 enable 和 disable 这些肯定是需要
实现的否则的话打开/关闭 PWM设置 PWM 的占空比这些就没操作了。
*/1 struct pwm_ops {
2 int (*request)(struct pwm_chip *chip, //请求 PWM
3 struct pwm_device *pwm);
4 void (*free)(struct pwm_chip *chip, //释放 PWM
5 struct pwm_device *pwm);
6 int (*config)(struct pwm_chip *chip, //配置 PWM 周期和占空比
7 struct pwm_device *pwm,
8 int duty_ns, int period_ns);
9 int (*set_polarity)(struct pwm_chip *chip, //设置 PWM 极性
10 struct pwm_device *pwm,
11 enum pwm_polarity polarity);
12 int (*enable)(struct pwm_chip *chip, //使能 PWM
13 struct pwm_device *pwm);
14 void (*disable)(struct pwm_chip *chip, //关闭 PWM
15 struct pwm_device *pwm);
16 struct module *owner;
17 }; PWM 子系统驱动的核心就是初始化 pwm_chip 结构体各成员变量然后向内核注册初始化完成以后的 pwm_chip。
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 成功负数 失败。
*/ 3、PWM驱动源码分析
1 static const struct of_device_id imx_pwm_dt_ids[] {
2 { .compatible fsl,imx1-pwm, .data imx_pwm_data_v1, },
3 { .compatible fsl,imx27-pwm, .data imx_pwm_data_v2, },
/*
第 3 行当设备树 PWM 节点的 compatible 属性值为“fsl,imx27-pwm”的话就会匹配此驱
动
后面的.data 为 imx_pwm_data_v2这是一个 imx_pwm_data 类型的结构体变量内容
如下
1 static struct imx_pwm_data imx_pwm_data_v2 {
2 .config imx_pwm_config_v2,
3 .set_enable imx_pwm_set_enable_v2,
4 };imx_pwm_config_v2 函数就是最终操作 I.MX6ULL 的 PWM 外设寄存器进行实际配置的
函数。 imx_pwm_set_enable_v2 就是具体使能 PWM 的函数。
*/
4 { /* sentinel */ }
5 };
6 7
......
8 9
static struct platform_driver imx_pwm_driver {
10 .driver {
11 .name imx-pwm,
12 .of_match_table imx_pwm_dt_ids,
13 },
14 .probe imx_pwm_probe,/*第 14 行当设备树节点和驱动匹配以后 imx_pwm_probe 函数就会执行。*/
15 .remove imx_pwm_remove,
16 };
17
18 module_platform_driver(imx_pwm_driver); imx_pwm_probe 函数如下(有缩减)
1 static int imx_pwm_probe(struct platform_device *pdev)
2 {
3 const struct of_device_id *of_id
4 of_match_device(imx_pwm_dt_ids, pdev-dev);
5 const struct imx_pwm_data *data;
6 struct imx_chip *imx;
7 struct resource *r;
8 int ret 0;
9
10 if (!of_id)
11 return -ENODEV;
12
13 imx devm_kzalloc(pdev-dev, sizeof(*imx), GFP_KERNEL);
/*
第 13 行 imx 是一个 imx_chip 类型的结构体指针变量这里为其申请内存。 imx_chip 结
构体有个重要的成员变量 chip chip 是 pwm_chip 类型的。所以这一行就引出了 PWM 子系统
核心部件 pwm_chip稍后的重点就是初始化 chip。
*/
14 if (imx NULL)
15 return -ENOMEM;
......
/*
第 31~35 行初始化 imx 的 chip 成员变量也就是初始化 pwm_chip
*/
31 imx-chip.ops imx_pwm_ops;
32 imx-chip.dev pdev-dev;
33 imx-chip.base -1;
34 imx-chip.npwm 1;
35 imx-chip.can_sleep true;
36
/*37 和 38 行从设备树中获取 PWM 节点中关于 PWM 控
制器的地址信息然后再进行内存映射这样我们就得到了 PWM 控制器的基地址。
*/
37 r platform_get_resource(pdev, IORESOURCE_MEM, 0);
38 imx-mmio_base devm_ioremap_resource(pdev-dev, r);
39 if (IS_ERR(imx-mmio_base))
40 return PTR_ERR(imx-mmio_base);
41
42 data of_id-data;/*第 43 和 44 行这两行设置 imx 的 config 和 set_enable 这两个成员变量为 data-config 和
data-set_enableimx_pwm_enable、 imx_pwm_disable 和 imx_pwm_config 这三个函数最终调用就是
imx_pwm_config_v2 和 imx_pwm_set_enable_
*/
43 imx-config data-config;
44 imx-set_enable data-set_enable;
45
46 ret pwmchip_add(imx-chip);
47 if (ret 0)
48 return ret;
49
50 platform_set_drvdata(pdev, imx);
51 return 0;
52 } 设置 pwm_chip的 ops 操作集为 imx_pwm_ops imx_pwm_ops 定义如下
1 static struct pwm_ops imx_pwm_ops {
2 .enable imx_pwm_enable,
3 .disable imx_pwm_disable,
4 .config imx_pwm_config,
5 .owner THIS_MODULE,
6 }; imx_pwm_set_enable_v2打开或关闭对应的PWM
1 static void imx_pwm_set_enable_v2(struct pwm_chip *chip, bool enable)
2 {
3 struct imx_chip *imx to_imx_chip(chip);
4 u32 val;
5
6 val readl(imx-mmio_base MX3_PWMCR);/*第 6 行读取 PWMCR 寄存器的值。*/
7
8 if (enable)
9 val | MX3_PWMCR_EN;/*第 9 行如果 enable 为真表示使能 PWM将 PWMCR 寄存器的 bit0 置 1 即可宏
MX3_PWMCR_EN 为(10)。*/
10 else
11 val ~MX3_PWMCR_EN;/*第 11 行如果 enable 不为真表示关闭 PWM将 PWMCR 寄存器的 bit0 清 0 即可。*/
12
13 writel(val, imx-mmio_base MX3_PWMCR);/*第 13 行将新的 val 值写入到 PWMCR 寄存器中。*/
14 } imx_pwm_config_v2 函数用于设置 PWM 的频率和占空比相关操作如下
1 static int imx_pwm_config_v2(struct pwm_chip *chip,
2 struct pwm_device *pwm, int duty_ns, int period_ns)
3 {
4 struct imx_chip *imx to_imx_chip(chip);
5 struct device *dev chip-dev;
6 unsigned long long c;
7 unsigned long period_cycles, duty_cycles, prescale;
8 unsigned int period_ms;
9 bool enable test_bit(PWMF_ENABLED, pwm-flags);
10 int wait_count 0, fifoav;
11 u32 cr, sr;
12
......
42/*
第43~62行根据参数duty_ns和period_ns来计算出应该写入到寄存器里面的值 duty_cycles
和 period_cycles。
*/
43 c clk_get_rate(imx-clk_per);
44 c c * period_ns;
45 do_div(c, 1000000000);
46 period_cycles c;
47
48 prescale period_cycles / 0x10000 1;
49
50 period_cycles / prescale;
51 c (unsigned long long)period_cycles * duty_ns;
52 do_div(c, period_ns);
53 duty_cycles c;
54
55 /*
56 * according to imx pwm RM, the real period value should be
57 * PERIOD value in PWMPR plus 2.
58 */
59 if (period_cycles 2)
60 period_cycles - 2;
61 else
62 period_cycles 0;
63
64 writel(duty_cycles, imx-mmio_base MX3_PWMSAR);/*第 64 行将计算得到的 duty_cycles 写入到 PWMSAR 寄存器中设置 PWM 的占空比*/
65 writel(period_cycles, imx-mmio_base MX3_PWMPR);/*第 65 行将计算得到的 period_cycles 写入到 PWMPR 寄存器中设置 PWM 的频率。*/
66
67 cr MX3_PWMCR_PRESCALER(prescale) |
68 MX3_PWMCR_DOZEEN | MX3_PWMCR_WAITEN |
69 MX3_PWMCR_DBGEN | MX3_PWMCR_CLKSRC_IPG_HIGH;
70
71 if (enable)
72 cr | MX3_PWMCR_EN;
73
74 writel(cr, imx-mmio_base MX3_PWMCR);
75
76 return 0;
77 } 二、PWM驱动编写
1、修改设备树
1)添加 GPIO1_IO04 引脚信息
在 iomuxc 节点下添加 GPIO1_IO04 的引脚信息如下所示
1 pinctrl_pwm3: pwm3grp {
2 fsl,pins
3 MX6UL_PAD_GPIO1_IO04__PWM3_OUT 0x110b0
4 ;
5 };
2)向 pwm3 节点追加信息 在 imx6ull-alientek-emmc.dts 文件中向 pwm3 节点追加一些内容在 imx6ull-alientek-emmc.dts 文件中加入如下所示内容
1 pwm3 {
2 pinctrl-names default;
3 pinctrl-0 pinctrl_pwm3;/*第 3 行 pinctrl-0 属性指定 PWM3 所使用的输出引脚对应的 pinctrl 节点*/
4 clocks clks IMX6UL_CLK_PWM3,/*第 4 和 5 行设置时钟第 4 行设置 ipg 时钟第 5 行设置 per 时钟。*/
5 clks IMX6UL_CLK_PWM3;
6 status okay;
7 }; 3)屏蔽掉其他复用的 IO
2、使能PWM驱动 - Device Drivers - Pulse-Width Modulation (PWM) Support - * i.MX PWM support
三、PWM驱动测试 使用新的设备树启动系统然后将开发板 JP2 排针上的 GPIO_4(GPIO1_IO04)引脚连接到示波器上通过示波器来查看 PWM 波形图。 直接在用户层来配置 PWM进入目录/sys/class/pwm 中: 1、调出 pwmchip2 的 pwm0 子目录
echo 0 /sys/class/pwm/pwmchip2/export 执行完成会在 pwmchip2 目录下生成一个名为“pwm0”的子目录
2、使能 PWM3
echo 1 /sys/class/pwm/pwmchip2/pwm0/enable
3、设置 PWM3 的频率
echo 50000 /sys/class/pwm/pwmchip2/pwm0/period 4、设置 PWM3 的占空比
不能直接设置占空比而是设置的一个周期的 ON 时间也就是高电平时间
echo 10000 /sys/class/pwm/pwmchip2/pwm0/duty_cycle
5、PWM背光设置
必要的属性如下
compatible 内容必须为“pwm-backlight”通过这个可以匹配到内核自带的 PWM 背光驱动驱动文件为 drivers/video/backlight/pwm_bl.c这里就不去分析驱动源码了。
pwms 此属性指定背光使用哪一路 PWM以及 PWM 相关的属性。
brightness-levels 背光等级数组范围 0~255对应占空比为 0%~100%。数组内的值必须从 0 开始也就是 0%占空比最后一个值必须是 255也就是 100%占空比。数组中间值的个数以及值大小可以自行定义。
default-brightness-level 默认的背光等级也就是 brightness-levels 属性中第几个值注意这里是数索引编号不是具体的数值
power-supply 支持的电压此属性可以不需要。 以正点原子 ALPHA 开发板为例看一下 PWM 背光节点是如何设置的打开 imx6ullalientek-emmc.dts找到如下所示节点内容
1 backlight {
2 compatible pwm-backlight;/*compatible 属性必须为“pwm-backlight”*/
3 pwms pwm1 0 5000000;
/*
pwms 属性指定背光所使用的 pwm 通道第一个参数指定使用 pwm1由于
I.MX6ULL 的 PWM 只有一个通道因此这里为 0。最后一个参数是 PWM 周期单位为 ns这
里 PWM 周期为 5000000ns频率为 200Hz。
*/
4 brightness-levels 0 4 8 16 32 64 128 255;/*第 4 行背光等级数组一共 8 个等级索引编号从 0 到 7。*/
5 default-brightness-level 7;/*第 5 行背光默认处于第 7 等级也就是 255为 100%占空比。*/
6 status okay;
7 }; 本笔记为参考正点原子开发板配套教程整理而得仅用于学习交流使用未经允许不得用于商业用途。