MCU模拟IIC读写

时间:2021-01-21 16:36:51

MCU模拟IIC读写,以E2PROM AT24C256为例

/* 模拟IIC */
#define EEPROM_WP_Pin                   GPIO_PIN_15
#define EEPROM_WP_GPIO_Port             GPIOE
#define WP_GPIO_CLK_ENABLE()            __HAL_RCC_GPIOE_CLK_ENABLE()

#define EEPROM_I2C2_SCL_Pin             GPIO_PIN_10
#define EEPROM_I2C2_SCL_GPIO_Port       GPIOB
#define SCL_GPIO_CLK_ENABLE()           __HAL_RCC_GPIOB_CLK_ENABLE()

#define EEPROM_I2C2_SDA_Pin             GPIO_PIN_11
#define EEPROM_I2C2_SDA_GPIO_Port       GPIOB
#define SDA_GPIO_CLK_ENABLE()           __HAL_RCC_GPIOB_CLK_ENABLE()


#define SCL_EEPROM_H()                  HAL_GPIO_WritePin(EEPROM_I2C2_SCL_GPIO_Port, EEPROM_I2C2_SCL_Pin, GPIO_PIN_SET)
#define SCL_EEPROM_L()                  HAL_GPIO_WritePin(EEPROM_I2C2_SCL_GPIO_Port, EEPROM_I2C2_SCL_Pin, GPIO_PIN_RESET)

#define SDA_EEPROM_H()                  HAL_GPIO_WritePin(EEPROM_I2C2_SDA_GPIO_Port, EEPROM_I2C2_SDA_Pin, GPIO_PIN_SET)
#define SDA_EEPROM_L()                  HAL_GPIO_WritePin(EEPROM_I2C2_SDA_GPIO_Port, EEPROM_I2C2_SDA_Pin, GPIO_PIN_RESET)

#define SDA_EEPROM_READ()               HAL_GPIO_ReadPin(EEPROM_I2C2_SDA_GPIO_Port, EEPROM_I2C2_SDA_Pin)



/* IIC设备常量 */
#define     EXTEEPROM_HW_ADDRESS     0xA0        // EXTEEPROM地址

#define     AT24C256                 1           //使用AT24C256使能
#define     AT24C64                  0           //使用AT24C64使能

#if AT24C256
    #define Page_Bite                64
    #define EXTEEPROM_STORAGE_SIZE   0x8000
#endif

#if AT24C64
    #define Page_Bite                32            //页大小
    #define EXTEEPROM_STORAGE_SIZE   0x2000        //EEPROM大小
#endif

#define     EEPROM_MAX_WRITE_LEN     1024        // 最大写入长度1024=1K



unsigned char HAL_ReadDataFromEEPROM(unsigned short eeprom_add, unsigned short len, unsigned char *ram_add);
unsigned char HAL_WriteDataToEEPROM(unsigned short eeprom_add, unsigned short len, unsigned char *ram_add);
unsigned char ReadDataFromExtStoreWithCS(unsigned short Addr, unsigned short Len, unsigned char *DataBuff);
unsigned char WriteDataToExtStoreWithCS(unsigned short Addr, unsigned short Len, unsigned char *DataBuff);
void MX_GPIO_Init(void)
{
  GPIO_InitTypeDef GPIO_InitStruct;
  
  /* GPIO Ports Clock Enable */
  WP_GPIO_CLK_ENABLE();
  SCL_GPIO_CLK_ENABLE();
  SCL_GPIO_CLK_ENABLE();
  
  /*eeprom*/
  GPIO_InitStruct.Pin = EEPROM_WP_Pin;
  GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;        /*推挽*/
  GPIO_InitStruct.Pull = GPIO_PULLDOWN;
  GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
  HAL_GPIO_Init(GPIOE, &GPIO_InitStruct);
  HAL_GPIO_WritePin(EEPROM_WP_GPIO_Port, EEPROM_WP_Pin, GPIO_PIN_RESET);

  GPIO_InitStruct.Pin = EEPROM_I2C2_SCL_Pin;
  GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_OD;        /*开漏*/
  GPIO_InitStruct.Pull = GPIO_PULLUP;
  GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
  HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
  HAL_GPIO_WritePin(EEPROM_I2C2_SCL_GPIO_Port, EEPROM_I2C2_SCL_Pin, GPIO_PIN_SET);

  GPIO_InitStruct.Pin =  EEPROM_I2C2_SDA_Pin;
  GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_OD;       /*开漏*/
  GPIO_InitStruct.Pull = GPIO_PULLUP;
  GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
  HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
  HAL_GPIO_WritePin(EEPROM_I2C2_SDA_GPIO_Port, EEPROM_I2C2_SDA_Pin, GPIO_PIN_SET);
}

/*******************************************************************************
* 函数名称: HAL_WriteWPState
* 函数功能: 设置写保护状态 -->针对eeprom
* 输入参数: 使能,静止
* 输出参数: 无
* 返 回 值: 无
*******************************************************************************/
void HAL_WriteWPState(GPIO_PinState PinState)
{
    HAL_GPIO_WritePin(EEPROM_WP_GPIO_Port,EEPROM_WP_Pin,PinState);
}


static void EEPROM_I2C_delay(void) //写引脚延时
{
    volatile uint32_t i = 40; //这里可以优化速度 ,经测试最低到5还能写入
    for(i=0; i<40; i++)
    {
        ;
    }
}

void udelay(uint32_t usec) //usec延时 可以优化
{
    volatile  uint32_t  i;

    for(i=(usec*4); i!=0; i--)
    {
        asm("NOP");
        asm("NOP");
        asm("NOP");
        asm("NOP");
        asm("NOP");
        asm("NOP");
        asm("NOP");
        asm("NOP");
        asm("NOP");
        asm("NOP");
        asm("NOP");
    }
}

static uint8 EEPROM_I2C_Start(void)
{
    SDA_EEPROM_H();
    EEPROM_I2C_delay();
    SCL_EEPROM_H();
    EEPROM_I2C_delay();
    SDA_EEPROM_L();
    EEPROM_I2C_delay();
    SCL_EEPROM_L();
    EEPROM_I2C_delay();
}

static void EEPROM_I2C_Stop(void)
{
    SCL_EEPROM_L();
    EEPROM_I2C_delay();
    SDA_EEPROM_L();
    EEPROM_I2C_delay();
    SCL_EEPROM_H();
    EEPROM_I2C_delay();
    SDA_EEPROM_H();
    EEPROM_I2C_delay();
}

static void EEPROM_I2C_Ack(void)
{
    SDA_EEPROM_L();
    EEPROM_I2C_delay();
    SCL_EEPROM_H();
    EEPROM_I2C_delay();
    SCL_EEPROM_L();
    EEPROM_I2C_delay();
    SDA_EEPROM_H();
    EEPROM_I2C_delay();
}

static void EEPROM_I2C_NoAck(void)
{
    SDA_EEPROM_H();
    EEPROM_I2C_delay();
    SCL_EEPROM_H();
    EEPROM_I2C_delay();
    SCL_EEPROM_L();
    EEPROM_I2C_delay();
}

static uint8 EEPROM_I2C_WaitAck(void)   //返回为:=1有ACK
{

    SCL_EEPROM_H();
    EEPROM_I2C_delay();
    SCL_EEPROM_L();
    EEPROM_I2C_delay();
    return 1;
}

static void EEPROM_I2C_SendByte(uint8_t SendByte) //数据从高位到低位//
{

    uint8 i = 8;
    while(i--)
    {
        if(SendByte&0x80)       //1000 0000
            SDA_EEPROM_H();
        else
            SDA_EEPROM_L();
        SendByte<<=1;
        EEPROM_I2C_delay();
        SCL_EEPROM_H();
        EEPROM_I2C_delay();
        SCL_EEPROM_L();
        EEPROM_I2C_delay();
    }

}

static uint8 EEPROM_I2C_ReceiveByte(void)  //数据从高位到低位//
{

    uint8 i = 8;
    uint8 ReceiveByte = 0;

    while(i--)
    {
        ReceiveByte<<=1;       //0000 0000 
        SCL_EEPROM_H();
        EEPROM_I2C_delay();
        if(SDA_EEPROM_READ())
        {
            ReceiveByte|=0x01;//0000 0001 移位8次
        }
        SCL_EEPROM_L();
        EEPROM_I2C_delay();
    }
    return ReceiveByte;

}

/*******************************************************************************
* 函数名称: ReadbytefromEEPROM
* 函数功能: 读从设备指定地址指定长度字节
* 输入参数: slave_addr:从设备地址
            addr:设备内部地址,读字节的起始地址
            len:读字节长度
* 输出参数: *data:接收数据指针
* 返 回 值: 1 -->读结束
*******************************************************************************/
static uint8 ReadbytefromEEPROM(uint8_t slave_addr, uint16_t addr, uint8_t *data, uint16_t len)
{
    unsigned int i;

    EEPROM_I2C_Start();
    EEPROM_I2C_SendByte(slave_addr);
    if(EEPROM_I2C_WaitAck() != 1)
    {
        //return IIC_ERR_WADDR;
    }
    EEPROM_I2C_SendByte(addr>>8);
    if(EEPROM_I2C_WaitAck() != 1)
    {
        //return IIC_ERR_WADDR;
    }
    EEPROM_I2C_SendByte(addr);
    if(EEPROM_I2C_WaitAck() != 1)
    {
        //return IIC_ERR_WADDR;
    }
    EEPROM_I2C_Start();
    EEPROM_I2C_SendByte(slave_addr|0x01);
    if(EEPROM_I2C_WaitAck() != 1)
    {
        //return IIC_ERR_WADDR;
    }
    for(i = 0; i < len - 1; i++)
    {
        data[i] = EEPROM_I2C_ReceiveByte();
        EEPROM_I2C_Ack();
    }
    data[i] = EEPROM_I2C_ReceiveByte();
    EEPROM_I2C_NoAck();
    EEPROM_I2C_Stop();
    return 1;
}

/*******************************************************************************
* 函数名称: ReadbytefromEEPROM
* 函数功能: 读从设备指定地址指定长度字节
* 输入参数: slave_addr:从设备地址
            addr:设备内部地址,写字节的起始地址
            len:要写的字节长度
            *data:要写的数据
* 输出参数: 
* 返 回 值: 1 -->写结束
*******************************************************************************/
static uint8 WritebytetoEEPROM(uint8_t slave_addr, uint16_t addr, uint8_t *data, uint16_t len)
{
    unsigned int i;

    EEPROM_I2C_Start();
    EEPROM_I2C_SendByte(slave_addr);
    if(EEPROM_I2C_WaitAck() != 1)
    {
        //return IIC_ERR_WADDR;
    }
    EEPROM_I2C_SendByte(addr>>8);
    if(EEPROM_I2C_WaitAck() != 1)
    {
        //return IIC_ERR_WADDR;
    }
    EEPROM_I2C_SendByte(addr);
    if(EEPROM_I2C_WaitAck() != 1)
    {
        //return IIC_ERR_WADDR;
    }
    for(i = 0; i < len; i++)
    {
        EEPROM_I2C_SendByte(data[i]);
        if(EEPROM_I2C_WaitAck() != 1)
        {
            //return IIC_ERR_WADDR;
        }
    }
    EEPROM_I2C_Stop();

    return 1;
}

/***************************************************************************************
* 名称: HAL_WriteDataToEEPROM
* 功能: 向EEPROM写数据
* 输入: EEPROM_add  数据地址
*       len         数据项长度
*       data     数据缓存
* 输出:
* 处理:
* 返回: 1     写入成功
*       0     写入失败
***************************************************************************************/
uint8 HAL_WriteDataToEEPROM(uint16 eeprom_add, uint16 len, uint8 *data)
{
    unsigned short WriteLen = len;
    unsigned short DateOffect = 0;
    unsigned short size = 0;
    unsigned short PageOffect = eeprom_add % Page_Bite; //


    if((eeprom_add + len > EXTEEPROM_STORAGE_SIZE)||(len>EEPROM_MAX_WRITE_LEN))
    {
        return FALSE;
    }

    HAL_WriteWPState(GPIO_PIN_RESET);//关闭写保护
    udelay(1);

    //EEPROM支持以32字节为单位的页写,而实际是EPPROM按照每16字节进行页的划分
    //写到页的边界后,要往后写必须重新调用写入
    while(WriteLen > 0)
    {
        if(PageOffect + WriteLen > Page_Bite)
        {
            size = Page_Bite - PageOffect;
        }
        else
        {
            size = WriteLen;
        }

        if(WritebytetoEEPROM(EXTEEPROM_HW_ADDRESS, eeprom_add + DateOffect, data+DateOffect, size) != 1)
        {
            return 0;
        }

        PageOffect = 0;
        DateOffect += size;
        WriteLen -= size;
        udelay(6000);
    }

    HAL_WriteWPState(GPIO_PIN_SET);//打开写保护
    return 1;
}

/***************************************************************************************
* 名称: HAL_ReadDataFromEEPROM
* 功能: 向EEPROM写数据
* 输入: eeprom_add  数据偏移
*       len         数据项长度
*       data     数据缓存
* 输出:
* 处理:
* 返回: > 0     写入成功
*       = 0     写入失败
***************************************************************************************/
uint8 HAL_ReadDataFromEEPROM(uint16 eeprom_add, uint16 len, uint8 *data)
{
    unsigned short ReadLen = len;
    unsigned short DateOffect = 0;
    unsigned short size = 0;
    unsigned short PageOffect = eeprom_add % Page_Bite;
    
    if((eeprom_add + len > EXTEEPROM_STORAGE_SIZE)||(len > EEPROM_MAX_WRITE_LEN))
    {
        return 0;
    }

    //EEPROM支持以32字节为单位的页写,而实际是EPPROM按照每16字节进行页的划分
    //写到页的边界后,要往后写必须重新调用写入
    while(ReadLen > 0)
    {
        if(PageOffect + ReadLen > Page_Bite)
        {
            size = Page_Bite - PageOffect;
        }
        else
        {
            size = ReadLen;
        }

        if(ReadbytefromEEPROM(EXTEEPROM_HW_ADDRESS, eeprom_add + DateOffect, data + DateOffect, size) == 1)
        {
            //return size;
        }

        PageOffect = 0;
        DateOffect += size;
        ReadLen -= size;
        udelay(6000);
    }
    return 1;
}


unsigned char WriteDataToExtStoreWithCS(unsigned short Addr, unsigned short Len, unsigned char *DataBuff)//补校验和
{
    uint32  i;
    uint8   TempBuff[1025]= {0};
    uint8   CS = 0;

    if(Len > 1024)
    {
      return  FALSE;
    }
    //数据暂存到缓冲区
    memcpy(TempBuff, DataBuff, Len);

    //计算校验和
    CS = 0x33;
    for (i=0; i < Len; i++)
    {
        CS += TempBuff[i];
    }

    //将校验和追加到缓冲区
    TempBuff[Len] = CS;

    HAL_WriteDataToEEPROM(Addr, Len+1, TempBuff);

    return  TRUE;
}

unsigned char ReadDataFromExtStoreWithCS(unsigned short Addr, unsigned short Len, unsigned char *DataBuff)
{
    uint32   i;
    uint8   TempBuff[1025]= {0};
    uint8   CS = 0;
    
    if(Len > 1024)
    {
        return FALSE;
    }

    HAL_ReadDataFromEEPROM(Addr, Len+1, TempBuff);

    //计算校验和
    CS = 0x33;
    for (i=0; i < Len; i++)
    {
        CS += TempBuff[i];
    }

    //校验和相等,数据正确,直接返回
    if (TempBuff[Len] == CS)
    {
        //从缓冲区取数据返回
        memcpy(DataBuff, TempBuff, Len);

        return 1;
    }
    return 0;
}