網(wǎng)上流傳比較多的C51版本是龐波的《25045操作標(biāo)準(zhǔn)子程序集41.c》,但是經(jīng)我實(shí)際使用過(guò)后發(fā)現(xiàn)有一些錯(cuò)誤,如&和&&的區(qū)別及一些邏輯的問(wèn)題。還有總是有人問(wèn)電路的接法,由于X25045的datasheet寫的不清楚,的確很容易弄錯(cuò),這次就把接法的說(shuō)明也貼上來(lái)了。希望能夠?qū)Υ蠹矣袔椭?BR>分為兩個(gè)文件:X25045.h和X25045.C內(nèi)容如下 1、X25045.h
#ifndef __X25045_H__ #define __X25045_H__ /* ;程 序 最 后 修 改 時(shí) 間 2006-2-13 12:11 ;軟 件 標(biāo) 題:25045操作標(biāo)準(zhǔn)子程序集 ;軟 件 說(shuō) 明:X25045已經(jīng)停產(chǎn),替代產(chǎn)品為X5045 ;_________________________________________
;原作者: 龐波 ;程序修改人: 貓賊 ;版本號(hào): ;_________________________________________ ;貓賊:感謝原作者提供參考程序,雖然其中有一些錯(cuò)誤,但是其公開(kāi)源碼的方式值得敬佩。 ;我沒(méi)有測(cè)試連續(xù)讀和連續(xù)寫程序,不知道會(huì)不會(huì)有問(wèn)題,其他的函數(shù)都已經(jīng)正確了。 */ /* 參考電路說(shuō)明:X25045/X5045 pin1-CS :直接接到MCU的IO口 2-SO :直接接到MCU的IO口 3-WP :電阻上拉5V, 個(gè)人感覺(jué)接到MCU的IO口沒(méi)什么意義 4-VSS:GND 5-SI :直接接到MCU的IO口 6-SCK:直接接到MCU的IO口 7-RST:電阻上拉5V,該引腳直接與MCU的RST腳相連,注意該引腳不能接下拉電阻 8-VCC:+5V 應(yīng)用范例: 環(huán)境:AT89C2051,+5V供電,6MHZ晶振頻率 編譯器:偉福,Keil C 1.設(shè)置狀態(tài)寄存器(設(shè)置/開(kāi)啟Watchdog)。狀態(tài)寄存器的值請(qǐng)?jiān)O(shè)置STATUS_REG void main(void){ wrsr_cmd();//復(fù)位時(shí)間位和數(shù)據(jù)保護(hù)位寫入狀態(tài)寄存器 //該語(yǔ)句最好放在起始位置,執(zhí)行完畢后Watchdog就設(shè)置為需要的時(shí)間參數(shù)。 //Watchdog其實(shí)在上電的時(shí)候就已經(jīng)開(kāi)始計(jì)時(shí)了,默認(rèn)的時(shí)間為1.4秒。 //狀態(tài)寄存器值默認(rèn)為00 //注意不要忘記喂狗rst_wdog(); ........ } 2.寫/讀一個(gè)字節(jié)到EEPROM。 void main(void){ .......... byte_write(0x55,0x01);//寫一個(gè)字節(jié)數(shù)據(jù)0x55到的EEPROM,地址0x01 .......... Array_Vector = byte_read(0x01);//讀取0x01地址的內(nèi)容,如果之前沒(méi)有做過(guò)改變,則應(yīng)該為0X55 .......... }
*/
sbit SO =P1^3;/*25045輸出*/ sbit SI =P1^5;/*25045輸入*/ sbit SCK=P1^6;/*25045時(shí)鐘*/ sbit CS =P1^4;/*25045片選*/
#define STATUS_REG 0X00 /* Status register,設(shè)置DOG時(shí)間設(shè)置為1.4秒,無(wú)寫保護(hù) 這是狀態(tài)寄存器的值,他的意義在于第5,第4位為WDI1,WDI0代表DOG的時(shí)間,00為1.4秒,01為600毫秒,10為200毫秒,00為disabled 第3位和第2位為BL1,BL0,是寫保護(hù)設(shè)置位,00為無(wú)保護(hù),01為保護(hù)180-1FF,10為保護(hù)100-1FF,11為保護(hù)000-1FF.第1位為WEL, 當(dāng)他為1時(shí)代表已經(jīng)"寫使能"設(shè)置了,現(xiàn)在可以寫了,只讀位.第0位為WIP,當(dāng)他為1時(shí)代表正在進(jìn)行寫操作,是只讀*/ #define MAX_POLL 0x99 /* Maximum number of polls 最大寫過(guò)程時(shí)間,確定25045的最大的寫入過(guò)程的時(shí)間*/
void wren_cmd(void);/*寫使能子程序*/ void wrdi_cmd(void);/*寫使能復(fù)位*/ void wrsr_cmd(void);/*復(fù)位時(shí)間位和數(shù)據(jù)保護(hù)位寫入狀態(tài)寄存器*/ unsigned char rdsr_cmd(void);/*讀狀態(tài)寄存器*/ void byte_write(unsigned char aa,unsigned int dd);/*字節(jié)寫入,aa為寫入的數(shù)據(jù),dd為寫入的地址*/ unsigned char byte_read(unsigned int dd);/*字節(jié)讀出,dd為讀出的地址,返回讀出的數(shù)據(jù)*/ void page_write(unsigned char aa1,unsigned char aa2,unsigned char aa3,unsigned char aa4,unsigned int dd);/*頁(yè)寫入*/ void sequ_read(void);/*連續(xù)讀出*/ void rst_wdog(void);/*DOG復(fù)位*/
#endif /* __X25045_H__ */
2、X25045.C
#include <stdio.h> #include <ATMEL\AT892051.H> #include "X25045.H" #define uchar unsigned char #define uint unsigned int
uchar code WREN_INST=0X06; /* Write enable latch instruction (WREN)*/ uchar code WRDI_INST=0X04; /* Write disable latch instruction (WRDI)*/ uchar code WRSR_INST=0X01; /* Write status register instruction (WRSR)*/ uchar code RDSR_INST=0X05; /* Read status register instruction (RDSR)*/ uchar code WRITE_INST=0X02; /* Write memory instruction (WRITE)*/ /*寫入25045的先導(dǎo)字,應(yīng)當(dāng)為0000A010,其中的A為寫入25045的高位地址 將此WRITE_INST和寫入高位地址相或后即為正確的寫先導(dǎo)字*/ uchar code READ_INST=0X03; /* Read memory instruction (READ)*/ /*讀出25045的先導(dǎo)字,應(yīng)當(dāng)為0000A011,其中的A為讀出25045的高位地址 將此READ_INST和讀出高位地址相或后即為正確的讀先導(dǎo)字*/ uint code BYTE_ADDR=0X55; /* Memory address for byte mode operations*/ uchar code BYTE_DATA=0X11; /*Data byte for byte write operation*/ uint code PAGE_ADDR=0X1F; /* Memory address for page mode operations*/ /*頁(yè)面寫入的其始地址*/ uchar code PAGE_DATA1=0X22; /* 1st data byte for page write operation*/ uchar code PAGE_DATA2=0X33; /* 2nd data byte for page write operation*/ uchar code PAGE_DATA3=0X44; /* 3rd data byte for page write operation*/
uchar code INIT_STATE=0x09; /* Initialization value for control ports*/ uint code SLIC=0x30; /* Address location of SLIC*/
void outbyt(uchar aa);/*輸出一個(gè)字節(jié)到25045中,不包括先導(dǎo)字等*/ uchar inputbyt();/*由25045輸入一個(gè)字節(jié),不包括先導(dǎo)字等額外的東西*/ void wip_poll(void);/*檢查寫入過(guò)程是否結(jié)束*/
void delay(char n){ char a; for(a=0;a<n;a++) ; } /*25045操作子程序集*/ /*;********************************************************** * ;* Name: WREN_CMD ;* Description: Set write enable latch ;* Function: This routine sends the command to enable writes to the EEPROM memory array or ;* status register ;* Calls: outbyt ;* Input: None ;* Outputs: None ;* Register Usage: A ;************************************************************** */ /*寫使能子程序*/ void wren_cmd(void) { uchar aa; SCK=0;/* Bring SCK low */ CS=0;/* Bring /CS low */ aa=WREN_INST; outbyt(aa);/* Send WREN instruction */ delay(1); SCK=0;/* Bring SCK low */ CS=1;/* Bring /CS high */ }
/*;******************************************************************** * ;* Name: WRDI_CMD ;* Description: Reset write enable latch ;* Function: This routine sends the command to disable writes to the EEPROM memory array or ;* status register ;* Calls: outbyt ;* Input: None ;* Outputs: None ;* Register Usage: A ;******************************************************************** */ /*寫使能復(fù)位子程序*/ void wrdi_cmd(void) { uchar aa; SCK=0;/* Bring SCK low */ CS=0;/* Bring /CS low */ aa=WRDI_INST; outbyt(aa);/* Send WRDI instruction */ delay(1); SCK=0;/* Bring SCK low */ CS=1;/* Bring /CS high */ }
/*;*********************************************************************** * ;* Name: WRSR_CMD ;* Description: Write Status Register ;* Function: This routine sends the command to write the WD0, WD1, BP0 and BP0 EEPROM ;* bits in the status register ;* Calls: outbyt, wip_poll ;* Input: None ;* Outputs: None ;* Register Usage: A ;********************************************************************** */ /*寫狀態(tài)寄存器子程序*/ void wrsr_cmd(void) { uchar aa;
wren_cmd();//寫使能子程序
SCK=0;/* Bring SCK low */ CS=0;/* Bring /CS low */ aa=WRSR_INST; outbyt(aa) ;/* Send WRSR instruction */ aa=STATUS_REG; outbyt(aa);/* Send status register */ delay(1); SCK=0;/* Bring SCK low */ CS=1;/* Bring /CS high */ wip_poll();/* Poll for completion of write cycle */
wrdi_cmd();//寫使能復(fù)位,其實(shí)這句可以省略,每寫一次就自動(dòng)復(fù)位 }
/*;************************************************************************* * ;* Name: RDSR_CMD ;* Description: Read Status Register ;* Function: This routine sends the command to read the status register ;* Calls: outbyt, inputbyt ;* Input: None ;* Outputs: A = status registerXicor Application Note AN21 ;* Register Usage: A ;******************************************************************** */ /*讀狀態(tài)寄存器,讀出的數(shù)據(jù)放入到aa中*/ uchar rdsr_cmd (void) { uchar aa; SCK=0; CS=0; aa=RDSR_INST; outbyt(aa); aa=inputbyt(); SCK=0; CS=1; return aa; }
/*;********************************************************************** * ;* Name: BYTE_WRITE ;* Description: Single Byte Write ;* Function: This routine sends the command to write a single byte to the EEPROM memory array ;* Calls: outbyt, wip_poll ;* Input: None ;* Outputs: None ;* Register Usage: A, B ;*************************************************************** */ /*字節(jié)寫入,aa為寫入的數(shù)據(jù),dd為寫入的地址,對(duì)于25045而言為000-1FF*/ void byte_write(aa,dd) uchar aa; uint dd; { uchar tmp;
wren_cmd();//寫使能子程序
SCK=0; CS=0; if(dd>0xff) tmp = WRITE_INST | 0x08; else tmp = WRITE_INST; outbyt(tmp);/* Send WRITE instruction including MSB of address */ /*將高位地址左移3位與寫入先導(dǎo)字相或,得到正確的先導(dǎo)字寫入25045*/ outbyt((uchar)(dd&0xff)); /*輸出低位地址到25045*/ outbyt(aa); /*寫入數(shù)據(jù)到25045的對(duì)應(yīng)單元*/ SCK=0; CS=1; wip_poll(); /*檢測(cè)是否寫完*/
wrdi_cmd();//寫使能復(fù)位,其實(shí)這句可以省略,每寫一次就自動(dòng)復(fù)位 }
/*;*********************************************************************** * ;* Name: BYTE_READ ;* Description: Single Byte Read ;* Function: This routine sends the command to read a single byte from the EEPROM memory array ;* Calls: outbyt, inputbyt ;* Input: None ;* Outputs: A = read byte ;* Register Usage: A, BXicor Application Note AN21 ;********************************************************************* */ /*字節(jié)讀出,其中dd為讀出的地址,返回的值為讀出的數(shù)據(jù)*/ uchar byte_read(dd) uint dd; { uchar cc,tmp; SCK=0; CS=0; if(dd>0xff) tmp = READ_INST | 0x08; else tmp = READ_INST; outbyt(tmp);/* Send READ_INST instruction including MSB of address */ /*將高位地址左移3位與讀出先導(dǎo)字相或,得到正確的先導(dǎo)字寫入25045*/ outbyt((uchar)(dd&0xff)); /*輸出低位地址到25045*/ cc=inputbyt();/*得到讀出的數(shù)據(jù)*/ SCK=0; CS=1; return cc; }
/*;********************************************************************** * ;* Name: PAGE_WRITE ;* Description: Page Write ;* Function: This routine sends the command to write three consecutive bytes to the EEPROM ;* memory array using page mode ;* Calls: outbyt, wip_poll ;* Input: None ;* Outputs: None ;* Register Usage: A, B ;************************************************************************** */ /*頁(yè)面寫入,其中aa1,aa2,aa3,aa4為需要寫入的4個(gè)數(shù)據(jù)(最大也就只能一次寫入4個(gè)字,dd為寫入的首地址*/ void page_write(aa1,aa2,aa3,aa4,dd) uchar aa1,aa2,aa3,aa4; uint dd; { SCK=0; CS=0; outbyt((((uchar)(dd-0XFF))<<3)|WRITE_INST);// Send WRITE instruction including MSB of address //將高位地址左移3位與寫入先導(dǎo)字相或,得到正確的先導(dǎo)字寫入25045 outbyt((uchar)(dd)); //寫入低位地址到25045 outbyt(aa1); //寫入數(shù)據(jù)1到25045的對(duì)應(yīng)單元 outbyt(aa2); //寫入數(shù)據(jù)2到25045的對(duì)應(yīng)單元 outbyt(aa3); //寫入數(shù)據(jù)3到25045的對(duì)應(yīng)單元 outbyt(aa4); //寫入數(shù)據(jù)4到25045的對(duì)應(yīng)單元 SCK=0; CS=1; wip_poll(); }
/*;********************************************** * ;* Name: SEQU_READ ;* Description: Sequential Read ;* Function: This routine sends the command to read three consecutive bytes from the EEPROM ;* memory array using sequential mode ;* Calls: outbyt, inputbyt ;* Input: None ;* Outputs: A = last byte read ;* Register Usage: A, B ;************************************************************ */ /*連續(xù)讀出,由于函數(shù)的返回值只能為1個(gè),對(duì)于連續(xù)讀出的數(shù)據(jù)只能使用指針作為函數(shù)的返回值才能做到返回一系列的數(shù)組*/ //sequ_read: unsigned int *page_read(n,dd) uchar n;//n是希望讀出的數(shù)據(jù)的個(gè)數(shù),n<=11 unsigned int dd;//dd是讀出數(shù)據(jù)的首地址 { uchar i; uchar pp[10]; unsigned int *pt=pp; SCK=0; CS=0; outbyt((((uchar)(dd-0XFF))<<3)|READ_INST); for (i=0;i<n;i++) { pp[i]=inputbyt(); } return (pt); } /*調(diào)用的方法如下 unsigned int *p; p=page_read(4,100); a=*(p) b=*(p+1) c=*(p+2) d=*(p+3) //abcd中存放25045中由100地址開(kāi)始的4個(gè)數(shù)據(jù) */ /* Send WRITE MOV DPTR, #PAGE_ADDR ; Set address of 1st byte to be read clr sck ; Bring SCK low clr cs ; Bring /CS low MOV A, #READ_INST MOV B, DPH MOV C, B.0 MOV ACC.3, C lcall outbyt ; Send READ instruction with MSB of address MOV A, DPL lcall outbyt ; Send low order address byte lcall inputbyt ; Read 1st data byte lcall inputbyt ; Read 2nd data byte lcall inputbyt ; Read 3rd data byte clr sck ; Bring SCK low setb cs ; Bring /CS high ret*/
/*;********************************************************************* * ;* Name: RST_WDOG ;* Description: Reset Watchdog Timer ;* Function: This routine resets the watchdog timer without sending a command ;* Calls: None ;* Input: None ;* Outputs: None ;* Register Usage: None ;*********************************************************************** */ /*復(fù)位DOG*/ void rst_wdog (void) { CS=0; CS=1; }
/*;************************************************************************ * ;* Name: WIP_POLL ;* Description: Write-In-Progress Polling ;* Function: This routine polls for completion of a nonvolatile write cycle by examining the ;* WIP bit of the status register ;* Calls: rdsr_cmdXicor Application Note AN21 ;* Input: None ;* Outputs: None ;* Register Usage: R1, A ;************************************************************************ */ /*檢測(cè)寫入的過(guò)程是否結(jié)束*/ void wip_poll(void) { uchar aa; uchar my_flag; for (aa=0;aa<MAX_POLL;aa++) { my_flag=rdsr_cmd(); if ((my_flag&0x01)==0) {aa=MAX_POLL;}/*判斷是否WIP=0,即判斷是否寫入過(guò)程已經(jīng)結(jié)束,若結(jié)束就跳出,否則繼續(xù)等待直到達(dá)到最大記數(shù)值*/ } //aa=1; //while(aa){ // my_flag=rdsr_cmd(); // if ((my_flag&0x01)==0){ // aa=0;/*判斷是否WIP=0,即判斷是否寫入過(guò)程已經(jīng)結(jié)束,若結(jié)束就跳出,否則繼續(xù)等待直到達(dá)到最大記數(shù)值*/ // } //} aa = 0; }
/*;************************************************************************** * ;* Name: OUTBYT ;* Description: Sends byte to EEPROM ;* Function: This routine shifts out a byte, starting with the MSB, to the EEPROM ;* Calls: None ;* Input: A = byte to be sent ;* Outputs: None ;* Register Usage: R0, A ;*********************************************************************** */ /*輸出一個(gè)數(shù)據(jù)到25045,此數(shù)據(jù)可能為地址,先導(dǎo)字,寫入的數(shù)據(jù)等*/ void outbyt(aa) uchar aa; { uchar my_flag1,my_flag2,i; my_flag1=aa; for (i=0;i<8;i++) { my_flag2=my_flag1&0x80 ; SI=my_flag2>>7; delay(1); SCK=0; SCK=1; delay(3); my_flag1 <<= 1 ; } SI=0;/*使SI處于確定的狀態(tài)*/ }
/*;*************************************************************************** * ;* Name: INPUTBYT ;* Description: Recieves byte from EEPROM ;* Function: This routine recieves a byte, MSB first, from the EEPROM ;* Calls: None ;* Input: None ;* Outputs: A = recieved byte ;* Register Usage: R0, A ;********************************************************************** */ /*得到一個(gè)數(shù)據(jù),此數(shù)據(jù)可能為狀態(tài)寄存器數(shù)據(jù),讀出的單元數(shù)據(jù)等*/ uchar inputbyt(void) { uchar aa,my_flag; char i; aa = 0; for (i=7;i>=0;i--) { SCK=0; //delay(1); my_flag=SO; SCK=1;
//delay(1); my_flag <<= i; aa |= my_flag ; my_flag=0x00; } return aa; }
|
|