目录
IIC核心函数
i2c_add_adapter()
i2c_add_driver()宏
i2c_transfer()
i2c_master_send()
i2c_master_recv()
i2c_transfer_buffer_flags()
万能的i2c驱动(i2c-dev.c文件)
i2c_dev_init()
i2cdev_attach_adapter()
i2cdev_fops结构体
i2cdev_open()
i2cdev_read()
i2cdev_write()
IIC驱动实验:读取mpu6050数据
mpu6050
添加设备树iomuxc子节点
I2C1子节点
设备树插入编译加载
i2c_mpu6050.c文件
App.c文件
Makefile文件
执行过程
IIC核心函数
i2c_add_adapter()
该函数存放在内核/drivers/i2c/i2c-core-base.c文件。注册一个i2c适配器。
/*
* 注册一个i2c适配器
* adapter->nr:适配器的编号
* adapter:i2c物理控制器对应的适配器
*/
// 由kernel分配适配器的编号
int i2c_add_adapter(struct i2c_adapter *adapter)
// 自己指定适配器的编号
int i2c_add_numbered_adapter(struct i2c_adapter *adapter)
/*
* 返回值:
* 成功:0
* 失败:负值
*/
i2c_add_driver()宏
该函数存放在内核/include/linux/i2c.h文件。注册一个i2c驱动。
// struct i2c_driver
#define i2c_add_driver(driver) \ i2c_register_driver(THIS_MODULE, driver)
/*
* 存放在内核/drivers/i2c/i2c-core-base.c文件
* 注册一个i2c驱动
* owner:一般为THIS_MODULE
* driver:要注册的i2c_driver,对应的驱动结构体由我们自己来实现
*/
int i2c_register_driver(struct module *owner, struct i2c_driver *driver);
/*
* 返回值:
* 成功:0
* 失败:负值
*/
i2c_transfer()
该函数存放在内核/drivers/i2c/i2c-core-base.c文件。进行iic消息收发(可收可发)。
该函数的核心是:adap → algo → master_xfer(adap, msgs, num),也就是适配器的具体通信方法。
/*
* 收发消息都要用到i2c_adapter适配器
* adap:所使用的的I2C适配器,i2c_client会保存其对应的i2c_adapter
* msgs:i2c要发生的一个或多个消息,每个消息对应一个消息结构体
* num:消息数量(即msgs的数量)
*/
int i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num);
/*
* 返回值:
* 成功:发送的msgs的数量
* 失败:负值
*/
/* 该结构体存放在内核/include/uapi/linux/i2c.h文件 */
struct i2c_msg
{
__u16 addr; // 从机地址,发送到哪个从设备地址
__u16 flags; // 读写等其他特性,见下面的宏
// 此宏表示读,0表示写
#define I2C_M_RD 0x0001 /* read data, from slave to master */
/* I2C_M_RD is guaranteed to be 0x0001! */
#define I2C_M_TEN 0x0010 /* this is a ten bit chip address */
#define I2C_M_DMA_SAFE 0x0200 /* the buffer of this message is DMA safe */
/* makes only sense in kernelspace */
/* userspace buffers are copied anyway */
#define I2C_M_RECV_LEN 0x0400 /* length will be first received byte */
#define I2C_M_NO_RD_ACK 0x0800 /* if I2C_FUNC_PROTOCOL_MANGLING */
#define I2C_M_IGNORE_NAK 0x1000 /* if I2C_FUNC_PROTOCOL_MANGLING */
#define I2C_M_REV_DIR_ADDR 0x2000 /* if I2C_FUNC_PROTOCOL_MANGLING */
#define I2C_M_NOSTART 0x4000 /* if I2C_FUNC_NOSTART */
#define I2C_M_STOP 0x8000 /* if I2C_FUNC_PROTOCOL_MANGLING */
__u16 len; // 消息数据的长度
__u8 *buf; // 发送或接收的消息缓冲区
};
i2c_master_send()
该函数存放在内核/include/linux/i2c.h文件。发送一个i2c消息(只能是一个)。其实最终调用的还是i2c_transfer()。
static inline int i2c_master_send(const struct i2c_client *client,
const char *buf, int count)
{
return i2c_transfer_buffer_flags(client, (char *)buf, count, 0);
};
i2c_master_recv()
该函数存放在内核/include/linux/i2c.h文件。接收一个i2c消息(只能是一个)。其实最终调用的还是i2c_transfer()。
static inline int i2c_master_recv(const struct i2c_client *client,
char *buf, int count)
{
return i2c_transfer_buffer_flags(client, buf, count, I2C_M_RD);
};
i2c_transfer_buffer_flags()
该函数存放在内核/drivers/i2c/i2c-core-base.c文件。发送一个i2c消息。其实最终调用的还是i2c_transfer()。
int i2c_transfer_buffer_flags(const struct i2c_client *client, char *buf, int count, u16 flags)
{
int ret;
// 根据传入参数构建消息结构体,此结构体定义详见上
struct i2c_msg msg =
{
.addr = client->addr,
.flags = flags | (client->flags & I2C_M_TEN),
.len = count,
.buf = buf,
};
ret = i2c_transfer(client->adapter, &msg, 1);
/*
* If everything went ok (i.e. 1 msg transferred), return #bytes
* transferred, else error code.
*/
return (ret == 1) ? count : ret;
}
万能的i2c驱动(i2c-dev.c文件)
文件存放于内核/drivers/i2c/。
内核集成i2c_dev驱动模块,开机自动加载
为每个i2c_adapter生成一个设备文件,通过该设备文件间接使用i2c核心函数收发数据
注册i2c总线的通知函数,解决加载顺序问题
i2c_dev_init()
#define I2C_MAJOR 89 /* Device major number */
#define MINORBITS 20
#define MINORMASK ((1U << MINORBITS) - 1)
#define I2C_MINORS MINORMASK
static int __init i2c_dev_init(void)
{
int res;
printk(KERN_INFO "i2c /dev entries driver\n");
// 申请设备号,I2C_MAJOR为89,次设备号为0,I2C_MINORS为1<<20-1,表示次设备号的数量。
// 就是把这个主设备号对应的次设备号都申请了。
res = register_chrdev_region(MKDEV(I2C_MAJOR, 0), I2C_MINORS, "i2c");
if (res)
goto out;
// 创建一个同名类,在 /sys/class中可以看到
i2c_dev_class = class_create(THIS_MODULE, "i2c-dev");
if (IS_ERR(i2c_dev_class)) {
res = PTR_ERR(i2c_dev_class);
goto out_unreg_chrdev;
}
...
// 注册i2c总线的通知函数
// 参数2详见下
res = bus_register_notifier(&i2c_bus_type, &i2cdev_notifier);
if (res)
goto out_unreg_class;
/* Bind to already existing adapters right away */
// 遍历i2c总线上的所有设备,每次都执行第二个参数对应的函数
i2c_for_each_dev(NULL, i2cdev_attach_adapter);
return 0;
out_unreg_class:
class_destroy(i2c_dev_class);
out_unreg_chrdev:
unregister_chrdev_region(MKDEV(I2C_MAJOR, 0), I2C_MINORS);
out:
printk(KERN_ERR "%s: Driver Initialisation failed\n", __FILE__);
return res;
}
static struct notifier_block i2cdev_notifier =
{
// 详见下
.notifier_call = i2cdev_notifier_call,
};
static int i2cdev_notifier_call(struct notifier_block *nb, unsigned long action,
void *data)
{
struct device *dev = data;
// 发生的事件类型
switch (action) {
// 此i2c总线下发生添加设备事件
case BUS_NOTIFY_ADD_DEVICE:
// 创建设备文件之类的操作
return i2cdev_attach_adapter(dev, NULL);
// 此i2c总线下发生删除设备事件
case BUS_NOTIFY_DEL_DEVICE:
return i2cdev_detach_adapter(dev, NULL);
}
return 0;
}
int i2c_for_each_dev(void *data, int (*fn)(struct device *, void *))
{
int res;
mutex_lock(&core_lock);
res = bus_for_each_dev(&i2c_bus_type, NULL, data, fn);
mutex_unlock(&core_lock);
return res;
}
int bus_for_each_dev(struct bus_type *bus, struct device *start,
void *data, int (*fn)(struct device *, void *))
{
struct klist_iter i;
struct device *dev;
int error = 0;
if (!bus || !bus->p)
return -EINVAL;
klist_iter_init_node(&bus->p->klist_devices, &i,
(start ? &start->p->knode_bus : NULL));
while (!error && (dev = next_device(&i)))
error = fn(dev, data);
klist_iter_exit(&i);
return error;
}
i2cdev_attach_adapter()
i2c总线添加设备后回调执行的函数。
static int i2cdev_attach_adapter(struct device *dev, void *dummy)
{
struct i2c_adapter *adap;
struct i2c_dev *i2c_dev;
int res;
// 若设备类型不是i2c适配器,直接返回
// 也有可能是 i2c_client 设备
if (dev->type != &i2c_adapter_type)
return 0;
// 从i2c设备结构体中获取i2c适配器结构体
adap = to_i2c_adapter(dev);
// 分配内存
i2c_dev = get_free_i2c_dev(adap);
if (IS_ERR(i2c_dev))
return PTR_ERR(i2c_dev);
// 设置文件操作接口
cdev_init(&i2c_dev->cdev, &i2cdev_fops);
i2c_dev->cdev.owner = THIS_MODULE;
// 注意次设备号为适配器对应的编号,可以自己指定,也可以有系统分配
// 参数3为次设备号的数量,此fops只对应次设备号的文件
res = cdev_add(&i2c_dev->cdev, MKDEV(I2C_MAJOR, adap->nr), 1);
if (res)
goto error_cdev;
/* register this i2c device with the driver core */
// 在/sys下,在i2c_dev_class类(参数1)下创建一个新的目录, "i2c-适配器编号"就是目录名
// 然后会通知用户空间的uevent守护进程,此守护进程会创建一个同参数4名的设备文件
i2c_dev->dev = device_create(i2c_dev_class, &adap->dev,
MKDEV(I2C_MAJOR, adap->nr), NULL,
"i2c-%d", adap->nr);
if (IS_ERR(i2c_dev->dev)) {
res = PTR_ERR(i2c_dev->dev);
goto error;
}
pr_debug("i2c-dev: adapter [%s] registered as minor %d\n",
adap->name, adap->nr);
return 0;
error:
cdev_del(&i2c_dev->cdev);
error_cdev:
put_i2c_dev(i2c_dev);
return res;
}
i2cdev_fops结构体
static const struct file_operations i2cdev_fops =
{
.owner = THIS_MODULE,
.llseek = no_llseek,
.read = i2cdev_read,
.write = i2cdev_write,
.unlocked_ioctl = i2cdev_ioctl,
.compat_ioctl = compat_i2cdev_ioctl,
.open = i2cdev_open,
.release = i2cdev_release,
};
i2cdev_open()
static int i2cdev_open(struct inode *inode, struct file *file)
{
// 从inode获取次设备号,就是适配器的编号
unsigned int minor = iminor(inode);
struct i2c_client *client;
struct i2c_adapter *adap;
// 根据次设备号从i2c总线获取对应的适配器
adap = i2c_get_adapter(minor);
if (!adap)
return -ENODEV;
// client表示某个具体的i2c设备,为其分配内存
client = kzalloc(sizeof(*client), GFP_KERNEL);
if (!client) {
i2c_put_adapter(adap);
return -ENOMEM;
}
// 设置具体的i2c设备的名字
snprintf(client->name, I2C_NAME_SIZE, "i2c-dev %d", adap->nr);
// 设置具体的i2c设备归属的适配器
client->adapter = adap;
// 以后可以通过file的此成员获取client指针
file->private_data = client;
return 0;
}
i2cdev_read()
static ssize_t i2cdev_read(struct file *file, char __user *buf, size_t count,
loff_t *offset)
{
char *tmp;
int ret;
struct i2c_client *client = file->private_data;
if (count > 8192)
count = 8192;
// 分配内存,用于接收消息
tmp = kmalloc(count, GFP_KERNEL);
if (tmp == NULL)
return -ENOMEM;
pr_debug("i2c-dev: i2c-%d reading %zu bytes.\n", iminor(file_inode(file)), count);
// 核心函数,接受一个i2c消息
ret = i2c_master_recv(client, tmp, count);
if (ret >= 0)
ret = copy_to_user(buf, tmp, count) ? -EFAULT : ret;
kfree(tmp);
return ret;
}
i2cdev_write()
static ssize_t i2cdev_write(struct file *file, const char __user *buf,
size_t count, loff_t *offset)
{
int ret;
char *tmp;
struct i2c_client *client = file->private_data;
if (count > 8192)
count = 8192;
把用户空间的buf拷贝内核空间的tmp
tmp = memdup_user(buf, count);
if (IS_ERR(tmp))
return PTR_ERR(tmp);
pr_debug("i2c-dev: i2c-%d writing %zu bytes.\n", iminor(file_inode(file)), count);
// 核心函数,发送一个i2c消息
ret = i2c_master_send(client, tmp, count);
kfree(tmp);
return ret;
}
IIC驱动实验:读取mpu6050数据
mpu6050
空间运动传感器芯片:3轴加速度、3轴角速度。
添加设备树iomuxc子节点
采用传统设备树的方法,不使用插件设备树。设备树文件中添加新的引脚组。
pinctrl_i2c1: i2c1grp {
// 添加i2c控制器的两个引脚,对应的pin复用为i2c1控制器的相关引脚
fsl,pins = <
MX6UL_PAD_UART4_TX_DATA__I2C1_SCL 0x4001b8b0
MX6UL_PAD_UART4_RX_DATA__I2C1_SDA 0x4001b8b0
>;
};
I2C1子节点
&i2c1
{
// 设置i2c1控制器的时钟频率,100k
clock-frequency = <100000>;
// 声明设备节点所使用的的引脚状态(pinctrl-0),使用pinctrl_i2c1引脚组
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_i2c1>;
status = "okay";
// i2c1节点下的每个子节点,在kernel加载时都会被解析成一个i2c_client结构体
i2c_mpu6050@68
{
compatible = "fire,i2c_mpu6050";
// 会被转换成i2c设备地址
reg = <0x68>;
status = "okay";
};
};
设备树插入编译加载
虚拟机:
重新编译设备树:make ARCH=arm -j4 CROSS_COMPILE=arm-linux-gnueabihf- dtbs
然后拷贝到共享文件夹:sudo cp 内核/arch/arm/boot/dts/imx6ull-mmc-npi.dtb /home/couvrir/桌面/sharedir
开发板:
替代旧的二进制设备树:sudo cp /mnt/imx6ull-mmc-npi.dtb /usr/lib/linux-image-4.19.35-imx6/imx6ull-mmc-npi.dtb
同步缓冲区:sync
重启设备:sudo reboot
i2c_mpu6050.c文件
#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/uaccess.h>
#include <linux/i2c.h>
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/ide.h>
#include <linux/errno.h>
#include <linux/gpio.h>
#include <linux/device.h>
#include <linux/platform_device.h>
#include <asm/mach/map.h>
#include <asm/io.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_gpio.h>
#define SMPLRT_DIV 0X19 /* Sample Rate Divider:采样速率分频器 */
#define CONFIG 0X1A /* 配置:EXT_SYNC_SET(外部帧同步)+DLPF_CFG(数字低通滤波器) */
#define GYRO_CONFIG 0X1B /* 陀螺仪配置 */
#define ACCEL_CONFIG 0X1C /* 加速度计配置 */
#define ACCEL_XOUT_H 0X3B
#define ACCEL_XOUT_L 0X3C
#define ACCEL_YOUT_H 0X3D
#define ACCEL_YOUT_L 0X3E
#define ACCEL_ZOUT_H 0X3F
#define ACCEL_ZOUT_L 0X40
#define TEMP_OUT_H 0X41 /* 温度测量 */
#define TEMP_OUT_L 0X42 /* 温度测量 */
#define GYRO_XOUT_H 0X43
#define GYRO_XOUT_L 0X44
#define GYRO_YOUT_H 0X45
#define GYRO_YOUT_L 0X46
#define GYRO_ZOUT_H 0X47
#define GYRO_ZOUT_L 0X48
#define PWR_MGMT_1 0X6B /* Power Management:电源管理 */
#define WHO_AM_I 0X75 /* 7位I2C地址的高6位 */
#define DEV_NAME "I2C1_mpu6050"
#define DEV_CNT (1)
static dev_t mpu6050_devno;
static struct cdev mpu6050_chrdev;
struct class *class_mpu6050;
struct device *device_mpu6050;
struct device_node *mpu6050_device_node;
/* 保存mpu6050设备对应的i2c_client结构体,匹配成功后由.probe函数传入参数 */
struct i2c_client *mpu6050_client = NULL;
/*
* 通过i2c向mpu6050写入数据
* mpu6050_client:mpu6050的i2c_client结构体
* address:数据要写入的地址
* data:要写入的数据
*/
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在i2c总线上的地址
send_msg.flags = 0; //标记为发送数据
send_msg.buf = write_data; //写入的首地址
send_msg.len = 2; //reg长度
error = i2c_transfer(mpu6050_client->adapter, &send_msg, 1);
if(error != 1){
printk(KERN_DEBUG "i2c_transfer error\n");
return -1;
}
return 0;
}
/*
* 通过i2c向mpu6050读取数据
* mpu6050_client:mpu6050的i2c_client结构体
* address:数据要读取的地址
* data:保存读取的数据
* length:读取到的数据长度
*/
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 send_msg[2];
send_msg[0].addr = mpu6050_client->addr; //mpu6050在i2c总线上的地址
send_msg[0].flags = 0; //标记为发送数据
send_msg[0].buf = &address_data; //写入的首地址
send_msg[0].len = 1; //写入长度
send_msg[1].addr = mpu6050_client->addr; //mpu6050在i2c总线上的地址
send_msg[1].flags = I2C_M_RD; //标记为读取数据
send_msg[1].buf = data; //保存读取数据
send_msg[1].len = length; //读取长度
error = i2c_transfer(mpu6050_client->adapter, send_msg, 2);
if(error != 2){
printk(KERN_DEBUG "i2c_transfer error\n");
return -1;
}
return 0;
}
static int mpu6050_init(void)
{
int error = 0;
/* 配置mpu6050电源管理,0x00, 解除休眠状态 */
error += i2c_write_mpu6050(mpu6050_client, PWR_MGMT_1, 0X00);
/* 设置mpu6050的采样率:1k/(1+7)= 125kz */
error += i2c_write_mpu6050(mpu6050_client, SMPLRT_DIV, 0X07);
/* 设置数字低通滤波器频率5hz和帧同步引脚采样 */
error += i2c_write_mpu6050(mpu6050_client, CONFIG, 0X06);
/* 加速度计不自检,输出满量程范围:±2g */
error += i2c_write_mpu6050(mpu6050_client, ACCEL_CONFIG, 0X01);
if(error < 0){
printk(KERN_DEBUG "mpu6050_init error\n");
return -1;
}
return 0;
}
static int mpu6050_open(struct inode *inode, struct file *filp)
{
/* 向mpu6050发送配置数据,让mpu6050处于正常状态 */
mpu6050_init();
return 0;
}
static ssize_t mpu6050_read(struct file *filp, char __user *buf, size_t cnt, loff_t *off)
{
char data_H, data_L;
int error;
short mpu6050_result[6]; //保存mpu6050转换的原始数据
/* 读取3轴加速度原始值 */
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;
/* 读取3轴角速度原始值 */
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;
/* 将读取的数据拷贝到用户空间 */
error = copy_to_user(buf, mpu6050_result, cnt);
if(error != 0){
printk("copy_to_user error!\n");
return -1;
}
return 0;
}
static int mpu6050_release(struct inode *inode, struct file *filp)
{
return 0;
}
static struct file_operations mpu6050_chrdev_fops = {
.owner = THIS_MODULE,
.open = mpu6050_open,
.read = mpu6050_read,
.release = mpu6050_release,
};
static int mpu6050_probe(struct i2c_client *client, const struct i2c_device_id *id)
{
int ret = -1; //保存错误状态码
printk(KERN_EMERG "match successed!\n");
/***********************************注册字符设备部分**************************************/
// 采用动态分配的方式,获取设备编号,次设备号为0,设备名称为rgb-leds,可通过命令cat /proc/devices查看
// DEV_CNT为1,当前只申请一个设备编号
ret = alloc_chrdev_region(&mpu6050_devno, 0, DEV_CNT, DEV_NAME);
if(ret < 0){
printk("fail to alloc mpu6050_devno\n");
goto alloc_err;
}
// 关联字符设备结构体cdev与文件操作结构体file_operations
mpu6050_chrdev.owner = THIS_MODULE;
cdev_init(&mpu6050_chrdev, &mpu6050_chrdev_fops);
// 添加设备到cdev_map哈希表中
ret = cdev_add(&mpu6050_chrdev, mpu6050_devno, DEV_CNT);
if(ret < 0){
printk("fail to add cdev\n");
goto add_err;
}
// 创建类
class_mpu6050 = class_create(THIS_MODULE, DEV_NAME);
// 创建设备DEV_NAME指定设备名
device_mpu6050 = device_create(class_mpu6050, NULL, mpu6050_devno, NULL, DEV_NAME);
mpu6050_client = client;
return 0;
alloc_err:
return -1;
add_err:
//添加设备失败时,需要注销设备号
unregister_chrdev_region(mpu6050_devno, DEV_CNT);
}
static int mpu6050_remove(struct i2c_client *client)
{
device_destroy(class_mpu6050, mpu6050_devno);
class_destroy(class_mpu6050);
cdev_del(&mpu6050_chrdev);
unregister_chrdev_region(mpu6050_devno, DEV_CNT);
return 0;
}
/* 定义ID匹配表 */
static const struct i2c_device_id gtp_device_id[] = {
{"fire,i2c_mpu6050", 0},
{/* sentinel */},
};
/* 定义设备树匹配表 */
static const struct of_device_id mpu6050_of_match_table[] = {
{.compatible = "fire,i2c_mpu6050"},
{/* sentinel */},
};
struct i2c_driver mpu6050_driver ={
.probe = mpu6050_probe,
.remove = mpu6050_remove,
.id_table = gtp_device_id,
.driver = {
/* 根据.name去和设备树的i2c1节点匹配,
匹配成功后设备树的i2c1节点会自动转化为i2c_client结构体,
然后i2c_client结构体和i2c_driver结构体进行配对
*/
.name = "fire,i2c_mpu6050",
.owner = THIS_MODULE,
.of_match_table = mpu6050_of_match_table,
},
};
static int __init mpu6050_driver_init(void)
{
int ret;
pr_info("mpu6050_driver_init!\n");
ret = i2c_add_driver(&mpu6050_driver);
return ret;
}
static void __exit mpu6050_driver_exit(void){
pr_info("mpu6050_driver_exit!\n");
i2c_del_driver(&mpu6050_driver);
}
module_init(mpu6050_driver_init);
module_exit(mpu6050_driver_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("couvrir");
MODULE_DESCRIPTION("led module");
MODULE_ALIAS("led module");
App.c文件
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#include <stdlib.h>
int main(int argc, char *argv[])
{
/* 保存接收到的mpu6050转换结构数据,依次是Ax、Ay、Az、Gx、Gy、Gz */
short receive_data[6];
int fd = open("/dev/I2C1_mpu6050", O_RDWR);
if(fd < 0){
printf("open file:%s failed!!!\n", argv[0]);
return -1;
}
int error = read(fd, receive_data, 12);
if(error < 0){
printf("read file error!\n");
close(fd);
}
printf("AX=%d, AY=%d, AZ=%d ", (int)receive_data[0], (int)receive_data[1], (int)receive_data[2]);
printf(" GX=%d, GY=%d, GZ=%d\n\n", (int)receive_data[3], (int)receive_data[4], (int)receive_data[5]);
error = close(fd);
if(error < 0){
printf("close file error!\n");
}
return 0;
}
Makefile文件
照旧
执行过程
虚拟机:
执行make和make copy。生成.ko文件。
debian系统里面默认加载了一部分插件(使用到i2c控制器),为了不干扰实验结果,我们屏蔽掉iic的插件。#屏蔽掉iic内容的dtbo,包括i2c1、i2c2、lcd5(i2c触摸芯片)、lcd43(i2c触摸芯片)。
sudo vim /boot/uEnv.txt
sudo reboot
开发板(在挂载目录下执行):
sudo insmod i2c_mpu6050.ko
ls /dev/I2C1_mpu6050
sudo ./App
sudo rmmod i2c_mpu6050.ko文章来源:https://www.toymoban.com/news/detail-683833.html
文章来源地址https://www.toymoban.com/news/detail-683833.html
到了这里,关于14.IIC核心函数与万能驱动的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!