瑞萨RA系列FSP库开发实战指南之I2C读写EEPROM实验

23185 2026-01-28

21.4.1

硬件设计

野火启明6M5开发板的EEPROM电路图如图所示:

7b169140-fa6e-11f0-92de-92fbcf53809c.png

图22‑13a EEPROM硬件连接图

野火启明4M2开发板的EEPROM电路图如图所示:

图22‑13b EEPROM硬件连接图

野火启明2L1开发板的EEPROM电路图如图所示:

7bd856ea-fa6e-11f0-92de-92fbcf53809c.png

图22‑13c EEPROM硬件连接图

EEPROM芯片连接到MCU的引脚如下表所示。

表2:EEPROM引脚

21.4.2

软件设计

使用瑞萨官方提供的FPS库进行编程,瑞萨官方提供的FPS库具有方便、快捷、简洁的特性。

21.4.2.1

新建工程

因为本章节的EEPROM相关实验例程需要用到板子上的串口功能,因此我们可以直接以前面的“19_UART_Receive_Send”工程为基础进行修改。

对于e2studio开发环境:拷贝一份我们之前的e2s工程“19_UART_Receive_Send”“22_EEPROM_Hardware”,然后将工程文件夹重命名为“16_ICU_External_IRQ”,最后再将它导入到我们的e2studio工作空间中。

对于Keil开发环境:拷贝一份我们之前的Keil工程“19_UART_Receive_Send”“22_EEPROM_Hardware”,然后将工程文件夹重命名为“16_ICU_External_IRQ”,并进入该文件夹里面双击Keil工程文件,打开该工程。

21.4.2.2

FSP配置

新建工程后,我们先打开“22_EEPROM_Hardware”项目的FSP配置界面进行配置。新建工程后,

在FSP配置界面里面我们依次点击“Stacks”->“NewStack”->“Connectivity”->“I2CMaster”来配置IIC模块。如图22_14。

7c8be412-fa6e-11f0-92de-92fbcf53809c.png

图22-14 加入IIC

按照图片顺序依次进行点击然后点击“I2CMaster”在左下角的属性界面里进行配置。如图22_15。

7cf59eac-fa6e-11f0-92de-92fbcf53809c.png

图22-15 配置图

配置完成之后可以按下快捷键“Ctrl+S”保存,最后点右上角的“GenerateProjectContent”按钮,让软件自动生成配置代码即可。

21.4.2.3

R_IIC_MASTER_Write函数

列表1:代码清单22_1:

R_IIC_MASTER_Write结构体

左右滑动查看完整内容

fsp_err_tR_IIC_MASTER_Write(i2c_master_ctrl_t*constp_api_ctrl,uint8_t␣
→*constp_src,uint32_tconst bytes,boolconst restart)

当我们调用该函数,在数据传输的开始时会发送从设备的地址位。之后根据p_src数组发送第一个位在数据位,传输的过程中硬件会自动发送确认位和结束位。传输数据的长度与bytes有关,完成之后restart来决定此次通信之后是否通过发出重复的START条件来保持总线。

在调用这个函数之后我们需要延时一段时间或者使用回调函数判断,之后再调用下一段IIC函数,详细的可以看下面IIC的写入代码,原因是因为当你使用了R_IIC_MASTER_Write函数之后,IIC通信还未完成,如果你再次调用其他的IIC函数就会覆盖掉第一次执行的函数,从而出现时序错误。

21.4.2.4

R_IIC_MASTER_Read函数

列表2:代码清单22_2:

R_IIC_MASTER_Read结构体

左右滑动查看完整内容

fsp_err_tR_IIC_MASTER_Read(i2c_master_ctrl_t*constp_api_ctrl,uint8_t*␣
→constp_dest,uint32_tconst bytes,boolconst restart)

当我们调用该函数,在数据传输的开始时会发送从设备的地址位。之后根据p_src数组保存第一个获取的数据,传输的过程中硬件会自动加载应答位和结束位。传输数据的长度与bytes有关,完成之后restart来决定此次通信之后是否通过发出重复的START条件来保持总线。

该函数与之前的R_IIC_MASTER_Write函数一样在使用之后需要需要延时一段时间或者使用回调函数判断。

21.4.2.5. 向EEPROM写入一个字节

初始化好I2C外设后,就可以使用I2C通讯了,更具上面两个函数的介绍我们就可以写出EEPROM的写入以及读取函数。我们看看如何向EEPROM写入一个字节的数据,见代码清单22_3。

代码清单 22_3:EEPROM写入一个字节函数

 
 
/**
* @brief 以单字节的方式到I2C EEPROM中
* @param
*   @arg address:写地址
*   @arg byte:写的数据
* @retval  无
*/
void I2C_EE_ByteWrite(unsigned char address, unsigned char byte)
{
   iic_complete = false;
   unsigned char send_buffer[2] = {};

   send_buffer[0] = address;
   send_buffer[1] = byte;
   R_IIC_MASTER_Write(&EEPROM_ctrl, &send_buffer[0], 2, false); //每当写完数据 false 总线拉高

   while ((I2C_MASTER_EVENT_TX_COMPLETE != g_i2c_callback_event) && timeout_ms)
   {
      R_BSP_SoftwareDelay(1U, BSP_DELAY_UNITS_MILLISECONDS);
      timeout_ms--;
   }
   timeout_ms = 500;
}

这里我们只是简单调用库函数R_IIC_MASTER_Write就可以实现,通过封装一次使用更为方便。

在这个通讯过程中,RA6M5实际上通过I2C向EEPROM发送了两个数据, 但为何第一个数据被解释为EEPROM的内存地址? 这是由EEPROM的自己定义的单字节写入时序,见图22_16.

wKgZO2l4HXeAAOYIAAD_2zjG3HM55.jpeg

图 22‑16 EEPROM单字节写入时序(摘自《AT24C02》规格书)

EEPROM的单字节时序规定,向它写入数据的时候,第一个字节为内存地址,第二个字节是要写入的数据内容。所以我们需要理解:命令、地址的本质都是数据,对数据的解释不同,它就有了不同的功能。

21.4.2.6. EEPROM的页写入

在以上的数据通讯中,每写入一个数据都需要向EEPROM发送写入的地址,我们希望向连续地址写入多个数据的时候,只要告诉EEPROM第一个内存地址address1,后面的数据按次序写入到address2、address3… 这样可以节省通讯的内容,加快速度。为应对这种需求,EEPROM定义了一种页写入时序,见图22_17。

wKgZO2l4HXeAMtAdAADKHFsFXwc17.jpeg

图 22‑17 EEPROM页写入时序(摘自《AT24C02》规格书)

根据页写入时序,第一个数据被解释为要写入的内存地址address1,后续可连续发送n个数据, 这些数据会依次写入到内存中。其中AT24C02型号的芯片页写入时序最多可以一次发送8个数据(即n = 8 ),该值也称为页大小,某些型号的芯片每个页写入时序最多可传输16个数据。EEPROM的页写入代码实现 见代码清单22_4。

代码清单 22‑4 EEPROM的页写入

 
/**
* @brief   将缓冲区中的数据以页写入的方式写到I2C EEPROM中
* @param
*   @arg ptr_write:缓冲区指针
*   @arg WriteAddr:写地址
*     @arg len:写的长度
* @retval  无
*/
void I2C_EE_Writepage(unsigned char* ptr_write , unsigned char WriteAddr,unsigned char len)      //页写入   page 0~31
{

   unsigned char send_buffer[9] = {};
   send_buffer[0] = WriteAddr;

   for(unsigned char i = 0;i
 

  

}

21.4.2.7. 多字节写入

多次写入数据时,利用EEPROM的页写入方式,避免单字节读写时候的等待。多个数据写入过程 见代码清单22_5。

代码清单 22‑5 多字节写入



  
/**
* @brief   将缓冲区中的数据写到I2C EEPROM中
* @param
*   @arg pBuffer:缓冲区指针
*   @arg WriteAddr:写地址
*     @arg NumByteToWrite:写的字节数
* @retval  无
*/
void I2C_EE_BufferWrite(uint8_t* pBuffer, uint8_t WriteAddr,uint16_t NumByteToWrite)
{
   uint8_t NumOfPage = 0, NumOfSingle = 0, Addr = 0, count = 0;

   Addr = WriteAddr % EEPROM_PAGESIZE;
   count = EEPROM_PAGESIZE - Addr;
   NumOfPage =  NumByteToWrite / EEPROM_PAGESIZE;
   NumOfSingle = NumByteToWrite % EEPROM_PAGESIZE;

   /* If WriteAddr is I2C_PageSize aligned  */
   if (Addr == 0) {
      /* If NumByteToWrite < I2C_PageSize */
      if (NumOfPage == 0) {
            I2C_EE_Writepage(pBuffer, WriteAddr, NumOfSingle);
      }
      /* If NumByteToWrite > I2C_PageSize */
      else {
            while (NumOfPage--) {
               I2C_EE_Writepage(pBuffer, WriteAddr, EEPROM_PAGESIZE);
               WriteAddr +=  EEPROM_PAGESIZE;
               pBuffer += EEPROM_PAGESIZE;
            }

            if (NumOfSingle!=0) {
               I2C_EE_Writepage(pBuffer, WriteAddr, NumOfSingle);
            }
      }
   }
   /* If WriteAddr is not I2C_PageSize aligned  */
   else {
      /* If NumByteToWrite < I2C_PageSize */
      if (NumOfPage== 0) {
            I2C_EE_Writepage(pBuffer, WriteAddr, NumOfSingle);
      }
      /* If NumByteToWrite > I2C_PageSize */
      else {
            NumByteToWrite -= count;
            NumOfPage =  NumByteToWrite / EEPROM_PAGESIZE;
            NumOfSingle = NumByteToWrite % EEPROM_PAGESIZE;

            if (count != 0) {
               I2C_EE_Writepage(pBuffer, WriteAddr, count);
               WriteAddr += count;
               pBuffer += count;
            }

            while (NumOfPage--) {
               I2C_EE_Writepage(pBuffer, WriteAddr, EEPROM_PAGESIZE);
               WriteAddr +=  EEPROM_PAGESIZE;
               pBuffer += EEPROM_PAGESIZE;
            }
            if (NumOfSingle != 0) {
               I2C_EE_Writepage(pBuffer, WriteAddr, NumOfSingle);
            }
      }
   }
}

21.4.2.8. EEPROM读取函数

从EEPROM读取数据是一个复合的I2C时序,它实际上包含一个写过程和一个读过程, 见图22_18。

wKgZPGl4HXeAN_ntAAHAl8HPEOo34.jpeg

图 22‑18 EEPROM数据读取时序

读时序的第一个通讯过程中,使用I2C发送设备地址寻址(写方向),接着发送要读取的“内存地址”;第二个通讯过程中, 再次使用I2C发送设备地址寻址,但这个时候的数据方向是读方向;在这个过程之后,EEPROM会向主机返回从“内存地址”开始的数据, 一个字节一个字节地传输,只要主机的响应为“应答信号”,它就会一直传输下去,主机想结束传输时,就发送“非应答信号”, 并以“停止信号”结束通讯,作为从机的EEPROM也会停止传输。FSP库已经帮我们实现了这一个过程, 我们只是简单封装一下就可以直接使用,实现代码见代码清单22_6。

代码清单 22_6:EEPROM读取函数

/**
* @brief 读取I2C EEPROM数据
* @param
*   @arg ptr_read:读取缓冲区指针
*   @arg address:地址
*     @arg byte:读取的字节数
* @retval  无
*/
void I2C_EE_BufferRead(unsigned char* ptr_read,unsigned char address,unsigned char byte)
{

   unsigned char send_buffer[2] = {};
   unsigned char read_buffer[1] = {};

   send_buffer[0] = address;
   R_IIC_MASTER_Write(&EEPROM_ctrl, &send_buffer[0], 1, true);
   while ((I2C_MASTER_EVENT_TX_COMPLETE != g_i2c_callback_event) && timeout_ms)
   {
      R_BSP_SoftwareDelay(400U, BSP_DELAY_UNITS_MICROSECONDS);
      timeout_ms--;
   }
   timeout_ms = 500;

   R_BSP_SoftwareDelay(250U, BSP_DELAY_UNITS_MICROSECONDS);

   R_IIC_MASTER_Read(&EEPROM_ctrl, ptr_read, byte, false);

}

这个函数是在指定的地址读取一个字节的数据,第一个变量EEPROM的地址,最后会返回一个整型的数据。如果不想使用printf函数可以将其进行注释,但需要适当增加延时时间。 这里代码非常简单,我们只需要确定I2C的地址、数据格式、数据存储指针、数据大小、超时设置,就可以把想要的数据读回来。

21.4.2.9. EEPROM测试函数

代码清单 22_7:EEPROM测试函数

/**
* @brief  I2C(AT24C02)读写测试
* @param  无
* @retval 正常返回1 ,不正常返回0
*/
uint8_t I2C_Test(void)
{
   uint16_t i;
   unsigned char DATA_Size = 30;
   unsigned char I2c_Buf_Write[33] = {};
   unsigned char I2c_Buf_Read[33] = {};

   //将I2c_Buf_Write中顺序递增的数据写入EERPOM中
   printf("写入的数据\r\n");
   for ( i=0; i
   

    

}

21.4.2.10. 主函数

代码清单 22_8:主函数



    
void hal_entry(void)
{

   I2C_EE_Init();
   Debug_UART4_Init();

   printf("欢迎使用野火  RA6M5 开发板。\r\n");
   printf("这是一个I2C外设(AT24C02)读写测试例程 \r\n");
   R_BSP_SoftwareDelay(1, BSP_DELAY_UNITS_SECONDS);

   while (1)
   {
      I2C_EE_Writedrase();
      if (I2C_Test() ==1) {
            LED_GREEN;
      } else {
            LED_RED;
      }

      while(1);
   }

#if BSP_TZ_SECURE_BUILD
   /* Enter non-secure code */
   R_BSP_NonSecureEnter();
#endif

}

21.4.3. 下载验证

保证开发板相关硬件连接正确,用Type-C线连接开发板“USB TO UART”接口电脑, 在电脑端打开串口调试助手,把编译好的程序下载到开发板, 此时串口调试助手即可收到开发板发过来的数据。 在串口调试助手可看到EEPROM测试的调试信息。如下图所示:

wKgZO2l4HXeAH-36AAI2R70bSpc273.jpg