I.MX6ULL_Linux_驱动篇(56)linux PWM驱动

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 节点信息如下:

1 pwm3: pwm@02088000 {
2     compatible = "fsl,imx6ul-pwm", "fsl,imx27-pwm";
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 行, 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 中,定义如下:

1 struct pwm_chip {
2     struct device *dev;
3     struct list_head list;
4     const struct pwm_ops *ops;
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 };

第 4 行, pwm_ops 结构体就是 PWM 外设的各种操作函数集合,编写 PWM 外设驱动的时候需要开发人员实现。 pwm_ops 结构体也定义在 pwm.h 头文件中,定义如下:

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_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 驱动,驱动文件前面都说了,是 pwmimx.c 这个文件。打开这个文件,可以看到,这是一个标准的平台设备驱动文件,如下所示:

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, },
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,
15         .remove = imx_pwm_remove,
16     };
17
18 module_platform_driver(imx_pwm_driver);

第 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 的函数。
第 14 行,当设备树节点和驱动匹配以后 imx_pwm_probe 函数就会执行。
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);
14     if (imx == NULL)
15         return -ENOMEM;
......
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     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     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 }

第 13 行, imx 是一个 imx_chip 类型的结构体指针变量,这里为其申请内存。 imx_chip 结构体有个重要的成员变量 chip, chip 是 pwm_chip 类型的。所以这一行就引出了 PWM 子系统核心部件 pwm_chip,稍后的重点就是初始化 chip。
第 31~35 行,初始化 imx 的 chip 成员变量,也就是初始化 pwm_chip!第 31 行设置 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_enable、 imx_pwm_disable 和 imx_pwm_config 这三个函数就是使能、关闭和配置PWM 的函数。
37 和 38 行,从设备树中获取 PWM 节点中关于 PWM 控制器的地址信息,然后在进行内存映射,这样我们就得到了 PWM 控制器的基地址。
第 43 和 44 行,这两行设置 imx 的 config 和 set_enable 这两个成员变量为 data->config 和data->set_enable,也就是示例代码中的 imx_pwm_config_v2 和 imx_pwm_set_enable_v2这两个函数。 imx_pwm_enable、 imx_pwm_disable 和 imx_pwm_config 这三个函数最终调用就是imx_pwm_config_v2 和 imx_pwm_set_enable_v2。
整 个 pwm-imx.c 文 件 里 面 , 最 终 和 I.MX6ULL 的 PWM 寄 存 器 打 交 道 的 就 是imx_pwm_config_v2 和 imx_pwm_set_enable_v2 这 两 个 函 数 , 我 们 先 来 看 一 下
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);
7 
8     if (enable)
9         val |= MX3_PWMCR_EN;
10    else
11        val &= ~MX3_PWMCR_EN;
12
13    writel(val, imx->mmio_base + MX3_PWMCR);
14 }

第 6 行,读取 PWMCR 寄存器的值。
第 9 行,如果 enable 为真,表示使能 PWM,将 PWMCR 寄存器的 bit0 置 1 即可,宏MX3_PWMCR_EN 为(1<<0)。
第 11 行,如果 enable 不为真,表示关闭 PWM,将 PWMCR 寄存器的 bit0 清 0 即可。
第 13 行,将新的 val 值写入到 PWMCR 寄存器中。
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     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);
65     writel(period_cycles, imx->mmio_base + MX3_PWMPR);
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 }

第43~62行,根据参数duty_ns和period_ns来计算出应该写入到寄存器里面的值duty_cycles和 period_cycles。
第 64 行,将计算得到的 duty_cycles 写入到 PWMSAR 寄存器中,设置 PWM 的占空比
第 65 行,将计算得到的 period_cycles 写入到 PWMPR 寄存器中,设置 PWM 的频率。
至此, I.MX6ULL 的 PWM 驱动我们就分析完了。
 

PWM 驱动测试

使用新的设备树启动系统,然后将开发板 JP2 排针上的 GPIO_4(GPIO1_IO04)引脚连接到示波器上,通过示波器来查看 PWM 波形图。我们可以直接在用户层来配置 PWM,进入目录
/sys/class/pwm 中,如图所示:

图中 pwmchip0~pwmchip7 对应 I.MX6ULL 的 PWM1~PWM8,所以我们需要用到pwmchip2。
首先需要调出 pwmchip2 下的 pwm0 目录,否则后续就没法操作了,输入如下命令:

echo 0 > /sys/class/pwm/pwmchip2/export

执行完成会在 pwmchip2 目录下生成一个名为“pwm0”的子目录,如图所示:

输入如下命令使能 PWM3:

echo 1 > /sys/class/pwm/pwmchip2/pwm0/enable

注意,这里设置的是周期值,单位为 ns,比如 20KHz 频率的周期就是 50000ns,输入如下命令:

echo 50000 > /sys/class/pwm/pwmchip2/pwm0/period

这里不能直接设置占空比,而是设置的一个周期的 ON 时间,也就是高电平时间,比如20KHz 频率下 20%占空比的 ON 时间就是 10000,输入如下命令:

echo 10000 > /sys/class/pwm/pwmchip2/pwm0/duty_cycle

设置完成使用示波器查看波形是否正确,正确的话如图所示:

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/286684.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

『VUE』01. 开发前的准备(详细图文注释)

目录 nodejs安装软件检查环境变量设置镜像源 安装vue环境并搭建项目全局安装 Vue 的命令行工具&#xff08;Vue CLI&#xff09;验证安装是否成功创建新vue项目 启动vue项目进入项目根目录安装依赖启动项目 配置开发ide (vscode)安装vscode配置vue插件vue2与vue3兼容性插件js插…

Linux之时间子系统(四): tick 层模块(broadcast )

一、前言 在内核中&#xff0c;有cpuidle framework可以控制cpu的节电&#xff1a;当没有进程调度到该cpu上执行的时候&#xff0c;swapper进程粉墨登场&#xff0c;将该cpu会被推入到idle状态。当然CPU的idle状态有深有浅&#xff0c;当CPU睡的比较深入的时候&#xff0c;有可…

【前端】-【性能优化常识】

目录 前端性能优化指标首屏速度、白屏时间性能优化收效很大的操作收效不大或者特殊情况的优化操作 操作速度、渲染速度造成操作卡顿和渲染慢的场景性能优化 数据缓存 前端性能优化指标 首屏速度、白屏时间 页面一打开的白屏时间&#xff0c;主要是由资源加载&#xff08;耗时多…

vue 隐藏导航栏和菜单栏,已解决

初始效果&#xff1a; 效果&#xff1a; 出现问题&#xff1a; 解决方法&#xff1a;

Linux——进程程序替换

替换原理 用fork创建子进程后执行的是和父进程相同的程序(但有可能执行不同的代码分支),子进程往往要调用一种exec函数 以执行另一个程序。当进程调用一种exec函数时,该进程的用户空间代码和数据完全被新程序替换,从新程序的启动 例程开始执行。调用exec并不创建新进程,所以调用…

JS加密解密之应用如何保存到桌面书签

前言 事情起因是这样的&#xff0c;有个客户解密了一个js&#xff0c;然后又看不懂里边的一些逻辑&#xff0c;想知道它是如何自动拉起谷歌浏览器和如何保存应用到书签的&#xff0c;以及如何下载应用的。继而诞生了这篇文章&#xff0c;讲解一下他的基本原理。 渐进式Web应用…

C++动态内存管理:new/delete与malloc/free的对比

在C中&#xff0c;动态内存管理是一个至关重要的概念。它允许我们在程序运行时根据需要动态地分配和释放内存&#xff0c;为对象创建和销毁提供了灵活性。在C中&#xff0c;我们通常会用到两对工具&#xff1a;new/delete 和 malloc/free。虽然它们都能够完成类似的任务&#x…

人工心脏术后两个月,他给父母做了一顿饭丨心脏病专家联合访谈

假如人生即将走到尽头&#xff0c;你最后的愿望会是什么&#xff1f; 不同的人会有不同的答案。 对于陈华&#xff08;化名&#xff09;来说&#xff0c;他的愿望是亲手为父母做顿饭。 罹患心脏病多年&#xff0c;陈华的病情反反复复逐渐发展为终末期心衰。虽然自己与父母家仅…

PMP考试费用涨价了?或将涨至4100元!

现在国内线下笔试的PMP报名费是3900元&#xff0c;但最近听到消息&#xff0c;说国外的PMP报名费用已经确认上调&#xff08;从2024年3月1日开始调整&#xff09;&#xff0c;由原本的555美元上调至575美元&#xff0c;根据h率换算&#xff0c;575美元≈4100元。既然国外的报名…

公司调研 | 空间机械臂GITAI | 日企迁美

最近做的一些公司 / 产品调研没有从技术角度出发&#xff0c;而更关注宏观发展&#xff1a;主营方向、产品介绍、商业化落地情况、融资历程、公司愿景、创始人背景等。部分调研放在知乎上&#xff0c;大部分在飞书私人链接上 最近较关注人形Robot的发展情况&#xff0c;欢迎感兴…

SCI一区 | Matlab实现PSO-TCN-BiGRU-Attention粒子群算法优化时间卷积双向门控循环单元融合注意力机制多变量时间序列预测

SCI一区 | Matlab实现PSO-TCN-BiGRU-Attention粒子群算法优化时间卷积双向门控循环单元融合注意力机制多变量时间序列预测 目录 SCI一区 | Matlab实现PSO-TCN-BiGRU-Attention粒子群算法优化时间卷积双向门控循环单元融合注意力机制多变量时间序列预测预测效果基本介绍模型描述…

Capture One Pro 23中文---颠覆性的图像编辑与色彩配置

Capture One Pro 23是一款功能强大且专业的RAW图像编辑处理软件。它拥有全球领先的色彩管理技术和精细的图像编辑工具&#xff0c;可以对图片进行多种精细调整&#xff0c;包括曝光、色温、对比度、锐度等&#xff0c;以满足用户特定的后期处理需求。此外&#xff0c;Capture O…

DNS 服务 Unbound 部署最佳实践

文章目录 安装unbound-control配置启动服务测试 参考&#xff1a; 官网地址&#xff1a;https://nlnetlabs.nl/projects/unbound/about/ 详细文档&#xff1a;https://unbound.docs.nlnetlabs.nl/en/latest/index.html DNS服务Unbound部署于使用 https://cloud.tencent.com/…

Filter、Listener、AJAX

Filter 概念&#xff1a;Filter 表示过滤器&#xff0c;是JavaWeb三大组件(Servlet、Filter、 Listener)之一。 过滤器可以把对资源的请求拦截下来&#xff0c;从而实现一些特殊的功能。 过滤器一般完成一些通用的操作&#xff0c;比如&#xff1a;权限控制、统一编码处理、敏感…

CentOS使用Docker部署Halo并结合内网穿透实现公网访问本地博客

文章目录 1. Docker部署Halo1.1 检查Docker版本如果未安装Docker可参考已安装Docker步骤&#xff1a;1.2 在Docker中部署Halo 2. Linux安装Cpolar2.1 打开服务器防火墙2.2 安装cpolar内网穿透 3. 配置Halo个人博客公网地址4. 固定Halo公网地址 本文主要介绍如何在CentOS 7系统使…

Nextcloud激活被锁用户

Nextcloud激活用户 如果docker下没有安装sudo 和 vim执行下面命令&#xff0c;安装了则跳过 #进入docker内部 #更新apt-get apt-get update #安装sudo apt-get install sudo #安装vim apt-get install vim 修改下面文件内容&#xff0c;否则执行occ命令可能报错 进入上面查询…

连接数据库(MySQL)的JDBC

目录 JDBC简介快速入门API详解DriverManager&#xff08;驱动管理类&#xff09;注册驱动&#xff1a;获取数据库连接(对象)&#xff1a; Connection&#xff08;数据库连接对象&#xff09;获取执行SQL的对象管理事务 Statement(执行SQL语句)执行DML、DDL语句执行DQL语句 Resu…

轨迹预测后处理之非极大值抑制(NMS)

非极大值抑制是图像处理里面的一种算法&#xff08;比如边缘检测会使用到&#xff09; 轨迹预测这里借鉴了其思想&#xff0c;比如说对于某个场景中的某辆车&#xff0c;我们使用模型预测 64 条轨迹或者更多&#xff0c;以很好地捕获多模态性&#xff0c;同时每条轨迹对应一个…

JavaScript 基础、内置对象、BOM 和 DOM 常用英文单词总结

一提到编程、软件、代码。对于英语不是很熟悉的同学望而却步。其实没有想像中的难么难&#xff0c;反复练习加上自己的思考、总结&#xff0c;会形成肌肉记忆。整理一下&#xff0c;初学者每天30遍。 1、JavaScript 基础语法 break&#xff1a;中断循环或 switch 语句的执行。…

Gif动图怎么快速制作?两招教你在线做

Gif动图作为一种实用的图片格式&#xff0c;因为其体积小&#xff0c;画面丰富&#xff0c;所以在各大聊天软件中非常的受欢迎。小伙伴们是不是很好奇这种gif动态图片是如何制作的吧&#xff01;下面&#xff0c;小编就给大家分享两个快速制作gif动画的小技巧&#xff01;不用下…