日本高清不卡中文字幕-一起草草视频在线观看-亚洲精品一区二区三区色-国产亚洲精品免费视频

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

I2C總線與EEPROM

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

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

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

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

  1、I2C時(shí)序初步認(rèn)識(shí)

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

I2C總線的上拉電阻

圖1 I2C總線的上拉電阻

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

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

          I2C時(shí)序流程圖

圖2 I2C時(shí)序流程圖

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

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

            I2C通信流程解析

圖3 I2C通信流程解析

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

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

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

  2、I2C尋址模式

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

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

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

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

 24C02原理圖

圖4 24C02原理圖

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

/***********************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()  //等待液晶準(zhǔn)備好

{

    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)  //顯示字符串,屏幕起始坐標(biāo)(x,y),字符串指針str

{

    unsigned char addr;

    

    //由輸入的顯示坐標(biāo)計(jì)算顯示RAM的地址

    if (y == 0)

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

    else

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

    

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

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

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

    {

        LcdWriteDat(*str);

        str++;

    }

}

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

{

    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>

#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)生總線起始信號(hào)

{

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

    I2C_SCL = 1;

    I2CDelay();

    I2C_SDA = 0; //先拉低SDA

    I2CDelay();

    I2C_SCL = 0; //再拉低SCL

}

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

{

    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,返回值為從機(jī)應(yīng)答位的值

{

    bit 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

    I2CDelay();

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

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

 

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

}

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

{

    bit ack;

 

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

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

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

    

    return ack;

}

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

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

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

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


編輯:admin  最后修改時(shí)間: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號(hào)-4

奈曼旗| 太白县| 沭阳县| 衡山县| 锡林浩特市| 新建县| 南川市| 健康| 云霄县| 合山市| 沙湾县| 额济纳旗| 湖北省| 双流县| 马龙县| 江北区| 三明市| 吉林省| 中西区| 加查县| 宜章县| 巩义市| 乾安县| 绵阳市| 政和县| 大荔县| 吉林市| 名山县| 体育| 襄城县| 龙陵县| 东海县| 灵台县| 柳林县| 金乡县| 林芝县| 达州市| 泸水县| 金沙县| 子洲县| 东乡族自治县|