i2c 驱动三:自己实现设备和驱动分离

版权声明:本文为博主原创文章,遵循 CC 4.0 by-sa 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/qqliyunpeng/article/details/52958650

有关linux的i2c相关文章有一下几篇,他们互相关联,应该一同看:

    - i2c 驱动一:简介

    - i2c 驱动二:devfs文件系统

    - i2c 驱动三:自己实现设备和驱动分离

    - i2c 驱动四:sysfs文件系统

    - i2c 驱动五:gpio模拟i2c


对i2c的驱动和设备的匹配说完之后,感觉如何获得 adapter 等的一些小的注意事项还是不能尽然说尽,干脆来个自己实现 platform 架构的 i2c 驱动


.
├── dev
│   ├── Makefile
│   └── mpu6050_dev.c
└── dri
    ├── Makefile
    └── mpu6050_dri.c


mpu6050_dev.c

#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/i2c.h>
#include <linux/delay.h>

MODULE_LICENSE("GPL");

static struct i2c_client *mpu6050_clientp;

static struct i2c_board_info mpu6050_i2c_devs = {
        .type = "mpu6050",
        .addr = 0x68,
        /*I2C_BOARD_INFO("mpu6050", 0x50), //这个宏是上边两个变量赋值的替换*/
};

static int mpu6050_dev_init(void)
{
    struct i2c_adapter *i2c_adap;    //分配一个适配器的指针

    printk("mpu6050_dev_init.\n");

    i2c_adap = i2c_get_adapter(0);   //0 是 i2c 总线编号

    mpu6050_clientp = i2c_new_device(i2c_adap, &mpu6050_i2c_devs); //只能添加一个
    if (!mpu6050_clientp)
        printk("register i2c error!\n");

    i2c_put_adapter(i2c_adap);       //释放 adapter

    return 0;
}

static void mpu6050_dev_exit(void)
{
    printk("mpu6050_dev_exit.\n");
    i2c_unregister_device(mpu6050_clientp);
}

module_init(mpu6050_dev_init);
module_exit(mpu6050_dev_exit);

Makefile

ifeq ($(KERNELRELEASE),)

#KERNELDIR ?= /lib/modules/$(shell uname -r)/build 
KERNELDIR ?= ~/wor_lip/linux-3.4.112
PWD := $(shell pwd)

modules:
	$(MAKE) -C $(KERNELDIR) M=$(PWD) modules

modules_install:
	$(MAKE) -C $(KERNELDIR) M=$(PWD) modules_install

clean:
	rm -rf *.o *~ core .depend .*.cmd *.ko *.mod.c .tmp_versions modules* Module*

.PHONY: modules modules_install clean

else
	obj-m := mpu6050_dev.o
endif

mpu6050_dri.c

#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/i2c.h>
#include <linux/delay.h>

MODULE_LICENSE("GPL");

#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

static int mpu6050_read_byte(struct i2c_client *client, unsigned char reg)
{
    int ret;

    char txbuf[1] = { reg  };
    char rxbuf[1];

    struct i2c_msg msg[] = {
        {client->addr, 0, 1, txbuf},
        {client->addr, I2C_M_RD, 1, rxbuf}

    };

    ret = i2c_transfer(client->adapter, msg, ARRAY_SIZE(msg));
    if (ret < 0) {
        printk("ret = %d\n", ret);
        return ret;

    }

    return rxbuf[0];
}

static int mpu6050_write_byte(struct i2c_client *client, unsigned char reg, unsigned char val)
{
    char txbuf[2] = {reg, val};

    struct i2c_msg msg[] = {
        {client->addr, 0, 2, txbuf},

    };

    i2c_transfer(client->adapter, msg, ARRAY_SIZE(msg));

    return 0;
}

static int mpu6050_probe(struct i2c_client *client, const struct i2c_device_id *id)
{
    unsigned short accel_x = 0, accel_y = 0, accel_z = 0;
    unsigned short gyro_x = 0, gyro_y = 0, gyro_z = 0;
    unsigned short temp = 0;

    printk("match OK!\n");
    /*printk("irqno = %d\n", client->irq);*/
    printk("addr = %d\n", client->addr);

    mpu6050_write_byte(client, PWR_MGMT_1, 0x00);
    mpu6050_write_byte(client, SMPLRT_DIV, 0x07);
    mpu6050_write_byte(client, CONFIG, 0x06);
    mpu6050_write_byte(client, GYRO_CONFIG, 0x18);
    mpu6050_write_byte(client, ACCEL_CONFIG, 0x01);

    while(1) {
        accel_x = mpu6050_read_byte(client, ACCEL_XOUT_L);
        accel_x |= mpu6050_read_byte(client, ACCEL_XOUT_H) << 8;

        accel_y =  mpu6050_read_byte(client, ACCEL_YOUT_L);
        accel_y |= mpu6050_read_byte(client, ACCEL_YOUT_H) << 8;

        accel_z = mpu6050_read_byte(client, ACCEL_ZOUT_L);
        accel_z |= mpu6050_read_byte(client, ACCEL_ZOUT_H) << 8;

        printk("acceleration data: x = %04x, y = %04x, z = %04x\n", accel_x, accel_y, accel_z);

        gyro_x = mpu6050_read_byte(client, GYRO_XOUT_L);
        gyro_x |= mpu6050_read_byte(client, GYRO_XOUT_H) << 8;

        gyro_y = mpu6050_read_byte(client, GYRO_YOUT_L);
        gyro_y |= mpu6050_read_byte(client, GYRO_YOUT_H) << 8;

        gyro_z = mpu6050_read_byte(client, GYRO_ZOUT_L);
        gyro_z |= mpu6050_read_byte(client, GYRO_ZOUT_H) << 8;

        printk("gyroscope data: x = %04x, y = %04x, z = %04x\n", gyro_x, gyro_y, gyro_z);

        temp = mpu6050_read_byte(client, TEMP_OUT_L);
        temp |= mpu6050_read_byte(client, TEMP_OUT_H) << 8;

        printk("temperature data: %x\n", temp);

        mdelay(500);
    }

    return 0;
}

static int mpu6050_remove(struct i2c_client *client)
{
    printk("driver removed.\n");

    return 0;
}

static const struct i2c_device_id mpu6050_id[] = {
    { "mpu6050", 0 },
}; 

/*MODULE_DEVICE_TABLE(i2c, mpu6050_id);*/

static struct i2c_driver mpu6050_driver = {
    .driver = {
        .name           = "xx",    //供模块匹配用
        .owner          = THIS_MODULE,
    },
    .probe      = mpu6050_probe,
    .remove     = mpu6050_remove,
    .id_table   = mpu6050_id,     /* 用于I2C driver的probe函数调用 */     //供平台文件匹配用
};

/*i2c_add_driver();*/

module_i2c_driver(mpu6050_driver);//简单方式,内核中是个宏,完成了模块的初始化和卸载

Makefile

ifeq ($(KERNELRELEASE),)

#KERNELDIR ?= /lib/modules/$(shell uname -r)/build 
KERNELDIR ?= ~/wor_lip/linux-3.4.112
PWD := $(shell pwd)

modules:
	$(MAKE) -C $(KERNELDIR) M=$(PWD) modules

modules_install:
	$(MAKE) -C $(KERNELDIR) M=$(PWD) modules_install

clean:
	rm -rf *.o *~ core .depend .*.cmd *.ko *.mod.c .tmp_versions modules* Module*

.PHONY: modules modules_install clean

else
	obj-m := mpu6050_dri.o
endif

各自生成,放到板子的根目录下 insmod ,就会直接打印mpu的数据,打印结果如上一片中结果

展开阅读全文

没有更多推荐了,返回首页