015 - STM32学习笔记 - IIC读写存储器(二)

015 - STM32学习笔记 - I2C访问存储器(二)

1、完善I2C读写EEPROM

在上节内容中,学习了EEPROM的读写,我用的F429中EEPROM型号为:AT24C02,其容量为256*8=2Kb,这节学习一下如何对EEPROM进行更深层次的读写。

在上节的程序中,向EEPROM写入数据是一个byte写入,在读出一个byte,如果要写入大量数据,就得反复去调用EPROM_Byte_WriteEEPROM_Byte_Read(uint8_t addr)函数,这样就需要不停的等待响应,耗时又耗力。因此本节实现多字节读写和页写入。

页写入,在AT24C02中,按照8byte分页,可以分为256页,因此一页可以写入8个byte,实现如下:

/** @brief  向EEPROM里面写入一块数据
  * @param
  * @arg    pBuffer:存放向EEPROM写入的数据的缓冲区指针
  * @arg    ReadAddr:写入EEPROM的起始地址
  * @arg    NumByteToRead:要写入的字节数
  * @retval 返回结果
  */
uint8_t EEPROM_Page_Write(u8* pBuffer, u8 WriteAddr,u8 NumByteToWrite)
{
    /*设置超时等待时间*/
    I2CTimeout = I2CT_FLAG_TIMEOUT;
    //检测I2C总线状态,获取
    while(I2C_GetFlagStatus(EEPROM_I2C, I2C_FLAG_BUSY))   
    {
        if((I2CTimeout--) == 0) 
            return I2C_TIMEOUT_UserCallback(13);
    }
    /*设置超时等待时间*/
    I2CTimeout = I2CT_FLAG_TIMEOUT;
    /*-----------------------1 产生开始信号 ----------------------------*/
    I2C_GenerateSTART(EEPROM_I2C,ENABLE); 
    /*-----------------------2 等待EV5事件 ----------------------------*/
    while(I2C_CheckEvent(EEPROM_I2C,I2C_EVENT_MASTER_MODE_SELECT) != SUCCESS)
    {
        if((I2CTimeout--) == 0)
            return I2C_TIMEOUT_UserCallback(14);
    }
    /*设置超时等待时间*/
    I2CTimeout = I2CT_FLAG_TIMEOUT;
    /*-----------------------3 发送要访问的设备地址 ----------------------------*/
    I2C_Send7bitAddress(EEPROM_I2C,EEPROM_ADDR,I2C_Direction_Transmitter);

    /*-----------------------4 等待EV6事件 ----------------------------*/
    while(I2C_CheckEvent(EEPROM_I2C,I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED) != SUCCESS)
    {
        if((I2CTimeout--) == 0)
            return I2C_TIMEOUT_UserCallback(15);
    }
    /*设置超时等待时间*/
    I2CTimeout = I2CT_FLAG_TIMEOUT;
    /*------5 发送要写入的EEPROM内部地址,即EEPROM内部存储器地址 -----*/
    I2C_SendData(EEPROM_I2C,WriteAddr);
    /*-----------------------6 等待EV8事件 ----------------------------*/
    while(I2C_CheckEvent(EEPROM_I2C,I2C_EVENT_MASTER_BYTE_TRANSMITTED) != SUCCESS)
    {
        if((I2CTimeout--) == 0)
            return I2C_TIMEOUT_UserCallback(16);
    }
    while(NumByteToWrite--)
    {
        /*设置超时等待时间*/
        I2CTimeout = I2CT_FLAG_TIMEOUT;
        /*-----------------------7 发送数据 ----------------------------*/
        I2C_SendData(EEPROM_I2C,*pBuffer);
        /*-----------------------8 等待EV8事件 ----------------------------*/
        while(I2C_CheckEvent(EEPROM_I2C,I2C_EVENT_MASTER_BYTE_TRANSMITTED) != SUCCESS)
        {
            if((I2CTimeout--) == 0)
                return I2C_TIMEOUT_UserCallback(17);
        }
        pBuffer++;
    }
    /*-----------------------9 发送停止信号 ----------------------------*/
    I2C_GenerateSTOP(EEPROM_I2C,ENABLE);
    return 1;
}

按页写入的实现,和之前EPROM_Byte_Write基本类似,区别在于:

1、函数参数需要传入写入的首地址、写入数据的首地址以及写入数据的数量;

2、在发送数据的时候,通过while循环来判断数据是否发完;

3、每次发完数据后,指针递增,数量递减。

利用页写入的功能,可以对多字节写入的函数进行改进,加快传输速度,情况比较多,程序里面有详细注释,各位看官可以看一下,不懂的话可以留言一起讨论,实现如下:

/** @brief  向EEPROM里面写入一组数据
  * @param
  * @arg    pBuffer:存放向EEPROM写入的数据的缓冲区指针
  * @arg    WriteAddr:写入EEPROM的首地址
  * @arg    NumByteToWrite:要向EEPROM写入的字节数
  * @retval 无返回结果
  */
void EEPROM_Buffer_Write(u8* pBuffer, u8 WriteAddr,u16 NumByteToWrite)
{
    u8 NumOfPage=0,NumOfSingle=0,Addr =0,count=0,temp =0;
    /* I2C_PageSize为宏定义,AT24C02每页有8个字节*/
    Addr = WriteAddr % I2C_PageSize;                    /* 若writeAddr是I2C_PageSize整数倍,运算结果Addr值为0,表示写入地址与页首地址对齐 */
    NumOfPage = NumByteToWrite / I2C_PageSize;          /* 计算需要写入多少页 */
    NumOfSingle = NumByteToWrite % I2C_PageSize;        /* 计算写入整页后,剩余多少字节,若写入数据量为页容量的倍数,且首地址与页首地址对齐,则NumOfSingle为0 */
    count = I2C_PageSize - Addr;                        /* 差count个数据值,刚好可以对齐到页地址 */
    /* 多字节写入分以下几种情况:
    1、写入首地址与EEPROM页首地址对齐:
        a、写入数据≤8个byte;
        c、写入数据 = (n * 8) + m(n为页数,m为不满1页数据量,m < 8)
    2、写入首地址与EEPROM页首地址不对齐:
        a、写入数据 ≤ 8个byte(一页内可写完);
        b、写入数据 = x+(n * 8) + y byte(x为前端不满一页数据量,x=0时,字节对齐,n为页数,y为后端不满一页数据量)
    */
    if(Addr == 0)                                       /* 如果写入地址与页首地址对齐 */
    {
        if (NumOfPage == 0)                             /* 若写入数据刚好一页或不满一页 */
        {
            EEPROM_Page_Write(pBuffer, WriteAddr, NumOfSingle);
            WaitEEpromStandbyState();
        }
        else 
        {
            while (NumOfPage--)                         /* 若写入数据大于一页,则按页写入 */
            {
                EEPROM_Page_Write(pBuffer, WriteAddr,I2C_PageSize);
                WaitEEpromStandbyState();
                WriteAddr += I2C_PageSize;
                pBuffer += I2C_PageSize;
            }
			if (NumOfSingle!=0)                         /* 若有多余,但不满一页的数据 */
			{
				EEPROM_Page_Write(pBuffer, WriteAddr, NumOfSingle);
				WaitEEpromStandbyState();
			}
        }
    }
    else                                                 /* 若写入地址与页首地址不对齐 */
	{
		if (NumOfPage== 0)                               /* 若写入数据不满一页 */
		{
			if (NumOfSingle > count)                     /* 数据不满一页,但横跨两页 */
			{		
				temp = NumOfSingle - count;              /* 先将不满一页,但首地址与页首地址不对齐的数据写入 */
				EEPROM_Page_Write(pBuffer, WriteAddr, count);
				WaitEEpromStandbyState();         /* 等待写入完成 */
				WriteAddr += count;                                 /* 这里计算出来下一页的起始地址 */
				pBuffer += count;                                   /* pBuffer地址后移count */
				EEPROM_Page_Write(pBuffer, WriteAddr, temp);        /* 写入下一页数据(剩余数据) */
				WaitEEpromStandbyState();         /* 等待写入完成 */
			}
            else                                            /* 如果本页可写完,直接写入即可 */
			{
				EEPROM_Page_Write(pBuffer, WriteAddr, NumByteToWrite);
				WaitEEpromStandbyState();
			}
        }
		else                                                /* 如果要写入的数据超过页大小 */
		{
			NumByteToWrite -= count;                        /* 这里计算的是地址不对齐的情况下,前面多出的数据数量 */
			NumOfPage = NumByteToWrite / I2C_PageSize;      /* 计算除了前部多出的数据外,需要的页数 */
			NumOfSingle = NumByteToWrite % I2C_PageSize;    /* 计算除了前部多出的数据外,后端不足一页的数据量,若后端数据量为整页,则此结果为0 */
			if (count != 0)
			{
				EEPROM_Page_Write(pBuffer, WriteAddr, count);           /* 写入前端多余的数据 */
				WaitEEpromStandbyState();
				WriteAddr += count;                                     /* 写入地址加上前端多余的数据量,就是下一页的首地址 */
				pBuffer += count;
			}
			while (NumOfPage--)                                         /* 这里就是写中间满页的数据 */
			{
				EEPROM_Page_Write(pBuffer, WriteAddr, I2C_PageSize);
				WaitEEpromStandbyState();
				WriteAddr += I2C_PageSize;                              /* 地址按页增加 */
				pBuffer += I2C_PageSize;
			}
			if (NumOfSingle != 0)                                       /* 写入后端不足一页的数据 */
			{
				EEPROM_Page_Write(pBuffer, WriteAddr, NumOfSingle);
				WaitEEpromStandbyState();
			}
		}
    }
}

在快速写入的过程中使用到了WaitEEpromStandbyState函数,其作用是为了等待每次写入数据后,EEPROM回到准备状态,否则可能连续写入的时候,前面的数据刚写入,还没等EEPROM处理完,后面的数据就来了,这样容易写入失败。

static void WaitEEpromStandbyState(void)
{
    __IO uint16_t SR1_Tmp = 0;
    do
    {
        I2C_GenerateSTART(EEPROM_I2C, ENABLE);									/* 发送起始信号 */
        SR1_Tmp = I2C_ReadRegister(EEPROM_I2C, I2C_Register_SR1);      			  /* 读 I2C1 SR1 寄存器 */
        I2C_Send7bitAddress(EEPROM_I2C, EEPROM_ADDR,I2C_Direction_Transmitter);    /* 发送 EEPROM 地址 + 写方向 */
    }while (!(I2C_ReadRegister(EEPROM_I2C, I2C_Register_SR1) & 0x0002));			 /* 等待地址发送成功 */
    I2C_ClearFlag(EEPROM_I2C, I2C_FLAG_AF);									   /* 清除 AF 位 */
    I2C_GenerateSTOP(EEPROM_I2C, ENABLE);									   /* 发送停止信号 */
}

再看一下多字节读取,和EEPROM_Page_Write类似,同样是通过EEPROM_Buffer_Read更改后完成的,实现如下:

/** @brief  从EEPROM里面读取一块数据
  * @param
  * @arg    pBuffer:存放EEPROM读取的数据的缓冲区指针
  * @arg    ReadAddr:读取EEPROM的起始地址
  * @arg    NumByteToRead:要从EEPROM读取的字节数
  * @retval 返回结果
  */
uint32_t EEPROM_Buffer_Read(u8* pBuffer,u8 ReadAddr,u16 NumByteToRead)
{
    I2CTimeout = I2CT_FLAG_TIMEOUT;															 /* 设置超时等待时间 */
    while(I2C_GetFlagStatus(EEPROM_I2C, I2C_FLAG_BUSY))   									   /* 检测I2C总线状态 */
    {
        if((I2CTimeout--) == 0) 
            return I2C_TIMEOUT_UserCallback(18);
    }
    I2CTimeout = I2CT_FLAG_TIMEOUT;															/* 设置超时等待时间 */
    /*-----------------------1 产生开始信号 ----------------------------*/
    I2C_GenerateSTART(EEPROM_I2C,ENABLE); 
    /*-----------------------2 等待EV5事件 ----------------------------*/
    while(I2C_CheckEvent(EEPROM_I2C,I2C_EVENT_MASTER_MODE_SELECT) != SUCCESS)
    {
        if((I2CTimeout--) == 0)
            return I2C_TIMEOUT_UserCallback(19);
    }
    I2CTimeout = I2CT_FLAG_TIMEOUT;															/* 设置超时等待时间 */
    /*-----------------------3 发送要访问的设备地址 ----------------------------*/
    I2C_Send7bitAddress(EEPROM_I2C,EEPROM_ADDR,I2C_Direction_Transmitter);
    /*-----------------------4 等待EV6事件 ----------------------------*/
    while(I2C_CheckEvent(EEPROM_I2C,I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED) != SUCCESS)
    {
        if((I2CTimeout--) == 0)
            return I2C_TIMEOUT_UserCallback(20);
    }
    
    I2CTimeout = I2CT_FLAG_TIMEOUT;															/* 设置超时等待时间 */
    /*------5 发送要写入的EEPROM内部地址,即EEPROM内部存储器地址 -----*/
    I2C_SendData(EEPROM_I2C,ReadAddr);
    /*-----------------------6 等待EV8事件 ----------------------------*/
    while(I2C_CheckEvent(EEPROM_I2C,I2C_EVENT_MASTER_BYTE_TRANSMITTED) != SUCCESS)
    {
        if((I2CTimeout--) == 0)
            return I2C_TIMEOUT_UserCallback(21);
    }
    /****************************************************************************************/
    I2CTimeout = I2CT_FLAG_TIMEOUT;															 /* 设置超时等待时间 */
    /*-----------------------7 产生第二次开始信号 ----------------------------*/
     I2C_GenerateSTART(EEPROM_I2C,ENABLE); 
    /*-----------------------8 等待EV5事件 ----------------------------*/
    while(I2C_CheckEvent(EEPROM_I2C,I2C_EVENT_MASTER_MODE_SELECT) != SUCCESS)
    {
        if((I2CTimeout--) == 0)
            return I2C_TIMEOUT_UserCallback(22);
    }
    /*----------------------- 发送要访问的设备地址:发送read方向 ----------------------------*/
    I2C_Send7bitAddress(EEPROM_I2C,EEPROM_ADDR,I2C_Direction_Receiver);
    /*-----------------------9 等待EV6事件 ----------------------------*/
    while(I2C_CheckEvent(EEPROM_I2C,I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED) != SUCCESS)
    {
        if((I2CTimeout--) == 0)
            return I2C_TIMEOUT_UserCallback(23);
    }
    while(NumByteToRead)
    {
        if(NumByteToRead == 1)
        {
            I2C_AcknowledgeConfig(EEPROM_I2C,DISABLE);												 /* 关闭响应 */
            I2C_GenerateSTOP(EEPROM_I2C,ENABLE);
        }
        I2CTimeout = I2CT_FLAG_TIMEOUT;															/* 设置超时等待时间 */
        /*-----------------------11 等待EV7事件 ----------------------------*/
        while(I2C_CheckEvent(EEPROM_I2C,I2C_EVENT_MASTER_BYTE_RECEIVED) != SUCCESS)
        {
            if((I2CTimeout--) == 0)
                return I2C_TIMEOUT_UserCallback(24);
        }
        /*-----------------------10 读取数据 ----------------------------*/
        *pBuffer = I2C_ReceiveData(EEPROM_I2C);
        pBuffer ++;
        NumByteToRead --;
    }
	/*-----------------------12 发送停止信号 ----------------------------*/
    I2C_GenerateSTOP(EEPROM_I2C,ENABLE);
    I2C_AcknowledgeConfig(EEPROM_I2C,ENABLE);                                                  /* 使能响应,方便下一次操作 */	
}

大致的内容就是这些了,下面呈上测试程序:

#include "stm32f4xx.h"
#include "bsp_led.h"
#include "bsp_systick.h"
#include "bsp_usart_dma.h"
#include "bsp_i2c_ee.h"
#include <stdio.h>

int main(void)
{
    uint16_t i = 0;
    u8 tmp = 0;
    u8 writeBuffer[256];
    u8 readBuffer[256];
    
    LED_Config();
    DEBUG_USART1_Config();
    SysTick_Init();
    printf("这是一个EEPROM测试实验\n");
    
    printf("\r\n初始化EEPROM\r\n");
    EEPROM_GPIO_Config();
    
    printf("\r\n初始化I2C模式配置\r\n");
    EEPROM_I2C_ModeConfig();
    
	EEPROM_INFO("待写入数据:\r\n");
    for(i = 0;i <= 255;i++)							/* 向writeBuffer写入数据 */
    {
        writeBuffer[i] = i;
        printf("0x%02X ",writeBuffer[i]);
        if(i%16 == 15)								/* 逢15换行 */
            printf("\n");
    }
    EEPROM_Buffer_Write(writeBuffer,0x00,256);
    EEPROM_INFO("I2C写入EEPROM成功!\r\n");
    
    EEPROM_Buffer_Read(readBuffer,0x00,256);
    EEPROM_INFO("读出数据:\r\n");
    for(i = 0;i <= 255;i++)
    {
        printf("0x%02X ",readBuffer[i]);
        if(i%16 == 15)
            printf("\n");
        if(readBuffer[i] != writeBuffer[i])
        {
            tmp = 1;
        }
    }
    if(tmp == 0)
    	EEPROM_INFO("I2C读取EEPROM成功\r\n");
    while(1)
    {
        if(tmp)
        {
            LED_R_TOGGLE;
            Delay_ms(1000);
        }
        else
        {
            LED_G_TOGGLE;
            Delay_ms(1000);
        }
    }
}

以上所有用到的bsp程序都是在之前的学习过程中慢慢写出来的,后面学习过程中也会不停的去积累,学习过的内容就不在此处重复占篇幅了,不懂得往前翻一翻看一下。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

放学校门口见

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

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

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

打赏作者

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

抵扣说明:

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

余额充值