目录
- 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-gcc
DIR_86 := /usr/bin/
OBJECT := mpu6050.o main.o
ifeq ($(ARCH),arm)
CC := $(DIR_ARM)$(GCC)
else
CC := $(DIR_86)gcc
endif
all:$(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的引用减1
return 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);
#endif
MODULE_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);
#endif
MODULE_LICENSE("GPL");
2.5 Makefile
CUR_DIR := $(shell pwd)
ifeq ($(filename),)
ifeq ($(KERNELRELEASE), )
ifeq ($(ARCH),arm)
KERNEL_DIR := /home/mao/linux/linux-3.14
ROOTFS_DIR := /opt/4412/rootfs
else
KERNEL_DIR := /lib/modules/$(shell uname -r)/build
endif
all :
$(MAKE) -C $(KERNEL_DIR) M=$(CUR_DIR) modules
install:
#$(MAKE) -C $(KERNEL_DIR) M=$(CUR_DIR) INSTALL_MOD_PATH=$(ROOTFS_DIR) modules_install
cp *.ko $(ROOTFS_DIR)/drv -rf
clean :
make -C $(KERNEL_DIR) M=$(CUR_DIR) clean
else
obj-m += mpu6050_i2c_client.o
obj-m += mpu6050_i2c_driver.o
endif
else
ifeq ($(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/
endif
all:
$(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目录中