I2C(Inter-Integrated Circuit)是一种多主机、两线制、串行计算机总线,用于连接低速外围设备到处理器或微控制器。这种通信协议由Philips Semiconductor(现在的NXP Semiconductors)在1980年代初期开发。
bit7~bit1:I2C从设备地址
bit0:读写位 写为0,读为1,与bit7~bit1 凑成8位地址,所以有些设备分为读地址,写地址。
ACK:响应信号,发送完地址后,主设备会置高,从设备响应后会马上拉低,主设备读取到拉低,判断设备存在
stop:结束信号,SCL置高,SDA拉高
I2C协议支持多种速率模式,包括:
I2C设备通常使用7位或10位地址格式,这使得总线上可以有多个设备同时存在而不发生冲突。
当两个主设备同时尝试控制总线时,I2C协议提供了仲裁机制来处理冲突,确保只有一个主设备能够控制总线。
i2c驱动开发:
以i2c设备芯片 tda7313为例,数据手册如下
tda7313挂载在i2c5下,设备树如下:
- &i2c5 {
- //时钟100k
- clock-frequency = <100000>;
- status = "okay";
- //@44设备地址
- tda7313: tda7313@44 {
- status = "okay";
- compatible = "st,tda7313";
- //描述设备地址
- reg = <0x44>;
- };
- }
Linux下发送I2C数据有三种方式:
1.i2c驱动
2.系统应用编程
3.命令发送
- //i2c驱动
- #include
- #include
- #include
- #include
- #include
- #include
- #include
-
- int myprobe(struct i2c_client *cli, const struct i2c_device_id *id)
- {
- int ret;
- struct i2c_msg msgs[1];
- char addr[2] = { 0x11,0x22 }; //2个数据
- msgs[0].addr = cli->addr;//设备地址 0x44
- msgs[0].flags = 0;//读写标志位
- msgs[0].len = 2;//数据长度
- msgs[0].buf = addr;//数据地址
- //执行i2c_transfer = 1个开始信号 + 写地址 + 0x11 + 0x22 + 停止信号
- ret = i2c_transfer(cli->adapter, msgs, 1);
- if (ret < 0){
- msleep(10);
- }
- return 0;
- }
- int myremove(struct i2c_client *cli)
- {
- return 0;
- }
- struct i2c_device_id ids[] = {
- { "st,tda7313", 0 },
- {},
- };
- MODULE_DEVICE_TABLE(i2c, ids);
- //配对节点
- static const struct of_device_id tda7313_of_match[] = {
- { .compatible = "st,tda7313" },
- {},
- };
- struct i2c_driver mydrv = {
- .probe = myprobe,
- .remove = myremove,
- .driver = {
- .name = "tda7313",
- .of_match_table = tda7313_of_match,
- .owner = THIS_MODULE,
- },
- .id_table = ids,
- };
-
- module_i2c_driver(mydrv);
- MODULE_LICENSE("GPL");
Linux系统编程:
设备树打开i2c-5后会在产生/dev/i2c-5节点:
- rk3568_s:/ $ ls /dev/i2c-5 -l
- crw-rw-rw- 1 system system 89, 5 2017-08-04 17:00 /dev/i2c-5
可以对该节点操作i2c
i2c_test.c
- // 2.系统应用编程
- #include
- #include
- #include
- #define I2C_ADDR 0x44
- int fd;
- unsigned char Tuner_WriteBuffer(unsigned char *buf, uint16_t len)
- {
- uint16_t i;
- uint8_t r;
- if (fd < 0)
- {
- printf("fd < 0");
- return -1;
- }
- //i2c数据填充
- struct i2c_msg msgs[1] = {
- {I2C_ADDR, 0, len, buf},//第一个I2c地址,写标志位,长度,buf数据地址
- };
- struct i2c_rdwr_ioctl_data idata = {
- .msgs = msgs,
- .nmsgs = 1, //对应msgs长度
- };
- //下发i2c驱动,驱动帮你写数据
- if (ioctl(fd, I2C_RDWR, &idata) < 0)
- {
- printf("ioctl I2C_RDWR error %d", fd);
- return -1;
- }
- usleep(1 * 1000);
- return 1;
- }
-
- unsigned char Tuner_ReadBuffer(unsigned char *addr, uint16_t addrLen, unsigned char *buf, uint16_t len)
- {
- if (fd < 0)
- {
- printf("fd < 0");
- return -1;
- }
- unsigned char reg = 0;
- uint16_t i = 1;
- struct i2c_msg msgs[2] = {
- {I2C_ADDR, 0, addrLen, addr},
- {I2C_ADDR, I2C_M_RD, len, buf},
- };
-
- struct i2c_rdwr_ioctl_data idata = {
- .msgs = msgs,
- .nmsgs = 2,
- };
-
- if (ioctl(fd, I2C_RDWR, &idata) < 0)
- {
- printf("chenjx I2C_RDWR error");
- return -1;
- }
- return 1;
- }
-
-
- void Tuner_I2C_Init()
- {
- fd = open("/dev/i2c-5", I2C_RDWR);
- if (fd < 0)
- {
- printf("open %s false", "/dev/i2c-5");
- return;
- }
- printf("open %s succuss", "/dev/i2c-5");
- }
- void Tuner_I2C_UnInit()
- {
- close(fd);
- }
-
- int main(){
- unsigned char data[2] = {0x11,0x22};
- Tuner_I2C_Init();
- Tuner_WriteBuffer(data,2);
- Tuner_I2C_UnInit();
- }
3.Linux命令:
- i2ctransfer -f -y 5 w2@0x44 0x11 0x22
- -y 5:哪条总线
- w2:写两个字节地址
- 0x11 0x22:数据
I2C调试 经验分享 :
i2cdetect -y -r 5 :扫描5总线上挂载的是有设备地址
一.查不到设备地址
1. i2cdetect -y 5如果没有扫到该地址确认供电部分
2.有些设备需要时钟信号i2c才能正常工作(大部分图像处理芯片,xs9922)
无法写入数据:
1.无法写入数据返回-5,确认同一总线挂载上的设备影响被拉低,确认它们的电压
2.先用示波器测量数据,发现从设备应答电压无法拉低,改变上拉电阻阻值,尝试是否恢复