I2C(Inter-Integrated Circuit)是一种串行通信接口,用于在集成电路之间进行数字数据传输。它由Philips(现在的NXP Semiconductors)公司在上世纪80年代开发,并已广泛应用于各种电子设备中。下面是I2C接口从原理到应用的简要介绍:
原理:
I2C接口基于两根线,即串行数据线(SDA)和串行时钟线(SCL)。设备之间通过这两根线进行双向的通信。在I2C通信中,有两种基本角色:主设备(master)和从设备(slave)。主设备负责控制通信的发起和时序控制,而从设备则响应主设备的指令。
应用:
芯片间通信。
显示器的寄存器配置。
温度传感器。
EEPROM存储器。
cmos的寄存器配置。
总线初始化:在I2C通信开始之前,需要进行总线初始化。初始化通常由主设备(Master)发起,它会发送一个起始信号来指示通信的开始。起始信号是将时钟线(SCL)保持高电平的同时,将数据线(SDA)从高电平切换到低电平。
地址发送:一旦总线初始化完成,主设备会发送要通信的从设备(Slave)的地址。地址由7位或10位表示,取决于使用的是标准模式还是扩展模式。地址的最低位用于指示读取(1)或写入(0)操作。
数据传输:根据通信操作(读取或写入),主设备或从设备会在时钟的每次上升边沿上,通过数据线(SDA)传输数据。每个数据字节的传输通常由8个位组成。在主设备或从设备发送数据后,接收方会向发送方发送一个应答(ACK)信号。
停止信号:当主设备完成通信时,它会发送一个停止信号。停止信号是将时钟线(SCL)保持在高电平的同时,将数据线(SDA)从低电平切换到高电平。
在I2C通信中,所有数据传输都是以字节为单位进行的。这意味着每次传输只能发送一个字节的数据,并且在每个字节传输后都需要进行应答。如果需要传输更大的数据,可以分为多个字节进行传输。
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;
}
#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;
}
#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");