zoukankan      html  css  js  c++  java
  • [Linux] pwm设备驱动调试

    转载请注明出处:https://www.cnblogs.com/lialong1st/p/11436190.html

    CPU:RK3288

    系统:Linux

    客户需求是通过 pwm 控制激光的强弱,写驱动前,需要先了解几个相关的概念和相关函数

    概念:

    PWM:脉冲宽度调试(Pulse width modulation),最典型的应用是调节 LED 的亮度

    占空比:高电平或者低电平时间占一个周期时间的比例

    驱动中 pwm 相关函数:

    /*
     * 功能:申请pwm设备
     * pwm_id:pwm编号
     * label:自定义名称
     */
    struct pwm_device *pwm_request(int pwm_id, const char *label);
    
    /*
     * 功能:释放pwm设备
     * pwm:pwm申请到的设备
     */
    void pwm_free(struct pwm_device *pwm);
    
    /*
     * 功能:使能pwm
     * pwm:pwm申请到的设备
     */
    int pwm_enable(struct pwm_device *pwm);
    
    /*
     * 功能:失能pwm
     * pwm:pwm申请到的设备
     */
    void pwm_disable(struct pwm_device *pwm);
    
    /*
     * 功能:设置pwm周期
     * pwm:pwm申请到的设备
     * period:pwm周期时间,单位为ns
     */
    void pwm_set_period(struct pwm_device *pwm, unsigned int period);
    
    /*
     * 功能:获取pwm周期
     * pwm:pwm申请到的设备
     * 返回值:pwm周期时间,单位为ns
     */
    unsigned int pwm_get_period(struct pwm_device *pwm);
    
    /*
     * 功能:设置pwm有效极性
     * pwm:pwm申请到的设备
     * polarity:pwm有效极性 PWM_POLARITY_NORMAL / PWM_POLARITY_INVERSED
     */
    int pwm_set_polarity(struct pwm_device *pwm, enum pwm_polarity polarity);
    
    /*
     * 功能:配置pwm
     * pwm:pwm申请到的设备
     * duty_ns:pwm占空比时间,单位为ns
     * period_ns:pwm周期时间,单位为ns
     */
    int pwm_config(struct pwm_device *pwm, int duty_ns, int period_ns);

    根据需求,底层给用户提供四个功能接口,包括设置周期时间、获取当前周期时间、设置占空比、获取当前占空比

    1、在 dts 中配置 pwm 默认参数

    pwm-laser {
        compatible = "pwm-laser";
        status = "okay";
        /* pwm1 */
        pwm-id = <1>;
        /* pwm1周期为1ms */
        period = <1000000>;
        /* 占空比默认为0 */
        default-level = <0>;
    };
    
    &pwm1 {
        status = "okay";
    };

    2、编写 pwm 驱动,按照需求为用户提供接口

    #include <linux/gpio.h>
    #include <linux/of_gpio.h>
    #include <linux/module.h>
    #include <linux/kernel.h>
    #include <linux/init.h>
    #include <linux/uaccess.h>
    #include <linux/fs.h>
    #include <linux/platform_device.h>
    #include <linux/fb.h>
    #include <linux/err.h>
    #include <linux/pwm.h>
    #include <linux/slab.h>
    #include <linux/miscdevice.h>
    #include <linux/delay.h>
    
    #define PWM_LASER_SET_PERIOD        _IOW('A', 0x11, unsigned int)
    #define PWM_LASER_GET_PERIOD        _IOR('A', 0x12, unsigned int)
    #define PWM_LASER_SET_DUTY          _IOW('A', 0x13, unsigned int)
    #define PWM_LASER_GET_DUTY          _IOR('A', 0x14, unsigned int)
    #define PWM_LASER_ENABLE            _IO('A', 0x15)
    #define PWM_LASER_DISABLE           _IO('A', 0x16)
    
    struct pwm_para {
        int pwm_id;
        unsigned int period;
        unsigned int level;
        unsigned int duty;
        struct pwm_device *pwm;
    };
    
    struct pwm_para laser;
    
    static void pwm_laser_update(void)
    {
        laser.duty = laser.level * laser.period / 100;
    
        pwm_config(laser.pwm, laser.duty, laser.period);
    }
    
    static void pwm_laser_enable(void)
    {
        pwm_enable(laser.pwm);
    }
    
    static void pwm_laser_disable(void)
    {
        // 关闭pwm之前将占空比设置为0
        laser.level = 0;
        pwm_laser_update();
        pwm_disable(laser.pwm);
    }
    
    static void pwm_laser_set_period(unsigned int period)
    {
        laser.period = period * 1000000;
        
        pwm_set_period(laser.pwm, laser.period);
    }
    
    static unsigned int pwm_laser_get_period(void)
    {
        return (laser.period / 1000000);
    }
    
    static int pwm_laser_open(struct inode *inode, struct file *filp)
    {
        return 0;
    }
    
    static int pwm_laser_release(struct inode *inode, struct file *filp)
    {
        pwm_laser_disable();
        
        return 0;
    }
    
    static ssize_t pwm_laser_read(struct file *filp, char __user *buf, size_t len, loff_t *pos)
    { 
        return 0;
    }
    
    static ssize_t pwm_laser_write(struct file *filp, const char __user *buf, size_t len, loff_t *pos)
    {
        return 0;
    }
    
    static long pwm_laser_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
    {
        unsigned int period;
        unsigned int level;
        
        void __user *argp = (void __user *)arg;
        
        switch (cmd)
        {
            case PWM_LASER_SET_PERIOD:
                if (argp == NULL) {
                    printk("laser: invalid argument.");
                    return -EINVAL;
                }
    
                if (copy_from_user(&period, argp, sizeof(unsigned int))) {
                    printk("copy_from_user failed.");
                    return -EFAULT;
                }
                
                pwm_laser_set_period(period);
                break;
                
            case PWM_LASER_GET_PERIOD:
                period = pwm_laser_get_period();
            
                if (copy_to_user(argp, &period, sizeof(unsigned int))) {
                    printk("copy_to_user failed.");
                    return -EFAULT;
                }
                break;
                
            case PWM_LASER_SET_DUTY    :
                if (argp == NULL) {
                    printk("laser: invalid argument.");
                    return -EINVAL;
                }
    
                if (copy_from_user(&level, argp, sizeof(unsigned int))) {
                    printk("copy_from_user failed.");
                    return -EFAULT;
                }
    
                if ((level < 0) || (level > 100)) {
                    printk("laser: invalid argument.");
                    return -EINVAL;
                }
    
                laser.level = level;
                pwm_laser_update();
                break;
                
            case PWM_LASER_GET_DUTY    :
                if (copy_to_user(argp, &laser.level, sizeof(unsigned int))) {
                    printk("copy_to_user failed.");
                    return -EFAULT;
                }
                break;
                
            case PWM_LASER_ENABLE:
                pwm_laser_update();
                pwm_laser_enable();
                break;
                
            case PWM_LASER_DISABLE:
                pwm_laser_disable();
                break;
                
            default:
                printk("laser: cmd error!
    ");
                return -EFAULT;
        }
        
        return 0;
    }
    
    struct file_operations pwm_laser_fops = {
        .owner = THIS_MODULE,
        .open = pwm_laser_open,
        .release = pwm_laser_release,
        .write = pwm_laser_write,
        .read = pwm_laser_read,
        .unlocked_ioctl = pwm_laser_ioctl,
    };
    
    struct miscdevice pwm_laser_dev =
    {  
        .minor  =   MISC_DYNAMIC_MINOR,
        .fops   =   &pwm_laser_fops,
        .name   =   "pwm-laser",
    };
    
    static ssize_t pwm_laser_parse_dt(struct platform_device *pdev)
    {
        struct device_node *node = pdev->dev.of_node;
        int ret;
    
        if (!node) {
            dev_err(&pdev->dev, "laser: Device Tree node missing
    ");
            return -EINVAL;
        }
        
        ret = of_property_read_u32(node, "pwm-id", &laser.pwm_id);
        if (ret < 0) {
            dev_err(&pdev->dev, "laser: pwm-id missing
    ");
            return ret;
        }
    
        ret = of_property_read_u32(node, "period", &laser.period);
        if (ret < 0) {
            dev_err(&pdev->dev, "laser: period missing
    ");
            return ret;
        }
        
        ret = of_property_read_u32(node, "default-level", &laser.level);
        if (ret < 0) {
            dev_err(&pdev->dev, "laser: default-level missing
    ");
            return ret;
        }
    
        return 0;
    }
    
    static void pwm_laser_test(void)
    {
        int i = 0, dir = 0;
        
        pwm_laser_enable();
        
        while (1)
        {
            if (dir == 0) {
                laser.level = i++;
                if (i == 100)
                    dir = 1;
            } else {
                laser.level = i--;
                if (i == 0)
                    dir = 0;
            }
    
            pwm_laser_update();
            msleep(100);
        }
        
        pwm_laser_disable();
    }
    
    static int pwm_laser_probe(struct platform_device *pdev)
    {
        int ret;
    
        ret = pwm_laser_parse_dt(pdev);
        if (ret < 0) {
            dev_err(&pdev->dev, "laser: Device not found
    ");
            return -ENODEV;
        }
        
        laser.pwm = pwm_request(laser.pwm_id, "pwm-laser");
        if (IS_ERR(laser.pwm)) {
            dev_err(&pdev->dev, "laser: unable to request legacy PWM
    ");
            ret = PTR_ERR(laser.pwm);
            return ret;
        }
    
        // 设置pwm参数
        pwm_laser_update();
    
        // 设置极性必须在设置参数之后,pwm使能之前,否则会设置失败
        ret = pwm_set_polarity(laser.pwm, PWM_POLARITY_NORMAL);
        if (ret < 0)
            printk("pwm set polarity fail, ret = %d
    ", ret);
    
        // 调试完屏蔽使能函数,交由app控制
    //  pwm_laser_enable();
    
        misc_register(&pwm_laser_dev);
    
        // 调试完屏蔽测试函数
    //  pwm_laser_test();
    
        return 0;
    }
    
    static int pwm_laser_remove(struct platform_device *pdev)
    {
        pwm_disable(laser.pwm);
        pwm_free(laser.pwm);
        
        misc_deregister(&pwm_laser_dev);
        
        return 0;
    }
    
    int pwm_laser_suspend(struct device *dev)
    {
        return 0;
    }
    
    int pwm_laser_resume(struct device *dev)
    {
        return 0;
    }
    
    const struct dev_pm_ops pwm_laser_pm_ops = {
        .suspend = pwm_laser_suspend,
        .resume = pwm_laser_resume,
    };
    
    static const struct of_device_id of_pwm_laser_match[] = {
        {.compatible = "pwm-laser", },
        {},
    };
    
    static struct platform_driver pwm_laser = {
        .probe = pwm_laser_probe,
        .remove = pwm_laser_remove,
        .driver = {
            .name = "pwm-laser",
            .owner = THIS_MODULE,
            .pm = &pwm_laser_pm_ops,
            .of_match_table = of_match_ptr(of_pwm_laser_match),
        },
    };
    
    module_platform_driver(pwm_laser);
    
    MODULE_DESCRIPTION("pwm control driver");
    MODULE_LICENSE("GPL");
    MODULE_AUTHOR("AaronLee");

    3、编写用户测试 demo,测试需求中的所有接口

    #include <stdio.h>
    #include <stdlib.h>
    #include <sys/stat.h>
    #include <sys/types.h>
    #include <sys/ioctl.h>
    #include <unistd.h>
    #include <fcntl.h>
    
    #define PWM_LASER_SET_PERIOD            _IOW('A', 0x11, unsigned int)
    #define PWM_LASER_GET_PERIOD            _IOR('A', 0x12, unsigned int)
    #define PWM_LASER_SET_DUTY              _IOW('A', 0x13, unsigned int)
    #define PWM_LASER_GET_DUTY              _IOR('A', 0x14, unsigned int)
    #define PWM_LASER_ENABLE                _IO('A', 0x15)
    #define PWM_LASER_DISABLE               _IO('A', 0x16)
    
    int main(void)
    {
        unsigned int i = 0;
        int dir = 0;
        int duty, period;
        int fd;
    
        fd = open("/dev/pwm-laser", O_RDWR);
        if (fd < 0)
        {
                perror("open pwm-laser fail");
                exit(1);
        }
        
        /* 获取pwm周期 */
        ioctl(fd, PWM_LASER_GET_PERIOD, &period);
        printf("pwm: period = %d
    ", period);
        
        /* 设置pwm周期为2ms */
        period = 2;
        ioctl(fd, PWM_LASER_SET_PERIOD, &period);
    
        /* 获取pwm占空比 */
        ioctl(fd, PWM_LASER_GET_DUTY, &duty);
        printf("pwm: duty = %d
    ", duty);
    
        /* 使能pwm */
        ioctl(fd, PWM_LASER_ENABLE);
    
        while(1)
        {
            /* 设置pwm占空比 */
            ioctl(fd, PWM_LASER_SET_DUTY, &i);
    
            if (dir == 0) {
                i++;
                if (i == 100)
                    dir = 1;
            } else {
                i--;
                if (i == 0)
                    dir = 0;
            }
    
            sleep(1);
        }
    
        /* 关闭pwm */
        ioctl(fd, PWM_LASER_DISABLE);
    
        close(fd);
    
        return 0;
    }
  • 相关阅读:
    HDU 3951 (博弈) Coin Game
    HDU 3863 (博弈) No Gambling
    HDU 3544 (不平等博弈) Alice's Game
    POJ 3225 (线段树 区间更新) Help with Intervals
    POJ 2528 (线段树 离散化) Mayor's posters
    POJ 3468 (线段树 区间增减) A Simple Problem with Integers
    HDU 1698 (线段树 区间更新) Just a Hook
    POJ (线段树) Who Gets the Most Candies?
    POJ 2828 (线段树 单点更新) Buy Tickets
    HDU 2795 (线段树 单点更新) Billboard
  • 原文地址:https://www.cnblogs.com/lialong1st/p/11436190.html
Copyright © 2011-2022 走看看