linux-2.6.22.6内核i2c驱动框架源码分析

这篇具有很好参考价值的文章主要介绍了linux-2.6.22.6内核i2c驱动框架源码分析。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

i2c是常见的通信协议,协议比较简单,只有数据和时钟两条线(SDA和SCL),i2c的通信分为主机和从机,主机一般占主导地位,从机可以有多个。

i2c通信的数据格式为(SDA上的数据):开始的7位里面指定了设备地址(因为有多个从机),第8位是读或写信号,表示此次传输是读还是写,第9位是ack信号,也就是当某个从机收到信号好需要发ack信号(低电平)确认。

接下来就是发数据了,这个过程是由8个数据位和一个ack位组成,如果是读,这8位数据由从机驱动,第9位ack由主机发出(因为此时是主机向从机读数据,主机需要确认是否收到数据),如果是写,则这8位由主机驱动,第9位ack由从机发出,发数据这个过程是可以反复进行的,一旦发送或者读取完成,主机就会发出一个停止信号(SDA由低变高)来结束通信,这所有的过程都是在SDA上进行的。
linux-2.6.22.6内核i2c驱动框架源码分析
i2c通信可以分为读和写,读比写的过程更复杂,要读则先写,读取某设备内某地址的数据过程为:先发送设备地址,然后还要写入设备内要读取的地址,最后再发送设备地址后才可进行数据读取。写入数据则少了再发设备地址这一步,写是直接发送设备地址和设备内要写入的地址后就可以发送要写入的数据了。

写:
linux-2.6.22.6内核i2c驱动框架源码分析
读:
linux-2.6.22.6内核i2c驱动框架源码分析
内核驱动框架分析:

在开发i2c驱动程序时,我们可以自己根据芯片手册进行相关寄存器和时序等设置,这种开发比较原始,完全从底层开始,工作量大并且出错概率大,这种其实就是裸机开发了。如果是在Linux内核上进行开发,则完全可以利用内核提供的i2c框架进行开发。i2c框架主要分为两大部分,一部分是用平台设备总线驱动模型实现的,主要封装了主机(s3c2440芯片)i2c控制器的寄存器和时序设置等操作,另一部分就是设备驱动(相当于从机),主要提供主机部分具体操作所需要的数据信息,如果用总线设备驱动模型来看,这一部分就相当于提供设备信息,所以这两大部分又可以看成总线设备驱动模型。

i2c框架代码在/linux-2.6.22.6/drivers/i2c这个目录下,这里面又有algos、busses、chips三个部分,algos主要是协议算法相关的东西,busses里是主机(s3c2440芯片)i2c控制器的寄存器和时序设置等操作部分,chips里就是具体的设备驱动(相当于从机),框架入口就在busses下i2c-s3c2410.c这个文件里。

先分析i2c-s3c2410.c

static int s3c24xx_i2c_probe(struct platform_device *pdev)
{
	struct s3c24xx_i2c *i2c = &s3c24xx_i2c;
	struct resource *res;
	int ret;

	/* find the clock and enable it */

	i2c->dev = &pdev->dev;
	i2c->clk = clk_get(&pdev->dev, "i2c");
	if (IS_ERR(i2c->clk)) {
		dev_err(&pdev->dev, "cannot get clock\n");
		ret = -ENOENT;
		goto err_noclk;
	}

	dev_dbg(&pdev->dev, "clock source %p\n", i2c->clk);

	clk_enable(i2c->clk);

	/* map the registers */

	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
	if (res == NULL) {
		dev_err(&pdev->dev, "cannot find IO resource\n");
		ret = -ENOENT;
		goto err_clk;
	}

	i2c->ioarea = request_mem_region(res->start, (res->end-res->start)+1,
					 pdev->name);

	if (i2c->ioarea == NULL) {
		dev_err(&pdev->dev, "cannot request IO\n");
		ret = -ENXIO;
		goto err_clk;
	}

	i2c->regs = ioremap(res->start, (res->end-res->start)+1);

	if (i2c->regs == NULL) {
		dev_err(&pdev->dev, "cannot map IO\n");
		ret = -ENXIO;
		goto err_ioarea;
	}

	dev_dbg(&pdev->dev, "registers %p (%p, %p)\n", i2c->regs, i2c->ioarea, res);

	/* setup info block for the i2c core */

	i2c->adap.algo_data = i2c;
	i2c->adap.dev.parent = &pdev->dev;

	/* initialise the i2c controller */

	ret = s3c24xx_i2c_init(i2c);
	if (ret != 0)
		goto err_iomap;

	/* find the IRQ for this unit (note, this relies on the init call to
	 * ensure no current IRQs pending 
	 */

	res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
	if (res == NULL) {
		dev_err(&pdev->dev, "cannot find IRQ\n");
		ret = -ENOENT;
		goto err_iomap;
	}

	ret = request_irq(res->start, s3c24xx_i2c_irq, IRQF_DISABLED,
			  pdev->name, i2c);

	if (ret != 0) {
		dev_err(&pdev->dev, "cannot claim IRQ\n");
		goto err_iomap;
	}

	i2c->irq = res;
		
	dev_dbg(&pdev->dev, "irq resource %p (%lu)\n", res,
		(unsigned long)res->start);

	ret = i2c_add_adapter(&i2c->adap);
	if (ret < 0) {
		dev_err(&pdev->dev, "failed to add bus to i2c core\n");
		goto err_irq;
	}

	platform_set_drvdata(pdev, i2c);

	dev_info(&pdev->dev, "%s: S3C I2C adapter\n", i2c->adap.dev.bus_id);
	return 0;

 err_irq:
	free_irq(i2c->irq->start, i2c);

 err_iomap:
	iounmap(i2c->regs);

 err_ioarea:
	release_resource(i2c->ioarea);
	kfree(i2c->ioarea);

 err_clk:
	clk_disable(i2c->clk);
	clk_put(i2c->clk);

 err_noclk:
	return ret;
}


static struct platform_driver s3c2440_i2c_driver = {
	.probe		= s3c24xx_i2c_probe,
	.remove		= s3c24xx_i2c_remove,
	.resume		= s3c24xx_i2c_resume,
	.driver		= {
		.owner	= THIS_MODULE,
		.name	= "s3c2440-i2c",
	},
};

static int __init i2c_adap_s3c_init(void)
{
	int ret;

	ret = platform_driver_register(&s3c2410_i2c_driver);
	if (ret == 0) {
		ret = platform_driver_register(&s3c2440_i2c_driver);
		if (ret)
			platform_driver_unregister(&s3c2410_i2c_driver);
	}

	return ret;
}


static struct resource s3c_i2c_resource[] = {
	[0] = {
		.start = S3C24XX_PA_IIC,
		.end   = S3C24XX_PA_IIC + S3C24XX_SZ_IIC - 1,
		.flags = IORESOURCE_MEM,
	},
	[1] = {
		.start = IRQ_IIC,
		.end   = IRQ_IIC,
		.flags = IORESOURCE_IRQ,
	}

};

devs.c

static struct resource s3c_i2c_resource[] = {
	[0] = {
		.start = S3C24XX_PA_IIC,
		.end   = S3C24XX_PA_IIC + S3C24XX_SZ_IIC - 1,
		.flags = IORESOURCE_MEM,
	},
	[1] = {
		.start = IRQ_IIC,
		.end   = IRQ_IIC,
		.flags = IORESOURCE_IRQ,
	}

};

struct platform_device s3c_device_i2c = {
	.name		  = "s3c2410-i2c",
	.id		  = -1,
	.num_resources	  = ARRAY_SIZE(s3c_i2c_resource),
	.resource	  = s3c_i2c_resource,
};

devs.c和i2c-s3c2410.c是标准的总线设备驱动模型,主要提供主机(s3c2440芯片)i2c控制器的寄存器和时序设置等操作,当设备和驱动匹配后,会调用s3c24xx_i2c_probe,s3c24xx_i2c_probe里又会通过ret = i2c_add_adapter(&i2c->adap);去调剂一个adapter,这个adapter就是给设备驱动(从机部分)开了一个口子,使得这两大部分又构成一个设备驱动模型。

分析i2c_add_adapter(i2c-core.c)

int i2c_add_adapter(struct i2c_adapter *adapter)
{
	int	id, res = 0;

retry:
	if (idr_pre_get(&i2c_adapter_idr, GFP_KERNEL) == 0)
		return -ENOMEM;

	mutex_lock(&core_lists);
	/* "above" here means "above or equal to", sigh */
	res = idr_get_new_above(&i2c_adapter_idr, adapter,
				__i2c_first_dynamic_bus_num, &id);
	mutex_unlock(&core_lists);

	if (res < 0) {
		if (res == -EAGAIN)
			goto retry;
		return res;
	}

	adapter->nr = id;
	return i2c_register_adapter(adapter);
}

static int i2c_register_adapter(struct i2c_adapter *adap)
{
  list_add_tail(&adap->list, &adapters);
	....
	....
	
	list_for_each(item,&drivers) {
		driver = list_entry(item, struct i2c_driver, list);
		if (driver->attach_adapter)
			/* We ignore the return code; if it fails, too bad */
			driver->attach_adapter(adap);
	}

}

i2c_add_adapter里调用了i2c_register_adapter,i2c_register_adapter里先把自己放入adapters数组,然后会循环遍历drivers这个链表,drivers存放的是设备驱动(从机部分),这个操作会调用
drivers里的每个driver的attach_adapter函数,这个函数会检测这个driver对应的设备是否匹配,这个后面会讲到。

接下来看下设备驱动(从机部分)代码,以chips下eeprom.c为例:

static int __init eeprom_init(void)
{
	return i2c_add_driver(&eeprom_driver);
}
static inline int i2c_add_driver(struct i2c_driver *driver)
{
	return i2c_register_driver(THIS_MODULE, driver);
}

int i2c_register_driver(struct module *owner, struct i2c_driver *driver)
{
	...
	...

	list_add_tail(&driver->list,&drivers);
	
		list_for_each_entry(adapter, &adapters, list) {
			driver->attach_adapter(adapter);
		}
	
...
...
}

eeprom.c里也会去注册一个driver,在i2c_register_driver里先把自己加入到drivers这个链表里,然后遍历adapters,取出每一个adapter然后调用driver的attach_adapter,这个步骤和前面注册adapter时处理一样,下面就看下attach_adapter这个方法。

static struct i2c_driver eeprom_driver = {
	.driver = {
		.name	= "eeprom",
	},
	.id		= I2C_DRIVERID_EEPROM,
	.attach_adapter	= eeprom_attach_adapter,
	.detach_client	= eeprom_detach_client,
};

static int eeprom_attach_adapter(struct i2c_adapter *adapter)
{
	return i2c_probe(adapter, &addr_data, eeprom_detect);
}

attach_adapter就是eeprom_driver 的attach_adapter 方法,这个方法会调用i2c_probe(adapter, &addr_data, eeprom_detect);addr_data是从设备地址,如果检测到从设备匹配成功则会触发eeprom_detect函数的执行。

下面分析i2c_probe:

int i2c_probe(struct i2c_adapter *adapter,
	      struct i2c_client_address_data *address_data,
	      int (*found_proc) (struct i2c_adapter *, int, int))
{

	...
		err = i2c_probe_address(adapter, address_data->normal_i2c[i],
					-1, found_proc);
   ...
	
}

static int i2c_probe_address(struct i2c_adapter *adapter, int addr, int kind,
			     int (*found_proc) (struct i2c_adapter *, int, int))
{
	...
	if (kind < 0) {
		if (i2c_smbus_xfer(adapter, addr, 0, 0, 0,
				   I2C_SMBUS_QUICK, NULL) < 0)
			return 0;

		/* prevent 24RF08 corruption */
		if ((addr & ~0x0f) == 0x50)
			i2c_smbus_xfer(adapter, addr, 0, 0, 0,
				       I2C_SMBUS_QUICK, NULL);
	}
	...
}

s32 i2c_smbus_xfer(struct i2c_adapter * adapter, u16 addr, unsigned short flags,
                   char read_write, u8 command, int size,
                   union i2c_smbus_data * data)
{
	...
		res = i2c_smbus_xfer_emulated(adapter,addr,flags,read_write,
	                                      command,size,data);

...
}

static s32 i2c_smbus_xfer_emulated(struct i2c_adapter * adapter, u16 addr,
                                   unsigned short flags,
                                   char read_write, u8 command, int size,
                                   union i2c_smbus_data * data)
{
...
	if (i2c_transfer(adapter, msg, num) < 0)
		return -1;
		...
		}

int i2c_transfer(struct i2c_adapter * adap, struct i2c_msg *msgs, int num)
{
...
		ret = adap->algo->master_xfer(adap,msgs,num);
		...
}

i2c_probe里面的层级调用关系如下:
i2c_probe
i2c_probe_address
i2c_smbus_xfer
i2c_smbus_xfer_emulated
i2c_transfer
adap->algo->master_xfer(adap,msgs,num);
可以看到,i2c_probe最终会调用到adapter里面 algo结构体的 master_xfer函数,adapter是在i2c-s3c2410.c 里构造的,也就是在设置是主机(s3c2440芯片)i2c控制器的寄存器和时序等操作时构造的。也就是说,master_xfer是一个真正i2c通信时数据传输函数,最终这个函数里会去发出中断和开始信号,并设置相关寄存器发起数据传输。

static const struct i2c_algorithm s3c24xx_i2c_algorithm = {
	.master_xfer		= s3c24xx_i2c_xfer,
	.functionality		= s3c24xx_i2c_func,
};

static struct s3c24xx_i2c s3c24xx_i2c = {
	.lock		= __SPIN_LOCK_UNLOCKED(s3c24xx_i2c.lock),
	.wait		= __WAIT_QUEUE_HEAD_INITIALIZER(s3c24xx_i2c.wait),
	.tx_setup	= 50,
	.adap		= {
		.name			= "s3c2410-i2c",
		.owner			= THIS_MODULE,
		.algo			= &s3c24xx_i2c_algorithm,
		.retries		= 2,
		.class			= I2C_CLASS_HWMON,
	},
};
static int s3c24xx_i2c_xfer(struct i2c_adapter *adap,
			struct i2c_msg *msgs, int num)
{
	struct s3c24xx_i2c *i2c = (struct s3c24xx_i2c *)adap->algo_data;
	int retry;
	int ret;

	for (retry = 0; retry < adap->retries; retry++) {

		ret = s3c24xx_i2c_doxfer(i2c, msgs, num);

		if (ret != -EAGAIN)
			return ret;

		dev_dbg(i2c->dev, "Retrying transmission (%d)\n", retry);

		udelay(100);
	}

	return -EREMOTEIO;
}
static int s3c24xx_i2c_doxfer(struct s3c24xx_i2c *i2c, struct i2c_msg *msgs, int num)
{
	unsigned long timeout;
	int ret;

	ret = s3c24xx_i2c_set_master(i2c);
	if (ret != 0) {
		dev_err(i2c->dev, "cannot get bus (error %d)\n", ret);
		ret = -EAGAIN;
		goto out;
	}

	spin_lock_irq(&i2c->lock);

	i2c->msg     = msgs;
	i2c->msg_num = num;
	i2c->msg_ptr = 0;
	i2c->msg_idx = 0;
	i2c->state   = STATE_START;

	s3c24xx_i2c_enable_irq(i2c);
	s3c24xx_i2c_message_start(i2c, msgs);
	spin_unlock_irq(&i2c->lock);
	
	timeout = wait_event_timeout(i2c->wait, i2c->msg_num == 0, HZ * 5);

	ret = i2c->msg_idx;

	/* having these next two as dev_err() makes life very 
	 * noisy when doing an i2cdetect */

	if (timeout == 0)
		dev_dbg(i2c->dev, "timeout\n");
	else if (ret != num)
		dev_dbg(i2c->dev, "incomplete xfer (%d)\n", ret);

	/* ensure the stop has been through the bus */

	msleep(1);

 out:
	return ret;
}


static void s3c24xx_i2c_message_start(struct s3c24xx_i2c *i2c, 
				      struct i2c_msg *msg)
{
	unsigned int addr = (msg->addr & 0x7f) << 1;
	unsigned long stat;
	unsigned long iiccon;

	stat = 0;
	stat |=  S3C2410_IICSTAT_TXRXEN;

	if (msg->flags & I2C_M_RD) {
		stat |= S3C2410_IICSTAT_MASTER_RX;
		addr |= 1;
	} else
		stat |= S3C2410_IICSTAT_MASTER_TX;

	if (msg->flags & I2C_M_REV_DIR_ADDR)
		addr ^= 1;

	// todo - check for wether ack wanted or not
	s3c24xx_i2c_enable_ack(i2c);

	iiccon = readl(i2c->regs + S3C2410_IICCON);
	writel(stat, i2c->regs + S3C2410_IICSTAT);
	
	dev_dbg(i2c->dev, "START: %08lx to IICSTAT, %02x to DS\n", stat, addr);
	writeb(addr, i2c->regs + S3C2410_IICDS);
	
	/* delay here to ensure the data byte has gotten onto the bus
	 * before the transaction is started */

	ndelay(i2c->tx_setup);

	dev_dbg(i2c->dev, "iiccon, %08lx\n", iiccon);
	writel(iiccon, i2c->regs + S3C2410_IICCON);
	
	stat |=  S3C2410_IICSTAT_START;
	writel(stat, i2c->regs + S3C2410_IICSTAT);
}

总结:i2c驱动框架也是一个总线设备驱动模型,它可以分为两大部分,第一部分就是标准的总线设备驱动模型,这一部分主要封装对主机(s3c2440芯片)i2c控制器的相关操作,这些操作就是对应芯片手册里的相关寄存器设置等操作,并且还会去注册一个adapter链表,这个adapter里封装一些真正数据传输函数,这样的好处时我们在开发一个具体从设备驱动时,不用关系主机控制器设置这些复杂操作了,我们只需要调用它提供的传输函数就可完成数据的收发。

开发一个i2c驱动时,我们只需关心从设备部分,主要提供主机控制器操作所需要的从设备信息,这就是另一部分。在注册adapter和从设备driver时,两变都会把自己添加进自己的链表里,然后遍历对方链表去调用driver的attach_adapter方法进行匹配。从这个层面看,这两大部分又构成了一个总线设备驱动模型,而从设备驱动类型设备,主要提供主机部分所需操作信息。
linux-2.6.22.6内核i2c驱动框架源码分析
i2c驱动实例:

#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/jiffies.h>
#include <linux/i2c.h>
#include <linux/mutex.h>
#include <linux/fs.h>
#include <asm/uaccess.h>

static unsigned short ignore[]      = { I2C_CLIENT_END };
static unsigned short normal_addr[] = { 0x50, I2C_CLIENT_END }; /* 地址值是7位 */
                                        /* 改为0x60的话, 由于不存在设备地址为0x60的设备, 所以at24cxx_detect不被调用 */

static unsigned short force_addr[] = {ANY_I2C_BUS, 0x60, I2C_CLIENT_END};
static unsigned short * forces[] = {force_addr, NULL};
										
static struct i2c_client_address_data addr_data = {
	.normal_i2c	= normal_addr,  /* 要发出S信号和设备地址并得到ACK信号,才能确定存在这个设备 */
	.probe		= ignore,
	.ignore		= ignore,
	//.forces     = forces, /* 强制认为存在这个设备 */
};

static struct i2c_driver at24cxx_driver;


static int major;
static struct class *cls;
struct i2c_client *at24cxx_client;

static ssize_t at24cxx_read(struct file *file, char __user *buf, size_t size, loff_t * offset)
{
	unsigned char address;
	unsigned char data;
	struct i2c_msg msg[2];
	int ret;
	
	/* address = buf[0] 
	 * data    = buf[1]
	 */
	if (size != 1)
		return -EINVAL;
	
	copy_from_user(&address, buf, 1);

	/* 数据传输三要素: 源,目的,长度 */

	/* 读AT24CXX时,要先把要读的存储空间的地址发给它 */
	msg[0].addr  = at24cxx_client->addr;  /* 目的 */
	msg[0].buf   = &address;              /* 源 */
	msg[0].len   = 1;                     /* 地址=1 byte */
	msg[0].flags = 0;                     /* 表示写 */

	/* 然后启动读操作 */
	msg[1].addr  = at24cxx_client->addr;  /* 源 */
	msg[1].buf   = &data;                 /* 目的 */
	msg[1].len   = 1;                     /* 数据=1 byte */
	msg[1].flags = I2C_M_RD;                     /* 表示读 */


	ret = i2c_transfer(at24cxx_client->adapter, msg, 2);
	if (ret == 2)
	{
		copy_to_user(buf, &data, 1);
		return 1;
	}
	else
		return -EIO;
}

static ssize_t at24cxx_write(struct file *file, const char __user *buf, size_t size, loff_t *offset)
{
	unsigned char val[2];
	struct i2c_msg msg[1];
	int ret;
	
	/* address = buf[0] 
	 * data    = buf[1]
	 */
	if (size != 2)
		return -EINVAL;
	
	copy_from_user(val, buf, 2);

	/* 数据传输三要素: 源,目的,长度 */
	msg[0].addr  = at24cxx_client->addr;  /* 目的 */
	msg[0].buf   = val;                   /* 源 */
	msg[0].len   = 2;                     /* 地址+数据=2 byte */
	msg[0].flags = 0;                     /* 表示写 */

	ret = i2c_transfer(at24cxx_client->adapter, msg, 1);
	if (ret == 1)
		return 2;
	else
		return -EIO;
}


static struct file_operations at24cxx_fops = {
	.owner = THIS_MODULE,
	.read  = at24cxx_read,
	.write = at24cxx_write,
};

static int at24cxx_detect(struct i2c_adapter *adapter, int address, int kind)
{	
	printk("at24cxx_detect\n");

	/* 构构一个i2c_client结构体: 以后收改数据时会用到它 */
	at24cxx_client = kzalloc(sizeof(struct i2c_client), GFP_KERNEL);
	at24cxx_client->addr    = address;
	at24cxx_client->adapter = adapter;
	at24cxx_client->driver  = &at24cxx_driver;
	strcpy(at24cxx_client->name, "at24cxx");
	i2c_attach_client(at24cxx_client);
	
	major = register_chrdev(0, "at24cxx", &at24cxx_fops);

	cls = class_create(THIS_MODULE, "at24cxx");
	class_device_create(cls, NULL, MKDEV(major, 0), NULL, "at24cxx"); /* /dev/at24cxx */
	
	return 0;
}

static int at24cxx_attach(struct i2c_adapter *adapter)
{
	return i2c_probe(adapter, &addr_data, at24cxx_detect);
}

static int at24cxx_detach(struct i2c_client *client)
{
	printk("at24cxx_detach\n");
	class_device_destroy(cls, MKDEV(major, 0));
	class_destroy(cls);
	unregister_chrdev(major, "at24cxx");

	i2c_detach_client(client);
	kfree(i2c_get_clientdata(client));

	return 0;
}


/* 1. 分配一个i2c_driver结构体 */
/* 2. 设置i2c_driver结构体 */
static struct i2c_driver at24cxx_driver = {
	.driver = {
		.name	= "at24cxx",
	},
	.attach_adapter = at24cxx_attach,
	.detach_client  = at24cxx_detach,
};

static int at24cxx_init(void)
{
	i2c_add_driver(&at24cxx_driver);
	return 0;
}

static void at24cxx_exit(void)
{
	i2c_del_driver(&at24cxx_driver);
}

module_init(at24cxx_init);
module_exit(at24cxx_exit);

MODULE_LICENSE("GPL");

在这个驱动程序里,首先会注册at24cxx_driver,这个注册会触发at24cxx_attach函数的执行,这个函数会通过 i2c_probe(adapter, &addr_data, at24cxx_detect)去触发内核框架函数检测设备地址addr_data是否能匹配成功,如果匹配成功则会调用at24cxx_detect函数。

在at24cxx_detect里,进行了字符设备驱动类和设备的创建,然后就是字符设备驱动那一套东西了,在at24cxx_read里构造好数据调用框架函数i2c_transfer进行数据读取,在at24cxx_write里构造好数据调用框架函数i2c_transfer进行数据写入。文章来源地址https://www.toymoban.com/news/detail-514404.html

到了这里,关于linux-2.6.22.6内核i2c驱动框架源码分析的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Linux I2C 驱动实验

    目录 一、Linux I2C 驱动简介 1、I2C 总线驱动 2、I2C 设备驱动 1、 i2c_client 结构体 2、 i2c_driver 结构体 二、硬件分析 三、设备树编写 1、pinctrl_i2c1 2、在 i2c1 节点追加 ap3216c 子节点 3、验证 四、 代码编写 1、makefile 2、ap3216c.h  3、ap3216c.c ①、头文件 ②、驱动出入口  ③、 i2c驱动

    2024年02月08日
    浏览(41)
  • TP驱动——I2C驱动,细节分析——dts设备树

    TP驱动程序,是指带屏幕设备的触屏功能调用的驱动程序。TP外设与主板之间的关系如下框图: 产品常用的屏幕单元为液晶显示屏幕,TP的触摸功能也是一同集成到一起的,通过控制芯片以及外围电路、i2c总线与主板进行通信。 TP驱动的功能逻辑: 当有触点发生时,TP芯片向主

    2024年02月04日
    浏览(28)
  • STM32 SHT40驱动源码(使用硬件I2C)

    目录 简介: SHT40.c: SHT40.h 测试结果:         SHT40是瑞士Sensirion公司推出的第四代温湿度传感器,内部集成加热器用于去除表面微小液滴。集成I2C接口,典型的相对湿度精度1.8%RH,典型温度精度0.2℃,运行在0-100%RH和-40-125℃的环境中。 主控:STM32H7B0VBT6 平台:STM32CubeIDE SHT4

    2024年03月19日
    浏览(57)
  • Linux Kernel 4.19+内核使用GPIO模拟I2C的方法

    1.修改内核配置文件,使内核支持GPIO模拟I2C 2.对应的dts里面增加GPIO模拟I2C的设备树 编译后烧写,会发现/dev下多了一个i2c总线,多出来的那个就是了。

    2024年02月16日
    浏览(34)
  • Linux驱动开发(I2C系统的重要结构体)

    本篇文章来讲解I2C系统的重要结构体,了解这些结构体对于编写I2C驱动来说是至关重要的,所以要想编写好一个I2C驱动程序那么就必须先了解这些结构体。 这里使用百问网的一张图片来讲解: 一个芯片中可以有多个I2C控制器,并且一个I2C控制器可以控制多个设备。那么在对

    2024年02月07日
    浏览(33)
  • linux i2c驱动开发之最简单入门:设备树与驱动的匹配

    1在设备树中声明我们的模拟I2C设备: i2c的设备加入是必须放在某个i2c的总线下面的,我们这里是放在 i2c7下面。 可以直接在设备树搜索i2c7,然后把status 修改为okay,然后再加入设备信息. 这里我们指定和驱动匹配的字符串是 myi2c-dev,在bus segment 上分配的地址是 0x70 。 当然这

    2024年02月16日
    浏览(33)
  • Linux设备驱动开发学习笔记(等待队列,锁,字符驱动程序,设备树,i2C...)

    container_of函数可以通过结构体的成员变量检索出整个结构体 函数原型: 内核开发者只实现了循环双链表,因为这个结构能够实现FIFO和LIFO,并且内核开发者要保持最少代码。 为了支持链表,代码中要添加的头文件是linux/list.h。内核中链表实现核心部分的数据结构 是struct li

    2024年01月22日
    浏览(38)
  • 详解AT24CXX驱动开发(linux platform tree - i2c应用)

    目录 概述 1 认识AT24Cxx 1.1 AT24CXX的特性 1.2 AT24CXX描述 1.2.1 引脚 1.2.2 容量描述 1.2.3 设备地址 1.3 操作时序 1.3.1 写单个字节时序 1.3.2 写page字节时序 1.3.3 读取当前数据时序 1.3.4 随机读取数据 1.3.5 连续读取多个数据 2 驱动开发 2.1 硬件接口 2.2 代码实现 2.2.1 查看设备信息 2.2.2 编写

    2024年02月22日
    浏览(33)
  • EEPROM芯片(24c02)使用详解(I2C通信时序分析、操作源码分析、原理图分析)

    (1)本文主要是通过24c02芯片来讲解I2C接口的EEPROM操作方法,包含底层时序和读写的代码; (2)大部分代码是EEPROM芯片通用的,但是其中关于某些时间的要求,是和具体芯片相关的,和主控芯片和外设芯片都有关系,需要具体分析,但是逻辑顺序是不变的; (1)在嵌入式开发中,

    2024年02月09日
    浏览(53)
  • I2C总线驱动

    SOC芯片平台的外设分为: 一级外设:外设控制器集成在SOC芯片内部 二级外设:外设控制器由另一块芯片负责,通过一些通讯总线与SOC芯片相连 Inter-Integrated Circuit: 字面意思是用于“集成电路之间”的通信总线,简写:IIC(或者I2C) i2c传输的要点就是: 传输一个字节 后面必然

    2024年02月15日
    浏览(28)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包