• Linux I2C调试分享


    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下,设备树如下:

    1. &i2c5 {
    2. //时钟100k
    3. clock-frequency = <100000>;
    4. status = "okay";
    5. //@44设备地址
    6. tda7313: tda7313@44 {
    7. status = "okay";
    8. compatible = "st,tda7313";
    9. //描述设备地址
    10. reg = <0x44>;
    11. };
    12. }

    Linux下发送I2C数据有三种方式:

     1.i2c驱动 

     2.系统应用编程

     3.命令发送

    1. //i2c驱动
    2. #include
    3. #include
    4. #include
    5. #include
    6. #include
    7. #include
    8. #include
    9. int myprobe(struct i2c_client *cli, const struct i2c_device_id *id)
    10. {
    11. int ret;
    12. struct i2c_msg msgs[1];
    13. char addr[2] = { 0x11,0x22 }; //2个数据
    14. msgs[0].addr = cli->addr;//设备地址 0x44
    15. msgs[0].flags = 0;//读写标志位
    16. msgs[0].len = 2;//数据长度
    17. msgs[0].buf = addr;//数据地址
    18. //执行i2c_transfer = 1个开始信号 + 写地址 + 0x11 + 0x22 + 停止信号
    19. ret = i2c_transfer(cli->adapter, msgs, 1);
    20. if (ret < 0){
    21. msleep(10);
    22. }
    23. return 0;
    24. }
    25. int myremove(struct i2c_client *cli)
    26. {
    27. return 0;
    28. }
    29. struct i2c_device_id ids[] = {
    30. { "st,tda7313", 0 },
    31. {},
    32. };
    33. MODULE_DEVICE_TABLE(i2c, ids);
    34. //配对节点
    35. static const struct of_device_id tda7313_of_match[] = {
    36. { .compatible = "st,tda7313" },
    37. {},
    38. };
    39. struct i2c_driver mydrv = {
    40. .probe = myprobe,
    41. .remove = myremove,
    42. .driver = {
    43. .name = "tda7313",
    44. .of_match_table = tda7313_of_match,
    45. .owner = THIS_MODULE,
    46. },
    47. .id_table = ids,
    48. };
    49. module_i2c_driver(mydrv);
    50. MODULE_LICENSE("GPL");

    Linux系统编程:

     设备树打开i2c-5后会在产生/dev/i2c-5节点:

    1. rk3568_s:/ $ ls /dev/i2c-5 -l
    2. crw-rw-rw- 1 system system 89, 5 2017-08-04 17:00 /dev/i2c-5

    可以对该节点操作i2c

     i2c_test.c

    1. // 2.系统应用编程
    2. #include
    3. #include
    4. #include
    5. #define I2C_ADDR 0x44
    6. int fd;
    7. unsigned char Tuner_WriteBuffer(unsigned char *buf, uint16_t len)
    8. {
    9. uint16_t i;
    10. uint8_t r;
    11. if (fd < 0)
    12. {
    13. printf("fd < 0");
    14. return -1;
    15. }
    16. //i2c数据填充
    17. struct i2c_msg msgs[1] = {
    18. {I2C_ADDR, 0, len, buf},//第一个I2c地址,写标志位,长度,buf数据地址
    19. };
    20. struct i2c_rdwr_ioctl_data idata = {
    21. .msgs = msgs,
    22. .nmsgs = 1, //对应msgs长度
    23. };
    24. //下发i2c驱动,驱动帮你写数据
    25. if (ioctl(fd, I2C_RDWR, &idata) < 0)
    26. {
    27. printf("ioctl I2C_RDWR error %d", fd);
    28. return -1;
    29. }
    30. usleep(1 * 1000);
    31. return 1;
    32. }
    33. unsigned char Tuner_ReadBuffer(unsigned char *addr, uint16_t addrLen, unsigned char *buf, uint16_t len)
    34. {
    35. if (fd < 0)
    36. {
    37. printf("fd < 0");
    38. return -1;
    39. }
    40. unsigned char reg = 0;
    41. uint16_t i = 1;
    42. struct i2c_msg msgs[2] = {
    43. {I2C_ADDR, 0, addrLen, addr},
    44. {I2C_ADDR, I2C_M_RD, len, buf},
    45. };
    46. struct i2c_rdwr_ioctl_data idata = {
    47. .msgs = msgs,
    48. .nmsgs = 2,
    49. };
    50. if (ioctl(fd, I2C_RDWR, &idata) < 0)
    51. {
    52. printf("chenjx I2C_RDWR error");
    53. return -1;
    54. }
    55. return 1;
    56. }
    57. void Tuner_I2C_Init()
    58. {
    59. fd = open("/dev/i2c-5", I2C_RDWR);
    60. if (fd < 0)
    61. {
    62. printf("open %s false", "/dev/i2c-5");
    63. return;
    64. }
    65. printf("open %s succuss", "/dev/i2c-5");
    66. }
    67. void Tuner_I2C_UnInit()
    68. {
    69. close(fd);
    70. }
    71. int main(){
    72. unsigned char data[2] = {0x11,0x22};
    73. Tuner_I2C_Init();
    74. Tuner_WriteBuffer(data,2);
    75. Tuner_I2C_UnInit();
    76. }

    3.Linux命令:

    1. i2ctransfer -f -y 5 w2@0x44 0x11 0x22
    2. -y 5:哪条总线
    3. w2:写两个字节地址
    4. 0x11 0x22:数据

    I2C调试 经验分享 :

    i2cdetect -y -r 5 :扫描5总线上挂载的是有设备地址

    一.查不到设备地址

    1. i2cdetect -y 5如果没有扫到该地址确认供电部分 

    2.有些设备需要时钟信号i2c才能正常工作(大部分图像处理芯片,xs9922)

    无法写入数据:

    1.无法写入数据返回-5,确认同一总线挂载上的设备影响被拉低,确认它们的电压

    2.先用示波器测量数据,发现从设备应答电压无法拉低,改变上拉电阻阻值,尝试是否恢复

  • 相关阅读:
    【conda】——pack打包32位python,在服务器报 no such file
    Shopify独立站流量还可以从哪里来
    【离散化 二维差分】391. 完美矩形
    Spring boot与Spring cloud 之间的关系
    ZeRO论文阅读
    使用IDEA创建一个SpringBoot项目
    RPC 框架设计 四、Netty高级应用
    【网络安全带你练爬虫-100练】第21练:批量获取文件夹中文件名
    Linux文件系统
    CoM-Px30|RK3358核心模板以太网通信测试
  • 原文地址:https://blog.csdn.net/yinsui1839/article/details/136699551