首页 > 其他 > 详细

单片机控制IIC协议EEPROM芯片24C512之模块化编程

时间:2014-03-01 08:22:00      阅读:553      评论:0      收藏:0      [点我收藏+]

学习单片机已经有一段时间了,但是很多程序都缺少模块化的思想,之前以为只要把单个的功能封装在一个函数里面就是模块化,经过了十多天实习,才真正有了模块化的编程思想,这里将我编写的STC12C5A60S2单片机控制EEPROM芯片AT24C512的程序共享一下,一是希望没有模块化编程思想的后来者看看,二是希望前辈们给予斧正 。

(补充:以下代码只需要修改.h文件中含有 “选择” 字样的部分,就可以达到复用的效果,对于T24C512的数据“格式化”,所需要的时间大约是10s左右,需耐心等待)

(提示:因为其中的宏较多,建议source insight查看代码)

对于LCD2004模块,请参考我的另一篇文章《单片机控制2004A液晶屏之模块化编程


注意:这里图片可能无法全部显示,可以选择右键另存到电脑或者新标签页打开。然后查看

bubuko.com,布布扣

程序中只需要关注中文注释部分

/*################at24c512.h start################*/

#ifndef __AT24C512_H__
#define __AT24C512_H__

#include <reg52.h>
#include "common.h"

sbit at24c512_sclk_bit = P1^1 ;/*根据硬件选择*/
sbit at24c512_sda_bit =  P1^0 ;/*根据硬件选择*/

//Device number
#define AT24C512_DEVICE0 0
#define AT24C512_DEVICE1 1
#define AT24C512_DEVICE2 2
#define AT24C512_DEVICE3 3
#define AT24C512_MIN_DEVICE_NUMBER AT24C512_DEVICE0
#define AT24C512_MAX_DEVICE_NUMBER AT24C512_DEVICE3	


#define AT24C512_DEVICE_ADDRESS_BASE_VALUE	0xa0

#define AT24C512_DEVICE_READ				0x01
#define AT24C512_DEVICE_WRITE				(0x01 & (~(0x01<<0)))

//page
#define AT24C512_MIN_PAGE_ADDRESS			0
#define AT24C512_MAX_PAGE_ADDRESS			511/*512 pgaes in total */
#define AT24C512_TOTAL_PAGE	(AT24C512_MAX_PAGE_ADDRESS-AT24C512_MIN_PAGE_ADDRESS+1)

#define AT24C512_PAGE_LENGTH				128
//一次最大修改长度为一页 128byte
#define AT24C512_MAX_CHANGE_LENGTH_ON_BURST_MODE 	AT24C512_PAGE_LENGTH

//internal address
#define AT24C512_MIN_INTERNAL_BYTE_ADDRESS	0
#define AT24C512_MAX_INNERNAL_BYTE_ADDRESS	65535/*65536 bytes in total*/
#define AT24C512_TOTAL_BYTE (AT24C512_MAX_INNERNAL_BYTE_ADDRESS-AT24C512_MIN_INTERNAL_BYTE_ADDRESS+1)				


//error
#define AT24C512_DEVICE_NUMBER_OVERFLOW		 		-1 /*编号溢出*/
#define AT24C512_INTERNAL_PAGE_ADDRESS_OVERFLOW		-2 /*页地址溢出*/


//器件数据格式化,总线初始化
extern SB8 at24c512Init(UB8 deviceNumber, UB8 dataCode) ;

//地址写字节数据
extern SB8 at24c512AddressWriteByte(UB8 deviceNumber,UW16 deviceInternalAddress,		
							UB8	 dataCode) ;

//地址写页数据
extern SB8 at24c512AddressWriteBurst(UB8 deviceNumber,
							UW16 pageNumber,
							const UB8 *table) ;

//当前地址读字节数据
extern UB8 at24c512CurrentAddressReadByte(UB8 deviceNumber,SB8 *errorFlag) ;

//随即地址读字节数据
extern UB8 at24c512RandomAddressReadByte(UB8 deviceNumber,	
							UW16 deviceInternalAddress,SB8 *errorFlag) ;
//序列读
extern SB8 at24c512SequentialRead(UB8 deviceNumber, 
							UW16 deviceInternalAddress,
							UB8 table[],UB8 length);


//内部数据格式化
extern SB8 at24c512AllDataSetup(UB8 deviceNumber,UB8 dataCode) ;



#endif /*__AT24C512_H__*/


 

/*################at24c512.h end################*/

 

/*################at24c512.c start################*/

/***************************************************************************
Module	:at24c512.c

Purpose	:Implementation of at24c512 module.

Version	:0.01							2014/02/03 12:00(OK)

Complier:Keil 8051 C complier V9.01

MCU		:STC12C5A60S2


Author	:yangrui

Email	:yangrui90s@163.com


Modification:
=================
	2014/02/28 08:12
	Reason:
		1.删除类似于
		xxx(.. , UW16 deviceInternalAddress ,..)
			{
				if(deviceInternalAddress < AT24C512_MIN_INTERNAL_BYTE_ADDRESS|| 			   deviceInternalAddress > AT24C512_MAX_INNERNAL_BYTE_ADDRESS)
			{
				return -2 ;
			}
		}
		这样的代码。这些代码的本意是为了方式用户调用时,入参超过AT24C512的内部
		地址长度65536(取值范围为0~65535),但是这里因为入参是UW16类型,即
		unsigned short int 类型,所以这里的判断是不对的,没有任何意义,因为隐含
		了一个类型转换,简单地说,就是deviceInternalAddress永远不会超过65535,
		例如用户调用时deviceInternalAddress为65537,因为数据长度溢出unsigned 
		short int类型的长度,所以会自动截取为(65537-65536)=1 (65536开始溢出)。
		所以这里的判断没有工作,可以删除。这属于C语言范畴。
=================

=================
	2014/02/25 20:30
	Reason:
		1.修改at24c512AddressWriteBurst(...)中的
			for(i=0 ; i<AT24C512_MAX_CHANGE_LENGTH; i++)
			{
				....
			}

			为
			
			changeNumber = (strlen(table) > AT24C512_MAX_CHANGE_LENGTH)? 
					AT24C512_MAX_CHANGE_LENGTH : strlen(table) ;
	
			for(i=0 ; i<changeNumber; i++)
			{
				...
			}

			因为之前的算法中,需要把一页的数据全部写完,有时候可能
			只需要写几个 byte的数据,这时候采用第一种算法后,会将从
			table开始的地址后的128字节写到AT24C512中区,这样就可能会
			导致意外的发生。

			为了安全起见,改为第二种算法,


=================

=================
	2014/02/25 20:30
	Reason:
		1.Add function at24c02SequentialRead(...)

=================

=================
	2014/02/24 23:44
	Reason:
		1.修改SB8 at24c512AllDataSetup(UB8 deviceNumber,UB8 dataCode)的核心操作
			UW16 i ;
			for(i=0; i<= AT24C512_MAX_INNERNAL_BYTE_ADDRESS ; i++)
			{
				at24c512AddressWriteByte(deviceNumber,i,dataCode);
			}
			
			为:
			
			UB8 i;
			UB8 table[8] ;
			for(i=0 ; i< 8 ; i++)
			{
				table[i] = dataCode ;
			}

			for(i=0; i<= AT24C512_MAX_PAGE; i++)
			{
				at24c512AddressWriteBurst(deviceNumber,i,table);
			}

			即用页写操作代替字节写操作来给AT24C512进行数据格式化,速度更快,
			效率更高。
		
=================



***************************************************************************/

#include <reg52.h>
#include <string.h>
#include <intrins.h>
#include <stdlib.h>							             
#include "common.h"
#include "at24c512.h"


/******************************************************
Function	:delay10msForAt24c512
Input		:N/A
Output		:N/A
Return		:N/A
Description	:N/A
Note		:用于一次读/写操作后AT24C512的内部数据操作时间,
			 这段时间,是不可以对AT24C512进行操作的,所以利
			 用延时错过这段时间。10ms max  (datasheet)
			 crystal frequency is 11.0592MHZ.
******************************************************/
static void delay10msForAt24c512(void)
{
	unsigned char i, j;

	_nop_();
	_nop_();
	i = 108;
	j = 144;
	do
	{
		while (--j);
	} while (--i);	
}


/******************************************************
Function	:at24c512StartSignal
Input		:N/A
Output		:N/A
Return		:N/A
Description	:at24c512 start signal
Note		:N/A
******************************************************/
static void at24c512StartSignal(void)
{
	at24c512_sda_bit = HIGH_LEVEL ;
	//_nop_() ;
	at24c512_sclk_bit = HIGH_LEVEL ;
	//_nop_() ;
	at24c512_sda_bit = LOW_LEVEL ;
	//_nop_();
}

/******************************************************
Function	:at24c512StopSignal
Input		:N/A
Output		:N/A
Return		:N/A
Description	:at24c512 stop signal
Note		:N/A
******************************************************/
static void at24c512StopSignal(void)
{
	at24c512_sda_bit = LOW_LEVEL ;
	//_nop_() ;
	at24c512_sclk_bit = HIGH_LEVEL ;
	//_nop_() ;
	at24c512_sda_bit = HIGH_LEVEL ;

	/*AT24C512 self timed write cycle (5ms max)*/
	delay10msForAt24c512();	
}

/******************************************************
Function	:at24c512WriteByte
Input		:the data which is ready to write to at24c512
Output		:N/A
Return		:N/A
Description	:N/A
Note		:N/A
******************************************************/
static void at24c512WriteByte(UB8 dataCode)
{
	UB8 i ;
	UB8 temp = dataCode ;

	for(i=0 ; i<8 ; i++)
	{
		at24c512_sclk_bit = LOW_LEVEL ;
		//_nop_();

		//方法一
		at24c512_sda_bit = (bit)(temp & (0x80>>i)) ;

		//方法二
		//temp <<= 1 ;
		//at24c512_sda_bit = CY ;

		//_nop_();
		at24c512_sclk_bit = HIGH_LEVEL ;
		//_nop_();
	}
}

/******************************************************
Function	:at24c512ReadByte
Input		:N/A
Output		:N/A
Return		:the byte-data which read from at24c512
Description	:N/A
Note		:N/A
******************************************************/
static UB8 at24c512ReadByte(void)
{
	UB8 i ;
	UB8 dataCode = 0x00 ;

	//Ready
	/*Data on sda pin may change during scl low timer period*/
	at24c512_sclk_bit = LOW_LEVEL ;
	//_nop_() ;
	at24c512_sda_bit = HIGH_LEVEL ;
	//_nop_() ;

	for(i=0; i<8 ; i++)
	{
		at24c512_sclk_bit = HIGH_LEVEL ;
		//_nop_() ;
		dataCode<<= 1;
		dataCode |= at24c512_sda_bit ;
		//_nop_() ;
		at24c512_sclk_bit = LOW_LEVEL ;
		//_nop_() ;
	}


	return dataCode ;
}

/******************************************************
Function	:at24c512Acknowledge
Input		:N/A
Output		:N/A
Return		:N/A
Description	:When at24c512 receive a data from mcu , at24c512 write the data
			to internal address, after completed write ,at24c512 send a zero
			to mcu,then mcu can input data into at24c512.
			(Once the internally timed write cycle has started and 
			the EEPROM inputs are disabled until write finished.)
Note		:N/A
******************************************************/
static void at24c512Acknowledge(void)
{
	UB8 i=0 ;

	at24c512_sclk_bit = LOW_LEVEL ;
	//_nop_() ;

	at24c512_sclk_bit = HIGH_LEVEL ;
	//_nop_() ;

	
	while((at24c512_sda_bit) && (i<250))
	{
		i++;//暂时,具体见调试
	}
	at24c512_sclk_bit = LOW_LEVEL ;
}

/******************************************************
Function	:at24c512AddressWriteByte
Input		:器件编号,内部地址,数据
Output		:N/A
Return		:0 (OK)
			 -1(at24c512 device number overflow)
			 -2(at24c512 device internal  byte address overflow)
Description	:N/A
Note		:N/A
******************************************************/
SB8 at24c512AddressWriteByte(UB8 deviceNumber,UW16 deviceInternalAddress,		
							UB8	 dataCode)
{
	if(deviceNumber < AT24C512_MIN_DEVICE_NUMBER|| 
	   deviceNumber > AT24C512_MAX_DEVICE_NUMBER)
	{
		return AT24C512_DEVICE_NUMBER_OVERFLOW ;
	}

	at24c512StartSignal() ;
	at24c512WriteByte(AT24C512_DEVICE_ADDRESS_BASE_VALUE	| 									(deviceNumber<<1)	| 									AT24C512_DEVICE_WRITE) ;
	at24c512Acknowledge() ;

	at24c512WriteByte((deviceInternalAddress>>8) & 0xff) ;/*MSB*/
	at24c512Acknowledge() ;

	at24c512WriteByte(deviceInternalAddress & 0xff) ;/*LSB*/
	at24c512Acknowledge() ;

	at24c512WriteByte(dataCode) ;
	at24c512Acknowledge() ;

	at24c512StopSignal() ;

	return 0;
}

/******************************************************
Function	:at24c512AddressWriteBurst
Input		:器件编号,页地址,数据指针
Output		:N/A
Return		:0 (Ok)
			 -1(at24c512 device number overflow)
			 -3(at24c512 device internal  page address overflow)		 
Description	:N/A
Note		:N/A
******************************************************/
SB8 at24c512AddressWriteBurst(UB8 deviceNumber,UW16 deviceInternalAddress,const UB8 *table)
{
	UB8 i;
	UW16 changeLength ;

	if(deviceNumber < AT24C512_MIN_DEVICE_NUMBER|| 	   deviceNumber > AT24C512_MAX_DEVICE_NUMBER)
	{
		return AT24C512_DEVICE_NUMBER_OVERFLOW ;
	}
	at24c512StartSignal() ;

	at24c512WriteByte(AT24C512_DEVICE_ADDRESS_BASE_VALUE	| 					 (deviceNumber<<1)					| 					  AT24C512_DEVICE_WRITE) ;
	at24c512Acknowledge() ;



	at24c512WriteByte(((deviceInternalAddress)>>8) & 0xff) ;
	at24c512Acknowledge() ;

	at24c512WriteByte((deviceInternalAddress) & 0xff) ;
	at24c512Acknowledge() ;
	
	changeLength = (strlen(table) > AT24C512_MAX_CHANGE_LENGTH_ON_BURST_MODE)?
			AT24C512_MAX_CHANGE_LENGTH_ON_BURST_MODE:strlen(table);
	
	for(i=0 ; i<changeLength; i++)
	{
		at24c512WriteByte(*table) ;
		at24c512Acknowledge() ;
		table++;
	}

	at24c512StopSignal() ;

	return 0 ;
}

/******************************************************
Function	:at24c512AllDataSetup
Input		:器件编号,格式化的数据
Output		:N/A
Return		:0 (ok)
			 -1(at24c512 device number overflow)
Description	:N/A
Note		:注意页写操作的"Roll over"
******************************************************/
SB8 at24c512AllDataSetup(UB8 deviceNumber,UB8 dataCode)
{
	UW16 i;
	UB8 table[AT24C512_MAX_CHANGE_LENGTH_ON_BURST_MODE];

	if(deviceNumber < AT24C512_MIN_DEVICE_NUMBER|| 	   deviceNumber > AT24C512_MAX_DEVICE_NUMBER)
	{
		return AT24C512_DEVICE_NUMBER_OVERFLOW ;
	}
	
	for(i=0 ; i< AT24C512_MAX_CHANGE_LENGTH_ON_BURST_MODE ; i++)
	{
		table[i] = dataCode ;
	}

	for(i=0; i<= AT24C512_MAX_PAGE_ADDRESS; i++)
	{
		at24c512AddressWriteBurst(deviceNumber,i,table);
	}
	
	
	return 0;
}

/******************************************************
Function	:at24c512Init
Input		:器件编号,格式化的内容
Output		:N/A
Return		:0 (OK)
			 -1(at24c512 device number overflow)
Description	:AT24C512内部数据格式化,总线初始化
Note		:经过试验(结合LCD屏),整个初始化过程大约为10s左右,需耐心等待
******************************************************/
SB8 at24c512Init(UB8 deviceNumber, UB8 dataCode)
{
	if(deviceNumber < AT24C512_MIN_DEVICE_NUMBER || 	   deviceNumber > AT24C512_MAX_DEVICE_NUMBER)
	{
		return AT24C512_DEVICE_NUMBER_OVERFLOW ;
	}
	
	at24c512AllDataSetup(deviceNumber,dataCode);
	
	at24c512_sclk_bit = HIGH_LEVEL ;
	at24c512_sda_bit = HIGH_LEVEL ;

	return 0;
}

/******************************************************
Function	:mcuAcknowledge
Input		:N/A
Output		:N/A
Return		:N/A
Description	:when mcu receive a data word,mcu send a zero to at24c512.
Note		:N/A
******************************************************/
static void mcuAcknowledge(void)
{
	at24c512_sclk_bit = LOW_LEVEL ;
	//_nop_();
	at24c512_sda_bit = LOW_LEVEL ;
	//_nop_();
	at24c512_sclk_bit = HIGH_LEVEL;
}

/******************************************************
Function	:at24c512RandomAddressReadByteReady
Input		:器件编号
			 器件内部地址
Output		:N/A
Return		:N/A
Description	:序列读操作有两种初始化方法
					1.与当前地址读操作准备阶段相同
					2.与随即地址读操作准备阶段相同
			这里采用方法二,所以在这里进行了封装。
			分别在at24c512RandomAddressReadByte(...)
			和at24c512SequentialRead(...)中调用。	
Note		:N/A
******************************************************/
static void at24c512RandomAddressReadByteReady(UB8 deviceNumber,
											UW16 deviceInternalAddress)
{
	at24c512WriteByte(AT24C512_DEVICE_ADDRESS_BASE_VALUE	| 	                 (deviceNumber << 1)				| 	                 AT24C512_DEVICE_WRITE)	;
	at24c512Acknowledge() ;
	

	at24c512WriteByte((deviceInternalAddress>>8) & 0xff ) ;
	at24c512Acknowledge() ;

	at24c512WriteByte(deviceInternalAddress & 0xff ) ;
	at24c512Acknowledge() ;

	at24c512StartSignal() ; 

	at24c512WriteByte(AT24C512_DEVICE_ADDRESS_BASE_VALUE	| 	                 (deviceNumber << 1)				| 	                 AT24C512_DEVICE_READ)	;
	at24c512Acknowledge() ;
}

/******************************************************
Function	:at24c512RandomAddressReadByte
Input		:器件编号,内部地址
Output		:N/A
Return		:the data from at24c512 internal random adress.
Description	:N/A
Note		:这里为用户调用错误预留下了接口*errorFlag,在具体使用中,如果用户
			 确定自己调用的入参deviceNumber满足0~3,则可以使用NULL,即格式:
				at24c512RandomAddressReadByte(.., .., NULL);		 
******************************************************/
UB8 at24c512RandomAddressReadByte(UB8 deviceNumber,UW16 deviceInternalAddress,SB8 *errorFlag)
{
	UB8 dataCode ;
	
	if(deviceNumber < AT24C512_MIN_DEVICE_NUMBER|| 	   deviceNumber > AT24C512_MAX_DEVICE_NUMBER)
	{
		
		*errorFlag = AT24C512_DEVICE_NUMBER_OVERFLOW;
		//exit(0);
		return 0;
		/*这里不够完善,这里的判断主要是为用户留下的判断机制,因为在一条总线上
		所允许同时能够接AT24C512的最大数量是4,如果超过,就无法识别,操作也就
		会出错*/
	}

	at24c512StartSignal() ;

	at24c512RandomAddressReadByteReady(deviceNumber,deviceInternalAddress);
	
	dataCode = at24c512ReadByte();
	

	at24c512StopSignal() ;

	return dataCode ;
}

/******************************************************
Function	:at24c512CurrentAddressReadByte
Input		:器件编号
Output		:N/A
Return		:the data from at24c512 internal current address.
Description	:N/A
Note		:这里为用户调用错误预留下了接口*errorFlag,在具体使用中,如果用户
			 确定自己调用的入参deviceNumber满足0~3,则可以使用NULL,即格式:
				at24c512CurrentAddressReadByte(..,  NULL);	
******************************************************/
UB8 at24c512CurrentAddressReadByte(UB8 deviceNumber, SB8 *errorFlag)
{
	UB8 dataCode ;
	if(deviceNumber < AT24C512_MIN_DEVICE_NUMBER|| 	   deviceNumber > AT24C512_MAX_DEVICE_NUMBER)
	{
		*errorFlag = AT24C512_DEVICE_NUMBER_OVERFLOW ;
		//exit(0);
		return 0;
		/*这里不够完善,这里的判断主要是为用户留下的判断机制,因为在一条总线上
		所允许同时能够接AT24C512的最大数量是4,如果超过,就无法识别,操作也就
		会出错*/
	}

	at24c512StartSignal() ; 
	
	/*------------------准备阶段--------------------------*/

	at24c512WriteByte(AT24C512_DEVICE_ADDRESS_BASE_VALUE	| 					 (deviceNumber<<1					| 					 AT24C512_DEVICE_READ)) ;
	at24c512Acknowledge() ;
	/*----------------------------------------------------*/
	
	dataCode = at24c512ReadByte() ;
	

	at24c512StopSignal() ;

	return dataCode ;
}

/******************************************************
Function	:at24c512SequentialRead
Input		:器件编号 ;
			 器件内部地址(起始地址) ;
			 数据指针
			 读取长度
			 
Output		:数据指针
Return		:0 (ok)
Description	:序列读
Note		:
		1.最后一个数据不用主机回复,其他数据传输时,需要主机的应答。

		2.序列地址读操作有两种初始化方法:
					(1).与当前地址读操作准备阶段相同
					(2).与随即地址读操作准备阶段相同

			这里采用第二种方法,第一种方法不够灵活,
******************************************************/
SB8 at24c512SequentialRead(UB8 deviceNumber, UW16 deviceInternalAddress,	
							UB8	 *table,UB8 length)
{
	UB8 dataCode ;
	
	if(deviceNumber < AT24C512_MIN_DEVICE_NUMBER|| 	   deviceNumber > AT24C512_MAX_DEVICE_NUMBER)
	{
		return AT24C512_DEVICE_NUMBER_OVERFLOW ;
	}

	at24c512StartSignal() ;

	at24c512RandomAddressReadByteReady(deviceNumber,deviceInternalAddress);
	
	while(--length)
	{
		*table = at24c512ReadByte();
		mcuAcknowledge();
		table++ ;
	}
	*table = at24c512ReadByte();

	at24c512StopSignal() ;

	return dataCode ;
}


 

/*################at24c512.c end################*/

 

测试程序:

/*################main.c start################*/

#include <reg52.h>
#include <stdlib.h>
#include "common.h"
#include "lcd2004.h"
#include "at24c512.h"

void main(void)
{	

	lcd2004Init();

	//为了显示效果明显,暂时修改为光标不显示
	lcd2004WriteCommand(0x0c)	;

	//AT24C512芯片0初始化,并显示前20个字符(全是‘^’)
	at24c512Init(0,‘^‘);
	lcd2004AddressWriteString(LCD2004_ROW0,0,"First Init over!");
	for(i=0 ; i<20 ; i++)
	{
		lcd2004AddressWriteByte(LCD2004_ROW1,i,at24c512RandomAddressReadByte(0,i,NULL));
		
	}


	//AT24C512芯片1初始化,并显示前20个字符(全是‘#’)
	at24c512Init(1,‘#‘);
	lcd2004AddressWriteString(LCD2004_ROW2,0,"Second Init over!");
	for(i=0 ; i<20 ; i++)
	{
		lcd2004AddressWriteByte(LCD2004_ROW3,i,at24c512RandomAddressReadByte(1,i,NULL));
		
	}


	while(1);
	

}


 

/*################main.c  end################*/

 

补充:common.h

#ifndef __COMMON_H__
#define __COMMON_H__

typedef unsigned char UB8 ;
typedef unsigned short int UW16 ;
typedef unsigned long UL32 ;

typedef char SB8;
typedef short int SW16 ;
typedef long SL32 ;
	
#define HIGH_LEVEL 1	
#define LOW_LEVEL  0


#endif	/*__COMMON_H__*/


 


 

单片机控制IIC协议EEPROM芯片24C512之模块化编程,布布扣,bubuko.com

单片机控制IIC协议EEPROM芯片24C512之模块化编程

原文:http://blog.csdn.net/yagnruinihao/article/details/20150679

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!