AT24C02学习笔记

看手册:

AT24Cxx xx代表能写入xxK bit=(xx K)/8 byte   

内部写周期很关键,代表每一次页写或字节写结束后时间要大于5ms(延时5ms确保完成写周期),否则时序会出错。

页写:型不同号每一页可能写入不同大小的字节数

AT24C02:         一页8字节,地址第一页可写地址0x00~0x07

AT24C04/08/16: 一页16字节,地址第一页可写地址0x00~0x0f

AT24C32/64:      一页32字节,地址第一页可写地址0x00~0x1f

基本的读写操作(字节读写,页写,随机读,序列读)芯片内部会自动递增地址,无需在代码里操作地址

芯片引脚

器件地址(7位):

AT24C02: 前四位固定1010,后3位根据芯片引脚接高低电平决定

AT24C04: 前四位固定1010,A2、A1位根据芯片引脚接高低电平决定,后一位必须接地。

AT24C08: 前四位固定1010,A2位根据芯片引脚接高低电平决定,后两位必须接地。

AT24C16: 前四位固定1010,后3位必须接地。

读写位:读1  、写0

AT24Cxx系列优点就是低功耗

基本读写操作

字节写:

I2C时序通信:发出起始条件->发送8位地址(器件地址7位+读写位0)->主机接收应答(从机发送应答)->发送字地址(芯片内部)->主机接收应答(从机发送应答)->发送8位数据->主机接收应答(从机发送应答)->发出停止条件->等待5ms(延时5ms)保证完成写周期;

页写:

I2C时序通信:发出起始条件->发送8位地址(器件地址7位+读写位0)->主机接收应答(从机发送应答)->发送字地址(芯片内部)->主机接收应答(从机发送应答)->发送8位数据->主机接收应答(从机发送应答)->…………(最多发送8个数据否则会覆盖先前写入的数据(字节))->发出停止条件->等待5ms(延时5ms)保证完成写周期;

内部写周期机制:发送写命令->……->发送停止信号,启动写周期->可以通过发送I2起始信号->(1)(发送一个字节->读取EEPROM应答判断芯片是否应答0 )->不是就重复操作(1)->是,就进行下一个操作

随机读一个数据:

发出起始条件->发送8位地址(器件地址7位+读写位0)->主机接收应答(从机发送应答)->发送字地址(芯片内部)->主机接收应答(从机发送应答)->发出起始条件->发送8位地址(器件地址7位+读写位1)->主机接收应答(从机发送应答)->可用指针来操作(存储)读取8位数据->主机发送不应答->发送停止信号

顺序读(连续读数据):

发出起始条件->发送8位地址(器件地址7位+读写位0)->主机接收应答(从机发送应答)->发送字地址(芯片内部)->主机接收应答(从机发送应答)->发出起始条件->发送8位地址(器件地址7位+读写位1)->主机接收应答(从机发送应答)->可用指针来操作(存储)读取8位数据->主机发送应答->……(直到主机不想接受了)->主机发送不应答->发送停止信号

原理图

AT24C02实现连续页写思路:

用一个变量来判断当前地址还可写入多少个字节,判断是否要跨页写,判断为需要跨页,先写完本页,再通过地址偏移(+前一页还可写入的字节数)得到下一页的地址,在进行一页写操作,如果写完一页,还没停止还要写,再通过地址偏移(+一页可写的字节数)得到下一页的地址,再进行重复操作。

总代码

AT24C02.c

#include "AT24C02.h"                  // Device header
#include "Delay.h"

//AT24C02模块地址0x01010111 0x57  0x10101111 AF AE

#define AT24C02_ADDRESS (0xA0)

void AT24C02_W_SCL(uint8_t x)
{
    GPIO_WriteBit(GPIOB,GPIO_Pin_10,(BitAction) x);
    Delay_us(10); 
}

void AT24C02_W_SDA(uint8_t x)
{
    GPIO_WriteBit(GPIOB,GPIO_Pin_11,(BitAction) x);
    Delay_us(10); 
}

uint8_t AT24C02_R_SDA(void)
{
    uint8_t BitVal;
    BitVal = GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_11);
    Delay_us(10);
    return BitVal;    
}

void AT24C02_Init(void)
{
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);	//开启GPIOB的时钟
	
	/*GPIO初始化*/
	GPIO_InitTypeDef GPIO_InitStructure;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_OD;
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10 | GPIO_Pin_11;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOB, &GPIO_InitStructure);					//将PB10和PB11引脚初始化为开漏输出
	
	/*设置默认电平*/
	GPIO_SetBits(GPIOB, GPIO_Pin_10 | GPIO_Pin_11);			//设置PB10和PB11引脚初始化后默认为高电平(释放总线状态)
}

void AT24C02_Start(void)
{ 
    AT24C02_W_SDA(1) ;
    AT24C02_W_SCL(1) ;
   
    AT24C02_W_SDA(0) ;
    AT24C02_W_SCL(0) ;
}

void AT24C02_Stop(void)
{
    AT24C02_W_SDA(0) ;
    AT24C02_W_SCL(1) ;
    AT24C02_W_SDA(1) ;
}


void AT24C02_SendByte(uint8_t Byte)
{
    uint8_t i;
    for( i=0;i<8;i++)
    {   
      AT24C02_W_SDA(Byte &(0x80 >> i));//从最高位一位一位移出
      AT24C02_W_SCL(1);
      AT24C02_W_SCL(0);//用完拉低时钟线,防止时序出差
    }
   
}

uint8_t AT24C02_RecviceByte(void)
{   uint8_t i;
    uint8_t Byte = 0x00; 
    AT24C02_W_SDA(1);//主机放开对数据线的控制
    for(i=0;i<8;i++)
    {
        AT24C02_W_SCL(1);//时钟线高电平期间读取时钟线
        if(AT24C02_R_SDA()==1)   {Byte |= (0x80>>i);} 
        AT24C02_W_SCL(0);
    }
    return Byte;
}

void AT24C02_SendAck(uint8_t x)
{
    //AT24C02_W_SCL(0);
    AT24C02_W_SDA(x);
    AT24C02_W_SCL(1);
    AT24C02_W_SCL(0);//用完拉低时钟线,防止时序出差
}

uint8_t AT24C02_RecviceAck(void)
{
    uint8_t Ack=0;
    AT24C02_W_SDA(1);//主机放开对数据线的控制
    
    AT24C02_W_SCL(1);//时钟线高电平期间读取时钟线
    Ack = AT24C02_R_SDA();
    AT24C02_W_SCL(0);//用完拉低时钟线,防止时序出差
    return Ack; 
}

uint8_t AT24C02_WriteByte(uint8_t address,uint8_t data)
{
    AT24C02_Start();
    AT24C02_SendByte(AT24C02_ADDRESS);
    if(AT24C02_RecviceAck()) return 1;
    
    AT24C02_SendByte(address);
    if(AT24C02_RecviceAck()) return 2;
    
    AT24C02_SendByte(data);
    if(AT24C02_RecviceAck()) return 3;
    
    AT24C02_Stop();
    Delay_ms(5);//重点,最少要满足AT24C02的写循环的最小周期
    return 0;
}


uint8_t AT24C02_ReadByte(uint8_t address,uint8_t *data )
{
    AT24C02_Start();
    AT24C02_SendByte(AT24C02_ADDRESS);
    if(AT24C02_RecviceAck()) return 1;
    
    AT24C02_SendByte(address);
    if(AT24C02_RecviceAck()) return 2; 

    AT24C02_Start();   
    AT24C02_SendByte(AT24C02_ADDRESS+1);
    if(AT24C02_RecviceAck()) return 3;
    
    *data = AT24C02_RecviceByte();
    
    AT24C02_SendAck(1);
    AT24C02_Stop();
    Delay_ms(5);//重点,最少要满足AT24C02的写循环的最小周期
    return 0;
}

uint8_t AT24C02_PageWrite(uint8_t address,uint8_t *str,uint8_t count)
{
   
    AT24C02_Start();
    AT24C02_SendByte(AT24C02_ADDRESS);
    if(AT24C02_RecviceAck()) return 1;
    
    AT24C02_SendByte(address);
    if(AT24C02_RecviceAck()) return 2; 
    
    
    
    for (uint8_t i = 0; i < count; i++)
    {
        AT24C02_SendByte(*str++);
        if (AT24C02_RecviceAck()) return 3;
    
    }
   
//    AT24C02_SendAck(1);  //无需应答
    AT24C02_Stop();
    Delay_ms(5);
    return 0;
}   

uint8_t AT24C02_NPageWrite(uint8_t address,uint8_t *str,uint8_t count)
{
    uint8_t num =  address%8 +count  ; //判读是否跨页

   if(num>8)
   {
       while(count)
      {
            uint8_t temp =  8 - address%8  ;
            if(count>=temp)
            {
                AT24C02_PageWrite(address,str,temp);  
                address +=temp;
                str += temp;
                count -= temp;               
            }
            else
            {
                AT24C02_PageWrite(address,str,count);
                address +=count;
                str += count;
                count -= count;                             
            }
        }
   }        
    else
    {
        AT24C02_PageWrite(address,str,count);
       
    }
    return 0;
}   
    
u8 AT24C02_NReadData(uint8_t address,uint8_t *str,uint8_t count)
{
    
    AT24C02_Start();
    AT24C02_SendByte(AT24C02_ADDRESS);
    if(AT24C02_RecviceAck()) return 1;
    
    AT24C02_SendByte(address);
    if(AT24C02_RecviceAck()) return 2; 

    AT24C02_Start(); 
    AT24C02_SendByte(AT24C02_ADDRESS+1);
    if(AT24C02_RecviceAck()) return 3;
    
   for(uint8_t i = 0;i<count-1;i++)
   {   
       *str = AT24C02_RecviceByte();
        AT24C02_SendAck(0);
        str++;
        count--;
        
   } 
    *str = AT24C02_RecviceByte();
    AT24C02_SendAck(1);
    AT24C02_Stop();
   // Delay_ms(5);//重点,最少要满足AT24C02的写循环的最小周期
    return 0;
}    

AT24C02.h

#ifndef     _AT24C02_H_
#define     _AT24C02_H_

#include "stm32f10x.h"                  // Device header

//void AT24C02_WriteByte(uint8_t WordAddress,uint8_t data);
//uint8_t AT24C02_ReadByte(uint8_t WordAddress);
uint8_t AT24C02_WriteByte(uint8_t address,uint8_t data);
uint8_t AT24C02_ReadByte(uint8_t address,uint8_t *data );
uint8_t AT24C02_PageWrite(uint8_t address,uint8_t *str,uint8_t count);
uint8_t AT24C02_NPageWrite(uint8_t address,uint8_t *str,uint8_t count);

void AT24C02_Init(void);

u8 AT24C02_NReadData(u8 page_Address,u8 *buf,u8 Datalen);


#endif

FPGA读EEPROM芯片AT24C02实验Verilog逻辑源码Quartus11.0工程文件, FPGA型号为CYCLONE4E系列中的EP4CE6E22C8,可以做为你的学习设计参考。 module iic_com( clk,rst_n, sw1,sw2, scl,sda, dis_data ); input clk; // 50MHz input rst_n; //复位信号,低有效 input sw1,sw2; //按键1、2,(1按下执行操作,2按下执行读操作) output scl; // 24C02的时钟端口 inout sda; // 24C02的数据端口 output[7:0] dis_data; //数码管显示的数据 //按键检测 reg sw1_r,sw2_r; //键值锁存寄存器,每20ms检测一次键值 reg[19:0] cnt_20ms; //20ms计数寄存器 always @ (posedge clk or negedge rst_n) begin if(!rst_n) cnt_20ms <= 20'd0; else cnt_20ms <= cnt_20ms+1'b1; //不断计数 end always @ (posedge clk or negedge rst_n) begin if(!rst_n) begin sw1_r <= 1'b1; //键值寄存器复位,没有键盘按下时键值都为1 sw2_r <= 1'b1; end else if(cnt_20ms == 20'hfffff) begin sw1_r <= sw1; //按键1值锁存 sw2_r <= sw2; //按键2值锁存 end end //--------------------------------------------- //分频部分 reg[2:0] cnt; // cnt=0:scl上升沿,cnt=1:scl高电平中间,cnt=2:scl下降沿,cnt=3:scl低电平中间 reg[8:0] cnt_delay; //500循环计数,产生iic所需要的时钟 reg scl_r; //时钟脉冲寄存器 always @ (posedge clk or negedge rst_n) begin if(!rst_n) cnt_delay <= 9'd0; else if(cnt_delay == 9'd499) cnt_delay <= 9'd0; //计数到10us为scl的周期,即100KHz else cnt_delay <= cnt_delay+1'b1; //时钟计数 end always @ (posedge clk or negedge rst_n) begin if(!rst_n) cnt <= 3'd5; else begin case (cnt_delay) 9'd124: cnt <= 3'd1; //cnt=1:scl高电平中间,用于数据采样 9'd249: cnt <= 3'd2; //cnt=2:scl下降沿 9'd374: cnt <= 3'd3; //cnt=3:scl低电平中间,用于数据变化 9'd499: cnt <= 3'd0; //cnt=0:scl上升沿 default: cnt <= 3'd5; endcase end end `define SCL_POS (cnt==3'd0) //cnt=0:scl上升沿 `define SCL_HIG (cnt==3'd1) //cnt=1:scl高电平中间,用于数据采样 `define SCL_NEG (cnt==3'd2) //cnt=2:scl下降沿 `define SCL_LOW (cnt==3'd3) //cnt=3:scl低电平中间,用于数据变化 always @ (posedge clk or negedge rst_n) begin if(!rst_n) scl_r <= 1'b0; else if(cnt==3'd0) scl_r <= 1'b1; //scl信号上升沿
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值