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、通信协议及开发工具链等核心内容。

全部评论

相关推荐

评论
4
1
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务