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

news/2024/11/8 20:01:32/

目录

  • 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 驱动代码实现


http://www.ppmy.cn/news/291308.html

相关文章

苹果计算机如何添加桌面上,苹果笔记本显示桌面怎样设置

在Windows操作系统中&#xff0c;任务栏(快速启动栏)中的显示桌面图标的实际位置是“C:WindowsSystem显示桌面.scf”。快速启动是通过放在“C:WindowsApplication DataMicrosoftInternet ExplorerQuick Launch显示桌面.scf”实现的。当显示桌面图标被误删需要恢复该图标时&…

Mac/ios配置java并用记事本写java

java安装分为三个步骤&#xff1a; 安装jdk配置环境变量用记事本写java程序 1. 检查是否安装好了jdk&#xff1a;java -version 输入&#xff1a; > java -version如果如下显示版本信息&#xff0c;则安装成功&#xff0c;可以进行下一步骤。 2.检查环境变量是否配置 …

苹果计算机免费的,《苹果Mac电脑使用的日常》免费版 Mac软件推荐

大家好&#xff0c;很多拥有Mac电脑的用户&#xff0c;都用过虚拟机或者双系统来运行Windows的。主要原因是Mac电脑上找不到自己常用的软件。Mac软件推荐&#xff0c;虽然Mac电脑和WindowsPC都能干活&#xff0c;但是&#xff0c;做同一件事情&#xff0c;用到的软件 可不一定相…

MAC OS和Windows自带记事本比较

Windows中我新建文本的方式一般是&#xff0c;先定位想要存储此文本的文件夹&#xff0c;然后右键->新建文本文档 MAC OS中右键菜单中没有新建文本文档的选项&#xff0c;事实上MAC在文件夹空白处右键只有下面这几个选项 新建文件夹显示简介显示>排列方式按>查看显示…

无法保存关闭文件 苹果电脑 显示正在保存_??Linux文件编辑 - 给你骨质唱疏松

1.vi与vim的简介 可以理解为windows下面的文本编辑器&#xff0c;比如记事本&#xff0c;比如word文档 vi编辑器通常被简称为vi&#xff0c;而vi又是visual editor的简称 它在Linux上的地位就像Edit程序在DOS上一样 它可以执行输出、 删除、查找、替换、块操作等众多文本操作 而…

Android课程设计,生活记事本

目录 0 绪论..............................................................................................................................................1 1 系统需求分析 2 1&#xff0e;1 系统介绍 2 1&#xff0e;2 系统的功能性需求 2 1&#xff0e;3 …

Ipone桌面计算机没了,苹果电脑重启软件不见了怎么办

1. macbook air 的苹果系统 上下载的软件 开机后就消失了 完全不需要重新安装的! 这是因为你根本就没有安装在Finder-应用程序里啊! 你需要将应用程序拖到Finder-应用程序里,那样才算是安装好了的,而且重启后也不会被删除; 你会发现,在App Store下载的应用程序,都是自动…

android 记事本软件,安卓日历记事本软件

哪款手机记事本或者日历软件比较好 这些都可以使用系统自带的&#xff0c;自己的记事本都差不多&#xff0c;严格来讲&#xff0c;这类功能单一的东西差别还是比较小的&#xff0c;苹果的有手写功能&#xff0c;锤子便签可以写markdown&#xff0c;还有魅族的便签&#xff0c;各…