I2C通信:
一.硬件初识:
IIC(inter-intergrated-Circu):内部集成总线
四线通讯:SCL,SDA,GND,VCC
,串行,半双工
-
I2C 总线是同步,串行,半双工通信总线。
-
I2C 总线由时钟线
SDA
和SCL
两根信号线构成。并且都有上拉电阻。确保总线空闲状态为高电平。 -
I2C 总线支持多设备连接,允许多主机存在,但同一时刻只允许一台主机。
-
每一个I2C 外设都会对应一个唯一的地址(这个地址可以从 I2C 外设器件的数据手册中得知),主机和从机之间的通信就是通过这个地址来确定主机要和哪个从机进行通信的。
-
I2C 总线上挂载的设备的数量受总线最大电容 400pF 限制。如果挂的是相同型号的器件还受到器件的地址限制。
-
I2C 总线在标准模式下传输速率可达
100kbit/s
,在快速模式下可达400kbit/s
,在高速模
式下可达3.4Mbit/s
。 -
I2C 总线上的主机和从机之间以字节(8位)为单位进行数据传输。
-
I2C 有硬件12C 和软件 12C。
通讯时序:
高位先传输
I2C子系统框架:
驱动开发,只需实现设备驱动层的Client,和Driver即可:向内核中添加一个描述i2c外设资源的device部分,在driver中调用核心层的API实现I2C的通讯驱动
二.1. I2C_client部分:
用于描述一个I2c外设的资源,地址,GPIO,中断信息等等…表示通信的对象
向内核中添加一个client的方法可以是设备树或者使用c程序添加
设备树:
//挂载在i2c1设备控制器下
&i2c1{
//状态
status = "okay";
//标签@地址
myft5x06:my-ft5x06@38{
//配对属性
compatible = "my-ft5x06";
//regI2c地址
reg = <0x38>;
};
}
c语言添加:
#include <linux/init.h>
#include <linux/module.h>
#include <linux/i2c.h>
struct i2c_adapter *i2c_ada;
// 定义I2C设备信息,用于描述一个I2C设备的资源
struct i2c_board_info ft5x06[] = {
{
I2C_BOARD_INFO("my-ft5x06", 0x38)},
{
}
};
static int __init iic_device_init(void)
{
i2c_ada = i2c_get_adapter(1); // 获取I2C适配器
i2c_new_device(i2c_ada, ft5x06); // 创建设备
i2c_put_adapter(i2c_ada); // 释放适配器
return 0;
}
static void __exit iic_device_exit(void)
{
}
module_init(iic_device_init);
module_exit(iic_device_exit);
MODULE_LICENSE("GPL");t);
MODULE_LICENSE("GPL");
2. I2C_driver部分:
驱动框架:
#include <linux/init.h>
#include <linux/module.h>
#include <linux/i2c.h>
int iic_driver_probe(struct i2c_client *client, const struct i2c_device_id *id)
{
return 0;
}
int iic_driver_remove(struct i2c_client *client)
{
return 0;
}
// const struct of_device_id my_match_table[] ={
// {.compatible = "my-ft5x06"},
// {}
// }
const struct i2c_device_id *my_id_table[] = {
{
"my-ft5x06"},
{
}
} ;
struct i2c_driver my_iic_driver =
{
.driver = {
//.name用于和使用c注册的device名字作匹配
.name = "my-ft5x06",
.owner = THIS_MODULE,
//.of_match_table用于匹配设备树中的节点
// .of_match_table = my_match_table,
},
.probe = iic_driver_probe,
.remove = iic_driver_remove,
//.id_table和.name用于和使用c注册的device名字作匹配,优先使用table
.id_table = my_id_table,
};
static int iic_driver_init(void)
{
int ret = i2c_add_driver(&my_iic_driver); // 添加驱动程序
if (ret < 0)
{
printk("i2c_add_driver failed\n");
}
return 0;
}
static void iic_driver_exit(void)
{
i2c_del_driver(&my_iic_driver); // 删除驱动程序
}
module_init(iic_driver_init);
module_exit(iic_driver_exit);
MODULE_LICENSE("GPL");
3. 获取设备信息:
使用设备树与driver匹配成功后,执行probe函数->获取设备树中资源信息:补全probe函数功能功能:
struct gpio_desc *reset_gpio;
struct gpio_desc *irq_gpio;
// 中断处理函数
irqreturn_t ft5x06_handler(int irq, void *args)
{
printk("ft5x06_handler\n");
// 中断处理逻辑
return IRQ_RETVAL(IRQ_HANDLED);//表示中断已经处理完毕
}
int iic_driver_probe(struct i2c_client *client, const struct i2c_device_id *id)
{
int ret = 0;
printk("iic_driver_probe\n");
// 获取设备树中的节点信息
// 获取复位引脚信息
reset_gpio = gpiod_get_optional(&client->dev, "reset", 0);
if (IS_ERR(reset_gpio))
{
printk("get reset gpio failed\n");
return PTR_ERR(reset_gpio);
}
// 获取中断引脚信息
irq_gpio = gpiod_get_optional(&client->dev, "irq", 0);
if (IS_ERR(irq_gpio))
{
printk("get irq gpio failed\n");
return PTR_ERR(irq_gpio);
}
// 复位设备
gpiod_direction_output(reset_gpio, 0);
mdelay(5);
gpiod_direction_output(reset_gpio, 1);
// 申请中断
// IRO_TYPE_EDGE_FALLING |IROF_ONESHOT:下降沿触发,中断可嵌套
ret = request_irq(client->irq, ft5x06_handler, IRO_TYPE_EDGE_FALLING | IROF_ONESHOT, "ft5x06_irq", NULL);
if (ret < 0)
{
printk("request_irq failed\n")