打印
[单片机芯片]

CH32V103的硬件IIC连续读写AT24系列EEPROM

[复制链接]
794|8
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
药无尘|  楼主 | 2024-3-7 16:53 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
看到网上没有CH32V系列的关于硬件IIC连续读写AT24系列EEPROM的文章。我在借鉴了网上一些资料后在官方例程基础上做了修改,由于AT24系列EEPROM的写Page是有长度限制的,低容量的是8bytes,高容量的是16/32bytes。所以在写之前先要做地址对齐,先将不对齐的部分写入,然后连续按照页大小写入,最后收尾即可。

        要注意的是每次写入之间要加延时,经测试要大于2ms为妙,测试过1ms延时会卡死。

代码如下:

/*
*@Note
I2C接口操作EEPROM外设例程: 此代码在官方例程基础上修改,其中部分代码借鉴了stm32等其他类型单片机的IIC流程
I2C1_SCL(PB10)、I2C1_SDA(PB11)。
本例程使用 EEPROM 为 AT24Cxx系列。
操作步骤:
READ EEPROM:Start + 0xA0 + 8bit Data Address + Start + 0xA1 + Read Data + Stop.
WRITE EERPOM:Start + 0xA0 + 8bit Data Address + Write Data + Stop.
*/

#include "debug.h"

/**********************************************************************
*@Note:
AT24Cxx:
READ EEPROM:Start + 0xA0 + 8bit Data Address + Start + 0xA1 + Read Data + Stop.
WRITE EERPOM:Start + 0xA0 + 8bit Data Address + Write Data + Stop.
*******************************************************************************/
/* EERPOM DATA ADDRESS Length Definition */
#define Address_8bit     0
#define Address_16bit    1

/* EERPOM DATA ADDRESS Length Selection */
#define Address_Lenth    Address_8bit
//#define Address_Lenth   Address_16bit

/* Global define */
#define SIZE             sizeof(TEXT_Buffer)
//写入和读取的初始地址
#define WR_Address       2
//器件的页大小
#define PAGE_SIZE        16
/* Global Variable */
const u8 TEXT_Buffer[] = {"CH32V103 IIC TEST GOOD LUCK!\r Let's Go!\r AT24C02 Page=8,AT24C4/8/16 Page=16,AT24C32/64 Page=32."};

/*********************************************************************
* @fn      IIC_Init
*
* [url=home.php?mod=space&uid=247401]@brief[/url]   Initializes the IIC peripheral.
*
* [url=home.php?mod=space&uid=266161]@return[/url]  none
*/
void IIC_Init(u32 bound, u16 address)
{
    GPIO_InitTypeDef GPIO_InitStructure = {0};
    I2C_InitTypeDef  I2C_InitTSturcture = {0};

    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C2, ENABLE);

    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_OD;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOB, &GPIO_InitStructure);

    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_11;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_OD;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOB, &GPIO_InitStructure);

    I2C_InitTSturcture.I2C_ClockSpeed = bound;
    I2C_InitTSturcture.I2C_Mode = I2C_Mode_I2C;
    I2C_InitTSturcture.I2C_DutyCycle = I2C_DutyCycle_2;
    I2C_InitTSturcture.I2C_OwnAddress1 = address;
    I2C_InitTSturcture.I2C_Ack = I2C_Ack_Enable;
    I2C_InitTSturcture.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit;
    I2C_Init(I2C2, &I2C_InitTSturcture);

    I2C_Cmd(I2C2, ENABLE);

    I2C_AcknowledgeConfig(I2C2, ENABLE);
}

/*********************************************************************
* @fn      AT24CXX_Init
*
* @brief   Initializes AT24xx EEPROM.
*
* @return  none
*/
void AT24CXX_Init(void)
{
    IIC_Init(100000, 0xA0);
}

/*********************************************************************
* @fn      AT24CXX_ReadOneByte
*
* @brief   Read one data from EEPROM.
*
* @param   ReadAddr - Read frist address.
*
* @return  temp - Read data.
*/
u8 AT24CXX_ReadOneByte(u16 ReadAddr)
{
    u8 temp = 0;

    while(I2C_GetFlagStatus(I2C2, I2C_FLAG_BUSY) != RESET);
    I2C_GenerateSTART(I2C2, ENABLE);

    while(!I2C_CheckEvent(I2C2, I2C_EVENT_MASTER_MODE_SELECT));
    I2C_Send7bitAddress(I2C2, 0XA0, I2C_Direction_Transmitter);

    while(!I2C_CheckEvent(I2C2, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED));

#if(Address_Lenth == Address_8bit)
    I2C_SendData(I2C2, (u8)(ReadAddr & 0x00FF));
    while(!I2C_CheckEvent(I2C2, I2C_EVENT_MASTER_BYTE_TRANSMITTED));

#elif(Address_Lenth == Address_16bit)
    I2C_SendData(I2C2, (u8)(ReadAddr >> 8));
    while(!I2C_CheckEvent(I2C2, I2C_EVENT_MASTER_BYTE_TRANSMITTED));

    I2C_SendData(I2C2, (u8)(ReadAddr & 0x00FF));
    while(!I2C_CheckEvent(I2C2, I2C_EVENT_MASTER_BYTE_TRANSMITTED));

#endif

    I2C_GenerateSTART(I2C2, ENABLE);

    while(!I2C_CheckEvent(I2C2, I2C_EVENT_MASTER_MODE_SELECT));
    I2C_Send7bitAddress(I2C2, 0XA0, I2C_Direction_Receiver);

    while(!I2C_CheckEvent(I2C2, I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED));
    while(I2C_GetFlagStatus(I2C2, I2C_FLAG_RXNE) == RESET)
        I2C_AcknowledgeConfig(I2C2, DISABLE);

    temp = I2C_ReceiveData(I2C2);
    I2C_GenerateSTOP(I2C2, ENABLE);

    return temp;
}



/*********************************************************************
* @fn      AT24CXX_MulitReadByte
*
* @brief   Read one data from EEPROM.
*
* @param   ReadAddr - Read frist address.
*          Readbuffer - Read data buffer.
*          Num - Size will be read.
*
* @return  read_count
*/
u8 AT24CXX_MulitReadBytes(u16 ReadAddr,u8 *Readbuffer, u8 Num )
{
    u8 read_count=0;
    while(I2C_GetFlagStatus(I2C2, I2C_FLAG_BUSY) != RESET);
    I2C_GenerateSTART(I2C2, ENABLE);

    while(!I2C_CheckEvent(I2C2, I2C_EVENT_MASTER_MODE_SELECT));
    I2C_Send7bitAddress(I2C2, 0XA0, I2C_Direction_Transmitter);

    while(!I2C_CheckEvent(I2C2, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED));

#if(Address_Lenth == Address_8bit)
    I2C_SendData(I2C2, (u8)(ReadAddr & 0x00FF));
    while(!I2C_CheckEvent(I2C2, I2C_EVENT_MASTER_BYTE_TRANSMITTED));

#elif(Address_Lenth == Address_16bit)
    I2C_SendData(I2C2, (u8)(ReadAddr >> 8));
    while(!I2C_CheckEvent(I2C2, I2C_EVENT_MASTER_BYTE_TRANSMITTED));

    I2C_SendData(I2C2, (u8)(ReadAddr & 0x00FF));
    while(!I2C_CheckEvent(I2C2, I2C_EVENT_MASTER_BYTE_TRANSMITTED));

#endif

    I2C_GenerateSTART(I2C2, ENABLE);

    while(!I2C_CheckEvent(I2C2, I2C_EVENT_MASTER_MODE_SELECT));
    I2C_Send7bitAddress(I2C2, 0XA0, I2C_Direction_Receiver);

    while(!I2C_CheckEvent(I2C2, I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED));

    while(Num--)
       {

        if(Num==0) //最后一个数据产生停止应答信号
        {
         //应答必须关闭,否则下次IIC将不能用
         I2C_AcknowledgeConfig(I2C2, DISABLE);  //需要在接受到最后一位数据前关闭应答
         }
        while(I2C_GetFlagStatus(I2C2, I2C_FLAG_RXNE) == RESET);       //等待收到数据
         *Readbuffer++=I2C_ReceiveData(I2C2);  //收到数据放入缓存
         read_count++;
      }
       I2C_GenerateSTOP(I2C2, ENABLE);          //发送停止信号
       I2C_AcknowledgeConfig(I2C1,ENABLE) ;     //开启应答方便下一次使用

       return read_count;
}



/*********************************************************************
* @fn      AT24CXX_WriteOneByte
*
* @brief   Write one data to EEPROM.
*
* @param   WriteAddr - Write frist address.
*
* @return  DataToWrite - Write data.
*/
void AT24CXX_WriteOneByte(u16 WriteAddr, u8 DataToWrite)
{
    while(I2C_GetFlagStatus(I2C2, I2C_FLAG_BUSY) != RESET);
    I2C_GenerateSTART(I2C2, ENABLE);

    while(!I2C_CheckEvent(I2C2, I2C_EVENT_MASTER_MODE_SELECT));
    I2C_Send7bitAddress(I2C2, 0XA0, I2C_Direction_Transmitter);

    while(!I2C_CheckEvent(I2C2, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED));

#if(Address_Lenth == Address_8bit)
    I2C_SendData(I2C2, (u8)(WriteAddr & 0x00FF));
    while(!I2C_CheckEvent(I2C2, I2C_EVENT_MASTER_BYTE_TRANSMITTED));

#elif(Address_Lenth == Address_16bit)
    I2C_SendData(I2C2, (u8)(WriteAddr >> 8));
    while(!I2C_CheckEvent(I2C2, I2C_EVENT_MASTER_BYTE_TRANSMITTED));

    I2C_SendData(I2C2, (u8)(WriteAddr & 0x00FF));
    while(!I2C_CheckEvent(I2C2, I2C_EVENT_MASTER_BYTE_TRANSMITTED));

#endif

    if(I2C_GetFlagStatus(I2C2, I2C_FLAG_TXE) != RESET)
    {
        I2C_SendData(I2C2, DataToWrite);
    }

    while(!I2C_CheckEvent(I2C2, I2C_EVENT_MASTER_BYTE_TRANSMITTED)) ;
    I2C_GenerateSTOP(I2C2, ENABLE);
}

/*********************************************************************
* @fn      AT24CXX_WritePage
*
* @brief   Write one data to EEPROM.
*
* @param   WriteAddr - Write frist address.
*          WriteBuffer - data will be write
*          Num - data num 1~8,page is 8 byte
*
* @return  DataToWrite - Write data.
*/
void AT24CXX_WritePage(u16 WriteAddr, u8 *WriteBuffer, u8 Num)
{
    while(I2C_GetFlagStatus(I2C2, I2C_FLAG_BUSY) != RESET);
    I2C_GenerateSTART(I2C2, ENABLE);

    while(!I2C_CheckEvent(I2C2, I2C_EVENT_MASTER_MODE_SELECT));
    I2C_Send7bitAddress(I2C2, 0XA0, I2C_Direction_Transmitter);

    while(!I2C_CheckEvent(I2C2, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED));

#if(Address_Lenth == Address_8bit)
    I2C_SendData(I2C2, (u8)(WriteAddr & 0x00FF));
    while(!I2C_CheckEvent(I2C2, I2C_EVENT_MASTER_BYTE_TRANSMITTED));

#elif(Address_Lenth == Address_16bit)
    I2C_SendData(I2C2, (u8)(WriteAddr >> 8));
    while(!I2C_CheckEvent(I2C2, I2C_EVENT_MASTER_BYTE_TRANSMITTED));

    I2C_SendData(I2C2, (u8)(WriteAddr & 0x00FF));
    while(!I2C_CheckEvent(I2C2, I2C_EVENT_MASTER_BYTE_TRANSMITTED));

#endif

    while(!(I2C_GetFlagStatus(I2C2,I2C_FLAG_TXE)));
    I2C_SendData(I2C2, *WriteBuffer++);
    while(!(I2C_GetFlagStatus(I2C2,I2C_FLAG_TXE)));
    while(Num)
    {
        Num--;
        if(Num==0)  break;  //全部发送完毕,直接退出
        //还有数据则继续发送
        I2C_SendData(I2C2, *WriteBuffer++);
        while(!(I2C_GetFlagStatus(I2C2,I2C_FLAG_TXE))); //等待数据发送完毕
    }

    while(!I2C_CheckEvent(I2C2, I2C_EVENT_MASTER_BYTE_TRANSMITTED)) ;
    I2C_GenerateSTOP(I2C2, ENABLE);
}


/*********************************************************************
* @fn      AT24CXX_Read
*
* @brief   Read multiple data from EEPROM.
*
* @param   ReadAddr - Read frist address. (AT24c02: 0~255)
*          pBuffer - Read data.
*          NumToRead - Data number.
*
* @return  none
*/
void AT24CXX_Read(u16 ReadAddr, u8 *pBuffer, u16 NumToRead)
{
    while(NumToRead)
    {
        *pBuffer++ = AT24CXX_ReadOneByte(ReadAddr++);
        NumToRead--;
    }
}

/*********************************************************************
* @fn      AT24CXX_Write
*
* @brief   Write multiple data to EEPROM.
*
* @param   WriteAddr - Write frist address. (AT24c02: 0~255)
*          pBuffer - Write data.
*          NumToWrite - Data number.
*
* @return  none
*/
void AT24CXX_Write(u16 WriteAddr, u8 *pBuffer, u16 NumToWrite)
{
    while(NumToWrite--)
    {
        AT24CXX_WriteOneByte(WriteAddr, *pBuffer);
        WriteAddr++;
        pBuffer++;
        Delay_Ms(2);
    }
}


void AT24CXX_MulitWriteBytes(u16 WriteAddr, u8 *pBuffer, u16 NumToWrite)
{
    BOOL last_time = 0;
    u8 duiqi_count = 0;
    //首先要对齐到页
    if(WriteAddr%PAGE_SIZE !=0 ){
        duiqi_count = ((u8)(WriteAddr/PAGE_SIZE )+1)*PAGE_SIZE -WriteAddr;
        printf("duiqi_count:%d\r\n", duiqi_count);
        AT24CXX_WritePage(WriteAddr, pBuffer,duiqi_count);
        NumToWrite = NumToWrite - duiqi_count;
        WriteAddr = WriteAddr + duiqi_count;
        pBuffer = pBuffer + duiqi_count;
        Delay_Ms(3);
    }
    //将剩余数据写入
    while(!last_time)
        {
            if(NumToWrite <= PAGE_SIZE ) //如果小于页大小,为最后 一次写入
            {
                AT24CXX_WritePage(WriteAddr, pBuffer,NumToWrite);
                last_time = 1;
            }
            else { //连续页写入
                AT24CXX_WritePage(WriteAddr, pBuffer,PAGE_SIZE);
                WriteAddr = WriteAddr + PAGE_SIZE;
                pBuffer = pBuffer + PAGE_SIZE;
                NumToWrite = NumToWrite -PAGE_SIZE;
            }
            Delay_Ms(3);
        }
}

/*********************************************************************
* @fn      main
*
* @brief   Main program.
*
* @return  none
*/
int main(void)
{
    u8 data[SIZE];

    Delay_Init();
    USART_Printf_Init(115200);
    printf("SystemClk:%d\r\n", SystemCoreClock);

    AT24CXX_Init();

    printf("Start Write 24Cxx....\r\n");
    //AT24CXX_Write(50, (u8 *)TEXT_Buffer, SIZE); //官方是单个写入,多次重复
    AT24CXX_MulitWriteBytes(WR_Address, (u8 *)TEXT_Buffer, SIZE); //修改后是多次页写入
    printf("24Cxx Write Sucess!\r\n");

    Delay_Ms(50);

    printf("Start Read 24Cxx....\r\n");
    AT24CXX_MulitReadBytes(WR_Address,data,SIZE); //修改后是直接连续读取
    //AT24CXX_Read(100, data, SIZE); //官方程序是单个读取,多次重复
    printf("The Data Readed Is: \r\n");
    printf("%s\r\n", data);

    while(1);
}


使用特权

评论回复
评论
呐咯密密 2024-4-17 20:15 回复TA
理想的应该是判断EEPROM是否空闲,然后再开始写下一页 
沙发
tpgf| | 2024-4-2 12:13 | 只看该作者
延时的时间是否应该根据通讯数据的多少来定呢

使用特权

评论回复
板凳
八层楼| | 2024-4-2 12:46 | 只看该作者
如果延时时间过短  就会卡死的原因是什么呢

使用特权

评论回复
地板
guanjiaer| | 2024-4-2 13:50 | 只看该作者
这个等待时间和iic的通讯速度有关系吗

使用特权

评论回复
5
keaibukelian| | 2024-4-2 14:27 | 只看该作者
这种使用模块的 难道底层不自动进行延时?

使用特权

评论回复
6
heimaojingzhang| | 2024-4-2 23:50 | 只看该作者
eeprom的读写的速度最快能达到多少呢

使用特权

评论回复
7
呐咯密密| | 2024-4-17 20:14 | 只看该作者
EEPROM写数据需要时间,每次切页要等待,这个时间需要根据手册来定

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

77

主题

492

帖子

2

粉丝