PCB上线的长短可能影响数据传输的时间,写I2C时序可能就要加一点延时!!!!!!
I2C时序结构
- 起始条件:SCL高电平期间,SDA从高电平切换到低电平
- 终止条件:SCL高电平期间,SDA从低电平切换到高电平
发送一个字节:SCL低电平期间,主机将数据位依次放到SDA线上(高位在前),然后拉高SCL,从机将在SCL高电平期间读取数据位,素有SCL高电平期间SDA不允许有数据变化,依次循环上述过程8次,即可发送一个字节
接收一个字节:SCL低电平期间,从机将数据位依次放到SDA线上(高位在前),然后拉高SCL,主机将在SCL高电平期间读取数据位,所以SCL高电平期间SDA不允许有数据变化,依次循环上述过程8次,即可接收一个字节(主机在接收之前,需要释放SDA)
- 发送应答:在接收完一个字节之后,主机在下一个时钟发送一位数据,数据0表示应答,数据1表示非应答
- 接收应答:在发送完一个字节之后,主机在下一个时钟接收一位数据,判断从机是否应答,数据0表示应答,数据1表示非应答(主机在接收前,需要释放SDA)
I2C代码
1. #include "I2C.h" 2. 3. //15开发板中AT24C02的IIC通信引脚 4. sbit I2C_SDA = P2^1; 5. sbit I2C_SCL = P2^0; 6. 7. /** 8. * @brief I2C起始信号 9. * @param 无 10. * @retval 无 11. */ 12. void I2C_Start(void) 13. { 14. I2C_SDA = 1; //拼接其他块,确保默认是1 15. I2C_SCL = 1; 16. I2C_SDA = 0; //时序上先拉低 17. I2C_SCL = 0; 18. } 19. 20. /** 21. * @brief I2C停止信号 22. * @param 无 23. * @retval 无 24. */ 25. void I2C_Stop(void) 26. { 27. I2C_SDA = 0; //确保默认0,保证由低电平拉高至高电平 28. I2C_SCL = 1; 29. I2C_SDA = 1; 30. } 31. 32. /** 33. * @brief I2C发送一个字节数据 34. * @param Byte 要发送的字节 35. * @retval 无 36. */ 37. void I2C_SendByte(unsigned char Byte) 38. { 39. unsigned char i; 40. // I2C_SCL = 0; //起始信号已经拉低了,无需重复 41. for (i = 0; i < 8; i ++) 42. { 43. I2C_SDA = Byte & (0x80>>i); //把Byte最高位取出,赋值给I2C_SDA,依次从高到底 44. I2C_SCL = 1; 45. // _nop_();_nop_();_nop_();_nop_();_nop_();//需要0.4us 46. I2C_SCL = 0; 47. } 48. } 49. 50. /** 51. * @brief I2C接收一个字节数据 52. * @param 无 53. * @retval 接受到的一个字节数据 54. */ 55. unsigned char I2C_ReceiveByte(void) 56. { 57. unsigned char i,Byte=0x00; 58. 59. I2C_SDA = 1; //接收之前,先把I2C_SDA置1,释放总线 60. for(i=0;i<8;i++) 61. { 62. I2C_SCL = 1; //拉高SCL,SCL读取SDA 63. if(I2C_SDA == 1){Byte |= (0x80>>i);} 64. /*如果读取到的SDA为1,把Byte最高位置1,如果读取到的SDA为0,不处理,默认最高位为0,放在循环里从高位到低位依次读取*/ 65. I2C_SCL = 0; //拉低SCL,结束当前循环 66. } 67. return Byte; 68. } 69. 70. /** 71. * @brief I2C发送应答 72. * @param AckBit 应答位,0为应答,1为非应答 73. * @retval 无 74. */ 75. void I2C_SendAck(unsigned char AckBit) 76. { 77. I2C_SDA = AckBit;//给应答就是0,不给应答就是1 78. I2C_SCL = 1; 79. I2C_SCL = 0; 80. } 81. 82. /** 83. * @brief I2C接收应答 84. * @param 无 85. * @retval 接收到的应答位,0为应答,1为非应答 86. */ 87. unsigned char I2C_ReceiveAck(void) 88. { 89. unsigned char AckBit; 90. I2C_SDA = 1; //先释放SDA 91. I2C_SCL = 1; //高电平期间读取SDA 92. AckBit=I2C_SDA; //读取到什么,就返回什么 93. I2C_SCL = 0; 94. return AckBit; //返回0,代表有应答,返回1,代表无应答 95. }
1. #ifndef __I2C_H__ 2. #define __I2C_H__ 3. 4. #include <STC15F2K60S2.H> 5. #include "intrins.h" 6. 7. void I2C_Start(void); 8. void I2C_Stop(void); 9. void I2C_SendByte(unsigned char Byte); 10. unsigned char I2C_ReceiveByte(void); 11. void I2C_SendAck(unsigned char AckBit); 12. unsigned char I2C_ReceiveAck(void); 13. 14. #endif
AT24C02设备地址:
A2A1A0这三个引脚是用来扩展的,如果不用就默认是000
所以默认设备地址是10100000(0xA0 写数据),10100001(0xA1 读数据)
AT24C02代码(继承I2C底层代码)
1. #include "AT24C02.h" 2. 3. #define AT24C02_ADDRESS 0xA0 //从机地址+写 1010 0000 4. 5. /** 6. *@brief 名称:AT24C02写入一个字节 7. *@param 参数:WordAddess 要写入字节的地址(0~255) 8. *@param 参数: Data 要写入的数据 9. *@retval返回值:无 10. */ 11. void AT24C02_WriteByte(unsigned char WordAddress, unsigned char Data) 12. { 13. // unsigned char Ack; 14. I2C_Start(); 15. I2C_SendByte(AT24C02_ADDRESS);//从机地址+写 16. // Ack = I2C_ReceiveAck(); 17. // if(Ack == 0) 18. // { 19. // P2 = 0x80; // 1000 0000 20. // P0 = 0x00; 21. // } //先测试是否有应答,定义的Ack被I2C_ReceiveAck();赋值,然后就可以测试了 22. I2C_ReceiveAck(); 23. I2C_SendByte(WordAddress); //要写入数据的地址 24. I2C_ReceiveAck(); 25. I2C_SendByte(Data); //写入数据 26. I2C_ReceiveAck(); 27. I2C_Stop(); 28. } 29. 30. /** 31. *@brief 名称:AT24C02读取一个字节 32. *@param 参数:WordAddess 要读出字节的地址 33. *@retval返回值:读出的数据 34. */ 35. unsigned char AT24C02_ReadByte(unsigned char WordAddess) 36. { 37. unsigned char Data; 38. I2C_Start(); 39. I2C_SendByte(AT24C02_ADDRESS); //从机地址+写 40. I2C_ReceiveAck(); //接受应答 41. I2C_SendByte(WordAddess); //数据的地址 42. I2C_ReceiveAck(); 43. 44. I2C_Start(); 45. I2C_SendByte(AT24C02_ADDRESS | 0x01);//发送从机地址+读本函数第二个函数,或上0x01变成读地址(0xA1) 46. I2C_ReceiveAck(); 47. Data = I2C_ReceiveByte(); 48. I2C_SendAck(1); //不发送应答 49. I2C_Stop(); 50. 51. return Data; 52. }
1. #ifndef __AT24C02_H__ 2. #define __AT24C02_H__ 3. 4. #include <STC15F2K60S2.H> 5. #include "I2C.h" 6. 7. void AT24C02_WriteByte(unsigned char WordAddress, unsigned char Data); 8. unsigned char AT24C02_ReadByte(unsigned char WordAddress); 9. 10. #endif
测试:先写入,不读取,然后下载只读取的函数,如果读出数值没有问题,那就正确了
1. #include <STC15F2K60S2.H> 2. #include "smg.h" 3. #include "I2C.h" 4. #include "AT24C02.h" 5. 6. unsigned char Data; 7. 8. void main() 9. { 10. All_Init(); 11. 12. AT24C02_WriteByte(0,88); 13. Delay_ms(5); //写数据的周期 5ms 14. Data = AT24C02_ReadByte(0); 15. 16. while(1) 17. { 18. Nixie(1,Data/10); 19. Nixie(2,Data%10); 20. } 21. }
PCF8591
PCF8591地址:1001 0000(0x90 写),1001 0001(0x91 读)
支持四路AD转换(模拟信号转数字信号),支持一路DA转换(数字信号转模拟信号)
//滑动变阻器接AIN3通道
//光敏电阻接AIN1通道
PCF8591 I2C
I2C.c
不能直接移植51单片机的I2C程序,STC15单片机运行速度比51单片机快,直接移植某些有些时序块不正常,数据就不能正确读取!!!
所以直接调用官方的IIC驱动代码,稍加修改就变成自己的代码了
1. #include "I2C.h" 2. #include "intrins.h" 3. 4. #define DELAY_TIME 5 5. 6. //15开发板中AT24C02的IIC通信引脚 7. sbit I2C_SCL = P2^0; /*时钟线*/ 8. sbit I2C_SDA = P2^1; /*数据线*/ 9. 10. void IIC_Delay(unsigned char i) 11. { 12. do{_nop_();} 13. while(i--); 14. } 15. 16. /** 17. * @brief I2C起始信号 18. * @param 无 19. * @retval 无 20. */ 21. void I2C_Start(void) 22. { 23. I2C_SDA = 1; //拼接其他块,确保默认是1 24. I2C_SCL = 1; IIC_Delay(DELAY_TIME); 25. I2C_SDA = 0; IIC_Delay(DELAY_TIME); //时序上先拉低 26. I2C_SCL = 0; 27. } 28. 29. /** 30. * @brief I2C停止信号 31. * @param 无 32. * @retval 无 33. */ 34. void I2C_Stop(void) 35. { 36. I2C_SDA = 0; //确保默认0,保证由低电平拉高至高电平 37. I2C_SCL = 1; IIC_Delay(DELAY_TIME); 38. I2C_SDA = 1; IIC_Delay(DELAY_TIME); 39. } 40. 41. 42. 43. /** 44. * @brief I2C发送一个字节数据 45. * @param Byte 要发送的字节 46. * @retval 无 47. */ 48. void I2C_SendByte(unsigned char Byte) 49. { 50. unsigned char i; 51. 52. for (i = 0; i < 8; i ++) 53. { 54. I2C_SCL = 0; IIC_Delay(DELAY_TIME); 55. I2C_SDA = Byte & (0x80>>i); //把Byte最高位取出,赋值给I2C_SDA,依次从高到底 56. IIC_Delay(DELAY_TIME); 57. I2C_SCL = 1; IIC_Delay(DELAY_TIME);//需要0.4us 58. } 59. I2C_SCL = 0; 60. } 61. 62. /** 63. * @brief I2C接收一个字节数据 64. * @param 无 65. * @retval 接受到的一个字节数据 66. */ 67. unsigned char I2C_ReceiveByte(void) 68. { 69. unsigned char i,Byte=0x00; 70. 71. I2C_SDA = 1; //接收之前,先把I2C_SDA置1,释放总线 72. for(i=0;i<8;i++) 73. { 74. I2C_SCL = 1; IIC_Delay(DELAY_TIME);//拉高SCL,SCL读取SDA 75. 76. if(I2C_SDA == 1){Byte |= (0x80>>i);} 77. /*如果读取到的SDA为1,把Byte最高位置1,如果读取到的SDA为0,不处理,默认最高位为0,放在循环里从高位到低位依次读取*/ 78. I2C_SCL = 0; IIC_Delay(DELAY_TIME);//拉低SCL,结束当前循环 79. } 80. return Byte; 81. } 82. 83. 84. 85. /** 86. * @brief I2C发送应答 87. * @param AckBit 应答位,0为应答,1为非应答 88. * @retval 无 89. */ 90. void I2C_SendAck(bit AckBit) 91. { 92. I2C_SCL = 0; 93. I2C_SDA = AckBit; IIC_Delay(DELAY_TIME); //给应答就是0,不给应答就是1 94. I2C_SCL = 1; IIC_Delay(DELAY_TIME); 95. I2C_SCL = 0; 96. I2C_SDA = 1; IIC_Delay(DELAY_TIME); 97. } 98. 99. /** 100. * @brief I2C接收应答 101. * @param 无 102. * @retval 接收到的应答位,0为应答,1为非应答 103. */ 104. unsigned char I2C_ReceiveAck(void) 105. { 106. unsigned char AckBit; 107. I2C_SDA = 1; //先释放SDA 108. I2C_SCL = 1; IIC_Delay(DELAY_TIME);//高电平期间读取SDA 109. AckBit=I2C_SDA; //读取到什么,就返回什么 110. I2C_SCL = 0; 111. return AckBit; //返回0,代表有应答,返回1,代表无应答 112. }
I2C.h
1. #ifndef __I2C_H__ 2. #define __I2C_H__ 3. 4. #include <STC15F2K60S2.H> 5. #include "intrins.h" 6. #include "smg.h" 7. 8. void I2C_Start(void); 9. void I2C_Stop(void); 10. void I2C_SendByte(unsigned char Byte); 11. unsigned char I2C_ReceiveByte(void); 12. void I2C_SendAck(unsigned char AckBit); 13. unsigned char I2C_ReceiveAck(void); 14. 15. #endif
PCF8591代码
PCF8591.c
1. #include "PCF8591.h" 2. 3. #define PCF8591_ADDRESS 0x90 //从机地址+写 1001 0000 4. 5. /** 6. *@brief 名称:PCF8591写入一个字节 7. *@param 参数:WordAddess 要写入字节的地址(0~255) 8. *@param 参数: Data 要写入的数据 9. *@retval返回值:无 10. */ 11. void PCF8591_WriteByte(unsigned char WordAddress, unsigned char Data) 12. { 13. // unsigned char Ack; 14. I2C_Start(); 15. I2C_SendByte(PCF8591_ADDRESS);//从机地址+写 16. // Ack = I2C_ReceiveAck(); 17. // if(Ack == 0) 18. // { 19. // P2 = 0x80; // 1000 0000 20. // P0 = 0x00; 21. // } //先测试是否有应答,定义的Ack被I2C_ReceiveAck();赋值,然后就可以测试了 22. I2C_ReceiveAck(); 23. I2C_SendByte(WordAddress); //要写入数据的地址 24. I2C_ReceiveAck(); 25. I2C_SendByte(Data); //写入数据 26. I2C_ReceiveAck(); 27. I2C_Stop(); 28. } 29. 30. /** 31. *@brief 名称:PCF8591读取一个字节 32. *@param 参数:WordAddess 要读出字节的地址 33. *@retval返回值:读出的数据 34. */ 35. unsigned char PCF8591_ReadByte(unsigned char WordAddess) 36. { 37. unsigned char Data; 38. I2C_Start(); 39. I2C_SendByte(PCF8591_ADDRESS); //从机地址+读取 40. I2C_ReceiveAck(); //接受应答 41. I2C_SendByte(WordAddess); //数据的地址 42. I2C_ReceiveAck(); 43. I2C_Stop(); 44. 45. I2C_Start(); 46. I2C_SendByte(PCF8591_ADDRESS | 0x01);//发送从机地址+读本函数第二个函数,或上0x01变成读地址(0x91) 47. I2C_ReceiveAck(); 48. Data = I2C_ReceiveByte(); 49. // I2C_SendAck(1); //不发送应答 50. I2C_Stop(); 51. Data*=0.395;//限制在0~100内 52. return Data; 53. }
PCF8591.h
1. #ifndef __PCF8591_H__ 2. #define __PCF8591_H__ 3. 4. #include <STC15F2K60S2.H> 5. #include "I2C.h" 6. 7. void PCF8591_WriteByte(unsigned char WordAddress, unsigned char Data); 8. unsigned char PCF8591_ReadByte(unsigned char WordAddess); 9. 10. #endif
主函数
1. #include <STC15F2K60S2.H> 2. #include "smg.h" 3. #include "I2C.h" 4. #include "PCF8591.h" 5. 6. int Data; 7. 8. void main() 9. { 10. All_Init(); 11. 12. while(1) 13. { 14. // Data = PCF8591_ReadByte(0X01);//光敏电阻接AIN1通道 15. Data = PCF8591_ReadByte(0X03);//滑动变阻器接AIN3通道 16. 17. Nixie(1, Data/1000%10); 18. Nixie(2, Data/100%10); 19. Nixie(3, Data/10%10); 20. Nixie(4, Data/1%10); 21. } 22. }
学习资料:
江科大自化协---51单片机入门教程-2020版 程序全程纯手打 从零开始入门【12-2】