一周搞定MPU6050Linux驱动(2)

xiaoxiao2021-02-28  75

第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,会有以下过程

在虚拟文件系统VFS中的查找对应与字符设备对应 struct inode节点遍历字符设备列表(chardevs数组),根据inod节点中的 cdev_t设备号找到cdev对象创建struct file对象(系统采用一个数组来管理一个进程中的多个被打开的设备,每个文件秒速符作为数组下标标识了一个设备对象)初始化struct file对象,将 struct file对象中的 file_operations成员指向 struct cdev对象中的 file_operations成员(file->fops =  cdev->fops)回调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的驱动。

 

 

 

 

 

 

 

 

 

 

 

 

 

转载请注明原文地址: https://www.6miu.com/read-75264.html

最新回复(0)