微波EDA网,见证研发工程师的成长!
首页 > 研发问答 > 嵌入式设计讨论 > MCU和单片机设计讨论 > 单片机练习 - I2C总线协议-转载

单片机练习 - I2C总线协议-转载

时间:10-02 整理:3721RD 点击:

这次利用单片机通过软件模拟I2C总线协议, 并对基于I2C协议的AT24C02 EEPROM进行读写操作, 具体说明与功能见代码注释.
转载MK2大神博客
AT24C02与单片机的连接电路图如下:



单片机利用P2.0模拟SDA, P2.1模拟SCL.
程序代码:

  1. //用软件方法模拟I2C总线协议来读写AT24C02 EEPROM
  2. //每次上电后, 继续按照上次的计数结果计数, 每半秒计数一次
  3. #include <reg52.H>
  4. #include <intrins.H>

  5. #define uchar unsigned char
  6. sbit SDA = P2^0;
  7. sbit SCL = P2^1;

  8. //仿真延时5.43us
  9. void delay5us()
  10. {
  11.     _nop_();
  12. }

  13. //延时10ms, 仿真约11ms
  14. //实际测试中, 对于写一个字节, 只须2ms
  15. void delay10Ms()
  16. {
  17.     uchar a,b;
  18.     for(a=50;a>0;a--)
  19.      for(b=100;b>0;b--);
  20. }

  21. //延时2ms
  22. void delay2Ms()
  23. {
  24.     uchar a,b;
  25.     for(a=10;a>0;a--)
  26.      for(b=100;b>0;b--);
  27. }

  28. /***************************** I2C总线协议 ************************************/
  29. //起始信号
  30. void start()
  31. {
  32.     SDA = 1;  //启动I2C总线
  33.     SCL = 1;
  34.     delay5us();    //延时
  35.     SDA = 0;    //起始信号
  36.     delay5us();    //SDA拉低时间至少4us后, 才能拉低SCL
  37.     SCL = 0;   
  38. }

  39. //终止信号
  40. void stop()
  41. {
  42.     SDA = 0;
  43.     SCL = 1;    //SCL拉高至少4us后, 才能拉高SDA, 产生终止信号
  44.     delay5us();
  45.     SDA = 1;
  46.     delay5us();    //保持SDA拉高4.7us以上
  47.     //终止后, 总线处于空闲状态
  48. }

  49. //发送应答
  50. void ack()
  51. {
  52.     SDA = 0;
  53.     SCL = 1;
  54.     delay5us();
  55.     SCL = 0;
  56.     SDA = 1;
  57. }

  58. //发送非应答
  59. void nack()
  60. {
  61.     SDA = 1;
  62.     SCL = 1;
  63.     delay5us();
  64.     SCL = 0;
  65. }

  66. //获取应答, 有应答返回0, 非应答返回1
  67. bit getAck()
  68. {
  69.     bit flag;
  70.     SDA = 1;
  71.     SCL = 1;
  72.     flag = SDA;
  73.     SCL = 0;
  74.     return flag;
  75. }

  76. //发送一字节数据, 有应答返回0, 非应答返回1
  77. bit sendByte(uchar dat)
  78. {
  79.     uchar i;
  80.     for(i = 0; i < 8; i++)
  81.     {
  82.         dat = dat << 1; //左移, 最高位将移到CY中
  83.         SDA = CY;
  84.         SCL = 1;
  85.        delay5us();
  86.         SCL = 0;
  87.     }
  88.     return getAck();
  89. }

  90. //接收一字节数据
  91. uchar recvByte()
  92. {
  93.     uchar i, tmp, dat;
  94.     for(i = 0; i < 8; i++)
  95.     {
  96.         SCL = 1;
  97.         delay5us();
  98.         if(SDA == 1)
  99.        {
  100.             tmp = 1;
  101.         }
  102.         else
  103.            tmp = 0;
  104.         dat = (dat << 1) | tmp;
  105.        SCL = 0;
  106.         delay5us();
  107.    }
  108.    return dat;
  109. }

  110. /***************************** I2C总线协议 ************************************/


  111. /***************************** AT24C02 EEPROM的读写操作 ************************************/
  112. //AT24C02 EEPROM 在TX1-B实验板上的地址是 1010 000B

  113. //向EEPROM指定地址写一字节数据
  114. void writeByte(uchar dat, uchar add)
  115. {
  116.     start();
  117.     sendByte(0xa0);    //找出EEPROM芯片, 写数据
  118.     sendByte(add); //先写地址
  119.     sendByte(dat); //读数据
  120.     stop(); //释放总线
  121.     delay2Ms(); //发送完写数据, stop()后, 需要延时10ms让芯片完成内部写周期
  122.     //实际测试中, 写一字节, 只须2ms
  123. }

  124. //连续写指定长度的字节流(Page Write)
  125. void writeBytes(uchar * dats, uchar length, uchar add)
  126. {
  127.     uchar i;
  128.     start();
  129.     sendByte(0xa0);
  130.     sendByte(add);
  131.     for(i = 0; i < length; i++)
  132.     {
  133.         sendByte(dats[i]);
  134.     }
  135.     stop();
  136.    delay10Ms();
  137. }

  138. //读取EEPROM指定地址的一字节数据(Random Read)
  139. uchar readByte(uchar add)
  140. {
  141.     uchar dat;
  142.     start();
  143.     sendByte(0xa0);    //找出EEPROM芯片, 写数据
  144.     sendByte(add); //先写地址
  145.     start();
  146.     sendByte(0xa1); //读数据
  147.    dat = recvByte();
  148.     nack(); //非响应
  149.     stop(); //释放总线
  150.     return dat;
  151. }

  152. //连续读指定大小的字节数(Sequential Read)
  153. void readBytes(uchar * buffer, uchar size, uchar add)
  154. {
  155.     uchar i, count = size - 1;
  156.     start();
  157.     sendByte(0xa0);
  158.     sendByte(add);
  159.     start();
  160.     sendByte(0xa1);
  161.     for(i = 0; i < count; i++)
  162.     {
  163.         buffer[i] = recvByte();
  164.         ack();  //应答
  165.     }
  166.     buffer[count] = recvByte();
  167.     nack();    //非应答, 结束
  168.     stop();
  169. }

  170. /***************************** AT24C02 EEPROM的读写操作 ************************************/
  171. sbit wela = P2^7;  //数码管位选
  172. sbit dula = P2^6;  //数码管段选

  173. //0-F数码管的编码(共阴极)
  174. unsigned char code table[]={0x3f,0x06,0x5b,0x4f,0x66,
  175.     0x6d,0x7d,0x07,0x7f,0x6f,0x77,0x7c,0x39,0x5e,0x79,0x71};
  176. void display(uchar v)
  177. {
  178.     unsigned char count;
  179.     unsigned char datas[] = {0, 0, 0};
  180.     datas[0] = v / 100;
  181.     datas[1] = v % 100 / 10;
  182.     datas[2] = v % 10;
  183.     for(count = 0; count != 3; count++)
  184.     {
  185.         //关位选, 去除对上一位的影响
  186.         P0 = 0xff;
  187.        wela = 1; //打开锁存, 给它一个下降沿量
  188.         wela = 0;
  189.         //段选
  190.         P0 = table[datas[count]];
  191.         dula = 1;  //打开锁存, 给它一个下降沿量
  192.         dula = 0;
  193.         //位选
  194.         P0 = _crol_(0xfe, count); //选择第(count + 1) 个数码管
  195.        wela = 1; //打开锁存, 给它一个下降沿量
  196.        wela = 0;
  197.         delay2Ms();
  198.     }
  199. }

  200. uchar value, th, tl, tCount;
  201. void main()
  202. {
  203.     value = readByte(0);  //向EEPROM地址0读一字节数据
  204.     tCount = 0;
  205.     EA = 1;     //开中断
  206.     ET1 = 1;    //允许T1中断
  207.     TMOD = 0x10; //工作方式1
  208.     TH1 = th = 0x4b;//(65536 - 50000/1.085) / 256; //定时50ms
  209.     TL1 = tl = 0xfd;//(65536 - 50000/1.085) - th * 256;
  210.    TR1 = 1;     //T1开始计时
  211.     while(1)
  212.    {
  213.         if(tCount > 10)  //满500ms, 加1
  214.         {
  215.             value++;
  216.             writeByte(value, 0); //将当前值保存在EEPROM地址0中
  217.             tCount = 0;
  218.         }
  219.         display(value);
  220.     }
  221. }

  222. void time1() interrupt 3
  223. {
  224.     TH1 = th;
  225.     TL1 = tl;
  226.     tCount++;
  227. }

复制代码



利用单片机通过软件模拟I2C总线协议, 并对基于I2C协议的AT24C02 EEPROM进行读写操作

谢谢

感谢分享

真心感谢分享

学习学习啊

貌似是郭天祥的原理图……

今天刚好看到这,学习了

正好遇到这个问题

有更贱答的方法看计算机测控

真心感谢分享

小编用的是什么单片机啊?

确实挺好的,

Copyright © 2017-2020 微波EDA网 版权所有

网站地图

Top