国产成人av人人爽人人澡-亚洲国产日韩欧美一区-好吊日视频这里只有精品-日本高清精品视频在线

你好!歡迎來到深圳市穎特新科技有限公司!
語言
當前位置:首頁 >> 技術(shù)中心 >> 單片機入門 >> I2C總線與EEPROM

I2C總線與EEPROM

作者:admin 來源:不詳 發(fā)布時間:2018-05-08  瀏覽:13

I2C總線是由PHILIPS公司開發(fā)的兩線式串行總線,多用于連接微處理器及其外圍設(shè)備。I2C總線的主要特點是接口方式簡單,兩條線可以掛多個參與通信的器件,即多機模式,而且任何一個器件都可以作為主機,當然同一時刻只能一個主機。

  從原理上來講,UART屬于異步通信,比如電腦發(fā)送給單片機,電腦只負責把數(shù)據(jù)通過TXD發(fā)送出來即可,接收數(shù)據(jù)是單片機自己的事情。而I2C屬于同步通信,SCL時鐘線負責收發(fā)雙方的時鐘節(jié)拍,SDA數(shù)據(jù)線負責傳輸數(shù)據(jù)。I2C的發(fā)送方和接收方都以SCL這個時鐘節(jié)拍為基準進行數(shù)據(jù)的發(fā)送和接收。

  從應(yīng)用上來講,UART通信多用于板間通信,比如單片機和電腦,這個設(shè)備和另外一個設(shè)備之間的通信。而I2C多用于板內(nèi)通信,比如單片機和我們本章要學(xué)的EEPROM之間的通信。

  1、I2C時序初步認識

  在硬件上,I2C總線是由時鐘總線SCL和數(shù)據(jù)總線SDA兩條線構(gòu)成,連接到總線上的所有的器件的SCL都連到一起,所有的SDA都連到一起。I2C總線是開漏引腳并聯(lián)的結(jié)構(gòu),因此我們外部要添加上拉電阻。對于開漏電路外部加上拉電阻的話,那就組成了線“與”的關(guān)系?偩上線“與”的關(guān)系,那所有接入的器件保持高電平,這條線才是高電平。而任意一個器件輸出一個低電平,那這條線就會保持低電平,因此可以做到任何一個器件都可以拉低電平,也就是任何一個器件都可以作為主機,如圖1所示,我們添加了R63和R64兩個上拉電阻。

I2C總線的上拉電阻

圖1 I2C總線的上拉電阻

雖然說任何一個設(shè)備都可以作為主機,但絕大多數(shù)情況下我們都是用微處理器,也就是我們的單片機來做主機,而總線上掛的多個器件,每一個都像電話機一樣有自己唯一的地址,在信息傳輸?shù)倪^程中,通過這唯一的地址可以正常識別到屬于自己的信息,在我們的KST-51開發(fā)板上,就掛接了2個I2C設(shè)備,一個是24C02,一個是PCF8591。

我們在學(xué)習(xí)UART串行通信的時候,知道了我們的通信流程分為起始位、數(shù)據(jù)位、停止位這三部分,同理在I2C中也有起始信號、數(shù)據(jù)傳輸和停止信號,如圖2所示。

          I2C時序流程圖

圖2 I2C時序流程圖

  從圖上可以看出來,I2C和UART時序流程有相似性,也有一定的區(qū)別。UART每個字節(jié)中,都有一個起始位,8個數(shù)據(jù)位和1位停止位。而I2C分為起始信號,數(shù)據(jù)傳輸部分,最后是停止信號。其中數(shù)據(jù)傳輸部分,可以一次通信過程傳輸很多個字節(jié),字節(jié)數(shù)是不受限制的,而每個字節(jié)的數(shù)據(jù)最后也跟了一位,這一位叫做應(yīng)答位,通常用ACK表示,有點類似于UART的停止位。

  下面我們一部分一部分的把I2C通信時序進行剖析。之前我們學(xué)過了UART,所以學(xué)習(xí)I2C的過程我盡量拿UART來作為對比,這樣有助于更好的理解。但是有一點大家要理解清楚,就是UART通信雖然我們用了TXD和RXD兩根線,但是實際一次通信,1條線就可以完成,2條線是把發(fā)送和接收分開而已,而I2C每次通信,不管是發(fā)送還是接收,必須2條線都參與工作才能完成,為了更方便的看出來每一位的傳輸流程,我們把圖2改進成圖3。

            I2C通信流程解析

圖3 I2C通信流程解析

  起始信號:UART通信是從一直持續(xù)的高電平出現(xiàn)一個低電平標志起始位;而I2C通信的起始信號的定義是SCL為高電平期間,SDA由高電平向低電平變化產(chǎn)生一個下降沿,表示起始信號,如圖14-3中的start部分所示。

  數(shù)據(jù)傳輸:首先,UART是低位在前,高位在后;而I2C通信是高位在前,低位在后。第二,UART通信數(shù)據(jù)位是固定長度,波特率分之一,一位一位固定時間發(fā)送完畢就可以了。而I2C沒有固定波特率,但是有時序的要求,要求當SCL在低電平的時候,SDA允許變化,也就是說,發(fā)送方必須先保持SCL是低電平,才可以改變數(shù)據(jù)線SDA,輸出要發(fā)送的當前數(shù)據(jù)的一位;而當SCL在高電平的時候,SDA絕對不可以變化,因為這個時候,接收方要來讀取當前SDA的電平信號是0還是1,因此要保證SDA的穩(wěn)定不變化,如圖14-3中的每一位數(shù)據(jù)的變化,都是在SCL的低電平位置。8為數(shù)據(jù)位后邊跟著的是一位響應(yīng)位,響應(yīng)位我們后邊還要具體介紹。

  停止信號:UART通信的停止位是一位固定的高電平信號;而I2C通信停止信號的定義是SCL為高電平期間,SDA由低電平向高電平變化產(chǎn)生一個上升沿,表示結(jié)束信號,如圖14-3中的stop部分所示。

  2、I2C尋址模式

  上面介紹的是I2C每一位信號的時序流程,而I2C通信在字節(jié)級的傳輸中,也有固定的時序要求。I2C通信的起始信號(Start)后,首先要發(fā)送一個從機的地址,這個地址一共有7位,緊跟著的第8位是數(shù)據(jù)方向位(R/W),‘0’表示接下來要發(fā)送數(shù)據(jù)(寫),‘1’表示接下來是請求數(shù)據(jù)(讀)。

  我們知道,打電話的時候,當撥通電話,接聽方撿起電話肯定要回一個“喂”,這就是告訴撥電話的人,這邊有人了。同理,這個第九位ACK實際上起到的就是這樣一個作用。當我們發(fā)送完了這7位地址和1位方向位,如果我們發(fā)送的這個地址確實存在,那么這個地址的器件應(yīng)該回應(yīng)一個ACK‘0’,如果不存在,就沒“人”回應(yīng)ACK。

  那我們寫一個簡單的程序,訪問一下我們板子上的EEPROM的地址,另外在寫一個不存在的地址,看看他們是否能回一個ACK,來了解和確認一下這個問題。

  我們板子上的EEPROM器件型號是24C02,在24C02的數(shù)據(jù)手冊3.6部分說明了,24C02的7位地址中,其中高4位是固定的1010,而低3位的地址取決于我們電路的設(shè)計,由芯片上的A2、A1、A0這3個引腳的實際電平?jīng)Q定,來看一下我們的24C02的電路圖,如圖4所示。

 24C02原理圖

圖4 24C02原理圖

  從圖4可以看出來,我們的A2、A1、A0都是接的GND,也就是說都是0,因此我們的7位地址實際上是二進制的1010000,也就是0x50。我們用I2C的協(xié)議來尋址0x50,另外再尋址一個不存在的地址0x62,尋址完畢后,把返回的ACK顯示到我們的1602液晶上,大家對比一下。

/***********************lcd1602.c文件程序源代碼*************************/

#include <reg52.h>

 

#define LCD1602_DB   P0

 

sbit LCD1602_RS = P1^0;

sbit LCD1602_RW = P1^1;

sbit LCD1602_E  = P1^5;

 

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ù)檢測直到其等于0為止

}

void LcdWriteCmd(unsigned char cmd)  //寫入命令函數(shù)

{

    LcdWaitReady();

    LCD1602_RS = 0;

    LCD1602_RW = 0;

    LCD1602_DB = cmd;

    LCD1602_E  = 1;

    LCD1602_E  = 0;

}

void LcdWriteDat(unsigned char dat)  //寫入數(shù)據(jù)函數(shù)

{

    LcdWaitReady();

    LCD1602_RS = 1;

    LCD1602_RW = 0;

    LCD1602_DB = dat;

    LCD1602_E  = 1;

    LCD1602_E  = 0;

}

void LcdShowStr(unsigned char x, unsigned char y, const unsigned char *str)  //顯示字符串,屏幕起始坐標(x,y),字符串指針str

{

    unsigned char addr;

    

    //由輸入的顯示坐標計算顯示RAM的地址

    if (y == 0)

        addr = 0x00 + x; //第一行字符地址從0x00起始

    else

        addr = 0x40 + x; //第二行字符地址從0x40起始

    

    //由起始顯示RAM地址連續(xù)寫入字符串

    LcdWriteCmd(addr | 0x80); //寫入起始地址

    while (*str != '\0')      //連續(xù)寫入字符串數(shù)據(jù),直到檢測到結(jié)束符

    {

        LcdWriteDat(*str);

        str++;

    }

}

void LcdInit()  //液晶初始化函數(shù)

{

    LcdWriteCmd(0x38);  //16*2顯示,5*7點陣,8位數(shù)據(jù)接口

    LcdWriteCmd(0x0C);  //顯示器開,光標關(guān)閉

    LcdWriteCmd(0x06);  //文字不動,地址自動+1

    LcdWriteCmd(0x01);  //清屏

}

/*************************main.c文件程序源代碼**************************/

#include <reg52.h>

#include <intrins.h>

 

#define I2CDelay()  {_nop_();_nop_();_nop_();_nop_();}

 

sbit I2C_SCL = P3^7;

sbit I2C_SDA = P3^6;

 

bit I2CAddressing(unsigned char addr);

extern void LcdInit();

extern void LcdShowStr(unsigned char x, unsigned char y, const unsigned char *str);

 

void main ()

{

    bit ack;

    unsigned char str[10];

 

    LcdInit();        //初始化液晶

    

    ack = I2CAddressing(0x50); //查詢地址為0x50的器件

    str[0] = '5';              //將地址和應(yīng)答值轉(zhuǎn)換為字符串

    str[1] = '0';

    str[2] = ':';

    str[3] = (unsigned char)ack + '0';

    str[4] = '\0';

    LcdShowStr(0, 0, str);     //顯示到液晶上

    

    ack = I2CAddressing(0x62); //查詢地址為0x62的器件

    str[0] = '6';              //將地址和應(yīng)答值轉(zhuǎn)換為字符串

    str[1] = '2';

    str[2] = ':';

    str[3] = (unsigned char)ack + '0';

    str[4] = '\0';

    LcdShowStr(8, 0, str);     //顯示到液晶上

    

    while(1)

    {}

}

 

void I2CStart()  //產(chǎn)生總線起始信號

{

    I2C_SDA = 1; //首先確保SDA、SCL都是高電平

    I2C_SCL = 1;

    I2CDelay();

    I2C_SDA = 0; //先拉低SDA

    I2CDelay();

    I2C_SCL = 0; //再拉低SCL

}

void I2CStop()   //產(chǎn)生總線停止信號

{

    I2C_SCL = 0; //首先確保SDA、SCL都是低電平

    I2C_SDA = 0;

    I2CDelay();

    I2C_SCL = 1; //先拉高SCL

    I2CDelay();

    I2C_SDA = 1; //再拉高SDA

    I2CDelay();

}

bit I2CWrite(unsigned char dat) //I2C總線寫操作,待寫入字節(jié)dat,返回值為從機應(yīng)答位的值

{

    bit ack;  //用于暫存應(yīng)答位的值

    unsigned char mask;  //用于探測字節(jié)內(nèi)某一位值的掩碼變量

 

    for (mask=0x80; mask!=0; mask>>=1) //從高位到低位依次進行

    {

        if ((mask&dat) == 0)  //該位的值輸出到SDA上

            I2C_SDA = 0;

        else

            I2C_SDA = 1;

        I2CDelay();

        I2C_SCL = 1;          //拉高SCL

        I2CDelay();

        I2C_SCL = 0;          //再拉低SCL,完成一個位周期

    }

    I2C_SDA = 1;   //8位數(shù)據(jù)發(fā)送完后,主機釋放SDA,以檢測從機應(yīng)答

    I2CDelay();

    I2C_SCL = 1;   //拉高SCL

    I2CDelay();

    ack = I2C_SDA; //讀取此時的SDA值,即為從機的應(yīng)答值

    I2C_SCL = 0;   //再拉低SCL完成應(yīng)答位,并保持住總線

 

    return ack;    //返回從機應(yīng)答值

}

bit I2CAddressing(unsigned char addr) //I2C尋址函數(shù),即檢查地址為addr的器件是否存在,返回值為其應(yīng)答值,即應(yīng)答則表示存在,非應(yīng)答則表示不存在

{

    bit ack;

 

    I2CStart();  //產(chǎn)生起始位,即啟動一次總線操作

    ack = I2CWrite(addr<<1);  //器件地址需左移一位,因?qū)ぶ访畹淖畹臀粸樽x寫位,用于表示之后的操作是讀或?qū)?/P>

    I2CStop();   //不需進行后續(xù)讀寫,而直接停止本次總線操作

    

    return ack;

}

  我們把這個程序在KST-51開發(fā)板上運行完畢,會在液晶上邊顯示出來我們預(yù)想的結(jié)果,主機發(fā)送一個存在的從機地址,從機會回復(fù)一個應(yīng)答位;主機如果發(fā)送一個不存在的從機地址,就沒有從機應(yīng)答。

  前邊我有提到過有一個利用庫函數(shù)_nop_()來進行精確延時,一個_nop_()的時間就是一個機器周期,這個庫函數(shù)是包含在了intrins.h這個庫文件中,我們?nèi)绻褂眠@個庫函數(shù),只需要在程序最開始,和包含reg52.h一樣,include<intrins.h>之后,我們程序就可以直接使用這個庫函數(shù)了。

  還有一點要提一下,I2C通信分為低速模式100kbit/s,快速模式400kbit/s和高速模式3.4Mbit/s。因為所有的I2C器件都支持低速,但卻未必支持另外兩種速度,所以作為通用的I2C程序我們選擇100k這個速率來實現(xiàn),也就是說實際程序產(chǎn)生的時序必須小于等于100k的時序參數(shù),很明顯也就是要求SCL的高低電平持續(xù)時間都不短于5us,因此我們在時序函數(shù)中通過插入I2CDelay()這個總線延時函數(shù)(它實際上就是4個NOP指令,用define在文件開頭做了定義),加上改變SCL值語句本身占用的至少一個周期,來達到這個速度限制。如果以后需要提高速度,那么只需要減小這里的總線延時時間即可。

  此外我們要學(xué)習(xí)一個發(fā)送數(shù)據(jù)的技巧,就是I2C通信時如何將一個字節(jié)的數(shù)據(jù)發(fā)送出去。大家注意寫函數(shù)中,我用的那個for循環(huán)的技巧。for (mask=0x80; mask!=0; mask>>=1),由于I2C通信是從高位開始發(fā)送數(shù)據(jù),所以我們先從最高位開始,0x80和dat進行按位與運算,從而得知dat第7位是0還是1,然后右移一位,也就是變成了用0x40和dat按位與運算,得到第6位是0還是1,一直到第0位結(jié)束,最終通過if語句,把dat的8位數(shù)據(jù)依次發(fā)送了出去。其他的邏輯大家對照前邊講到的理論知識,認真研究明白就可以了。


編輯:admin  最后修改時間:2018-05-08

聯(lián)系方式

0755-82591179

傳真:0755-82591176

郵箱:vicky@yingtexin.net

地址:深圳市龍華區(qū)民治街道民治大道973萬眾潤豐創(chuàng)業(yè)園A棟2樓A08

Copyright © 2014-2023 穎特新科技有限公司 All Rights Reserved.  粵ICP備14043402號-4

99热中文字幕在线精品| 久久亚洲成熟女人毛片| 少妇在线一区二区三区| 黄片免费播放一区二区| 国产内射一级一片内射高清视频 | 欧美字幕一区二区三区| 日韩精品免费一区二区三区 | 深夜视频在线观看免费你懂| 国产成人精品综合久久久看| 91久久精品在这里色伊人| 91精品蜜臀一区二区三区| 国产在线日韩精品欧美| 亚洲国产av在线视频| 欧美日韩国产午夜福利| 免费人妻精品一区二区三区久久久| 熟女乱一区二区三区丝袜| 五月综合婷婷在线伊人| 九九热视频网在线观看| 国产又色又爽又黄又大| 扒开腿狂躁女人爽出白浆av| 欧美多人疯狂性战派对| 国产精品推荐在线一区| 亚洲一区二区精品国产av| 老司机精品视频在线免费看| 国产不卡视频一区在线| 国产成人人人97超碰熟女| 偷自拍亚洲欧美一区二页| 爱在午夜降临前在线观看| 亚洲av又爽又色又色| 欧美成人黄色一级视频| 香港国产三级久久精品三级| 东京热男人的天堂久久综合| 欧美色欧美亚洲日在线| 国产一区欧美一区日本道| 精品偷拍一区二区三区| 日韩在线精品视频观看| 精品欧美国产一二三区| 亚洲天堂精品在线视频| 欧美日韩亚洲综合国产人 | 中文久久乱码一区二区| 98精品永久免费视频|