AVR外部扩展EEPROM C编程完全解密(全部程序、时序讲解)【极力推荐】

 

.

下载地址:   http://wqygogo.download.csdn.net/

 

//==============头文件=================//
#include "24C16.h"


//===========================================================================================//
//函 数 名:init_24C16
//函数功能:I2C引脚初始化。
//输入参数:无。
//输出参数:无。
//返 回 值:无。
//修改者  :---
//修改时间:---
//==========================================================================================//
void init_24C16(void)
{
 //设置SCL为输出,输出'0'
 //设置SDA(输入/输出先不管,默认),使能上拉电阻
 SCL_OUT_L();     //SCL=0
 SET_SCL_OUT();     //set SCL put out

 SDA_OUT_H();     //pull up resistance for SDA
#ifdef _AVR_GCC_  
 SFIOR &= ~(1<<PUD);    //enable PUD
#endif //_AVR_GCC_
}

//===========================================================================================//
//函 数 名:I2CStart
//函数功能:模拟I2C起始信号。
//输入参数:无。
//输出参数:无。
//返 回 值:无。
//修改者  :---
//修改时间:---
//==========================================================================================//
void I2CStart(void)
{
//为确保在"SCL = 1"期间,SDA只有一次下降沿,故有以下步骤:
//1、确定SDA的初始状态为高电平;
//2、下降沿
//3、结束,数据线的任何变化不会被作为总线的起始或停止信号
 SCL_OUT_L();    //SCL=0,step1: init
 SET_SDA_OUT();    //set put out for SDA
 SDA_OUT_H();    //SDA=1,SDA put out 1
 SOME_NOP();     //等待EEPROM采样到SCL
 
 SCL_OUT_H();    //SCL=1,step2: start
 SOME_NOP();     //等待EEPROM采样到SCL
 SDA_OUT_L();    //SDA=0,SDA put out 0,pull down edge
 SOME_NOP();     //延时,确保SDA的下降沿在SCL的高电平期间产生
 
 SCL_OUT_L();    //SCL=0,step3: end
}

//===========================================================================================//
//函 数 名:I2CStop
//函数功能:模拟I2C停止信号。
//输入参数:无。
//输出参数:无。
//返 回 值:无。
//修改者  :---
//修改时间:---
//==========================================================================================//
void I2CStop(void)
{
//为确保在"SCL = 1"期间,SDA只有一次上升沿,故有以下步骤:
//1、确定SDA的初始状态为低电平;
//2、上升沿
//3、结束,数据线的任何变化不会被作为总线的起始或停止信号
 SCL_OUT_L();    //SCL=0,step1: init
 SET_SDA_OUT();    //set put out for SDA
 SDA_OUT_L();    //SDA=0,put out
 SOME_NOP();     //等待EEPROM采样到SCL
 
 SCL_OUT_H();    //SCL=1,step2: start
 SOME_NOP();     //等待EEPROM采样到SCL
 SDA_OUT_H();    //SDA=1,put out,pull up edge
 SOME_NOP();     //延时,确保SDA的上升沿在SCL的高电平期间产生
 
 SCL_OUT_L();    //SCL=0,step3: end
}

//===========================================================================================//
//函 数 名:WaitAck
//函数功能:模拟I2C等待应答。
//输入参数:无。
//输出参数:无。
//返 回 值:应答状态,成功->TRUE or 失败->FALSE。
//修改者  :---
//修改时间:---
//==========================================================================================//
uint8 WaitAck(void)
{
 uint8 ErrTime = 255;  //set receive time out(超时值) for ACK : 255
 
 SCL_OUT_L();    //SCL=0,step1: init
 SET_SDA_OUT();    //set put out for SDA
 SDA_OUT_H();    //SDA=1,put out,step2: start
 SOME_NOP();     //等待EEPROM采样到SCL
 SCL_OUT_H();    //SCL=1
 
 while( READ_SDA() )   //Read SDA,cycle if true
 {
  ErrTime--;    //计超时
  if(!ErrTime)   //if time out
  {
   I2CStop();   //Stop
   return FALSE;  //faile  
  }
 }

 SCL_OUT_L();    //SCL=0,step3: end

 return TRUE;    //succeed
}

//===========================================================================================//
//函 数 名:SendAck
//函数功能:模拟I2C发送应答。
//输入参数:无。
//输出参数:无。
//返 回 值:无。
//修改者  :---
//修改时间:---
//==========================================================================================//
void SendAck(void)
{
//"ACK"即为在第9个时钟周期内,保持SDA为低电平
//确保不产生起始、停止信号。
//总之,SDA跳变或者给SDA赋值(假设赋值前,SDA的状态未知)时,须确保SCL为低电平
 SCL_OUT_L();    //SCL=0,step1: init
 SOME_NOP();     //等待EEPROM采样到SCL
 SET_SDA_OUT();    //set put out for SDA
 SDA_OUT_L();    //SDA=0,put out,step2: start
//------SCL周期的上半部分-------//
 SCL_OUT_H();    //SCL=1,pull up edge
 SOME_NOP();     //SCL、SDA保持时间
 SCL_OUT_L();    //SCL=0,pull down edge
 SCL_OUT_L();    //延时,确保SCL为低电平

 SDA_OUT_H();    //SDA=1,end
}

//===========================================================================================//
//函 数 名:SendNotAck
//函数功能:模拟I2C发送非应答。
//输入参数:无。
//输出参数:无。
//返 回 值:无。
//修改者  :---
//修改时间:---
//==========================================================================================//
void SendNotAck(void)
{
//"NO ACK" 即为在第9个时钟周期内,保持SDA为高电平
//确保不产生起始、停止信号。
//总之,SDA跳变或者给SDA赋值(假设赋值前,SDA的状态未知)时,须确保SCL为低电平
 SCL_OUT_L();    //SCL=0,step1: init
 SOME_NOP();     //等待EEPROM采样到SCL
 SET_SDA_OUT();    //set put out for SDA
 SDA_OUT_H();    //SDA=1,put out,step2: start
//-------SCL周期的上半部分------//
 SCL_OUT_H();    //SCL=1,pull up edge
 SOME_NOP();     //SCL、SDA保持时间
 SCL_OUT_L();    //SCL=0,pull down edge
 SCL_OUT_L();    //延时,确保SCL为低电平

 SDA_OUT_L();    //SDA=0,step3: end
}

//===========================================================================================//
//函 数 名:I2CSendByte
//函数功能:模拟I2C发送字节。
//输入参数:所要写的字节。
//输出参数:无。
//返 回 值:无。
//修改者  :---
//修改时间:---
//==========================================================================================//
void I2CSendByte(uint8 ch)
{
//SDA上的数据状态仅在SCL为低电平期间才能改变
 register uint8 i=8;   //8 bit Data

 SET_SDA_OUT();    //set put out for SDA
 while(i--)
 {
  //SDA上的数据状态仅在SCL为低电平期间才能改变
  SCL_OUT_L();   //SCL=0
  SOME_NOP();    //等待EEPROM采样到SCL
//----Send Data,先发送高位-----//
  if( ch&0x80 ) //1
  { SDA_OUT_H(); }  //SDA=1,put out
  else   //0
  { SDA_OUT_L(); }  //SDA=0,put out
  ch <<= 1;    //shift
  SOME_NOP();    //等待EEPROM采样到SDA

  SCL_OUT_H();   //SCL=1,pull up edge
  SOME_NOP();    //等待EEPROM采样到SCL 
 }
 //保持低电平,从而SDA上的数据状态变化,不会产生起始、停止信号
 SCL_OUT_L();    //SCL=0,end
}

//===========================================================================================//
//函 数 名:I2CReceiveByte
//函数功能:模拟I2C接收字节。
//输入参数:无。
//输出参数:无。
//返 回 值:接收的数据。
//修改者  :---
//修改时间:---
//==========================================================================================//
uint8 I2CReceiveByte(void)
{
 register uint8 i=8;    //8bit Data
 uint8 dat=0;     //存放接收的数据

 SET_SDA_IN();     //set put in for SDA,and pull up resistance
 while(i--)
 {
  //SDA上的数据状态仅在SCL为低电平期间才能改变
  SCL_OUT_L();   //SCl=0
  //时间主要耗在EEPROM芯片控制器更新SDA上的数值,
  //因EEPROM芯片控制器要从EEPROM中读取数值.( 读操作耗时)
  //而写操作耗时主要在:EEPROM芯片控制器往EEPROM中写数值.(写周期)
  DELAY();     //等待EEPROM采样到SCL,并更新SDA上的数值(耗时)    

//----Receive Data,先接收高位------//
  dat <<= 1;     //shift,第一次为空移,即8bit Data只要移7bit
  if( READ_SDA() ) //1  //read SDA,put in
  {      
   dat |= 0x01;
   
   #ifdef _DEBUG_
    USART_Transmit('1');//Send '1'
   #endif
  }

  else    //0
  { 
   dat &= 0xfe;

   #ifdef _DEBUG_
    USART_Transmit('0');//Send '0'
   #endif
  }
  SCL_OUT_H();    //SCL=1,pull up edge
  SOME_NOP();     //等待EEPROM采样到SCL
 }
 //保持低电平,从而SDA上的数据状态变化,不会产生起始、停止信号
 SCL_OUT_L();     //SCL=0,end

 return(dat);
}

//===========================================================================================//
//函 数 名:_24C16WriteChar
//函数功能:I2C的字节写。
//输入参数:所要写数据的地址、所要写数据。
//输出参数:无。
//返 回 值:应答状态,成功->TRUE or 失败->FALSE。
//修改者  :---
//修改时间:---
//==========================================================================================//
uint8 _24C16WriteChar(uint16 addr, uint8 ch)//addr(0~2047)(0x7ff)
{
 uint8 StateAck;        //应答状态

 I2CStart();         //发送I2C起始命令
  I2CSendByte( 0xA0 | ((addr>>7)&0x0e) );    //发送EEPROM型号信息及高三位的字节地址(针对24C16)
  StateAck = WaitAck();      //wait for ACK
 if( StateAck )//succeed
 {
  I2CSendByte((char)(addr));    //发送低8位的字节地址
   StateAck = WaitAck();     //wait for ACK
  if( StateAck )//succeed
  {
   I2CSendByte(ch);     //发送数据 
    StateAck = WaitAck();    //wait for ACK
   if( StateAck )//succeed    
    I2CStop();      //发送停止命令      
  }//end if2              
 }//end if1

 return StateAck;       //应答状态              
}//end

//===========================================================================================//
//函 数 名:_24c16WritePage
//函数功能:I2C的页写。
//输入参数:所要写数据的地址、字符串首地址、字符串长度。
//输出参数:无。
//返 回 值:应答状态,成功->TRUE or 失败->FALSE。
//修改者  :---
//修改时间:---
//==========================================================================================//
uint8 _24c16WritePage(uint16 addr, uint8 *str, uint8 len)//addr(0~2047)(0x7ff),len(0~16)
{
//16-byte page write buffer,超过,地址计数器自动翻转,先前写入数据将被覆盖。
 uint8 StateAck;        //应答状态

 I2CStart();         //发送I2C起始命令
  I2CSendByte( 0xA0 | ((addr>>7)&0x0e) );    //发送EEPROM型号信息及高三位的字节地址(针对24C16)
 StateAck = WaitAck();      //wait for ACK
 if( !StateAck )
  return FALSE;       //faile,无应答跳出

  I2CSendByte( (char)addr );     //发送低8位的字节地址
 StateAck = WaitAck();      //wait for ACK
 if( !StateAck )
  return FALSE;       //faile,无应答跳出
 
 if(16 != len)
  len &= 0x0f;       //赋值保护(取值范围),max=16
 while(len--)
 {
  I2CSendByte(*str++);     //发送数据 
   StateAck = WaitAck();     //wait for ACK
  if( !StateAck )
   return FALSE;      //faile,无应答跳出
 }

  I2CStop();         //发送停止命令

 return TRUE;        //succeed
}

//===========================================================================================//
//函 数 名:_24c16AtOnceRead
//函数功能:I2C的立即性读。
//输入参数:无。
//输出参数:无。
//返 回 值:应答状态(高8字节)及读到数据(低8字节)。(应答状态,成功->TRUE or 失败->FALSE。)
//修改者  :---
//修改时间:---
//==========================================================================================//
uint16 _24c16AtOnceRead(void)//addr(0~2047)(0x7ff)
{
//无选择性读的伪写操作部分 
 uint16 Data;        //return
 uint8 StateAck;        //应答状态

//---------读数据操作---------//
  I2CStart();         //发送I2C起始命令
  I2CSendByte(0xA1);       //发送EEPROM型号信息
  StateAck = WaitAck();
 if( !StateAck )
  return FALSE;       //无应答跳出,( Data&0xff )=FALSE,其中Data=0,FALSE=0

 Data = I2CReceiveByte();     //接收数据
  SendNotAck();        //发送非应答

  I2CStop();          //发送停止命令

 return( Data | 0x0100 );     //( Data|0x0100 )=( Data|(TRUE<<8) )
}

//===========================================================================================//
//函 数 名:_24c16SelRead
//函数功能:I2C的选择性读。
//输入参数:所要读的地址。
//输出参数:无。
//返 回 值:应答状态(高8字节)及读到数据(低8字节)。(应答状态,成功->TRUE or 失败->FALSE。)
//修改者  :---
//修改时间:---
//==========================================================================================//
//========================================//
uint16 _24c16SelRead(uint16 addr) //addr(0~2047)(0x7ff)
{ //也可“I2C的字节写”的写法
 uint16 Data;        //return
 uint8 StateAck;        //应答状态

//----------伪写操作----------//
  I2CStart();         //发送I2C起始命令
  I2CSendByte( 0xA0 | ((addr>>7)&0x0e) );    //发送EEPROM型号信息及高三位的字节地址(针对24C16)
  StateAck = WaitAck();
 if( !StateAck )
  return FALSE;       //无应答跳出,( Data&0xff )=FALSE,其中Data=0,FALSE=0
            
  I2CSendByte( (char)(addr) );    //发送低8位的字节地址
  StateAck = WaitAck();
 if( !StateAck )
  return FALSE;       //无应答跳出,( Data&(FALSE<<8) )=FALSE
            
//---------读数据操作---------//
  I2CStart();         //发送I2C起始命令
  I2CSendByte(0xA1);       //发送EEPROM型号信息
  StateAck = WaitAck();
 if( !StateAck )
  return FALSE;       //无应答跳出,( Data&(FALSE<<8) )=FALSE
            
 Data = I2CReceiveByte();     //接收数据

  SendNotAck();        //发送非应答
  I2CStop();          //发送停止命令

 return( Data | 0x0100 );     //( Data|0x0100 )=( Data|(TRUE<<8) )
}

//===========================================================================================//
//函 数 名:_24c16SeriesRead
//函数功能:I2C的连续读。
//输入参数:所要读数据的地址、所要读数据的长度。
//输出参数:无。
//返 回 值:动态空间的起始地址,内容包括应答状态(第一字节)、读到数据(len Byte长度)。
//         (应答状态,成功->TRUE or 失败->FALSE。)
//修改者  :---
//修改时间:---
//==========================================================================================//
uint8 * _24c16SeriesRead(uint16 addr, uint16 len)//addr(0~2047)(0x7ff),len(0~2047)
{
//len为接收字节数
//两种启动方式:立即性读启动、选择性读启动;
//默认为选择性读启动,定义AT_ONCE_READ_MODE后为立即性启动

 uint8 *p,*pp;        //p,pp指向同一地址,第一字节用来存放应答信息
 //动态存储空间
 //len+1,最后一个空间用来存放最后一个数据,因第一个字节空间作为它用
// p = (uint8 *)malloc(len+1);     //建立动态存储空间,返回空间起始地址
// pp = p;          //保留空间起始地址,待用

 //静态存储空间
 static uint8 buf[20];    
 p = buf;
 pp = buf;
           
#ifndef AT_ONCE_READ_MODE      //默认为选择性读操作启动
//----------伪写操作----------//
  I2CStart();         //发送I2C起始命令
  I2CSendByte( 0xA0 | ((addr>>7)&0x0e) );    //发送EEPROM型号信息及高三位的字节地址(针对24C16)
  *p = WaitAck();        //存放应答状态,TRUE->succeed,FALSE->failed
 if( !*p )          //
  return pp;        //无应答跳出,

  I2CSendByte( (char)(addr) );    //发送低8位的字节地址
  *p = WaitAck();        //存放应答状态
 if( !*p )           
  return pp;        //无应答跳出,
#endif

 I2CStart();         //发送I2C起始命令
  I2CSendByte(0xA1);       //发送EEPROM型号信息
  *p = WaitAck();        //存放应答状态
 if( !*p )
  return pp;        //无应答跳出,

 len--;          //少次for循环,空间第一字节作为它用
 p++;          //空间的第一字节已用,存放应答状态
 while(len--)
 {
  *p++ = I2CReceiveByte();    //接收数据
  SendAck();        //发送应答
 }
 *p++ = I2CReceiveByte();     //接收最后一个数据//第len+1个空间

 SendNotAck();        //发送非应答

  I2CStop();          //发送停止命令

 return pp;
}

/*
//===========================================================================================//
//函 数 名:QueryACk
//函数功能:应答查询。
//输入参数:无。
//输出参数:无。
//返 回 值:无。
//修改者  :---
//修改时间:---
//==========================================================================================//
void QueryACk(void)
{
//在EEPROM写周期期间,启动应答查询,来查询下次操作时间
 do
 {
  I2CStart();        //发送I2C起始命令
   I2CSendByte( 0xA0);        //发送从地址
  DataDebug=WaitAck();
 }
  while( !DataDebug );      //等待应答,应答状态,成功->TRUE or 失败->FALSE。
}
*/

...等,这只是其中一个C文件,要的话赶快下载吧。

下载地址:   http://wqygogo.download.csdn.net/

 

【作者产品开发资料】完全分析EEPROM的驱动时序,编程规范、代码优化等难得的参考资料,本程序兼容Keil和GCCAVR。你是不是想在C51开发的基础上,再学习AVR呢,那赶快下载吧。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值