字符设备驱动编写

文章目录

环境

树莓派 3b+

mpu6050 模块

插在物理 3、5 引脚

一、添加驱动(/sys/bus/i2c/drivers/mpu6050_1)

mpu6050.c

c 复制代码
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/i2c.h>
#include <linux/fs.h>

struct i2c_driver mpu6050_driver = {
    // .probe = mpu6050_probe,
    // .remove = mpu6050_remove,
    .driver = {
		.name = "mpu6050_1",
		// .of_match_table = mpu6050_of_match_table,
	},

};

static int __init mpu6050_driver_init(void)
{
	i2c_add_driver(&mpu6050_driver);

	return 0;
}

static void __exit mpu6050_driver_exit(void)
{
	i2c_del_driver(&mpu6050_driver);

	return;
}

module_init(mpu6050_driver_init);
module_exit(mpu6050_driver_exit);

MODULE_LICENSE("GPL");

Makefile

c 复制代码
obj-m = mpu6050.o

KDIR=/home/liyongjun/project/board/buildroot/RPi3/build/linux-custom
CROSS_COMPILE=/home/liyongjun/project/board/buildroot/RPi3/host/bin/arm-buildroot-linux-gnueabihf-

all:
	make -C ${KDIR} M=${PWD} ARCH=arm CROSS_COMPILE=${CROSS_COMPILE} modules

clean:
	make -C ${KDIR} M=${PWD} ARCH=arm CROSS_COMPILE=${CROSS_COMPILE} clean

使用 i2c_add_driver() 向 i2c 总线上添加一个驱动,这个驱动没有任何功能,只有一个驱动名 "mpu6050_1"。

可以在 /sys/bus/i2c/drivers 目录下查看到该驱动。

bash 复制代码
# ls /sys/bus/i2c/drivers
dummy      stmpe-i2c
# 
# insmod mpu6050.ko 
#
# ls /sys/bus/i2c/drivers
dummy      mpu6050_1  stmpe-i2c
#
# ls /sys/bus/i2c/drivers/mpu6050_1/
bind    module  uevent  unbind

驱动和设备树扯上关系

arch/arm/boot/dts/bcm2710-rpi-3-b-plus.dts

c 复制代码
&i2c1 {
	pinctrl-names = "default";
	pinctrl-0 = <&i2c1_pins>;
	clock-frequency = <100000>;
	status = "okay";

	mpu6050: i2cdev@68 {
		compatible = "lyj,mpu6050";
		reg = <0x68>;
		status = "okay";
	};
};

mpu6050.c

c 复制代码
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/i2c.h>
#include <linux/fs.h>

int mpu6050_probe(struct i2c_client *client, const struct i2c_device_id *id)
{
    printk("mpu6050_probe()\n");

    printk("flags = 0x%x\n", client->flags);
    printk("addr = 0x%x\n", client->addr);
    printk("name = %s\n", client->name);
    printk("adapter name = %s\n", client->adapter->name);
    printk("dev init_name = %s\n", client->dev.init_name);

    return 0;
}

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

    return 0;
}

/*定义设备树匹配表*/
static const struct of_device_id mpu6050_of_match_table[] = {
	{.compatible = "lyj,mpu6050"},
	{},
};

struct i2c_driver mpu6050_driver = {
    .probe = mpu6050_probe,
    .remove = mpu6050_remove,
    .driver = {
		.name = "mpu6050_1",
		.of_match_table = mpu6050_of_match_table,
	},

};

static int __init mpu6050_driver_init(void)
{
	i2c_add_driver(&mpu6050_driver);

	return 0;
}

static void __exit mpu6050_driver_exit(void)
{
	i2c_del_driver(&mpu6050_driver);

	return;
}

module_init(mpu6050_driver_init);
module_exit(mpu6050_driver_exit);

MODULE_LICENSE("GPL");

insmod ko 后,执行 i2c_add_driver(&mpu6050_driver); 向 i2c 总线上注册驱动,内核 i2c 总线会遍历总线上的设备,试图找到和该驱动匹配的未被驱动的设备。

结果就找到了 i2c1 下的 mpu6050: i2cdev@68 ,该设备是在内核解析设备树时添加到 i2c 总线上的。

该驱动支持的列表 .compatible = "lyj,mpu6050" 和设备树的 compatible = "lyj,mpu6050"; 信息匹配,则调用该驱动的 probe() 函数,开始驱动该设备。

并且,内核会将设备信息传递到 probe() 函数的参数中。比如,设备地址为 0x68,设备名称为 mpu6050,设备所属的 i2c 控制器为 i2c@7e804000。

bash 复制代码
# insmod mpu6050.ko 
[ 4901.348057] mpu6050_probe()
[ 4901.352230] flags = 0x0
[ 4901.356057] addr = 0x68
[ 4901.359835] name = mpu6050
[ 4901.363850] adapter name = bcm2835 (i2c@7e804000)
[ 4901.369882] dev init_name = (null)

rmmod 时,会执行 remove() 函数

bash 复制代码
# rmmod mpu6050
[ 6416.835182] mpu6050_remove()

二、注册一个(种/类?)字符设备(/proc/devices,243 mpu6050_1)

mpu6050.c

c 复制代码
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/i2c.h>
#include <linux/fs.h>

static int major;

static const struct file_operations mpu6050_fops = {
    // .open =		mpu6050_dev_open,
	// .read =		mpu6050_dev_read,
};

int mpu6050_probe(struct i2c_client *client, const struct i2c_device_id *id)
{
    printk("mpu6050_probe()\n");

    printk("flags = 0x%x\n", client->flags);
    printk("addr = 0x%x\n", client->addr);
    printk("name = %s\n", client->name);
    printk("adapter name = %s\n", client->adapter->name);
    printk("dev init_name = %s\n", client->dev.init_name);

    major = register_chrdev(0, "mpu6050_1", &mpu6050_fops); // 注册字符设备

    return 0;
}

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

    unregister_chrdev(major, "mpu6050_1");  // 取消注册字符设备

    return 0;
}

/*定义设备树匹配表*/
static const struct of_device_id mpu6050_of_match_table[] = {
	{.compatible = "lyj,mpu6050"},
	{},
};

struct i2c_driver mpu6050_driver = {
    .probe = mpu6050_probe,
    .remove = mpu6050_remove,
    .driver = {
		.name = "mpu6050_1",
		.of_match_table = mpu6050_of_match_table,
	},

};

static int __init mpu6050_driver_init(void)
{
	i2c_add_driver(&mpu6050_driver);

	return 0;
}

static void __exit mpu6050_driver_exit(void)
{
	i2c_del_driver(&mpu6050_driver);

	return;
}

module_init(mpu6050_driver_init);
module_exit(mpu6050_driver_exit);

MODULE_LICENSE("GPL");
bash 复制代码
# insmod mpu6050.ko 
[ 6602.199592] mpu6050_probe()
[ 6602.203828] flags = 0x0
[ 6602.207592] addr = 0x68
[ 6602.211352] name = mpu6050
[ 6602.215408] adapter name = bcm2835 (i2c@7e804000)
[ 6602.221470] dev init_name = (null)
# cat /proc/devices | grep mpu6050
243 mpu6050_1

三、手动创建一个字符设备(mknod /dev/mpu6050 c 243 0)

bash 复制代码
# mknod /dev/mpu6050 c 243 0
#
# ls /dev/mpu6050 -lh
crw-r--r--    1 root     root      243,   0 Jan  1 02:12 /dev/mpu6050

之后,就可以对该设备进行 open()、read()、write()、ioctl()、close() 等操作了,前提是得先注册好上述操作函数。

上面的示例中,我们还没有提供操作函数,如果这个时候去读写设备文件,将会发生错误。

接下来,我们完善操作函数。

提供操作函数

mpu6050.c

c 复制代码
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/i2c.h>
#include <linux/fs.h>
#include "mpu6050.h"

struct i2c_client *mpu6050_client;
static int major;

static int i2c_read_mpu6050(struct i2c_client *mpu6050_client, u8 address, void *data, u32 length)
{
	int error = 0;
	u8 address_data = address;
	struct i2c_msg mpu6050_msg[2];

	/* 先写 */
	mpu6050_msg[0].addr = mpu6050_client->addr; // mpu6050 设备地址
	mpu6050_msg[0].flags = 0;					// 写指令
	mpu6050_msg[0].buf = &address_data;			// 写入的数据
	mpu6050_msg[0].len = 1;						// 数据长度

	/* 再读 */
	mpu6050_msg[1].addr = mpu6050_client->addr; // mpu6050 设备地址
	mpu6050_msg[1].flags = I2C_M_RD;			// 读指令
	mpu6050_msg[1].buf = data;					// 读取得到的数据保存位置
	mpu6050_msg[1].len = length;				// 读取长度

	error = i2c_transfer(mpu6050_client->adapter, mpu6050_msg, 2);
	if (error != 2) {
		printk("i2c_read_mpu6050 error\n");

		return -1;
	}

	return 0;
}

ssize_t mpu6050_dev_read(struct file *filp, char __user *buf, size_t cnt, loff_t *off)
{
	char data_H;
	char data_L;
	int error;
	short mpu6050_result[6]; // 保存 mpu6050 转换得到的原始数据

	// printk("\n mpu6050_read \n");

	i2c_read_mpu6050(mpu6050_client, ACCEL_XOUT_H, &data_H, 1);
	i2c_read_mpu6050(mpu6050_client, ACCEL_XOUT_L, &data_L, 1);
	mpu6050_result[0] = data_H << 8;
	mpu6050_result[0] += data_L;

	i2c_read_mpu6050(mpu6050_client, ACCEL_YOUT_H, &data_H, 1);
	i2c_read_mpu6050(mpu6050_client, ACCEL_YOUT_L, &data_L, 1);
	mpu6050_result[1] = data_H << 8;
	mpu6050_result[1] += data_L;

	i2c_read_mpu6050(mpu6050_client, ACCEL_ZOUT_H, &data_H, 1);
	i2c_read_mpu6050(mpu6050_client, ACCEL_ZOUT_L, &data_L, 1);
	mpu6050_result[2] = data_H << 8;
	mpu6050_result[2] += data_L;

	i2c_read_mpu6050(mpu6050_client, GYRO_XOUT_H, &data_H, 1);
	i2c_read_mpu6050(mpu6050_client, GYRO_XOUT_L, &data_L, 1);
	mpu6050_result[3] = data_H << 8;
	mpu6050_result[3] += data_L;

	i2c_read_mpu6050(mpu6050_client, GYRO_YOUT_H, &data_H, 1);
	i2c_read_mpu6050(mpu6050_client, GYRO_YOUT_L, &data_L, 1);
	mpu6050_result[4] = data_H << 8;
	mpu6050_result[4] += data_L;

	i2c_read_mpu6050(mpu6050_client, GYRO_ZOUT_H, &data_H, 1);
	i2c_read_mpu6050(mpu6050_client, GYRO_ZOUT_L, &data_L, 1);
	mpu6050_result[5] = data_H << 8;
	mpu6050_result[5] += data_L;

	// printk("AX=%d, AY=%d, AZ=%d \n",(int)mpu6050_result[0],(int)mpu6050_result[1],(int)mpu6050_result[2]);
	// printk("GX=%d, GY=%d, GZ=%d \n \n",(int)mpu6050_result[3],(int)mpu6050_result[4],(int)mpu6050_result[5]);

	/* 将读取得到的数据拷贝到用户空间 */
	error = copy_to_user(buf, mpu6050_result, cnt);
	if (error != 0) {
		printk("copy_to_user error!");
		return -1;
	}

	return 0;
}

static int i2c_write_mpu6050(struct i2c_client *mpu6050_client, u8 address, u8 data)
{
	int error = 0;
	u8 write_data[2];
	struct i2c_msg send_msg; // 要写入的数据结构体

	/* 设置要写入的数据 */
	write_data[0] = address; // 寄存器地址
	write_data[1] = data;    // 数据

	send_msg.addr = mpu6050_client->addr; // mpu6050 设备地址
	send_msg.flags = 0;         // 写指令
	send_msg.buf = write_data;  // 写入的数据
	send_msg.len = 2;           // 数据长度

	/* 执行写入 */
	error = i2c_transfer(mpu6050_client->adapter, &send_msg, 1);
	if (error != 1) {
		printk("i2c_transfer error\n");

		return -1;
	}

	return 0;
}

static int mpu6050_init(void)
{
	int error = 0;

	/* 配置 mpu6050 */
	error += i2c_write_mpu6050(mpu6050_client, PWR_MGMT_1, 0X00);
	error += i2c_write_mpu6050(mpu6050_client, SMPLRT_DIV, 0X07);
	error += i2c_write_mpu6050(mpu6050_client, CONFIG, 0X06);
	error += i2c_write_mpu6050(mpu6050_client, ACCEL_CONFIG, 0X01);
	if (error < 0) {
		/* 初始化失败 */
		printk("mpu6050_init error\n");
		return -1;
	}

	return 0;
}

int mpu6050_dev_open(struct inode *, struct file *)
{
	printk("mpu6050_dev_open()\n");

	/* 向 mpu6050 发送配置数据,让 mpu6050 处于正常工作状态 */
	mpu6050_init();

	return 0;
}

static const struct file_operations mpu6050_fops = {
	.open = mpu6050_dev_open,
	.read = mpu6050_dev_read,
};

int mpu6050_probe(struct i2c_client *client, const struct i2c_device_id *id)
{
	printk("mpu6050_probe()\n");

	printk("flags = 0x%x\n", client->flags);
	printk("addr = 0x%x\n", client->addr);
	printk("name = %s\n", client->name);
	printk("adapter name = %s\n", client->adapter->name);
	printk("dev init_name = %s\n", client->dev.init_name);

	major = register_chrdev(0, "mpu6050_1", &mpu6050_fops); // 注册字符设备

    mpu6050_client = client;

	return 0;
}

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

	unregister_chrdev(major, "mpu6050_1"); // 取消注册字符设备

	return 0;
}

/*定义设备树匹配表*/
static const struct of_device_id mpu6050_of_match_table[] = {
	{ .compatible = "lyj,mpu6050" },
	{},
};

struct i2c_driver mpu6050_driver = {
    .probe = mpu6050_probe,
    .remove = mpu6050_remove,
    .driver = {
		.name = "mpu6050_1",
		.of_match_table = mpu6050_of_match_table,
	},

};

static int __init mpu6050_driver_init(void)
{
	i2c_add_driver(&mpu6050_driver);

	return 0;
}

static void __exit mpu6050_driver_exit(void)
{
	i2c_del_driver(&mpu6050_driver);

	return;
}

module_init(mpu6050_driver_init);
module_exit(mpu6050_driver_exit);

MODULE_LICENSE("GPL");

test_app.c

c 复制代码
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>

int main(int argc, char *argv[])
{
	int error;
	short resive_data[6]; // 保存收到的 mpu6050 转换结果数据, 依次为 AX(x轴角度), AY, AZ; GX(x轴加速度), GY, GZ

	int fd = open(argv[1], O_RDWR);
	if (fd < 0) {
		printf("open file : %s failed !\n", argv[0]);
		return -1;
	}

	/*读取数据*/
	while (1) {
		error = read(fd, resive_data, 12);
		if (error < 0) {
			printf("read file error! \n");
		} else {
			printf("AX = %6d, AY = %6d, AZ = %6d", (int)resive_data[0], (int)resive_data[1], (int)resive_data[2]);
			printf("\t\tGX = %6d, GY = %6d, GZ = %6d\n", (int)resive_data[3], (int)resive_data[4], (int)resive_data[5]);
		}

		sleep(1);
	}

	close(fd);

	return 0;
}

Makefile

c 复制代码
obj-m = mpu6050.o

KDIR=/home/liyongjun/project/board/buildroot/RPi3/build/linux-custom
CROSS_COMPILE=/home/liyongjun/project/board/buildroot/RPi3/host/bin/arm-buildroot-linux-gnueabihf-
CC=${CROSS_COMPILE}gcc

all:
	make -C ${KDIR} M=${PWD} ARCH=arm CROSS_COMPILE=${CROSS_COMPILE} modules

test:
	${CC} test_app.c -o test_app.out -Wall

clean:
	make -C ${KDIR} M=${PWD} ARCH=arm CROSS_COMPILE=${CROSS_COMPILE} clean
bash 复制代码
# ./test_app.out /dev/mpu6050 
[ 8909.123645] mpu6050_dev_open()
AX = -12966, AY =   9260, AZ =  -7862		GX =  -1282, GY =     40, GZ =    193
AX = -12948, AY =   9264, AZ =  -7864		GX =  -1284, GY =     39, GZ =    193
AX = -12950, AY =   9284, AZ =  -7850		GX =  -1282, GY =     40, GZ =    192

四、类文件(/sys/class)

mpu6050.c

c 复制代码
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/i2c.h>
#include <linux/fs.h>
#include "mpu6050.h"

struct i2c_client *mpu6050_client;
static int major;
struct class *class_mpu6050;

static int i2c_read_mpu6050(struct i2c_client *mpu6050_client, u8 address, void *data, u32 length)
{
	int error = 0;
	u8 address_data = address;
	struct i2c_msg mpu6050_msg[2];

	/* 先写 */
	mpu6050_msg[0].addr = mpu6050_client->addr; // mpu6050 设备地址
	mpu6050_msg[0].flags = 0; // 写指令
	mpu6050_msg[0].buf = &address_data; // 写入的数据
	mpu6050_msg[0].len = 1; // 数据长度

	/* 再读 */
	mpu6050_msg[1].addr = mpu6050_client->addr; // mpu6050 设备地址
	mpu6050_msg[1].flags = I2C_M_RD; // 读指令
	mpu6050_msg[1].buf = data; // 读取得到的数据保存位置
	mpu6050_msg[1].len = length; // 读取长度

	error = i2c_transfer(mpu6050_client->adapter, mpu6050_msg, 2);
	if (error != 2) {
		printk("i2c_read_mpu6050 error\n");

		return -1;
	}

	return 0;
}

ssize_t mpu6050_dev_read(struct file *filp, char __user *buf, size_t cnt, loff_t *off)
{
	char data_H;
	char data_L;
	int error;
	short mpu6050_result[6]; // 保存 mpu6050 转换得到的原始数据

	// printk("\n mpu6050_read \n");

	i2c_read_mpu6050(mpu6050_client, ACCEL_XOUT_H, &data_H, 1);
	i2c_read_mpu6050(mpu6050_client, ACCEL_XOUT_L, &data_L, 1);
	mpu6050_result[0] = data_H << 8;
	mpu6050_result[0] += data_L;

	i2c_read_mpu6050(mpu6050_client, ACCEL_YOUT_H, &data_H, 1);
	i2c_read_mpu6050(mpu6050_client, ACCEL_YOUT_L, &data_L, 1);
	mpu6050_result[1] = data_H << 8;
	mpu6050_result[1] += data_L;

	i2c_read_mpu6050(mpu6050_client, ACCEL_ZOUT_H, &data_H, 1);
	i2c_read_mpu6050(mpu6050_client, ACCEL_ZOUT_L, &data_L, 1);
	mpu6050_result[2] = data_H << 8;
	mpu6050_result[2] += data_L;

	i2c_read_mpu6050(mpu6050_client, GYRO_XOUT_H, &data_H, 1);
	i2c_read_mpu6050(mpu6050_client, GYRO_XOUT_L, &data_L, 1);
	mpu6050_result[3] = data_H << 8;
	mpu6050_result[3] += data_L;

	i2c_read_mpu6050(mpu6050_client, GYRO_YOUT_H, &data_H, 1);
	i2c_read_mpu6050(mpu6050_client, GYRO_YOUT_L, &data_L, 1);
	mpu6050_result[4] = data_H << 8;
	mpu6050_result[4] += data_L;

	i2c_read_mpu6050(mpu6050_client, GYRO_ZOUT_H, &data_H, 1);
	i2c_read_mpu6050(mpu6050_client, GYRO_ZOUT_L, &data_L, 1);
	mpu6050_result[5] = data_H << 8;
	mpu6050_result[5] += data_L;

	// printk("AX=%d, AY=%d, AZ=%d \n",(int)mpu6050_result[0],(int)mpu6050_result[1],(int)mpu6050_result[2]);
	// printk("GX=%d, GY=%d, GZ=%d \n \n",(int)mpu6050_result[3],(int)mpu6050_result[4],(int)mpu6050_result[5]);

	/* 将读取得到的数据拷贝到用户空间 */
	error = copy_to_user(buf, mpu6050_result, cnt);
	if (error != 0) {
		printk("copy_to_user error!");
		return -1;
	}

	return 0;
}

static int i2c_write_mpu6050(struct i2c_client *mpu6050_client, u8 address, u8 data)
{
	int error = 0;
	u8 write_data[2];
	struct i2c_msg send_msg; // 要写入的数据结构体

	/* 设置要写入的数据 */
	write_data[0] = address; // 寄存器地址
	write_data[1] = data; // 数据

	send_msg.addr = mpu6050_client->addr; // mpu6050 设备地址
	send_msg.flags = 0; // 写指令
	send_msg.buf = write_data; // 写入的数据
	send_msg.len = 2; // 数据长度

	/* 执行写入 */
	error = i2c_transfer(mpu6050_client->adapter, &send_msg, 1);
	if (error != 1) {
		printk("i2c_transfer error\n");

		return -1;
	}

	return 0;
}

static int mpu6050_init(void)
{
	int error = 0;

	/* 配置 mpu6050 */
	error += i2c_write_mpu6050(mpu6050_client, PWR_MGMT_1, 0X00);
	error += i2c_write_mpu6050(mpu6050_client, SMPLRT_DIV, 0X07);
	error += i2c_write_mpu6050(mpu6050_client, CONFIG, 0X06);
	error += i2c_write_mpu6050(mpu6050_client, ACCEL_CONFIG, 0X01);
	if (error < 0) {
		/* 初始化失败 */
		printk("mpu6050_init error\n");
		return -1;
	}

	return 0;
}

int mpu6050_dev_open(struct inode *, struct file *)
{
	printk("mpu6050_dev_open()\n");

	/* 向 mpu6050 发送配置数据,让 mpu6050 处于正常工作状态 */
	mpu6050_init();

	return 0;
}

static const struct file_operations mpu6050_fops = {
	.open = mpu6050_dev_open,
	.read = mpu6050_dev_read,
};

int mpu6050_probe(struct i2c_client *client, const struct i2c_device_id *id)
{
	printk("mpu6050_probe()\n");

	printk("flags = 0x%x\n", client->flags);
	printk("addr = 0x%x\n", client->addr);
	printk("name = %s\n", client->name);
	printk("adapter name = %s\n", client->adapter->name);
	printk("dev init_name = %s\n", client->dev.init_name);

	major = register_chrdev(0, "mpu6050_1", &mpu6050_fops); // 注册字符设备
	class_mpu6050 = class_create(THIS_MODULE, "mpu6050_2"); // 创建类

	mpu6050_client = client;

	return 0;
}

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

	unregister_chrdev(major, "mpu6050_1"); // 取消注册字符设备
	class_destroy(class_mpu6050); // 删除类

	return 0;
}

/*定义设备树匹配表*/
static const struct of_device_id mpu6050_of_match_table[] = {
	{ .compatible = "lyj,mpu6050" },
	{},
};

struct i2c_driver mpu6050_driver = {
    .probe = mpu6050_probe,
    .remove = mpu6050_remove,
    .driver = {
		.name = "mpu6050_1",
		.of_match_table = mpu6050_of_match_table,
	},

};

static int __init mpu6050_driver_init(void)
{
	i2c_add_driver(&mpu6050_driver);

	return 0;
}

static void __exit mpu6050_driver_exit(void)
{
	i2c_del_driver(&mpu6050_driver);

	return;
}

module_init(mpu6050_driver_init);
module_exit(mpu6050_driver_exit);

MODULE_LICENSE("GPL");
bash 复制代码
# ls /sys/class/mpu6050_2/

五、自动创建设备文件(/dev/mpu6050_3)

mpu6050.c

c 复制代码
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/i2c.h>
#include <linux/fs.h>
#include "mpu6050.h"

struct i2c_client *mpu6050_client;
static int major;
struct class *class_mpu6050;

static int i2c_read_mpu6050(struct i2c_client *mpu6050_client, u8 address, void *data, u32 length)
{
	int error = 0;
	u8 address_data = address;
	struct i2c_msg mpu6050_msg[2];

	/* 先写 */
	mpu6050_msg[0].addr = mpu6050_client->addr; // mpu6050 设备地址
	mpu6050_msg[0].flags = 0; // 写指令
	mpu6050_msg[0].buf = &address_data; // 写入的数据
	mpu6050_msg[0].len = 1; // 数据长度

	/* 再读 */
	mpu6050_msg[1].addr = mpu6050_client->addr; // mpu6050 设备地址
	mpu6050_msg[1].flags = I2C_M_RD; // 读指令
	mpu6050_msg[1].buf = data; // 读取得到的数据保存位置
	mpu6050_msg[1].len = length; // 读取长度

	error = i2c_transfer(mpu6050_client->adapter, mpu6050_msg, 2);
	if (error != 2) {
		printk("i2c_read_mpu6050 error\n");

		return -1;
	}

	return 0;
}

ssize_t mpu6050_dev_read(struct file *filp, char __user *buf, size_t cnt, loff_t *off)
{
	char data_H;
	char data_L;
	int error;
	short mpu6050_result[6]; // 保存 mpu6050 转换得到的原始数据

	// printk("\n mpu6050_read \n");

	i2c_read_mpu6050(mpu6050_client, ACCEL_XOUT_H, &data_H, 1);
	i2c_read_mpu6050(mpu6050_client, ACCEL_XOUT_L, &data_L, 1);
	mpu6050_result[0] = data_H << 8;
	mpu6050_result[0] += data_L;

	i2c_read_mpu6050(mpu6050_client, ACCEL_YOUT_H, &data_H, 1);
	i2c_read_mpu6050(mpu6050_client, ACCEL_YOUT_L, &data_L, 1);
	mpu6050_result[1] = data_H << 8;
	mpu6050_result[1] += data_L;

	i2c_read_mpu6050(mpu6050_client, ACCEL_ZOUT_H, &data_H, 1);
	i2c_read_mpu6050(mpu6050_client, ACCEL_ZOUT_L, &data_L, 1);
	mpu6050_result[2] = data_H << 8;
	mpu6050_result[2] += data_L;

	i2c_read_mpu6050(mpu6050_client, GYRO_XOUT_H, &data_H, 1);
	i2c_read_mpu6050(mpu6050_client, GYRO_XOUT_L, &data_L, 1);
	mpu6050_result[3] = data_H << 8;
	mpu6050_result[3] += data_L;

	i2c_read_mpu6050(mpu6050_client, GYRO_YOUT_H, &data_H, 1);
	i2c_read_mpu6050(mpu6050_client, GYRO_YOUT_L, &data_L, 1);
	mpu6050_result[4] = data_H << 8;
	mpu6050_result[4] += data_L;

	i2c_read_mpu6050(mpu6050_client, GYRO_ZOUT_H, &data_H, 1);
	i2c_read_mpu6050(mpu6050_client, GYRO_ZOUT_L, &data_L, 1);
	mpu6050_result[5] = data_H << 8;
	mpu6050_result[5] += data_L;

	// printk("AX=%d, AY=%d, AZ=%d \n",(int)mpu6050_result[0],(int)mpu6050_result[1],(int)mpu6050_result[2]);
	// printk("GX=%d, GY=%d, GZ=%d \n \n",(int)mpu6050_result[3],(int)mpu6050_result[4],(int)mpu6050_result[5]);

	/* 将读取得到的数据拷贝到用户空间 */
	error = copy_to_user(buf, mpu6050_result, cnt);
	if (error != 0) {
		printk("copy_to_user error!");
		return -1;
	}

	return 0;
}

static int i2c_write_mpu6050(struct i2c_client *mpu6050_client, u8 address, u8 data)
{
	int error = 0;
	u8 write_data[2];
	struct i2c_msg send_msg; // 要写入的数据结构体

	/* 设置要写入的数据 */
	write_data[0] = address; // 寄存器地址
	write_data[1] = data; // 数据

	send_msg.addr = mpu6050_client->addr; // mpu6050 设备地址
	send_msg.flags = 0; // 写指令
	send_msg.buf = write_data; // 写入的数据
	send_msg.len = 2; // 数据长度

	/* 执行写入 */
	error = i2c_transfer(mpu6050_client->adapter, &send_msg, 1);
	if (error != 1) {
		printk("i2c_transfer error\n");

		return -1;
	}

	return 0;
}

static int mpu6050_init(void)
{
	int error = 0;

	/* 配置 mpu6050 */
	error += i2c_write_mpu6050(mpu6050_client, PWR_MGMT_1, 0X00);
	error += i2c_write_mpu6050(mpu6050_client, SMPLRT_DIV, 0X07);
	error += i2c_write_mpu6050(mpu6050_client, CONFIG, 0X06);
	error += i2c_write_mpu6050(mpu6050_client, ACCEL_CONFIG, 0X01);
	if (error < 0) {
		/* 初始化失败 */
		printk("mpu6050_init error\n");
		return -1;
	}

	return 0;
}

int mpu6050_dev_open(struct inode *, struct file *)
{
	printk("mpu6050_dev_open()\n");

	/* 向 mpu6050 发送配置数据,让 mpu6050 处于正常工作状态 */
	mpu6050_init();

	return 0;
}

static const struct file_operations mpu6050_fops = {
	.open = mpu6050_dev_open,
	.read = mpu6050_dev_read,
};

int mpu6050_probe(struct i2c_client *client, const struct i2c_device_id *id)
{
	printk("mpu6050_probe()\n");

	printk("flags = 0x%x\n", client->flags);
	printk("addr = 0x%x\n", client->addr);
	printk("name = %s\n", client->name);
	printk("adapter name = %s\n", client->adapter->name);
	printk("dev init_name = %s\n", client->dev.init_name);

	major = register_chrdev(0, "mpu6050_1", &mpu6050_fops); // 注册字符设备
	class_mpu6050 = class_create(THIS_MODULE, "mpu6050_2"); // 创建类
	device_create(class_mpu6050, NULL, MKDEV(major, 0), NULL, "mpu6050_3"); // 创建设备 /dev/mpu6050_3

	mpu6050_client = client;

	return 0;
}

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

	unregister_chrdev(major, "mpu6050_1"); // 取消注册字符设备
	class_destroy(class_mpu6050); // 删除类
	device_destroy(class_mpu6050, MKDEV(major, 0)); // 删除设备 /dev/mpu6050_3

	return 0;
}

/*定义设备树匹配表*/
static const struct of_device_id mpu6050_of_match_table[] = {
	{ .compatible = "lyj,mpu6050" },
	{},
};

struct i2c_driver mpu6050_driver = {
    .probe = mpu6050_probe,
    .remove = mpu6050_remove,
    .driver = {
		.name = "mpu6050_1",
		.of_match_table = mpu6050_of_match_table,
	},

};

static int __init mpu6050_driver_init(void)
{
	i2c_add_driver(&mpu6050_driver);

	return 0;
}

static void __exit mpu6050_driver_exit(void)
{
	i2c_del_driver(&mpu6050_driver);

	return;
}

module_init(mpu6050_driver_init);
module_exit(mpu6050_driver_exit);

MODULE_LICENSE("GPL");
bash 复制代码
# ls /dev/mpu6050_3 -lh
crw-------    1 root     root      243,   0 Jan  1 02:47 /dev/mpu6050_3
#
# ./test_app.out /dev/mpu6050_3 
[10287.394410] mpu6050_dev_open()
AX = -12956, AY =   9256, AZ =  -7830		GX =  -1281, GY =     39, GZ =    189
AX = -12952, AY =   9258, AZ =  -7848		GX =  -1282, GY =     36, GZ =    191
AX = -12954, AY =   9280, AZ =  -7840		GX =  -1282, GY =     34, GZ =    192

总结

  • 内核将设备信息传递给 probe(),设备信息包括 i2c 设备的设备地址、i2c 设备所属的控制器。
    这样,驱动就可以操作控制器,向对应的 i2c 设备读写数据。
  • 每一个主设备号(major) 对应 /proc/devices 下的一个设备,使用 register_chrdev() 创建
  • 每一个次设备号(minor) 对应 /dev/ 下的一个设备文件,使用 mknod 或 device_create() 创建
  • 使用 class_create() 在 /sys/class/ 下面创建一个类,
    创建一个类不需要依赖特别的参数,可以理解为在内核任意处,使用任意名称,可以创建一个类
  • 使用 device_create() 可以在任意类下创建一个设备文件,会在 /sys/class/类/ 和 /dev/ 下均生成一个文件
    也就是说,一个设备可以随意放入任何一个类中。不过,设备是和驱动有关系的,因为要填入一个参数 MKDEV(major, 0)
  • 使用 device_create() 在 /dev/ 目录下创建一个设备的前提是先在 /sys/class/ 下先创建一个类(内核设计这样的架构是出于什么目的?面向对象?一个对象必须归属一个类?)
    使用 mknod 命令手动创建一个设备,不需要先创建一个类。
相关推荐
pk_xz1234561 小时前
Shell 脚本中变量和字符串的入门介绍
linux·运维·服务器
小珑也要变强2 小时前
Linux之sed命令详解
linux·运维·服务器
Lary_Rock4 小时前
RK3576 LINUX RKNN SDK 测试
linux·运维·服务器
云飞云共享云桌面5 小时前
8位机械工程师如何共享一台图形工作站算力?
linux·服务器·网络
Peter_chq6 小时前
【操作系统】基于环形队列的生产消费模型
linux·c语言·开发语言·c++·后端
一坨阿亮7 小时前
Linux 使用中的问题
linux·运维
dsywws8 小时前
Linux学习笔记之vim入门
linux·笔记·学习
幺零九零零9 小时前
【C++】socket套接字编程
linux·服务器·网络·c++
小林熬夜学编程10 小时前
【Linux系统编程】第四十一弹---线程深度解析:从地址空间到多线程实践
linux·c语言·开发语言·c++·算法
程思扬11 小时前
为什么Uptime+Kuma本地部署与远程使用是网站监控新选择?
linux·服务器·网络·经验分享·后端·网络协议·1024程序员节