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

news2024/11/29 8:39:47

目录

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

3.2 驱动代码实现

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/626363.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Python3数据分析与挖掘建模(10)复合分析-交叉分析与实现示例

1. 复合分析 1.1 概述 复合分析&#xff08;Factorial Analysis&#xff09;是一种统计分析方法&#xff0c;用于研究多个因素对观测结果的影响&#xff0c;并探究各个因素之间的相互作用效应。 在复合分析中&#xff0c;研究者会选择多个因素&#xff08;也称为处理变量或独…

武汉涉密系统集成资质保密制度涵盖哪些内容

涉密信息系统集成资质申请其中最核心的就是保密制度管理&#xff0c;这里面涉及到保密管理的方方面面&#xff0c;现场审查也是围绕保密制度建设内容来打分的。申报单位应当建立规范、操作性强的保密制度&#xff0c;并根据实际情况及时修订完善。保密制度的具体要求应当体现在…

highlight clock tree

当分析clock tree需要在图形界面highlight clock tree时&#xff0c;最朴实无华的方法就是贴报告&#xff0c;除此之外这里也分享一下用命令的方法。 1.Imported Path Pins 1&#xff09;Highlight > Color By > Imported Path Pins 2&#xff09;report_timing icc2…

智慧园区物业可视化大屏

随着万物互联、数字信息时代的到来&#xff0c;也给物业园区管理行业带来变革性影响。 园区作为城市的基本组成单元&#xff0c;是人口和产业的重要聚集区&#xff0c;现已逐渐成为中国经济转型升级和创新发展的主力。 智慧园区物业可视化整合园区现有信息系统的数据资源&#…

OpenStack部署(四)

OpenStack部署 8. Dashboard8.1 安装并配置8.2 重启web服务器以及会话存储服务8.3 浏览器访问配置 9. Designate9.1 创建Designate数据库并授权9.2 获得admin凭证9.3 创建designate用户并设置密码9.4 添加admin角色到designate用户9.5 创建designate服务实体9.6 创建designate服…

【计算机组成与体系结构Ⅰ】章节测试(1-3)

下列是计算机中几种常见的机器数编码&#xff1a; ①原码 ②反码 ③补码 零的表示唯一的是&#xff08; &#xff09; A&#xff0e;仅③ B&#xff0e;② C&#xff0e;①、② D&#xff0e;①、③ 下列采用偶校验的8位奇偶校验编码中正确的是&#xff08; &#xff09;…

【Spring】——Spring简单 读和取

前言 ❤️❤️❤️Spring专栏更新中&#xff0c;各位大佬觉得写得不错&#xff0c;支持一下&#xff0c;感谢了&#xff01;❤️❤️❤️ Spring_冷兮雪的博客-CSDN博客 上期我们讲解了Spring的创建与使用&#xff0c;发现 将Bean 注册到容器 这一步中&#xff0c;如果Bean对象…

Matter协议高速崛起,你真的了解它吗?

今天我们要聊的话题&#xff0c;和智能家居有关。 说到智能家居&#xff0c;大家应该都不会陌生。早在本世纪初&#xff0c;物联网概念刚刚诞生的时候&#xff0c;最主要的应用领域&#xff0c;就是智能家居。 这些年来&#xff0c;随着数字技术的不断发展&#xff0c;越来越多…

MINIX 已死,Linux 又将如何呢?

导读MINIX 操作系统大约的确已经死了。Minix 原来是荷兰阿姆斯特丹的 Vrije 大学计算机科学系的 Andrew S. Tanenbaum 教授所开发的一个类 Unix 操作系统&#xff0c;全部代码共约 12,000 行&#xff0c;起初是为给学生讲解操作系统的运作细节而开发。 Linus Torvalds 也曾表示…

报表生成器FastReport .Net用户指南:“Rich Text“对象

FastReport .Net是一款全功能的Windows Forms、ASP.NET和MVC报表分析解决方案&#xff0c;使用FastReport .NET可以创建独立于应用程序的.NET报表&#xff0c;同时FastReport .Net支持中文、英语等14种语言&#xff0c;可以让你的产品保证真正的国际性。 FastReport.NET官方版…

1999-2020年31省省农村人口就业和文化程度相关数据

1999-2020年31省省农村人口就业和文化程度相关数据 1、时间&#xff1a; 2、范围&#xff1a;包括全国31省 3、来源&#xff1a;整理自各省NJ、统计NJ、农村NJ 4、指标包括&#xff1a; 乡村人口和乡村就业人员&#xff1a;乡村人口&#xff08;万人&#xff09;、乡村人口&…

opencv实践项目-停车位检测

目录 1. 步骤1.1 selector选择器1.2 detector探测器 2. 代码3. 效果图 1. 步骤 1.1 selector选择器 我们可以选择摄网络摄像头提供的第一帧&#xff0c;在该图像上选择停车位。为此&#xff0c;保存并使用该图像选择停车位。使用selectROIs函数标记停车位。ROI被定义为感兴趣…

分布式限流算法及实现介绍

分布式系统架构下面对突增的高并发访问请求&#xff0c;如何实现限流以保护系统的可用性是需要关注的一个问题。分布式限流实现机制上有很多中&#xff0c;包括基于网关实现、基于中间件如Redis实现等&#xff0c;本文简要介绍限流的常用算法以及实现方案。 1、分布式限流概述 …

Windows操作系统渗透测试

Windows操作系统渗透测试 任务环境说明&#xff1a;服务器场景名&#xff1a;Server02服务器场景操作系统&#xff1a;未知&#xff08;关闭链接&#xff09; 1.通过本地PC中渗透测试平台Kali对服务器场景进行系统服务及版本扫描渗透测试&#xff0c;并将该操作显示结果中808…

可视化报表系统推荐

在当今信息时代&#xff0c;数据的处理和分析已经成为了企业管理中不可或缺的一部分。而报表则是这个过程中最常见的工具之一。手工写报表虽然简单易懂&#xff0c;但是随着数据量的增加&#xff0c;这种方式逐渐暴露出许多痛点。比如说&#xff1a; 1.时间耗费长&#xff1a;…

Linux之进程间通信——管道

文章目录 前言一、进程间通信1.概念2.目的3.进程间通信分类 二、管道1.管道介绍2.管道分类1.匿名管道pipi创建管道文件&#xff0c;打开读写端fork子进程关闭父进程的读取端&#xff0c;关闭子进程的写入端读写特征管道特征 2.命名管道mkfifo创建管道文件删除管道文件通信 三、…

【openframework】实时路径规划(RTRRTstar算法)

程序框架 视频演示 实时RRT-star算法介绍 实时RRT-star算法是一种基于采样的运动规划算法&#xff0c;它可以在有限的时间内找到一条渐进最优的路径。实时RRT-star算法是在RRT-star算法的基础上进行了改进&#xff0c;主要有两个方面&#xff1a; - 实时更新起始点。实时RRT-st…

那些漏洞挖掘高手都是怎么挖漏洞的?

前言 说到安全就不能不说漏洞&#xff0c;而说到漏洞就不可避免地会说到三座大山&#xff1a; 漏洞分析 漏洞利用 漏洞挖掘 从个人的感觉上来看&#xff0c;这三者尽管通常水乳交融、相互依赖&#xff0c;但难度是不尽相同的。本文就这三者分别谈谈自己的经验和想法。 漏洞分析…

Mac图片批处理工具

PhotoMill X是一款强大的图像处理软件&#xff0c;它可以帮助用户快速地对照片进行编辑、调整和转换。它支持在单个或批量模式下处理大量的图像文件&#xff0c;并具有直观的用户界面和易于使用的工具。 PhotoMill X具有广泛的编辑功能&#xff0c;包括裁剪、缩放、旋转、调整明…

记录基于Vue.js的Tree组件_Liquor Tree

酒树 (Liquor Tree) A Vue tree component that allows you to present hierarchically organized data in a nice and logical manner. Vue 树组件&#xff0c;可让您以美观和逻辑的方式呈现层次结构的数据。 supports mobile, and has a variety of response events. Flex…