【STC15单片机】模拟I2C操作AT24C02数据读取,PCF8591的A/D转换代码

本文涉及的产品
容器服务 Serverless 版 ACK Serverless,317元额度 多规格
容器服务 Serverless 版 ACK Serverless,952元额度 多规格
简介: 【STC15单片机】模拟I2C操作AT24C02数据读取,PCF8591的A/D转换代码

d22b3b33b3063f468afd56ab2ebf5d8f_8767baea0f184615b47c7c13440d1821.png

PCB上线的长短可能影响数据传输的时间,写I2C时序可能就要加一点延时!!!!!!

I2C时序结构

  • 起始条件:SCL高电平期间,SDA从高电平切换到低电平
  • 终止条件:SCL高电平期间,SDA从低电平切换到高电平

7056ad15245e09620b68f00cee76b885_121d87640e17466997ceb8450ee5fb7f.png


发送一个字节:SCL低电平期间,主机将数据位依次放到SDA线上(高位在前),然后拉高SCL,从机将在SCL高电平期间读取数据位,素有SCL高电平期间SDA不允许有数据变化,依次循环上述过程8次,即可发送一个字节

1572bd53ee1b6ba12710442af3c235e1_5aeeca12e053440eb2a458715b698cb9.png

接收一个字节:SCL低电平期间,从机将数据位依次放到SDA线上(高位在前),然后拉高SCL,主机将在SCL高电平期间读取数据位,所以SCL高电平期间SDA不允许有数据变化,依次循环上述过程8次,即可接收一个字节(主机在接收之前,需要释放SDA)

4553ed43a365ae38f4455b84a950e358_c8ea676536254521aac5b870feb0d0bd.png


  • 发送应答:在接收完一个字节之后,主机在下一个时钟发送一位数据,数据0表示应答,数据1表示非应答
  • 接收应答:在发送完一个字节之后,主机在下一个时钟接收一位数据,判断从机是否应答,数据0表示应答,数据1表示非应答(主机在接收前,需要释放SDA)

9f208c040f6c19fc8863ccee9d40de4e_e3707d5a4b494a579c9632b7e22d2db6.png

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

c7e8dd00a1f819f0425680128cd72b88_de6a266787ce4c4dbb3e8ca9fbcc989f.png

AT24C02设备地址:

f7ebd2d721c4aec1b20dbb347559eac3_18fe4b28481d4cf9863c2cb465e14dac.png

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

0ef402d4f6e5cd7c5abc937837f455fe_a3f36db932c8402d91a5fca35a03bdfc.png

PCF8591地址:1001 0000(0x90   写),1001 0001(0x91   读)

90cc4b001c2994f5f1d19d1ac537ec02_8c6baf0febae4afea6ce295a79a892c2.png

支持四路AD转换(模拟信号转数字信号),支持一路DA转换(数字信号转模拟信号)

7dfdc611e63b283d48c05ed2d5db2d59_baf64fb43d3d45d4aea929ca1f8516a7.png

//滑动变阻器接AIN3通道

//光敏电阻接AIN1通道

9d071fb9af91f73a105c76bffd6be4b8_5cb3fd8b623c43f095516ff86fd99bac.png

PCF8591 I2C

I2C.c

不能直接移植51单片机的I2C程序,STC15单片机运行速度比51单片机快,直接移植某些有些时序块不正常,数据就不能正确读取!!!

所以直接调用官方的IIC驱动代码,稍加修改就变成自己的代码了

42bb742712d4f66890c8136dfaa9ebdf_9974c2dead3944218f2ab9887e766008.jpeg

 

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】

2dc88500c1f2d1c70a0bb29bb214553c_98c30848f95c49ff8580a0a23750c307.png


相关实践学习
通过Ingress进行灰度发布
本场景您将运行一个简单的应用,部署一个新的应用用于新的发布,并通过Ingress能力实现灰度发布。
容器应用与集群管理
欢迎来到《容器应用与集群管理》课程,本课程是“云原生容器Clouder认证“系列中的第二阶段。课程将向您介绍与容器集群相关的概念和技术,这些概念和技术可以帮助您了解阿里云容器服务ACK/ACK Serverless的使用。同时,本课程也会向您介绍可以采取的工具、方法和可操作步骤,以帮助您了解如何基于容器服务ACK Serverless构建和管理企业级应用。 学习完本课程后,您将能够: 掌握容器集群、容器编排的基本概念 掌握Kubernetes的基础概念及核心思想 掌握阿里云容器服务ACK/ACK Serverless概念及使用方法 基于容器服务ACK Serverless搭建和管理企业级网站应用
相关文章
|
5天前
|
C语言
【51单片机】LCD1602显示字符串,时间、时间+按键校准、秒表计时的功能代码。
【51单片机】LCD1602显示字符串,时间、时间+按键校准、秒表计时的功能代码。
|
5天前
|
C语言
51单片机汇编语言流水灯代码
51单片机汇编语言流水灯代码
|
3月前
|
传感器
51单片机循迹小车原理介绍和代码示例
51单片机循迹小车原理介绍和代码示例
51单片机循迹小车原理介绍和代码示例
|
3月前
|
传感器
51单片机矩阵键盘超详解!(内含LCD1602代码)
51单片机矩阵键盘超详解!(内含LCD1602代码)
40 0
【51单片机】一文带你利用【Keil软件的模板功能】【自定义模板】简化操作(带图详解)
【51单片机】一文带你利用【Keil软件的模板功能】【自定义模板】简化操作(带图详解)
【51单片机】自定义动态数码管显示(设计思路&原理&代码演示)
【51单片机】自定义动态数码管显示(设计思路&原理&代码演示)
【51单片机】自定义静态数码管显示(设计思路&代码演示)
【51单片机】自定义静态数码管显示(设计思路&代码演示)
|
3月前
|
C++
【51单片机】添加模块代码的常见问题(图示&代码演示)
【51单片机】添加模块代码的常见问题(图示&代码演示)
|
3月前
|
编译器 C语言 开发者
单片机原理与应用:探索微型计算机世界
单片机原理与应用:探索微型计算机世界
37 1
|
3月前
|
数据采集 数据处理 C语言
单片机:探索其原理、应用与编程实践
单片机:探索其原理、应用与编程实践
46 1

热门文章

最新文章