動手寫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");  

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