成员。我们可以看到消息结构体里面有从设备地址,读写标志,数据长度以及存储数据buf。这些成员我们看完之后会发现它大致符合先给设备地址,然后给写信号以及数据的时序。其实但我们写代码的时候并不一定是addr非得定义在flags前面,因为内核会自动帮助我们完成这些具体的时序操作。但有一点,我们必须要填充好nmsgs以及i2c_msg中的成员。
那么我们具体的i2c下的 ioctl 函数是怎么样的呢?我们暂且把i2c-dev.c看作一个设备驱动。里面的fops结构体显示
我们继续追踪看看i2cdev_ioctl这个函数
static long i2cdev_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
struct i2c_client *client = file->private_data;
unsigned long funcs;
dev_dbg(&client->adapter->dev, "ioctl, cmd=0x%02x, arg=0x%02lx\n",
cmd, arg);
switch (cmd) {
case I2C_SLAVE:
case I2C_SLAVE_FORCE:
/* NOTE: devices set up to work with "new style" drivers
* can't use I2C_SLAVE, even when the device node is not
* bound to a driver. Only I2C_SLAVE_FORCE will work.
*
* Setting the PEC flag here won't affect kernel drivers,
* which will be using the i2c_client node registered with
* the driver model core. Likewise, when that client has
* the PEC flag already set, the i2c-dev driver won't see
* (or use) this setting.
*/
if ((arg > 0x3ff) "|
(((client->flags & I2C_M_TEN) == 0) && arg > 0x7f))
return -EINVAL;
if (cmd == I2C_SLAVE && i2cdev_check_addr(client->adapter, arg))
return -EBUSY;
/* REVISIT: address could become busy later */
client->addr = arg; //设置addr
return 0;
case I2C_TENBIT://设置10 bit地址模式
if (arg)
client->flags |= I2C_M_TEN;
else
client->flags &= ~I2C_M_TEN;
return 0;
case I2C_PEC://设置传输后增加PEC标志
if (arg)
client->flags |= I2C_CLIENT_PEC;
else
client->flags &= ~I2C_CLIENT_PEC;
return 0;
case I2C_FUNCS://获取函数支持
funcs = i2c_get_functionality(client->adapter);
return put_user(funcs, (unsigned long __user *)arg);
case I2C_RDWR://读取和发送数据
return i2cdev_ioctl_rdrw(client, arg);
case I2C_SMBUS: //SMBUS协议数据传输
return i2cdev_ioctl_smbus(client, arg);
case I2C_RETRIES://设置重试次数
client->adapter->retries = arg;
break;
case I2C_TIMEOUT://设置超时时间
/* For historical reasons, user-space sets the timeout
* value in units of 10 ms.
*/
client->adapter->timeout = msecs_to_jiffies(arg * 10);
break;
default:
/* NOTE: returning a fault code here could cause trouble
* in buggy userspace code. Some old kernel bugs returned
* zero in this case, and userspace code might accidentally
* have depended on that bug.
*/
return -ENOTTY;
}
return 0;
}
对于简单使用来说,我现在并没有全深入整明白,所以暂且知道:
ioctl是设备驱动程序中对设备的I/O通道进行管理的函数。
在驱动程序中实现的ioctl函数体内,实际上是有一个switch{case}的结构,每一个case对应一个cmd操作命令,并有相对应的操作。
我这里cmd参数使用的是I2C_RDWR这个命令码,根据i2c-dev.c里的源码看i2cdev_ioctl_rdrw结构体可知
I2C 设备的写操作经历了如下几个步骤。
(1) 从用户空间到字符设备驱动写函数接口,写函数构造 I2C 消息数组。
(2) 写函数把构造的 I2C 消息数组传递给 I2C 核心的传输函数 i2c_transfer()。
(3) I2C 核心的传输函数 i2c_transfer()找到对应适配器 algorithm 的通信方法函数 master_xfer()去最终完成 I2C 消息的处理。
PS:I2c_transfer这个函数实现了core与adapter的联系。想更深入的探究,可以自己去看看I2C总线驱动中的I2C_algorithm结构以及其中的s3c24xx_i2c_xfer(),s3c24xx_i2c_doxfer()和s3c24xx_i2c_message_start()函数和i2c_transfer()函数。
下面我们看看代码在开发板中运行的现象:
1.在把可执行文件放入开发板启动之前我们先检查下I2C控制器s3c2410-i2c节点是否配置好。
上面的i2c_dev/i2c-0是在注册i2c-dev.c后产生的,代表一个可操作的适配器。如果不使用i2c-dev.c的方式,就没有,也不需要这个节点。
2.确保出现I2C控制器s3c2410-i2c后即可通过交叉编译器将编译后可执行文件放入开发板中执行。
仔细看上面的结果。可以看到我们原本的数据test1234\n在上面的显示中出了点问题。后来我又测试一次test123\n与test12345678
我第一次是通过at24.c的read和write以及lseek直接对eeprom读写,所以可以一次写入超过8个字节,第二次通过内部i2c控制器的ioctl来间接读写eeprom时则遭遇了阻击,一次时序,若超过8个字节,超过的字节数自动将前面的数据覆盖掉。然后我想到at24c02是32个页,一页8个字节。通过网上查阅知:
===================================================================================================
================================================================================================================================
由于E2PROM的半导体工艺特性,对E2PROM的写入时间要5~10ms,但AT24CXX系列串行E2PROM芯片内部设置了一个具有SRAM性质的输入缓冲器,称为页写缓冲器。CPU对该芯片写操作时,AT24CXX系列芯片先将CPU输入的数据暂存页写缓冲器内,然后,慢慢写入E2PROM中。因此,CPU对AT24CXX系列E2PROM一次写入的数据,受到该芯片页写缓冲器容量的限制。页写缓冲器的容量:AT24C01A/02为8B,AT24C04/08/16为16B,AT24C32/64为32B。
====================================================================================================
注意:
写AT24CXX应用时,若CPU需写入超过芯片页写缓冲器容量的字节数据,应在一页写完后,隔5~10ms重新启动一次写操作。其次,若不是从页写缓冲器零地址(指AT24CXX片内末位地址0或8)写起,一次写入不能超出页内地址111,若超出页写缓冲器最大地址时,也应将超出部分,隔5~10ms重新启动一次写操作。
最后通我们回顾AT24C02的官方datasheet来看看本质:
Byte write:
==================================================================================================================
Byte write的操作时序如上图所示。主机在发送device address,并且接受到确定回应Ask后再接着发送需要写的地址(把这个数据写到芯片的哪个地址上),然后收到确定回应ask后再发送数据。当AT24C02接受完毕这个数据时会输出一个确认回应Ack,此时主机发送一个停止信号Stop,然后AT240C2进入写时序,将刚才接受到的数据从缓冲器写到存储单元中,并在此期间不响应任何输入,直到操作完成。
==================================================================================================================
Pagewrite:
==================================================================================================================
Page write前面几步的操作和Bytewrite操作类似,只是在成功发送第一个数据之后,主机在收到AT24C02的确认回应Ask之后不会发送停止信号Stop而是继续发送剩余的7个字节数据。直到一个page的8字节数据发送完毕之后才发送停止信号Stop。在页操作的时候word address用与表示业内的低地址的低3bit会每收到一个数据就自动增长,页地址维持不变。所以,当业内地址到顶端时,此时假如还有数据,则数据将会被放到页的起始地址处,页起始地址中之前存放的数据也将会被覆盖。即AT24C02页操作时,写入的数据大于8byte,则大于8byte的数据将重新从此页起始处存放,并覆盖掉之前写入的的数据
==================================================================================================================
另外我们再说一下随机读取:
==================================================================================================================
随机读写的操作就是主机先用一个写操作来骗过AT24C02器件,使AT24C02内部的data word address中的地址值修改,然后再通过current ad
[1] [2] [3] [4]
本网站转载的所有的文章、图片、音频视频文件等资料的版权归版权所有人所有,本站采用的非本站原创文章及图片等内容无法一一联系确认版权者。如果本网所选内容的文章作者及编辑认为其作品不宜公开自由传播,或不应无偿使用,请及时通过电子邮件或电话通知我们,以迅速采取适当措施,避免给双方造成不必要的经济损失。