驱动层:
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/i2c.h>
#include <linux/cdev.h>
#include <linux/slab.h>
#include <linux/fs.h>
#include <linux/delay.h>
#include <asm/uaccess.h>
#include "mpu6050.h"
MODULE_LICENSE("GPL");
#define SMPLRT_DIV 0x19
#define CONFIG 0x1A
#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
#define MPU6050_MAJOR 500
#define MPU6050_MINOR 0
/**
* struct i2c_client - represent an I2C slave device
* @flags: I2C_CLIENT_TEN indicates the device uses a ten bit chip address;
* I2C_CLIENT_PEC indicates it uses SMBus Packet Error Checking
* @addr: Address used on the I2C bus connected to the parent adapter.
* @name: Indicates the type of the device, usually a chip name that's
* generic enough to hide second-sourcing and compatible revisions.
* @adapter: manages the bus segment hosting this I2C device
* @dev: Driver model device node for the slave.
* @irq: indicates the IRQ generated by this device (if any)
* @detected: member of an i2c_driver.clients list or i2c-core's
* userspace_devices list
* @slave_cb: Callback when I2C slave mode of an adapter is used. The adapter
* calls it to pass on slave events to the slave driver.
*
* An i2c_client identifies a single device (i.e. chip) connected to an
* i2c bus. The behaviour exposed to Linux is defined by the driver
* managing the device.
*/
/**
* struct i2c_client {
* unsigned short flags; // div., see below
* unsigned short addr; // chip address - NOTE: 7bit
* // addresses are stored in the
* // _LOWER_ 7 bits
* char name[I2C_NAME_SIZE];
* struct i2c_adapter *adapter; // the adapter we sit on(坐落,依附于)
* struct device dev; // the device structure (i2c设备是一个设备-继承)
* int irq; // irq issued by device 设备对应的中断
* struct list_head detected;
* #if IS_ENABLED(CONFIG_I2C_SLAVE)
* i2c_slave_cb_t slave_cb; // callback for slave mode
* #endif
* };
*/
struct mpu6050_device {
struct cdev cdev;
// i2c客户端
// represent an I2C slave device
struct i2c_client *client;
};
struct mpu6050_device *mpu6050;
/**
*参数:
* @client 代表具体的i2c设备
* @reg 具体的寄存器值
*/
/**
* struct i2c_msg - an I2C transaction segment beginning with START
* @addr: Slave address, either seven or ten bits. When this is a ten
* bit address, I2C_M_TEN must be set in @flags and the adapter
* must support I2C_FUNC_10BIT_ADDR.
* @flags: I2C_M_RD is handled by all adapters. No other flags may be
* provided unless the adapter exported the relevant I2C_FUNC_*
* flags through i2c_check_functionality().
* @len: Number of data bytes in @buf being read from or written to the
* I2C slave address. For read transactions where I2C_M_RECV_LEN
* is set, the caller guarantees that this buffer can hold up to
* 32 bytes in addition to the initial length byte sent by the
* slave (plus, if used, the SMBus PEC); and this value will be
* incremented by the number of block data bytes received.
* @buf: The buffer into which data is read, or from which it's written.
*
* An i2c_msg is the low level representation of one segment of an I2C
* transaction. It is visible to drivers in the @i2c_transfer() procedure,
* to userspace from i2c-dev, and to I2C adapter drivers through the
* @i2c_adapter.@master_xfer() method.
*
* Except when I2C "protocol mangling" is used, all I2C adapters implement
* the standard rules for I2C transactions. Each transaction begins with a
* START. That is followed by the slave address, and a bit encoding read
* versus write. Then follow all the data bytes, possibly including a byte
* with SMBus PEC. The transfer terminates with a NAK, or when all those
* bytes have been transferred and ACKed. If this is the last message in a
* group, it is followed by a STOP. Otherwise it is followed by the next
* @i2c_msg transaction segment, beginning with a (repeated) START.
*
* Alternatively, when the adapter supports I2C_FUNC_PROTOCOL_MANGLING then
* passing certain @flags may have changed those standard protocol behaviors.
* Those flags are only for use with broken/nonconforming slaves, and with
* adapters which are known to support the specific mangling options they
* need (one or more of IGNORE_NAK, NO_RD_ACK, NOSTART, and REV_DIR_ADDR).
*/
/*
struct i2c_msg {
__u16 addr; // slave address 从设备地址
__u16 flags; // 标志 下面宏所示
#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 10位的从机地址
#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 忽略NAK
#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; // msg length 消息长度
__u8 *buf; // pointer to msg data 存放消息的地址
};
*/
static int mpu6050_read_byte(struct i2c_client *client, unsigned char reg)
{
int ret;
char txbuf[1] = { reg };
char rxbuf[1];
// 消息帧
struct i2c_msg msg[2] = {
{client->addr, 0, 1, txbuf}, // 主发送
{client->addr, I2C_M_RD, 1, rxbuf} // 主接收
};
/**
* i2c_transfer - execute a single or combined I2C message
* @adap: Handle to I2C bus
* @msgs: One or more messages to execute before STOP is issued to
* terminate the operation; each message begins with a START.
* @num: Number of messages to be executed.
*
* Returns negative errno, else the number of messages executed.
*
* Note that there is no requirement that each message be sent to
* the same slave address, although that is the most common model.
*/
/*
* i2c_adapter is the structure used to identify a physical i2c bus along
* with the access algorithms necessary to access it.
* i2c_adapter 是一个类,这个类用来确定一个物理的i2c总线,并且其中包含了访
* 问该物理i2c总线adapter的访问接口机制方法
*/
/*
struct i2c_adapter {
struct module *owner;
unsigned int class; // classes to allow probing for
const struct i2c_algorithm *algo; //the algorithm to access the bus 总线的访问机制方法
void *algo_data;
/* data fields that are valid for all devices
const struct i2c_lock_operations *lock_ops;
struct rt_mutex bus_lock;
struct rt_mutex mux_lock;
int timeout; //in jiffies
int retries;
struct device dev; // the adapter device 适配器设备这里,adapter直接被抽象为设备
int nr;
char name[48];
struct completion dev_released;
struct mutex userspace_clients_lock;
struct list_head userspace_clients;
struct i2c_bus_recovery_info *bus_recovery_info;
const struct i2c_adapter_quirks *quirks;
struct irq_domain *host_notify_domain;
};
*/
/**
* struct i2c_algorithm - represent I2C transfer method
* @master_xfer: Issue a set of i2c transactions to the given I2C adapter
* defined by the msgs array, with num messages available to transfer via
* the adapter specified by adap.
* @smbus_xfer: Issue smbus transactions to the given I2C adapter. If this
* is not present, then the bus layer will try and convert the SMBus calls
* into I2C transfers instead.
* @functionality: Return the flags that this algorithm/adapter pair supports
* from the I2C_FUNC_* flags.
* @reg_slave: Register given client to I2C slave mode of this adapter
* @unreg_slave: Unregister given client from I2C slave mode of this adapter
*
* The following structs are for those who like to implement new bus drivers:
* i2c_algorithm is the interface to a class of hardware solutions which can
* be addressed using the same bus algorithms - i.e. bit-banging or the PCF8584
* to name two of the most common.
*
* The return codes from the @master_xfer field should indicate the type of
* error code that occurred during the transfer, as documented in the kernel
* Documentation file Documentation/i2c/fault-codes.
*/
/*
struct i2c_algorithm {
// If an adapter algorithm can't do I2C-level access, set master_xfer
// to NULL. If an adapter algorithm can do SMBus access, set
// smbus_xfer. If set to NULL, the SMBus protocol is simulated
// using common I2C messages
// master_xfer should return the number of messages successfully
// processed, or a negative value on error
int (*master_xfer)(struct i2c_adapter *adap, struct i2c_msg *msgs,
int num);
int (*smbus_xfer) (struct i2c_adapter *adap, u16 addr,
unsigned short flags, char read_write,
u8 command, int size, union i2c_smbus_data *data);
// To determine what the adapter supports
u32 (*functionality) (struct i2c_adapter *);
#if IS_ENABLED(CONFIG_I2C_SLAVE)
int (*reg_slave)(struct i2c_client *client);
int (*unreg_slave)(struct i2c_client *client);
#endif
};
*/
// 发送消息 --> 调用 i2c-core.c 的 int __i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
// 调用--> adap->algo->master_xfer(adap, msgs, num);
// --> 调用板级支持的samsung的i2c控制器硬件实现发送接收数据
ret = i2c_transfer(client->adapter, msg, ARRAY_SIZE(msg)); // i2c-core.c
if (ret < 0) {
printk("ret = %d\n", ret);
return ret;
}
return rxbuf[0];
}
// reg: 要操作的寄存器
// val: 发送给寄存器的值
static int mpu6050_write_byte(struct i2c_client *client, unsigned char reg, unsigned char val)
{
char txbuf[2] = {reg, val};
struct i2c_msg msg[2] = {
{client->addr, 0, 2, txbuf}, 主发送,从接收
};
i2c_transfer(client->adapter, msg, ARRAY_SIZE(msg));
return 0;
}
static int mpu6050_open(struct inode *inode, struct file *file)
{
return 0;
}
static int mpu6050_release(struct inode *inode, struct file *file)
{
return 0;
}
static long mpu6050_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
union mpu6050_data data;
struct i2c_client *client = mpu6050->client;
switch(cmd) {
case GET_ACCEL:
data.accel.x = mpu6050_read_byte(client, ACCEL_XOUT_L);
data.accel.x |= mpu6050_read_byte(client, ACCEL_XOUT_H) << 8;
data.accel.y = mpu6050_read_byte(client, ACCEL_YOUT_L);
data.accel.y |= mpu6050_read_byte(client, ACCEL_YOUT_H) << 8;
data.accel.z = mpu6050_read_byte(client, ACCEL_ZOUT_L);
data.accel.z |= mpu6050_read_byte(client, ACCEL_ZOUT_H) << 8;
break;
case GET_GYRO:
data.gyro.x = mpu6050_read_byte(client, GYRO_XOUT_L);
data.gyro.x |= mpu6050_read_byte(client, GYRO_XOUT_H) << 8;
data.gyro.y = mpu6050_read_byte(client, GYRO_YOUT_L);
data.gyro.y |= mpu6050_read_byte(client, GYRO_YOUT_H) << 8;
data.gyro.z = mpu6050_read_byte(client, GYRO_ZOUT_L);
data.gyro.z |= mpu6050_read_byte(client, GYRO_ZOUT_H) << 8;
break;
case GET_TEMP:
data.temp = mpu6050_read_byte(client, TEMP_OUT_L);
data.temp |= mpu6050_read_byte(client, TEMP_OUT_H) << 8;
break;
default:
printk("invalid argument\n");
return -EINVAL;
}
if (copy_to_user((void *)arg, &data, sizeof(data)))
return -EFAULT;
return sizeof(data);
}
struct file_operations mpu6050_fops = {
.owner = THIS_MODULE,
.open = mpu6050_open,
.release = mpu6050_release,
.unlocked_ioctl = mpu6050_ioctl,
};
static int mpu6050_probe(struct i2c_client *client, const struct i2c_device_id *id)
{
int ret;
dev_t devno = MKDEV(MPU6050_MAJOR, MPU6050_MINOR);
printk("match OK!\n");
mpu6050 = kzalloc(sizeof(*mpu6050), GFP_KERNEL);
if (mpu6050 == NULL) {
return -ENOMEM;
}
mpu6050->client = client;
ret = register_chrdev_region(devno, 1, "mpu6050");
if (ret < 0) {
printk("failed to register char device region!\n");
goto err1;
}
cdev_init(&mpu6050->cdev, &mpu6050_fops);
mpu6050->cdev.owner = THIS_MODULE;
ret = cdev_add(&mpu6050->cdev, devno, 1);
if (ret < 0) {
printk("failed to add device\n");
goto err2;
}
mpu6050_write_byte(client, PWR_MGMT_1, 0x00);
mpu6050_write_byte(client, SMPLRT_DIV, 0x07);
mpu6050_write_byte(client, CONFIG, 0x06);
mpu6050_write_byte(client, GYRO_CONFIG, 0xF8);
mpu6050_write_byte(client, ACCEL_CONFIG, 0x19);
return 0;
err2:
unregister_chrdev_region(devno, 1);
err1:
kfree(mpu6050);
return ret;
}
static int mpu6050_remove(struct i2c_client *client)
{
dev_t devno = MKDEV(MPU6050_MAJOR, MPU6050_MINOR);
cdev_del(&mpu6050->cdev);
unregister_chrdev_region(devno, 1);
kfree(mpu6050);
return 0;
}
static const struct i2c_device_id mpu6050_id[] = {
{ .name = "mpu6050"},
{}
};
static struct of_device_id mpu6050_dt_match[] = {
{.compatible = "invensense,mpu6050" },
{/*northing to be done*/},
};
struct i2c_driver mpu6050_driver = {
.driver = {
.name = "mpu6050",
.owner = THIS_MODULE,
.of_match_table = of_match_ptr(mpu6050_dt_match),
},
.probe = mpu6050_probe,
.remove = mpu6050_remove,
.id_table = mpu6050_id,
};
module_i2c_driver(mpu6050_driver);
应用层:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include "mpu6050.h"
int main(int argc, const char *argv[])
{
int fd;
union mpu6050_data data;
fd = open("/dev/mpu6050", O_RDWR);
if (fd < 0) {
perror("open");
exit(1);
}
while(1) {
ioctl(fd, GET_ACCEL, &data);
printf("\racceleration data: x = %04x, y = %04x, z = %04x",
data.accel.x, data.accel.y, data.accel.z);
}
close(fd);
return 0;
}