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;
    }
  • 相关阅读:
    李洪强IOS经典面试题 33-计算有多少个岛屿
    李洪强iOS经典面试题32-简单介绍 ARC 以及 ARC 实现的原理
    李洪强iOS经典面试题31-解释垃圾回收的原理
    iOS音频合并
    Macbook小问题
    weex-iOS集成
    WEEX快速入门
    Mac上Nginx-增加对HLS的支持
    iOS直播-基于RTMP的视频推送
    iOS直播-播放基于RTMP协议的视频
  • 原文地址:https://www.cnblogs.com/lialong1st/p/11436190.html
Copyright © 2011-2022 走看看