Linux I2C调试分享

本文介绍了I2C总线的基本概念、工作原理,包括其两线制设计、多主机功能、地址分配以及数据传输速率。此外,详细讲解了Linux下通过i2c驱动、系统应用编程和命令发送进行I2C通信的方法,以及I2C调试中的常见问题和解决方案。
摘要由CSDN通过智能技术生成

I2C简介

I2C(Inter-Integrated Circuit)是一种多主机、两线制、串行计算机总线,用于连接低速外围设备到处理器或微控制器。这种通信协议由Philips Semiconductor(现在的NXP Semiconductors)在1980年代初期开发。

I2C的特点

  • 两线制总线:只需要两根线,一根是数据线SDA(Serial Data Line),另一根是时钟线SCL(Serial Clock Line)。
  • 多主机能力:允许多个主设备在同一总线上通信。
  • 地址分配:每个从设备都有一个唯一的地址。
  • 简单性和灵活性:硬件实现简单,支持不同速度的数据传输。

I2C通信过程

  1. 起始信号:主设备通过将SDA从高电平拉低到低电平,同时SCL保持高电平来发出起始信号。
  2. 地址和读写位:主设备发送从设备地址和一个读/写位(0表示写,1表示读)。
  3. 应答信号:从设备接收到地址后,如果识别到自己的地址,会在时钟线的下一个高电平期间将数据线拉低,表示应答(ACK)。
  4. 数据传输:数据按字节传输,每个字节后跟一个应答位。
  5. 停止信号:通信结束时,主设备发出停止信号,即在SCL为高电平时,将SDA从低电平变为高电平。

start:开始信号,SCL置高电平 SDA拉低

bit7~bit1:I2C从设备地址

bit0:读写位 写为0,读为1,与bit7~bit1 凑成8位地址,所以有些设备分为读地址,写地址。

 ACK:响应信号,发送完地址后,主设备会置高,从设备响应后会马上拉低,主设备读取到拉低,判断设备存在

stop:结束信号,SCL置高,SDA拉高

数据传输速率

I2C协议支持多种速率模式,包括:

  • 标准模式:最高100kbps
  • 快速模式:最高400kbps
  • 快速模式+:最高1Mbps
  • 高速模式:最高3.4Mbps
  • 超快模式:最高5Mbps(仅限单向传输)

I2C地址格式

I2C设备通常使用7位或10位地址格式,这使得总线上可以有多个设备同时存在而不发生冲突。

I2C总线冲突和仲裁

当两个主设备同时尝试控制总线时,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 <linux/init.h>
#include <linux/module.h>
#include <linux/i2c.h>
#include <linux/device.h>
#include <linux/platform_device.h>
#include <linux/of.h>
#include <linux/of_address.h>

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 <linux/i2c.h>
#include <linux/i2c-dev.h>
#include <stdint.h>
#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.先用示波器测量数据,发现从设备应答电压无法拉低,改变上拉电阻阻值,尝试是否恢复

  • 15
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

hmbbPdx_

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

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

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

打赏作者

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

抵扣说明:

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

余额充值