基于STM32的AT21CS01 EEPROM驱动代码(MASTER)

文章涉及到的源码链接:点击这里

1 AT21CS01/ AT21CS11

        AT21CS01/11 是一种 2 引脚存储器(SI/O 信号和地),其从信号引脚获取电能,从而为集成电路供电。它提供 1024 位串行电可擦除的可编程只读存储器(Electrically-Erasable Programmable Read-Only Memory,EEPROM),该存储器划分为 128 个 8 位字。
        该器件经过了优化,可使用两点机械连接(仅将一个信号(SI/O)和 GND 连接到无电配件)在无电配件中添加配置和使用信息。部分无电配件应用示例包括模拟传感器校准数据存储、打印机墨水/墨粉盒识别以及售后市场消耗品的管理。该器件的软件寻址方案允许最多 8 个器件共享一条通用单线总线。该器件提供多种节省空间的封装选项,SI/O 线的外部上拉电压范围为 1.7V 至 3.6V(AT21CS01)/2.7V 至 4.5V(AT21CS11)。
        AT21CS01/ AT21CS11使用单线串行接口,采用 I2C 协议结构,通过单个 I/O 引脚来实现器件通信。
Alt

封装类型
 

Alt

使用单线串行 EEPROM 的系统配置
 

框图

框图
 

2 复位ack时序

Alt

        在器件上电后或主器件复位器件(将 SI/O 线保持低电平并持续 tRESET或 tDSCHG)后,主器件必须释放SI/O 线,该线随后将通过外部上拉电阻拉为高电平。之后,主器件必须再等待一段最小 tRRT 时间,然后才能请求从器件获取发现响应应答。
        在发现响应应答序列中,首先是主器件将 SI/O 线驱动为低电平,这将启动 AT21CS01/11 内部时序电路。主器件必须继续将此线驱动为低电平并持续 tDRR
        在 tDRR 时间内,AT21CS01/11 将以并发的方式将 SI/O 驱动为低电平来进行响应。器件会继续将 SI/O 线驱动为低电平,总时长为 tDACK。主器件应从 tDRR 开始的 tMSDR 后对 SI/O 线的状态进行采样。按照定义,最小 tDACK 时间应比最大 tMSDR 时间长,从而确保主器件始终能够正确地从 SI/O 线上采样到低于 VIL 的电压。在 tDACK 时间过后,AT21CS01/11 将释放 SI/O 线,该线随后通过外部上拉电阻拉为高电平。

代码:

Ack_t AT21CS01_Reset(void)
{
    u8 res = 0;

    AT21CS01_PowerOn();
	delay_us(20);
	/*for(int i = 0; i < 70 ; i++)*/delay_ms(100);
	
    AT21CS01_IO_L();
    delay_us(400);	//RESET
    AT21CS01_IO_H();
    delay_us(10);	//T_RRT
    AT21CS01_IO_L();
    delay_us(1);	//T_MSDR  2~6us
    AT21CS01_IO_Input();
	delay_us(1);
    res = GPIO_ReadInputDataBit(AT21CS01_IO_PORT, AT21CS01_IO_PIN);	//read ACK
    AT21CS01_IO_Output();
    AT21CS01_IO_H();
    return res ? NACK : ACK;	//read 1 -> NACK
}

3 发送时序

主机逻辑0发送波形
在这里插入图片描述
主机逻辑1发送波形在这里插入图片描述
代码:

Ack_t AT21CS01_SendByte(u8 data)
{
    u8 i = 0, res = 0;

    AT21CS01_IO_Output();
    for(; i < 8; i++)
    {
        if(data & 0x80)	//高位先发,高位为1
        {
            if(SPEED_MODE == HIGH_SPEED)	//高速模式
            {
                AT21CS01_IO_L();
                delay_us(TLOW_1_HighSpeed);		//发送逻辑1中的低电平
                AT21CS01_IO_H();
                delay_us(THIGH_1_HighSpeed);	//发送逻辑1中的高电平
            }
            else							//标速模式
            {
                AT21CS01_IO_L();
                delay_us(TLOW_1_StandardSpeed);	//发送逻辑1中的低电平
                AT21CS01_IO_H();
                delay_us(THIGH_1_StandardSpeed);//发送逻辑1中的高电平
            }
        }
        else								//高位先发,高位为0
        {
            if(SPEED_MODE == HIGH_SPEED)	//高速模式
            {
                AT21CS01_IO_L();
                delay_us(TLOW_0_HighSpeed);	//发送逻辑0中的低电平
                AT21CS01_IO_H();
                delay_us(THIGH_0_HighSpeed);	//发送逻辑0中的高电平
            }
            else								//标速模式
            {
                AT21CS01_IO_L();
                delay_us(TLOW_0_StandardSpeed);	//发送逻辑0中的低电平
                AT21CS01_IO_H();
                delay_us(THIGH_0_StandardSpeed);	//发送逻辑0中的高电平
            }
        }
        data <<= 1;
    }
    AT21CS01_IO_L();	//拉低准备接收ACK
    if(SPEED_MODE == HIGH_SPEED)
    {
        delay_us(TWACK_HighSpeed);
    }
    else
    {
        delay_us(TWACK_StandardSpeed);
    }
    AT21CS01_IO_Input();
    __nop();
    res = GPIO_ReadInputDataBit(AT21CS01_IO_PORT, AT21CS01_IO_PIN);	//read ACK
    AT21CS01_IO_Output();
    AT21CS01_IO_H();	//释放ACK结束
    if(SPEED_MODE == HIGH_SPEED)
    {
        delay_us(TWACKHIGH_HighSpeed);
    }
    else
    {
        delay_us(TWACKHIGH_StandardSpeed);
    }
    return res ? NACK : ACK;
}

4 接收时序

从机逻辑0反馈波形
在这里插入图片描述
从机逻辑1反馈波形
在这里插入图片描述
代码:

u8 AT21CS01_ReadByte(Ack_t ack)
{
    u8 i = 0, res = 0;

    AT21CS01_IO_Output();
    for(; i < 8; i++)
    {
        res <<= 1;
        AT21CS01_IO_L();
        if(SPEED_MODE == HIGH_SPEED)
        {
            delay_us(TRD_HighSpeed);
        }
        else
        {
            delay_us(TRD_StandardSpeed);
        }
        AT21CS01_IO_Input();
		delay_us(1);	//上拉时间
        res |= GPIO_ReadInputDataBit(AT21CS01_IO_PORT, AT21CS01_IO_PIN);
        AT21CS01_IO_Output();
        AT21CS01_IO_H();
        if(SPEED_MODE == HIGH_SPEED)
        {
            delay_us(TRDHIGH_HighSpeed);
        }
        else
        {
            delay_us(TRDHIGH_StandardSpeed);
        }
    }
    if(SPEED_MODE == HIGH_SPEED)
    {
        if(ack == ACK)
        {
            MACK_HighSpeed();
        }
        else//if(ack == NACK)
        {
            MNACK_HighSpeed();
        }
    }
    else
    {
        if(ack == ACK)
        {
            MACK_StandardSpeed();
        }
        else//if(ack == NACK)
        {
            MNACK_StandardSpeed();
        }
    }
    return res;
}

5 连续发送/接收示例

//连续发送
void AT21CS01_SendData(u8* data, u16 len)
{
    while(len--)
    {
        AT21CS01_SendByte(*data);
        data++;
    }
}
//连续接收
void AT21CS01_ReadData(u8* buffer, u16 len)
{
    int i = 0;
    for( ; i < len - 1; i++)
    {
        buffer[i] = AT21CS01_ReadByte(ACK);
    }
    buffer[i] = AT21CS01_ReadByte(NACK);
}

6 EEPROM访问

        EEPROM区有多种读写方式,下面以常用的随机地址连续读取和页写操作为例。

6.1 EEPROM读取

        随机读操作以与字节写操作相同的方式开始,也就是将新的 EEPROM 存储器地址装入地址指针。但是并不发送字节写操作的数据字节和停止条件,而是将重复的启动条件发送到器件。这一序列称为“虚拟写入”。在发送了“虚拟写入”的器件地址和存储器地址字节之后,AT21CS01/11 将返回 ACK 响应。主器件随后即可启动当前地址读操作(以新的启动条件开始),以从 EEPROM 读取数据。

在这里插入图片描述

以随机读操作开始的连续读操作
 

代码:

void EEPROMRead(u8* buffer, u8 addr, u8 len)
{
    DELAY_HTSS();
    AT21CS01_SendByte(EEPROM_ACCESS | WRITE);//dummy write
    AT21CS01_SendByte(addr);
    delay_us(800);
    AT21CS01_SendByte(EEPROM_ACCESS | READ);
    AT21CS01_ReadData(buffer, len);
    DELAY_HTSS();
}

6.2 EEPROM页写

        页写操作允许在一个写周期内最多写入 8 个字节,前提是所有字节都在存储器阵列的同一行(地址位 A6到 A3 相同)。此外,还支持不足 8 字节的部分页写操作。
        接收到每个数据字节后,EEPROM 都将以 ACK 进行响应。在发送了所有数据字节后,器件需要一个停止条件来开始写周期。但是,由于停止条件定义为空位帧(SI/O 拉为高电平),因此主器件无需驱动 SI/O线即可实现这一点。如果在任何其他时间发送停止条件,则写操作将中止。在停止条件完成后,内部自计时写周期将开始。SI/O 引脚在整个 tWR 周期内必须通过外部上拉电阻上拉到高电平。因此,在有多个从器件的环境下,如果有任何器件处于内部写周期,则不应尝试与总线上的其他单线器件通信。
        接收到每个数据字节后,存储器地址的低三位在内部递增。高地址位不会递增,器件将保持存储器页位置。无论实际写入的字节数如何,页写操作都仅限于在单个物理页内写入字节。当递增的字地址达到页边界时,地址计数器将“计满返回”到当前页的开头。但是应避免创建“计满返回”,因为页中先前装入的数据可能会被意外更改。在经过了最大 tWR 时间之后,主器件可能会开始新的总线事务。
在这里插入图片描述

页写操作
 

代码:

Ack_t EEPROMWritePage(u8* data, u8 addr, u16 len)
{
    int i = 0;
    Ack_t ack = ACK;

    DELAY_HTSS();
    ack &= AT21CS01_SendByte(EEPROM_ACCESS | WRITE);
    ack &= AT21CS01_SendByte(addr);
    for(i = 0; i < len; i++)
    {
        ack &= AT21CS01_SendByte(data[i]);
    }
    DELAY_HTSS();
    delay_ms(5);	//t_WR
    return ack;
}

6.3 ROM区域寄存器写

        ROM 区域寄存器只能写入逻辑 1,该操作会将相应的存储区域设置为 ROM 状态。一旦写入 ROM 区域寄存器,便无法再次更改。
代码:

u8 RomZoneRegWrite(u8 addr)
{
    Ack_t ack = ACK;

    if(addr != 1 && addr != 2 && addr != 4 && addr != 8)
    {
        return 0xEE;	//ERROR
    }
    DELAY_HTSS();
    ack &= AT21CS01_SendByte(ROM_ZONE_REG_ACCESS | WRITE);
    ack &= AT21CS01_SendByte(addr);
    ack &= AT21CS01_SendByte(0xFF);
    DELAY_HTSS();
    delay_ms(5);	//t_WR
    return (u8)ack;
}

6.4 ROM区域寄存器读

        要检查 ROM 区域寄存器的当前状态,主器件必须对随机读操作序列进行仿真,但使用的是操作码 0111b(7h)。为了指定将要读取的 ROM 区域寄存器地址,需要使用随机读操作序列的虚拟写入部分。
代码:

u8 RomZoneRegWrite(u8 addr)
{
    Ack_t ack = ACK;

    if(addr != 1 && addr != 2 && addr != 4 && addr != 8)
    {
        return 0xEE;	//ERROR
    }
    DELAY_HTSS();
    ack &= AT21CS01_SendByte(ROM_ZONE_REG_ACCESS | WRITE);
    ack &= AT21CS01_SendByte(addr);
    ack &= AT21CS01_SendByte(0xFF);
    DELAY_HTSS();
    delay_ms(5);	//t_WR
    return (u8)ack;
}

6.5 冻结ROM区域寄存器

        通过冻结当前 ROM 区域状态,可防止对 ROM 区域寄存器进行进一步修改。一旦冻结,便无法撤销此事件。
代码:

u8 RomZoneRegFreeze(void)
{
    Ack_t ack = ACK;

    DELAY_HTSS();
    ack &= AT21CS01_SendByte(FREEZE_ROM_ZONE_STATE | WRITE);
    if(ack == NACK)	//可能是之前已经冻结
    {
        return ack;
    }
    ack &= AT21CS01_SendByte(0x55);
    ack = AT21CS01_SendByte(0xAA);	//NACK:已经锁定 ACK:锁定成功
    DELAY_HTSS();
    delay_ms(5);	//t_WR
    return ack;
}

7 安全寄存器访问

在这里插入图片描述

安全寄存器架构
 

代码:

//安全寄存器页写
Ack_t SecRegWritePage(u8* data, u8 addr, u16 len)
{
    int i = 0;
    Ack_t ack = ACK;

    DELAY_HTSS();
    ack &= AT21CS01_SendByte(SECURITY_REG_ACCESS | WRITE);
    ack &= AT21CS01_SendByte(addr);
    for(i = 0; i < len; i++)
    {
        ack &= AT21CS01_SendByte(data[i]);
    }
    DELAY_HTSS();
    delay_ms(5);	//t_WR
    return ack;
}
//读取安全寄存器中的序列号
void SecRegReadSN(u8* buffer)
{
    DELAY_HTSS();
    AT21CS01_SendByte(SECURITY_REG_ACCESS | WRITE);//dummy write
    AT21CS01_SendByte(0x00);
    delay_us(800);
    AT21CS01_SendByte(SECURITY_REG_ACCESS | READ);
    AT21CS01_ReadData(buffer, 8);
    DELAY_HTSS();
}

        锁定命令是一个不可逆序列,在此之后将永久阻止对 AT21CS01/11 上的安全寄存器高 16 字节的所有写操作。一旦执行了锁定命令,整个 32 字节的安全寄存器都变为只读。一旦锁定了安全寄存器,便无法将其解锁。

//锁定安全寄存器
Ack_t SecRegLock(void)
{
    Ack_t ack = ACK;

    DELAY_HTSS();
    ack &= AT21CS01_SendByte(SECURITY_REG_LOCK | WRITE);
    ack &= AT21CS01_SendByte(0x60);		//NACK:已经锁定 ACK:锁定成功
    if(ack == NACK)
    {
        return ack;
    }
    ack &= AT21CS01_SendByte(0x00);
    DELAY_HTSS();
    delay_ms(5);	//t_WR
    return ack;
}

8 制造商ID读取

        AT21CS01/11 提供了查询器件制造商、容量和版本信息的功能。通过使用特定的操作码并遵循当前地址读操作的格式,器件将返回一个 24 位值,该值对应为 Microchip 保留的 I2C 标识符值以及用于表示 1 Kb 容量和器件版本的其他数据。
在这里插入图片描述

制造商ID读取

代码:

void MfrIDRead(u8* buffer)
{
    DELAY_HTSS();
    AT21CS01_SendByte(MFRID_READ | READ);//注意这个没有假写
    AT21CS01_ReadData(buffer, 3);
    DELAY_HTSS();
}

9 高速/标准速度模式

9.1 模式切换

        可以使用 Eh 操作码将器件设置为高速模式或检查其是否处于高速模式。该事务只需要 8 位。AT21CS01/11 在上电后默认处于高速模式。
        可以使用 Dh 操作码将 AT21CS01 设置为标准模式或检查其是否处于标准模式。该事务只需要 8 位。
代码:

void SpeedModeSet(u8 mode)
{
    DELAY_HTSS();
    if(mode == HIGH_SPEED_MODE || mode == HIGH_SPEED)
    {
        AT21CS01_SendByte(HIGH_SPEED_MODE | WRITE);
        SPEED_MODE = HIGH_SPEED;
    }
    else if(mode == STANDARD_SPEED_MODE || mode == STANDARD_SPEED)
    {
        AT21CS01_SendByte(STANDARD_SPEED_MODE | WRITE);
        SPEED_MODE = STANDARD_SPEED;
    }
    DELAY_HTSS();
}

9.2 模式检查

        要确定器件是否已设置为标准模式,必须将器件地址字节(操作码为 1101b(Dh))连同相应的从器件地址组合一起发送到器件,并且将读/写 位设置为逻辑 1。如果器件已设置为标准速度模式,则将返回 ACK(逻辑 0)。如果器件当前未设置为标准速度模式,则将返回 NACK(逻辑 1)。
        要确定器件是否已设置为高速模式,必须将器件地址字节(指定操作码 1110b(Eh))连同相应的从器件地址组合一起发送到器件,并且将读/写 位设置为逻辑 1。如果器件已设置为高速模式,则将返回 ACK(逻辑 0)。如果器件当前未设置为高速模式,则将返回 NACK(逻辑 1)。
代码:

Ack_t SpeedModeCheck(u8 mode)
{
    Ack_t ack = ACK;

    DELAY_HTSS();
    if(mode == HIGH_SPEED_MODE || mode == HIGH_SPEED)
    {
        ack = AT21CS01_SendByte(HIGH_SPEED_MODE | READ);
    }
    else if(mode == STANDARD_SPEED_MODE || mode == STANDARD_SPEED)
    {
        ack = AT21CS01_SendByte(STANDARD_SPEED_MODE | READ);
    }
    DELAY_HTSS();
    return ack;
}
  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

emXiaoMing

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值