字符设备驱动编写

文章目录

环境

树莓派 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 命令手动创建一个设备,不需要先创建一个类。
相关推荐
xq5148631 小时前
Linux系统下安装mongodb
linux·mongodb
柒七爱吃麻辣烫1 小时前
在Linux中安装JDK并且搭建Java环境
java·linux·开发语言
孤寂大仙v2 小时前
【Linux笔记】——进程信号的产生
linux·服务器·笔记
深海蜗牛2 小时前
Jenkins linux安装
linux·jenkins
愚戏师2 小时前
Linux复习笔记(三) 网络服务配置(web)
linux·运维·笔记
JANYI20182 小时前
嵌入式MCU和Linux开发哪个好?
linux·单片机·嵌入式硬件
熊大如如3 小时前
Java NIO 文件处理接口
java·linux·nio
晚秋大魔王3 小时前
OpenHarmony 开源鸿蒙南向开发——linux下使用make交叉编译第三方库——nettle库
linux·开源·harmonyos
农民小飞侠3 小时前
ubuntu 24.04 error: cannot uninstall blinker 1.7.0, record file not found. hint
linux·运维·ubuntu
某不知名網友3 小时前
Linux 软硬连接详解
linux·运维·服务器