单片机读写FT24C256A详解

一、FT24C256A介绍

产品简介:

FT24C256A系列是262,144位串行电可擦除可编程只读存储器,俗称 EEPROM。它们可存储32,768个字,每个字8位(一个字节)。这些器件采用专有的先进CMOS工艺制造,适用于低功率和低电压应用。这些器件采用标准8引脚DIP、8引脚SOP、8引脚MSOP、8引脚 TSSOP和8引脚UDFN封装。标准的2线串行接口用于处理所有读取和写入功能。我们范围的VCC(1.8V至5.5V)器件支持广泛的应用。

产品特点:

低电压和低功耗操作:

FT24C256A: VCC 1.8V~5.5V

64 字节页写模式。

允许部分页写操作。

内部组织:32768x8(256K)。

标准2线双向串行接口。

施密特触发器,用于噪声保护的滤波输入。

自定时写周期(最大5ms)。

1 MHz(2.5V-5V)、400 kHz(1.8V)兼容性。

写操作前自动擦除。

写保护引脚用于硬件数据保护。

高可靠性:通常1,000,000次循环耐久性。

100年数据保留。

工业温度范围(—40℃至85℃)。

标准8引脚 DIP/SOP/MSOP/TSSOP/UDFN 无铅封装。

上面这段话是从官网上抄下来的,写得很有水平但个人觉得有点美中不足。个人认为有空把这些写得这么详细还不如直接贴几个例程出来,好歹还能让新手可以节省很多前期工作。虽然在他们那些工程师看来使用这个EEPROM不是“有手就会的”,但对于刚入门的来说这个手还可能真没有。

在官网上图片(详见下图)右侧有一行小字:“FT24Cxx系列EEPROM使用I2C接口,芯片容量从2kb到1Mb ”。这样就知道了这个也是使用的IIC通信,至于芯片容量这都是默认的FT24Cxxx后面这个xxx就是说的芯片容量,就这块芯片来说容量有256kb。

二、FT24C256A的IIC通信搭建

IIC通信这都是老生常谈的话题了,在CSDN上面关于IIC的博文没有1000篇也有800篇。好多讲得很详细的我这里就不再献丑了(我自己能理解,但要很好的讲出来我做不到)。这里就直接贴各部分的代码了。IIC通信开始、停止、应答时序如下:

CPU发起I2C总线启动信号:

/*
*********************************************************************************************************
*    函 数 名: IIC_Start_EEPROM
*    功能说明: CPU发起I2C总线启动信号
*    形    参:无
*    返 回 值: 无
*********************************************************************************************************
*/
void IIC_Start_EEPROM(void)
{
    /* 当SCL高电平时,SDA出现一个下跳沿表示I2C总线启动信号 */
    SDA_OUT_EEPROM();    //sda线输出
    IIC_SDA_H_EEPROM;
    IIC_SCL_H_EEPROM;
    mDelayuS(5);
    IIC_SDA_L_EEPROM;    //START:when CLK is high,DATA change form high to low
    mDelayuS(5);
    IIC_SCL_L_EEPROM;    //钳住I2C总线,准备发送或接收数据
}

CPU发起I2C总线停止信号

/*
*********************************************************************************************************
*    函 数 名: IIC_Stop_EEPROM
*    功能说明: CPU发起I2C总线停止信号
*    形    参:无
*    返 回 值: 无
*********************************************************************************************************
*/
void IIC_Stop_EEPROM(void)
{
    /* 当SCL高电平时,SDA出现一个上跳沿表示I2C总线停止信号 */
    SDA_OUT_EEPROM();    //sda线输出
    IIC_SCL_L_EEPROM;
    IIC_SDA_L_EEPROM;
    IIC_SCL_H_EEPROM;
    mDelayuS(5);
    IIC_SDA_H_EEPROM;    //发送I2C总线结束信号
    mDelayuS(5);
    IIC_SDA_L_EEPROM;
}

CPU产生一个ACK信号和一个NACK信号

/*
*********************************************************************************************************
*    函 数 名: IIC_Ack_EEPROM
*    功能说明: CPU产生一个ACK信号
*    形    参:无
*    返 回 值: 无
*********************************************************************************************************
*/
void IIC_Ack_EEPROM(void)
{
    IIC_SCL_L_EEPROM;
    SDA_OUT_EEPROM();
    IIC_SDA_L_EEPROM;    /* CPU驱动SDA = 0 */
    mDelayuS(2);
    IIC_SCL_H_EEPROM;    /* CPU产生1个时钟 */
    mDelayuS(2);
    IIC_SCL_L_EEPROM;
}

/*
*********************************************************************************************************
*    函 数 名: IIC_NAck_EEPROM
*    功能说明: CPU产生1个NACK信号
*    形    参:无
*    返 回 值: 无
*********************************************************************************************************
*/
void IIC_NAck_EEPROM(void)
{
    IIC_SCL_L_EEPROM;
    SDA_OUT_EEPROM();
    IIC_SDA_H_EEPROM;    /* CPU驱动SDA = 1 */
    mDelayuS(2);
    IIC_SCL_H_EEPROM;    /* CPU产生1个时钟 */
    mDelayuS(2);
    IIC_SCL_L_EEPROM;    
}

CPU产生一个时钟,并读取器件的ACK应答信号

/*
*********************************************************************************************************
*    函 数 名: IIC_WaitAck_EEPROM
*    功能说明: CPU产生一个时钟,并读取器件的ACK应答信号
*    形    参:无
*    返 回 值: 0:表示正确应答,什么都不做
*              1:接收应答失败,IIC直接退出
*********************************************************************************************************
*/
uint8_t IIC_WaitAck_EEPROM(void)
{
    UINT8 ucErrTime=0;
    SDA_IN_EEPROM();
    IIC_SDA_H_EEPROM;    /* CPU释放SDA总线 */
    IIC_SCL_H_EEPROM;    /* CPU驱动SCL = 1, 此时器件会返回ACK应答 */
    mDelayuS(2);
    if (READ_SDA_EEPROM)    /* CPU读取SDA口线状态 */
    {
        ucErrTime++;
        if(ucErrTime>250)
        {
            IIC_Stop_EEPROM();
            return 1;
        }
    }
    IIC_SCL_L_EEPROM;
    return 0;
}

CPU向I2C总线设备发送8bit数据

/*
*********************************************************************************************************
*    函 数 名: IIC_WriteByte_EEPROM
*    功能说明: CPU向I2C总线设备发送8bit数据
*    形    参:_ucByte : 等待发送的字节
*    返 回 值: 无
*********************************************************************************************************
*/
void IIC_WriteByte_EEPROM(uint8_t u8Data)
{                        
    uint8_t i;   
    SDA_OUT_EEPROM();        
    IIC_SCL_L_EEPROM;//拉低时钟开始数据传输
    for(i=0;i<8;i++)
    {              
        if((u8Data&0x80)>>7) IIC_SDA_H_EEPROM;
                else IIC_SDA_L_EEPROM;
        u8Data<<=1;       
        mDelayuS(2);  
        IIC_SCL_H_EEPROM;    //时钟保持高电平
        mDelayuS(2); 
        IIC_SCL_L_EEPROM;    //时钟拉低,才允许SDA变化
        mDelayuS(2);
    }     
}

CPU从I2C总线设备读取1个字节

/*
*********************************************************************************************************
*    函 数 名: IIC_ReadByte_EEPROM
*    功能说明: CPU从I2C总线设备读取1个字节
*    形    参:ack=1时,发送ACK,ack=0,发送nACK 
*    返 回 值: 读到的数据
*********************************************************************************************************
*/
uint8_t IIC_ReadByte_EEPROM(void)
{
    uint8_t i;
    uint8_t receive=0;
    SDA_IN_EEPROM();//SDA设置为输入
    for(i=0;i<8;i++ )
    {
        IIC_SCL_L_EEPROM; 
        mDelayuS(2);
        IIC_SCL_H_EEPROM;
        receive<<=1;
        if(READ_SDA_EEPROM)receive++;   
                mDelayuS(1); 
    }                     
    return receive;
}

这些就是FT24C256A IIC通信需要用到的函数,里面也就一些管脚的标识不一样其他都是大同小异的(准确来说是一摸一样的)。都可以在其他关于IIC通信的例程里面直接修改。

三、FT24C256A读写数据函数

这一部分就相当于把前面的函数按照一定的顺序(读写协议)排列来实现对数据的读写。

(一) EEPROM写数据代码:

上图是FT24C256A写操作时序。上图中上面的时序是写一个字节的时序,下面的时序是写一页的时序。关于写数据时序的说明在FT24C256A数据手册里面有提及,全是用英文写的,我这里偷个懒就直接放截图了。

翻译过来就是:

写操作

(A) 字节写入:写操作需要两个8位的数据字地址在设备地址字和ACKNOWLEDGE信号之后。在收到这个地址时,EEPROM将以一个“O”回应,然后在第一个8位数据字中计时。在接收到8位数据字后,EEPROM将再次输出一个“O”。寻址设备,如微控制器,必须在STOP条件下终止写序列。此时,EEPROM进入内部定时写周期状态。在这个写周期中,所有输入都被禁用,EEPROM直到写完成才会响应(图3)。

(B) 页写入:256K EEPROM能够写64字节的页。

页写入与字节写入的方式相同,但微控制器在第一个数据字被写入后不发送STOP条件。在EEPROM承认接收到第一数据字后,微控制器可以再传输多达63个数据字。在接收到每个数据字后,EEPROM将响应一个“O”。微控制器必须以STOP条件终止页写入序列(参见图4)。

数据字地址的下六位位在接收到每个数据字后在内部递增。较高的数据字地址位不增加,保留内存页行位置。如果超过64个数据字被传输到EEPROM,数据字地址将“滚动”,之前的数据将被覆盖。

(C) ACK信号轮询:ACK信号轮询可用于在自我计时的内部编程期间轮询编程状态。通过发出一个有效的读或写地址命令,如果设备仍然在自计时编程模式,EEPROM将不会在9时钟周期确认。然而,如果编程完成并且芯片返回到待机模式,设备将在第9个时钟周期返回一个有效的ACK信号。

按照上面时序可以写出写操作代码:

写一字节:

/*******************************************************************************
  * 函数名:x24Cxx_WriteByte
  * 功  能:写一个字节
  * 参  数:u16Addr要写入的地址
            u8Data要写入的数据
  * 返回值:无
  * 说  明:器件地址(包含写入命令) -> 1或2个字节WORD ADDR -> 数据
*******************************************************************************/
void x24Cxx_WriteByte(uint16_t u16Addr, uint8_t u8Data)
{
//    x24Cxx_WriteEnable();//使能写入
    IIC_Start_EEPROM();//起始信号    
    #if    (ADDR_BYTE_NUM == 1)//地址只有1个字节
    {
        IIC_WriteByte_EEPROM(DEV_ADDR | EEPROM_I2C_WR | (((uint8_t)((u16Addr >> 8) & 0x07)) << 1));//器件寻址+写+页选择位
        IIC_WaitAck_EEPROM();//等待应答
        IIC_WriteByte_EEPROM((uint8_t)(u16Addr & 0xFF));//只取地址的低字节
        IIC_WaitAck_EEPROM();//等待应答
    }
    #endif
    #if    (ADDR_BYTE_NUM == 2)//地址有2个字节
    {
        IIC_WriteByte_EEPROM(DEV_ADDR | EEPROM_I2C_WR);//器件寻址+写
        IIC_WaitAck_EEPROM();//等待应答
        IIC_WriteByte_EEPROM((uint8_t)((u16Addr >> 8) & 0xFF));//地址高字节
        IIC_WaitAck_EEPROM();//等待应答
        IIC_WriteByte_EEPROM((uint8_t)(u16Addr & 0xFF));//地址低字节
        IIC_WaitAck_EEPROM();//等待应答
    }
    #endif
    IIC_WriteByte_EEPROM(u8Data);
    IIC_WaitAck_EEPROM();//等待应答
    IIC_Stop_EEPROM();
//    x24Cxx_WriteDisble();//禁止写入
}

写一页代码:

/*******************************************************************************
  * 函数名:x24Cxx_WritePage
  * 功  能:页写
  * 参  数:u16Addr要写入的首地址;
            u8Len写入数据字节数,最大为PAGE_SIZE
            pData要写入的数据首地址
  * 返回值:无
  * 说  明:最多写入1页,防止翻卷,如果地址跨页则去掉跨页的部分
*******************************************************************************/
void x24Cxx_WritePage(uint16_t u16Addr, uint8_t u8Len, uint8_t *pData)
{
    uint8_t i;
    if (u8Len > PAGE_SIZE)//长度大于页的长度
    {
        u8Len = PAGE_SIZE;
    }
    if ((u16Addr + (uint16_t)u8Len) > CAPACITY_SIZE)//超过容量
    {
        u8Len = (uint8_t)(CAPACITY_SIZE - u16Addr);
    }
    if (((u16Addr % PAGE_SIZE) + (uint16_t)u8Len) > PAGE_SIZE)//判断是否跨页
    {
        u8Len -= (uint8_t)((u16Addr + (uint16_t)u8Len) % PAGE_SIZE);//跨页,截掉跨页的部分
    }
//    x24Cxx_WriteEnable();//使能写入
    IIC_Start_EEPROM();//起始信号
    #if    (ADDR_BYTE_NUM == 1)//地址只有1个字节
    {
        IIC_WriteByte_EEPROM(DEV_ADDR | EEPROM_I2C_WR | (((uint8_t)((u16Addr >> 8) & 0x07)) << 1));//器件寻址+写+页选择位
        IIC_WaitAck_EEPROM();//等待应答
        IIC_WriteByte_EEPROM((uint8_t)(u16Addr & 0xFF));//只取地址的低字节
        IIC_WaitAck_EEPROM();//等待应答
    }
    #endif
    #if    (ADDR_BYTE_NUM == 2)//地址有2个字节
    {
        IIC_WriteByte_EEPROM(DEV_ADDR | EEPROM_I2C_WR);//器件寻址+写
        IIC_WaitAck_EEPROM();//等待应答
        IIC_WriteByte_EEPROM((uint8_t)((u16Addr >> 8) & 0xFF));//地址高字节
        IIC_WaitAck_EEPROM();//等待应答
        IIC_WriteByte_EEPROM((uint8_t)(u16Addr & 0xFF));//地址低字节
        IIC_WaitAck_EEPROM();//等待应答
    }
    #endif    
    for (i = 0; i < u8Len; i++)
    {
        IIC_WriteByte_EEPROM(*(pData + i));
        IIC_WaitAck_EEPROM();//等待应答
    }
    IIC_Stop_EEPROM();    
//    x24Cxx_WriteDisble();//禁止写入
}

(二)EEPROM读取数据代码:

上图是FT24C256A读操作时序。上图中最上面的时序是读取一个字节的时序,中间的时序是连续读取一段空间的时序,最下面的是读取一页的时序。关于读数据时序的说明在FT24C256A数据手册里面有提及,全是用英文写的,我这里继续偷个懒放截图了。

翻译过来就是:

读操作

读命令与写命令类似,只是地址字的第8位读写位设置为1。三种读操作方式说明如下:

(A) 当前地址读取:EEPROM内部地址字计数器维护最后的读或写地址加1,如果设备的电源没有被切断。为了启动当前地址读操作,微控制器发出一个START位和一个读写位(第8位)设置为“1”的有效设备地址字。EEPROM将在串行时钟周期第9位响应ACK信号。然后一个8位数据字将被连续输出。内部地址字计数器将自动增加1。对于当前地址读取,微控制器在时钟周期第18位内不会发出确认信号。微控制器在时钟周期第18位后发出一个有效的STOP位,以终止读取操作。然后设备返回到STANDBY模式(见图5)。

(B) 顺序读取:顺序读取与当前地址读取非常相似。微控制器发出一个START位和一个有效的设备地址字,读写位(第八位)设置为“1”。EEPROM将在串行时钟周期(第9位)上响应ACKNOWLEDGE信号。然后,一个8位数据字将被串行输出。与此同时,内部地址字计数器将自动增加1。

与当前地址读取不同,微控制器在时钟周期第18位发送一个确认信号,向EEPROM设备发出它想要另一个字节的数据。在接收到ACK信号后,EEPROM将串行输出一个基于内部地址计数器的8位数据字。当微控制器需要另一个数据时,它在时钟周期第27位发送一个ACK信号。另一个8位数据字将被连续输出。只要微控制器在l接收到一个新的数据字后发送一个ACK信号,这个顺序读取就会继续。当内部地址计数器达到其最大有效地址时,它将滚动到内存数组地址的开头。与当前地址读取类似,微控制器可以通过不确认接收到的最后一个数据字来终止顺序读取,但随后发送一个STOP位代替(图6)。

(C) 随机读取:随机读分为两步。第一步是用一个“伪写”指令初始化一个目标读地址的内部地址计数器。第二步是读取当前地址。

为了用目标读地址初始化内部地址计数器,微控制器首先发出一个START位,然后是一个有效的设备地址,读写位(第8位)设置为“0”。此时,EEPROM将确认,微控制器将发送两个地址字。EEPROM将再次确认。微控制器执行当前地址读指令来读取数据,而不是向EEPROM发送有效的写入数据。注意,一旦发出START位,EEPROM将重置内部编程过程,并继续执行新指令-即读取当前地址(图7)。

按照上面时序可以写出读操作代码:

读一个字节代码:

/*******************************************************************************
  * 函数名:x24Cxx_ReadByte
  * 功  能:读一个字节
  * 参  数:u16Addr要读取的地址
  * 返回值:u8Data读出的数据
  * 说  明:无
*******************************************************************************/
uint8_t x24Cxx_ReadByte(uint16_t u16Addr)
{
    uint8_t u8Data = 0;
    IIC_Start_EEPROM();//起始信号    
    #if    (ADDR_BYTE_NUM == 1)//地址只有1个字节
    {
        IIC_WriteByte_EEPROM(DEV_ADDR | EEPROM_I2C_WR | (((uint8_t)((u16Addr >> 8) & 0x07)) << 1));//器件寻址+写+页选择位
        IIC_WaitAck_EEPROM();//等待应答
        IIC_WriteByte_EEPROM((uint8_t)(u16Addr & 0xFF));//只取地址的低字节
        IIC_WaitAck_EEPROM();//等待应答
    }
    #endif
    #if    (ADDR_BYTE_NUM == 2)//地址有2个字节
    {
        IIC_WriteByte_EEPROM(DEV_ADDR | EEPROM_I2C_WR);//器件寻址+写
        IIC_WaitAck_EEPROM();//等待应答
        IIC_WriteByte_EEPROM((uint8_t)((u16Addr >> 8) & 0xFF));//地址高字节
        IIC_WaitAck_EEPROM();//等待应答
        IIC_WriteByte_EEPROM((uint8_t)(u16Addr & 0xFF));//地址低字节
        IIC_WaitAck_EEPROM();//等待应答
    }
    #endif
    IIC_Start_EEPROM();//起始信号
    IIC_WriteByte_EEPROM(DEV_ADDR | EEPROM_I2C_RD);//器件寻址+读
    IIC_WaitAck_EEPROM();//等待应答
    u8Data = IIC_ReadByte_EEPROM();
    IIC_NAck_EEPROM();
    IIC_Stop_EEPROM();
    return u8Data;
}

读一页代码:相当于顺序读取但是有固定长度(长度为一页)

/*******************************************************************************
  * 函数名:x24Cxx_ReadPage
  * 功  能:页读
  * 参  数:u16Addr要读取的首地址;
            u8Len读取数据字节数,最大为PAGE_SIZE
            pBuff读取数据存入的缓存
  * 返回值:无
  * 说  明:无
*******************************************************************************/
void x24Cxx_ReadPage(uint16_t u16Addr, uint8_t u8Len, uint8_t *pBuff)
{
    uint8_t i;
    if (u8Len > PAGE_SIZE)//长度大于页的长度
    {
        u8Len = PAGE_SIZE;
    }
    if ((u16Addr + (uint16_t)u8Len) > CAPACITY_SIZE)//超过容量
    {
        u8Len = (uint8_t)(CAPACITY_SIZE - u16Addr);
    }
    if (((u16Addr % PAGE_SIZE) + (uint16_t)u8Len) > PAGE_SIZE)//判断是否跨页
    {
        u8Len -= (uint8_t)((u16Addr + (uint16_t)u8Len) % PAGE_SIZE);//跨页,截掉跨页的部分
    }
    IIC_Start_EEPROM();//起始信号    
    #if    (ADDR_BYTE_NUM == 1)//地址只有1个字节
    {
        IIC_WriteByte_EEPROM(DEV_ADDR | EEPROM_I2C_WR | (((uint8_t)((u16Addr >> 8) & 0x07)) << 1));//器件寻址+写+页选择位
        IIC_WaitAck_EEPROM();//等待应答
        IIC_WriteByte_EEPROM((uint8_t)(u16Addr & 0xFF));//只取地址的低字节
        IIC_WaitAck_EEPROM();//等待应答
    }
    #endif
    #if    (ADDR_BYTE_NUM == 2)//地址有2个字节
    {
        IIC_WriteByte_EEPROM(DEV_ADDR | EEPROM_I2C_WR);//器件寻址+写
        IIC_WaitAck_EEPROM();//等待应答
        IIC_WriteByte_EEPROM((uint8_t)((u16Addr >> 8) & 0xFF));//地址高字节
        IIC_WaitAck_EEPROM();//等待应答
        IIC_WriteByte_EEPROM((uint8_t)(u16Addr & 0xFF));//地址低字节
        IIC_WaitAck_EEPROM();//等待应答
    }
    #endif
    IIC_Start_EEPROM();//起始信号
    IIC_WriteByte_EEPROM(DEV_ADDR | EEPROM_I2C_RD);//器件寻址+读
    IIC_WaitAck_EEPROM();//等待应答
    for (i = 0; i < (u8Len - 1); i++)
    {
        *(pBuff + i) = IIC_ReadByte_EEPROM();
        IIC_Ack_EEPROM();
    }
    *(pBuff + u8Len - 1) = IIC_ReadByte_EEPROM();
    IIC_NAck_EEPROM();//最后一个不应答
    IIC_Stop_EEPROM();
}

四、验证读写函数功能

自己编了一个简易的读写例子,具体代码如下:

void Save_Val(UINT16 u16Addr,UINT16 Num,float Val)
{
    static UINT16 N1,N2,N3,N4;
    N1 = (UINT16)Num/100;
    N2 = (UINT16)Num%100;
    N3 = (UINT16)((Val*100)/100);
    N4 = (UINT16)((int)(Val*100)%100);
//    printf("写入地址:0x%04x        执行次数:%4d    电流值:%2.2f\r\n",u16Addr,Num,Val);
    x24Cxx_WriteByte((u16Addr + 0),N1);
    mDelaymS(5);
    x24Cxx_WriteByte((u16Addr + 1),N2);
    mDelaymS(5);
    x24Cxx_WriteByte((u16Addr + 2),N3);
    mDelaymS(5);
    x24Cxx_WriteByte((u16Addr + 3),N4);
    mDelaymS(5);
}

//读取写入的数据是否正常
void Read_Val(UINT16 u16Addr)
{
    static UINT16 N1,N2,N3,N4;
    static UINT16 Num;
    static float Val;
    
    N1 = (UINT16)x24Cxx_ReadByte(u16Addr + 0);
    N2 = (UINT16)x24Cxx_ReadByte(u16Addr + 1);
    N3 = (UINT16)x24Cxx_ReadByte(u16Addr + 2);
    N4 = (UINT16)x24Cxx_ReadByte(u16Addr + 3);
    
    Num = N1*100 + N2;
    Val = N3 + N4/100.0;
    if(N1 == 255)    printf("地址:0x%04x        组号:%4d        标准值:%2.2f\r\n",u16Addr,N2,Val);
    else    if(Num != 0)    printf("地址:0x%04x        执行次数:%4d        电流值:%2.2f\r\n",u16Addr,Num,Val);
}

//读取写入的数据记录
void Read_Val_List(UINT16 u16Addr)
{
    static UINT16 N1,N2,N3,N4;
    static UINT16 Num;
    static float Val;
    
    N1 = (UINT16)x24Cxx_ReadByte(u16Addr + 0);
    N2 = (UINT16)x24Cxx_ReadByte(u16Addr + 1);
    N3 = (UINT16)x24Cxx_ReadByte(u16Addr + 2);
    N4 = (UINT16)x24Cxx_ReadByte(u16Addr + 3);
    
    Num = N1*100 + N2;
    Val = N3 + N4/100.0;
    if((N1 == 255)&&\
       ((N2<100)&&(N2>0)))    printf("地址:0x%04x        组号:%4d        标准值:%2.2f\r\n",u16Addr,N2,Val);
    else    
        if(((N1!=255)&&(N2!=255))&&\
           ((N3!=255)&&(N4!=255)))    
        printf("地址:0x%04x        执行次数:%4d        电流值:%2.2f\r\n",u16Addr,Num,Val);
}

测试结果如下:

posted @ 2023-03-08 17:18  归依龙井  阅读(490)  评论(0编辑  收藏  举报  来源