22_Linux 3.4.2 IIC驱动
文章目录
1、框架介绍
1.1、连接图
1.2、IIC读写操作原理
黑色->主到从、红色->从到主
写:1_START、2_设备地址(7位) | 方向(1位)、3_回应、4_数据(8位) | 回应、5_P(结束)
读:1_START、2_设备地址(7位) | 方向(1位)、3_回应、4_数据(8位) | 回应、5_数据 | 回应、6_P(结束)
方向:0->写 1->读
START:SCL保持高电平、SDA从高变低。
P(停止):SCL保持高电平、SDA从低变高。
主设备发出START信号之后,用9个CLK来传输8位数据
第9个CLK是回应信号ACK(SDA为低电平)。
SCL低电平时,SDA可以变化;SCL高电平时,SDA保持不变。
写操作:
读操作:
1.3、数据的双向传输
1 主设备发送时,从设备不发送:可以通过SCL区分
2 主设备发送时,从设备的“发送引脚”不影响数据发送,反之相同
发送引脚连接三极管如图:
ACK:SDA为低电平
A B SDA
0 0 1(由上拉电阻决定)
0 1 0
1 0 0
1 1 0
① 不影响SDA,不驱动三极管;②想输出高电平,不驱动;③想输出低电平,驱动
例:主——(8bit)—>从
① 前8个CLK
从设备不要影响,不驱动三极管
主设备决定数据
② 第9个CLK,由从设备决定数据
主设备不驱动三极管,SDA高
从设备决定数据:
SDA高:NO ACK
SDA低:ACK
1.4、2440的IIC控制器
1 设置IICCON来设置时钟
2 设置分频系数把时钟降低为想要的频率
3 设置IICSTAT发出START信号
4 将数据写入IICDS中,之后就自动的设置时钟、将数据通过SDA发送出去
5 第9个CLK来检查IICSTAT查询是否有ACK信号:
有ACK表示数据成功发出,继续发下一个数据:再次将数据写入IICDS中…
无表示数据发送失败
6 发完数据不想再发了,就设置IICSTAT来发送P信号停止发送
在第9个CLK发生中断,中断处理过程,SCL为低,谁都不能使用IIC:
① 对于写:
若无ACK:出错结束(发出P信号)
若有ACK:
若任有数据,写入IICDS,清中断
若无,结束
② 对于读:
回应一个ACK信号:
还想再读:清中断,启动传输
不想读了:结束
重点:发生IIC中断时,SCL被拉低。阻止继续使用IIC,清中断后才能继续使用。
S3C2440读写数据流程:
写(发送):
① IICDS = val
② 发完,产生中断,拉低SCL
③ 中断程序里,判断状态,IICDS = val,IIC继续工作②
读(接收):
① 发起传输,接收数据
② 产生中断,SCL被拉低
③ 中断程序中,判断,设置好之后,value= IICDS,IIC继续工作,继续接收新数据—>②
IIC控制器传输信号:
IIC传输原理:
主机发送地址时:
由于i2c设备不同,地址数量也不同。在1k2k都只有一页地址,4k有2页。。。
发送地址时:
P2 P1 P0表示不同的页数;A2 A1 A0为硬件引脚。
例如:AT24C02只有一个page,8位的地址可以访问到;
AT24C08有4个page,在Start信号之后,再发出设备地址时可以指定访问那一页。
1.5、字符设备驱动框架
1.6、IIC驱动框架
1.7、IIC总线设备驱动模型
2、框架编写代码
bus-drv-dev模型及写程序,设备的4种构建方法
2.1、定义一个i2c_board_info, 里面有:名字, 设备地址
然后i2c_register_board_info(busnum, ...) (把它们放入__i2c_board_list链表)
list_add_tail(&devinfo->list, &__i2c_board_list);
链表何时使用:
i2c_register_adapter > i2c_scan_static_board_info > i2c_new_device
使用限制:必须在 i2c_register_adapter 之前 i2c_register_board_info
所以:不适合我们动态加载insmod
2.2、直接i2c_new_device, i2c_new_probed_device
1、i2c_new_device : 认为设备肯定存在,地址50改为60
2、i2c_new_probed_device :对于"已经识别出来的设备"(probed_device),才会创建("new")
i2c_new_probed_device
probe(adap, addr_list[i]) /* 确定设备是否真实存在 */
info->addr = addr_list[i];
i2c_new_device(adap, info);
2.3、从用户空间创建设备
创建设备
echo at24c08 0x50 > /sys/class/i2c-adapter/i2c-0/new_device
导致i2c_new_device被调用
删除设备
echo 0x50 > /sys/class/i2c-adapter/i2c-0/delete_device
导致i2c_unregister_device
2.4、前面的3种方法都要事先确定适配器(I2C总线,I2C控制器)
如果我事先并不知道这个I2C设备在哪个适配器上,怎么办?去class表示的所有的适配器上查找,有上一些I2C设备的地址是一样,怎么继续分配它是哪一款?用detect函数。
static struct i2c_driver at24cxx_driver = {
.class = I2C_CLASS_HWMON, /* 表示去哪些适配器上找设备 */
.driver = {
.name = "100ask",
.owner = THIS_MODULE,
},
.probe = at24cxx_probe,
.remove = __devexit_p(at24cxx_remove),
.id_table = at24cxx_id_table,
.detect = at24cxx_detect, /* 用这个函数来检测设备确实存在 */
.address_list = addr_list, /* 这些设备的地址 */
};
去"class表示的这一类"I2C适配器,用"detect函数"来确定能否找到"address_list里的设备",
如果能找到就调用i2c_new_device来注册i2c_client, 这会和i2c_driver的id_table比较,
如果匹配,调用probe。
i2c_add_driver
i2c_register_driver
driver_register:
a. at24cxx_driver放入i2c_bus_type的drv链表
并且从dev链表里取出能匹配的i2c_client并调用probe
b. 对于每一个适配器,调用__process_new_driver
对于每一个适配器,调用它的函数确定address_list里的设备是否存在
如果存在,再调用detect进一步确定、设置,然后i2c_new_device
/* Walk the adapters that are already present */
i2c_for_each_dev(driver, __process_new_driver);
__process_new_driver
i2c_do_add_adapter
/* Detect supported devices on that bus, and instantiate them */
i2c_detect(adap, driver);
for (i = 0; address_list[i] != I2C_CLIENT_END; i += 1) {
err = i2c_detect_address(temp_client, driver);
/* 判断这个设备是否存在:简单的发出S信号确定有ACK */
if (!i2c_default_probe(adapter, addr))
return 0;
memset(&info, 0, sizeof(struct i2c_board_info));
info.addr = addr;
// 设置info.type
err = driver->detect(temp_client, &info);
i2c_new_device
3、编写设备驱动
4、用户态直接访问
不自己写驱动直接访问
Device Drivers
I2C support
<*> I2C device interface
nfs 30000000 192.168.1.123:/work/nfs_root/uImage_i2c; bootm 30000000
i2c-dev已经把0x50这个设备在内核中使用了,再来装载的话就不会成功:
5、编写总线驱动程序
Device Drivers
I2C support
I2C Hardware Bus support
< > S3C2410 I2C Driver
nfs 30000000 192.168.1.123:/work/nfs_root/uImage_noi2cbus; bootm 30000000
测试: