6.7 Linux 驱动开发 IIC驱动
i2c_driver 的注册示例代码如下:
// i2c_driver 注册流程
/* i2c 驱动的 probe 函数 */
static int xxx_probe(struct i2c_client *client, const struct i2c_device_id *id)
{
/* 函数具体程序 */
return 0;
}
/* i2c 驱动的 remove 函数 */
static int xxx_remove(struct i2c_client *client){
/* 函数具体程序 */
return 0;
}
/* 传统匹配方式 ID 列表。i2c_device_id,无设备树的时候匹配 ID 表。*/
static const struct i2c_device_id xxx_id[] = {
{"xxx", 0},
{}
};
/* 设备树匹配列表。of_device_id,设备树所使用的匹配表。*/
static const struct of_device_id xxx_of_match[] = {
{ .compatible = "xxx" },
{ /* Sentinel */ }
};
/* i2c 驱动结构体。i2c_driver,当 I2C 设备和 I2C 驱动匹配成功以后 probe 函数就会执行,这些和 platform 驱动一样,probe 函数里面基本就是标准的字符设备驱动那一套了。 */
static struct i2c_driver xxx_driver = {
.id_table = xxx_id,
.driver = {
.owner = THIS_MODULE,
.name = "xxx",
.of_match_table = xxx_of_match,
},
.probe = xxx_probe,
.remove = xxx_remove,
};
/* 驱动入口函数 */
static int __init xxx_init(void){
int ret = 0;
ret = i2c_add_driver(&xxx_driver);
return ret;
}
/* 驱动出口函数 */
static void __exit xxx_exit(void){
i2c_del_driver(&xxx_driver);
}
module_init(xxx_init);
module_exit(xxx_exit);
/******************************************I2C消息传送**************************************************/
struct i2c_msg {
__u16 addr; // 从设备地址(7位或10位)
__u16 flags; // 传输标志(读/写、地址模式等)
__u16 len; // 数据长度(字节数)
__u8 *buf; // 数据缓冲区指针
};
addr:
7 位地址:直接填写设备地址(如 0x3C)。
10 位地址:需设置 I2C_M_TEN 标志。
flags:
I2C_M_RD:标记为读操作(从设备到主设备)。
I2C_M_TEN:使用 10 位地址模式。
I2C_M_NOSTART:不发送起始条件(用于复合消息)。
I2C_M_REV_DIR_ADDR:反转读写方向(某些特殊设备需要)。
0:标记为写操作。
len:数据缓冲区 buf 的字节数。
buf:存储待发送或接收的数据。
例:
struct i2c_msg msg[2];
u8 reg_addr = 0x00;
u8 data;
// 消息1:写寄存器地址
msg[0].addr = 0x1E;
msg[0].flags = 0;
msg[0].len = 1;
msg[0].buf = ®_addr;
// 消息2:读数据
msg[1].addr = 0x1E;
msg[1].flags = I2C_M_RD;
msg[1].len = 1;
msg[1].buf = &data;
// 执行传输
i2c_transfer(adapter, msg, 2);
对应的实际I2C总线时序:
[Start] → [Addr+Write] → [Reg 0x00] → [Start] → [Addr+Read] → [Data1] → [Data2] → [Stop]
代码示例:
#define NAME_DEV "ap3216c"
struct dev
{
dev_t devid;
int major;
int minor;
struct class *class;
struct device *device; /* 设备 */
struct cdev cdev;
struct device_node *nd; /* 设备节点 */
void *private_data; /* 私有数据 */
unsigned short ir, als, ps; /* 三个光传感器数据 */
};
struct dev dev;
static s32 ap3216c_write_regs(struct dev *dev, u8 reg, u8 *buf, u8 len){
u8 b[256];
struct i2c_msg msg;
struct i2c_client *client = (struct i2c_client *)dev->private_data;
b[0] = reg; /* 寄存器首地址 */
memcpy(&b[1],buf,len); /* 将要写入的数据拷贝到数组 b 里面 */
msg.addr = client->addr; /* ap3216c 地址 */
msg.flags = 0; /* 标记为写数据 */
msg.buf = b; /* 要写入的数据缓冲区 */
msg.len = len + 1; /* 要写入的数据长度 */
return i2c_transfer(client->adapter, &msg, 1);
}
static void ap3216c_write_reg(struct dev *dev, u8 reg, u8 data)
{
u8 buf = 0;
buf = data;
ap3216c_write_regs(dev, reg, &buf, 1);
}
static int dev_open(struct inode *inode, struct file *filp)
{
filp->private_data = &dev;
/* 初始化 AP3216C */
ap3216c_write_reg(&dev, AP3216C_SY
剩余60%内容,订阅专栏后可继续查看/也可单篇购买
C++/嵌入式开发 秋招面经 文章被收录于专栏
一名985硕,在25年秋招中斩获多个C++/嵌入式开发Offer。本专栏将分享我的面经,涵盖C/C++、操作系统、计算机网络、ARM体系与架构、Linux应用/驱动开发、Qt、通信协议及开发工具链等核心内容。
查看12道真题和解析