一周搞定MPU6050Linux驱动(2)

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/coolliugang123/article/details/71319917
第2-3日
参考:
《Linux设备驱动程序》 第三版
有了前面的源码学习和分析,对i2c驱动有了大概的认识。那么接下来,我们就开始我们自己的mpu6500/6050驱动的编写。这里说一下,mpu6500和mpu6050在寄存器上基本没什么区别,只有version ID不同,6500的是0x70,6050是0x68。
之前分析了airk000的源码,发现他的驱动方式是在后台完成6050的数据定时读取。那么,我们所需要实现的驱动,是对mpu6050/6500进行中断读取。并且包含了字符设备驱动,用来在用户空间获取数据。
1、学习字符设备驱动,实现fifo
在建立一个字符设备之前,需要获得一个或多个设备编号。可以选择静态分配和动态分配,推荐动态分配。使用alloc_chrdev_region函数来进行设备号的动态分配。
字符驱动的三个内核数据结构file_operations,file,inode
file_operations定义操作方法,包括字符的open,wirte,ioctrl等。
file结构表示一个打开的文件
inode在内核表示一个文件实体,包含了大量的有关文件的信息。
可以参考这个博客的内容,写的很好 http://www.cnblogs.com/chen-farsight/p/6177870.html,在用户控件使用open函数打开一个inode,会有以下过程
  1. 在虚拟文件系统VFS中的查找对应与字符设备对应 struct inode节点
  2. 遍历字符设备列表(chardevs数组),根据inod节点中的 cdev_t设备号找到cdev对象
  3. 创建struct file对象(系统采用一个数组来管理一个进程中的多个被打开的设备,每个文件秒速符作为数组下标标识了一个设备对象)
  4. 初始化struct file对象,将 struct file对象中的 file_operations成员指向 struct cdev对象中的 file_operations成员(file->fops =  cdev->fops)
  5. 回调file->fops->open函数
我们可以看到我们必须实现几个结构中的方法,尤其是file_operations,然后实例化cdev,file结构体,并将他们建立联系。
那么一个字符设备文件的建立,需要以下几个步骤:
1、调用alloc_chrdev_region(&dev, myfifomajor,1,"myfifo");来动态分配设备号。
2、 cdev_init(&myfifo_dev->cdev,&myfifo_fops); 初始化cdev,一般cdev会定义在自己的数据结构中,本文中定义在Fifo_Dev中。
3、cdev_add(&myfifo_dev->cdev,devno,1);将cdev添加到内核中。
有了前三个步骤,加载模块的时候,会在/proc/device中出现myfifo。如果想在/dev中创建节点。那么还需要另外两个步骤:
4、class_create(THIS_MODULE, "myfifo"); 创建类目录
5、 device_create(cls,NULL,devno,NULL,"myfifo");创建device
执行完以上代码之后,即可完成设备的初始化。
初始化代码如下:
int myfifo_init_module(void)
{
int result, i,err;
dev_t dev,devno;

//×¢²ácdev£¬ »ñÈ¡É豸ºÅ£¬0´ú±í×Ô¶¯·ÖÅä
printk("hello fifo\n");
result = alloc_chrdev_region(&dev, myfifomajor,1,"myfifo");
if (result < 0) {
printk(KERN_WARNING "myfifo: can't get major %d\n",myfifomajor);
return result;
}
printk(KERN_DEBUG "ALLOC SUCCEED\n");

if(myfifomajor == 0) myfifomajor = MAJOR(dev);
//·ÖÅämyfifodev
myfifo_dev = kmalloc(sizeof(Fifo_Dev), GFP_KERNEL);
if (!myfifo_dev) {
result = -ENOMEM;
goto fail;
}
myfifo_dev->data = kmalloc(MYFIFOSIZE,GFP_KERNEL);
if (!myfifo_dev->data) {
result = -ENOMEM;
goto fail;
}
//memset(myfifo_dev, 0, sizeof(Fifo_Dev));
memset(myfifo_dev->data, 0, MYFIFOSIZE);
myfifo_dev->head = 0;
myfifo_dev->tail = 0;
sema_init(&myfifo_dev->sem,1);
// initial signal

devno = MKDEV(myfifomajor, 0);

cdev_init(&myfifo_dev->cdev,&myfifo_fops);

myfifo_dev->cdev.owner = THIS_MODULE;//ËùÊôÄ£¿é
myfifo_dev->cdev.ops = &myfifo_fops;

err = cdev_add(&myfifo_dev->cdev,devno,1);//×¢²áÉ豸,·µ»Ø0±íʾ³É¹¦,·Ç0±íʾʧ°Ü
printk(KERN_DEBUG "init SUCCEED\n");
if(err){
printk(KERN_NOTICE "Error %d adding fifo",err);
goto fail;
}

cls = class_create(THIS_MODULE, "myfifo");

if(IS_ERR(cls))
{
goto fail;
}
printk("class create.\n");

test_device = device_create(cls,NULL,devno,NULL,"myfifo");//mknod /dev/hello
if(IS_ERR(test_device))
{
class_destroy(cls);
goto fail;
}

printk(KERN_DEBUG "my fifo device alloc succeed!\n");
return 0; /* succeed */

fail:
myfifo_cleanup_module();
return result;
}
struct file_operations myfifo_fops = {
.owner = THIS_MODULE,
.read = myfifo_read,
.write = myfifo_write,
.open = myfifo_open,
.release = myfifo_release,
};结构体中的open,read,write,release实现好之后。按照《一周搞定MPU6500驱动(1)》中的方法进行编译。
通过filezila下载到开发板中,加载模块。
编写测试文件,功能是向myfifo设备中写20个数,再读出20个数,查看是否相同。如下:
#include <stdio.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

int main(int argc,char *argv[])
{
unsigned char buf[50];
int fd;
int i;

for(i=0; i<20; i++)
{
buf[i] = i;
}

fd = open("/dev/myfifo",O_RDWR);

if(fd<0)
{
printf("error!\n");
}

write(fd,buf,20);
read(fd,buf+20,20);
for(i=0; i<20; i++)
{
printf(" %d",*(buf+20+i));
}
return 1;
}

可以得到结果:


读出的数与写入的数相同,说明内核函数能够正常运行。

这里需要注意的是,运行程序需要以sudo来运行,因为设备驱动的权限是root。第一次执行的时候,我没有使用sudo,一直报错,设备无法打开,fd返回-1。这个要注意。

折腾了两个晚上,明天开始写mpu6050/6500的驱动。

有什么疑问或者交流的加我QQ475292178,请注明“交流探讨”













猜你喜欢

转载自blog.csdn.net/coolliugang123/article/details/71319917