一,前言
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
四,遇到的问题
一开始不清楚pwm设备如何注册。pwm的linux驱动框架应该如何写。
因为芯片厂商已经为soc写了pwm驱动,而我其实不需要再写了,那么app如何通过open,read,write使用pwm呢?pwm中是config,enable等pwm_ops结构体,不是file_ops,后来想到的办法是索性用misc框架来注册驱动进行pwm打包。然后调用底层的pwm.h中的函数。一开始不清楚应用程序如何打开设备。
网上搜索了下按如下也可以用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
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
/**********************************************************************************
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
#include
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");