在线观看不卡亚洲电影_亚洲妓女99综合网_91青青青亚洲娱乐在线观看_日韩无码高清综合久久

鍍金池/ 教程/ 數(shù)據(jù)庫(kù)/ 14.4 單片機(jī) EEPROM 單字節(jié)讀寫操作時(shí)序
18. RS485 通信與 Modbus 協(xié)議
17.5 A/D 差分輸入信號(hào)
15.8 C 語(yǔ)言復(fù)合數(shù)據(jù)類型(結(jié)構(gòu)體,共用體,枚舉類型)
16.3 NEC 協(xié)議紅外遙控器
13.1 單片機(jī)通信時(shí)序解析
14.4 單片機(jī) EEPROM 單字節(jié)讀寫操作時(shí)序
13.3 多個(gè) .c 文件的初步認(rèn)識(shí)
18.2 Modbus 通信協(xié)議介紹
15.1 BCD 碼介紹
18.3 單片機(jī) Modbus 多機(jī)通信程序設(shè)計(jì)
18.1 單片機(jī) RS485 通信接口、控制線、原理圖及程序?qū)嵗?/span>
15. 實(shí)時(shí)時(shí)鐘 DS1302
14.7 單片機(jī) I2C 和 EEPROM 的綜合編程
17. 模數(shù)轉(zhuǎn)換與數(shù)模轉(zhuǎn)換
16.2 紅外遙控通信原理
13.2 1602 液晶整屏移動(dòng)程序
17.6 D/A 輸出
17.7 單片機(jī)信號(hào)發(fā)生器程序
16.4 溫度傳感器 DS18B20
14.6 單片機(jī)EEPROM的頁(yè)寫入
13.4 單片機(jī)計(jì)算器程序設(shè)計(jì)[詳細(xì)]
17.2 A/D(模數(shù)轉(zhuǎn)換)的主要指標(biāo)
17.4 PCF8591 應(yīng)用程序
17.1 A/D 和 D/A 的基本概念
17.3 PCF8591硬件接口(電路圖引腳圖)
14.3 單片機(jī) EEPROM 簡(jiǎn)介
13.5 單片機(jī)串口通信原理和控制程序
15.5 DS1302 寄存器介紹
15.2 單片機(jī) SPI 通信接口
15.6 DS1302 通信時(shí)序介紹
14.5 單片機(jī) EEPROM 多字節(jié)讀寫操作時(shí)序
16. 紅外通信與 DS18B20 溫度傳感器
14.1 單片機(jī) I2C 時(shí)序介紹
15.3 實(shí)時(shí)時(shí)鐘芯片 DS1302 介紹
15.9 單片機(jī)電子時(shí)鐘程序設(shè)計(jì)
16.1 紅外光的基本原理
15.4 DS1302 的硬件信息
15.7 DS1302 的 BURST 模式
14.2 單片機(jī) I2C 尋址模式
14. 單片機(jī) I2C 總線與 EEPROM
13. 單片機(jī) 1602 液晶與串口的應(yīng)用實(shí)例

14.4 單片機(jī) EEPROM 單字節(jié)讀寫操作時(shí)序

EEPROM 寫數(shù)據(jù)流程

第一步,首先是 I2C 的起始信號(hào),接著跟上首字節(jié),也就是我們前邊講的 I2C 的器件地址,并且在讀寫方向上選擇“寫”操作。

第二步,發(fā)送數(shù)據(jù)的存儲(chǔ)地址。24C02 一共256個(gè)字節(jié)的存儲(chǔ)空間,地址從 0x00~0xFF,我們想把數(shù)據(jù)存儲(chǔ)在哪個(gè)位置,此刻寫的就是哪個(gè)地址。

第三步,發(fā)送要存儲(chǔ)的數(shù)據(jù)第一個(gè)字節(jié)、第二個(gè)字節(jié)??注意在寫數(shù)據(jù)的過程中,EEPROM 每個(gè)字節(jié)都會(huì)回應(yīng)一個(gè)“應(yīng)答位0”,來告訴我們寫 EEPROM 數(shù)據(jù)成功,如果沒有回應(yīng)答位,說明寫入不成功。

在寫數(shù)據(jù)的過程中,每成功寫入一個(gè)字節(jié),EEPROM 存儲(chǔ)空間的地址就會(huì)自動(dòng)加1,當(dāng)加到 0xFF 后,再寫一個(gè)字節(jié),地址會(huì)溢出又變成了 0x00。

EEPROM 讀數(shù)據(jù)流程

第一步,首先是 I2C 的起始信號(hào),接著跟上首字節(jié),也就是我們前邊講的 I2C 的器件地址,并且在讀寫方向上選擇“寫”操作。這個(gè)地方可能有同學(xué)會(huì)詫異,我們明明是讀數(shù)據(jù)為何方向也要選“寫”呢?剛才說過了,24C02 一共有256個(gè)地址,我們選擇寫操作,是為了把所要讀的數(shù)據(jù)的存儲(chǔ)地址先寫進(jìn)去,告訴 EEPROM 我們要讀取哪個(gè)地址的數(shù)據(jù)。這就如同我們打電話,先撥總機(jī)號(hào)碼(EEPROM 器件地址),而后還要繼續(xù)撥分機(jī)號(hào)碼(數(shù)據(jù)地址),而撥分機(jī)號(hào)碼這個(gè)動(dòng)作,主機(jī)仍然是發(fā)送方,方向依然是“寫”。

第二步,發(fā)送要讀取的數(shù)據(jù)的地址,注意是地址而非存在 EEPROM 中的數(shù)據(jù),通知EEPROM 我要哪個(gè)分機(jī)的信息。

第三步,重新發(fā)送 I2C 起始信號(hào)和器件地址,并且在方向位選擇“讀”操作。

這三步當(dāng)中,每一個(gè)字節(jié)實(shí)際上都是在“寫”,所以每一個(gè)字節(jié) EEPROM 都會(huì)回應(yīng)一個(gè)“應(yīng)答位0”。

第四步,讀取從器件發(fā)回的數(shù)據(jù),讀一個(gè)字節(jié),如果還想繼續(xù)讀下一個(gè)字節(jié),就發(fā)送一個(gè)“應(yīng)答位 ACK(0)”,如果不想讀了,告訴 EEPROM,我不想要數(shù)據(jù)了,別再發(fā)數(shù)據(jù)了,那就發(fā)送一個(gè)“非應(yīng)答位 NAK(1)”。

和寫操作規(guī)則一樣,我們每讀一個(gè)字節(jié),地址會(huì)自動(dòng)加1,那如果我們想繼續(xù)往下讀,給 EEPROM 一個(gè) ACK(0)低電平,那再繼續(xù)給 SCL 完整的時(shí)序,EEPROM 會(huì)繼續(xù)往外送數(shù)據(jù)。如果我們不想讀了,要告訴 EEPROM 不要數(shù)據(jù)了,那我們直接給一個(gè) NAK(1)高電平即可。這個(gè)地方大家要從邏輯上理解透徹,不能簡(jiǎn)單的靠死記硬背了,一定要理解明白。梳理一下幾個(gè)要點(diǎn): A、在本例中單片機(jī)是主機(jī),24C02 是從機(jī); B、無論是讀是寫,SCL 始終都是由主機(jī)控制的; C、寫的時(shí)候應(yīng)答信號(hào)由從機(jī)給出,表示從機(jī)是否正確接收了數(shù)據(jù); D、讀的時(shí)候應(yīng)答信號(hào)則由主機(jī)給出,表示是否繼續(xù)讀下去。

那我們下面寫一個(gè)程序,讀取 EEPROM 的 0x02 這個(gè)地址上的一個(gè)數(shù)據(jù),不管這個(gè)數(shù)據(jù)之前是多少,我們都將讀出來的數(shù)據(jù)加1,再寫到 EEPROM 的 0x02 這個(gè)地址上。此外我們將 I2C 的程序建立一個(gè)文件,寫一個(gè) I2C.c 程序文件,形成我們又一個(gè)程序模塊。大家也可以看出來,我們連續(xù)的這幾個(gè)程序,Lcd1602.c 文件里的程序都是一樣的,今后我們大家寫 1602 顯示程序也可以直接拿過去用,大大提高了程序移植的方便性。

/******************************I2C.c 文件程序源代碼******************************/
#include <reg52.h>
#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)答位的值 */
bit I2CWrite(unsigned char dat){
    bit ack; //用于暫存應(yīng)答位的值
    unsigned char mask; //用于探測(cè)字節(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,以檢測(cè)從機(jī)應(yīng)答
    I2CDelay();
    I2C_SCL = 1; //拉高 SCL
    ack = I2C_SDA; //讀取此時(shí)的 SDA 值,即為從機(jī)的應(yīng)答值
    I2CDelay();
    I2C_SCL = 0; //再拉低 SCL 完成應(yīng)答位,并保持住總線
    //應(yīng)答值取反以符合通常的邏輯:
    //0=不存在或忙或?qū)懭胧。?=存在且空閑或?qū)懭氤晒?    return (~ack);
}
/* 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;
}

/* I2C 總線讀操作,并發(fā)送應(yīng)答信號(hào),返回值-讀到的字節(jié) */
unsigned char I2CReadACK(){
    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 = 0; //8 位數(shù)據(jù)發(fā)送完后,拉低 SDA,發(fā)送應(yīng)答信號(hào)
    I2CDelay();
    I2C_SCL = 1; //拉高 SCL
    I2CDelay();
    I2C_SCL = 0; //再拉低 SCL 完成應(yīng)答位,并保持住總線
    return dat;
}

I2C.c 文件提供了 I2C 總線所有的底層操作函數(shù),包括起始、停止、字節(jié)寫、字節(jié)讀+應(yīng)答、字節(jié)讀+非應(yīng)答。

/***************************Lcd1602.c 文件程序源代碼*****************************/
#include <reg52.h>

#define LCD1602_DB P0
sbit LCD1602_RS = P1^0;
sbit LCD1602_RW = P1^1;
sbit LCD1602_E = P1^5;

/* 等待液晶準(zhǔn)備好 */
void LcdWaitReady(){
    unsigned char sta;
    LCD1602_DB = 0xFF;
    LCD1602_RS = 0;
    LCD1602_RW = 1;
    do {
        LCD1602_E = 1;
        sta = LCD1602_DB; //讀取狀態(tài)字
        LCD1602_E = 0;
    }while (sta & 0x80); //bit7 等于 1 表示液晶正忙,重復(fù)檢測(cè)直到其等于 0 為止
}
/* 向 LCD1602 液晶寫入一字節(jié)命令,cmd-待寫入命令值 */
void LcdWriteCmd(unsigned char cmd){
    LcdWaitReady();
    LCD1602_RS = 0;
    LCD1602_RW = 0;
    LCD1602_DB = cmd;
    LCD1602_E = 1;
    LCD1602_E = 0;
}
/* 向 LCD1602 液晶寫入一字節(jié)數(shù)據(jù),dat-待寫入數(shù)據(jù)值 */
void LcdWriteDat(unsigned char dat){
    LcdWaitReady();
    LCD1602_RS = 1;
    LCD1602_RW = 0;
    LCD1602_DB = dat;
    LCD1602_E = 1;
    LCD1602_E = 0;
}
/* 設(shè)置顯示 RAM 起始地址,亦即光標(biāo)位置,(x,y)-對(duì)應(yīng)屏幕上的字符坐標(biāo) */
void LcdSetCursor(unsigned char x, unsigned char y){
    unsigned char addr;

    if (y == 0){ //由輸入的屏幕坐標(biāo)計(jì)算顯示 RAM 的地址
        addr = 0x00 + x; //第一行字符地址從 0x00 起始
    }else{
        addr = 0x40 + x; //第二行字符地址從 0x40 起始
    }
    LcdWriteCmd(addr | 0x80); //設(shè)置 RAM 地址
}
/* 在液晶上顯示字符串,(x,y)-對(duì)應(yīng)屏幕上的起始坐標(biāo),str-字符串指針 */
void LcdShowStr(unsigned char x, unsigned char y, unsigned char *str){
    LcdSetCursor(x, y); //設(shè)置起始地址
    while (*str != '\0'){ //連續(xù)寫入字符串?dāng)?shù)據(jù),直到檢測(cè)到結(jié)束符
        LcdWriteDat(*str++);
    }
}
/* 初始化 1602 液晶 */
void InitLcd1602(){
    LcdWriteCmd(0x38); //16*2 顯示,5*7 點(diǎn)陣,8 位數(shù)據(jù)接口
    LcdWriteCmd(0x0C); //顯示器開,光標(biāo)關(guān)閉
    LcdWriteCmd(0x06); //文字不動(dòng),地址自動(dòng)+1
    LcdWriteCmd(0x01); //清屏
}
/*****************************main.c 文件程序源代碼******************************/
#include <reg52.h>
extern void InitLcd1602();
extern void LcdShowStr(unsigned char x, unsigned char y, unsigned char *str);
extern void I2CStart();
extern void I2CStop();
extern unsigned char I2CReadNAK();
extern bit I2CWrite(unsigned char dat);
unsigned char E2ReadByte(unsigned char addr);
void E2WriteByte(unsigned char addr, unsigned char dat);

void main(){
    unsigned char dat;
    unsigned char str[10];

    InitLcd1602(); //初始化液晶
    dat = E2ReadByte(0x02); //讀取指定地址上的一個(gè)字節(jié)
    str[0] = (dat/100) + '0'; //轉(zhuǎn)換為十進(jìn)制字符串格式
    str[1] = (dat/10%10) + '0';
    str[2] = (dat%10) + '0';
    str[3] = '\0';

    LcdShowStr(0, 0, str); //顯示在液晶上
    dat++; //將其數(shù)值+1
    E2WriteByte(0x02, dat); //再寫回到對(duì)應(yīng)的地址上
    while (1);
}

/* 讀取 EEPROM 中的一個(gè)字節(jié),addr-字節(jié)地址 */
unsigned char E2ReadByte(unsigned char addr){
    unsigned char dat;

    I2CStart();
    I2CWrite(0x50<<1); //尋址器件,后續(xù)為寫操作
    I2CWrite(addr); //寫入存儲(chǔ)地址
    I2CStart(); //發(fā)送重復(fù)啟動(dòng)信號(hào)
    I2CWrite((0x50<<1)|0x01); //尋址器件,后續(xù)為讀操作
    dat = I2CReadNAK(); //讀取一個(gè)字節(jié)數(shù)據(jù)
    I2CStop();
    return dat;
}
/* 向 EEPROM 中寫入一個(gè)字節(jié),addr-字節(jié)地址 */
void E2WriteByte(unsigned char addr, unsigned char dat){
    I2CStart();
    I2CWrite(0x50<<1); //尋址器件,后續(xù)為寫操作
    I2CWrite(addr); //寫入存儲(chǔ)地址
    I2CWrite(dat); //寫入一個(gè)字節(jié)數(shù)據(jù)
    I2CStop();
}

這個(gè)程序,以同學(xué)們現(xiàn)在的基礎(chǔ),獨(dú)立分析應(yīng)該不困難了,遇到哪個(gè)語(yǔ)句不懂可以及時(shí)問問別人或者搜索一下,把該解決的問題理解明白。大家把這個(gè)程序復(fù)制過去后,編譯一下會(huì)發(fā)現(xiàn) Keil 軟件提示了一個(gè)警告:*** WARNING L16: UNCALLED SEGMENT, IGNORED FOR OVERLAY PROCESS,這個(gè)警告的意思是在代碼中存在沒有被調(diào)用過的變量或者函數(shù),即 I2C.c 文件中的 I2CReadACK()這個(gè)函數(shù)在本例中沒有用到。

大家仔細(xì)觀察一下這個(gè)程序,我們讀取 EEPROM 的時(shí)候,只讀了一個(gè)字節(jié)就要告訴 EEPROM 不需要再讀數(shù)據(jù)了,讀完后直接發(fā)送一個(gè)“NAK”,因此只調(diào)用了 I2CReadNAK()這個(gè)函數(shù),而并沒有調(diào)用 I2CReadACK()這個(gè)函數(shù)。我們今后很可能讀數(shù)據(jù)的時(shí)候要連續(xù)讀幾個(gè)字節(jié),因此這個(gè)函數(shù)寫在了 I2C.c 文件中,作為 I2C 功能模塊的一部分是必要的,方便我們這個(gè)文件以后移植到其他程序中使用,因此這個(gè)警告在這里就不必管它了。