大家好,通過前一期的學習,我們已經對ICD2 仿真燒寫器和增強型PIC 實驗板的使用方法及學習方式有所了解與熟悉,學會了如何用單片機來控制發光管、繼電器、蜂鳴器、按鍵、數碼管、RS232 串口、步進電機、溫度傳感器等資源,體會到了學習板的易用性與易學性,看了前幾期實例,當你實驗成功后一定很興奮,很有成就感吧!現在我們就趁熱打鐵,再向上跨一步,一起來學習一下I2C 總線的工作原理及使用方法,這樣我們可以將一些我們要保存的數據存儲到I2C總線的非易失存儲器中,實現斷電保持的功能,比如:你設置了一個密碼,但不至于這個設備斷過電以后就要重新設置過,我們可以將密碼數據寫在非易失存儲器里面,還有如汽車的量程表的讀數是不斷累計的,可以通過不斷訪問I2C 存儲器實現。
一、I2C總線特點
I2C 總線是主從結構,單片機是主器件,存儲器是從器件。一條總線可以帶多個從器件( 也可以有多主結構),I2C 總線的SDA 和SCL 是雙向的,開路門結構,通過上拉電阻接正電源。進行數據傳輸時,SDA 線上的數據必須在時鐘的高電平周期保持穩定。數據線的高或低電平狀態只有在SCL 線的時鐘信號是低電平時才能改變,如圖1 所示。
圖1 數據位的有效性規定
在SCL 線是高電平時,SDA 線從高電平向低電平切換表示起始條件;當SCL 是高電平時SDA 線由低電平向高電平切換表示停止條件如圖2 所示。
圖2 起始和停止信號
發送到SDA 線上的每個字節必須為8 位。
可以由高位到低位傳輸多個字節。每個字節后必須跟一個響應位(ACK)。響應時鐘脈沖由主機產生。主機釋放SDA 線從機將SDA 線拉低,并在時鐘脈沖的高電平期間保持穩定。如圖3 示。當主機接受數據時,它收到最后一個數據字節后,必須向從機發出一個結束傳送的信號。這個信號是由主機對從機的“非應答”來實現的。然后,從機釋放SDA 線,以允許主機產生終止或重復起始信號。
圖3 字節格式與應答
二、數據幀格式
(1)主機向從機發送數據,數據的傳送方向在傳輸過程中不改變,如圖4 所示。
圖4 主機向從機發送數據
注:陰影部分:表示主機向從機發送數據;無陰影部分:表示主機向從機讀取數據。
A:表示應答;:表示非應答。S:起始信號;P :終止信號。
(2)主機在第一個字節后,立即向從機讀取數據,如圖5 所示。
圖5 主機在第一個字節后立即讀從機
(3)復合格式,如圖6 所示。傳輸改變方向的時候,起始條件和從機地址都會被重復,但R/ W-位取反。如果主機接收器發送一個停止或重復起始信號,它之前應該發送了一個不響應信號()。
圖6 復合格式
由以上格式可見,無論哪種傳輸方式,起始信號、終止信號和地址均由主機發出(圖中陰影部分),數據字節的傳送方向則由尋址字節中的方向位規定,每個字節的傳送都必須有應答位(A 或)。
下面通過24C02 實例在增強型PIC 實驗板上編程,其硬件原理圖如圖7 所示,U7 為實驗板上24C02 芯片,SDA 與單片機的RB5 口相連,SCL 與單片機RB4 相連,七段數碼管D5、D7、D8 組成了顯示單元,字形碼的數據通過RC 口送入,各數碼管的顯示片選信號分別不同的RA 口進行控制。
圖7 讀/ 寫AT24C 系列存儲器原理圖
在MPLab IDE 軟件中新建工程,加入源程序代碼,同時進行芯片型號的選擇和配置位的設置,我們實驗所用的芯片型號為PIC16F877A。
編寫的程序代碼如下,其中程序流程圖如圖8 所示。
三、軟件流程圖
圖8 I2C 總線讀/ 寫數據流程圖
四、軟件代碼
/**********/
/* 目標器件:PIC16F877A */
/* 晶振:4.0MHZ */
/* 編譯環境:MPLAB V7.51 */
/**********/
/**********
包含頭文件
**********/
#include
/**********
數據定義
**********/
#define address 0xa
#define nop() asm("nop")
#define OP_READ 0xa1
// 器件地址以及讀取操作
#define OP_WRITE 0xa0
// 器件地址以及寫入操作
/**********
端口定義
**********/
#define SCL RB4
#define SDA RB5
#define SCLIO TRISB4
#define SDAIO TRISB5
/**********
共陰LED 段碼表
**********/
const char table[]={0xC0,0xF9,0xA4,0x
B0,0x99,0x92,0x82,0xF8,0x80,0x90,0x88,0x
83,0xC6,0xA1,0x86,0x8E};
/**********
函數功能: 延時子程序
**********/
void delay()
{
int i;
for(i=0;i<100;i++)
{;}
}
/**********
函數功能: 開始信號
**********/
void start()
{
SDA=1;
nop();
SCL=1;
nop();nop();nop();nop();nop();
SDA=0;
nop();nop();nop();nop();nop();
SCL=0;
nop();nop();
}
/**********
函數功能: 停止信號
**********/
void stop()
{
SDA=0;
nop();
SCL=1;
nop();nop();nop();nop();nop();
SDA=1;
nop();nop();nop();nop();
}
/**********
函數功能: 讀取數據
出口參數:read_data
**********/
unsigned char shin()
{
unsigned char i,read_data;
for(i=0;i<8;i++)
{ nop();nop();nop();
SCL=1;
nop();nop();
read_data《=1;
if(SDA == 1)
read_data=read_data+1;
nop();
SCL=0;
}
return(read_data);
}
/**********
函數功能: 向EEPROM 寫數據
入口參數:write_data
出口參數:ack_bit
**********/
bit shout(unsigned char write_data)
{
unsigned char i;
unsigned char ack_bit;
for(i = 0; i < 8; i++)
{
if(write_data&0x80)
SDA=1;
else
SDA=0;
nop();
SCL = 1;
nop();nop();nop();nop();nop();
SCL = 0;
nop();
write_data 《= 1;
}
nop();nop();
SDA = 1;
nop();nop();
SCL = 1;
nop();nop();nop();
ack_bit = SDA; // 讀取應答
SCL = 0;
nop();nop();
return ack_bit;
// 返回AT24Cxx 應答位
}
/**********
函數功能: 向指定地址寫數據
入口參數:addr,write_data
**********/
void write_byte(unsigned char addr,
unsigned char write_data)
{
start();
shout(OP_WRITE);
shout(addr);
SDAIO = 0;
// 在寫入數據前SDA 應設置為輸出
shout(write_data);
stop();
delay();
}
/**********
函數功能: 向指定地址讀數據
入口參數:random_addr
出口參數:read_data
**********/
unsigned char read_random(unsigned
char random_addr)
{ unsigned char read_data;
start();
shout(OP_WRITE);
shout(random_addr);
start();
shout(OP_READ);
SDAIO = 1;
// 讀取數據前SDA 應設置為輸入
read_data = shin();
stop();
return(read_data);
}
/**********
函數功能: 顯示子程序
入口參數:k
**********/
void display(unsigned char k)
{
TRISA=0X00;
// 設置A 口全為輸出
PORTC=table[k/1000];
// 顯示千位
PORTA=0xEF;
delay();
PORTC=table[k/100%10];
// 顯示百位
PORTA=0xDF;
delay();
PORTC = table [k/ 10%10] ;
// 顯示十位
PORTA=0xFB;
delay();
PORTC=table[k%10]; // 顯示個位
PORTA=0xF7;
delay();
}
/**********
函數功能: 主程序
**********/
void main()
{
unsigned char eepromdata;
TRISB=0X00;
OPTION&=~(1《7);
// 設置RB 口內部上拉電阻有效
TRISC=0X00;
PORTB=0X00;
PORTC=0xff;
TRISA=0X00;
eepromdata=0;
write_byte(0x01,0x55);
// 向0x01 地址寫入0x55(85) 的數據
delay();
write_byte(0x02,0xaa);
// 向0x02 地址寫入0xAA(170) 的數據
delay();
eepromdata=read_random(0x02);
// 讀取其中一個地址內的數據來驗證
while(1)
{
display(eepromdata);
}
}
編好程序后將編譯好的HEX 碼通過ICD2仿真燒寫器燒入單片機芯片,上電運行,主程序中在0x01 地址寫入了“0x55”, 在0x02 地址寫入了“0xaa”,然后在while 循環中讀出0x02地址的值,也就是我們之前寫入的“0x55”,讀出后顯示在數碼管上,我們可以看到數碼管顯示“170”,即“0xaa”相應的十進制數。
作為初學者的讀者一定對有些語句會有點疑問,可以看程序中的注釋部份,24c 系列IC 數據手冊和源程序相結合來進行分析。
-
單片機
+關注
關注
6037文章
44565瀏覽量
635984 -
存儲器
+關注
關注
38文章
7502瀏覽量
163932 -
I2C
+關注
關注
28文章
1489瀏覽量
123908
原文標題:PIC單片機之I2C總線
文章出處:【微信號:changxuemcu,微信公眾號:暢學單片機】歡迎添加關注!文章轉載請注明出處。
發布評論請先 登錄
相關推薦
評論