找回密码
 立即注册

QQ登录

只需一步,快速开始

帖子
查看: 1460|回复: 1
打印 上一主题 下一主题
收起左侧

这个单片机IIC通讯程序的疑问

[复制链接]
跳转到指定楼层
楼主
ID:847776 发表于 2020-12-27 22:43 | 只看该作者 回帖奖励 |正序浏览 |阅读模式
如下疑问 :
关于程序中这个读字节的子函数 uchar read_byte() ,读取EEPROM内数据时,用了 “ k=(k<<1)|sda; ”,然后循环8次,这里是如何得到一个字节数据的,没弄明白。

我理解是k左移之后(不管k初值是什么),低位补0,而sda要么是0000 0000,要么是0000 0001,是固定的,所以进行按位或之后的k要么是0000 0000,要么是0000 0001,这个也是固定的,不管循环多少次,每次得到的k都是0000 0000,或者是0000 0001,怎么就读取到内部的0x55这个数据了呢? 而且实际运行用P1点亮LED也是正确的。
感谢指点!

以下是单片机代码(注释是自己写的,有错误的地方还请不吝指正):

#include <reg52.h>
#include <intrins.h>
sbit scl=P2^1;
sbit sda=P2^0;
typedef unsigned char uchar;
typedef unsigned int uint;
void delayms(uchar z)
{
     int x,y;
     for(x=z;x>0;x--)
       for(y=110;y>0;y--);
}
void delayus()
{
     _nop_();_nop_();_nop_();_nop_();_nop_();
}
void start()        //开始信号
{
     sda=1;
     scl=1;
     delayus();     //根据时序图,scl和sda都为1的时候要保持4.7us以上。
     sda=0;         //在scl为高电平的状态下,sda产生下降沿
     delayus();     //根据时序图,数据拉低后保持4us以上。
     scl=0;         //根据时序图,将scl拉低,后面开始字节传输。
     delayus();
}
void stop()         //停止信号
{   
     sda=0;
     scl=1;
     delayus();     //根据时序图,scl和sda分别为1、0的时候要保持4us以上。
     sda=1;         //在scl为高电平的状态下,sda产生上升沿
     delayus();     //根据时序图,数据拉高后保持4.7us以上。
     sda=0;         //根据时序图,将sda拉低。
     delayus();
}
void respons()      //应答信号
{
     uchar i;
     sda=1;         //不管之前sda是什么状态,先释放sda
     delayus();
     scl=1;         //将scl拉高,用于sda响应。
     delayus();
     while((sda==1)&&(i<250)) i++;
     scl=0;
     delayus();     
}

void write_byte(uchar date)    //写字节,可以用来写 器件地址 或 内存地址 或 数据。
{
     uchar i,temp;
     temp=date;
     scl=0;
     for(i=0;i<8;i++)          //总共8位(器件地址是7位+1位读写位,内存地址是8位),所以需要循环8次。
     {
       temp=temp<<1;           //将器件地址进行左移,左移之后会溢出,因为IIC是从MSB开始写,所以要左移。
       sda=CY;                 //每当有数据溢出1,CY都会置1,溢出0,会置0,赋给sda,就得到了本次移出来的地址数据
       delayus();     
       scl=1;
       delayus();              //延时,给从机时间读取sda信号。
       scl=0;                  //传输完一位后就将SCL拉低,下一次循环时sda才允许发生变化。
     }
       scl=0;
       delayus();
       sda=1;
       delayus();

}

uchar read_byte()              //字节读取
{
     uchar j,k;
     scl=0;   
     delayus();
     sda=1;
     delayus();
     for(j=0;j<8;j++)
     {
          scl=1;
          delayus();
          k=(k<<1)|sda;        //k的初值不管是什么,左移之后LSB都是0
          scl=0;
          delayus();
     }
     return k;                 
}
void init()
{
     sda=1;
     scl=1;
     delayus();
}

void main()                    // 主程序思路为先往24C02中写0x55,然后再读出来并且赋值给P1
{
     init();
     start();
     write_byte(0xa0);        //寻址,并且下一步为写。
     respons();               //从机响应,疑问:respons函数中,没有响应时超时后也会继续,那主机往哪里写?
     write_byte(22);          //写存储地址,指定后面要往存储器的第22个地址去写数据。24c02总共256个字节地址,所以0~255都可以。
     respons();  
     write_byte(0x55);        //往存储器的第22个地址中写入数据0x55
     respons();
     stop();

     delayms(100);           //进入24C02的写周期,需>10ms

/*下面一段参考24c02的随机读时序来写,将上面刚刚写进第22个地址的数据读出来赋值给P1*/
     start();
     write_byte(0xa0);      //寻址,并且下一步为写
     respons();             //从机响应
     write_byte(22);        //指定要访问的地址为22
     respons();
     start();               //由写变为读,所以要重新开始一下   
     write_byte(0xa1);      //寻址,并且下一步为读
     respons();
     P1=read_byte();
     stop();
     while(1);              //主程序写、读完之后,将读到的数据给P1,然后让程序停在这里。

}





分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
收藏收藏 分享淘帖 顶 踩
回复

举报

沙发
ID:213173 发表于 2020-12-28 08:27 | 只看该作者
k的初值为 0000 0000 ,k=(k<<1)|sda; ,逻辑运算符“|”是按位或。移位后最低位补0,每“|”1次最低位被赋值0或1,0000 000x,循环8次后k=xxxx xxxx,就得到一个字节数据。

评分

参与人数 1黑币 +20 收起 理由
admin + 20 回帖助人的奖励!

查看全部评分

回复

举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|51黑电子论坛 |51黑电子论坛6群 QQ 管理员QQ:125739409;技术交流QQ群281945664

Powered by 单片机教程网

快速回复 返回顶部 返回列表