文章目录
-
- 环境
- 一、添加驱动(/sys/bus/i2c/drivers/mpu6050_1)
- 驱动和设备树扯上关系
- [二、注册一个(种/类?)字符设备(/proc/devices,243 mpu6050_1)](#二、注册一个(种/类?)字符设备(/proc/devices,243 mpu6050_1))
- [三、手动创建一个字符设备(mknod /dev/mpu6050 c 243 0)](#三、手动创建一个字符设备(mknod /dev/mpu6050 c 243 0))
- 提供操作函数
- 四、类文件(/sys/class)
- 五、自动创建设备文件(/dev/mpu6050_3)
- 总结
环境
树莓派 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 命令手动创建一个设备,不需要先创建一个类。