找回密碼
 立即注冊

QQ登錄

只需一步,快速開始

搜索
查看: 1390|回復: 1
打印 上一主題 下一主題
收起左側(cè)

這個單片機IIC通訊程序的疑問

[復制鏈接]
跳轉(zhuǎn)到指定樓層
樓主
ID:847776 發(fā)表于 2020-12-27 22:43 | 只看該作者 回帖獎勵 |倒序瀏覽 |閱讀模式
如下疑問 :
關(guān)于程序中這個讀字節(jié)的子函數(shù) uchar read_byte() ,讀取EEPROM內(nèi)數(shù)據(jù)時,用了 “ k=(k<<1)|sda; ”,然后循環(huán)8次,這里是如何得到一個字節(jié)數(shù)據(jù)的,沒弄明白。

我理解是k左移之后(不管k初值是什么),低位補0,而sda要么是0000 0000,要么是0000 0001,是固定的,所以進行按位或之后的k要么是0000 0000,要么是0000 0001,這個也是固定的,不管循環(huán)多少次,每次得到的k都是0000 0000,或者是0000 0001,怎么就讀取到內(nèi)部的0x55這個數(shù)據(jù)了呢? 而且實際運行用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();     //根據(jù)時序圖,scl和sda都為1的時候要保持4.7us以上。
     sda=0;         //在scl為高電平的狀態(tài)下,sda產(chǎn)生下降沿
     delayus();     //根據(jù)時序圖,數(shù)據(jù)拉低后保持4us以上。
     scl=0;         //根據(jù)時序圖,將scl拉低,后面開始字節(jié)傳輸。
     delayus();
}
void stop()         //停止信號
{   
     sda=0;
     scl=1;
     delayus();     //根據(jù)時序圖,scl和sda分別為1、0的時候要保持4us以上。
     sda=1;         //在scl為高電平的狀態(tài)下,sda產(chǎn)生上升沿
     delayus();     //根據(jù)時序圖,數(shù)據(jù)拉高后保持4.7us以上。
     sda=0;         //根據(jù)時序圖,將sda拉低。
     delayus();
}
void respons()      //應答信號
{
     uchar i;
     sda=1;         //不管之前sda是什么狀態(tài),先釋放sda
     delayus();
     scl=1;         //將scl拉高,用于sda響應。
     delayus();
     while((sda==1)&&(i<250)) i++;
     scl=0;
     delayus();     
}

void write_byte(uchar date)    //寫字節(jié),可以用來寫 器件地址 或 內(nèi)存地址 或 數(shù)據(jù)。
{
     uchar i,temp;
     temp=date;
     scl=0;
     for(i=0;i<8;i++)          //總共8位(器件地址是7位+1位讀寫位,內(nèi)存地址是8位),所以需要循環(huán)8次。
     {
       temp=temp<<1;           //將器件地址進行左移,左移之后會溢出,因為IIC是從MSB開始寫,所以要左移。
       sda=CY;                 //每當有數(shù)據(jù)溢出1,CY都會置1,溢出0,會置0,賦給sda,就得到了本次移出來的地址數(shù)據(jù)
       delayus();     
       scl=1;
       delayus();              //延時,給從機時間讀取sda信號。
       scl=0;                  //傳輸完一位后就將SCL拉低,下一次循環(huán)時sda才允許發(fā)生變化。
     }
       scl=0;
       delayus();
       sda=1;
       delayus();

}

uchar read_byte()              //字節(jié)讀取
{
     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函數(shù)中,沒有響應時超時后也會繼續(xù),那主機往哪里寫?
     write_byte(22);          //寫存儲地址,指定后面要往存儲器的第22個地址去寫數(shù)據(jù)。24c02總共256個字節(jié)地址,所以0~255都可以。
     respons();  
     write_byte(0x55);        //往存儲器的第22個地址中寫入數(shù)據(jù)0x55
     respons();
     stop();

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

/*下面一段參考24c02的隨機讀時序來寫,將上面剛剛寫進第22個地址的數(shù)據(jù)讀出來賦值給P1*/
     start();
     write_byte(0xa0);      //尋址,并且下一步為寫
     respons();             //從機響應
     write_byte(22);        //指定要訪問的地址為22
     respons();
     start();               //由寫變?yōu)樽x,所以要重新開始一下   
     write_byte(0xa1);      //尋址,并且下一步為讀
     respons();
     P1=read_byte();
     stop();
     while(1);              //主程序?qū)、讀完之后,將讀到的數(shù)據(jù)給P1,然后讓程序停在這里。

}





分享到:  QQ好友和群QQ好友和群 QQ空間QQ空間 騰訊微博騰訊微博 騰訊朋友騰訊朋友
收藏收藏 分享淘帖 頂 踩
回復

使用道具 舉報

沙發(fā)
ID:213173 發(fā)表于 2020-12-28 08:27 | 只看該作者
k的初值為 0000 0000 ,k=(k<<1)|sda; ,邏輯運算符“|”是按位或。移位后最低位補0,每“|”1次最低位被賦值0或1,0000 000x,循環(huán)8次后k=xxxx xxxx,就得到一個字節(jié)數(shù)據(jù)。

評分

參與人數(shù) 1黑幣 +20 收起 理由
admin + 20 回帖助人的獎勵!

查看全部評分

回復

使用道具 舉報

您需要登錄后才可以回帖 登錄 | 立即注冊

本版積分規(guī)則

手機版|小黑屋|51黑電子論壇 |51黑電子論壇6群 QQ 管理員QQ:125739409;技術(shù)交流QQ群281945664

Powered by 單片機教程網(wǎng)

快速回復 返回頂部 返回列表