目录

  • 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目录中

3.2 驱动代码实现

由于设备树匹配的代码只是在原有的名称匹配基础上做了很少更改。因此,这里不完整列出代码,只对修改部分做出说明:
1、只保留如上的mpu6050_i2c_driveer.c程序。由于设备树匹配模式下,i2c设备的对象i2c_client 的数据都取自设备树节点。因此,不需要mpu6050_12c_client.c中的静态数据struct i2c_board_info数据结构来提供。

2、增加一个struct of_device_id 数据结构,用于匹配设备树节点,具体如下:

3、更改struct i2c_driver数据结构,把mpu6050_dt 添加到 of_match_table成员中,如下:

然后,重新编译后完成。

【嵌入式环境下linux内核及驱动学习笔记-(15-1)例程】相关推荐

  1. 【嵌入式环境下linux内核及驱动学习笔记-(16)linux总线、设备、驱动模型之input框架】

    目录 1.Linux内核输入子系统概念导入 1.1 输入设备工作机制 1.2 运行框架 1.3 分层思想 2.驱动开发步骤 2.1 在init()或probe()函数中 2.2 在exit()或rem ...

  2. 【嵌入式环境下linux内核及驱动学习笔记-(11-设备树)】

    目录 1.设备树体系 1.1 DTS /DTSI / DTC / DTB 2.基础语法 2.1 节点语法 2.1.1 通用名称建议 2.2 属性语法 2.2.1 属性值 2.3 关于label 2.4 ...

  3. Zynq linux的I2C驱动学习笔记

    最近在用米尔的Z-TURN BOARD单板做小项目.顺便也加强学习I2C驱动,记一篇做记录.  I2C总线知识非常简单,SDA,SCL,他们的时序规则是:I2C总线是由数据线SDA和时钟SCL构成的串 ...

  4. 反编译linux内核 kmem,Volatility学习笔记一:使用手册

    0x00 概述 Volatility是一款开源内存取证框架,能够对导出的内存镜像进行分析,通过获取内核数据结构,使用插件获取内存的详细情况以及系统的运行状态. 特点: 开源:Python编写,易于和基 ...

  5. linux 网卡gso,linux内核网络协议栈学习笔记:关于GRO/GSO/LRO/TSO等patch的分析和测试...

    TSO,全称是TCP Segmentation Offload,我们知道通常以太网的MTU是1500,除去TCP/IP的包头,TCP的MSS (Max Segment Size)大小是1460,通常情 ...

  6. linux内核--设备驱动程序(学习笔记)

    字符设备驱动 一个字符设备要能够工作,需要三部分配合: 有一个设备驱动程序的ko模块,包含中断处理函数.设备操作函数.模块初始化时,将设备号注册到内核的全局数据结构cdev_map中. /dev目录下 ...

  7. linux内核地址映射,Linux内核设备驱动地址映射笔记整理

    #include #define ioremap(cookie,size) __arm_ioremap(cookie, size, MT_DEVICE)//cookie表示物理地址, size表示映射 ...

  8. 【python环境下Z3约束求解器学习笔记】And和Or的用法

    在Z3约束求解器中,我们可能需要寻找同时满足两个条件的模型,也可能需要寻找满足两个条件中一个条件的模型,这个时候,我们可以借助And方法和Or方法来实现 引入 我们先来举一个简单的例子 当我们想要查找 ...

  9. Linux环境下使用 USB转串口驱动(二)

    minicom是linux下串口通信的软件,它的使用完全依靠键盘的操作,虽然没有"超级终端"那么易用,但是使用习惯之后读者将会体会到它的高效与便利,下面将讲解minicom的安装和 ...

最新文章

  1. CodeSmith应用(一)
  2. mysql 左连接b表的一条数据_如果你正在找MySQL精品资源,那来这里看看
  3. mormot支持websocket
  4. linux-windows主动推送文件同步目录数据 linux-windows数据目录同步
  5. 人体上身各部位图_【肝货】画好人体结构,你还需要了解这些
  6. BZOJ 4174 tty的求助 莫比乌斯反演
  7. [转载]Oracle 游标使用全解
  8. 停车管理系统汽车到达汽车离去c语言,停车场管理系统 C语言实现
  9. selenium配置无界面chrome浏览器
  10. 用Python生成测试数据
  11. C语言之pthread_cond_wait()和pthread_cond_timedwait()区别(十五)
  12. 【Python - wxpython】- 卫星通信系统链路计算软件
  13. 干货文:企业 IT 基础架构|(精华篇)
  14. 怎样区别7290喷壳机与原壳黑莓手机,里面有详图
  15. 我们为什么要学习保险知识
  16. 永中office属于职称计算机吗,职称计算机考核永中Office辅导之文字处理.docx
  17. 【RedNet2018】RedNet: Residual Encoder-Decoder Network for indoor RGB-D Semantic Segmentation
  18. 【计算1970年到任意一个年月距离有多久】
  19. win7系统打开定位服务器地址,win7系统手动打开或关闭系统定位服务的步骤
  20. Nvidia TX2 刷机全过程

热门文章

  1. 常见的数据库,默认端口号是多少?
  2. 仿淘宝网商品SKU系统设计经验分享
  3. pmp之进度数据和进度计划
  4. ssh连接管理iPhone
  5. 亲测有效:spring boot中parent节点报错解决办法
  6. python 多进程并发与多线程并发总结
  7. Mac系统中Clion上实现伪的按任意键继续...
  8. UM2002 一款低功耗SUB-1G 无线接收机芯片
  9. linux下文件读写
  10. java学习系列2(并发锁问题-乐观锁与悲观锁以及乐观锁的一种实现方式-CAS)