学习单片机已经有一段时间了,但是很多程序都缺少模块化的思想,之前以为只要把单个的功能封装在一个函数里面就是模块化,经过了十多天实习,才真正有了模块化的编程思想,这里将我编写的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