在线观看www成人影院-在线观看www日本免费网站-在线观看www视频-在线观看操-欧美18在线-欧美1级

0
  • 聊天消息
  • 系統消息
  • 評論與回復
登錄后你可以
  • 下載海量資料
  • 學習在線課程
  • 觀看技術視頻
  • 寫文章/發帖/加入社區
會員中心
創作中心

完善資料讓更多小伙伴認識你,還能領取20積分哦,立即完善>

3天內不再提示

基于51單片機的I2C-EEPROM實驗

科技綠洲 ? 來源:云龍派 ? 作者:云龍派 ? 2023-09-11 10:37 ? 次閱讀
加入交流群
微信小助手二維碼

掃碼添加小助手

加入工程師交流群

1. I2C介紹

I2C ( Inter—Integrated Circuit)總線是由PHILIPS公司開發的兩線式串行總線,用于連接微控制器及其外圍設備。是微電子通信控制領域廣泛采用的一種總線標準。它是同步通信的一種特殊形式,具有接口線少,控制方式簡單,器件封裝形式小,通信速率較高等優點。I2C總線只有兩根雙向信號線。一根是數據線SDA,另一根是時鐘線SCL。 由于其管腳少,硬件實現簡單,可擴展性強等特點,因此被廣泛的使用在各大集成芯片內。下面從I2C的物理層與協議層來了解 I2C。

1.1 I2C物理層

I2C通信設備常用的連接方式如下圖所示:

圖片

它的物理層有如下特點:

(1)它是一個支持多設備的總線。“總線”指多個設備共用的信號線。在一個I2C通訊總線中,可連接多個I2C通訊設備,支持多個通訊主機及多個通訊從機。

(2)一個I2C總線只使用兩條總線線路,一條雙向串行數據線(SDA),一條串行時鐘線(SCL)。數據線即用來表示數據,時鐘線用于數據收發同步。

(3)每個連接到總線的設備都有一個獨立的地址,主機可以利用這個地址進行不同設備之間的訪問。

(4)總線通過上拉電阻接到電源。當I2C設備空閑時,會輸出高阻態,而當所有設備都空閑,都輸出高阻態時,由上拉電阻把總線拉成高電平。

(5)多個主機同時使用總線時,為了防止數據沖突,會利用仲裁方式決定由哪個設備占用總線。

(6)具有三種傳輸模式:標準模式傳輸速率為100kbit/s,快速模式為400kbit/s,高速模式下可達3.4Mbit/s,但目前大多 I2C設備尚不支持高速模式。

(7)連接到相同總線的IC數量受到總線的最大電容400pF限制。

高阻態這是一個數字電路里常見的術語,指的是電路的一種輸出狀態,既不是高電平也不是低電平, 如果高阻態再輸入下一級電路的話,對下級電路無任何影響,和沒接一樣,如果用萬用表測的話有可能是高電平也有可能是低電平,隨它后面接的東西定。

MCU(Micro Control Unit),中文為微控制單元,又稱單片微型計算機(Single Chip Microcomputer)或者單片機 ,是指將計算機的CPURAMROM、定時計數器和多種I/O接口集成在一片芯片上,形成芯片級的計算機。

I2C總線常涉及的術語

主機:啟動數據傳送并產生時鐘信號的設備;
從機:被主機尋址的器件;
多主機:同時有多于一個主機嘗試控制總線但不破壞傳輸;
主模式:用I2CNDAT支持自動字節計數的模式;位 I2CRM,I2CSTT,I2CSTP控制數據的接收和發送;
從模式:發送和接收操作都是由I2C模塊自動控制的;
仲裁:是一個在有多個主機同時嘗試控制總線但只允許其中一個控制總線并使傳輸不被破壞的過程;
同步:兩個或多個器件同步時鐘信號的過程;
發送器:發送數據到總線的器件;
接收器:從總線接收數據的器件。

1.2 I2C協議層

I2C的協議定義了通信的起始和停止信號、數據有效性、響應、仲裁、時鐘同步和地址廣播等環節。

(1)數據有效性規定

I2C總線進行數據傳送時,時鐘信號為高電平期間,數據線上的數據必須保持穩定,只有在時鐘線上的信號為低電平期間,數據線上的高電平或低電平狀態才允許變化。 如下圖:

圖片

每次數據傳輸都以字節為單位,每次傳輸的字節數不受限制。

(2)起始和停止信號

SCL線為高電平期間,SDA 線由高電平向低電平的變化表示起始信號;SCL線為高電平期間,SDA線由低電平向高電平的變化表示終止信號。 如下圖:

圖片

起始和終止信號都是由主機發出的,在起始信號產生后,總線就處于被占用的狀態;在終止信號產生后,總線就處于空閑狀態。

(3)應答響應

每當發送器件傳輸完一個字節的數據后,后面必須緊跟一個校驗位,這個校驗位是接收端通過控制SDA(數據線)來實現的,以提醒發送端數據我這邊已經接收完成,數據傳送可以繼續進行。這個校驗位其實就是數據或地址傳輸過程中的響應。響應包括“應答(ACK)”和“非應答(NACK)”兩種信號。作為數據接收端時,當設備(無論主從機)接收到I2C傳輸的一個字節數據或地址后,若希望對方繼續發送數據,則需要向對方發送“應答(ACK)”信號即特定的低電平脈沖,發送方會繼續發送下一個數據;若接收端希望結束數據傳輸,則向對方發送“非應答(NACK)”信號即特定的高電平脈沖,發送方接收到該信號后會產生一個停止信號,結束信號傳輸。應答響應時序圖如下:

圖片

每一個字節必須保證是8位長度。數據傳送時,先傳送最高位(MSB),每一個被傳送的字節后面都必須跟隨一位應答位(即一幀共有9位)。

由于某種原因從機不對主機尋址信號應答時(如從機正在進行實時性的處理工作而無法接收總線上的數據),它必須將數據線置于高電平,而由主機產生一個終止信號以結束總線的數據傳送。

如果從機對主機進行了應答,但在數據傳送一段時間后無法繼續接收更多的數據時,從機可以通過對無法接收的第一個數據字節的“非應答”通知主機,主機則應發出終止信號以結束數據的繼續傳送。

當主機接收數據時,它收到最后一個數據字節后,必須向從機發出一個結束傳送的信號。這個信號是由對從機的“非應答”來實現的。然后,從機釋放SDA線,以允許主機產生終止信號。

這些信號中,起始信號是必需的,結束信號和應答信號都可以不要。

也就是說一個主機一個從機,主機向從機發送數據,首先主機產生一個起始信號,主機開始發送數據給從機,如果從機接收數據之后,還想主機繼續發送數據,就發送一個應答給主機,主機收到應答信號后,繼續向從機發送數據。如果從機接收數據之后,不想主機繼續發送數據,終止數據傳輸,可以向主機發送一個非應答,主機就會產生一個停止信號,結束I2C的通信。

主機從從機讀取數據時,主機發送一個起始信號,主機接收數據之后,也要產生一個應答或非應答,如果主機產生一個應答,表示想繼續讀取從機數據,從機接收到應答信號,從機繼續發送數據給主機。主機如果不想讀取從機數據,就會產生一個非應答后,會產生一個停止信號,從機接收到非應答信號,停止數據發送。

(4)總線的尋址方式

I2C總線尋址按照從機地址位數可分為兩種,一種是7位,另一種是10位。采用7位的尋址字節(尋址字節是起始信號后的第一個字節)的位定義如下:

圖片

D7~D1 位組成從機的地址。D0位是數據傳送方向位,為“0”時表示主機向從機寫數據,為“1”時表示主機由從機讀數據。

10位尋址和7位尋址兼容,而且可以結合使用。10位尋址不會影響已有的7位尋址,有7位和10位地址的器件可以連接到相同的I2C總線。我們就以7位尋址為例進行介紹。

當主機發送了一個地址后,總線上的每個器件都將頭7位與它自己的地址比較,如果一樣,器件會判定它被主機尋址,其他地址不同的器件將被忽略后面的數據信號。 至于是從機接收器還是從機發送器,都由R/W位決定的。從機的地址由固定部分和可編程部分組成。在一個系統中可能希望接入多個相同的從機,從機地址中可編程部分決定了可接入總線該類器件的最大數目。如一個從機的7位尋址位有4位是固定位,3位是可編程位,2^3=8這時僅能尋址8個同樣的器件,即可以有8個同樣的器件接入到該I2C總線系統中。

(5)數據傳輸

I2C總線上傳送的數據信號是廣義的,既包括地址信號,又包括真正的數據信號。在起始信號后必須傳送一個從機的地址(7位),第8位是數據的傳送方向位(R/W),用“0”表示主機發送(寫)數據(W), “1”表示主機接收數據(R)。每次數據傳送總是由主機產生的終止信號結束。但是,若主機希望繼續占用總線進行新的數據傳送,則可以不產生終止信號,馬上再次發出起始信號對另一從機進行尋址。

在總線的一次數據傳送過程中,可以有以下幾種組合方式:

圖片

注意:有陰影部分表示數據由主機向從機傳送,無陰影部分則表示數據由從機向主機傳送。A表示應答,A非表示非應答(高電平)。S表示起始信號,Р表示終止信號。一個數據幀是9位。

圖片

圖片

由于51單片機沒有硬件IIC接口,即使有硬件接口我們通常還是采用軟件模擬I2C。 主要原因是硬件 IIC設計的比較復雜,而且穩定性不怎么好,程序移植比較麻煩,而用軟件模擬IIC,最大的好處就是移植方便,同一個代碼兼容所有單片機,任何一個單片機只要有IO口(不需要特定IO),都可以很快的移植過去。

EEPROM (Electrically Erasable Programmable read only memory)是指帶電可擦可編程只讀存儲器。是一種掉電后數據不丟失的存儲芯片。 EEPROM 可以在電腦上或專用設備上擦除已有信息,重新編程。一般用在即插即用。

2.AT24C02芯片介紹

AT24C02芯片也是EEPROM存儲芯片,是一種掉電后數據不丟失的存儲芯片。AT24C0、01/02/04/08/16...是一個1K/2K/4K/8K/16k位串行CMOS,內部含有128/256/512/1024/2048個8位字節,AT24C01有一個8字節頁寫緩沖器,AT24CO2/04/08/16有一個16 字節頁寫緩沖器。該器件通過I2C總線接口進行操作,它有一個專門的寫保護功能。開發板上使用的是 AT24C02 (EEPROM) 芯片,此芯片具有I2C通信接口,芯片內保存的數據在掉電情況下都不丟失,所以通常用于存放一些比較重要的數據等。AT24CO2芯片管腳及外觀圖如下圖所示:

圖片

圖片

芯片管腳說明如下圖所示:

圖片

AT24C02器件地址為7位,高4位固定為1010,低3位由A0/A1/A2信號線的電平決定。因為傳輸地址或數據是以字節為單位傳送的,當傳送地址時,器件地址占7位,還有最后一位(最低位R/W)用來選擇讀寫方向,它與地址無關。其格式如下:

圖片

開發板已經將芯片的 A0/A1/A2連接到GND,所以器件地址為1010000,即 0x50(未計算最低位)。如果要對芯片進行寫操作時,R/W即為0,寫器件地址即為0XA0;如果要對芯片進行讀操作時,R/W即為1,此時讀器件地址為0XA1。開發板上也將 WP引腳直接接在GND 上,此時芯片允許數據正常讀寫。

I2C總線時序如下圖所示:

圖片

圖片

開發板硬件連接

圖片

從圖中可以看出,芯片的SCL和SDA管腳是連接在單片機的P2.1和P2.0上,為了讓IIC總線默認為高電平,通常會在IIC總線上接上拉電阻,在圖中并沒有看到SCL和SDA管腳有上拉電阻,這是因為開發板單片機IO都外接了10K上拉電阻,當單片機IO口連接到芯片的SCL和SDA腳時即相當于它們外接上拉電阻,所以此處可以省去。

**3.多文件工程創建
**

實現的功能是:系統運行時,數碼管右 3 位顯示 0,按矩陣按鍵 S1 鍵將數據寫入到 EEPROM 內保存,按 S2 鍵讀取 EEPROM 內保存的數據,按 S3 鍵顯示數據加 1,按 S4 鍵顯示數據清零,最大能寫入的數據是 255。

圖片

App文件夾:用于存放外設驅動文件,如LED數碼管、定時器等。
Obj文件夾:用于存放編譯產生的 c/匯編/鏈接的列表清單、調試信息、hex文件、預覽信息、封裝庫等文件。
Public文件夾:用于存放51單片機公共的文件,如延時、51頭文件、變量類型重定義等。
User文件夾:用于存放用戶主函數文件,如 main.c。

1.新建一個工程

圖片

選擇對應的芯片型號

圖片

圖片

2.向工程添加文件

按照需要給工程分組并添加對應文件,在工程中分3組,User、App、Public,至于前面創建的Obj文件夾是在工程中無需體現,因為只是編譯器生成的一些中間文件和.hex執行文件。通常在工程組的命名與創建的文件夾名保持一致,方便查找到源文件位置。如下所示:

圖片

新建分組,雙擊可以修改名字,用英文分組,完成后,點擊OK

圖片

分組后,在工程中就會出現剛才的分組列表,如下所示:

圖片

對分組添加相應的文件,點擊小方框,選擇要添加文件的分組,添加文件。

圖片

同樣的方法,將App、Public工程組中文件也添加進去。一般除了main.c沒有對應的main.h的頭文件,其他文件通常會有對應的.h頭文件,例如public.h和public.c。

圖片

頭文件的書寫

#ifndef _public_H  //public是與文件名相同  ifndef是c語言中的條件編譯,意思是如果沒有定義對應的頭文件,就會定義該頭文件
#define _public_H




#endif

主要目的是防止頭文件的重復包含和編譯。

配置魔術棒選項

這一步的配置工作非常重要,很多人編寫完程序編譯后發現找不到HEX文件,還有的人直接編譯前面添加好文件的工程出現報錯,這些問題都是在這個地方沒有配置好導致的。

(1)C51選項卡配置,此處目的是將我們前面添加到工程組中的文件路徑包括進來,否則程序中調用其他文件夾的頭文件則會報錯找不到頭文件路徑,具體步驟如下:

點擊魔術棒

圖片

圖片

圖片

頭文件的作用一般是(1)包含其他的頭文件;(2)聲明全局變量;(3)聲明自定義函數(4)自定義的變量類型(u8 u16),當其他程序中加載了該頭文件就可以使用該函數和全局變量。

public.h文件

#ifndef _public_H//public是與文件名相同  
//ifndef是c語言中的條件編譯,意思是如果沒有定義對應的頭文件,就會定義該頭文件
//主要目的是防止頭文件的重復包含和編譯。
#define _public_H


#include "reg52.h" //加載reg52.h頭文件
//定義類型別名
typedef unsigned int u16;
typedef unsigned char u8;


//聲明兩個延時函數
void delay_10us(u16 time);
void delay_ms(u16 time);


#endif

public.c文件

#include "public.h"


void delay_10us(u16 time)  //延時函數 time=1大概延時10us
{
  while(time--)
  ;
}


void delay_ms(u16 time)       //延時函數 time=1大概延時1ms
{
  u16 i,j;
  for(i=time;i >0;i--)
    for(j=110;j >0;j--)
    ;
}

(2)Output選項卡中把輸出文件夾定位到我們實驗目錄下的 Obj文件夾,如果想在編譯的過程中生成hex文件,那么、Create HEX File 選項勾上。配置如下:

圖片

(3) Listing 選項卡中把輸出文件夾也定位到我們實驗目錄下的Obj文件夾。其它設置默認,配置如下:

圖片

同樣的方式把其他文件夾的文件添加到對應的分組,并將頭文件的路徑添加上。

圖片

4.實驗內容和程序

實現的功能是:系統運行時,數碼管右 3 位顯示 0,按矩陣按鍵 S1 鍵將數據寫入到 EEPROM 內保存,按 S2 鍵讀取 EEPROM 內保存的數據,按 S3 鍵顯示數據加 1,按 S4 鍵顯示數據清零,最大能寫入的數據是 255。

實驗結果

圖片

,時長00:36

[ ]

主函數main.c

#include "public.h"
#include "key.h"
#include "smg.h"
#include "at24c02.h"
//#include "iic.h"   at24c02函數內容調用了iic.h
/*
實現的功能是:系統運行時,數碼管右 3 位顯示 0,
按 S1 鍵將數據 寫入到 EEPROM 內保存,
按 S2 鍵讀取 EEPROM 內保存的數據,
按 S3 鍵顯示數據加 1,
按 S4 鍵顯示數據清零,最大能寫入的數據是 255。
*/


#define EEPROM_ADDRESS 0 //宏定義數據存儲的地址
void main()
{
  u8 key_value=0;  //保存按鍵的鍵值
  u8 save_value =0;//定義變量,存儲數據的變量
  u8 save_buf[3];//三位數
   while(1)
  {
    key_value=key_juzhen_fanzhuan_scan();
    if(key_value==1)  //按 K1 鍵將數據 寫入到 EEPROM 內保存
    {
      at24c02_write_one_byte(EEPROM_ADDRESS,save_value);


    }
    else if(key_value==2)  //按 K2 鍵讀取 EEPROM 內保存的數據
    {
      save_value=at24c2_read_one_byte(EEPROM_ADDRESS);
    }
    else if(key_value==3)
    {
       save_value++;
       //不能一直加 8位二進制 最大值255
       if(save_value==255)//當數據大于255后會溢出 重新從0開始
         save_value=255;
    }
    else if(key_value==4)
    {
           save_value=0;
    }
    //數碼管顯示
    //對十進制數據進行取百位 十位 個位
    /*
    三位數提取 百位 假設三位數是100 100/100=1 取出百位
    十位提取 (100%100)/10 數據對100取余 ,再除以10 取出十位
    個位提取  (100%100)%10  數據對100取余,再對10取余 取出個位 
    */
//    save_buf[0]=save_value/100;//取出百位
//    save_buf[1]= save_value%100/10;// 取出十位
//    save_buf[2]= save_value%100%10;//取出個位
    save_buf[0]=gsmg[save_value/100];//取出百位
    save_buf[1]= gsmg[save_value%100/10];// 取出十位
    save_buf[2]= gsmg[save_value%100%10];//取出個位
      smg_display(save_buf,6);//數組名表示數組元素首地址
  }
}

Key.c 矩陣按鍵程序

#include "key.h"








u8 key_juzhen_fanzhuan_scan(void) //定義線翻轉法函數
{
   /*
  線翻轉法,就是使所有行線為低電平時,檢測所有列線是否有低電平,
  如果有,就記錄列線值;然后再翻轉,使所有列線都為低電平,檢測所有行線的值,
  由于有按鍵按下,行線的值也會有變化,記錄行線的值。從而就可以檢測到全部按鍵。
   */
    static u8 key_value=0;   
    //定義靜態局部變量 key_value的值會賦完一次初值之后,key_value的值會保存上一次的結果值
    /*開發板 4*4矩陣鍵盤 行控制端口 P1.7 P1.6 P1.5 P1.4    列控制端口: P1.3 P1.2 P1.1 P1.0
     P1口輸出默認是高電平,使所有行線為低電平  此時P1控制端 二進制 0000 1111  十六進制 0x0f
     找出按鍵的列值
     {
       按鍵在第1列時,此時P1控制端 二進制 0000 0111  十六進制 0x07     key_value=1
    按鍵在第2列時,此時P1控制端 二進制 0000 1011  十六進制 0x0b     key_value=2
    按鍵在第3列時,此時P1控制端 二進制 0000 1101  十六進制 0x0d     key_value=3
    按鍵在第4列時,此時P1控制端 二進制 0000 1110  十六進制 0x0e       key_value=4

     }  
     再使所有的列線為低電平  此時P1控制端 二進制 1111 0000  十六進制 0xf0
     {
    按鍵在第1行時,此時P1控制端 二進制  0111 0000  十六進制 0x70    按鍵的位置 key_value=對應的行數=key_value
    按鍵在第2行時,此時P1控制端 二進制  1011 0000  十六進制 0xb0    按鍵的位置 key_value=對應的行數+4*1 = key_value+4    第一行有4個按鍵
    按鍵在第3行時,此時P1控制端 二進制  1101  0000    十六進制 0xd0    按鍵的位置 key_value=對應的行數+4*2  = key_value+8  前兩行有8個按鍵
    按鍵在第4行時,此時P1控制端 二進制  1110  0000    十六進制 0xe0      按鍵的位置 key_value=對應的行數+4*3  = key_value+12 前兩行有12個按鍵
     }
     */
    KEY_Port=0x0f;       //使所有行線為低電平 
    if(KEY_Port!=0x0f)
    {
       delay_10us(1000);//延時10ms實現按鍵消抖
      //測試列
      if(KEY_Port!=0x0f) //實現消抖后再次檢測
      {
      KEY_Port=0x0f;  //設置再次賦值 行線為低電平,方便進行檢查 
         switch(KEY_Port)
         {
          case 0x07: key_value = 1;break;
          case 0x0b: key_value = 2;break;
          case 0x0d: key_value = 3;break;
          case 0x0e: key_value = 4;break;
         }
         //測試列
        KEY_Port=0xf0;//使所有的列線為低電平
        switch(KEY_Port)
         {
          case 0x70: key_value = key_value;break;
          case 0xb0: key_value = key_value+4;break;
          case 0xd0: key_value = key_value+8;break;
          case 0xe0: key_value = key_value+12;break;
         }
         while(KEY_Port!=0xf0);//等待按鍵松開 當按鍵松開時 KEY_Port = 0xf0 會跳出死循環  
      }


    }
     else
    key_value=0;
    return key_value;//返回函數值


}

Key.h 頭文件

#ifndef _key_H     //條件編譯語句 如果沒有定義頭文件,就在下面重新定義頭文件
#define _key_H
#include "public.h"  //加載公共部分Public中的頭文件的內容


#define KEY_Port P1 //宏定義 無分號 將P1口定義為矩陣鍵盤的端口 
//開發板的數碼管段選信號由P0口(P0.0-P0.7)控制   位選 P2口默認是輸出高電平 默認選擇LED8 第1個數碼管
//定義數碼管顯示內容的數組


u8 key_juzhen_fanzhuan_scan(void);//定義線翻轉法函數




#endif

Smg.c 數碼管顯示函數

#include "smg.h"
u8 gsmg[17]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,
      0x6f,0x77,0x7c,0x39,0x5e,0x79,0x71,0x00};
      //共陰極數碼管顯示數據1-F,最后一個是數碼管全滅  表示c語言中的續行符
void smg_display(u8 dat[],u8 pos)   //dat[]是數碼管段選數據 顯示0 1 2 3...
// pos 位選     1 2 3 4 5 6 7 8 第幾個數據管
//更改數碼管的顯示 提供數據和數碼管使用的位置
{
 u8 i=0;     //定義變量的時候賦初值,防止一些編譯器識別未用的變量產生報錯
 u8 pos_temp=pos-1;//
 for(i=pos_temp;i< 8;i++)
 {
    switch(i)  //用switch-case語句進行位選
  {
   case 7: W_C =0;W_B = 0;W_A = 0;break; //對應選擇了Y0-LED1   最后一個數碼管
   case 6: W_C =0;W_B = 0;W_A = 1;break; //對應選擇了Y0-LED2
   case 5: W_C =0;W_B = 1;W_A = 0;break; //對應選擇了Y0-LED3
   case 4: W_C =0;W_B = 1;W_A = 1;break; //對應選擇了Y0-LED4
   case 3: W_C =1;W_B = 0;W_A = 0;break; //對應選擇了Y0-LED5
   case 2: W_C =1;W_B = 0;W_A = 1;break; //對應選擇了Y0-LED6
   case 1: W_C =1;W_B = 1;W_A = 0;break; //對應選擇了Y0-LED7
   case 0: W_C =1;W_B = 1;W_A = 1;break; //對應選擇了Y0-LED8  第一個數碼管


  }


//  SMG_Port = gsmg[dat[i-pos_temp]];//段選信號  dat[]表示0 1 2 3 4 5 6 7 -F
  SMG_Port = dat[i-pos_temp];//段選信號  dat[]表示0 1 2 3 4 5 6 7 -F
  delay_10us(100);//大概1ms延時
  SMG_Port=0x00;//消隱
 }
}

Smg.h 頭文件

#ifndef _smg_H
#define _smg_H
#include "public.h"


#define SMG_Port P0 //宏定義 將P0口定義SMG_Port 
//開發板的數碼管段選信號由P0口(P0.0-P0.7)控制
//定義數碼管顯示內容的數組
//定義的是全局變量 gsmg 開頭
extern u8 gsmg[17];   //對數組聲明


sbit W_C = P2^4;//sbit定義位變量  位控制選擇數碼管
sbit W_B = P2^3; //sbit定義位變量
sbit W_A = P2^2;//sbit定義位變量
void smg_display(u8 dat[],u8 pos); //聲明數碼管動態掃描顯示函數
#endif

Iic.c

#include "iic.h"


//IIC起始和停止信號和應答信號函數
/*
SCL線為高電平期間,SDA 線由高電平向低電平的變化表示起始信號;
SCL線為高電平期間,SDA線由低電平向高電平的變化表示終止信號。
每當發送器件傳輸完一個字節的數據后,后面必須緊跟一個校驗位,
這個校驗位是接收端通過控制SDA(數據線)來實現的,
以提醒發送端數據我這邊已經接收完成,數據傳送可以繼續進行。
這個校驗位其實就是數據或地址傳輸過程中的響應。
響應包括"應答(ACK)"和"非應答(NACK)"兩種信號。
作為數據接收端時,當設備(無論主從機)接收到I2C傳輸的一個字節數據或地址后,
若希望對方繼續發送數據,則需要向對方發送"應答(ACK)"信號即特定的低電平脈沖,
發送方會繼續發送下一個數據;若接收端希望結束數據傳輸,
則向對方發送"非應答(NACK)"信號即特定的高電平脈沖
,發送方接收到該信號后會產生一個停止信號,結束信號傳輸。
每一個字節必須保證是8位長度。數據傳送時,先傳送最高位(MSB),
每一個被傳送的字節后面都必須跟隨一位應答位(即一幀共有9位)。*/


void iic_start(void)  //根據起始信號的時序編寫iic的起始信號函數
{

  //根據數據手冊的時間參數級或者us SDA由高電平編程低電平  
  IIC_SDA=1;//如果把該條語句放在SCL后面,第二次讀寫會出現問題
  delay_10us(1);
  IIC_SCL=1;  //SCL為高電平
  delay_10us(1);
  IIC_SDA=0;
  delay_10us(1);
   IIC_SCL=0; //SCL為低電平時,表示占用總線,總線處于工作狀態
  delay_10us(1);
}


void iic_stop(void) //iic的停止信號
{
  IIC_SDA=0; //如果把該條語句放在SCL后面,第二次讀寫會出現問題
    //根據數據手冊的時間參數級或者us SDA由低電平編程高電平
   delay_10us(1);  
  IIC_SCL=1; //SCL為高電平
  delay_10us(1);
  IIC_SDA=1;
  delay_10us(1);


}
/*
 響應包括"應答(ACK)"和"非應答(NACK)"兩種信號。
 作為數據接收端時,當設備(無論主從機)接收到I2C傳輸的一個字節數據或地址后,
 若希望對方繼續發送數據,則需要向對方發送"應答(ACK)"信號即特定的低電平脈沖,
 發送方會繼續發送下一個數據;若接收端希望結束數據傳輸,
 則向對方發送"非應答(NACK)"信號即特定的高電平脈沖,
 發送方接收到該信號后會產生一個停止信號,結束信號傳輸。
*/


//主機發送數據給從機 的應答信號和非應答信號
void iic_ack(void) //iicACK應答信號函數
{
   //ACK是特殊的低電平信號
  //由時序圖編寫
  IIC_SCL=0;
  IIC_SDA=0;
  delay_10us(1);
  IIC_SCL=1;
  delay_10us(1);
  IIC_SCL=0;


}
void iic_noack(void) //iic非應答信號函數
{
   //NACK是特殊的高電平信號
  //由時序圖編寫
  IIC_SCL=0;
  IIC_SDA=1;
  delay_10us(1);
  IIC_SCL=1;
  delay_10us(1);
  IIC_SCL=0;


}


//判斷應答信號的函數 就是主機讀取IIC_SDA的電平
//函數設置一個返回值 1表示非應答  0表示應答
u8 iic_wait_ack(void)
{
   /*第一步:因為SCL為高電平的時候數據才是穩定的,將SCL設置為高電平.
    第二步;延時一點時間進行讀取SDA的電平.
    第三步:利用while循環   while(IIC_SDA) 當IIC_SDA=0時,響應是ACK應答信號會跳出循環,
    將SCL設置為低電平,數據可以修改,等待下一次數據的變化,并且返回函數值0.
    當IIC_SDA=1時表示響應是NACK非應答信號會執行循環體里面的函數,一直循環,設置一個變量,用來計時
    的時間,不能一直等待,當計時超過一定的時間,就認為SDA是高電平,此時是非應答,要產生停止信號
  */
  u8 time_temp=0;
  IIC_SCL=1;
  delay_10us(1);
  while(IIC_SDA)
   {
    time_temp++;//等價與time_step=time_step+1
    if(time_temp >100)
    {
      iic_stop();
      return 1;
    }


   }
     IIC_SCL=0;
  return 0;
}
//IIC的讀寫字節數據函數
//IIC的寫字節函數
void iic_write_byte(u8 dat)
{
 //要寫數據或地址 從高位開始寫 SCL低電平數據可以變化 SCL高電平的時候數據穩定,就可以發送出去
  //一個字節有8位,因此要一位一位的寫用循環
  u8 i=0;
  IIC_SCL=0;//SCL是低電平 數據可以修改 先寫高位
  for(i=0;i< 8;i++)
  {
     /*獲取dat最高位的 dat&0x80   d7 d6 d5 d4 d3 d2 d1 d0  & 1 0 0 0 0 0 0 0
  根據與運算  dat的低7位都為0 當d7=1 時邏輯表達式的值為1 當d7=0時,邏輯表達式的值為0
  */
  if((dat&0x80) >0) //dat最高位是1
    IIC_SDA=1; //傳輸數據是1
  else
    IIC_SDA=0;  //傳輸數據是0
/*對dat的次高位變成最高位 移位運算符dat< < 1 表示dat左移一位但是不會改變dat的值,
 表達式的值是dat左移一位的結果  左移移位運算符 dat< <=1      表示dat左移一位將結果賦值給dat*/
   dat< <=1;
  delay_10us(1);//延時10us
    IIC_SCL=1; //數據要進行傳輸 SCL為高電平 數據穩定 可以進行數據傳輸
  delay_10us(1);//延時10us
  IIC_SCL=0; //重新SCL設置為0 為下一位的數據修改做準備 
  delay_10us(1);//延時10us
  }


}
//IIC的讀字節函數
u8 iic_read_byte(u8 ack)  
//IIC的讀字節函數,要讀取數據,因此由函數的返回值,一個字節的數據u8
//讀取數據需要看返回的響應信號 1 ACK 繼續讀取 0 NACK不讀取數據
{
  u8 i=0;
  u8 receive_dat=0;//接收數據
  for(i=0;i< 8;i++)
  {
    IIC_SCL=0;//SCL為低電平 可以修改數據
    delay_10us(1);
    IIC_SCL=1;  //SCL為高電平 數據穩定 可以進行讀取
    receive_dat< <=1;
    if(IIC_SDA)  //開始讀取數據 判斷SDA管腳電平 SDA==1 得到一個信號
    //對最低位進行讀取
    receive_dat++;
    delay_10us(1);  
  /*例如  i=0 SDA=1 receive_dat=0+1=1  高位      先讀取高位
      i=1 SDA=1 receive_dat=1+1=2  次高位
    1 二進制  0000 0001
    2 二進制  0000 0010
    與實際不符合 實際應該是0000 0011 并且需要進行移位最后11移動最高位
    即 receive_dat進行左移 假設i=0 SDA=1  0000 0001 
                  i=1 SDA=0  0000 0010  
                  添加  receive_dat< <=1;左移語句
    假設從機數據是 1001 0001
    i    receive_dat< <=1;    receive_dat  主機從從機讀取的數據
    i=0  0000 0000      0000 0001     最高位數據 SDA=1
    i=1  0000 0010      0000 0010     次高位數據 SDA=0
    i=2  0000 0100      0000 0100     SDA=0
    i=3  0000 1000      0000 1001     SDA=1
    i=4  0001 0010      0001 0010        SDA=0
    i=5  0010 0100      0010 0100         SDA=0
    i=6  0100 1000      0100 1000        SDA=0
    i=7  1001 0000      1001 0001        SDA=1
    數據傳輸完成*/
  }
  //對響應信號進行處理 看主機是否需要繼續向從機讀取數據 ACK=0 繼續讀取數據 ACK=1停此讀取數據
  if(!ack) // ack=0時發送一個ack
  iic_noack();
  else
     iic_ack();    // ack=1  !ack=0 時發送一個nack


  return receive_dat;//返回讀取的數據
}

Iic.h

#ifndef _iic_H
#define _iic_H




#include "public.h"
//利用I/O模擬IIC的時序 開發板的P2.1連接SCL時鐘線 P2.0連接SDA雙向數據傳輸線
sbit IIC_SCL=P2^1;
sbit IIC_SDA=P2^0;
//函數聲明
void iic_start(void);//根據起始信號的時序編寫iic的起始信號函數
void iic_stop(void); //iic的停止信號
void iic_ack(void); //iicACK應答信號函數
void iic_noack(void); //iic非應答信號函數
//判斷應答信號的函數 就是主機讀取IIC_SDA的電平
//函數設置一個返回值 1表示非應答  0表示應答
u8 iic_wait_ack(void);
//IIC的寫字節函數
void iic_write_byte(u8 dat);
//IIC的讀字節函數
u8 iic_read_byte(u8 ack);
#endif

At24c02.c 程序

#include "at24c02.h"
#include "iic.h"
//at24c02的讀寫程序,根據IIC協議層數據傳輸編寫
void at24c02_write_one_byte(u8 addr,u8 dat) 
//24C02有256個字節 要寫到寫入地址  和寫入的數據    主機寫入從機 主機向從機發送數據
{
   /* 第一步:發送起始信號 
  AT24C02器件地址為7位,高4位固定為1010,
  低3位由A0/A1/A2信號線的電平決定。
  因為傳輸地址或數據是以字節為單位傳送的,
  當傳送地址時,器件地址占7位,還有最后一位(最低位R/W)用來選擇讀寫方向,它與地址無關。 
     寫AT24C02引腳二進制 1010 0000  十六進制:0xA0
     讀AT24C02引腳二進制 1010 0001  十六進制:0xA1


     */
  iic_start();//發送起始信號  后第一個字節是從機地址+讀寫位
  iic_write_byte(0xA0);
  //從機發的ACK   主機需要等待ACK
  iic_wait_ack();   //等待一個ACK
  iic_write_byte(addr);  //發送寫地址的傳輸
  //因為數據傳輸方向沒有改變 因此不需要從新產生起始信號和
  iic_wait_ack();    //等待一個ACK
  iic_write_byte(dat);//寫入一個字節數據
  iic_wait_ack();   //等待一個ACK
  iic_stop();//傳輸一個字節后 主機發出停止信號
  delay_ms(10);//延時10ms  將數據存儲
}


//讀字節函數    就是主機向從機讀取數據  即從機向主機發送數據 
//數據的傳輸方向改變
u8 at24c2_read_one_byte(u8 addr)   //讀取數據的地址
{  //首先寫入讀取數據的存放的地址 然后改變數據傳輸方向  讀取數據傳輸方向改變
   u8 temp=0;//讀取數據存儲變量
   iic_start();//主機發送起始信號
  iic_write_byte(0xA0);//  第一個字節是從機地址+讀寫位
  iic_wait_ack();    //等待一個ACK
  iic_write_byte(addr);  //發送讀數據存放地址的傳輸
  iic_wait_ack();    //等待一個ACK
   //開始讀取數據   數據傳輸方向改變
    iic_start();//主機發送起始信號
  iic_write_byte(0xA1);//  第一個字節是從機地址+讀寫位
  iic_wait_ack();    //等待一個ACK
  temp=iic_read_byte(0);  //1 ACK 繼續讀取 0 NACK不讀取數據
  iic_stop();//產生停止信號
  return temp;
}

At24c02.h 頭文件

#ifndef _at24c02_H
#define _at24c02_H
#include "public.h"
void at24c02_write_one_byte(u8 addr,u8 dat) ;
u8 at24c2_read_one_byte(u8 addr);   //讀取數據的地址
#endif
聲明:本文內容及配圖由入駐作者撰寫或者入駐合作網站授權轉載。文章觀點僅代表作者本人,不代表電子發燒友網立場。文章及其配圖僅供工程師學習之用,如有內容侵權或者其他違規問題,請聯系本站處理。 舉報投訴
  • 微控制器
    +關注

    關注

    48

    文章

    7933

    瀏覽量

    154079
  • 51單片機
    +關注

    關注

    277

    文章

    5710

    瀏覽量

    127018
  • 總線
    +關注

    關注

    10

    文章

    2958

    瀏覽量

    89530
  • EEPROM
    +關注

    關注

    9

    文章

    1084

    瀏覽量

    83513
  • I2C
    I2C
    +關注

    關注

    28

    文章

    1538

    瀏覽量

    127406
收藏 人收藏
加入交流群
微信小助手二維碼

掃碼添加小助手

加入工程師交流群

    評論

    相關推薦
    熱點推薦

    C51單片機模擬I2C總線的C語言實現

    EEPROM為ATMEL公司的AT24C01A。單片機為ATMEL公司的AT89C512. 軟件說明
    發表于 08-13 13:55 ?88次下載

    51單片機實驗程序

    一共有9個51單片機實驗程序,實際上還有相關的匯編程序N例,單片機嵌入式程序和I2C源程序等等。
    發表于 09-11 10:47 ?19次下載
    <b class='flag-5'>51</b><b class='flag-5'>單片機</b><b class='flag-5'>實驗</b>程序

    I2C總線的單片機C語言實現及其應用

    I2C總線的單片機C語言實現及其應用 本文介紹了I2C總線的概念、接口特性和傳輸時序,提出了一種用51系列
    發表于 10-17 08:36 ?3162次閱讀
    <b class='flag-5'>I2C</b>總線的<b class='flag-5'>單片機</b><b class='flag-5'>C</b>語言實現及其應用

    I2C總線的51單片機通用驅動程序

    i2c 51單片機通用驅動程序
    發表于 05-20 10:26 ?28次下載

    使用C語言模擬51單片機I2C總線的資料和程序免費下載

    本文檔的主要內容詳細介紹的是使用C語言模擬51單片機I2C總線的資料和程序免費下載。
    發表于 08-06 17:34 ?11次下載
    使用<b class='flag-5'>C</b>語言模擬<b class='flag-5'>51</b><b class='flag-5'>單片機</b>的<b class='flag-5'>I2C</b>總線的資料和程序免費下載

    使用51單片機IO模擬I2C的程序免費下載

    本文檔的主要內容詳細介紹的是使用51單片機IO模擬I2C的程序免費下載。
    發表于 08-02 17:34 ?5次下載
    使用<b class='flag-5'>51</b><b class='flag-5'>單片機</b>IO模擬<b class='flag-5'>I2C</b>的程序免費下載

    使用51單片機模擬I2C的原理和程序免費下載

    本文檔的主要內容詳細介紹的是使用51單片機模擬I2C的原理和程序免費下載。
    發表于 07-12 17:39 ?3次下載
    使用<b class='flag-5'>51</b><b class='flag-5'>單片機</b>模擬<b class='flag-5'>I2C</b>的原理和程序免費下載

    如何使用C語言實現51單片機模擬I2C總線

    EEPROM為ATMEL公司的AT24C01A。單片機為ATMEL公司的AT89C51
    的頭像 發表于 05-05 15:32 ?4771次閱讀
    如何使用<b class='flag-5'>C</b>語言實現<b class='flag-5'>51</b><b class='flag-5'>單片機</b>模擬<b class='flag-5'>I2C</b>總線

    I2C串行EEPROM與PICmicro單片機的接口設計

    I2C串行EEPROM與PICmicro單片機的接口設計說明。
    發表于 05-11 10:24 ?7次下載

    STM32F4 I2C-EEPROM實驗例程

    STM32F4 I2C-EEPROM實驗例程(嵌入式開發專業課程)-STM32F4 I2C-EEPROM實驗例程,有需要的可以參考!
    發表于 07-30 16:02 ?19次下載
    STM32F4 <b class='flag-5'>I2C-EEPROM</b><b class='flag-5'>實驗</b>例程

    51單片機實驗12:EEPROM(IIC總線) 應用

    51單片機實驗12:EEPROM(IIC總線) 應用
    發表于 11-23 16:36 ?21次下載
    <b class='flag-5'>51</b><b class='flag-5'>單片機</b><b class='flag-5'>實驗</b>12:<b class='flag-5'>EEPROM</b>(IIC總線) 應用

    單片機I/O口輸出控制實驗

    單片機實驗I/O口輸出控制實驗一、實驗目的1.熟悉PROTEUS單片機仿真軟件的使用。
    發表于 11-23 17:51 ?20次下載
    【<b class='flag-5'>單片機</b>】<b class='flag-5'>I</b>/O口輸出控制<b class='flag-5'>實驗</b>

    基于51單片機的iic--24c02EEPROM讀寫程序

    基于51單片機的iic--24c02EEPROM讀寫例程源代碼
    發表于 05-12 16:44 ?0次下載

    基于51單片機的iic--24c02EEPROM讀寫例程源代碼

    基于51單片機的iic--24c02EEPROM讀寫例程源代碼
    發表于 05-18 09:55 ?4次下載

    CW32單片機I2C接口讀寫EEPROM芯片介紹

    CW32單片機I2C接口讀寫EEPROM芯片介紹
    的頭像 發表于 11-09 17:42 ?1514次閱讀
    CW32<b class='flag-5'>單片機</b><b class='flag-5'>I2C</b>接口讀寫<b class='flag-5'>EEPROM</b>芯片介紹
    主站蜘蛛池模板: 欧美在线观看www | 夜夜操夜夜 | 国产三级 在线播放 | 国产福利资源在线 | 人人看人人做人人爱精品 | 午夜香港三级在线观看网 | 69国产成人综合久久精品 | 美日韩一级 | 五月天婷亚洲 | 香港三澳门三日本三级 | 高清一区二区三区 | 青草青青视频 | 三级黄色片免费观看 | 五色网| 午夜视频入口 | 亚洲人成电影在线播放 | 视频在线观看高清免费大全 | 免费能看的黄色网址 | 免费超爽视频 | 国产精品九九久久一区hh | 毛片色毛片18毛片美女 | 插插操操| 骚黄视频 | 黄色网页在线观看 | 天天激情 | www.亚洲5555.com| 免费人成网址在线观看国内 | 色天使色护士 | 亚洲成在人线久久综合 | 国产精品美女一级在线观看 | 91黄色视屏 | 亚洲婷婷综合网 | 色免费观看| 日韩一级片在线播放 | 1024你懂的国产欧美日韩在 | 中文字幕一二三四区2021 | 免费看的一级毛片 | 亚洲日本三级 | 天天操天天干天天爱 | 色就是色欧美色图 | 1024手机最新手机在线 |