GPIO模拟I2C
手动编写i2c的通信时序
读写操作执行步骤:
#include <linux/init.h>
#include <linux/module.h>
#include <linux/gpio/consumer.h>
#include <linux/gpio.h>
#include <linux/delay.h>
#define IIC_SCL_GPIO 18
#define IIC_SDA_GPIO 19
// 定义IIC引脚
static struct gpio_desc *i2c_scl_gpio;
static struct gpio_desc *i2c_sda_gpio;
// i2c的起始信号
void i2c_start(void)
{
gpio_set_value(i2c_scl_gpio, 1);
gpio_set_value(i2c_sda_gpio, 1);
mdelay(1);
gpio_set_value(i2c_sda_gpio, 0);
mdelay(1);
gpio_set_value(i2c_scl_gpio, 0);
mdelay(1);
}
// i2c的停止信号
void i2c_stop(void)
{
gpio_set_value(i2c_scl_gpio, 0);
gpio_set_value(i2c_sda_gpio, 0);
mdelay(1);
gpio_set_value(i2c_scl_gpio, 1);
mdelay(1);
gpio_set_value(i2c_sda_gpio, 1);
}
// i2c发送应答
void i2c_send_ack(int ack)
{
gpiod_direction_output(i2c_scl_gpio, 0);
if (ack)
{
gpio_set_value(i2c_sda_gpio, 0);
}
else
{
gpio_set_value(i2c_sda_gpio, 1);
}
gpiod_direction_output(i2c_scl_gpio, 1);
mdelay(1);
gpiod_direction_output(i2c_scl_gpio, 0);
}
// i2c接收应答
int i2c_recv_ack(void)
{
int value = 0;
gpiod_direction_input(i2c_sda_gpio);
gpiod_direction_output(i2c_scl_gpio, 1);
mdelay(1);
if (gpiod_get_value(i2c_sda_gpio))
{
value = 1;
}
gpiod_direction_output(i2c_scl_gpio, 0);
gpiod_direction_output(i2c_sda_gpio, 1);
mdelay(1);
return value;
}
// i2c接受一个字节的数据
int i2c_recv_byte(void)
{
int i = 0;
int temp = 0;
int data = 0;
gpiod_direction_input(i2c_sda_gpio);
mdelay(1);
for (i = 0; i < 8; i++)
{
gpiod_direction_output(i2c_scl_gpio, 0);
mdelay(1);
gpiod_direction_output(i2c_scl_gpio, 1);
mdelay(1);
data = gpiod_get_value(i2c_sda_gpio);
temp = (temp << 1) | data;
}
gpiod_direction_output(i2c_sda_gpio, 1);
gpiod_direction_output(i2c_scl_gpio, 0);
mdelay(5);
return temp;
}
// i2c发送一个字节的数据
void i2c_send_byte(int data)
{
int i = 0;
int value;
gpiod_direction_output(i2c_scl_gpio, 0);
for (i = 0; i < 8; i++)
{
value = (data << i) & 0x80;
if (value)
{
gpio_direction_output(i2c_sda_gpio, 1);
}
else
{
gpio_direction_output(i2c_sda_gpio, 0);
}
gpiod_direction_output(i2c_scl_gpio, 1);
mdelay(1);
gpiod_direction_output(i2c_scl_gpio, 0);
mdelay(1);
}
}
// 写一个字节的数据到IIC总线上
void i2c_write_reg(int addr, int reg, int value)
{
int ack;
i2c_start();
i2c_send_byte((addr << 1) | 0x00); // 写地址
ack = i2c_recv_ack();
if (ack)
{
printk("i2c_write_reg: write address failed\n");
return;
}
i2c_send_byte(reg); // 写寄存器地址
ack = i2c_recv_ack();
if (ack)
{
printk("i2c_write_reg: write reg address failed\n");
return;
}
i2c_send_byte(value); // 写数据
ack = i2c_recv_ack();
if (ack)
{
printk("i2c_write_reg: write data failed\n");
return;
}
i2c_stop();
}
// 从IIC总线上读取一个字节的数据
int i2c_read_reg(int addr, int reg)
{
int ack;
int value;
i2c_start();
i2c_send_byte((addr << 1) | 0x00); // 写地址
ack = i2c_recv_ack();
if (ack)
{
printk("i2c_read_reg: write address failed\n");
return -1;
}
i2c_send_byte(reg); // 写寄存器地址
ack = i2c_recv_ack();
if (ack)
{
printk("i2c_read_reg: write reg address failed\n");
return -1;
}
i2c_start();
i2c_send_byte((addr << 1) | 0x01); // 读地址
ack = i2c_recv_ack();
if (ack)
{
printk("i2c_read_reg: read address failed\n");
return -1;
}
value = i2c_recv_byte(); // 读取数据
i2c_send_ack(0); // 发送应答
i2c_stop();
return value;
}
static int __init software_iic_init(void)
{
// 获取GPIO引脚
i2c_scl_gpio = gpio_to_desc(IIC_SCL_GPIO);
i2c_sda_gpio = gpio_to_desc(IIC_SDA_GPIO);
if (i2c_scl_gpio == NULL || i2c_sda_gpio == NULL)
{
printk("Failed to get GPIO descriptors\n");
return -1;
}
// 设置GPIO引脚为输出模式,高电平
gpiod_direction_output(i2c_scl_gpio, 1);
gpiod_direction_output(i2c_sda_gpio, 1);
return 0;
}
static void __exit software_iic_exit(void)
{
gpio_put(i2c_scl_gpio);
gpio_put(i2c_sda_gpio);
}
module_init(software_iic_init);
module_exit(software_iic_exit);
MODULE_LICENSE("GPL");
使用Linux提供的软件I2c:
- 编译模拟i2c驱动到内核
Device Driver -->
I2C support -->
I2C hardware Bus support -->
<*>GPIO-based bitbanging I2C
- 模拟I2c驱动函数的路径:
driver/i2c/busses/i2c-gpio.c
- 在设备树中添加节点:
i2c6:i2c6@gpiol{
#address-cells = <1>;
#size-cells = <0>
compatible ="i2c-gpio";
gpios = <&gpio0 RK_PB4 GPIO ACTIVEHIGH >,
<&gpio0 RK_PB3 GPIO ACTIVE_HIGH>;
i2c-gpio,delay-us = <5>
status = "disabled";
}
&i2c61{
status = "okay"
myft5x06:my-ft5x06@38{
compatible = "my-ft5x06"
reg = <0x38>;
};
};