两个吃奶一个添下面视频_人妻第一页香蕉网_欧美xxxx少妇_妺妺窝人体色www婷婷

我們這一講簡要講解如何往EEPROM的地址0x55寫入一個(gè)數(shù)據(jù),然后讀出這個(gè)數(shù)據(jù)的內(nèi)容。本講代碼圍繞的是宋老師的lesson14_2例程代碼講解。


1.寫入一個(gè)字節(jié)內(nèi)容

如果要在EEPROM的某個(gè)地址里寫入內(nèi)容,那么步驟的實(shí)現(xiàn)歸為:

起始信號(hào)→找到這個(gè)器件是否存在(尋址),發(fā)送的字節(jié)最低位要為0意為要往這個(gè)器件寫內(nèi)容→選擇EEPROM的哪個(gè)地址去寫→寫入8位的數(shù)據(jù)→停止信號(hào)。

宋老師寫的“void E2WriteByte(unsigned char addr, unsigned char dat)”函數(shù)里面,上一講都講解過里面的函數(shù)了,寫入一個(gè)字節(jié)內(nèi)容的講解我們就介紹完了。


2.讀出一個(gè)字節(jié)內(nèi)容

在“unsigned char E2ReadByte(unsigned char addr)”中前面三個(gè)函數(shù)與“void E2WriteByte(unsigned char addr, unsigned char dat)”都是一樣的操作步驟,選定好要讀出哪個(gè)地址的內(nèi)容,然后還需再重新發(fā)送起始信號(hào),接著是把尋址的字節(jié)最低位設(shè)置為1意為要讀出EEPROM的某個(gè)地址里面的內(nèi)容,因?yàn)橹蛔x一個(gè)字節(jié),所以單片機(jī)在接收完EEPROM發(fā)送回來的數(shù)據(jù)(這個(gè)數(shù)據(jù)就是當(dāng)初寫進(jìn)去的數(shù)據(jù))之后,不產(chǎn)生拉低應(yīng)答(是單片機(jī)不產(chǎn)生應(yīng)答,不是說EEPROM不產(chǎn)生應(yīng)答),這樣EEPROM就不會(huì)再發(fā)送數(shù)據(jù)回來了,達(dá)到了只讀一個(gè)字節(jié)的功能。

所謂單片機(jī)讀EEPROM的數(shù)據(jù)出來,其實(shí)就是EEPROM在SDA線上不停地拉高拉低變化,而單片機(jī)就是不斷地判斷第一位是0或者1,第二位是0或者1······,這個(gè)細(xì)節(jié)過程大家有能力的話可以一步步去解讀宋老師寫的“unsigned char I2CReadNAK()”和“unsigned char I2CReadACK()”,其實(shí)這兩個(gè)函數(shù)只有一處不同,那就是“I2C_SDA = 1;”和“I2C_SDA = 0;”是否產(chǎn)生應(yīng)答。如果大家一句句地很難理解這個(gè)函數(shù),那么不妨直接省去理解,拿來運(yùn)用就可以了。


3.獨(dú)立代碼

在main.c里復(fù)制以下代碼,編譯下載進(jìn)開發(fā)板,我們做的實(shí)驗(yàn)就是先在EEPROM的0x55的地址里寫入數(shù)據(jù)71(也就是字符‘G’的ASCII碼值),然后再去讀出這個(gè)數(shù)據(jù),將字符‘G’顯示在液晶屏上。

之前的章節(jié)有提過為了兼容性我們不打算使用bit型的數(shù)據(jù)類型,所以修改了一下宋老師的“bit I2CWrite(unsigned char dat)”函數(shù),改為unsigned char類型。

那么我們就不能像宋老師那樣寫為“return (~ack);”,而是寫為“return (!ack);”。unsigned char類型下,ack如果是0,那么~ack就為0xFF;ack如果是1,~ack就為0xFE,所以“~ack”的書寫方式會(huì)導(dǎo)致返回值不能限定在0或者1之間,也就會(huì)使代碼功能失效。

而“!ack”的表達(dá)就是說,當(dāng)ack等于0時(shí),!ack只會(huì)等于1,不會(huì)等于0xFF這些。當(dāng)ack不等于0時(shí)(比如“ack=0xFE;”這些),那么!ack只會(huì)等于0,這樣保證了返回值只有0和1。

#include <reg52.h> 
#include <function.h>//詳見第六章第8講
#include <lcd.h>     //詳見第十一章第3講
#include <intrins.h>
 
#define I2CDelay()  {_nop_();_nop_();_nop_();_nop_();}
sbit I2C_SCL = P3^7;
sbit I2C_SDA = P3^6;
 
/* 產(chǎn)生總線起始信號(hào) */
void I2CStart()
{
    I2C_SDA = 1; //首先確保SDA、SCL都是高電平
    I2C_SCL = 1;
    I2CDelay();
    I2C_SDA = 0; //先拉低SDA
    I2CDelay();
    I2C_SCL = 0; //再拉低SCL
}

/* 產(chǎn)生總線停止信號(hào) */
void I2CStop()
{
    I2C_SCL = 0; //首先確保SDA、SCL都是低電平
    I2C_SDA = 0;
    I2CDelay();
    I2C_SCL = 1; //先拉高SCL
    I2CDelay();
    I2C_SDA = 1; //再拉高SDA
    I2CDelay();
}

/* I2C總線寫操作,dat-待寫入字節(jié),返回值-從機(jī)應(yīng)答位的值 */
u8 I2CWrite(unsigned char dat)
{
    u8 ack;                            //用于暫存應(yīng)答位的值
    unsigned char mask;                //用于探測字節(jié)內(nèi)某一位值的掩碼變量
  
    for (mask=0x80; mask!=0; mask>>=1) //從高位到低位依次進(jìn)行
    {
        if ((mask&dat) == 0)  //該位的值輸出到SDA上
            I2C_SDA = 0;
        else
            I2C_SDA = 1;
        I2CDelay();
        I2C_SCL = 1;          //拉高SCL
        I2CDelay();
        I2C_SCL = 0;          //再拉低SCL,完成一個(gè)位周期
    }
    I2C_SDA = 1;   //8位數(shù)據(jù)發(fā)送完后,主機(jī)釋放SDA,以檢測從機(jī)應(yīng)答
    I2CDelay();
    I2C_SCL = 1;   //拉高SCL
    ack = I2C_SDA; //讀取此時(shí)的SDA值,即為從機(jī)的應(yīng)答值
    I2CDelay();
    I2C_SCL = 0;   //再拉低SCL完成應(yīng)答位,并保持住總線
  
    return (!ack); //應(yīng)答值取反以符合通常的邏輯:
                   //0=不存在或忙或?qū)懭胧。?=存在且空閑或?qū)懭氤晒?
}

/* I2C總線讀操作,并發(fā)送非應(yīng)答信號(hào),返回值-讀到的字節(jié) */
unsigned char I2CReadNAK()
{
    unsigned char mask;
    unsigned char dat;
  
    I2C_SDA = 1;                       //首先確保主機(jī)釋放SDA
    for (mask=0x80; mask!=0; mask>>=1) //從高位到低位依次進(jìn)行
    {
        I2CDelay();
        I2C_SCL = 1;      //拉高SCL
        if(I2C_SDA == 0)  //讀取SDA的值
            dat &= ~mask; //為0時(shí),dat中對(duì)應(yīng)位清零
        else
            dat |= mask;  //為1時(shí),dat中對(duì)應(yīng)位置1
        I2CDelay();
        I2C_SCL = 0;      //再拉低SCL,以使從機(jī)發(fā)送出下一位
    }
    I2C_SDA = 1;   //8位數(shù)據(jù)發(fā)送完后,拉高SDA,發(fā)送非應(yīng)答信號(hào)
    I2CDelay();
    I2C_SCL = 1;   //拉高SCL
    I2CDelay();
    I2C_SCL = 0;   //再拉低SCL完成非應(yīng)答位,并保持住總線
  
    return dat;
}
 
void main()
{  
    u8 str[2];
    InitLcd1602();//初始化液晶屏 
    while (1)
    {     
        I2CStart();
        I2CWrite(0x50<<1); //尋址器件,后續(xù)為寫操作
        I2CWrite(0x55);    //寫入要存儲(chǔ)的地址為0x55
        I2CWrite('G');     //寫入71這個(gè)數(shù)據(jù),'G'的表達(dá)更加直觀地表示要在液晶屏上顯示的內(nèi)容
        I2CStop();
    
        delay_ms(1000);    //等待一秒之后,準(zhǔn)備讀出這個(gè)數(shù)據(jù)
    
        I2CStart();
        I2CWrite(0x50<<1); //尋址器件,后續(xù)為寫操作
        I2CWrite(0x55);    //選擇要讀出內(nèi)容的地址為0x55
        I2CStart();        //發(fā)送重復(fù)啟動(dòng)信號(hào)
        I2CWrite((0x50<<1)|0x01); //尋址器件,后續(xù)為讀操作
        str[0] = I2CReadNAK();    //讀取這個(gè)器件0x55的地址里面的內(nèi)容存放在數(shù)組的0號(hào)元素里
        I2CStop();
    
        LcdShowStr_len(7, 0, str, 1);//顯示str[0]的內(nèi)容
        while (1);//程序暫停運(yùn)行
    }
}

為了更加直觀了解這些過程,筆者并沒有像宋老師那樣給大家封裝好寫函數(shù)和讀函數(shù),這是為了大家不用在各個(gè)函數(shù)中跳來跳去地分析,在主函數(shù)里直接一步到位地講解這些操作步驟,方便大家對(duì)這些細(xì)節(jié)的了解。

點(diǎn)贊(0)

C語言網(wǎng)提供由在職研發(fā)工程師或ACM藍(lán)橋杯競賽優(yōu)秀選手錄制的視頻教程,并配有習(xí)題和答疑,點(diǎn)擊了解:

一點(diǎn)編程也不會(huì)寫的:零基礎(chǔ)C語言學(xué)練課程

解決困擾你多年的C語言疑難雜癥特性的C語言進(jìn)階課程

從零到寫出一個(gè)爬蟲的Python編程課程

只會(huì)語法寫不出代碼?手把手帶你寫100個(gè)編程真題的編程百練課程

信息學(xué)奧賽或C++選手的 必學(xué)C++課程

藍(lán)橋杯ACM、信息學(xué)奧賽的必學(xué)課程:算法競賽課入門課程

手把手講解近五年真題的藍(lán)橋杯輔導(dǎo)課程

Dotcpp在線編譯      (登錄可減少運(yùn)行等待時(shí)間)