51接EEPROM用24C08
51關掉資料就不見了啊,用一顆24C08的EEPROM來記錄要儲存的變數,下次開機再讀出來就行了
執行影片
接法
電路圖
點圖可以放大
-------------------------------------------------------------------------------------
動作描述(用程式碼讓SDA程SCL輸出動作,讓EEPROM知道我們要作什麼事
void Start()
{
SDA=1;
SCL=1;
SDA=0;
SCL=0;
}
void Stop()
{
SCL=0;
SDA=0;
SCL=1;
SDA=1;
}
------------------------------------------------------------------------------------------------------------------------
void NoAck()
{
SDA=1;
SCL=1;
SCL=0;
}
bit TestAck()
{
bit ErrorBit;
SDA=1;
SCL=1;
ErrorBit=SDA;
SCL=0;
return(ErrorBit);
}
------------------------------------------------------------------------------------------------------------------------------------
1.寫入資料到EEPROM上(應用上面的動作描述)
void Write8Bit(uchar input)
{
uchar temp;
for(temp=8;temp!=0;temp--)
{
SDA=(bit)(input&0x80);
SCL=1;
SCL=0;
input=input<<1;
}
}
---------------------------------------------------------------------------------------------------------------------------------
2.從EEPROM讀出資料(應用上面的動作描述)
uchar Read8Bit()
{
uchar temp,rbyte=0;
for(temp=8;temp!=0;temp--)
{
SCL=1;
rbyte=rbyte<<1;
rbyte=rbyte|((uchar)(SDA));
SCL=0;
}
return(rbyte);
}
----------------------------------------------------------------------------------------------------------------------------
3.此程式寫法與硬體特性說明
如DATASHEET所示
24C08一次只能讀寫8BIT
若要存取16BIT資料,則要分成2次寫,2次讀
讀寫16BIT資料的示意圖
點圖可以放大
先將要寫入的資料777寫入高八位(Data Address 0~7)
777/256 =3(取商數)
再將777的低八位寫入(Data Address 8~15)
777/256 = 9(取餘數)
讀取的時候一樣,先讀出高八位(Data Address 0~7)
3 X 256 = 768
再讀出低八位(Data Address 8~15)
9
2者相加 768+9 =777
===================完整程式碼==========================================
/******************************************
使用8051的P1_0接24C08的SCL腳.P1_1接SDA腳
做資料儲存的動作
MCU:AT89S51
石英:12MHZ
EEPROM:I2C 24C08
SCL跟SDA的上拉電阻:10K
函數指令及用法:
Write24c08(8BIT長度資料,寫入地址)
Write24c08_uint(16BIT長度資料,寫入地址)
uchar Read24c08(讀出地址)=>會回傳8BIT長度資料
uint Read24c08_uint(讀出地址)=>會回傳16BIT長度資料
*******************************************/
#include<regx51.h>
#define uchar unsigned char //unsigend簡化成u
#define uint unsigned int //unsigend簡化成u
#define ulong unsigned long //unsigend簡化成u
#define WriteDeviceAddress 0xa0 //定格式操作指令不可改
#define ReadDviceAddress 0xa1 //定格式操作指令不可改
sbit SCL=P1^0; //定義腳位,隨電路圖改變
sbit SDA=P1^1; //定義腳位,隨電路圖改變
/*延遲副程式*/
void DelayMs(uint number)
{
uchar temp;
for(;number!=0;number--)
{
for(temp=112;temp!=0;temp--) ;
}
}
/*********************************
以下動作是照DATASHEET做的勿亂改
**********************************/
void Start()
{
SDA=1;
SCL=1;
SDA=0;
SCL=0;
}
void Stop()
{
SCL=0;
SDA=0;
SCL=1;
SDA=1;
}
void NoAck()
{
SDA=1;
SCL=1;
SCL=0;
}
bit TestAck()
{
bit ErrorBit;
SDA=1;
SCL=1;
ErrorBit=SDA;
SCL=0;
return(ErrorBit);
}
/*********************************
以上動作是照DATASHEET做的勿亂改
**********************************/
/*寫DATA的動作*/
void Write8Bit(uchar input)
{
uchar temp;
for(temp=8;temp!=0;temp--)
{
SDA=(bit)(input&0x80);
SCL=1;
SCL=0;
input=input<<1;
}
}
/*寫8位元函數*/
void Write24c08(uchar ch,uchar address)
{
EA=0;
Start();
Write8Bit(WriteDeviceAddress);
TestAck();
Write8Bit(address);
TestAck();
Write8Bit(ch);
TestAck();
Stop();
DelayMs(10);
EA=1;
}
/*寫16位元函數*/
void Write24c08_uint(uint dat,uchar address)
{
uchar hi8,lo8;
hi8=dat/256;
lo8=dat%256;
Write24c08(hi8,address);
DelayMs(10); //很重要!2次寫入動作不延時則會寫入失敗!*(接不同石英可能須要微調)*
Write24c08(lo8,address+8);
}
/*讀DATA的動作*/
uchar Read8Bit()
{
uchar temp,rbyte=0;
for(temp=8;temp!=0;temp--)
{
SCL=1;
rbyte=rbyte<<1;
rbyte=rbyte|((uchar)(SDA));
SCL=0;
}
return(rbyte);
}
/*讀8位元函數*/
uchar Read24c08(uchar address)
{
uchar ch;
Start();
Write8Bit(WriteDeviceAddress);
TestAck();
Write8Bit(address);
TestAck();
Start();
Write8Bit(ReadDviceAddress);
TestAck();
ch=Read8Bit();
NoAck();
Stop();
return(ch);
}
/*讀16位元函數*/
uint Read24c08_uint(uchar address)
{
uint dat;
uchar hi8,lo8;
hi8=Read24c08(address);
lo8=Read24c08(address+8);
dat=(256*hi8)+lo8;
return(dat);
}
code seven_seg[10]={0x00,0x08,0x10,0x18,0x20,0x28,0x30,0x38,0x40,0x48}; //74LS47編碼IC對應0~9
code scan[3]={0x06,0x05,0x03}; //掃描碼FOR 74LS47 S1 ,S2 ,S3
unsigned char counter[3]={0,0,0}; //存放十位數,個位數,小數點第一位
unsigned char i;
int steps; //記錄encoder的步數
unsigned int temp; //給計時器工作用
void timer0_initialize(void) //初始化計時器0
{
EA=0; //關閉中斷功能(所有中斷副程式失效)
ET0=1; //開啟計時器0中斷功能
EX0=1; /* 設定接受INT0的中斷 */
EX1=1; /* 設定接受INT1的中斷 */
IT0=1; //設INT0為負緣觸發*(避免一次訊號累加,造成錯步)*
IT1=1; //設INT1為負緣觸發*(避免一次訊號累加,造成錯步)*
PX0=1; //INT0中斷優先處理
PX1=1; //INT0中斷優先處理
PT0=0; //timer0中斷延後處理*(避免遺漏encoder的訊號)*
TMOD|=0x01; //設定計時器模式
temp=65536-8333; //設定中斷一次的時間
TH0=temp/256; //填入高八位
TL0=temp%256; //填入低八位
TR0=1; //啟動計時器
EA=1; //開啟中斷功能(中斷副程式可以執行)
}
void timer0_isr(void) interrupt TF0_VECTOR using 1 //計時器中斷副程式,計時器中斷一次即執行*(此程式中每秒中斷120次)*
{
TR0=0; //停止計時器
TH0=temp/256; //重新埴入高八位
TL0=temp%256; //重新埴入低八位
TR0=1; //啟動計時器
counter[0]=steps%10; //取steps的小數點第一位
counter[1]=steps%100/10; //取steps的個位數
counter[2]=steps/100; //取steps的十位數
P2=scan[i]; //先上掃描碼導通小數點第一位的七段
P2|=seven_seg[counter[i]]; //將小數點第一位的七段碼OR進P2,以免破壞掃描碼
if(i==1)P2_7=0; //掃描到個位數七段加上小數點
else P2_7=1; //若不是個位數不用加小數點
i++; //導通下一顆七段
if(i>2)i=0; //掃描到十位數後重新從小數第一位開始掃描
}
static void xint0_isr(void) interrupt IE0_VECTOR //INT0中斷副程式
{
if(steps<1000)steps++; //ENCODER步數+1
}
static void xint1_isr(void) interrupt IE1_VECTOR //INT1中斷副程式
{
if(steps>0)steps--; //ENCODER步數-1
}
void main(void) //主程式
{
timer0_initialize(); //呼叫一次計時器初始化
while(1)
{
/*測試程序*/
if(P1_2==0)
{
Write24c08_uint(steps,0);
}
if(P1_3==0)
{
steps=Read24c08_uint(0) ;
}
/*測試OK*/
}
}