最全的IIC介绍及其设备驱动编写
最全的IIC介绍及其设备驱动编写
参考资料:
1、IIC介绍
IIC是通信协议中的⼀种,为⼀主多从的结构,对于主从,所有的数据都是从主机这边发起,从机只能接受,不能主动引起数据传输,只有两条总线线路:⼀条串⾏数据线(SDA),⼀条串⾏时钟线(SCL),IIC有硬件IIC和软件IIC,这⾥简单解释,硬件IIC为硬件构成的IIC,⼀般只需要操作相关寄存器即可,对于软件IIC,可以由IO⼝来模拟IIC总线进⾏对IIC设备的通信。
对于多个IIC设备来说,每个连接到总线的器件都可以使⽤软件根据它的唯⼀地址来识别,当我们要使⽤⼀个IIC设备时,主机通过SDA线向各IIC设备寻址,等待地址对应的IIC设备的回应即ACK信号。
IIC是半双⼯的,即SDA总线是双向传输的。两条线在硬件上都使⽤了开极电路,且都接有上拉电阻(即空闲时,SDA和SCL都为⾼电平)。
[外链图⽚转存失败,源站可能有防盗链机制,建议将图⽚保存下来直接上传(img-uCKvuXrc-1628932720637)
(C:\Users\liang\AppData\Roaming\Typora\typora-user-images\image-20210812203900555.png)]传输过程:
当主机A要发送数据时,A拉低SDA总线,稍后向SCL输送时钟信号,开始传输数据(在每个时钟周期中,当SCL为⾼电平时,从机从SDA 线上采样(获得)数据;当SCL为低电平时,主机向SDA传送(更新)数据)。主机每传完8个⽐特位,就将SDA释放(SDA恢复到⾼电平),若从机正常接收完8个⽐特位,就将SDA线拉低,以表⽰向主机回送⼀个ACK信号,若没有接收到8个⽐特位,就不会拉低SDA线,主机就收不到ACK信号,所以主机会发送⼀个终⽌或开始(重传)信号给从机。当主机已经完成数据传输时,会先释放SCL线,然后再释放SDA线。
并⾮每传输8位数据之后,都会有ACK信号,有以下3种例外:
1. 当从机不能响应从机地址时(例如它正忙于其他事⽽⽆法响应I²总线的操作,或者这个地址没有对应的从机),在第9个SCL周期内
SDA线没有被拉低,即没有ACK信号。这时,主机发出⼀个Р信号终⽌传输或者重新发出⼀个S信号开始新的传输.
2. 如果从机接收器在传输过程中不能接收更多的数据时,它也不会发出ACK信号。这样,主机就可以意识到这点,从⽽发出⼀个Р信号
终⽌传输或者重新发出⼀个S信号开始新的传输.
3. 主机接收器在接收到最后⼀个字节后,也不会发出ACK信号。于是,从机发送器释放SDA线,以允许主机发出Р信号结束传输
使⽤IIC协议的芯⽚介绍—AT24C02/04/08/16
AT24Cxx系列芯⽚是采⽤IIC协议的EEPROM芯⽚,其读写过程如下:
写过程:主机先向芯⽚发出设备地址,再发出写地址后就可以开始传输数据;
读过程:主机先向芯⽚发出设备地址,再发出读地址,然后需要再发出⼀次设备地址,才能读数据,是因为AT24Cxx容量各有不同,有
1K,2K,4K,8K,16K等⼤⼩,对于4K芯⽚来说其架构为512*8bit,⽽512为2的9次⽅,因此只传输⼀次读地址是不够的。
2、IIC驱动框架
I²C驱动框架:
App:open()、 read() 、write()
教师节礼物小学生送什么合适
IIC设备驱动程序:drv_open()、 drv_read() 、drv_write() 知道数据含义
IIC总线驱动程序:1.识别设备; 2.提供读写函数 。 知道怎么收发数据
IIC硬件:如AT24C02/AT24C08
这三层通过总线设备驱动模型联系在⼀块,在内核中有很多虚拟的总线例如platfoem_bus_type,对于IIC来说是i2c_bus_type总线,在这条总线中有i2c_client和i2c_driver两条链表,当i2c_client加到链表中后,会先跟i2c_driver链表的每⼀项⽐较,如果能匹配的话就调⽤相对应的i2c_driver的probe函数;对于i2c_driver加到链表中后,⼀样会先跟i2c_client链表的每⼀项⽐较,如果能匹配的话就调⽤i2c_driver的probe函数。匹配是调⽤i2c_bus_type总线中的match函数进⾏⽐较,其中⽐较的是id_table,⽽在id_table中⽐较的是两者链表中的name,因此简单描述如下:
[外链图⽚转存失败,源站可能有防盗链机制,建议将图⽚保存下来直接上传(img-49sBhfqD-1628932720646)
(C:\Users\liang\AppData\Roaming\Typora\typora-user-images\image-20210813194017090.png)]
1. 左边注册⼀个设备:i2c_client
2. 右边注册⼀个驱动:i2c_driver
3. ⽐较他们的名字,如果相同,则调⽤probe函数
4. probe函数⾥,可以注册字符设备驱动
在i2c_bus_type总线中,dev链表不仅有i2c_client还有i2c_adapter,后⾯再解释。
对于裸板程序来说,可以直接对寄存器进⾏映射使⽤,但在内核中有完善的IIC总线驱动程序,会帮我们去发出起始信号,识别IIC设备参考i2c-s3c2410.c,在⼊⼝函数中注册了平台总线,在probe函数中设置adap并注册添加了adap(IIC适配
器:i2c_add_adapter>i2c_register_adapter),在设置adap中的algo(算法)设置s3c24xx_i2c_xfer函数,这个函数实现了识别IIC设备的功能(发S信号,设备地址,等待应答)
IIC总线驱动程序在内核源码中drivers\i2c\busses中参考i2c-s3c2410.c,在⼊⼝函数中注册了平台总线,在probe函数中设置adap 并注册添加了adap(IIC适配器:i2c_add_adapter>i2c_register_adapter),在设置adap中的algo(算法)设置s3c24xx_i2c_xfer函数,这个函数实现了识别IIC设备的功能(发S信号,设备地址,等待应答)
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 __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 int s3c24xx_i2c_probe(struct platform_device *pdev)
{
...
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);
...
}
...
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;
}
参考IIC设备驱动\drivers\i2c\chips\eeprom.c,在⼊⼝函数中i2c_add_driver添加driver设备,attach_adapter会加到适配器中去,在i2c_add_driver过程中总线为i2c_bus_type,会先把i2c_driver放⼊总线的dri链表,从adap链表中取出"适配器"并调⽤attach_adapter,⽽attach_adapter会调⽤i2c_probe,其中会使⽤master_xfer函数发S信号,发设备地址,如果能收到ACK信号,则会调⽤i2c_probe参数中的function函数,这其中我们可以注册字符设备驱动来完善IIC设备驱动程序,对于总线驱动程序中i2c_add_adapter函数会把adap(适配器)放⼊链表,⼀样会调⽤dri的attach_adapter然后调⽤master_xfer函数。
int i2c_register_driver(struct module *owner,struct i2c_driver *driver)
桃李满天下的下一句
{
int res;
...
if(driver->attach_adapter){
struct i2c_adapter *adapter;
list_for_each_entry(adapter,&adapters, list){
草药学训练师
driver->attach_adapter(adapter);
}
}
mutex_unlock(&core_lists);
return0;
}
static int eeprom_attach_adapter(struct i2c_adapter *adapter)
{
鬼泣4第8关return i2c_probe(adapter,&addr_data, eeprom_detect);
}
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 __init eeprom_init(void)
淘宝 团购{
return i2c_add_driver(&eeprom_driver);
}
i2c_probe函数过程如下:
i2c_probe(adapter, &addr_data, eeprom_detect);
i2c_probe_address // 发出S信号,发出设备地址(来⾃addr_data)
i2c_smbus_xfer
i2c_smbus_xfer_emulated
i2c_transfer
adap->algo->master_xfer // s3c24xx_i2c_xfer
i2c_add_driver:
1.把i2c_driver放⼊链表;
2.从adap链表⾥取出适配器,调⽤drv的attch_adapter函数;
3.调⽤i2c_probe(adapter, &addr_data, function),master_xfer函数⽤adapter参数发信号,确定有⽆设备,有则调⽤
function;
4.构造i2c_client结构体(.address .adapter(指向左边的adap) .driver(指向右边的dri) )
i2c_add_adapter:
1.把adap适配器放⼊链表;
2.调⽤drv的attch_adapter函数;
3.同上
3、⾃⼰编写IIC驱动程序(Linux2.6)
## 3.1基本框架
以I2C设备(EEPROM芯⽚)AT24CXX为例,编写其驱动程序的基本步骤如下:
1 分配⼀个i2c_driver结构体
2 设置
attach_adapter // 它直接调⽤ i2c_probe(adap, 设备地址, 发现这个设备后要调⽤的函数);
detach_client // 卸载这个驱动后,如果之前发现能够⽀持的设备,则调⽤它来清理
3 注册:i2c_add_driver
参考设备m41t00.c中设备地址怎么写(由AT24CXX的数据⼿册查得其地址为0xA0),在normal_i2c参数中地址值只需要7位,若识别设备先加⼊打印语句测试:

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。