I2C接口从原理到应用

1.简介

I2C(Inter-Integrated Circuit)是一种串行通信接口,用于在集成电路之间进行数字数据传输。它由Philips(现在的NXP Semiconductors)公司在上世纪80年代开发,并已广泛应用于各种电子设备中。下面是I2C接口从原理到应用的简要介绍:

原理:
I2C接口基于两根线,即串行数据线(SDA)和串行时钟线(SCL)。设备之间通过这两根线进行双向的通信。在I2C通信中,有两种基本角色:主设备(master)和从设备(slave)。主设备负责控制通信的发起和时序控制,而从设备则响应主设备的指令。

应用:

  1. 芯片间通信。

  2. 显示器的寄存器配置。

  3. 温度传感器。

  4. EEPROM存储器。

  5. cmos的寄存器配置。

2.I2C传输流程 

  1. 总线初始化:在I2C通信开始之前,需要进行总线初始化。初始化通常由主设备(Master)发起,它会发送一个起始信号来指示通信的开始。起始信号是将时钟线(SCL)保持高电平的同时,将数据线(SDA)从高电平切换到低电平。

  2. 地址发送:一旦总线初始化完成,主设备会发送要通信的从设备(Slave)的地址。地址由7位或10位表示,取决于使用的是标准模式还是扩展模式。地址的最低位用于指示读取(1)或写入(0)操作。

  3. 数据传输:根据通信操作(读取或写入),主设备或从设备会在时钟的每次上升边沿上,通过数据线(SDA)传输数据。每个数据字节的传输通常由8个位组成。在主设备或从设备发送数据后,接收方会向发送方发送一个应答(ACK)信号。

  4. 停止信号:当主设备完成通信时,它会发送一个停止信号。停止信号是将时钟线(SCL)保持在高电平的同时,将数据线(SDA)从低电平切换到高电平。

       在I2C通信中,所有数据传输都是以字节为单位进行的。这意味着每次传输只能发送一个字节的数据,并且在每个字节传输后都需要进行应答。如果需要传输更大的数据,可以分为多个字节进行传输。

      I2C通信的基本流程包括总线初始化、地址发送、数据传输和停止信号。通过这些步骤,主设备和从设备可以实现可靠的数据传输。

注意:读写是按照时序顺序进行的,始终只进行一个操作(读取或写入)并在每个位传输之前等待时钟信号的稳定。注意在每个操作后允许足够的延迟和时钟信号的控制,以确保正确的数据传输和时序。

3.GPIO模拟I2C 

#include 
#include 

// GPIO模拟的I2C引脚
#define SDA_PIN         2
#define SCL_PIN         3

// 初始化GPIO引脚
void gpio_init() 
{
    // TODO: 初始化GPIO引脚为输出模式
}

// 延时一段时间(微秒级)
void delay(int us) 
{
    // TODO: 实现延时函数
}

// 拉高SDA引脚
void sda_high() 
{
    // TODO: 将SDA引脚拉高
}

// 拉低SDA引脚
void sda_low() 
{
    // TODO: 将SDA引脚拉低
}

// 拉高SCL引脚
void scl_high() 
{
    // TODO: 将SCL引脚拉高
}

// 拉低SCL引脚
void scl_low() 
{
    // TODO: 将SCL引脚拉低
}

// 发送起始条件
void send_start() 
{
    scl_high();
    sda_high();
    sda_low();
    scl_low();
}

// 发送停止条件
void send_stop() 
{
    scl_high();
    sda_low();
    sda_high();
}

// 发送一个字节,并返回应答状态
bool send_byte(unsigned char byte) 
{
    for (int i = 7; i >= 0; i--) 
    {
        if (byte & (1 << i)) 
        {
            sda_high();
        } 
        else 
        {
            sda_low();
        }
        scl_high();
        scl_low();
    }
  
    // 等待应答
    sda_high();
    scl_high();
    bool ack = !sda_status(); // 读取SDA引脚状态,应答为低电平
    scl_low();

    return ack;
}

// 接收一个字节,并发送应答或非应答信号
unsigned char receive_byte(bool send_ack) 
{
    unsigned char byte = 0;
  
    sda_high();
    for (int i = 7; i >= 0; i--) 
    {
        scl_high();
        byte |= (sda_status() << i); // 读取SDA引脚状态,接收数据位
        scl_low();
    }
  
    // 发送应答或非应答信号
    if (send_ack) 
    {
        sda_low();
    } 
    else 
    {
        sda_high();
    }
    scl_high();
    scl_low();

    return byte;
}

int main() 
{
    gpio_init();
  
    // 示例:发送一个字节
    send_start();
    bool ack = send_byte(0x55);
    send_stop();
  
    if (ack) 
    {
        printf("发送成功\n");
    } 
    else 
    {
        printf("发送失败\n");
    }
  
    // 示例:接收一个字节
    send_start();
    unsigned char data = receive_byte(false);
    send_stop();
  
    printf("接收到的字节:%02X\n", data);
  
    return 0;
}

 4.linux应用层I2C驱动

#include 
#include 
#include 
#include 
#include 

int main() 
{
    int file;
    char *filename = "/dev/i2c-1"; // I2C总线设备文件路径
    int addr = 0x50; // 设备地址
  
    // 打开I2C总线设备文件
    if ((file = open(filename, O_RDWR)) < 0) 
    {
        perror("无法打开I2C总线设备文件");
        return 1;
    }
  
    // 设置I2C设备地址
    if (ioctl(file, I2C_SLAVE, addr) < 0) 
    {
        perror("设置设备地址失败");
        return 1;
    }
  
    // 写入操作
    unsigned char reg = 0x10; // 要写入的寄存器地址
    unsigned char data = 0xAB; // 要写入的数据
  
    if (write(file, ®, 1) != 1) 
    {
        perror("写入寄存器地址失败");
        return 1;
    }
  
    if (write(file, &data, 1) != 1) 
    {
        perror("写入数据失败");
        return 1;
    }
  
    // 读取操作
    unsigned char read_buf[1]; // 用于存储读取的数据
  
    if (write(file, ®, 1) != 1) 
    {
        perror("写入寄存器地址失败");
        return 1;
    }
  
    if (read(file, &read_buf, sizeof(read_buf)) != sizeof(read_buf)) 
    {
        perror("读取数据失败");
        return 1;
    }
  
    printf("读取到的数据:%02X\n", read_buf[0]);
  
    // 关闭I2C总线设备文件
    close(file);
  
    return 0;
}

 5.liunx驱动层I2C.ko

#include 
#include 
#include 

#define I2C_DEV_NAME "my_i2c_device"
#define BUF_SIZE 256

struct my_i2c_device 
{
    struct i2c_client *client;
    struct mutex buf_lock;
    char tx_buf[BUF_SIZE];
    char rx_buf[BUF_SIZE];
};

static ssize_t my_i2c_read(struct my_i2c_device *dev, char *buf, size_t count)
{
    ssize_t ret;
    mutex_lock(&dev->buf_lock);

    // 发送读取请求
    ret = i2c_master_recv(dev->client, buf, count);

    mutex_unlock(&dev->buf_lock);
    return ret;
}

static ssize_t my_i2c_write(struct my_i2c_device *dev, const char *buf, size_t count)
{
    ssize_t ret;
    mutex_lock(&dev->buf_lock);

    // 发送写入请求
    ret = i2c_master_send(dev->client, buf, count);

    mutex_unlock(&dev->buf_lock);
    return ret;
}

static int my_i2c_command(struct i2c_client *client, unsigned int cmd, void *arg)
{
    struct my_i2c_device *dev = i2c_get_clientdata(client);
    unsigned char *buf = (unsigned char *)arg;
    ssize_t ret = -EINVAL;

    if (!buf)
        return -EINVAL;

    switch (cmd) 
	{
        case I2C_READ_CMD:
            ret = my_i2c_read(dev, buf, BUF_SIZE);
            if (ret < 0) 
			{
                dev_err(&client->dev, "I2C read failed\n");
                return ret;
            }
            break;
        case I2C_WRITE_CMD:
            ret = my_i2c_write(dev, buf, BUF_SIZE);
            if (ret < 0) 
			{
                dev_err(&client->dev, "I2C write failed\n");
                return ret;
            }
            break;
        default:
            dev_err(&client->dev, "Invalid I2C command\n");
            return -EINVAL;
    }

    return ret;
}

static int my_i2c_probe(struct i2c_client *client, const struct i2c_device_id *id)
{
    struct my_i2c_device *dev;
    dev = devm_kzalloc(&client->dev, sizeof(*dev), GFP_KERNEL);
    if (!dev)
        return -ENOMEM;

    dev->client = client;
    mutex_init(&dev->buf_lock);

    i2c_set_clientdata(client, dev);

    // 读取示例
    char read_buf[BUF_SIZE];
    ssize_t ret = my_i2c_read(dev, read_buf, sizeof(read_buf));
    if (ret < 0) 
	{
        dev_err(&client->dev, "I2C read failed\n");
        return ret;
    }

    // 写入示例
    char write_buf[] = "Hello, I2C!";
    ret = my_i2c_write(dev, write_buf, sizeof(write_buf));
    if (ret < 0) 
	{
        dev_err(&client->dev, "I2C write failed\n");
        return ret;
    }

    // 设置回调函数
    client->command = my_i2c_command;
	
    // 添加其他设备初始化操作
    // ...

    return 0;
}

static int my_i2c_remove(struct i2c_client *client)
{
    struct my_i2c_device *dev = i2c_get_clientdata(client);

    mutex_destroy(&dev->buf_lock);

    // 添加设备的释放操作
    // ...

    return 0;
}

static const struct i2c_device_id i2c_id_table[] = 
{
    { I2C_DEV_NAME, 0 },
    { }
};
MODULE_DEVICE_TABLE(i2c, i2c_id_table);


struct i2c_driver my_i2c_driver = 
{
    .probe = my_i2c_probe,
    .remove = my_i2c_remove,
    .id_table = i2c_id_table,
    .driver = 
	{
        .name = I2C_DEV_NAME,
        .owner = THIS_MODULE,
    },
    .command = my_i2c_command, 
};
static int __init my_i2c_init(void)
{
    return i2c_add_driver(&my_i2c_driver);
}
module_init(my_i2c_init);

static void __exit my_i2c_exit(void)
{
    i2c_del_driver(&my_i2c_driver);
}
module_exit(my_i2c_exit);

MODULE_LICENSE("GPL");

你可能感兴趣的:(LINUX,从单片机到freertos,I2C)