因为 输入寄存器 是一个 D 触发器,当 SCL 为低电平时,Q 不受 D 端输入信号的控制,保持原状态不变。因此,我们就可以理解为什么上面的输入寄存器 和IO 引脚值会存在不同的情况了。
SCL 为高电平,此时 Q 接受 D 端输入的信号,其状态翻到和 D 的状态相同。因此,Input Port Register Data 就是我们读取到的寄存器值,不过我们看 Input Port Register Data 下面似乎还有一个 Polarity Inversion Register ,这个就是用来做极性反转的,因此我们读取到的数据,并不一定是实际电平数据,而是逻辑电平!但如果我们没有设置极性反转,那么读取到的电平值就是实际电平值。
SCL 为高电平时,因为此时 Q 和 D 电平值是一样的,理论上 INT 引脚状态会恢复。但是 7.3.3 Interrupt ( INT) Output 章节说了,INT 复位发生在 SCL 信号上升沿之后的应答(ACK)位。那么,这里的这个设计感觉很矛盾,这又如何理解呢?
很简单,如下图给的只是简略图,并不完善,异或门之后的 To INT 还有其他电路做电平钳位,只有到从机发送 ACK 的时候,电平钳位才会取消。而这里的 INT 引脚恢复,只是清除对应的标志位,也就是 7.3.3 Interrupt ( INT) Output 章节的 Because each 8-bit port is read independently, the interrupt caused by port 0 is not cleared by a read of port 1, or the interrupt caused by port 1 is not cleared by a read of port 0.(由于每个 8 位端口是独立读取的,因此读取端口 1 不会清除由端口 0 引起的中断,读取端口 0 也不会清除由端口 1 引起的中断)
#include <linux/module.h>
#include <linux/gpio/driver.h>
#include <linux/i2c.h>
struct my_gpio_chip {
struct gpio_chip chip;
struct i2c_client *client;
// 其他必要的成员变量
};
static int my_gpio_get_direction(struct gpio_chip *chip, unsigned offset) {
// 实现获取 GPIO 方向的逻辑
return 0; // 返回 0 表示输出,1 表示输入
}
static int my_gpio_direction_input(struct gpio_chip *chip, unsigned offset) {
// 实现设置 GPIO 为输入的逻辑
return 0;
}
static int my_gpio_direction_output(struct gpio_chip *chip, unsigned offset, int value) {
// 实现设置 GPIO 为输出的逻辑
return 0;
}
static int my_gpio_get(struct gpio_chip *chip, unsigned offset) {
// 实现读取 GPIO 状态的逻辑
return 0;
}
static void my_gpio_set(struct gpio_chip *chip, unsigned offset, int value) {
// 实现设置 GPIO 状态的逻辑
}
static int my_gpio_probe(struct i2c_client *client, const struct i2c_device_id *id) {
struct my_gpio_chip *my_chip;
int ret;
my_chip = devm_kzalloc(&client->dev, sizeof(*my_chip), GFP_KERNEL);
if (!my_chip)
return -ENOMEM;
/* 初始化I2C锁 */
mutex_init(&chip->i2c_lock);
/*
* In case we have an i2c-mux controlled by a GPIO provided by an
* expander using the same driver higher on the device tree, read the
* i2c adapter nesting depth and use the retrieved value as lockdep
* subclass for chip->i2c_lock.
*
* REVISIT: This solution is not complete. It protects us from lockdep
* false positives when the expander controlling the i2c-mux is on
* a different level on the device tree, but not when it's on the same
* level on a different branch (in which case the subclass number
* would be the same).
*
* TODO: Once a correct solution is developed, a similar fix should be
* applied to all other i2c-controlled GPIO expanders (and potentially
* regmap-i2c).
*/
lockdep_set_subclass(&chip->i2c_lock,
i2c_adapter_depth(client->adapter));
my_chip->client = client;
my_chip->chip.label = dev_name(&client->dev);
my_chip->chip.parent = &client->dev;
my_chip->chip.owner = THIS_MODULE;
my_chip->chip.base = -1; // 自动分配基地址
my_chip->chip.ngpio = 16; // 假设芯片有 16 个 GPIO 引脚
my_chip->chip.can_sleep = true;
my_chip->chip.get_direction = my_gpio_get_direction;
my_chip->chip.direction_input = my_gpio_direction_input;
my_chip->chip.direction_output = my_gpio_direction_output;
my_chip->chip.get = my_gpio_get;
my_chip->chip.set = my_gpio_set;
ret = devm_gpiochip_add_data(&client->dev, &my_chip->chip, my_chip);
if (ret)
return ret;
i2c_set_clientdata(client, my_chip);
return 0;
}
static const struct i2c_device_id my_gpio_id[] = {
{ "my_gpio_chip", 0 },
{ }
};
static struct i2c_driver my_gpio_driver = {
.driver = {
.name = "my_gpio_chip",
},
.probe = my_gpio_probe,
.id_table = my_gpio_id,
};
module_i2c_driver(my_gpio_driver);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Your Name");
MODULE_DESCRIPTION("A GPIO driver for my I/O expander");