Linux pwm (设备驱动)

2 篇文章 0 订阅

/*
 * drivers/pwm/pwm-sunxi-dev.c
 *
 * Allwinnertech pulse-width-modulation controller driver
 *
 * Copyright (C) 2018 AllWinner
 *
 *
 * This file is licensed under the terms of the GNU General Public
 * License version 2. This program is licensed "as is" without any
 * warranty of any kind, whether express or implied.
 */
#include <linux/module.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/uaccess.h>
#include <linux/slab.h>
#include <linux/clk.h>
#include <linux/gpio.h>
#include <linux/device.h>
#include <linux/pinctrl/consumer.h>
#include <asm/io.h>
#include <linux/pwm.h>

static u32 debug_mask;
#define dprintk(level_mask, fmt, arg...)                \
do {                                    \
    if (unlikely(debug_mask & level_mask))                \
        pr_warn("%s()%d - "fmt, __func__, __LINE__, ##arg);    \
} while (0)

#define IRTX_ERR(fmt, arg...) pr_err("%s()%d - "fmt, __func__, __LINE__, ##arg)

#define PWM_IOCTL_BASE 'P'
#define PWM_CONFIG    _IOW(PWM_IOCTL_BASE, 1 , struct pwm_config)
#define PWM_ENABLE    _IOW(PWM_IOCTL_BASE, 2 , int)
#define PWM_DISABLE   _IOW(PWM_IOCTL_BASE, 3 , int)
#define GROUP_PWM_CONFIG    _IOW(PWM_IOCTL_BASE, 4, struct pwm_config)

struct pwm_config {
    int pwm_id;        //id,0对应pwm0,类推
    int duty_ns;       //占空比时间,单位ns
    int period_ns;     //周期时间,单位ns
    int pwm_polarity;  // 0表示 PWM_POLARITY_NORMAL, 1表示PWM_POLARITY_INVERSED
    
    int group_channel;      //组使用 0表示不使用,1表示使用第零组,2表示使用第一组
    int group_run_count;    //组脉冲数
};
/*
struct group_pwm_config {
    int pwm_id;             //id, 组中用到的一个pwm    
    int group_duty_ns;        //组占空比
    int group_period_ns;    //组周期
    int group_polarity;     //组极性 0表示normal, 1表示inversed
    int group_run_count;    //组脉冲数
    int group_channel;         // 组使用 0表示不使用,1表示使用第零组,2表示使用第一组    
};
*/
struct sunxi_pwm_group {
    unsigned int group0_used;
    unsigned int group0_mask;
    
    unsigned int group1_used;
    unsigned int group1_mask;
};

struct sunxi_pwm_chip {
    struct pwm_chip chip;
    void __iomem *base;
    struct sunxi_pwm_config *config;
    struct sunxi_pwm_group group;
#if defined(CLK_GATE_SUPPORT)
    struct clk *pwm_clk;
#endif
    unsigned int g_channel;
};

struct sunxi_pwm_dev {
    struct device *dev;
    struct cdev cdev;
    dev_t chrdev;
};

static struct sunxi_pwm_dev    *sunxi_pwm_dev;
static struct class            *sunxi_pwm_class;

static int sunxi_pwm_open(struct inode *inode, struct file *filp)
{
    filp->private_data = sunxi_pwm_dev; 
    return 0;
}

static int sunxi_pwm_release(struct inode *inode, struct file *filp)
{
    return 0;
}

static long  sunxi_pwm_unlocked_ioctl(struct file *filp, unsigned int cmd,
        unsigned long arg)
{
    unsigned int size;
    struct pwm_config *code;
    struct group_pwm_config *group_config;
    int ret, i;

    static struct pwm_device *pwm;
    switch (cmd) {
    case PWM_CONFIG:
        size = _IOC_SIZE(cmd);
        code = (struct pwm_config *)kzalloc(size, GFP_KERNEL);
        if(IS_ERR_OR_NULL(code)){
            IRTX_ERR("not enough memory\n");
            return -ENOMEM;
        }

        if(copy_from_user(code, (void __user *)arg, size)){
            IRTX_ERR("copy buffer err\n");
            return -ENOMEM;
        }

        pwm = pwm_request(code->pwm_id, "sunxi_pwm0");
        if(IS_ERR(pwm)){
            IRTX_ERR("pwm err\n");
            return -ENOMEM;
        }else
            IRTX_ERR("pwm success\n");
        
//        pwm->chip_data = (struct pwm_config *)kzalloc(size, GFP_KERNEL);
        pwm->chip_data = code;
        
        ret = pwm_config(pwm, code->duty_ns, code->period_ns);
        if(ret < 0){
            IRTX_ERR("pwm ioctl err\n");
            return -ENOMEM;
        }

        pwm_set_polarity(pwm,code->pwm_polarity);
        
        kfree(code);
        break;
    case GROUP_PWM_CONFIG:
        size = _IOC_SIZE(cmd);
//        group_config = (struct group_pwm_config *)kzalloc(size, GFP_KERNEL);
        code = (struct pwm_config *)kzalloc(size, GFP_KERNEL);
        if(IS_ERR_OR_NULL(code)){
            IRTX_ERR("not enough memory\n");
            return -ENOMEM;
        }

        if(copy_from_user(code, (void __user *)arg, size)){
            IRTX_ERR("copy buffer err\n");
            return -ENOMEM;
        }

        pwm = pwm_request(0, "sunxi_group0");  //是否能重复request同一个id
        if(IS_ERR(pwm)){
            IRTX_ERR("pwm err\n");
            return -ENOMEM;
        }else
            IRTX_ERR("pwm success\n");
        
//        pwm->chip_data = (struct pwm_config *)kzalloc(size, GFP_KERNEL);
        pwm->chip_data = code;
        
//struct  pwm_chip *chip = to_sunxi_pwm_chip(pwm);
//struct  sunxi_pwm_chip *pc = to_sunxi_pwm_chip(chip);
struct  pwm_chip *chip = container_of(pwm, struct pwm_chip, pwms);    
struct    sunxi_pwm_chip *pc = container_of(chip, struct sunxi_pwm_chip, chip);
    
int group0_mask = pc->group.group0_mask;
        
        for (i = 0; i < 10; i ++) {
            if (group0_mask &= 1 << i) {
                pwm = pwm_request(i, "sunxi_group0");  //是否能重复request同一个id
                if(IS_ERR(pwm)){
                    IRTX_ERR("pwm err\n");
                    return -ENOMEM;
                }else
                    IRTX_ERR("pwm success\n");
                
                ret = pwm_config(pwm, code->duty_ns, code->period_ns);
                if(ret < 0){
                    IRTX_ERR("pwm ioctl err\n");
                    return -ENOMEM;
                }
            }
        }
        
        pwm_set_polarity(pwm,code->pwm_polarity);
        
        kfree(code);
        break;
    case PWM_ENABLE:
        pwm_enable(pwm);
        break;
    case PWM_DISABLE:
        pwm_disable(pwm);
        break;
    default:
        IRTX_ERR("a err cmd");
        return -ENOTTY;
    }
    return 0;
}

static long  sunxi_pwm_compat_ioctl(struct file *filp, unsigned int cmd,
        unsigned long arg)
{
    unsigned long translated_arg = (unsigned long)compat_ptr(arg);

    return sunxi_pwm_unlocked_ioctl(filp, cmd, translated_arg);
}

static const struct file_operations sunxi_pwm_fops = {
    .owner        = THIS_MODULE,
    .unlocked_ioctl    = sunxi_pwm_unlocked_ioctl,
    .compat_ioctl   = sunxi_pwm_compat_ioctl,
    .open        = sunxi_pwm_open,
    .release    = sunxi_pwm_release,
};

static int __init sunxi_pwm_init(void)
{
    int err = 0;
    struct device *dev;

    sunxi_pwm_dev= kzalloc(sizeof(struct sunxi_pwm_dev), GFP_KERNEL);
    if (sunxi_pwm_dev == NULL) {
        IRTX_ERR("kzalloc failed!\n");
        return -ENOMEM;
    }

    err = alloc_chrdev_region(&sunxi_pwm_dev->chrdev, 0, 1, "sunxi-pwm-dev");

    if (err) {
        IRTX_ERR("alloc_chrdev_region failed!\n");
        goto alloc_chrdev_err;
    }

    cdev_init(&(sunxi_pwm_dev->cdev), &sunxi_pwm_fops);
    sunxi_pwm_dev->cdev.owner = THIS_MODULE;
    err = cdev_add(&(sunxi_pwm_dev->cdev), sunxi_pwm_dev->chrdev, 1);
    if (err) {
        IRTX_ERR("cdev_add failed!\n");
        goto cdev_add_err;
    }

    sunxi_pwm_class = class_create(THIS_MODULE, "sunxi_pwm_char_class");
    if (IS_ERR(sunxi_pwm_class)) {
        err = PTR_ERR(sunxi_pwm_class);
        IRTX_ERR("class_create failed!\n");
        goto class_err;
    }

    dev = device_create(sunxi_pwm_class, NULL, sunxi_pwm_dev->chrdev, NULL,
            "sunxi_pwm%d", 0);
    if (IS_ERR(dev)) {
        err = PTR_ERR(dev);
        IRTX_ERR("device_create failed!\n");
        goto device_err;
    }

    return 0;

device_err:
    device_destroy(sunxi_pwm_class, sunxi_pwm_dev->chrdev);
class_err:
    cdev_del(&(sunxi_pwm_dev->cdev));
cdev_add_err:
    unregister_chrdev_region(sunxi_pwm_dev->chrdev, 1);
alloc_chrdev_err:
    kfree(sunxi_pwm_dev);

    return err;
}

static void __exit sunxi_pwm_exit(void)
{
    cdev_del(&(sunxi_pwm_dev->cdev));
    unregister_chrdev_region(sunxi_pwm_dev->chrdev, 1);
    device_destroy(sunxi_pwm_class, sunxi_pwm_dev->chrdev);
    class_destroy(sunxi_pwm_class);
    kfree(sunxi_pwm_dev);
}


module_init(sunxi_pwm_init);
module_exit(sunxi_pwm_exit);
MODULE_AUTHOR("Li huaxing");
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("SUNXI_PWM char");

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 当使用Linux时,PWM驱动程序是一种非常常见的驱动程序类型,用于控制数字信号,如LED的亮度,风扇的速度等等。 以下是一些在Linux中使用PWM驱动程序的步骤: 1. 确认您的系统支持PWM驱动程序。要检查此功能,请执行以下操作: $ ls /sys/class/pwm 如果输出结果为“pwmchip0”,则您的系统支持PWM驱动程序。 2. 选择一个PWM引脚。要选择PWM引脚,请执行以下操作: $ ls /sys/class/pwm/pwmchip0 这将显示您的系统中可用的所有PWM通道。选择一个通道,并记下其索引号。 3. 设置PWM参数。要设置PWM参数,请执行以下操作: $ cd /sys/class/pwm/pwmchip0/pwm0 $ echo 1000000 > period $ echo 500000 > duty_cycle $ echo 1 > enable 这将设置PWM周期为1秒(1000000微秒),占空比为50%(500000微秒)并启用PWM信号。 4. 您还可以使用PWM驱动程序的API来编写自己的PWM控制程序。这些API包括pwm_request,pwm_config和pwm_enable等。 这些API可用于从用户空间直接控制PWM驱动程序,以实现更高级的PWM控制功能。 希望这些步骤对您有所帮助! ### 回答2: PWM(Pulse Width Modulation)顾名思义是一种脉冲宽度调制技术,在电子工程领域中广泛应用于数字信号处理中。PWM技术可以通过改变脉冲信号的脉冲宽度,来控制电路中的电压、电流或功率大小。PWM控制技术在电机调速、LED亮度调节、UPS、DC-DC变换器等各种电子设备中均有较为广泛的应用。 在linux内核中,PWM是通过相应的PWM驱动程序来控制的。PWM驱动程序所做的工作主要包括以下几个方面: 第一,初始化PWM。在系统启动过程中,通过相应的平台设备驱动程序,向内核注册PWM设备,设置相应的参数,包括PWM的周期和脉冲宽度等信息。 第二,控制PWM输出。PWM输出可以通过向PWM设备文件写入相应的数值来完成。输出数值由占空比和周期组成,通过改变参数可以控制PWM输出的占空比和频率。 第三,提供用户接口。PWM驱动程序同时还提供相应的用户接口文件,用户可以通过读写PWM设备对应的文件来实现对PWM的控制。例如,在/sys/class/pwm目录下,可以找到相应的PWM设备子目录,通过读写相应的文件即可对PWM进行控制。 第四,支持中断处理。在PWM输出过程中,一般需要在到达周期末尾时触发中断,进行相应的处理。PWM驱动程序也需要对中断处理进行支持。 总之,PWM技术在现代电子领域中应用非常广泛,linux PWM驱动程序也是相应的关键组成部分之一。通过PWM驱动程序的实现,我们可以轻松地控制LED亮度、电机转速等各种应用场景。而PWM驱动程序在日后的不断发展和完善中,也将不断地被改进和优化,为现代电子技术的不断进步提供更为稳定可靠的支持。 ### 回答3: PWM(Pulse Width Modulation)或脉宽调制是一种控制电子器件的技术,通过调整脉冲的宽度和周期来达到控制电子设备的功率、速度和亮度的目的。Linux PWM驱动则是一种跨平台的PWM控制器,使用Linux的内核从驱动器中执行PWM协议操作。 Linux PWM驱动是基于Linux系统内核提供的高级驱动程序接口(API)实现对PWM设备的控制和管理。Linux PWM驱动使得PWM设备的编程接口更加简单和便捷,可以自由配置PWM信号的频率、占空比和周期等参数以控制输出信号。 Linux PWM驱动通常被用于控制一些设备,比如LED、电机、声音发生器和传感器等等。例如,我们可以通过PWM控制器驱动LED的亮度和颜色,也可以通过PWM控制器驱动电机来调节速度和方向。 要在Linux中使用PWM驱动程序,需要安装相应的内核模块并配置设备驱动程序参数。LinuxPWM API接口包括PWM子系统,该子系统提供了PWM设备的管理、底层硬件驱动和访问函数等。此外,Linux设备树(Device Tree)也提供一种类似于BIOS的抽象层,用于描述硬件平台的配置,包括PWM控制器等设备的物理和逻辑地址。 总之,Linux PWM驱动是一种实现PWM控制的程序接口,可用于管理和控制PWM设备。通过LinuxPWM API和设备树,开发人员可以轻松地访问硬件PWM信号并实现有用的控制功能。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值