精华内容
下载资源
问答
  • FM24C16A-AT24C16中文资料.pdfFM24C16A-AT24C16中文资料.pdf
  • IIC驱动AT24C16芯片

    2018-09-01 21:32:23
    msp430F2418微控制器以IIC方式读取AT24C16存储芯片的数据,通过IIC方式向AT24C16芯片写入存储数据
  • AT24C16芯片

    2011-12-29 20:11:58
    AT24C16芯片相关资料介绍 包括封装尺寸和功能介绍等
  • AT24C16数据手册

    2014-04-29 13:21:14
    AT24C16的数据手册,总共25页,英文版的。
  • FM24C16A-AT24C16中文资料

    2009-07-27 21:37:57
    FM24C16A的中文资料,可以用来来做AT24C16的参考
  • SIMULATE_AT24C16.rar

    2019-12-22 14:57:24
    STC8A8K64S4A12 单片机模拟口 AT24C16; 老生常谈的IIC通讯,接触单片机必走之路,按照教程调通之后,只限于单字节单地址; 此程序除单字节单地址外展示多字节多地址连续读写,每个人的写法不同,各取所需, 适用...
  • AT24C16 读写

    千次阅读 2019-03-07 13:52:52
    at24c16 有8块 256字节组成,共2K字节16K bit I2C开始信号后,第一个字节为器件地址,由1010+3位块地址+1位读写标志组成, 3位块地址刚好可以表示 8个块, 所以一次写完256字节,换到下一下块的时候,要重新更改...

    at24c16 有8块 256字节组成,共2K字节16K bit

    在这里插入图片描述
    在这里插入图片描述
    I2C开始信号后,第一个字节为器件地址,由1010+3位块地址+1位读写标志组成, 3位块地址刚好可以表示 8个块, 所以一次写完256字节,换到下一下块的时候,要重新更改器件地址
    在这里插入图片描述
    共有三种写入模式:
    字节写入模式: MODE脚可接高电平或低电平

    多字节模式:MODE接高电平,一次最多写8个字节,24c16收到数据后,地址低4bit递增,当地址超出边界时,会回滚到本页的开头,相当于低4bit溢出了,而且写入时间加倍到20ms。

    页写入模式:一次最多写入16字节。主机每发送一个字节,24c16收到确认,内部地址递增(仅限低4bit,所以1次可写16字节)。
    在这里插入图片描述
    写保护功能:
    8个块(每个块256byte)中,高四个块是可以写保护的,防止误改。
    从边界地址—到顶部0x07FF之间被保护,边界地址由PB1 PB0两个引脚电平和0x07FF这个字节确定。
    PB0 PB1选择四个块,地址0x07FF里面的内容的高4bit选择行号,一个块里有16行,每行16字节。低4bit 必须是0。所以边界地址是以行为单位的。

    写保护的顺序:
    1、将要保护的数据写入内存的顶部,最多但不包括location7FFh
    2、硬件选择PB0 PB1 电平
    3、通过在地址指针(位置7FFh的4个MSB)中写入正确的底部边界地址并将位b2(保护标志)设置为“0”来设置保护。

    注意:位置7FFh的低4bit必须写入0,然后当PRE引脚接高电平时,保护将起作用。

    备注:只有当PRE为高电平,并且7FFH存储的地址的bit2位为0,保护才起作用。其它情况,7FFH可以作为普通存储单元。


        /*****************************************************************************
        函数名称 : readAT24C16
        功能描述 :从at24c16读取数据
        输入参数 : 要读的开始地址,读出来的数据存放到数组的指针,数据长度
        返回参数 : hal_ok/hal_error
        使用说明 : 无
        *****************************************************************************/
    HAL_StatusTypeDef readAT24C16(uint16_t addr,uint8_t *data, uint16_t len)
    {
    	uint8_t rNum=0;
    	uint16_t lenLeft=len;
    	uint8_t deviceId;
    	uint8_t *p=data;
    
    	/*is the address overfolw*/
    	if(addr+len>E2PROM_SIZE)   return HAL_ERROR;
    
    	/*calculate the current read position to know how many word can read continully*/
    	rNum=16-addr & E2PROM_PAGE_MASK;
    	if(rNum == 0)  rNum=16;
    	rNum = lenLeft>=rNum ? rNum : lenLeft;//剩余未读字节数如果大于rNum, 则读rNum个,如果小于rNum,则一次读完了
    
    	/*read the data from e2prom*/
    	while(lenLeft)
    	{
    		//这里计算页地址,当地址小于256时,右移8位会小于0,所以器件地址为基地址A1
    		//如果读取的地址大于256时,右移8位则不会小于0,所以器件地址为 基地址A1 | 3位页地址
    		deviceId=(addr>>8)<0 ?  E2PROM_BASE_RID : (E2PROM_BASE_RID | (uint8_t)((addr>>7)&0x0E));
    
    		if(HAL_I2C_Mem_Read(&hi2c2,deviceId,addr&0x00FF,I2C_MEMADD_SIZE_8BIT,p,rNum, 20)!=HAL_OK)
    		{
    			printf("i2c read error\r\n");
    			continue;
    		}
    		addr+=rNum;//已经读了rNum个了,所以地址后移rNum个
    		lenLeft-=rNum;//剩余未读数据减少rNum个
    		p+=rNum;
    		rNum=lenLeft>16? 16 : lenLeft;//如果剩余大于16个,则下次再读16个,如果小于,则一次读完
    	}
        	return HAL_OK;
        }
    

    上页的函数,会调用

    HAL_I2C_Mem_Read(&hi2c2,deviceId,addr&0x00FF,I2C_MEMADD_SIZE_8BIT,p,rNum, 20)!
    

    来读取16个字节。这个函数再调用下面的函数来读取

    I2C_RequestMemoryRead(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, uint16_t MemAddress, uint16_t MemAddSize, uint32_t Timeout, uint32_t Tickstart)
    

    读取的流程为:
    设置CR1应答ACK-------产生START-------wait SB set -----发送器件地址-----wait ADDR set 表示地址发送完-----清除ADDR-----wait TXE set表示发送寄存器目前是空的---------发送子地址-------wait TXE set-------再次产生START------wait SB set ------发送器件地址-------wait ADDR set ------退回上一层。接收数据。

    展开全文
  • 1、基于IIC协议,采用verilog编写AT24C16驱动程序。 2、实现功能:在AT24C16的地址0~99之间顺序写入数据0~99,然后在读取出来,读取的数据通过串口调试助手显示出来。 3、测试平台Quartus 17.1
  • I2C驱动AT24C16

    2018-03-27 11:55:11
    驱动程序:I2C协议访问EEPROM(AT24C16)进行存储。经过本人实验证实。可以使用。分享给大家
  • 存储类产品AT24C16

    2012-10-31 16:28:41
    存储类产品的产品规格书AT24C16存储类产品的产品规格书
  • IC设计FPGA设计at24c16_sdram_sram VERILOG仿真模型源码文件,均是项目设计用到的测试激励模型,包括EEPROM-at24c16仿真模型,sdram,SRAM仿真模型,可以做为你的设计参考。
  • AT24c16工程(i2c io模拟)

    2018-10-30 22:01:01
    AT24c16 通信 i2c 读写 i2c IO模拟 PB6 pb7 测试写入 地址100 读出值 串口打印
  • AT24C16A.pdf

    2009-03-04 16:59:43
    AT24C16A,Two-wire Serial EEPROM
  • EEPROM(AT24C16)页写算法

    千次阅读 多人点赞 2017-11-16 13:58:11
    学习单片机或者从事嵌入式开发的,对于EEPROM绝不会陌生,尤其的24系列的EEPROM很是经典,或者与此兼容的FRAM系列,如AT24C02、AT24C16、FM24C16等。 驱动起这个系列的EEPROM,可以说是没有任何难点,各类教程、...

    1. 写在前面

          学习单片机或者从事嵌入式开发的,对于EEPROM绝不会陌生,尤其的24系列的EEPROM很是经典,或者与此兼容的FRAM系列,如AT24C02、AT24C16、FM24C16等。 驱动起这个系列的EEPROM,可以说是没有任何难点,各类教程、源码、驱动都可以轻松找到并作稍微改动即可使用。但是,对于使用技巧方面,大多的教程并不会提及,而只是作为学习“入门”的一个途径,真正在实际产品中使用,还需自身的经验去摸索改善。

     

    2.EEPROM页写算法

            驱动起一片EEPROM,实现的功能不外乎就是读、写。在写过程中,有一个“页写”功能,如果是大量数据写入,采用“页写”功能会大大提高效率。记得以前学习单片机的时候,遵循的是某开发板的教程,其中的“页写”算法过于复杂,包括现在看来依然是比较累赘,所以已经把好多人搞懵逼。而且网络上大部分的源码几乎都是大同小异,质量不算高。大体思路是这样的:用逻辑语句实现,首先检查写入总数据字节数是否大于一页,然后进行下一步检查,开始地址是否为页的开始,剩下未满一页的数据怎样处理等等。逻辑上来说,只要多阅读、画个逻辑流程图分析,这样处理还是非常好理解的,但显得过于累赘。逻辑用数学语句描述,则是算法,下面则是这样的实现。

     

            工作后,从Linux系统的驱动源码中发现了EEPROM的“页写”新方法,几句代码即把前面一堆逻辑描述清楚,第一次体会到算法的神奇之处。作了一些修改,关键代码如下。

    EEPROM页写时序图:

    修改后的页写算法:

    uint16_t ee_24clxx_writebytes(uint16_t write_addr, const char* pwrite_buff, uint16_t writebytes)
    {
        uint8_t write_len,page_offset;
        uint16_t temp= 0;
    	
        while(writebytes > 0)
        {
            page_offset = EE24CLXX_PAGESIZE - (write_addr % EE24CLXX_PAGESIZE); /* EE24CLXX_PAGESIZE为页大小,如24c16为16字节 */
    	write_len = writebytes > page_offset ? page_offset : writebytes;
    	i2c_24clxx_write(write_addr,pwrite_buff, write_len); /* 写一页函数 */
    	writebytes = writebytes - write_len;
            temp+= write_len;
    	if(writebytes > 0)
    	{
    	    pwrite_buff = pwrite_buff + write_len;
    	    write_addr  = write_addr + write_len;
    	    i2c_24clxx_waitstandby(0); /*页写判忙,FRAM则不用*/
    	}
        }
        return temp;
    }

     

    3. 参考源码

    [1]  https://github.com/Prry/drivers-for-mcu

    [2] https://github.com/Prry/_24Cxx-EEPROM-FRAM-Dev

    展开全文
  • LPC17XX IIC AT24C16读写测试程序,非常适合初学者使用
  • * 实现功能:把一个float 拆分成4个Byte存放在外置EEPROM中 (具体型号为AT24C16,换其他型号的应该用不了,2K字节空间)可以与DHT配合使用 * 实现的写入和读取的功能,亲测可用,测试的时候串口打印出0-511,共512个...
  • STM32快速读写AT24C16 代码 模拟I2C

    千次阅读 2018-11-05 11:27:31
    本帖只适用AT24C16及以下的芯片,AT24C32及以上的芯片读写方式不一样,故不适用!!! 如果你的代码可以正常读写24C01/02,直接拿来读取24C16是可以的,但是只能读取256字节。 AT24C16与AT24C01/02/04/08 不同,它...

    本帖只适用AT24C16及以下的芯片,AT24C32及以上的芯片读写方式不一样,故不适用!!!

    如果你的代码可以正常读写24C01/02,直接拿来读取24C16是可以的,但是只能读取256字节。

    AT24C16与AT24C01/02/04/08 不同,它引脚的A2,A1,A0是无效的,也就是它没有自己独立的地址,总线上只能挂一个AT24C16设备。

    AT24C16总共2048字节,分为128页,每页16字节,地址范围是0~2047。

    128页只需要7位地址,分为高3位和低4位,高3位在设备地址中,低4位在字节地址中。

    设备地址:1010+页地址高3位+读写方向(1:读  0:写)

    字节地址:页地址高4位+4位页内偏移地址

    例如读写地址:1864 ,首先计算该地址是多少页的多少个字节,1864/16=116(0x74)页,1864%16=8(0x08),即116页的第8个字节

    其中页地址0x74=0 1 1 1 0 1 0 0,最高位忽略,分为D6、D5、D4(高3位)和D3~D0(低4位)两个部分 。

    可以计算出 设备地址和字节地址:

    设备地址:1010+111+0/1  (AT24C16设备地址高4位固定为1010)

    字节地址:0100+1000(高4位是页地址低4位,低4位是页内偏移地址,即0x08)

    最后,根据标准I2C读写时序来对这个地址进行读写即可!

     

     

    模拟I2C.c

    #include "myiic.h"
    #include "delay.h"
    
    #if SoftDelay
    __asm void delay_us(u32 usec)  
    {                    
     ALIGN
    	PUSH.W {r1}		//2时钟周期
    	MOV r1,#18		//1时钟周期
    	MUL r0,r1		//1时钟周期
    	SUB r0,#3		//1时钟周期
    loop
    	SUBS r0,#1			//1时钟周期
    	BNE loop			//如果跳转则为3个周期,不跳则只有1个周期
    	POP {r1}			//2时钟周期
    	BX lr				//3个时钟周期
    						//总共所用周期为(usec*4)-4,此处减4主要用于抵消调用此函数的消耗时钟周期(传参1时钟,BLX跳转3时钟)
      //本函数内总共所用周期为usec*(freq/4)-2 +9,调用此函数的消耗5个时钟周期(传参2时钟,BLX跳转3时钟)
    } 
    #endif
    
    //初始化IIC
    void IIC_Init(void)
    {					     
    	GPIO_InitTypeDef GPIO_InitStructure;
    	RCC_APB2PeriphClockCmd(IIC_CLOCK, ENABLE );		   
    	GPIO_InitStructure.GPIO_Pin = SCL_PIN|SDA_PIN;    //引脚
    	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP ;   //推挽输出
    	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    	GPIO_Init(SCL_PORT, &GPIO_InitStructure);
     
    	IIC_SCL=1;
    	IIC_SDA=1;//拉高SDA和SCL
    
    }
    //产生IIC起始信号
    void IIC_Start(void)
    {
    	SDA_OUT();     //sda线输出
    	IIC_SDA=1;	  	  
    	IIC_SCL=1;
    	delay_us(4);
     	IIC_SDA=0;//START:when CLK is high,DATA change form high to low 
    	delay_us(4);
    	IIC_SCL=0;//钳住I2C总线,准备发送或接收数据 
    }	  
    //产生IIC停止信号
    void IIC_Stop(void)
    {
    	SDA_OUT();//sda线输出
    	IIC_SCL=0;
    	IIC_SDA=0;//STOP:when CLK is high DATA change form low to high
     	delay_us(4);
    	IIC_SCL=1; 
    	IIC_SDA=1;//发送I2C总线结束信号
    	delay_us(4);							   	
    }
    //等待应答信号到来
    //返回值:1,接收应答失败
    //        0,接收应答成功
    u8 IIC_Wait_Ack(void)
    {
    	u8 ucErrTime=0;
    	SDA_IN();      //SDA设置为输入  
    	IIC_SDA=1;delay_us(1);	   
    	IIC_SCL=1;delay_us(1);	 
    	while(READ_SDA)
    	{
    		ucErrTime++;
    		if(ucErrTime>250)
    		{
    			IIC_Stop();
    			return 1;
    		}
    	}
    	IIC_SCL=0;//时钟输出0 	   
    	return 0;  
    } 
    //产生ACK应答
    void IIC_Ack(void)
    {
    	IIC_SCL=0;
    	SDA_OUT();
    	IIC_SDA=0;
    	delay_us(2);
    	IIC_SCL=1;
    	delay_us(2);
    	IIC_SCL=0;
    }
    //不产生ACK应答		    
    void IIC_NAck(void)
    {
    	IIC_SCL=0;
    	SDA_OUT();
    	IIC_SDA=1;
    	delay_us(2);
    	IIC_SCL=1;
    	delay_us(2);
    	IIC_SCL=0;
    }					 				     
    //IIC发送一个字节
    //返回从机有无应答
    //1,有应答
    //0,无应答			  
    void IIC_Send_Byte(u8 txd)
    {                        
        u8 t;   
    	SDA_OUT(); 	    
        IIC_SCL=0;//拉低时钟开始数据传输
        for(t=0;t<8;t++)
        {              
            IIC_SDA=(txd&0x80)>>7;
            txd<<=1; 	  
    		delay_us(2);   //对TEA5767这三个延时都是必须的
    		IIC_SCL=1;
    		delay_us(2); 
    		IIC_SCL=0;	
    		delay_us(2);
        }	 
    } 	    
    //读1个字节,ack=1时,发送ACK,ack=0,发送nACK   
    u8 IIC_Read_Byte(unsigned char ack)
    {
    	unsigned char i,receive=0;
    	SDA_IN();//SDA设置为输入
        for(i=0;i<8;i++ )
    	{
            IIC_SCL=0; 
            delay_us(2);
    		IIC_SCL=1;
            receive<<=1;
            if(READ_SDA)receive++;   
    		delay_us(1); 
        }					 
        if (!ack)
            IIC_NAck();//发送nACK
        else
            IIC_Ack(); //发送ACK   
        return receive;
    }
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    

    i2c.h

    #ifndef __MYIIC_H
    #define __MYIIC_H
    #include "stm32f10x.h"
    //位段定义 方便后面直接操作IO口   Reg:寄存器地址  Bit:该寄存器的第多少位
    #define BITBAND_REG(Reg,Bit) (*((uint32_t volatile*)(0x42000000u + (((uint32_t)&(Reg) - (uint32_t)0x40000000u)<<5) + (((uint32_t)(Bit))<<2))))
    #define SoftDelay 0  //是否使用软件延时
    
    
    //IO口定义 移植需要修改
    #define SCL_PORT GPIOC
    #define SDA_PORT GPIOC
    #define SCL_PIN  GPIO_Pin_12
    #define SDA_PIN  GPIO_Pin_11
    //I2C 时钟 移植需要修改
    #define IIC_CLOCK RCC_APB2Periph_GPIOC
    
    //IO方向设置  移植需要修改
    #define SDA_IN()  {SDA_PORT->CRH&=0XFFFF0FFF;SDA_PORT->CRH|=8<<12;} // SDA配置为输入
    #define SDA_OUT() {SDA_PORT->CRH&=0XFFFF0FFF;SDA_PORT->CRH|=3<<12;} // SDA配置为输出
    
    //IO操作函数	移植需要修改
    #define IIC_SCL    BITBAND_REG(SCL_PORT->ODR,12) //SCL输出=?
    #define IIC_SDA    BITBAND_REG(SDA_PORT->ODR,11)  //SDA输出=?	 
    #define READ_SDA   (SDA_PORT->IDR&(1<<11))  //读SDA脚 
    
    //IIC所有操作函数
    void IIC_Init(void);         //初始化IIC的IO口
    void IIC_Start(void);				//发送IIC开始信号
    void IIC_Stop(void);	  			//发送IIC停止信号
    void IIC_Send_Byte(u8 txd);			//IIC发送一个字节
    u8 IIC_Read_Byte(unsigned char ack);//IIC读取一个字节
    u8 IIC_Wait_Ack(void); 				//IIC等待ACK信号
    void IIC_Ack(void);					//IIC发送ACK信号
    void IIC_NAck(void);				//IIC不发送ACK信号
    
    void IIC_Write_One_Byte(u8 daddr,u8 addr,u8 data);
    u8 IIC_Read_One_Byte(u8 daddr,u8 addr);
    #endif
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    

    AT24C16.c

    #include "24cxx.h"
    #include "delay.h"
    #include "stdio.h"
    
    //初始化IIC接口
    void AT24CXX_Init(void)
    {
        IIC_Init();
    }
    
    #if EE_TYPE<=AT24C08  //24C01/02/08
    //在AT24CXX指定地址读出一个数据
    //ReadAddr:开始读数的地址
    //返回值  :读到的数据
    u8 AT24CXX_ReadOneByte(u16 ReadAddr)
    {
        u8 temp=0;
        IIC_Start();
        if(EE_TYPE>AT24C16)
        {
            IIC_Send_Byte(0XA0);	   //发送写命令
            IIC_Wait_Ack();
            IIC_Send_Byte(ReadAddr>>8);//发送高地址
            IIC_Wait_Ack();
        } else IIC_Send_Byte(0XA0+((ReadAddr/256)<<1));   //发送器件地址0XA0,写数据
    
        IIC_Wait_Ack();
        IIC_Send_Byte(ReadAddr%256);   //发送低地址
        IIC_Wait_Ack();
        IIC_Start();
        IIC_Send_Byte(0XA1);//进入接收模式
        IIC_Wait_Ack();
        temp=IIC_Read_Byte(0);
        IIC_Stop();//产生一个停止条件
        return temp;
    }
    //在AT24CXX指定地址写入一个数据
    //WriteAddr  :写入数据的目的地址
    //DataToWrite:要写入的数据
    void AT24CXX_WriteOneByte(u16 WriteAddr,u8 DataToWrite)
    {
        IIC_Start();
        if(EE_TYPE>AT24C16)
        {
            IIC_Send_Byte(0XA0);//发送设备地址
            IIC_Wait_Ack();
            IIC_Send_Byte(WriteAddr>>8);//发送字节高地址
        } else
        {
            IIC_Send_Byte(0XA0+((WriteAddr/256)<<1));   //发送器件地址0XA0,写数据
        }
        IIC_Wait_Ack();
        IIC_Send_Byte(WriteAddr%256);   //发送字节低地址
        IIC_Wait_Ack();
        IIC_Send_Byte(DataToWrite);     //发送要写入的数据
        IIC_Wait_Ack();
        IIC_Stop();//产生一个停止条件
        delay_ms(10);
    }
    #else  //24C16
    /*****************************************************************
    *函数名: AT24CXX_ReadOneByte(u16 ReadAddr)
    *功能:AT24CXX 读指定地址的一个字节   AT24C16使用
    *调用:底层I2C读写函数
    *被调用:外部调用
    *形参:
    			ReadAddr:要读取的地址
    *返回值:返回读取的数据
    *其他:每次读就启动一次I2C时序
    *****************************************************************/
    u8 AT24CXX_ReadOneByte(u16 ReadAddr)
    {
        unsigned char Page=0,WordAddress=0,DeviceAddress=0xA0;
        u8 temp=0;
        Page=ReadAddr/AT24CXX_Page_Size;
        WordAddress=(ReadAddr%AT24CXX_Page_Size) & 0x0F;
        DeviceAddress |= (((Page<<1) & 0xE0)>>4);//High 3 bits
        WordAddress |= (Page & 0x0F)<<4;//Low 4 bits
        IIC_Start();
        IIC_Send_Byte(DeviceAddress&0xFE);//发送设备地址+写方向
        IIC_Wait_Ack();
        IIC_Send_Byte(WordAddress);//发送字节地址
        IIC_Wait_Ack();
        IIC_Start();                //起始信号
        IIC_Send_Byte(DeviceAddress|0x01);//发送设备地址+读方向
        IIC_Wait_Ack();
        temp=IIC_Read_Byte(0);
        IIC_Stop();//产生一个停止条件
        return temp;
    }
    /*****************************************************************
    *函数名: AT24CXX_WriteOneByte(u16 WriteAddr,u8 DataToWrite)
    *功能:AT24CXX 向指定地址写入一个字节   AT24C16使用
    *调用:
    *被调用:外部调用
    *形参:
    			WriteAddr:要写入的地址
    			DataToWrite:写入的数据
    *返回值:无
    *其他:每次写就启动一次I2C时序
    *****************************************************************/
    void AT24CXX_WriteOneByte(u16 WriteAddr,u8 DataToWrite)
    {
        unsigned char Page=0,WordAddress=0,DeviceAddress=0xA0;
        Page=WriteAddr/AT24CXX_Page_Size;
        WordAddress=(WriteAddr%AT24CXX_Page_Size) & 0x0F;
        DeviceAddress |= (((Page<<1) & 0xE0)>>4);//High 3 bits
        WordAddress |= (Page & 0x0F)<<4;//Low 4 bits
    #if DEBUG	> 0
        printf("Page:%x\r\n",Page);
        printf("WordAddress:%x\r\n",WordAddress);
        printf("DeviveAddress:%x\r\n",DeviceAddress);
    #endif
        IIC_Start();
        IIC_Send_Byte(DeviceAddress);//发送设备地址
        IIC_Wait_Ack();
        IIC_Send_Byte(WordAddress);//发送字节地址
        IIC_Wait_Ack();
        IIC_Send_Byte(DataToWrite);     //发送要写入的数据
        IIC_Wait_Ack();
        IIC_Stop();//产生一个停止条件
        delay_ms(10);
    }
    #endif
    
    
    
    /*---------------读写方式选择-----------------*/
    #if  QuickWR == 0
    //在AT24CXX里面的指定地址开始读出指定个数的数据
    //ReadAddr :开始读出的地址 对24c02为0~255
    //pBuffer  :数据数组首地址
    //NumToRead:要读出数据的个数
    void AT24CXX_Read(u16 ReadAddr,u8 *pBuffer,u16 NumToRead)
    {
        while(NumToRead)
        {
            *pBuffer++=AT24CXX_ReadOneByte(ReadAddr++);
            NumToRead--;
        }
    }
    //在AT24CXX里面的指定地址开始写入指定个数的数据
    //WriteAddr :开始写入的地址 对24c02为0~255
    //pBuffer   :数据数组首地址
    //NumToWrite:要写入数据的个数
    void AT24CXX_Write(u16 WriteAddr,u8 *pBuffer,u16 NumToWrite)
    {
        while(NumToWrite--)
        {
            AT24CXX_WriteOneByte(WriteAddr,*pBuffer);
            WriteAddr++;
            pBuffer++;
        }
    }
    #else  //快速读写方式
    /*****************************************************************
    *函数名: AT24CXX_Write_Bytes(u8 *pBuffer,u16 WriteAddress,u8 Len)
    *功能: 页写函数 最多写入一页(16字节)
    *调用: 底层I2C写函数
    *被调用:外部调用
    *形参:
          *pBuffer:指向写入缓存区
    			WriteAddr:要写入的地址
    			Len:写入数据长度
    *返回值:无
    *其他:启动一次I2C时序最多写入一页(16Bytes)数据,明显快于按字节写入
    *****************************************************************/
    void AT24CXX_Write_Bytes(u8 *pBuffer,u16 WriteAddress,u8 Len)
    {
        unsigned char Page=0,WordAddress=0,DeviceAddress=0xA0;
        u8 i=0;
        Page=WriteAddress/AT24CXX_Page_Size;
        WordAddress=(WriteAddress%AT24CXX_Page_Size) & 0x0F;
        DeviceAddress |= (((Page<<1) & 0xE0)>>4);//High 3 bits
        WordAddress |= (Page & 0x0F)<<4;//Low 4 bits
        IIC_Start();
        IIC_Send_Byte(DeviceAddress);//发送设备地址
        IIC_Wait_Ack();
        IIC_Send_Byte(WordAddress);//发送字节地址
        IIC_Wait_Ack();
        for(i=0; i<Len; i++)
        {
            IIC_Send_Byte(*pBuffer++);//发送字节地址
            IIC_Wait_Ack();
        }
        IIC_Stop();//产生一个停止条件
        delay_ms(10);
    }
    /*****************************************************************
    *函数名: AT24CXX_Write(u16 WriteAddr,u8 *pBuffer,u16 NumToWrite)
    *功能:AT24CXX 快速写入不定量字节
    *调用:
    *被调用:外部调用
    *形参:
    			WriteAddr:要写入的首地址
    			*pBuffer:指向写入缓存区
    			NumToWrite:写入的字节数
    *返回值:无
    *其他:快速模式 不用每次写一个字节就启动一次I2C时序
    *****************************************************************/
    void AT24CXX_Write(u16 WriteAddr,u8 *pBuffer,u16 NumToWrite)
    {
        unsigned char NumOfPage = 0, NumOfSingle = 0, Addr = 0, count = 0;
        Addr=WriteAddr%AT24CXX_Page_Size;//地址正好是16字节对齐
        count=AT24CXX_Page_Size-Addr;//不对齐字节数
        NumOfPage=NumToWrite/AT24CXX_Page_Size;//需要写入多少页
        NumOfSingle=NumToWrite%AT24CXX_Page_Size;//剩余需写入的字节数
        if(0==Addr)//如果地址对齐
        {
            if(NumToWrite<=AT24CXX_Page_Size)//写入字节<=1页
            {
                AT24CXX_Write_Bytes(pBuffer,WriteAddr,NumToWrite);
            }
            else
            {
                while(NumOfPage--)//按页写入
                {
                    AT24CXX_Write_Bytes(pBuffer,WriteAddr,AT24CXX_Page_Size);
                    pBuffer+=AT24CXX_Page_Size;
                    WriteAddr+=AT24CXX_Page_Size;
                }
                if(NumOfSingle != 0)//如果还剩下字节
                {
                    AT24CXX_Write_Bytes(pBuffer,WriteAddr,NumOfSingle);//把剩下的字节写入
                }
            }
        }
        else//地址不对齐
        {
            if(NumToWrite<=count)// 要写入的字节数<=count
            {
                AT24CXX_Write_Bytes(pBuffer,WriteAddr,NumToWrite);//写入实际字节数
            }
            else//要写入字节数大于count
            {
                AT24CXX_Write_Bytes(pBuffer,WriteAddr,count);//现将count个字节写入 写入后 地址刚好对齐
                NumToWrite-=count;//计算剩余字节数
                pBuffer+=count;//写入内容偏移count
                WriteAddr+=count;//写入地址偏移count
    
                NumOfPage=NumToWrite/AT24CXX_Page_Size;//需要写入多少页
                NumOfSingle=NumToWrite%AT24CXX_Page_Size;//剩余需写入的字节数
    
                while(NumOfPage--)//先按页写入
                {
                    AT24CXX_Write_Bytes(pBuffer,WriteAddr,AT24CXX_Page_Size);
                    pBuffer+=AT24CXX_Page_Size;
                    WriteAddr+=AT24CXX_Page_Size;
                }
                if(NumOfSingle != 0)//还剩余字节
                {
                    AT24CXX_Write_Bytes(pBuffer,WriteAddr,NumOfSingle);//把剩下的字节写入
                }
            }
        }
    }
    
    void AT24CXX_Read(u16 ReadAddr,u8 *pBuffer,u16 NumToRead)
    {
        unsigned char Page=0,WordAddress=0,DeviceAddress=0x50;
        Page=ReadAddr/AT24CXX_Page_Size;
        WordAddress=(ReadAddr%AT24CXX_Page_Size) & 0x0F;
        DeviceAddress |= (((Page<<1) & 0xE0)>>4);//High 3 bits
        WordAddress |= (Page & 0x0F)<<4;//Low 4 bits
        while(NumToRead)
        {
            *pBuffer++=AT24CXX_ReadOneByte(ReadAddr++);
            NumToRead--;
        }
    }
    
    #endif  //快速读写方式
    
    //检查AT24CXX是否正常
    //这里用了24XX的最后一个地址(255)来存储标志字.
    //如果用其他24C系列,这个地址要修改
    //返回1:检测失败
    //返回0:检测成功
    u8 AT24CXX_Check(void)
    {
        u8 temp;
        temp=AT24CXX_ReadOneByte(255);//避免每次开机都写AT24CXX
        if(temp==0X55)return 0;
        else//排除第一次初始化的情况
        {
            AT24CXX_WriteOneByte(255,0X55);
            temp=AT24CXX_ReadOneByte(255);
            if(temp==0X55)return 0;
        }
        return 1;
    }
    
    
    

    AT24C16.h

    #ifndef __24CXX_H
    #define __24CXX_H
    #include "myiic.h"
    //Mini STM32开发板
    //24CXX驱动函数(适合24C01~24C16,24C32~256未经过测试!有待验证!)
    //正点原子@ALIENTEK
    //2010/6/10
    //V1.2
    #define AT24C01		127
    #define AT24C02		255
    #define AT24C04		511
    #define AT24C08		1023
    #define AT24C16		2047
    #define AT24C32		4095
    #define AT24C64	    8191
    #define AT24C128	16383
    #define AT24C256	32767
    
    
    /*----------------EEPROM相关配置--------------------*/
    #define EE_TYPE AT24C16  //EEPROM类型
    #define AT24CXX_Page_Size 16 //AT24C16每页有16个字节
    #define DEBUG   0  //串口调试开关
    #define QuickWR 0 //快速读写开关
    
    
    u8 AT24CXX_ReadOneByte(u16 ReadAddr);							//指定地址读取一个字节
    void AT24CXX_WriteOneByte(u16 WriteAddr,u8 DataToWrite);		//指定地址写入一个字节
    void AT24CXX_Write(u16 WriteAddr,u8 *pBuffer,u16 NumToWrite);	//从指定地址开始写入指定长度的数据
    void AT24CXX_Read(u16 ReadAddr,u8 *pBuffer,u16 NumToRead);   	//从指定地址开始读出指定长度的数据
    
    u8 AT24CXX_Check(void);  //检查器件
    void AT24CXX_Init(void); //初始化IIC
    #endif
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    

    main.c

    #include "led.h"
    #include "delay.h"
    #include "sys.h"
    #include "usart.h"
    #include "24cxx.h" 
    #include "myiic.h"
    #include "stdio.h"
    //要写入到24c16的字符串数组
    const u8 TEXT_Buffer[]={"C++ is the best language!"};//要写入的内容
    #define SIZE sizeof(TEXT_Buffer)	//写入内容的大小
    #define ADDRESS 2020	//读写地址
     int main(void)
     { 
    	u8 datatemp[SIZE];
    	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);// 设置中断优先级分组2
    	delay_init();	    	 //延时函数初始化	  
    	uart_init(9600);	 	//串口初始化为9600
    	LED_Init();		  		//初始化与LED连接的硬件接口		 	
    	AT24CXX_Init();			//IIC初始化 	
     	while(AT24CXX_Check())//检测不到24c16
    	{		
    		delay_ms(500);
    		LED0=!LED0;//DS0闪烁
    	}	  
    	while(1)
    	{			
    			   		
    			AT24CXX_Write(ADDRESS,(u8*)TEXT_Buffer,SIZE);
    			printf("Write:%s\r\n",TEXT_Buffer); //显示写入内容		
    		    delay_ms(1000);		
    			AT24CXX_Read(ADDRESS,datatemp,SIZE);
    			printf("Read:%s\r\n",datatemp);//显示读取内容 						
    	}
    }
    

     演示结果:

    注意:上面的代码可以支持24C01/02 ,将AT24CXX.h中的宏定义EE_TYPE 改为AT24C01/02即可

    展开全文
  • EEPROM AT24C16读写注意事项

    千次阅读 2019-09-19 11:30:15
    AT24C16每页有16个字节,128页,共2048字节。128页分成8块,每块256字节。 I2C开始信号后,第一个字节为器件地址,由1010+3位块地址+1位读写标志组成, 3位块地址刚好可以表示8个块, 8块的器件地址分别为:0xa0、0...

    AT24C16每页有16个字节,128页,共2048字节。128页分成8块,每块256字节。

    I2C开始信号后,第一个字节为器件地址,由1010+3位块地址+1位读写标志组成, 3位块地址刚好可以表示8个块, 8块的器件地址分别为:0xa0、0xa2、0xa4、0xa6、0xa8、0xaa、0xac、0xae;

    写数据时,可连续写完256字节,换到下一块时,需要要重新更改器件地址。

    此处需特别说明,写第一块时,地址从0xa000~0xa0ff;写满后,需更新器件地址,从0xa200开始。如果写到0xa0ff后继续增加字节,地址变为0xa100,此时数据不会写到第二块,而是从第一块首地址开始覆盖。 0xa100其实是读数据地址。

    读数据时,不受页、块限制,可以从给定地址一直读下去。

     


    补充说明一点:

    写数据时,还受页限制,用循环写入数据时,一次最多只能写入16个字节,超出1页地址的数据不能被写入,也不会从页开始覆盖原有数据(这个跟块有区别)

    详细一点说

    /* 循环发送 NumByteToWrite 个数据 */
        while(NumByteToWrite--)  
        {
            I2C_SendData(EEPROM_I2C, *pBuffer); 
            pBuffer++; 
            I2CTimeout = I2CT_FLAG_TIMEOUT;
            while (!I2C_CheckEvent(EEPROM_I2C, I2C_EVENT_MASTER_BYTE_TRANSMITTED))
            {
                if((I2CTimeout--) == 0) return I2C_TIMEOUT_UserCallback(8);
            } 
        }

    这样一个循环是不可以跨页写数据的。

     

    保存自己写的两个函数,以备以后用到,这两个函数经实际应用无误。

    /**
      * @brief   在EEPROM的一个写循环中可以写多个字节,但一次写入的字节数
      *          不能超过EEPROM页的大小,AT24C16每页有16个字节
      * @param   
      *        @arg pBuffer:缓冲区指针
      *        @arg WriteAddr:写地址
      *     @arg NumByteToWrite:写的字节数
      * @retval  无
      */
    uint32_t I2C_EE_PageWrite(u8* pBuffer, u16 WriteAddr, u16 NumByteToWrite)
    {
        u8 EquipAddr = 0, PageAddr = 0;
        EquipAddr = (WriteAddr >> 8) & 0xfe;
        PageAddr = WriteAddr & 0xff;

        I2CTimeout = I2CT_LONG_TIMEOUT;
        while(I2C_GetFlagStatus(EEPROM_I2C, I2C_FLAG_BUSY))    //等空闲
        {
            if((I2CTimeout--) == 0) return I2C_TIMEOUT_UserCallback(4);
        } 
      
        I2C_GenerateSTART(EEPROM_I2C, ENABLE);     /* 产生起始信号 */
        I2CTimeout = I2CT_FLAG_TIMEOUT;    
        while(!I2C_CheckEvent(EEPROM_I2C, I2C_EVENT_MASTER_MODE_SELECT))  /* 检测 EV5 事件并清除标志*/
        {
            if((I2CTimeout--) == 0) return I2C_TIMEOUT_UserCallback(5);
        } 
            
        I2C_Send7bitAddress(EEPROM_I2C, EquipAddr,I2C_Direction_Transmitter); /* 发送 EEPROM 设备地址 */
        I2CTimeout = I2CT_FLAG_TIMEOUT;    
        while (!I2C_CheckEvent(EEPROM_I2C,I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED)) /* 检测 EV6 事件并清除标志*/
        {
            if ((I2CTimeout--) == 0) return I2C_TIMEOUT_UserCallback(6);
        }

        I2C_SendData(EEPROM_I2C, PageAddr);
        //I2C_Send7bitAddress(EEPROM_I2C, PageAddr , I2C_Direction_Transmitter);  /* 发送要写入的 EEPROM 内部地址(即 EEPROM 内部存储器的地址) */
        I2CTimeout = I2CT_FLAG_TIMEOUT;    
        while(!I2C_CheckEvent(EEPROM_I2C, I2C_EVENT_MASTER_BYTE_TRANSMITTED)) /* 检测 EV8 事件并清除标志*/
        {
            if((I2CTimeout--) == 0) return I2C_TIMEOUT_UserCallback(7);
        } 
         
         /* 循环发送 NumByteToWrite 个数据 */
        while(NumByteToWrite--)  
        {
            I2C_SendData(EEPROM_I2C, *pBuffer); 
            pBuffer++; 
            I2CTimeout = I2CT_FLAG_TIMEOUT;
            while (!I2C_CheckEvent(EEPROM_I2C, I2C_EVENT_MASTER_BYTE_TRANSMITTED))
            {
                if((I2CTimeout--) == 0) return I2C_TIMEOUT_UserCallback(8);
            } 
        }

        /* 发送停止信号 */
        I2C_GenerateSTOP(EEPROM_I2C, ENABLE);
        return 1;
    }

    /**
      * @brief   将缓冲区中的数据写到I2C EEPROM中
      * @param   
      *        @arg pBuffer:缓冲区指针
      *        @arg WriteAddr:写地址
      *     @arg NumByteToWrite:写的字节数,不能超过要写入块的容量
      * @retval  无
      */
    void I2C_EE_BufferWrite(u8* pBuffer, u16 WriteAddr, u16 NumByteToWrite)
    {
        u8 NumOfPage = 0, NumOfSingle = 0, Addr = 0, count = 0,temp = 0;

        Addr = WriteAddr % I2C_PageSize;
        count = I2C_PageSize - Addr;   
        //开始写入的地址是EEPROM的第WriteAddr /I2C_PageSize + 1页,该页还剩余count个字节
        
        NumOfPage =  NumByteToWrite / I2C_PageSize;   //计算出要写多少整数页
        NumOfSingle = NumByteToWrite % I2C_PageSize;

        //Addr=0,开始写入的地址是某页的开始
        if(Addr == 0) 
        {
            if(NumOfPage == 0)  //要写入的字节数不足一页
            {
                I2C_EE_PageWrite(pBuffer, WriteAddr, NumOfSingle);
                I2C_EE_WaitEepromStandbyState();
            }  
            else  
            {
                while(NumOfPage--)
                {
                    I2C_EE_PageWrite(pBuffer, WriteAddr, I2C_PageSize); 
                    I2C_EE_WaitEepromStandbyState();
                    WriteAddr +=  I2C_PageSize;
                    pBuffer += I2C_PageSize;
                }
                if(NumOfSingle!=0)
                {
                    I2C_EE_PageWrite(pBuffer, WriteAddr, NumOfSingle);
                    I2C_EE_WaitEepromStandbyState();
                }
            }  //end if(NumOfPage == 0) 
        }
        else   //开始写入的地址是某页的中间某位置
        {
            if(NumOfPage== 0) 
            {            
                if (NumOfSingle > count) //当前面写不完,要写到下一页
                {                
                    temp = NumOfSingle - count;  // temp 的数据要写到下一页
                    I2C_EE_PageWrite(pBuffer, WriteAddr, count);
                    I2C_EE_WaitEepromStandbyState();
                    WriteAddr += count;
                    pBuffer += count;

                    I2C_EE_PageWrite(pBuffer, WriteAddr, temp);
                    I2C_EE_WaitEepromStandbyState();
                } 
                else 
                { 
                    I2C_EE_PageWrite(pBuffer, WriteAddr, NumByteToWrite);
                    I2C_EE_WaitEepromStandbyState();
                }
            }
            else
            {
                //先把当前页剩余的count个字节写完,再将剩余的NumByteToWrite - count个字节写到后续页中
                NumByteToWrite -= count;
                NumOfPage =  NumByteToWrite / I2C_PageSize;
                NumOfSingle = NumByteToWrite % I2C_PageSize;    

                if(count != 0)
                {  
                    I2C_EE_PageWrite(pBuffer, WriteAddr, count);
                    I2C_EE_WaitEepromStandbyState();
                    WriteAddr += count;
                    pBuffer += count;
                } 

                while(NumOfPage--)
                {
                    I2C_EE_PageWrite(pBuffer, WriteAddr, I2C_PageSize);
                    I2C_EE_WaitEepromStandbyState();
                    WriteAddr +=  I2C_PageSize;
                    pBuffer += I2C_PageSize;  
                }
                if(NumOfSingle != 0)
                {
                    I2C_EE_PageWrite(pBuffer, WriteAddr, NumOfSingle); 
                    I2C_EE_WaitEepromStandbyState();
                }
            }
        }  
    }

     

    展开全文
  • 马伟力AVR笔记:九、基于AT24C16的数据存储实验.pdf
  • AT24C16A-10PI-2.7.pdf

    2010-01-11 11:46:52
    AT24C16A-10PI-2.7.pdfAT24C16A-10PI-2.7.pdf
  • AT24C16 读写注意点

    2020-12-22 19:36:04
    这篇文章介绍了AT24C16的页写、连续读、写保护功能:https://blog.csdn.net/liangbin414/article/details/88293321 页写算法我是参考这篇文章的:https://acuity.blog.csdn.net/article/details/78550427?utm_m 总...
  • IIC方式读驱动AT24C16芯片

    千次阅读 2018-09-01 22:37:59
    闲来无事,找了块msp430的板子编写了个IIC驱动AT24C16的程序。 IIC作是一种简单,双向,同步的二进制总线,由SDA数据线和SCL时钟线组成,所有接到IIC总线上的各设备的SDA数据线都连接到总线的SDA数据线上,用来进行...
  • 名称:C51IO口模拟I2C总线驱动AT24C16 说明:关于EEPROM,即这里的AT24C16是一个特殊形式的FLASH存储器,不过其容量一般较少。比较适合于存储少量的数据。 AT24C16的通信接口是标准的I2C通信,即我们需要根据I2C...
  • STM32F030 硬件I2C驱动 AT24C16

    千次阅读 2016-10-21 20:17:33
    在此十分感谢 畅学电子网 的对于AT24C16的资料,特别是AT24C16地址的解释。调试过程中这篇文章给了很大的帮助。建议不想只当伸手党的同志们认真阅读,否则只会Ctrl C Ctrl V,你又怎么能说自己是嵌入
  • 基于NIOS的I_2C总线接口芯片AT24C16读写的实现
  • Proteus8.9的VSM Studio使用的SDCC仿真_STC15W4k32S4_013_iic_04_AT24C16编程代码和仿真操作实验
  • 因为论坛里看到STM的I2C有点小bug,所以这里采用的是模拟I2C时序 ...对AT24C16的操作有读和写,读又分为CURRENT ADDRESS READ、RANDOM READ、SEQUENTIAL READ ,写又分为BYTE WRITE、PAGE WRITE。 WRITE...
  • AT24C16读写注意事项

    万次阅读 多人点赞 2019-01-06 19:31:11
    AT24C01/02/04/08/16的外形级封装和引脚说明 AT24C系列为美国ATMEL公司推出的串行COMS型E2PROM,是典型的串行通信E2PROM 。 AT24CXX是IIC总线串行器件,具有工作电源宽(1.8~6.0 V),抗干扰能力强(输入引脚内置...
  • AT24C16 STM32 外部EEPROM

    2019-05-07 10:53:33
    每页16字节,总共128页,亲测可用,如果有不懂的,可以在下方评论。

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 7,762
精华内容 3,104
关键字:

at24c16