linux随笔记-I2C驱动

I2C有自己的总线,所以在此跟之前的platform平台虚拟总线的不一样(SOC总线驱动在获取设备树资源的时候,可能还是会用到platform。用来获取匹配资源,调用probe.remove)。先讲一个概念:

IIC总线驱动-是SOC的I2C控制器相关的驱动

IIC设备驱动-是指某个I2C设备的的具体驱动,调用某I2C总线驱动提供的读写函数去控制具体的设备。

IIC总线驱动

此部分都是和SOC相关的,一般都是由原厂去实现。了解即可,如果不是在SOC原厂,只需要会用即可。

大概的就是想内核注册I2C控制其(i2c_adapter),在i2c_adapter中有一个i2c_algorithm,该变量下,有具体操作不同SOC的I2C的API接口。在使用I2C读写的时候最终会调用这儿的API。这是imx6ull原厂驱动里面的(不同SOC会不同,由原厂编写)

static struct i2c_algorithm i2c_imx_algo = {
	.master_xfer	= i2c_imx_xfer,
	.functionality	= i2c_imx_func,
};

IIC设备驱动

i2c_client:具体的某个I2C设备,不需要自己在驱动中去创建,只需要在设备树中添加具体的I2C 芯片设备,在系统解析设备树的时候就会知道这个i2c设备,然后自动创建对应的i2c_client。

直接在SOC原厂的设备树里面找一段i2c节点下面挂载的设备。比如这个设备就有I2C,中断脚,复位脚。通过i2c_client获取到对应的资源,然后就可以进行初始化,注册中断,使用对应的引脚啦。是不是很简单。

	gt9147:gt9147@14 {
		compatible = "goodix,gt9147", "goodix,gt9xx";
		reg = <0x14>;
		pinctrl-names = "default";
		pinctrl-0 = <&pinctrl_tsc
					&pinctrl_tsc_reset >; 
		interrupt-parent = <&gpio1>; 
		interrupts = <9 0>; 
		reset-gpios  = <&gpio5 9 GPIO_ACTIVE_LOW>;
		interrupt-gpios = <&gpio1 9 GPIO_ACTIVE_LOW>;
		status = "disable";  /* 如果需要改为okay */
	};

(如果系统比较老,就自己创建一个struct i2c_board_info的结构体去描述设备信息)

i2c_driver:需要驱动中去创建,初始化向系统注册,这一块就是非原厂的开发i2c设备类的驱动要做的重点工作。int i2c_register_driver(struct module *owner, struct i2c_driver *driver)。copy一段示例。这就platform平台那一套一样。

//int i2c_register_driver(struct module *owner, struct i2c_driver *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 列表 */
static const struct i2c_device_id xxx_id[] = {
	{"xxx", 0},
	{}
};

/* 设备树匹配列表 */
static const struct of_device_id xxx_of_match[] = {
	{ .compatible = "xxx" },
	{ /* Sentinel */ }
};

/* i2c 驱动结构体 */
static struct i2c_driver xxx_driver = {
	.probe = xxx_probe,
	.remove = xxx_remove,
	.driver = {
		.owner = THIS_MODULE,
		.name = "xxx",
		.of_match_table = xxx_of_match,
	},
	.id_table = xxx_id,
};

/* 驱动入口函数 */
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; /* 从机地址 */
	__u16 flags; /* 标志 */
	#define I2C_M_TEN 0x0010
	#define I2C_M_RD 0x0001
	#define I2C_M_STOP 0x8000
	#define I2C_M_NOSTART 0x4000
	#define I2C_M_REV_DIR_ADDR 0x2000
	#define I2C_M_IGNORE_NAK 0x1000
	#define I2C_M_NO_RD_ACK 0x0800
	#define I2C_M_RECV_LEN 0x0400
	__u16 len; /* 消息(本 msg)长度 */
	__u8 *buf; /* 消息数据 */
};
/*
adap :所使用的 I2C 适配器,i2c_client 会保存其对应的 i2c_adapter。
msgs:I2C 要发送的一个或多个消息。
num :消息数量,也就是 msgs 的数量。
返回值:负值,失败,其他非负值,发送的 msgs 数量
*/
int i2c_transfer(struct i2c_adapter *adap,struct i2c_msg *msgs,int num);
/* 封装后的APi */
int i2c_master_send(const struct i2c_client *client,const char *buf,int count);
int i2c_master_recv(const struct i2c_client *client,const char *buf,int count);

/* 抄一段代码 ,封装一下i2c操作寄存器的读写函数*/
/* 设备结构体 */
struct xxx_dev 
{
	......
	void *private_data; /* 私有数据,一般会设置为 i2c_client */
};

/*
* @description  : 读取 I2C 设备多个寄存器数据
* @param – dev  : I2C 设备
* @param – reg  : 要读取的寄存器首地址
* @param – val  : 读取到的数据
* @param – len  : 要读取的数据长度
* @return : 操作结果
*/
static int xxx_read_regs(struct xxx_dev *dev, u8 reg, void *val,int len)
{
	int ret;
	struct i2c_msg msg[2];
	struct i2c_client *client = (struct i2c_client *)dev->private_data;
	/* msg[0],第一条写消息,发送要读取的寄存器首地址 */
	msg[0].addr = client->addr; /* I2C 器件地址 */
	msg[0].flags = 0; /* 标记为发送数据 */
	msg[0].buf = &reg; /* 读取的首地址 */
	msg[0].len = 1; /* reg 长度 */
	/* msg[1],第二条读消息,读取寄存器数据 */
	msg[1].addr = client->addr; /* I2C 器件地址 */
	msg[1].flags = I2C_M_RD; /* 标记为读取数据  */
	msg[1].buf = val; /* 读取数据缓冲区 */
	msg[1].len = len; /* 要读取的数据长度 */
	ret = i2c_transfer(client->adapter, msg, 2);
	if(ret == 2) 
	{
		ret = 0;
	} 
	else 
	{
		ret = -EREMOTEIO;
	}
	return ret;
}

/*
* @description  : 向 I2C 设备多个寄存器写入数据
* @param – dev  : 要写入的设备结构体
* @param – reg  : 要写入的寄存器首地址
* @param – buf  : 要写入的数据缓冲区
* @param – len  : 要写入的数据长度
* @return : 操作结果
*/
static s32 xxx_write_regs(struct xxx_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; /* I2C 器件地址 */
	msg.flags = 0; /* 标记为写数据 */
	msg.buf = b; /* 要发送的数据缓冲区 */
	msg.len = len + 1; /* 要发送的数据长度 */
	return i2c_transfer(client->adapter, &msg, 1);
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值