89S52是典型的增强51型芯片,有丰富的资源且便宜易用,但内置RAM数据会在掉电后消失,为了保存数据在掉电后仍可使用,就得使用AT24C08一类的EEPROM外置芯片。AT24C08使用的是I2C(IIC)总线进行通信,而89S52并不原生支持此协议,故必须使用单片机模拟I2C协议。
I2C的原理及AT24C08的连接及使用方法,可以参考这个比较好的课件(下载),说得很详细。
下面是我的源码:(下载)
这个源码参考了网上很多的代码,其实都大同小异,
I2C的关键在于开始信号、结束信号、传1、传0、应答、非应答、检查应答、传字节、读写数据这些东西,详细课件有写、下面留点笔记、写上要留意的内容。
Point 1:其实I2C的信号也就只有四个:“开始”“结束”“传0”“传1”。应答实际就是传1,非应答实际就是传0,传字节就是在传8bit的0 or 1,检查应答就是在传的时候同时检查SDA,读写数据就是一系例的开始结束应答的组合。所以应答时我用checkACK而并非用ACK或NOACK函数,因为ACK或NOACK实质就是checkACK,不过checkACK多了一步就是检查应答,也就两三个指令罢了。
Point 2:某N人说,如果I2C出错,很大可能是出在延时上,我没有实现正常的块读取,因为我老是读不出来,24C不应答我的信号,只能读一个,于是我只好单字节读,具体原因不明,那部份代码已经注释起来了。
Point 3:这个程序实现了24C的单字节和多字节的读写。上网的例程虽然多但千奇百怪不知应该信哪个,不过这几个函数写下来后我个人觉得感觉还不错,简炼又实用。
Point 4:程序用单字节记录关机次数,够10个重来一遍。主功能很简单,没有用尽所有函数,所以直接编译有WARNING,大家可以直接把代码修改设置后用到项目中去。
#include #include #define uchar unsigned char #define uint unsigned int #define WRITE_SIGN 0xA0 #define READ_SIGN 0xA1 #define ACK 1 #define NOACK 0 #define _delay(); {_nop_();_nop_();_nop_();_nop_();} //延时约5us #define _delayMicro(); {_nop_();} sbit LED0=P3^0; sbit LED1=P3^1; sbit LED2=P3^2; sbit LED3=P3^3; sbit LED4=P3^4; sbit LED5=P3^5; sbit LED6=P3^6; sbit LED7=P3^7; sbit SDA = P2^3 ; //数据总线 sbit SCL = P2^2 ;//时钟总线 uchar code str[] = { 1,2,3,4,5 }; uchar read_buffer[] = {0x10,0x20,0x30,0x40,0x50}; sbit P37=P3^7; sbit P10=P1^0; uchar code table[]={0xC0,0xF9,0xA4,0xB0,0x99,0x92,0x82,0xf8,0x80,0x90,0x88,0x83,0xC6,0xA1,0x86,0x8E}; uchar num = 0; //延时N ms void _delayMs(uint ms){ uchar i; while(ms--){ for(i=0;i<120;i++) _nop_(); } } void led(uchar byte){ LED7=0; P0=table[byte]; } //起始信号 void start(void){ SDA = 1; SCL = 1; _delay(); SDA = 0; _delay(); SCL = 0; _delay(); } //结束信号 void stop(){ SDA = 0; SCL = 1; _delay(); SDA = 1; _delay(); SCL = 0; _delay(); } //检查应答 应答则返回 Ture bit checkAck(bit b){ SDA = b; _delay(); SCL = 1; _delay(); F0 = ~SDA; //如果SDA被从机拉低 则为收到应答 SCL = 0; return F0; } //接收 一字节数据 uchar receiveByte(){ uchar receive_data,i; for( i=0; i<8; i++ ){ SCL = 1; receive_data = ( receive_data << 1 ) | SDA; //从高位开始开取得数据位,然后左移 SCL = 0; } return receive_data; } //发送 一字节数据 void sendByte(uchar send_data){ uchar i; for( i=0; i<8; i++ ){ SDA = (bit)( send_data & 0x80 ); _delayMicro(); SCL = 1; _delay(); SCL = 0; send_data <<= 1; } } // 写入一个字节 void writeByte(uchar write_data,uchar addr){ start(); sendByte( WRITE_SIGN ); checkAck( ACK ); sendByte( addr ); checkAck( ACK ); sendByte( write_data ); checkAck( ACK ); stop(); _delayMs(10); //写入周期 芯片要一定的时间去写入数据 } //读取一个字节 uchar readByte(uchar addr){ uchar read_data; start(); //开始伪写 sendByte( WRITE_SIGN ); //读取前要先 伪写 checkAck( ACK ); sendByte( addr ); //读取的地址 checkAck( ACK ); start(); //开始读取 sendByte( READ_SIGN ); //操作码 checkAck( ACK ); read_data = receiveByte(); //读8位 checkAck( ACK ); stop(); //结束 return read_data; } //写N个字节 bit writeBytes(uchar *write_data,uchar n,uchar addr){ uchar i; start(); sendByte( WRITE_SIGN ); checkAck( ACK ); sendByte( addr ); checkAck( ACK ); for( i=0; i<0xff; j++ ){ if(!checkAck( i==(n-1) ? NOACK : ACK )){ break; } } } stop(); //结束 */ return 1; } void main(void){ uchar i; i = readByte(0); led(i); i = (i==9) ? 0 : i+1; writeByte( i, 0 ); while(1){;} }


Recent Comments