文章目录
- 1.I2C协议:总线空闲的时SCL和SDA处于高电平,I2C总线标准模式下速度可达100Kb/S,快速模式下可达400Kb/S
- 2.SMBUS:是I2C的一个子集
- i2ctools
- 3.i2c-dev.c
- 2.I2C设备添加:系统中对i2c_client的注册是在i2c_adapter的注册过程中完成
1.I2C协议:总线空闲的时SCL和SDA处于高电平,I2C总线标准模式下速度可达100Kb/S,快速模式下可达400Kb/S
i2c-dev.c是通用驱动。
I2C读时序(i2cget):如下sda写W低(所有都是scl高时,sda才生效),一个主机可控制多个从机,使用7位地址,最多可使用128个唯一地址,如下发送数据是读出的值。
i2c没有像spi片选线,主机将从机地址发给每个从机比较(寻址),匹配则向主机发送低电平ACK,不匹配则SDA保持高。
如下A表示ACK,逻辑分析仪一个接SDA,一个接SCL,一个接地。
I2C写时序(i2cset):
2.SMBUS:是I2C的一个子集
#include "stdio.h"
#include "stdlib.h"
typedef unsigned short u16;
typedef unsigned int u32;
typedef unsigned char u8;
#define POLY (0x1070U << 3)
static u8 crc8(u16 data)
{
int i;
for (i = 0; i < 8; i++) {
if (data & 0x8000)
data = data ^ POLY;
data = data << 1;
}
return (u8)(data >> 8);
}
static u8 i2c_smbus_pec(u8 crc, u8 *p, size_t count)
{
int i;
for (i = 0; i < count; i++)
crc = crc8((crc ^ p[i]) << 8);
return crc;
}
int main()
{
u8 pec;
char buf[12] = {
0xcc}; // cc是8位即addr(0x66)+写(0)
char buf0[12] = {
0xcd}; // cd是addr+读
char buf1[12] = {
0x88}; // 读的寄存器地址
char buf2[12] = {
0xc2,0xe0}; // 读出的数据
pec = i2c_smbus_pec(0, buf, 1);
pec = i2c_smbus_pec(pec, buf1, 1);
pec = i2c_smbus_pec(pec, buf0, 1);
pec = i2c_smbus_pec(pec, buf2, 2);
printf("test===0x%x\n",pec); // 正常解析前5个字节的crc8打印出0x3d,如果前面数据有错误,则打印出0xff,pec会多retry几次。
return 0;
}
i2ctools
--
:地址被探测了,但没有芯片应答。
UU
:探测被跳过了(不能确定有没有芯片,大概率有芯片),这个地址目前正被驱动程序使用,驱动里配置了这个地址(0x14)。
37
:在0x37这个地址找到了芯片。
如下-f:force, -y:yes,i2cdetect -y -q 5。
i2ctransfer支持16位/32位
寄存器的读写,w后面数字是设备/slave/器件即0x50后面的字节数。如下是8位寄存器,用i2cdump。
# dmesg
i2c i2c-1: Added multiplexed i2c bus 14
...
i2c i2c-1: Added multiplexed i2c bus 21
pca954x 1-0073: registered 8 multiplexed busses for I2C switch pca9548
2个msg都存在
3.i2c-dev.c
如果传入的major=0,则会遍历数组到空余的项并把fops结构体放入,返回空余项下标作为新的主设备号。
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <linux/i2c-dev.h>
#include <sys/ioctl.h>
#define I2C_SLAVE_FORCE_CLAIM 0x1
int main() {
unsigned long request;
const char *device = "/dev/i2c-37";
int file;
int addr = 0x58;
if ((file = open(device, O_RDWR)) < 0) {
printf("Failed to open the i2c bus");
return 1;
}
if (0x0 & I2C_SLAVE_FORCE_CLAIM) {
printf("I2C_SLAVE_FORCE\n");
request = I2C_SLAVE_FORCE;
} else {
printf("I2C_SLAVE\n"); // 0x0走这个
request = I2C_SLAVE;
}
int rc = ioctl(file, request, addr);
// I2C_SLAVE:0x48存在返回-1(卸载0x48驱动后0x48存在返回0), 0x58不存在返回0
// I2C_SLAVE_FORCE : 0x48存在和0x58不存在都返回0 // 0x98这些i2cdetetc不存在的都是返回-1
printf("rc: %d\n", rc);
close(file);
return 0;
}
// i2c-dev.c
static int __init i2c_dev_init(void)
{
int res;
printk(KERN_INFO "i2c /dev entries driver\n");
//从主设备号开始,次设备号从0开始,拥有I2C_MINORS个
res = register_chrdev_region(MKDEV(I2C_MAJOR, 0), I2C_MINORS, "i2c"); //注册字符驱动设备
if (res)
goto out;
i2c_dev_class = class_create(THIS_MODULE, "i2c-dev"); //创建名为i2c-dev的class
if (IS_ERR(i2c_dev_class)) {
res = PTR_ERR(i2c_dev_class);
goto out_unreg_chrdev;
}
i2c_dev_class->dev_groups = i2c_groups;
/* Keep track of adapters which will be added or removed later */
res = bus_register_notifier(&i2c_bus_type, &i2cdev_notifier);
if (res)
goto out_unreg_class;
/* Bind to already existing adapters right away */
/* 对于每一个已经存在的adapters都会去调用i2cdev_attach_adapter */
i2c_for_each_dev(NULL, i2cdev_attach_adapter);
return 0;
out_unreg_class:
class_destroy(i2c_dev_class);
out_unreg_chrdev:
unregister_chrdev_region(MKDEV(I2C_MAJOR, 0), I2C_MINORS);
out:
printk(KERN_ERR "%s: Driver Initialisation failed\n", __FILE__);
return res;
}
static int i2cdev_attach_adapter(struct device *dev, void *dummy)
{
struct i2c_adapter *adap;
struct i2c_dev *i2c_dev;
int res;
if (dev->type != &i2c_adapter_type)
return 0;
adap = to_i2c_adapter(dev);
i2c_dev = get_free_i2c_dev</