前言

  • 内核版本 linux5_10;
  • GPIO产生PWM方波,控制风扇转速;
  • 本驱动可在应用层 /sys/class/pwm/下生成文件接口,通过脚本进行配置;
  • 本驱动可将该 PWM 设备树节点用于 thermal 节点中,通过与传感器关联,根据温度自动控制风扇转速;

设备树

 pwm1:gpio-pwms {compatible = "gpio-pwms";pinctrl-names = "default";#pwm-cells = <3>;status = "okay";pwm1 {label = "pwm1";gpios = <&gpio4 2 0>;};};

驱动源码

/*** pwm_gpio.c create by gp
*/
#include <linux/clk.h>
#include <linux/err.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/init.h>
#include <linux/platform_device.h>
#include <linux/pwm.h>
#include <linux/device.h>
#include <linux/gpio.h>
#include <linux/delay.h>
#include <linux/of.h>
#include <linux/of_platform.h>
#include <linux/of_gpio.h>
#include <linux/of_device.h>#define _DEBUG_//pwm的设备对象
struct gpio_pwm_chip
{// 与 PWM 架构关联 struct pwm_chip chip;// 互斥锁struct mutex lock;// 高精度定时器struct hrtimer mytimer;// 时间变量ktime_t kt;// 周期记录u64 period;// 占空比记录u64 duty_cycle;// GPIO名字字符串const char *desc;// GPIO 号int gpio;int active_low;
};struct gpio_pwms_platform_data
{int npwms;struct gpio_pwm_chip pwms[0];
};static struct gpio_pwm_chip *gloabl_pwms_dev = NULL;
static struct gpio_pwms_platform_data *pdata = NULL;
static enum hrtimer_restart    hrtimer_handler(struct hrtimer *timer);static inline struct gpio_pwm_chip *to_gpio_chip(struct pwm_chip *chip)
{return container_of(chip, struct gpio_pwm_chip, chip);
}static inline struct gpio_pwm_chip *timer_to_gpio_chip(struct hrtimer *timer)
{return container_of(timer, struct gpio_pwm_chip, mytimer);
}
// 应用层,导出PWM的时候会调用这个函数,一般为硬件初始化,GPIO 不做操作
static int gpio_pwm_request(struct pwm_chip *chip, struct pwm_device *pwm)
{#ifdef _DEBUG_printk("%s %d Call\n", __func__, __LINE__);#endifreturn 0;
}
// 核心层回调函数,不做操作
static void gpio_pwm_free(struct pwm_chip *chip, struct pwm_device *pwm)
{#ifdef _DEBUG_printk("%s %d Call\n", __func__, __LINE__);#endif
}// 主要函数, 配置应用,应用层对PWM文件操作,enable 写 1 的时候调用
static int gpio_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm,const struct pwm_state *newstate)
{struct gpio_pwm_chip *gpwm = to_gpio_chip(chip);struct pwm_state *oldstate = &pwm->state;int ret = 0;#ifdef _DEBUG_printk("%s %d Call, old->enabled %d new->enabled %d period %lld duty %lld\n", __func__, __LINE__, oldstate->enabled, newstate->enabled,newstate->period, newstate->duty_cycle);#endif// 加锁互斥mutex_lock(&gpwm->lock);if (!newstate->enabled) {if (oldstate->enabled) {// 如果状态是从打开到关闭, GPIO设置为低, 定时器关闭// 使能  - 》 失能 关闭产生,恢复默认hrtimer_cancel(&gpwm->mytimer);gpio_set_value(gpwm->gpio, 0);#ifdef _DEBUG_printk("%s %d cancel timer\n", __func__, __LINE__);#endif}goto end_mutex;}// 设置好周期,占空比gpwm->period = newstate->period;gpwm->duty_cycle = newstate->duty_cycle;if (!oldstate->enabled) {// 如果从关闭到打开// 失能 -》 使能// 初始化定时器hrtimer_init(&gpwm->mytimer,CLOCK_MONOTONIC,HRTIMER_MODE_REL);// 超时时间gpwm->mytimer.function = hrtimer_handler;// 超时时间为  gpwm->period - gpwm->duty_cyclegpwm->kt = ktime_set(0, gpwm->period - gpwm->duty_cycle);// 启动定时器hrtimer_start(&gpwm->mytimer,gpwm->kt,HRTIMER_MODE_REL);#ifdef _DEBUG_printk("%s %d Start timer, kt %lld\n", __func__, __LINE__, gpwm->kt);#endif}end_mutex:mutex_unlock(&gpwm->lock);return ret;
}static const struct pwm_ops gpio_pwm_ops = {.request = gpio_pwm_request,.free = gpio_pwm_free,.apply = gpio_pwm_apply,.owner = THIS_MODULE,
};// 定时器到时,回调函数
static enum hrtimer_restart hrtimer_handler(struct hrtimer *timer)
{struct gpio_pwm_chip *gpwm = timer_to_gpio_chip(timer);// printk("%s %d get gpwm 0x%lx\n", __func__, __LINE__, (unsigned long)gpwm);// 如果GPIO输出为低,则改为高if (gpio_get_value(gpwm->gpio) == 0){// There is no need to pull down when the duty cycle is 100%if (gpwm->duty_cycle != 0){// 一个周期内,高电平时间为 gpwm->duty_cycle, 剩下的时间为低电平gpio_set_value(gpwm->gpio, 1);gpwm->kt = ktime_set(0, gpwm->duty_cycle);}// timer overflow// 重新设置超时时间 kthrtimer_forward_now(&gpwm->mytimer, gpwm->kt);}else{// 如果GPIO输出为高,则改为低// There is no need to pull up when the duty cycle is 0if (gpwm->duty_cycle != gpwm->period){// 一个周期内,高电平时间为 gpwm->duty_cycle, 剩下的时间为低电平gpio_set_value(gpwm->gpio, 0);gpwm->kt = ktime_set(0, gpwm->period - gpwm->duty_cycle);}// timer overflow// 重新设置超时时间 kthrtimer_forward_now(&gpwm->mytimer, gpwm->kt);}return HRTIMER_RESTART;
}static struct gpio_pwms_platform_data * gpio_pwms_get_devtree_pdata(struct device *dev)
{struct device_node *node, *pp;struct gpio_pwms_platform_data *pdata;struct gpio_pwm_chip *pwm;int error;int npwms;int i = 0;node = dev->of_node;if (!node)return NULL;// 获取设备树中的子节点数量,此处为1npwms = of_get_child_count(node);if (npwms == 0)return NULL;// 申请结构体内存pdata = devm_kzalloc(dev, sizeof(pdata->npwms) + npwms * sizeof(struct gpio_pwm_chip), GFP_KERNEL);if (!pdata){error = -ENOMEM;goto err_out;}pdata->npwms = npwms;for_each_child_of_node(node, pp){enum of_gpio_flags flags;// 检查子节点是否还有 gpios 属性if (!of_find_property(pp, "gpios", NULL)){pdata->npwms--;printk( "Found pwm without gpios\n");continue;}pwm = &pdata->pwms[i++];// 获取子节点用到的GPIOpwm->gpio = of_get_gpio_flags(pp, 0, &flags);#ifdef _DEBUG_printk("pwm->gpio = %d,flags = %d",pwm->gpio,flags);#endifif (pwm->gpio < 0){error = pwm->gpio;if (error != -ENOENT){if (error != -EPROBE_DEFER)dev_err(dev,"Failed to get gpio flags, error: %d\n",error);return ERR_PTR(error);}}else{pwm->active_low = flags ;}// 获取标签pwm->desc = of_get_property(pp, "label", NULL);}if (pdata->npwms == 0){error = -EINVAL;goto err_out;}return pdata;err_out:return ERR_PTR(error);}static int gpio_pwm_probe(struct platform_device *pdev)
{struct device *dev = &pdev->dev;int error = 0, i;unsigned int gpio;struct gpio_pwm_chip *gpwm = NULL;pdata = pdev->dev.platform_data;if (!pdata){pdata = gpio_pwms_get_devtree_pdata(dev);if (IS_ERR(pdata))return PTR_ERR(pdata);if (!pdata){printk( "missing platform data\n");return -EINVAL;}}// 申请内存gloabl_pwms_dev = devm_kzalloc(dev, pdata->npwms * sizeof(struct gpio_pwm_chip), GFP_KERNEL);if (!gloabl_pwms_dev){printk("no memory for gloabl_pwms_dev data\n");return -ENOMEM;}// 设置全局变量memcpy(gloabl_pwms_dev, pdata->pwms, pdata->npwms * sizeof(struct gpio_pwm_chip));for (i = 0; i < pdata->npwms; i++){gpwm = &gloabl_pwms_dev[i];mutex_init(&gpwm->lock);gpwm->chip.dev = dev;gpio = gpwm->gpio;if(!gpio_is_valid(gpio))printk("debug:invalid gpio,gpio=0x%x\n", gpio);// 设置gpio 为输出,并根据active_low 设置高低电平,此处没什么用处,因为上面已经写死了error = gpio_direction_output(gpio, !((gpwm->active_low == OF_GPIO_ACTIVE_LOW) ? 0 : 1));if (error){printk("unable to set direction on gpio %u, err=%d\n",gpio, error);return error;}// 向系统申请goioerror = devm_gpio_request(dev, gpio,gpwm->desc);if (error){printk( "unable to request gpio %u, err=%d\n",gpio, error);return error;}#ifdef _DEBUG_else{printk("successed to request gpio\n");}#endif// 填充 pwm_chip 结构体gpwm->chip.ops = &gpio_pwm_ops;gpwm->chip.of_xlate = of_pwm_xlate_with_flags;gpwm->chip.of_pwm_n_cells = 3;// base = -1 由系统分配basegpwm->chip.base = -1;gpwm->chip.npwm = 1;// 添加,成功后会在 /sys/class/pwm 下生成 gpiochip 目录error = pwmchip_add(&gpwm->chip);if (error < 0) {dev_err(&pdev->dev, "failed to add PWM chip: %d\n", error);return error;}#ifdef _DEBUG_printk("%s %d New gpwm 0x%lx, gpio %d\n", __func__, __LINE__, (unsigned long)gpwm, gpio);#endif}platform_set_drvdata(pdev, gpwm);return error;
}static int gpio_pwm_remove(struct platform_device *pdev)
{struct gpio_pwm_chip *gpwm = NULL;int i;// 移除 pwmchipfor (i = 0; i < pdata->npwms; i++){gpwm = &gloabl_pwms_dev[i];pwmchip_remove(&gpwm->chip);#ifdef _DEBUG_printk("%s %d Remove gpwm 0x%lx\n", __func__, __LINE__, (unsigned long)gpwm);#endif}return 0;
}static struct of_device_id gpio_pwm_of_match[] =
{// 与设备树匹配名字{.compatible = "gpio-pwms"},{},
};MODULE_DEVICE_TABLE(of, gpio_pwm_of_match);static struct platform_driver gpio_pwm_driver =
{.probe = gpio_pwm_probe,.remove = gpio_pwm_remove,.driver = {.name = "gpio-pwms",.owner = THIS_MODULE,.of_match_table = of_match_ptr(gpio_pwm_of_match),}
};module_platform_driver(gpio_pwm_driver);MODULE_DESCRIPTION("Module For PWM GPIO");
MODULE_AUTHOR("GUO PENG, <engguopeng@buaa.edu.cn>");
MODULE_LICENSE("GPL");
/* end pwm_gpio.c */

GPIO模拟PWM驱动风扇Linux设备驱动相关推荐

  1. linux 设备驱动 ppt,linux设备驱动开发详解讲座ppt

    PPT内容 这是linux设备驱动开发详解讲座ppt下载,主要介绍了设备驱动简介:建立和运行模块:字符驱动:调试技术:并发和竞争:分配内存:硬件通讯:中断处理:块设备驱动,欢迎点击下载. 嵌入式Lin ...

  2. linux 两个驱动 竞争,Linux设备驱动第五章(并发和竞争)读书笔记(国外英文资料).doc...

    Linux设备驱动第五章(并发和竞争)读书笔记(国外英文资料) Linux设备驱动第五章(并发和竞争)读书笔记(国外英文资料) The fifth chapter is concurrency and ...

  3. linux 设备驱动总结,linux设备驱动归纳总结.doc

    linux设备驱动归纳总结 linux设备驱动归纳总结 内核:用于管理软硬件资源,并提供运行环境.如分配4G虚拟空间等. linux设备驱动:是连接硬件和内核之间的桥梁. linux系统按个人理解可按 ...

  4. linux 设备驱动总结,linux设备驱动归纳总结(三):3面向对象思想和lseek

    linux设备驱动归纳总结(三):3.设备驱动面向对象思想和lseek的实现 一.结构体struct file和struct inode 在之前写的函数,全部是定义了一些零散的全局变量.有没有办法整合 ...

  5. 【驱动】linux设备驱动·字符设备驱动开发

    Preface 前面对linux设备驱动的相应知识点进行了总结,现在进入实践阶段! <linux设备驱动入门篇>:http://infohacker.blog.51cto.com/6751 ...

  6. linux 设备驱动 ppt,LINUX设备驱动开发3.ppt

    <LINUX设备驱动开发3.ppt>由会员分享,可在线阅读,更多相关<LINUX设备驱动开发3.ppt(30页珍藏版)>请在人人文库网上搜索. 1.第三天课程,FLASH设备驱 ...

  7. linux设备驱动子系统,Linux设备驱动子系统终极弹 - USB

    0. 预备理论 1. USB Core 2. USB Hub 3. USB OTG 4. USB Host 5. USB Gadget 6. USB Mass Storage USB博大精深,不是一两 ...

  8. linux 内核驱动模型,linux设备驱动模型架构分析 一

    linux设备驱动模型架构分析 一 发布时间:2018-07-04 15:14, 浏览次数:584 , 标签: linux 概述 LDD3中说:"Linux内核需要一个对系统结构的一般性描述 ...

  9. linux 设备驱动 百度,Linux设备驱动之input子系统

    该楼层疑似违规已被系统折叠 隐藏此楼查看此楼 作者:武汉华嵌嵌入式培训中心 讲师 李家凯 对于输入类设备如键盘.鼠标.触摸屏之类的Linux驱动,内核提供input子系统,使得这类设备的处理变得非常便 ...

最新文章

  1. 如何定制一款12306抢票浏览器——启动“人”线程
  2. SO_REUSEADDR
  3. 在linux中查找运行程序句柄,如何查找我的进程在Linux中打开的文件句柄?
  4. php 解析 标记,如何使用PHP-simple-HTML DOM解析器获取标记的属性
  5. 【guava】GuavaCache缓存失效的时候做一些操作 RemovalListener
  6. java动画帧储存路径_Java实现帧动画的实例代码
  7. python类和对象实验报告_python类和对象
  8. 产品id 关联 分类id mysql_MySQL的多表联查
  9. 数据库mysql基本查询语句_数据库mysql基础查询语句实录
  10. android 地球仪源码,android OpenGL ES 地球仪绘制——球体绘制及纹理映射
  11. python列表append方法_Python列表append()方法
  12. 树莓派配合迅雷搭建远程下载机
  13. Qt安装包下载(Windows平台)
  14. wx.getUserProfile踩坑填坑大全,is not a function?fail can only be invoked by user TAP gesture?
  15. 那么问题来了:为什么苹果设计被黑出翔还能大卖呢?
  16. Google代码规范书写格式,告别丑陋代码
  17. GPG(GnuPG)的安装和使用
  18. springboot执行批量插入_springboot2.1.8+elasticsearch7.3.2(三),添加文档,批量添加文档...
  19. 抑菌洗手液做MSDS中英文报告详细说明
  20. mysql常用命令orderby_mysql常用命令小结

热门文章

  1. android判断是否是蓝牙耳机,Android 检测是否连接蓝牙耳机
  2. iMindMap11百度云|iMindMap11序列号激活教程
  3. 暗黑2服务器Bnetd 延迟,暗黑破坏神2加不了别人服务器
  4. RK3288 7.1韦根 26位发送
  5. 推荐系统1--Deepfm学习笔记
  6. windows下Git与GitHub的安装及建仓基本操作
  7. Android Radio Interface Layer
  8. 盐城北大青鸟“致最爱的你”感恩母亲节特别活动
  9. qt Qt Table Widget item 设置 选中 透明色
  10. 并发与多线程之线程安全篇