51单片机 | 基于I2C总线的秒表模拟应用

————————————————————————————————————————————

参考地址:

http://blog.csdn.net/junyeer/article/details/46480863

http://blog.csdn.net/bob_fly1984/article/details/22690381

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

硬件结构:

  • SDA数据线
  • SCL时间线
  • 上拉电阻

    p.s. 当总线空闲状态时,两根线被上拉电阻拉高,保持高电平。连接总线上的任一器件输出的低电平都将使总线的信号变低

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

总线特征:

  • I2C总线上的每一个设备都可以作为主设备或从设备
  • 每个设备会对应一个唯一的地址,主从设备之间通过地址来确定与哪个器件通信

    p.s. 地址分为7位或10位,此处只介绍7

  • 通常情况下,把CPU带有I2C总线接口的模块作为主设备,其他挂在总线上的作为从设备
  • 主从设备之间以字节为单位进行双向数据传输

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

工作方式:

  • 主从工作方式:主器件启动数据的发送,产生时钟信号,发出停止信号

    p.s. 是没有I2C总线硬件接口的单片机采用软件模拟I2C总线常用的工作方式

  • 多主工作方式:需要总线竞争或仲裁

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

通信时序(主从工作方式):

  • 空闲状态:
    • SCL和SDA保持高电平状态
  • 忙状态:

    p.s. SDA线必须在SCL时钟的高电平周期才能保持稳定,SDA数据线的高或低电平状态只能在SCL低电平时才能改变

    • 起始条件 S:
      • SCL为高电平,SDA下降沿1→0时,主器件产生起始信号
      • 起始信号产生后,总线处于忙状态,其他I2C器件无法访问总线
    • 停止条件 P:
      • SCL为高电平,SDA上升沿0→1时,主器件产生停止信号

        p.s. 非应答信号规定:当主机为接收设备,主机对最后一个字节不应答,以向发送设备表示数据传送结束

      • 停止条件产生后,主从设备释放总线,总线再次处于空闲状态

    • 传输过程:
      • 主设备在传输有效数据之前先指定从设备的地址,设备地址为7位时,再加一个最低位表示数据传输的方向,0表示主设备→从设备写数据,1表示主设备读数据。

        7位地址码:其中DA3~DA0(硬件出厂时固有的地址编码)及A2~A0(用户设定)为从机地址

      • 主设备在SCL线上产生每个时钟脉冲的过程中将在SDA线上传输一个bit
      • 当一个字节按数据位从高到低顺序传输完之后,接收设备将SDA拉为低电平,表示数据传输正确,回传给主设备一个应答位。一个字节传输完毕。

        p.s. 并不是所有的字节传输都必须有一个应答位,比如:当从设备不能再接收主设备发送的数据时,从设备回传一个非应答位

        在一次传输中可以传输多个字节,图中只画出首字节

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

  • 当主机向从机发送1字节数据时

    p.s. 实际上主从机的SDA信号是在同一根线上的,分开画有助于理解各自的行为

  • 当主机从从机接收1字节数据时

————————————————————————————————————————————

AT24C02

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

主要型号:

ATMEL公司生产的AT24C系列EEPROM中具有I2C总线接口。

这类芯片可以解决掉电而造成的数据丢失的问题,可以保存数据100年,擦写100w次以上。

芯片地址固定部分为1010

型号

存储容量

AT24C01

128*8

AT24C02

256*8

AT24C04

512*8

AT24C08

1024*4

AT24C16

2048*8

芯片特性:略

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

引脚描述:

  • A0/A1/A23条地址线,用于确定芯片的硬件地址,在上图系统中均接地,选择000
  • 4脚和第8脚为正负电源
  • 5脚为SDA串行数据输入/输出,与单片机P3.5相连
  • 6脚为SCL串行时钟输入线,与单片机P3.6相连
  • SDASCL都需要与正电源间接一个5.1k欧上拉电阻
  • 7脚写保护功能接地
  • 24C02中带有片内地址寄存器,每写入或读出一个数据字节后,该地址寄存器自动+1,实现对下一个存储单元的读写。为降低总的写入时间,一次操作可以写入多达8个字节的数据。

————————————————————————————————————————————

AT24C02应用实例

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

设计要求:

  • 采用定时中断方式,设计一个0~59s变化的秒表,将每次显示在数码管上的时间存入AT24C02中

   

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

设计思路:

  • 通过定时器50ms触发中断,每次触发中断时中断计数,到达1s时flag标识为1
  • 在死循环中始终显示当前秒数
  • 每次秒数变化时写入flag清零并写入AT24C02中

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

硬件清单及连线情况:

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

实现代码:

  1 #include <reg51.h>
  2 typedef unsigned char uchar;
  3 typedef unsigned int uint;
  4 sbit scl = P3 ^ 0;
  5 sbit sda = P3 ^ 1;
  6 uchar second = 0;
  7 uchar count = 0;
  8 bit flag = 0;
  9 uchar code table[] =
 10 {
 11     0xFC, 0x60, 0xDA, 0xF2, 0x66, 0xB6, 0xBE, 0xE0, 0xFE, 0xF6, 0xEE, 0x3E, 0x9C, 0x7A, 0x9E, 0x8E
 12 };
 13 void Init(); //初始化总线
 14 void Start(); //开始
 15 void Stop(); //停止
 16 void Respons(); //响应位
 17 uchar ReadByte(); //读数据
 18 uchar ReadAddress(uchar address); //读地址
 19 void WriteByte(uchar dat); //写字节
 20 void WriteAddress(uchar address, uchar dat); //写地址
 21 void Display(); //显示数码管
 22 void Delay1ms(uchar m); //延时1ms
 23 void Delay(); //延时2个机器周期
 24 void main()
 25 {
 26     /* 初始化总线 */
 27     Init();
 28     /* 初始化LED */
 29     P1 = 0xff;
 30     /* 设置定时器 */
 31     TMOD = 0x01;
 32     TH0 = (65536 - 50000) / 256;
 33     TL0 = (65536 - 50000) % 256;
 34     /* 中断初始化 */
 35     EA = 1;
 36     ET0 = 1;
 37     /* 定时器开始 */
 38     TR0 = 1;
 39     /* 显示数码管并写入 */
 40     while(1)
 41     {
 42         if (flag == 1)
 43         {
 44             flag = 0;
 45             WriteAddress(2, second);
 46         }
 47         Display();
 48     }
 49 
 50 }
 51 void Init()
 52 {
 53     //空间状态时SCL与SDA均保持高电平
 54     sda = 1;
 55     Delay();
 56     scl = 1;
 57     Delay();
 58 }
 59 void Start()
 60 {
 61     //SCL高电平情况下,SDA下降沿启动
 62     sda = 1;
 63     Delay();
 64     scl = 1;
 65     Delay();
 66     sda = 0;
 67     Delay();
 68 }
 69 void Stop()
 70 {
 71     //SCL高电平情况下,SDA上升沿停止
 72     sda = 0;
 73     Delay();
 74     scl = 1;
 75     Delay();
 76     sda = 1;
 77     Delay();
 78 }
 79 void Respons()
 80 {
 81     //在SCL位为高电平时产生响应信号,数据传输正确时产生应答且SDA拉低
 82     uchar i;
 83     scl = 1; //此处scl拉高后在while循环中降低,参照时序图
 84     Delay();
 85     while ((sda == 1) && (i < 255))
 86     {
 87         //SDA拉低时跳出循环,表示数据传输正确,产生应答.如果在一段时间内未收到应答,则不再等待跳出循环
 88         i++;
 89         scl = 0;
 90         Delay();
 91     }
 92 }
 93 void WriteAddress(uchar address, uchar dat)
 94 {
 95     Start(); //开始
 96     WriteByte(0xA0); //写入 1010 0000 高4位为固定值,5-7位为A0 A1 A2,最低位0为写操作标识
 97     Respons();
 98     WriteByte(address); //写入 0000 0010
 99     Respons();
100     WriteByte(dat); //写入数据(秒数)
101     Respons();
102     Stop(); //停止
103 }
104 void WriteByte(uchar dat)
105 {
106     uchar i, temp;
107     temp = dat; //将dat读入temp
108     for (i = 0; i < 8; ++i)
109     {
110         temp = temp << 1; //temp左移,最高位移入CY
111         scl = 0; //SCL复位,等待最高位存入SDA后置位
112         Delay();
113         sda = CY; //CY在头文件中已规定,通过SDA发送bit
114         Delay();
115         scl = 1; //SCL置位,延迟2个机器周期后复位
116         Delay();
117     }
118     scl = 0; //为最后一个SCL高电平复位
119     Delay();
120     sda = 1; //一个字节数据发送完毕后置位准备响应
121     Delay();
122 }
123 uchar ReadAddress(uchar address)
124 {
125     uchar byte;
126     Start();
127     WriteByte(0xA0);
128     Respons();
129     WriteByte(address);
130     Respons();
131     Start();
132     WriteByte(0xA1); //写入地址 1010 0001,读写标志位1为读
133     Respons();
134     byte = ReadByte();
135     Stop();
136     return byte;
137 }
138 uchar ReadByte()
139 {
140     uchar i, k;
141     scl = 0;
142     Delay();
143     sda = 1;
144     Delay();
145     for (i = 0; i < 8; ++i)
146     {
147         scl = 1;
148         Delay();
149         k = (k << 1) | sda;
150         scl = 0;
151         Delay();
152     }
153     return k;
154 }
155 void Display()
156 {
157     P2 = 0xfe;
158     P1 = table[second % 10];
159     Delay1ms(1);
160     P2 = 0xfd;
161     // P1 = table[second / 10 %10];
162     P1 = table[second / 10];
163     Delay1ms(1);
164     // P2 = 0xfb;
165     // P1 = table[second / 100];
166     // Delay1ms(5);
167 }
168 void Delay()
169 {
170     ;;
171 }
172 void Delay1ms(uchar m) //使用1ms时二极管闪烁,此处将y=110改成y=50
173 {
174     uchar x, y;
175     for (x = m; x > 0; ++x)
176         for (y = 50; y > 0; --y)
177             ;
178 }
179 void time()interrupt 1
180 {
181     TH0 = (65536 - 50000) / 256;
182     TL0 = (65536 - 50000) % 256;
183     count++;
184     if (count == 20)
185     {
186         count = 0;
187         flag = 1;
188         second++;
189         if (second == 60)
190             second = 0;
191     }
192 }

posted @ 2017-06-12 16:23  hugh.dong  阅读(770)  评论(0编辑  收藏  举报