目录
- 1、在APP直接调用标准文件IO操作I2C(针对学习笔记-15的15.3节)
- 1.1 mail.c
- 1.2 mpu6050.h
- 1.3 mpu6050.c
- 1.4 Makefile
- 2、以外称id的方式进行匹配的i2c驱动
- 2.1 mpu6050.h
- 2.2 mpu6050_i2c_client.c
- 2.3 mpu6050_i2c_driver.c
- 2.4 read_mpu.c 测试的应用层APP
- 2.5 Makefile
- 3、以设备树方式进行匹配的i2c驱动
- 3.1 完善从设备节点
- 3.2 驱动代码实现
因为篇幅的原因,本文为【嵌入式环境下linux内核及驱动学习笔记-(15)linux总线、设备、驱动模型之I2C总线】的配套例程。
1、在APP直接调用标准文件IO操作I2C(针对学习笔记-15的15.3节)
1.1 mail.c
/*main.c文件*/#include <stdlib.h>
#include <string.h>#include "mpu6050.h"int main(int argc,char *argv[])
{int fd = -1;if(argc < 2){printf("Argument is too few\n");return 1;}/*open*/fd = open(argv[1],O_RDWR);if(fd < 0){printf("open %s failed\n",argv[1]);return 2;}/*init mpu6050*/init_mpu6050(fd);while(1){sleep(2);/*read and print data from 6050*/printf("Accel-X:0x%x\n",read_accelx(fd));printf("Accel-Y:0x%x\n",read_accely(fd));printf("Accel-Z:0x%x\n",read_accelz(fd));printf("Temp:0x%x\n",read_temp(fd));printf("GYRO-X:0x%x\n",read_gyrox(fd));printf("GYRO-Y:0x%x\n",read_gyroy(fd));printf("GYRO-z:0x%x\n",read_gyroz(fd));printf("\n");}/*close*/close(fd);fd = -1;return 0;
}
1.2 mpu6050.h
/*.h头文件*/
#ifndef MPU_6050_H
#define MPU_6050_H#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>int init_mpu6050(int fd);
int read_accelx(int fd);
int read_accely(int fd);
int read_accelz(int fd);
int read_temp(int fd);
int read_gyrox(int fd);
int read_gyroy(int fd);
int read_gyroz(int fd);#define SMPLRT_DIV 0x19
#define CONFIG 0x1A
#define GYRO_CONFIG 0x1B
#define ACCEL_CONFIG 0x1C#define ACCEL_XOUT_H 0x3B
#define ACCEL_XOUT_L 0x3C
#define ACCEL_YOUT_H 0x3D
#define ACCEL_YOUT_L 0x3E
#define ACCEL_ZOUT_H 0x3F
#define ACCEL_ZOUT_L 0x40
#define TEMP_OUT_H 0x41
#define TEMP_OUT_L 0x42
#define GYRO_XOUT_H 0x43
#define GYRO_XOUT_L 0x44
#define GYRO_YOUT_H 0x45
#define GYRO_YOUT_L 0x46
#define GYRO_ZOUT_H 0x47
#define GYRO_ZOUT_L 0x48#define PWR_MGMT_1 0x6B#define I2C_SLAVE 0x0703 /* Use this slave address */
#define I2C_TENBIT 0x0704 /* = 0 for 7 bit addrs, != 0 for 10 bit */
/*上面这两个宏的定义在内核原码目录下/include/uapi/linux/i2c/i2c-dev.h*/
#endif
1.3 mpu6050.c
#include <unistd.h>
#include <sys/ioctl.h>
#include <stdio.h>#include "mpu6050.h"static int read_data_from_mpu6050(int fd,unsigned char reg,unsigned char *pdata)
{int ret = 0;unsigned char buf[1] = {reg};ret = write(fd,buf,1);
/* 在用文件IO中write时,底层i2c_dev.c * 1会完成发送从设备地址及写标志,* 2,发送寄存器地址,* 3、发送从设备地址及读标志。而从设备地址已在init_mpu6050()中去通过ioctl()操作设置了。* */if(ret != 1){printf("write reg failed,in read_data_from_mpu6050\n");return -1;}buf[0] = 0;ret = read(fd,buf,1);if(ret != 1){printf("read data failed,in read_data_from_mpu6050\n");return -1;}*pdata = buf[0];return 0;
}static int write_data_to_mpu6050(int fd,unsigned char reg,unsigned char data)
{unsigned char buf[2] = {reg,data};int ret = 0;ret = write(fd,buf,2);if(ret != 2){printf("write data failed,in write_data_to_mpu6050\n");return -1;}return 0;
}int read_accelx(int fd)
{unsigned short val = 0;unsigned char d = 0;int ret = 0;ret = read_data_from_mpu6050(fd,ACCEL_XOUT_L,&d);val = d;ret = read_data_from_mpu6050(fd,ACCEL_XOUT_H,&d);val |= d << 8;if(ret < 0){printf("read accel x value failed,in read_accelx\n");return -1;}else{return val;}
}int read_accely(int fd)
{unsigned short val = 0;unsigned char d = 0;int ret = 0;ret = read_data_from_mpu6050(fd,ACCEL_YOUT_L,&d);val = d;ret = read_data_from_mpu6050(fd,ACCEL_YOUT_H,&d);val |= d << 8;if(ret < 0){printf("read accel y value failed,in read_accely\n");return -1;}else{return val;}
}int read_accelz(int fd)
{unsigned short val = 0;unsigned char d = 0;int ret = 0;ret = read_data_from_mpu6050(fd,ACCEL_ZOUT_L,&d);val = d;ret = read_data_from_mpu6050(fd,ACCEL_ZOUT_H,&d);val |= d << 8;if(ret < 0){printf("read accel z value failed,in read_accelz\n");return -1;}else{return val;}
}int read_temp(int fd)
{unsigned short val = 0;unsigned char d = 0;int ret = 0;ret = read_data_from_mpu6050(fd,TEMP_OUT_L,&d);val = d;ret = read_data_from_mpu6050(fd,TEMP_OUT_H,&d);val |= d << 8;if(ret < 0){printf("read temp value failed,in read_temp\n");return -1;}else{return val;}
}int read_gyrox(int fd)
{unsigned short val = 0;unsigned char d = 0;int ret = 0;ret = read_data_from_mpu6050(fd,GYRO_XOUT_L,&d);val = d;ret = read_data_from_mpu6050(fd,GYRO_XOUT_H,&d);val |= d << 8;if(ret < 0){printf("read gyro x value failed,in read_gyrox\n");return -1;}else{return val;}
}int read_gyroy(int fd)
{unsigned short val = 0;unsigned char d = 0;int ret = 0;ret = read_data_from_mpu6050(fd,GYRO_YOUT_L,&d);val = d;ret = read_data_from_mpu6050(fd,GYRO_YOUT_H,&d);val |= d << 8;if(ret < 0){printf("read gyro y value failed,in read_gyroy\n");return -1;}else{return val;}
}int read_gyroz(int fd)
{unsigned short val = 0;unsigned char d = 0;int ret = 0;ret = read_data_from_mpu6050(fd,GYRO_ZOUT_L,&d);val = d;ret = read_data_from_mpu6050(fd,GYRO_ZOUT_H,&d);val |= d << 8;if(ret < 0){printf("read gyro z value failed,in read_gyroz\n");return -1;}else{return val;}
}int init_mpu6050(int fd)
{int ret = 0;ret = ioctl(fd,I2C_TENBIT,0); //从设备地址为7位if(ret < 0){printf("ioctl I2C_TENBIT failed,in init_mpu6050\n");return -1;}ret = ioctl(fd,I2C_SLAVE,0x68); //设置从设备地址值if(ret < 0){printf("ioctl I2C_TENBIT failed,in init_mpu6050\n");return -1;}ret = write_data_to_mpu6050(fd,PWR_MGMT_1,0x00);ret += write_data_to_mpu6050(fd,SMPLRT_DIV,0x07);ret += write_data_to_mpu6050(fd,ACCEL_CONFIG,0x19);ret += write_data_to_mpu6050(fd,GYRO_CONFIG,0xF8);if(ret < 0){printf("write init data to mpu6050 failed,in init_mpu6050\n");return -1;}return 0;
}
1.4 Makefile
DIR_ARM := ~/linux/toolchain/gcc-4.6.4/bin/
GCC := arm-none-linux-gnueabi-gccDIR_86 := /usr/bin/OBJECT := mpu6050.o main.oifeq ($(ARCH),arm)CC := $(DIR_ARM)$(GCC) elseCC := $(DIR_86)gccendifall:$(OBJECT)$(CC) $(OBJECT) -o main.elf %.o:%.c$(CC) -c $< -Wall -o $@install:sudo cp *.elf /opt/4412/rootfs/work -rf clean:rm *.o -rf
2、以外称id的方式进行匹配的i2c驱动
2.1 mpu6050.h
/*************************************************************************> File Name: mpu6050.h> Author: maohm> Created Time: Thu 08 Jun 2023 03:52:31 PM CST************************************************************************/#ifndef _MPU6050_H
#define _MPU6050_H
/**存放从设备中读取的信息的数据结构体*/
struct accel_data
{unsigned short x;unsigned short y;unsigned short z;
};
struct gyro_data
{unsigned short x;unsigned short y;unsigned short z;
};union mpu6050_data
{struct accel_data accel;struct gyro_data gyro;unsigned short temp;
};
/** 以下宏构造ioctl函数的命令字*/
#define MPU6050_MAGIC 'K'#define GET_ACCEL _IOR(MPU6050_MAGIC,0,union mpu6050_data)
#define GET_GYRO _IOR(MPU6050_MAGIC,1,union mpu6050_data)
#define GET_TEMP _IOR(MPU6050_MAGIC,2,union mpu6050_data)#endif
2.2 mpu6050_i2c_client.c
/*mpu6050_i2c_client.c
* 在i2c框架中,client端是用创建i2c对象,并描述i2c设备参数的作用。
* 因此,构造的i2c设备是以struct i2c_client的数据结构型态存在的,
* 返过来说,就是i2c_client代表着一个i2c设备对象,与现实的i2c设备
* 一一对应。*/#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/i2c.h>#define ADAPTER_NUM 5 //i2c adapter 5号/*构造i2c设备的静态数据结构*/
static struct i2c_board_info mpu6050_info={I2C_BOARD_INFO("mpu6050-1",0x68) //确定i2c设备的名称和地址信息。名称可用于匹配驱动
};static struct i2c_client *gpmpu6050_client = NULL;static int __init mpu6050_client_init(void){struct i2c_adapter *padp = i2c_get_adapter(ADAPTER_NUM);/*构造i2c设备对象,把该设备挂接在5号adapter上*/gpmpu6050_client = i2c_new_device(padp , &mpu6050_info );i2c_put_adapter(padp); //马上把对adapter的引用减1return 0;}static void __exit mpu6050_client_exit(void){i2c_unregister_device(gpmpu6050_client);
}module_init(mpu6050_client_init);
module_exit(mpu6050_client_exit);
MODULE_LICENSE("GPL");
2.3 mpu6050_i2c_driver.c
/*mpu6050_i2c_driver.c
* 做为i2c外接设备的驱动程序,负责完成:
* 1、查找并匹配i2c设备对象,可以匹配多个i2c设备对象
* 2、实现常规的驱动设备号申请,inode与file_operations的对应,以及与内核数据结构的关联
* 3、创建设备文件节点
* 4、实现操作函数open read write ioctl等 */#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/i2c.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/wait.h>
#include <linux/sched.h>
#include <linux/poll.h>
#include <linux/slab.h>
#include <linux/mm.h>
#include <linux/io.h>
#include <asm/uaccess.h>
#include <asm/atomic.h>#include "mpu6050.h"/**以下为mpu6050中的各个寄存器地址标号*/#define SMPLRT_DIV 0x19 //采样率分配器配置寄存器
#define CONFIG 0x1A //配置FSYNC 和 DLPF 寄存器
#define GYRO_CONFIG 0x1B //配置陀螺仪的寄存器
#define ACCEL_CONFIG 0x1C //配置加速度计的寄存器#define ACCEL_XOUT_H 0x3B //X向加速度值的高8位
#define ACCEL_XOUT_L 0x3C //X向加速度值的低8位
#define ACCEL_YOUT_H 0x3D //Y向加速度值的高8位
#define ACCEL_YOUT_L 0x3E //Y向加速度值的低8位
#define ACCEL_ZOUT_H 0x3F //Z向加速度值的高8位
#define ACCEL_ZOUT_L 0x40 //Z向加速度值的低8位
#define TEMP_OUT_H 0x41 //温度的高8位
#define TEMP_OUT_L 0x42 //温度的氏8位
#define GYRO_XOUT_H 0x43 //x轴角速度的高8位
#define GYRO_XOUT_L 0x44 //x轴角速度的低8位
#define GYRO_YOUT_H 0x45 //Y轴角速度的高8位
#define GYRO_YOUT_L 0x46 //Y轴角速度的低8位
#define GYRO_ZOUT_H 0x47 //Z轴角速度的高8位
#define GYRO_ZOUT_L 0x48 //Z轴角速度的低8位#define PWR_MGMT_1 0x6B //电源管理寄存器#define MPU6050_NUM 1 //需要驱动的设备个数
struct class *dev_class=NULL ; //创建设备类
int major = 0; //采用自动分配的方式创建设备号,并自动创建设备文件节点
int minor = 0;struct mpu6050_dev //自定义数据结构,
{struct cdev mydev; //字符设备结构体struct i2c_client *pclt; //clinet设备结构体};struct mpu6050_dev *pgmydev = NULL;/****************************************************/
/*直接驱动i2c实现读一个字节的操作函数*/
int mpu6050_read_byte(struct i2c_client *pclt , unsigned char reg){int ret = 0;char txbuf[1] = {reg};struct i2c_msg msg[2] ={{pclt->addr , 0 , 1, txbuf}, //发送i2c地址,发送寄存器地址标号{pclt->addr ,I2C_M_RD , 1, txbuf}, //发送地址及读标志,读数据到rxbuf中};ret = i2c_transfer(pclt->adapter , msg , ARRAY_SIZE(msg));if (ret < 0){printk("driver: ret = %d, in mpu6050_read_byte\n",ret);return ret;}return txbuf[0];
}
/*直接驱动i2c实现写一个字节的操作函数*/
int mpu6050_write_byte(struct i2c_client *pclt , unsigned char reg , unsigned char val){int ret = 0;char txbuf[2] = {reg , val} ; //寄存器标号,寄存器值struct i2c_msg msg[1] = {{pclt->addr , 0 , 2 , txbuf}, };ret = i2c_transfer(pclt->adapter , msg , ARRAY_SIZE(msg));if (ret <0){printk("driver: ret= %d , in mpu6050_write_byte\n",ret);return ret;}return 0;
}/*对应文件标准IO函数ioctl的底层操作函数*/
long mpu6050_ioctl(struct file *pfile , unsigned int cmd , unsigned long arg){struct mpu6050_dev *pmydev = (struct mpu6050_dev *)pfile->private_data;union mpu6050_data data;switch(cmd){case GET_ACCEL:data.accel.x = mpu6050_read_byte(pmydev->pclt,ACCEL_XOUT_L);data.accel.x += mpu6050_read_byte(pmydev->pclt,ACCEL_XOUT_H) << 8;data.accel.y = mpu6050_read_byte(pmydev->pclt,ACCEL_YOUT_L);data.accel.y += mpu6050_read_byte(pmydev->pclt,ACCEL_YOUT_H) << 8;data.accel.z = mpu6050_read_byte(pmydev->pclt,ACCEL_ZOUT_L);data.accel.z += mpu6050_read_byte(pmydev->pclt,ACCEL_ZOUT_H) << 8;break;case GET_GYRO:data.gyro.x = mpu6050_read_byte(pmydev->pclt, GYRO_XOUT_L);data.gyro.x += mpu6050_read_byte(pmydev->pclt, GYRO_XOUT_H) << 8;data.gyro.y = mpu6050_read_byte(pmydev->pclt, GYRO_YOUT_L);data.gyro.y += mpu6050_read_byte(pmydev->pclt, GYRO_YOUT_H) << 8;data.gyro.z = mpu6050_read_byte(pmydev->pclt, GYRO_ZOUT_L);data.gyro.z += mpu6050_read_byte(pmydev->pclt, GYRO_ZOUT_H) << 8;break;case GET_TEMP:data.temp = mpu6050_read_byte(pmydev->pclt , TEMP_OUT_L);data.temp += mpu6050_read_byte(pmydev->pclt , TEMP_OUT_H) << 8;break;default:return -EINVAL;}if (copy_to_user((void*)arg , &data,sizeof(data))){return -EFAULT;}return sizeof(data);
}int mpu6050_open(struct inode *pnode , struct file *pfile){pfile->private_data = (void*)(container_of(pnode->i_cdev , struct mpu6050_dev , mydev));return 0;
}int mpu6050_close(struct inode *pnode , struct file *pfile){return 0;
}/*mpu6050的初始化设置函数*/
void init_mpu6050(struct i2c_client *pclt){mpu6050_write_byte(pclt ,PWR_MGMT_1,0x00);mpu6050_write_byte(pclt,SMPLRT_DIV,0X07);mpu6050_write_byte(pclt,CONFIG,0x06);mpu6050_write_byte(pclt,GYRO_CONFIG,0xF8);mpu6050_write_byte(pclt,ACCEL_CONFIG,0x19);
}struct file_operations myops = {.owner = THIS_MODULE,.open = mpu6050_open,.release = mpu6050_close,.unlocked_ioctl = mpu6050_ioctl ,};/*重要的驱动设备构造函数与驱动初始化函数*/
int mpu6050_probe(struct i2c_client *pclt , const struct i2c_device_id *pid){int ret = 0;dev_t devno;/**申请设备号*/if (!major){ // 主设备号只创建一次,同类设备只有子设备号区分ret = alloc_chrdev_region(&devno , 0 , MPU6050_NUM , "mpu6050_i2c_driver");if (ret){ //失败ret = -1;goto err0;}major = MAJOR(devno);minor = MINOR(devno);}/**分配全局数据结构的地址*/if (!pgmydev){pgmydev = (struct mpu6050_dev*)kzalloc(sizeof(struct mpu6050_dev) * MPU6050_NUM , GFP_KERNEL);if (!pgmydev){ret = -1;printk("driver: kzlloc struct pgmydev faild\n");goto err1;}memset(pgmydev , 0 , sizeof(struct mpu6050_dev)*MPU6050_NUM);}(pgmydev+minor)->pclt = pclt; //把在init函数中匹配好的i2c_client结构数据,存入全局数据结构里对应的数组元素里/**创建设备类*/dev_class = class_create(THIS_MODULE , "mpu6050_class");if (IS_ERR(dev_class)){ret = PTR_ERR(dev_class);printk("driver: create device class faild.\n ");goto err2;}/**初始化cdev对象,关联操作函数集,并将cdev添加到内核链表中*/devno = MKDEV(major , minor);cdev_init(&(pgmydev+minor)->mydev , &myops);(pgmydev+minor)->mydev.owner = THIS_MODULE;ret = cdev_add(&(pgmydev+minor)->mydev , devno , 1);if (ret){ //失败printk("driver:cdev_add faild\n");goto err2;}/**创建设备文件节点*/device_create(dev_class , NULL , devno , NULL , "/dev/mpu6050-%d",minor);/**初始化对应的mpu6050设备*/init_mpu6050((pgmydev+minor)->pclt);/*子设备号0完成后,就准备着如果有下一个匹配的子设备号了。*/minor++ ; return 0;err2:kfree(pgmydev);err1:unregister_chrdev_region(devno , MPU6050_NUM);err0:printk("driver: mpu6050 driver faild.\n");return ret; }/*驱动的移除函数*/
int mpu6050_remove(struct i2c_client *plct){dev_t devno = plct->dev.devt;int minor = MINOR(devno);cdev_del(&(pgmydev+minor)->mydev);unregister_chrdev_region(devno , 1);kfree(pgmydev);pgmydev = NULL;return 0;
}struct i2c_device_id mpu6050_ids[]={{"mpu6050-1" , 0},{},
};/*驱动对象的数据结构体,代表了驱动对象,挂接到驱动链表中*/
struct i2c_driver mpu6050_driver = {.driver ={.name = "mpu6050-1",.owner = THIS_MODULE,},.probe = mpu6050_probe,.remove = mpu6050_remove.id_table = mpu6050_ids,
};#if 0
int __intit mpu6050_driver_init(void){i2c_add_driver(mpu6050_driver);
}void __exit mpu6050_driver_exit(void){i2c_del_driver(mpu6050_driver);
}
module_init(mpu6050_driver_init);
module_exit(mpu6050_driver_exit);
#else
module_i2c_driver(mpu6050_driver);#endifMODULE_LICENSE("GPL");
2.4 read_mpu.c 测试的应用层APP
/*mpu6050_i2c_driver.c
* 做为i2c外接设备的驱动程序,负责完成:
* 1、查找并匹配i2c设备对象,可以匹配多个i2c设备对象
* 2、实现常规的驱动设备号申请,inode与file_operations的对应,以及与内核数据结构的关联
* 3、创建设备文件节点
* 4、实现操作函数open read write ioctl等 */#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/i2c.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/wait.h>
#include <linux/sched.h>
#include <linux/poll.h>
#include <linux/slab.h>
#include <linux/mm.h>
#include <linux/io.h>
#include <asm/uaccess.h>
#include <asm/atomic.h>#include "mpu6050.h"/**以下为mpu6050中的各个寄存器地址标号*/#define SMPLRT_DIV 0x19 //采样率分配器配置寄存器
#define CONFIG 0x1A //配置FSYNC 和 DLPF 寄存器
#define GYRO_CONFIG 0x1B //配置陀螺仪的寄存器
#define ACCEL_CONFIG 0x1C //配置加速度计的寄存器#define ACCEL_XOUT_H 0x3B //X向加速度值的高8位
#define ACCEL_XOUT_L 0x3C //X向加速度值的低8位
#define ACCEL_YOUT_H 0x3D //Y向加速度值的高8位
#define ACCEL_YOUT_L 0x3E //Y向加速度值的低8位
#define ACCEL_ZOUT_H 0x3F //Z向加速度值的高8位
#define ACCEL_ZOUT_L 0x40 //Z向加速度值的低8位
#define TEMP_OUT_H 0x41 //温度的高8位
#define TEMP_OUT_L 0x42 //温度的氏8位
#define GYRO_XOUT_H 0x43 //x轴角速度的高8位
#define GYRO_XOUT_L 0x44 //x轴角速度的低8位
#define GYRO_YOUT_H 0x45 //Y轴角速度的高8位
#define GYRO_YOUT_L 0x46 //Y轴角速度的低8位
#define GYRO_ZOUT_H 0x47 //Z轴角速度的高8位
#define GYRO_ZOUT_L 0x48 //Z轴角速度的低8位#define PWR_MGMT_1 0x6B //电源管理寄存器#define MPU6050_NUM 1 //需要驱动的设备个数
struct class *dev_class=NULL ; //创建设备类
int major = 0; //采用自动分配的方式创建设备号,并自动创建设备文件节点
int minor = 0;struct mpu6050_dev //自定义数据结构,
{struct cdev mydev; //字符设备结构体struct i2c_client *pclt; //clinet设备结构体};struct mpu6050_dev *pgmydev = NULL;/****************************************************/
/*直接驱动i2c实现读一个字节的操作函数*/
int mpu6050_read_byte(struct i2c_client *pclt , unsigned char reg){int ret = 0;char txbuf[1] = {reg};struct i2c_msg msg[2] ={{pclt->addr , 0 , 1, txbuf}, //发送i2c地址,发送寄存器地址标号{pclt->addr ,I2C_M_RD , 1, txbuf}, //发送地址及读标志,读数据到rxbuf中};ret = i2c_transfer(pclt->adapter , msg , ARRAY_SIZE(msg));if (ret < 0){printk("driver: ret = %d, in mpu6050_read_byte\n",ret);return ret;}return txbuf[0];
}
/*直接驱动i2c实现写一个字节的操作函数*/
int mpu6050_write_byte(struct i2c_client *pclt , unsigned char reg , unsigned char val){int ret = 0;char txbuf[2] = {reg , val} ; //寄存器标号,寄存器值struct i2c_msg msg[1] = {{pclt->addr , 0 , 2 , txbuf}, };ret = i2c_transfer(pclt->adapter , msg , ARRAY_SIZE(msg));if (ret <0){printk("driver: ret= %d , in mpu6050_write_byte\n",ret);return ret;}return 0;
}/*对应文件标准IO函数ioctl的底层操作函数*/
long mpu6050_ioctl(struct file *pfile , unsigned int cmd , unsigned long arg){struct mpu6050_dev *pmydev = (struct mpu6050_dev *)pfile->private_data;union mpu6050_data data;switch(cmd){case GET_ACCEL:data.accel.x = mpu6050_read_byte(pmydev->pclt,ACCEL_XOUT_L);data.accel.x += mpu6050_read_byte(pmydev->pclt,ACCEL_XOUT_H) << 8;data.accel.y = mpu6050_read_byte(pmydev->pclt,ACCEL_YOUT_L);data.accel.y += mpu6050_read_byte(pmydev->pclt,ACCEL_YOUT_H) << 8;data.accel.z = mpu6050_read_byte(pmydev->pclt,ACCEL_ZOUT_L);data.accel.z += mpu6050_read_byte(pmydev->pclt,ACCEL_ZOUT_H) << 8;break;case GET_GYRO:data.gyro.x = mpu6050_read_byte(pmydev->pclt, GYRO_XOUT_L);data.gyro.x += mpu6050_read_byte(pmydev->pclt, GYRO_XOUT_H) << 8;data.gyro.y = mpu6050_read_byte(pmydev->pclt, GYRO_YOUT_L);data.gyro.y += mpu6050_read_byte(pmydev->pclt, GYRO_YOUT_H) << 8;data.gyro.z = mpu6050_read_byte(pmydev->pclt, GYRO_ZOUT_L);data.gyro.z += mpu6050_read_byte(pmydev->pclt, GYRO_ZOUT_H) << 8;break;case GET_TEMP:data.temp = mpu6050_read_byte(pmydev->pclt , TEMP_OUT_L);data.temp += mpu6050_read_byte(pmydev->pclt , TEMP_OUT_H) << 8;break;default:return -EINVAL;}if (copy_to_user((void*)arg , &data,sizeof(data))){return -EFAULT;}return sizeof(data);
}int mpu6050_open(struct inode *pnode , struct file *pfile){pfile->private_data = (void*)(container_of(pnode->i_cdev , struct mpu6050_dev , mydev));return 0;
}int mpu6050_close(struct inode *pnode , struct file *pfile){return 0;
}/*mpu6050的初始化设置函数*/
void init_mpu6050(struct i2c_client *pclt){mpu6050_write_byte(pclt ,PWR_MGMT_1,0x00);mpu6050_write_byte(pclt,SMPLRT_DIV,0X07);mpu6050_write_byte(pclt,CONFIG,0x06);mpu6050_write_byte(pclt,GYRO_CONFIG,0xF8);mpu6050_write_byte(pclt,ACCEL_CONFIG,0x19);
}struct file_operations myops = {.owner = THIS_MODULE,.open = mpu6050_open,.release = mpu6050_close,.unlocked_ioctl = mpu6050_ioctl ,};/*重要的驱动设备构造函数与驱动初始化函数*/
int mpu6050_probe(struct i2c_client *pclt , const struct i2c_device_id *pid){int ret = 0;dev_t devno;/**申请设备号*/if (!major){ // 主设备号只创建一次,同类设备只有子设备号区分ret = alloc_chrdev_region(&devno , 0 , MPU6050_NUM , "mpu6050_i2c_driver");if (ret){ //失败ret = -1;goto err0;}major = MAJOR(devno);minor = MINOR(devno);}/**分配全局数据结构的地址*/if (!pgmydev){pgmydev = (struct mpu6050_dev*)kzalloc(sizeof(struct mpu6050_dev) * MPU6050_NUM , GFP_KERNEL);if (!pgmydev){ret = -1;printk("driver: kzlloc struct pgmydev faild\n");goto err1;}memset(pgmydev , 0 , sizeof(struct mpu6050_dev)*MPU6050_NUM);}(pgmydev+minor)->pclt = pclt; //把在init函数中匹配好的i2c_client结构数据,存入全局数据结构里对应的数组元素里/**创建设备类*/dev_class = class_create(THIS_MODULE , "mpu6050_class");if (IS_ERR(dev_class)){ret = PTR_ERR(dev_class);printk("driver: create device class faild.\n ");goto err2;}/**初始化cdev对象,关联操作函数集,并将cdev添加到内核链表中*/devno = MKDEV(major , minor);cdev_init(&(pgmydev+minor)->mydev , &myops);(pgmydev+minor)->mydev.owner = THIS_MODULE;ret = cdev_add(&(pgmydev+minor)->mydev , devno , 1);if (ret){ //失败printk("driver:cdev_add faild\n");goto err2;}/**创建设备文件节点*/device_create(dev_class , NULL , devno , NULL , "/dev/mpu6050-%d",minor);/**初始化对应的mpu6050设备*/init_mpu6050((pgmydev+minor)->pclt);/*子设备号0完成后,就准备着如果有下一个匹配的子设备号了。*/minor++ ; return 0;err2:kfree(pgmydev);err1:unregister_chrdev_region(devno , MPU6050_NUM);err0:printk("driver: mpu6050 driver faild.\n");return ret; }/*驱动的移除函数*/
int mpu6050_remove(struct i2c_client *plct){dev_t devno = plct->dev.devt;int minor = MINOR(devno);cdev_del(&(pgmydev+minor)->mydev);unregister_chrdev_region(devno , 1);kfree(pgmydev);pgmydev = NULL;return 0;
}struct i2c_device_id mpu6050_ids[]={{"mpu6050-1" , 0},{},
};/*驱动对象的数据结构体,代表了驱动对象,挂接到驱动链表中*/
struct i2c_driver mpu6050_driver = {.driver ={.name = "mpu6050-1",.owner = THIS_MODULE,},.probe = mpu6050_probe,.remove = mpu6050_remove.id_table = mpu6050_ids,
};#if 0
int __intit mpu6050_driver_init(void){i2c_add_driver(mpu6050_driver);
}void __exit mpu6050_driver_exit(void){i2c_del_driver(mpu6050_driver);
}
module_init(mpu6050_driver_init);
module_exit(mpu6050_driver_exit);
#else
module_i2c_driver(mpu6050_driver);#endifMODULE_LICENSE("GPL");
2.5 Makefile
CUR_DIR := $(shell pwd)ifeq ($(filename),)ifeq ($(KERNELRELEASE), )ifeq ($(ARCH),arm)
KERNEL_DIR := /home/mao/linux/linux-3.14ROOTFS_DIR := /opt/4412/rootfselseKERNEL_DIR := /lib/modules/$(shell uname -r)/buildendifall :$(MAKE) -C $(KERNEL_DIR) M=$(CUR_DIR) modulesinstall:#$(MAKE) -C $(KERNEL_DIR) M=$(CUR_DIR) INSTALL_MOD_PATH=$(ROOTFS_DIR) modules_installcp *.ko $(ROOTFS_DIR)/drv -rf
clean :make -C $(KERNEL_DIR) M=$(CUR_DIR) cleanelseobj-m += mpu6050_i2c_client.o
obj-m += mpu6050_i2c_driver.oendifelseifeq ($(ARCH),arm)
GCC_DIR := ~/linux/toolchain/gcc-4.6.4/bin/arm-none-linux-gnueabi-
ROOTFSDIR := /opt/4412/rootfs/work
else
GCC_DIR = /usr/bin/
ROOTFSDIR = $(shell pwd)/work/
endifall:$(GCC_DIR)gcc $(filename).c -o $(filename).elf sudo mv -f $(filename).elf $(ROOTFSDIR)cp -rf ./load.sh $(ROOTFSDIR)endif
3、以设备树方式进行匹配的i2c驱动
3.1 完善从设备节点
根据 【嵌入式环境下linux内核及驱动学习笔记-(15)linux总线、设备、驱动模型之I2C总线】的 第 4.1.小节所提供的设备原理图,需要在设备树里添加mpu6050的子节点,如下:
在exynosf442-fs4412.dts下添加节点。
在i2c总线节点下添加了mpu6050的设备子节点
记得进行编译 make dtbs
并将编译后的 exynos412-fs4412.dtb拷入tftpboot目录中