文章目录
- 环境
- 一、添加驱动(/sys/bus/i2c/drivers/mpu6050_1)
- 驱动和设备树扯上关系
- 二、注册一个(种/类?)字符设备(/proc/devices,243 mpu6050_1)
- 三、手动创建一个字符设备(mknod /dev/mpu6050 c 243 0)
- 提供操作函数
- 四、类文件(/sys/class)
- 五、自动创建设备文件(/dev/mpu6050_3)
- 总结
环境
树莓派 3b+
mpu6050 模块
插在物理 3、5 引脚
一、添加驱动(/sys/bus/i2c/drivers/mpu6050_1)
mpu6050.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
obj-m = mpu6050.oKDIR=/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} modulesclean:make -C ${KDIR} M=${PWD} ARCH=arm CROSS_COMPILE=${CROSS_COMPILE} clean
使用 i2c_add_driver() 向 i2c 总线上添加一个驱动,这个驱动没有任何功能,只有一个驱动名 “mpu6050_1”。
可以在 /sys/bus/i2c/drivers 目录下查看到该驱动。
# 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
&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
#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。
# 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() 函数
# rmmod mpu6050
[ 6416.835182] mpu6050_remove()
二、注册一个(种/类?)字符设备(/proc/devices,243 mpu6050_1)
mpu6050.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");
# 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)
# 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
#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
#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, GZint 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
obj-m = mpu6050.oKDIR=/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}gccall:make -C ${KDIR} M=${PWD} ARCH=arm CROSS_COMPILE=${CROSS_COMPILE} modulestest:${CC} test_app.c -o test_app.out -Wallclean:make -C ${KDIR} M=${PWD} ARCH=arm CROSS_COMPILE=${CROSS_COMPILE} clean
# ./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
#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");
# ls /sys/class/mpu6050_2/
五、自动创建设备文件(/dev/mpu6050_3)
mpu6050.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_3mpu6050_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_3return 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");
# 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 命令手动创建一个设备,不需要先创建一个类。