[linux/driver]I2C客户端驱动实现

linux设备平台驱动之I2C驱动

i2c硬件特点和读写流程

  • 协议
    • 双线双向串行传输,数据线SDA,时钟线SCL; SDA大端传输,每次传输一个字节(8bit)
    • 读写寄存器地址addr分为固定部分和可编程部分,需要查询芯片datasheet
    • 读写时序
      • 开始传输(SCL=1,SDA由高变低)
      • 结束传输(SCL=1,SDA由低变高)
      • 传输应答: master发送完8bit data之后就会在第九个clock等待ACK,收到SDA=0,否则SDA=1
      • 数据传输(SCL=1,SDA稳定,无跳变)
      • 数据改变(SCL=0, SDA改变传输的bit)
  • 读写流程
      • master发起开始传输信号
      • master发出i2c addr(7bit)和W操作(1bit),等待ACK
      • slave发送ACK
      • master发送reg add(8bit),等到ACK
      • slave发送ACK
      • master发送reg data(8bit),等到ACK
      • slave发送ACK
      • master发起结束传输信号
      • master发起开始传输信号
      • master发出i2c addr(7bit)和W操作(1bit),等待ACK
      • slave发送ACK
      • master发送reg add(8bit),等到ACK
      • slave发送ACK
      • master发起结束传输信号
      • master发起开始传输信号
      • master发出i2c addr(7bit)和R操作(1bit),等待ACK
      • slave发送ACK
      • slave发送reg data(8bit),等到ACK
      • master发起ACK

编写I2C客户端驱动程序需要的主体步骤

  • 1.申明驱动程序支持的设备ID,可以使用i2c_device_id声明
    • 如果支持设备树,可以使用of_device_id
    	static const struct i2c_device_id myi2c_ids[] = {
    		{"myi2c", 0},
    		{},
    	};
    	
    	//对于设备树的open firmware(OF)匹配机制,使用of_device_id
    	static const struct of_device_id of_myi2c_id[] = {
    		{.compatible = "buff,myi2c",},
    		{},
    	};
    
  • 2.调用MODULE_DEVICE_TABLE(i2c, myi2c_ids)I2C内核设备注册设备列表
    • 如果支持设备树,则必须使用MODULE_DEVICE_TABLE(of, of_lm3643_id)
    MODULE_DEVICE_TABLE(i2c, myi2c_ids);
    MODULE_DEVICE_TABLE(of, of_myi2c_id);
    
  • 3.按照实际需求编写proberemove函数
    
    static int myi2c_probe(struct i2c_client *client, const struct i2c_device_id *id)
    {
    	xxx
    }
    static int myi2c_remove(struct i2c_client *client)
    {
    	xxx
    }
    
  • 4.声明并且填充 struct i2c_driver, 用已经创建的id数组(lm3643_ids)设置结构体中的id_table,填充其他变量
    struct i2c_driver my_i2c_driver = {
    	.driver = {
    		.name = MYI2C_NAME,
    		.owner = THIS_MODULE,
    		.of_match_table = of_match_ptr(of_myi2c_id),
    	},
    	.probe = myi2c_probe,
    	.remove = myi2c_remove,
    	.id_table = myi2c_ids,
    };
    
  • 5.使用填充完毕的struct i2c_driver调用module_i2c_driver函数
    module_i2c_driver(&my_i2c_driver);
    
    • 或者使用传统驱动初始化函数
    static int __init myi2c_init(void)
    {
    	xxx
    	//核心函数
    	i2c_add_driver(&my_i2c_driver);
    	xxx
    }
    
    static void __exit myi2c_exit(void)
    {
    	xxx
    	i2c_del_driver(&my_i2c_driver);
    	xxx
    }
    module_init(myi2c_init);  //i2c_add_driver
    module_exit(myi2c_exit);  //i2c_del_driver
    

i2c客户端驱动读写接口实现

依据上文中的读写流程,I2C client读写存在区别

struct myi2c_dev_reg {
	u32 dev_id;			/*!< Device ID */
	u8 addr;			/*!< Register DRAM address */
	u32 data;			/*!< Register Value */
};

int myi2c_read_data(struct i2c_client *client, struct myi2c_dev_reg *reg)
{
	unsigned char buf[2];
	struct i2c_msg msg[2];
	int rval = 0;

//smbus可以使用i2c总线协议进行信号传输,反之不行
	if (client->adapter->algo->master_xfer != NULL)	{
		msg[0].addr = client->addr;
		msg[0].buf = &reg->addr;
		msg[0].len = sizeof(reg->addr);
		msg[0].flags = 0;

		msg[1].addr = client->addr;
		msg[1].buf = buf;
		msg[1].len = sizeof(buf);
		msg[1].flags = I2C_M_RD;

		rval = i2c_transfer(client->adapter, msg, ARRAY_SIZE(msg));
		if (ARRAY_SIZE(msg) == rval) {
			rval = buf[1];
			reg->data = (rval << 8) | buf[0];
		}
	} else if (client->adapter->algo->smbus_xfer != NULL) {
		rval = i2c_smbus_read_word_data(client, reg->addr);
	} else {
		rval = -EOPNOTSUPP;
	}

	return rval;
}

int myi2c_write_data(struct i2c_client *client, struct myi2c_dev_reg *reg)
{
	struct i2c_msg msg;
	char msg_count = 1;
	unsigned char buf[3];
	int rval = 0;

	if (client->adapter->algo->master_xfer != NULL) {
		//i2c一次只能传8bit的数据,所以只能按照8bit的长度截
		buf[0] = reg->addr;
		buf[1] = reg->data & 0xff;
		buf[2] = (reg->data >> 8) & 0xff; 

		msg.addr = client->addr;
		msg.buf = buf;
		msg.len = sizeof(buf);
		msg.flags = 0;

		rval = i2c_transfer(client->adapter, &msg, msg_count);
		if (msg_count == rval) {
			rval = 0;
		}
	} else if (client->adapter->algo->smbus_xfer != NULL) {
		rval = i2c_smbus_write_word_data(client, reg->addr, reg->data);
	} else {
		rval = -EOPNOTSUPP;
	}

	return rval;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值