以89S52为上位机控制AT24C08的源码(C语言+经验总结)

MCU Add comments

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){;}	
 
}

Leave a Reply

WP Theme & Icons by N.Design Studio
Entries RSS Comments RSS 登录