Linux 的i2c系统
Linux 的i2c系统十分庞大,主要由3部分构成,i2c核心,i2c总线驱动,以及i2c设备驱动。
i2c核心
主要提供了i2c总线驱动和设备驱动的注册,注销方法,i2c通信方法(即Algorithm)上层的与具体适配器无关的代码及探测设备、检测设备地址的上层代码等。
i2c总线驱动
i2c总线驱动是对i2c硬件体系结构中适配器端的实现,适配器可由CPU控制,甚至可以直接集成在CPU内部。
其主要包含i2c适配器数据结构i2c_adapter、i2c适配器的Algorithm数据结构i2c_algorithm和控制i2c适配器产生通信信号的函数。
经由i2c总线驱动的代码,我们可以控制i2c适配器以主控方式产生开始位,停止位,等。
I2C 总线驱动,或者说 I2C 适配器驱动的主要工作就是初始化 i2c_adapter 结构体变量,然后设置 i2c_algorithm 中的 master_xfer 函数。完成以后通过 i2c_add_numbered_adapter或 i2c_add_adapter 这两个函数向系统注册设置好的 i2c_adapter。
不过一般i2c总线驱动已经由半导体厂商编写好了。
i2c设备驱动
设备一般挂接在受CPU控制的i2c适配器上,由I2c适配器和CPU交换数据。
i2c设备驱动主要包含数据结构i2c_driver和i2c_client,我们需要根据具体设备实现其中的成员函数。如
设备树修改
&i2c1 {
clock-frequency = <100000>;
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_i2c1>;
status = "okay";
sht30-i2c@44 {
compatible = "wuhu,sht30";
reg = <0x44>;
};
驱动编写
i2c_driver
static struct i2c_driver sht30_drv = {
.probe = sht30_probe,
.remove = sht30_remove,
.driver = {
.name = "hoshino-sht30",
.owner = THIS_MODULE,
.of_match_table = of_sht30_match,
},
.id_table = sht30_id,
};
首先i2c_driver 结构体, 在probe 函数中完成分配设备号,注册字符设备,创建类设备等操作。
在remove函数中,注销销毁使用的资源。
接着实现注册cdev时使用到的file_operations
static struct file_operations sht30_fops = {
.owner = THIS_MODULE,
.open = sht30_open,
.read = sht30_read,
.release = sht30_release,
};
static int sht30_init(struct i2c_client *client)
{
int rv = 0;
uint16_t data = SOFTRESET;
unsigned char cmd[2];
cmd[0] = data >> 8;
cmd[1] = data;
rv = i2c_master_send(client,cmd,2);
if(rv<0)
{
dev_err(&client->dev,"Send i2c SOFTRESET cmd failure.\n");
return rv;
}
msleep(50);
return 0;
}
static int sht30_open(struct inode* inode , struct file* filp )
{
struct sht30_priv * priv = container_of(inode->i_cdev,struct sht30_priv,cdev);
int rv = 0;
//Init sht30.
rv = sht30_init(priv->client);
if(rv < 0)
{
dev_err(priv->dev, "sht30 init failure.\n");
return rv;
}
filp->private_data = priv;
return 0;
}
static int read_data(struct i2c_client* client ,uint8_t * buf,size_t cnt)
{
int rv =0;
uint16_t data = MEAS_NOSTRETCH_MEDREP;
unsigned char cmd[2];
cmd[0] = data >> 8;
cmd[1] = data;
rv = i2c_master_send(client,cmd,2);
if(rv < 0)
{
dev_err(&client->dev,"sht30 send read cmd failure.\n");
return -EFAULT;
}
msleep(85);
rv = i2c_master_recv(client,buf, cnt);
if(rv<6)
{
dev_err(&client->dev,"Read data failure.\n");
return -EFAULT;
}
return 0;
}
static ssize_t sht30_read(struct file *filp, char __user *buf, size_t cnt, loff_t *off)
{
struct sht30_priv * priv = filp->private_data;
int rv = 0 ;
uint8_t bufz[6];
uint8_t data[4];
if(!priv->client)
{
dev_err(priv->dev,"Can't get i2c client.\n");
return -EFAULT;
}
rv = read_data(priv->client,bufz,sizeof(bufz));
if(rv < 0 )
{
return -EFAULT;
}
data[0] = bufz[1];
data[1] = bufz[0];
data[2] = bufz[4];
data[3] = bufz[3];
rv = copy_to_user(buf, data, sizeof(data));
if(rv)
{
dev_err(priv->dev, "copy to user error.\n");
return -EFAULT;
}
printk("%s\n",data);
return sizeof(data);
}
static int sht30_release(struct inode *inode, struct file *filp)
{
return 0;
}
注册i2c驱动
module_i2c_driver(sht30_drv);