动手写linux下PWM驱动--Apple的学习笔记 一,前言 二,SG90原理 三,测试通过 四,遇到的问题 五,我的pwm驱动代码

一,前言

linux舵机硬件环境搭建成功--Apple的学习笔记昨天已经把SG90舵机的硬件环境搭建成功,并且通过命令行驱动验证通过。今天还是动手写驱动。PWM驱动比I2C还要简单,可以理解为不用写,只要写应用就可以了,但是我想让应用的人使用起来更简单,所以呢,按这样的思路,PWM设备驱动还是要写的。那么就要抽象一下,做做分工,应用主要输入角度即可控制舵机转动位置,其它的都是驱动来实现。APP和驱动为文件夹12,上传到了我的gitee

二,SG90原理

昨天不是验证过有PWM,但是舵机转下就停了,原因是PWM占空比不变就是这样的现象,我今天查看了下SG90的原理。参考网址:https://blog.csdn.net/qq_24037197/article/details/53464176
无负载速度:0.12秒/60度(4.8V) 0.002s/度
都说50Hz的时候线性度好。所以设置为20ms周期,但是SG90舵机有运行速度,所以要delay一段时间。2000us/度。
在一个特定的频率下,特定的占空比使得舵机会转到一个角度,占空比不变,则角度不会不会变化,所以想要舵机动,就要在国定的频率下不断改变占空比。

三,测试通过

我做了3个调试,一个是在0和45度来回转。一个从0开始是慢慢转到180度,角度越大越慢,还有一个是0-180度间delay时间设置为固定的10ms的就可以看到从0快速转到180度。命令比较简单如下。

Welcome to Buildroot
buildroot login: root
# cd /usr/study/
# insmod applePWM.ko 
[   16.795320] applePWM: loading out-of-tree module taints kernel.
[   16.802716] ApplePWM initilize ok
# ./pwm
^C

四,遇到的问题

  1. 一开始不清楚pwm设备如何注册。pwm的linux驱动框架应该如何写。
    因为芯片厂商已经为soc写了pwm驱动,而我其实不需要再写了,那么app如何通过open,read,write使用pwm呢?pwm中是config,enable等pwm_ops结构体,不是file_ops,后来想到的办法是索性用misc框架来注册驱动进行pwm打包。然后调用底层的pwm.h中的函数。

  2. 一开始不清楚应用程序如何打开设备。
    网上搜索了下按如下也可以用read,write操作。后来由于设计为了misc和platform其实类似,所以也可以用read,write,ioctrl了。

int pwm_enable()
{
    fd = open("/sys/class/pwm/pwmchip0/pwm0/enable", O_WRONLY);
    write(fd, "1", 0); // 为pwm0写值
    return 0;
}

五,我的pwm驱动代码

#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/init.h>
#include <linux/fs.h>  
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/timer.h>
#include <linux/uaccess.h>
#include <linux/cdev.h> 
#include <linux/pwm.h>
#include <linux/delay.h>
#include <linux/miscdevice.h>
/**********************************************************************************
Copyright (C), by AppleCai
Project                      : Study Kernel
Description                  : BSP level for cdev
History                      :
CPU and Compiler             : AM335x,ARM-LINIX-GCC
GenDate                      : 2020-11-16
OwnerBy                      : Apple Cai
**********************************************************************************/
#include <linux/pwm.h>
#include <linux/delay.h>

struct pwm_device *pwm;
struct semaphore sem;

#define PMW_START   1
#define PMW_STOP    2
/*
 * @description     : 打开设备
 * @param - inode   : 传递给驱动的inode
 * @param - filp    : 设备文件,file结构体有个叫做private_data的成员变量
 *                    一般在open的时候将private_data指向设备结构体。
 * @return          : 0 成功;其他 失败
 */
static int ApplePWM_open(struct inode *inode, struct file *filp)
{
    int ret = 0;
    if (!down_trylock(&sem)) //是否获得信号量,是 down_trylock(&lock)=0,否则非 0
    {
        pwm = pwm_request(0, "mypwmdev");
        if (NULL == pwm)
        {
            printk("no res\n");
            up(&sem); //释放信号量 lock
            return -ENODEV;
        }
        /*pwm_set_polarity(pwm, PWM_POLARITY_NORMAL);*/
        return ret;
    }
    else
    {
        printk("busy\n");
        return -EBUSY; //返回错误信息:请求的资源不可用
    }

}

/*设备控制函数:*/ 
static long ApplePWM_ioctl(struct file *filp, unsigned int cmd,unsigned long arg)
{  
    switch (cmd) {
        case PMW_START: //if cmd=1 即进入 case PWM_IOCTL_SET_FREQ
            if (arg < 0) //如果设置的频率参数是 0
                return -EINVAL; //返回错误信息,表示向参数传递了无效的参数
            pwm_config(pwm, arg, 20000000);
            pwm_enable(pwm);
            break;
        case PMW_STOP: // if cmd=2 即进入 case PWM_IOCTL_STOP
            pwm_disable(pwm); //停止蜂鸣器
            break;
    }
    return 0; //成功返回 
}  

/*
 * @description     : 关闭/释放设备
 * @param - filp    : 要关闭的设备文件(文件描述符)
 * @return          : 0 成功;其他 失败
 */
static int ApplePWM_release(struct inode *inode, struct file *filp)
{
    pwm_disable(pwm);
    pwm_free(pwm);
    up(&sem); //释放信号量 lock
    return 0;
}

/* AppleEEP操作函数 */
static const struct file_operations ApplePWM_ops = {
    .owner = THIS_MODULE,
    .open = ApplePWM_open,
    .release = ApplePWM_release,
    .unlocked_ioctl = ApplePWM_ioctl,
};

static struct miscdevice misc = {
    .minor = MISC_DYNAMIC_MINOR,
    .name = "ApplePWM",
    .fops = &ApplePWM_ops,
};
           
static int __init ApplePWM_init(void)
{
    int ret=0;
    sema_init(&sem, 1);
    ret = misc_register(&misc); //注册一个 misc 设备
    printk ("ApplePWM initilize ok\n");
    return ret;
}

static void __exit ApplePWM_exit(void)
{
     misc_deregister(&misc); //注销设备
}


module_init(ApplePWM_init);
module_exit(ApplePWM_exit);
MODULE_AUTHOR("AppleCai");  
MODULE_LICENSE("GPL");  

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章