• I2C相关实验


    I2C简介

    I2C是很常见的一种总线协议,I2C是NXP公司设计的,I2C使用两条线在主控制器和从机之间进行数据通信。一条是SCL(串行时钟线),另外一条是SDA(串行数据线),这两条数据线需要接上拉电阻,总线空闲的时候SCL和SDA处于高电平。I2C总线标准模式下速度可以达到100Kb/S,快速模式下可以达到400Kb/S。

    I2C是支持多从机的:

    在这里插入图片描述

    一般会接一个4.7k的上拉电阻(VDD)。

    I2C总线工作是按照一定的协议来运行的 ,其中相关术语:

    起始位

    I2C通信起始标志,在SCL为高电平的时候,SDA出现下降沿就表示为起始位。

    停止位 

    停止I2C通信的标志位,和起始位的功能相反。在SCL位高电平的时候,SDA出现上升沿就表示为停止位。

    在这里插入图片描述

    数据传输 

    I2C总线在数据传输的时候要保证在SCL高电平期间,SDA上的数据稳定,因此SDA上的数据变化只能在SCL低电平期间发生。

    在这里插入图片描述

     应答信号

    当I2C主机发送完8位数据以后会将SDA设置为输入状态,等待I2C从机应答,也就是等到I2C从机告诉主机它接收到数据了。应答信号是由从机发出的,主机需要提供应答信号所需的时钟,主机发送完8位数据以后紧跟着的一个时钟信号就是给应答信号使用的。从机通过将SDA拉低来表示发出应答信号,表示通信成功,否则表示通信失败。
    I2C主机与从机之间的操作只有读与写两个操作。其时序分别为:

    在这里插入图片描述

    1)、开始信号。
    2)、发送I2C设备地址,每个I2C器件都有一个设备地址,通过发送具体的设备地址来决定访问哪个I2C器件。这是一个8位的数据,其中高7位是设备地址,最后1位是读写位,为1的话表示这是一个读操作,为0的话表示这是一个写操作。
    3)、 I2C器件地址后面跟着一个读写位,为0表示写操作,为1表示读操作。
    4)、从机发送的ACK应答信号。
    5)、重新发送开始信号。
    6)、发送要写写入数据的寄存器地址。
    7)、从机发送的ACK应答信号。
    8)、发送要写入寄存器的数据。
    9)、从机发送的ACK应答信号。
    10)、停止信号。

    在这里插入图片描述

    1)、主机发送起始信号。
    2)、主机发送要读取的I2C从设备地址。
    3)、读写控制位,因为是向I2C从设备发送数据,因此是写信号。
    4)、从机发送的ACK应答信号。
    5)、重新发送START信号。
    6)、主机发送要读取的寄存器地址。
    7)、从机发送的ACK应答信号。
    8)、重新发送START信号。
    9)、重新发送要读取的I2C从设备地址。
    10)、读写控制位,这里是读信号,表示接下来是从I2C从设备里面读取数据。
    11)、从机发送的ACK应答信号。
    12)、从I2C器件里面读取到的数据。
    13)、主机发出NO ACK信号,表示读取完成,不需要从机再发送ACK信号了。
    14)、主机发出STOP信号,停止I2C通信。
    操作I2C需要几个重要的寄存器:

    在这里插入图片描述

    I2Cx_IADR(x=1~4)寄存器:寄存器I2Cx_IADR只有ADR(bit7:1)位有效,用来保存I2C从设备地址数据。当我们要访问某个I2C从设备的时候就需要将其设备地址写入到ADR里面。

    在这里插入图片描述

    寄存器I2Cx_IFDR:寄存器I2Cx_IFDR也只有IC(bit5:0)这个位,用来设置I2C的波特率,I2C的时钟源可以选择IPG_CLK_ROOT=66MHz,I.MX6U的I2C支持两种模式:标准模式和快速模式,标准模式下I2C数据传输速率最高是100Kbits/s,在快速模式下数据传输速率最高为400Kbits/s。通过设置IC位既可以得到想要的I2C波特率。 

    在这里插入图片描述

    这里可以设置为0x15或0x38,即分频为640。

    在这里插入图片描述

    寄存器I2Cx_I2CR:这个是I2C控制寄存器,其各个位的作用:

    IEN(bit7):I2C使能位,为1的时候使能I2C,为0的时候关闭I2C。
    IIEN(bit6):I2C中断使能位,为1的时候使能I2C中断,为0的时候关闭I2C中断。
    MSTA(bit5):主从模式选择位,设置IIC工作在主模式还是从模式,为1的时候工作在主模式,为0的时候工作在从模式。
    MTX(bit4):传输方向选择位,用来设置是进行发送还是接收,为0的时候是接收,为1的时候是发送。
    TXAK(bit3):传输应答位使能,为0的话发送ACK信号,为1的话发送NO ACK信号。
    RSTA(bit2):重复开始信号,为1的话产生一个重新开始信号。

    在这里插入图片描述

    寄存器I2Cx_I2SR:I2C的状态寄存器 。

    寄存器I2Cx_I2SR的各位含义如下:
    ICF(bit7):数据传输状态位,为0的时候表示数据正在传输,为1的时候表示数据传输完成。
    IAAS(bit6):当为1的时候表示I2C地址,也就是I2Cx_IADR寄存器中的地址是从设备地址。
    IBB(bit5):I2C总线忙标志位,当为0的时候表示I2C总线空闲,为1的时候表示I2C总线忙。
    IAL(bit4):仲裁丢失位,为1的时候表示发生仲裁丢失。
    SRW(bit2):从机读写状态位,当I2C作为从机的时候使用,此位用来表明主机发送给从机的是读还是写命令。为0的时候表示主机要向从机写数据,为1的时候表示主机要从从机读取数据。
    IIF(bit1):I2C中断挂起标志位,当为1的时候表示有中断挂起,此位需要软件清零。
    RXAK(bit0):应答信号标志位,为0的时候表示接收到ACK应答信号,为1的话表示检测到NO ACK信号。
    最后一个寄存器就是I2Cx_I2DR,这是I2C的数据寄存器,此寄存器只有低8位有效,当要发送数据的时候将要发送的数据写入到此寄存器,如果要接收数据的话直接读取此寄存器即可得到接收到的数据。
    了解后开始写:

    1. #include "bsp_i2c.h"
    2. #include "bsp_delay.h"
    3. #include "stdio.h"
    4. void i2c_init(I2C_Type *base)
    5. {
    6. /* 1、配置I2C */
    7. base->I2CR &= ~(1 << 7); /* 要访问I2C的寄存器,首先需要先关闭I2C */
    8. base->IFDR = 0X15 << 0;
    9. /* 设置寄存器I2CR,开启I2C */
    10. base->I2CR |= (1<<7);
    11. }
    12. unsigned char i2c_master_repeated_start(I2C_Type *base,
    13. unsigned char address,
    14. enum i2c_direction direction) {
    15. /* I2C忙并且工作在从模式,跳出 */
    16. if(base->I2SR & (1 << 5) && (((base->I2CR) & (1 << 5)) == 0))
    17. return 1;
    18. /*
    19. * 设置寄存器I2CR
    20. * bit[4]: 1 发送
    21. * bit[2]: 1 产生重新开始信号
    22. */
    23. base->I2CR |= (1 << 4) | (1 << 2);
    24. /*
    25. * 设置寄存器I2DR,bit[7:0] : 要发送的数据,这里写入从设备地址
    26. */
    27. base->I2DR = ((unsigned int)address << 1) |
    28. ((direction == kI2C_Read)? 1 : 0);
    29. return 0;
    30. }
    31. unsigned char i2c_master_start(I2C_Type *base,
    32. unsigned char address,
    33. enum i2c_direction direction)
    34. {
    35. if(base->I2SR & (1 << 5)) /* I2C忙 */
    36. return 1;
    37. /*
    38. * 设置寄存器I2CR
    39. * bit[5]: 1 主模式
    40. * bit[4]: 1 发送
    41. */
    42. base->I2CR |= (1 << 5) | (1 << 4);
    43. /*
    44. * 设置寄存器I2DR,bit[7:0] : 要发送的数据,这里写入从设备地址
    45. */
    46. base->I2DR = ((unsigned int)address << 1) |
    47. ((direction == kI2C_Read)? 1 : 0);
    48. return 0;
    49. }
    50. unsigned char i2c_check_and_clear_error(I2C_Type *base,
    51. unsigned int status)
    52. {
    53. if(status & (1<<4)) /* 检查是否发生仲裁丢失错误 */
    54. {
    55. base->I2SR &= ~(1<<4); /* 清除仲裁丢失错误位 */
    56. base->I2CR &= ~(1 << 7); /* 先关闭I2C */
    57. base->I2CR |= (1 << 7); /* 重新打开I2C */
    58. return I2C_STATUS_ARBITRATIONLOST;
    59. }
    60. else if(status & (1 << 0)) /* 没有接收到从机的应答信号 */
    61. {
    62. return I2C_STATUS_NAK; /* 返回NAK(No acknowledge) */
    63. }
    64. return I2C_STATUS_OK;
    65. }
    66. unsigned char i2c_master_stop(I2C_Type *base)
    67. {
    68. unsigned short timeout = 0XFFFF;
    69. /* 清除I2CR的bit[5:3]这三位 */
    70. base->I2CR &= ~((1 << 5) | (1 << 4) | (1 << 3));
    71. while((base->I2SR & (1 << 5))) /* 等待忙结束 */
    72. {
    73. timeout--;
    74. if(timeout == 0) /* 超时跳出 */
    75. return I2C_STATUS_TIMEOUT;
    76. }
    77. return I2C_STATUS_OK;
    78. }
    79. void i2c_master_write(I2C_Type *base, const unsigned char *buf,
    80. unsigned int size)
    81. {
    82. while(!(base->I2SR & (1 << 7))); /* 等待传输完成 */
    83. base->I2SR &= ~(1 << 1); /* 清除标志位 */
    84. base->I2CR |= 1 << 4; /* 发送数据 */
    85. while(size--)
    86. {
    87. base->I2DR = *buf++; /* 将buf中的数据写入到I2DR寄存器 */
    88. while(!(base->I2SR & (1 << 1))); /* 等待传输完成 */
    89. base->I2SR &= ~(1 << 1); /* 清除标志位 */
    90. /* 检查ACK */
    91. if(i2c_check_and_clear_error(base, base->I2SR))
    92. break;
    93. }
    94. base->I2SR &= ~(1 << 1);
    95. i2c_master_stop(base); /* 发送停止信号 */
    96. }
    97. void i2c_master_read(I2C_Type *base, unsigned char *buf,
    98. unsigned int size)
    99. {
    100. volatile uint8_t dummy = 0;
    101. dummy++; /* 防止编译报错 */
    102. while(!(base->I2SR & (1 << 7))); /* 等待传输完成 */
    103. base->I2SR &= ~(1 << 1); /* 清除中断挂起位 */
    104. base->I2CR &= ~((1 << 4) | (1 << 3)); /* 接收数据 */
    105. if(size == 1) /* 如果只接收一个字节数据的话发送NACK信号 */
    106. base->I2CR |= (1 << 3);
    107. dummy = base->I2DR; /* 假读 */
    108. while(size--)
    109. {
    110. while(!(base->I2SR & (1 << 1))); /* 等待传输完成 */
    111. base->I2SR &= ~(1 << 1); /* 清除标志位 */
    112. if(size == 0)
    113. i2c_master_stop(base); /* 发送停止信号 */
    114. if(size == 1)
    115. base->I2CR |= (1 << 3);
    116. *buf++ = base->I2DR;
    117. }
    118. }
    119. unsigned char i2c_master_transfer(I2C_Type *base,
    120. struct i2c_transfer *xfer)
    121. {
    122. unsigned char ret = 0;
    123. enum i2c_direction direction = xfer->direction;
    124. base->I2SR &= ~((1 << 1) | (1 << 4)); /* 清除标志位 */
    125. while(!((base->I2SR >> 7) & 0X1)){}; /* 等待传输完成 */
    126. /* 如果是读的话,要先发送寄存器地址,所以要先将方向改为写 */
    127. if ((xfer->subaddressSize > 0) && (xfer->direction ==
    128. kI2C_Read))
    129. direction = kI2C_Write;
    130. ret = i2c_master_start(base, xfer->slaveAddress, direction);
    131. if(ret)
    132. return ret;
    133. while(!(base->I2SR & (1 << 1))){}; /* 等待传输完成 */
    134. ret = i2c_check_and_clear_error(base, base->I2SR);
    135. if(ret)
    136. {
    137. i2c_master_stop(base); /* 发送出错,发送停止信号 */
    138. return ret;
    139. }
    140. /* 发送寄存器地址 */
    141. if(xfer->subaddressSize)
    142. {
    143. do
    144. {
    145. base->I2SR &= ~(1 << 1); /* 清除标志位 */
    146. xfer->subaddressSize--; /* 地址长度减一 */
    147. base->I2DR = ((xfer->subaddress) >> (8 *
    148. xfer->subaddressSize));
    149. while(!(base->I2SR & (1 << 1))); /* 等待传输完成 */
    150. /* 检查是否有错误发生 */
    151. ret = i2c_check_and_clear_error(base, base->I2SR);
    152. if(ret)
    153. {
    154. i2c_master_stop(base); /* 发送停止信号 */
    155. return ret;
    156. }
    157. } while ((xfer->subaddressSize > 0) && (ret ==
    158. I2C_STATUS_OK));
    159. if(xfer->direction == kI2C_Read) /* 读取数据 */
    160. {
    161. base->I2SR &= ~(1 << 1); /* 清除中断挂起位 */
    162. i2c_master_repeated_start(base, xfer->slaveAddress,
    163. kI2C_Read);
    164. while(!(base->I2SR & (1 << 1))){}; /* 等待传输完成 */
    165. /* 检查是否有错误发生 */
    166. ret = i2c_check_and_clear_error(base, base->I2SR);
    167. if(ret)
    168. {
    169. ret = I2C_STATUS_ADDRNAK;
    170. i2c_master_stop(base); /* 发送停止信号 */
    171. return ret;
    172. }
    173. }
    174. }
    175. /* 发送数据 */
    176. if ((xfer->direction == kI2C_Write) && (xfer->dataSize > 0))
    177. i2c_master_write(base, xfer->data, xfer->dataSize);
    178. /* 读取数据 */
    179. if ((xfer->direction == kI2C_Read) && (xfer->dataSize > 0))
    180. i2c_master_read(base, xfer->data, xfer->dataSize);
    181. return 0;
    182. }

  • 相关阅读:
    软件项目管理
    Excel设置自动编号的方法
    Thingsboard二次开发---5.在Thingsboard中增加解决方案管理功能
    基于JAVA摄影网站计算机毕业设计源码+数据库+lw文档+系统+部署
    基于Springboot+mybatis+mysql+html教育培训中心教学系统
    C++:运算符重载简介
    选择题汇总1-2(括号里填的答案都是对的,不用管下面那个答案正确与错误,因为作者懒得删了)
    双目立体视觉摄像头的标定、矫正、世界坐标计算(opencv)
    浏览器插件在content_script和top窗口之间进行消息通信
    QT制作简易时钟
  • 原文地址:https://blog.csdn.net/qq_66545503/article/details/126454503