学习单片机已经有一段时间了,但是很多程序都缺少模块化的思想,之前以为只要把单个的功能封装在一个函数里面就是模块化,经过了十多天实习,才真正有了模块化的编程思想,这里将我编写的STC12C5A60S2单片机控制EEPROM芯片AT24C512的程序共享一下,一是希望没有模块化编程思想的后来者看看,二是希望前辈们给予斧正 。
(补充:以下代码只需要修改.h文件中含有 “选择” 字样的部分,就可以达到复用的效果,对于T24C512的数据“格式化”,所需要的时间大约是10s左右,需耐心等待)
(提示:因为其中的宏较多,建议source insight查看代码)
对于LCD2004模块,请参考我的另一篇文章《单片机控制2004A液晶屏之模块化编程》
注意:这里图片可能无法全部显示,可以选择右键另存到电脑或者新标签页打开。然后查看
程序中只需要关注中文注释部分
/*################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