做任務(wù)賺錢的網(wǎng)站起什么名字好網(wǎng)站建設(shè)百度推廣
一、AT24C02
1.AT24C02介紹
·AT24C02是一種可以實現(xiàn)掉電不丟失的存儲器,可用于保存單片機運行時想要永久保存的數(shù)據(jù)信息
· 存儲介質(zhì):E2PROM
· 通訊接口:12C總線
· 容量:256字節(jié)
2.引腳即應(yīng)用電路
本開發(fā)板AT24C02原理圖
12C地址全接地,即全為0
WE接地,沒有寫使能
SCL接P21 SDA接P20
沒有外接上拉電阻是因為單片機的每個10口都接了上拉電阻
AT24C02內(nèi)部結(jié)構(gòu)框圖如圖所示
簡單來說,就是通過SCL和SDA接口獲得數(shù)據(jù),經(jīng)過一定的邏輯,數(shù)據(jù)存儲到EEPROM(通過X和Y來控制數(shù)據(jù)的存儲位置),并且可以通過一定的邏輯,將數(shù)據(jù)輸出出來
3.I2C總線
1.I2C總線介紹
· 12C總線(Inter IC BUS)是由Philips公司開發(fā)的一種通用數(shù)據(jù)總線
· 兩根通信線:SCL(Serial Clock)、SDA (Serial Data)
· 同步、半雙工,帶數(shù)據(jù)應(yīng)答
· 通用的I2C總線,可以使各種設(shè)備的通信標(biāo)準(zhǔn)統(tǒng)一,對于廠家來說,使用成熟的方案可以縮短芯片設(shè)計周期、提高穩(wěn)定性,對于應(yīng)用者來說,使用通用的通信協(xié)議可以避免學(xué)習(xí)各種各樣的自定義協(xié)議,降低了學(xué)習(xí)和應(yīng)用的難度
2.I2C電路規(guī)范
·所有I2C設(shè)備的SCL連在一起,SDA連在一起
· 設(shè)備的SCL和SDA均要配置成開漏輸出模式
· SCL和SDA各添加一個上拉電阻,阻值一般為4.7KΩ左右
· 開漏輸出和上拉電阻的共同作用實現(xiàn)了“線與”的功能,此設(shè)計主要是為了解決多機通信互相干擾的問題
本單片機所有IO口都是弱上拉模式,如圖
開關(guān)閉合,則輸出0
開關(guān)打開,則輸出1
高電平驅(qū)動能力弱,低電平驅(qū)動能力強
開漏模式如圖,沒有接上拉電阻
開關(guān)閉合,輸出0
開關(guān)斷開,引腳呈浮空狀態(tài)(什么都沒接),電平不穩(wěn)定
3.I2C時序結(jié)構(gòu)
一幀標(biāo)準(zhǔn)的寫數(shù)據(jù)幀
一幀寫數(shù)據(jù)是由時鐘線和數(shù)據(jù)線共同作用的,在同一時間,要么在發(fā)送信息,要么在讀取信息
當(dāng)處于空閑狀態(tài)時,數(shù)據(jù)線和時鐘線都處于高電平狀態(tài)
當(dāng)開始傳遞信息時,比如傳遞第一位起始位。此時必須要在時鐘信號為高電平期間,數(shù)據(jù)信號完成由高到低的跳變,也就是下降沿
接下來是7位設(shè)備地址碼,因為每一個從設(shè)備的地址碼都是唯一的,為了區(qū)別要和哪一個從設(shè)備通訊,需要先發(fā)送出7位地址碼,7位不同的0和1的排列組合,一共表可以表示128種結(jié)果
當(dāng)時鐘線為高電平時,數(shù)據(jù)線上的數(shù)據(jù)必須保持穩(wěn)定(時鐘線為高時,數(shù)據(jù)線上的數(shù)據(jù)始終為高),這樣就完成了邏輯1的傳輸。如果數(shù)據(jù)線上始終是低電平,則表示邏輯0
若發(fā)送1010000,且24c02的地址為1010000,此時就是單片機和24c02通訊
接下來是讀寫數(shù)據(jù)位,如果要寫數(shù)據(jù)幀,就給它置0,讀數(shù)據(jù)幀置1
再下面一位是應(yīng)答信號,這個信號是由從機發(fā)送給主機的,如果從機收到了之前的信息,它會回復(fù)0,沒有收到或者(主機)讀取接收完成回復(fù)1
下面的8位是設(shè)備寄存器的地址,因為是給24C02通訊的,24C02是一個存儲器,它可以存儲256個字節(jié),而發(fā)送的8位寄存器地址正好可以訪問這256個字節(jié)
比如寫的寄存器地址是0x01(二進制為0000 0001),它就會往0x01里寫入數(shù)據(jù)
然后單片機需要存儲器返回一個應(yīng)答信號
接下來的8位是給這個存儲器的寄存器要寫入的數(shù)據(jù)
比如發(fā)送0000 1111,則這8個位就會存儲這8位信息
即使后來斷電依舊保存,發(fā)送寫數(shù)據(jù)之后,需要再給主機發(fā)送應(yīng)答信號0,告訴主機寫入成功
最后再寫入停止位,它和起始位相反,是當(dāng)時鐘信號為高時,數(shù)據(jù)信號需要由低到高的跳變
這樣一個標(biāo)準(zhǔn)的寫數(shù)據(jù)幀就完成了
以下是一個標(biāo)準(zhǔn)的讀數(shù)據(jù)幀
首先寫入設(shè)備地址,然后是寫數(shù)據(jù)。接下來寫的是寄存器的地址,在收到從機的應(yīng)答信號之后,主機需要再發(fā)送一個起始信號。然后需要再發(fā)送一遍設(shè)備的地址,然后才能發(fā)送讀數(shù)據(jù)。接下來存儲器就會把寄存器里面的數(shù)據(jù)發(fā)送給單片機,這樣就完成了一幀數(shù)據(jù)的讀取
二、AT24C02數(shù)據(jù)存儲
I2C.c
#include <REGX52.H>sbit I2C_SCL=P2^1;
sbit I2C_SDA=P2^0;/*** @brief I2C開始* @param 無* @retval 無*/
void I2C_Start(void)
{I2C_SDA=1;I2C_SCL=1;I2C_SDA=0;I2C_SCL=0;
}/*** @brief I2C停止* @param 無* @retval 無*/
void I2C_Stop(void)
{I2C_SDA=0;I2C_SCL=1;I2C_SDA=1;
}/*** @brief I2C發(fā)送一個字節(jié)* @param Byte 要發(fā)送的字節(jié)* @retval 無*/
void I2C_SendByte(unsigned char Byte)
{unsigned char i;for(i=0;i<8;i++){I2C_SDA=Byte&(0x80>>i);I2C_SCL=1;I2C_SCL=0;}
}/*** @brief I2C接收一個字節(jié)* @param 無* @retval 接收到的一個字節(jié)數(shù)據(jù)*/
unsigned char I2C_ReceiveByte(void)
{unsigned char i,Byte=0x00;I2C_SDA=1;for(i=0;i<8;i++){I2C_SCL=1;if(I2C_SDA){Byte|=(0x80>>i);}I2C_SCL=0;}return Byte;
}/*** @brief I2C發(fā)送應(yīng)答* @param AckBit 應(yīng)答位,0為應(yīng)答,1為非應(yīng)答* @retval 無*/
void I2C_SendAck(unsigned char AckBit)
{I2C_SDA=AckBit;I2C_SCL=1;I2C_SCL=0;
}/*** @brief I2C接收應(yīng)答位* @param 無* @retval 接收到的應(yīng)答位,0為應(yīng)答,1為非應(yīng)答*/
unsigned char I2C_ReceiveAck(void)
{unsigned char AckBit;I2C_SDA=1;I2C_SCL=1;AckBit=I2C_SDA;I2C_SCL=0;return AckBit;
}
I2C.h
#ifndef __I2C_H__
#define __I2C_H__void I2C_Start(void);
void I2C_Stop(void);
void I2C_SendByte(unsigned char Byte);
unsigned char I2C_ReceiveByte(void);
void I2C_SendAck(unsigned char AckBit);
unsigned char I2C_ReceiveAck(void);#endif
AT24C02.c
#include <REGX52.H>
#include "I2C.h"#define AT24C02_ADDRESS 0xA0/*** @brief AT24C02寫入一個字節(jié)* @param WordAddress 要寫入字節(jié)的地址* @param Data 要寫入的數(shù)據(jù)* @retval 無*/
void AT24C02_WriteByte(unsigned char WordAddress,Data)
{I2C_Start();I2C_SendByte(AT24C02_ADDRESS);I2C_ReceiveAck();I2C_SendByte(WordAddress);I2C_ReceiveAck();I2C_SendByte(Data);I2C_ReceiveAck();I2C_Stop();
}/*** @brief AT24C02讀取一個字節(jié)* @param WordAddress 要讀出字節(jié)的地址* @retval 讀出的數(shù)據(jù)*/
unsigned char AT24C02_ReadByte(unsigned char WordAddress)
{unsigned char Data;I2C_Start();I2C_SendByte(AT24C02_ADDRESS);I2C_ReceiveAck();I2C_SendByte(WordAddress);I2C_ReceiveAck();I2C_Start();I2C_SendByte(AT24C02_ADDRESS|0x01);I2C_ReceiveAck();Data=I2C_ReceiveByte();I2C_SendAck(1);I2C_Stop();return Data;
}
AT24C02.h
#ifndef __AT24C02_H__
#define __AT24C02_H__void AT24C02_WriteByte(unsigned char WordAddress,Data);
unsigned char AT24C02_ReadByte(unsigned char WordAddress);#endif
main.c
#include <REGX52.H>
#include "LCD1602.h"
#include "Key.h"
#include "AT24C02.h"
#include "Delay.h"unsigned char KeyNum;
unsigned int Num;void main()
{LCD_Init();LCD_ShowNum(1,1,Num,5);while(1){KeyNum=Key();if(KeyNum==1) //K1按鍵,Num自增{Num++;LCD_ShowNum(1,1,Num,5);}if(KeyNum==2) //K2按鍵,Num自減{Num--;LCD_ShowNum(1,1,Num,5);}if(KeyNum==3) //K3按鍵,向AT24C02寫入數(shù)據(jù){AT24C02_WriteByte(0,Num%256);Delay(5);AT24C02_WriteByte(1,Num/256);Delay(5);LCD_ShowString(2,1,"Write OK");Delay(1000);LCD_ShowString(2,1," ");}if(KeyNum==4) //K4按鍵,從AT24C02讀取數(shù)據(jù){Num=AT24C02_ReadByte(0);Num|=AT24C02_ReadByte(1)<<8;LCD_ShowNum(1,1,Num,5);LCD_ShowString(2,1,"Read OK ");Delay(1000);LCD_ShowString(2,1," ");}}
}