Linux下使用GPIO模拟I2C通讯

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:

  1. 编译模拟i2c驱动到内核
Device Driver -->
	I2C support -->
        I2C hardware Bus support -->
            <*>GPIO-based bitbanging I2C
  1. 模拟I2c驱动函数的路径:

driver/i2c/busses/i2c-gpio.c

  1. 在设备树中添加节点:
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>;   
    };

};
  • 9
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
i2c-gpio是一个在Linux内核中实现的I2C总线驱动程序,它使用GPIO引脚模拟I2C总线的功能。通过i2c-gpio,您可以在不具备硬件I2C总线的系统上使用软件模拟I2C通信。 要使用i2c-gpio驱动程序,您需要进行以下步骤: 1. 确保内核配置中启用了i2c-gpio驱动程序。您可以通过查看内核配置文件(通常位于`/usr/src/linux/.config`)或通过内核配置工具(如make menuconfig)来检查。 2. 确保所需的GPIO引脚已正确配置为用于I2C通信。这通常涉及到在设备树或板级文件中进行配置。具体的配置方法取决于您所使用的硬件平台和内核版本。 3. 加载i2c-gpio模块。您可以使用`modprobe`命令来加载模块,例如: ``` modprobe i2c-gpio ``` 4. 配置i2c-gpio驱动程序的参数。您可以通过在/sys/class/i2c-gpio目录下的相应GPIO设备目录中设置参数文件来配置,例如: ``` echo "gpio=<SDA_GPIO_PIN>,<SCL_GPIO_PIN>" > /sys/class/i2c-gpio/<I2C_ADAPTER>/new_device ``` 其中,`<SDA_GPIO_PIN>`和`<SCL_GPIO_PIN>`分别是用于数据线(SDA)和时钟线(SCL)的GPIO引脚号,`<I2C_ADAPTER>`是i2c-gpio适配器的编号。 5. 完成上述步骤后,您应该能够在`/dev`目录下看到相应的I2C设备节点,例如`/dev/i2c-X`,其中X是适配器编号。 现在,您可以使用标准的I2C工具(如i2c-tools)或编写自己的应用程序来与通过i2c-gpio驱动程序模拟I2C设备进行通信。 请注意,i2c-gpio是一种软件模拟的解决方案,在某些情况下可能会受到性能或兼容性方面的限制。因此,如果您的系统有硬件I2C总线可用,最好使用硬件解决方案来获得更好的性能和可靠性。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

想和我重名?

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值