[研电赛技术支持] 【GD32F303红枫派使用手册】第二十一讲 I2C-EEPROM读写实验

[复制链接]
3433|0
 楼主| 聚沃科技 发表于 2024-6-21 10:11 | 显示全部楼层 |阅读模式
本帖最后由 聚沃科技 于 2024-6-21 10:32 编辑

红枫派首图.png
21.1 实验内容
通过本实验主要学习以下内容:
• AT24C16 EEPROM的工作原理;
• IIC模块原理以及IIC驱动原理。
21.2 实验原理
21.2.1 AT24C16 EEPROM的工作原理
下图为AT24CXX系列EEPROM相关参数,由该图可知,AT24C16的存储容量为16Kbit,共2048字节,共128页,每页为16字节。
图片1.png
由下图可知,AT24C168块组成,每块256字节。
图片2.png
I2C开始信号后,第一个字节为器件地址,由1010+3位块地址+1位读写标志组成, 3位块地址刚好可以表示 8个块, 所以一次写完256字节,换到下一下块的时候,要重新更改器件地址。
图片3.png
AT24C16支持页写入模式,一次最多可支持写入16字节。主机每发送一个字节,24c16收到确认,内部地址递增(仅限低4bit,所以1次可写16字节)
21.2.2 IIC接口原理
GD32F30X系列MCUI2C 接口模块实现了I2C 协议的标速模式,快速模式以及快速+模式,具备CRC 计算和校验功能、支持 SMBus(系统管理总线)和PMBus(电源管理总线),此外还支持多主机 I2C 总线架构,其主要特性如下:
并行总线至 I2C 总线协议的转换及接口;
同一接口既可实现主机功能又可实现从机功能;
主从机之间的双向数据传输;
支持 7 位和 10 位的地址模式和广播寻址;
支持 I2C 多主机模式;
支持标速(最高 100 KHz),快速(最高 400 KHz)和快速+ 模式(最高 1MHz);
从机模式下可配置的 SCL 主动拉低;
支持 DMA 模式;
兼容 SMBus 2.0 PMBus
两个中断:字节成功发送中断和错误事件中断;
可选择的 PEC(报文错误校验)生成和校验。  
IIC模块结构框图如下所示。
图片4.png
21.3 硬件设计
EEPROM硬件电路图如下所示,IIC引脚使用PB10PB11引脚,SDASCL总线通过4.7K电阻上拉,且对地接30pf电容以及100欧姆串阻滤波。
图片5.png

图片6.png

21.4 代码解析
21.4.1 EEPROM初始化配置函数
EEPROM初始化配置函数如下,主要实现对IIC总线引脚配置以及IIC模块配置。
  1. C
  2. void bsp_eeprom_init_AT24C16(void)
  3. {
  4.         driver_i2c_init(&EEPROM_I2C);
  5. }
  6. void driver_i2c_init(typdef_i2c_struct *i2cx)
  7. {
  8.     rcu_periph_clock_enable(i2cx->rcu_i2c_x);

  9.     i2c_deinit(i2cx->i2c_x);
  10.    
  11.     driver_gpio_general_init(i2cx->i2c_scl_gpio);
  12.     driver_gpio_general_init(i2cx->i2c_sda_gpio);        
  13.    
  14.     /* I2C clock configure */
  15.     i2c_clock_config(i2cx->i2c_x, i2cx->frequency, I2C_DTCY_2);
  16.     /* I2C address configure */
  17.     i2c_mode_addr_config(i2cx->i2c_x, I2C_I2CMODE_ENABLE, I2C_ADDFORMAT_7BITS, i2cx->slave_addr);
  18.     /* enable I2C0 */
  19.     i2c_enable(i2cx->i2c_x);
  20.     /* enable acknowledge */
  21.     i2c_ack_config(i2cx->i2c_x, I2C_ACK_ENABLE);   
  22. }
21.4.2 EEPROM buf写入接口函数
EEPROM buf写入接口函数实现如下,通过该函数可实现对AT24C16任意地址的多字节写入。内部已根据地址和写入长度自动识别从机地址以及对应的块,然后写入正确的地址空间。
  1. C
  2. EEPROM_STATE eeprom_buffer_write_AT24C16(uint8_t* p_buffer, uint16_t write_address, uint16_t number_of_byte)
  3. {
  4.           uint8_t number_of_page = 0, number_of_single = 0, address = 0, count = 0;
  5.     uint8_t deviceId;
  6.     address = write_address % I2C_PAGE_SIZE;
  7.     count = I2C_PAGE_SIZE - address;
  8.     number_of_page =  number_of_byte / I2C_PAGE_SIZE;
  9.     number_of_single = number_of_byte % I2C_PAGE_SIZE;
  10.    
  11.           if(write_address+write_address>EEPROM_SIZE)
  12.                 {
  13.                         return EEPROM_ERROR;
  14.                 }
  15.     /* if write_address is I2C_PAGE_SIZE aligned  */
  16.     if(0 == address){
  17.         while(number_of_page--){
  18.                                        
  19.                                           deviceId=(write_address>>8)>0 ? (EEPROM_ADDR | (uint8_t)((write_address>>7)&0x0E)):EEPROM_ADDR ;
  20.             if(driver_i2c_mem_poll_write(&EEPROM_I2C,deviceId,write_address,MEM_ADDRESS_8BIT,p_buffer,I2C_PAGE_SIZE) == DRV_ERROR)
  21.                                                 {
  22.                                                         return EEPROM_ERROR;
  23.                                                 }                                                        
  24.             if(eeprom_wait_standby_state(&EEPROM_I2C) == EEPROM_ERROR)
  25.                                                 {
  26.                                                         return EEPROM_ERROR;
  27.                                                 }
  28.             write_address +=  I2C_PAGE_SIZE;
  29.             p_buffer += I2C_PAGE_SIZE;
  30.         }
  31.         if(0 != number_of_single){
  32.                                          deviceId=(write_address>>8)>0 ? (EEPROM_ADDR | (uint8_t)((write_address>>7)&0x0E)):EEPROM_ADDR ;
  33.            if(driver_i2c_mem_poll_write(&EEPROM_I2C,deviceId,write_address,MEM_ADDRESS_8BIT,p_buffer, number_of_single)==DRV_ERROR)
  34.                                          {
  35.                                                  return EEPROM_ERROR;
  36.                                          }
  37.            if(eeprom_wait_standby_state(&EEPROM_I2C) == EEPROM_ERROR)
  38.                                          {
  39.                                                  return EEPROM_ERROR;
  40.                                          }
  41.         }
  42.         return         EEPROM_SUCCESS;                        
  43.     }else{
  44.         /* if write_address is not I2C_PAGE_SIZE aligned */
  45.         if(number_of_byte < count){
  46.                                         deviceId=(write_address>>8)>0 ? (EEPROM_ADDR | (uint8_t)((write_address>>7)&0x0E)):EEPROM_ADDR ;
  47.                                         if(driver_i2c_mem_poll_write(&EEPROM_I2C,deviceId,write_address,MEM_ADDRESS_8BIT,p_buffer, number_of_byte)==DRV_ERROR)
  48.                                         {
  49.                                                 return EEPROM_ERROR;
  50.                                         }
  51.                                         if(eeprom_wait_standby_state(&EEPROM_I2C)==EEPROM_ERROR)
  52.                                         {
  53.                                                 return EEPROM_ERROR;
  54.                                         }
  55.                                        
  56.         }else{
  57.             number_of_byte -= count;
  58.             number_of_page =  number_of_byte / I2C_PAGE_SIZE;
  59.             number_of_single = number_of_byte % I2C_PAGE_SIZE;
  60.             
  61.             if(0 != count){
  62.                                                           deviceId=(write_address>>8)>0 ? (EEPROM_ADDR | (uint8_t)((write_address>>7)&0x0E)):EEPROM_ADDR ;
  63.                                               if(driver_i2c_mem_poll_write(&EEPROM_I2C,deviceId,write_address,MEM_ADDRESS_8BIT,p_buffer, count)==DRV_ERROR)
  64.                                                                 {
  65.                                                                         return EEPROM_ERROR;
  66.                                                                 }
  67.                                                           if(eeprom_wait_standby_state(&EEPROM_I2C)==EEPROM_ERROR)
  68.                                                                 {
  69.                                                                         return EEPROM_ERROR;
  70.                                                                 }
  71.                 write_address += count;
  72.                 p_buffer += count;
  73.             }
  74.             /* write page */
  75.             while(number_of_page--){
  76.                                                           deviceId=(write_address>>8)>0 ? (EEPROM_ADDR | (uint8_t)((write_address>>7)&0x0E)):EEPROM_ADDR ;
  77.                                               if(driver_i2c_mem_poll_write(&EEPROM_I2C,deviceId,write_address,MEM_ADDRESS_8BIT,p_buffer, I2C_PAGE_SIZE)==DRV_ERROR)
  78.                                                                 {
  79.                                                                         return EEPROM_ERROR;
  80.                                                                 }
  81.                                                           if(eeprom_wait_standby_state(&EEPROM_I2C)==EEPROM_ERROR)
  82.                                                                 {
  83.                                                                         return EEPROM_ERROR;
  84.                                                                 }
  85.                 write_address +=  I2C_PAGE_SIZE;
  86.                 p_buffer += I2C_PAGE_SIZE;
  87.             }
  88.             /* write single */
  89.             if(0 != number_of_single){
  90.                                                           deviceId=(write_address>>8)>0 ? (EEPROM_ADDR | (uint8_t)((write_address>>7)&0x0E)):EEPROM_ADDR ;
  91.                                               if(driver_i2c_mem_poll_write(&EEPROM_I2C,deviceId,write_address,MEM_ADDRESS_8BIT,p_buffer, number_of_single)==DRV_ERROR)
  92.                                                                 {
  93.                                                                         return EEPROM_ERROR;
  94.                                                                 }
  95.                                                           if(eeprom_wait_standby_state(&EEPROM_I2C)==EEPROM_ERROR)
  96.                                                                 {
  97.                                                                         return EEPROM_ERROR;
  98.                                                                 }
  99.             }
  100.         }
  101.                                 return         EEPROM_SUCCESS;        
  102.     }  
  103. }
21.4.3 EEPROM buf读取接口函数
EEPROM buf读取接口函数实现如下,通过该函数可实现对EEPROM任意地址的多字节数据读取,内部也对读取的地址进行自动识别从机地址。
  1. C
  2. EEPROM_STATE eeprom_buffer_read_AT24C16(uint8_t* p_buffer, uint16_t read_address, uint16_t number_of_byte)
  3. {
  4.         uint8_t rNum=0; //读取的数据长度
  5.         uint16_t lenLeft=number_of_byte;//剩余的数据长度
  6.   uint8_t deviceId;//读取的器件地址
  7.         if(read_address+number_of_byte>EEPROM_SIZE)//如果读取的长度加上读取地址超过了EEPROM的空间大小,则报错误
  8.         {
  9.                 return EEPROM_ERROR;
  10.         }
  11.                 /*calculate the current read position to know how many word can read continully*/
  12.         rNum=16-read_address & 0x0F;
  13.         if(rNum == 0)  rNum=16;
  14.         rNum = lenLeft>=rNum ? rNum : lenLeft;//剩余未读字节数如果大于rNum, 则读rNum个,如果小于rNum,则一次读完了
  15.         /*read the data from e2prom*/
  16.         while(lenLeft)
  17.         {
  18.                 //这里计算页地址,当地址小于256时,右移8位会小于0,所以器件地址为基地址A1
  19.                 //如果读取的地址大于256时,右移8位则不会小于0,所以器件地址为 基地址A1 | 3位页地址
  20.     deviceId=(read_address>>8)>0 ? (EEPROM_ADDR | (uint8_t)((read_address>>7)&0x0E)):EEPROM_ADDR ;

  21.                 if(driver_i2c_mem_poll_read(&EEPROM_I2C,deviceId,read_address,MEM_ADDRESS_8BIT,p_buffer,rNum)==DRV_ERROR)
  22.                 {
  23. //                        printf("i2c read error\r\n");
  24.                           return EEPROM_ERROR;
  25.                 }
  26.                 read_address+=rNum;//已经读了rNum个了,所以地址后移rNum个
  27.                 lenLeft-=rNum;//剩余未读数据减少rNum个
  28.                 p_buffer+=rNum;
  29.                 rNum=lenLeft>16? 16 : lenLeft;//如果剩余大于16个,则下次再读16个,如果小于,则一次读完
  30.         }
  31.   return EEPROM_SUCCESS;
  32. }
21.4.4 EEPROM读写实验主函数
EEPROM读写实验主函数如下所示。通过该实验实现对AT24C16任意地址256字节的写入、读取以及校验测试。
  1. C
  2. int main(void)
  3. {
  4.           uint16_t i;
  5.     uint8_t i2c_buffer_write[BUFFER_SIZE];
  6.     uint8_t i2c_buffer_read[BUFFER_SIZE];
  7.         
  8.     bsp_eeprom_init_AT24C16();
  9.     /* initialize i2c_buffer_write */
  10.     for(i = 0;i < BUFFER_SIZE;i++){
  11.         i2c_buffer_write[i]=i;
  12. //        printf("0x%02X ",i2c_buffer_write[i]);
  13. //        if(15 == i%16){
  14. //            printf("\r\n");
  15. //        }
  16.     }
  17.                
  18.                 if(eeprom_buffer_write_AT24C16(i2c_buffer_write,0x0153,BUFFER_SIZE)==EEPROM_SUCCESS)
  19.                 {
  20.                         __nop();
  21.                 }
  22.                 if(eeprom_buffer_read_AT24C16(i2c_buffer_read,0x0153,BUFFER_SIZE)==EEPROM_SUCCESS)
  23.                 {
  24.                         __nop();
  25.                 }
  26.                     /* compare the read buffer and write buffer */
  27.     for(i = 0;i < BUFFER_SIZE;i++){
  28.         if(i2c_buffer_read[i] != i2c_buffer_write[i]){
  29.                                         __nop();
  30. //            printf("0x%02X ", i2c_buffer_read[i]);
  31. //            printf("Err:data read and write aren't matching.\n\r");
  32. //            return I2C_FAIL;
  33.         }
  34.         //printf("0x%02X ", i2c_buffer_read[i]);
  35. //        if(15 == i%16){
  36. //            printf("\r\n");
  37. //        }
  38.     }
  39.                 __nop();
  40. //    printf("I2C-AT24C02 test passed!\n\r");
  41.         while (1)
  42.         {
  43.         }
  44. }
21.5 实验结果
将本实验历程烧录到红枫派开发板中,运行后,可通过串口打印测试结果,可实现对于AT24C16任意地址写入、读取以及校验。
图片7.png
本教程由GD32 MCU方案商聚沃科技原创发布,了解更多GD32 MCU教程,关注聚沃科技官网,GD32MCU技术交流群:859440462

您需要登录后才可以回帖 登录 | 注册

本版积分规则

170

主题

190

帖子

13

粉丝
快速回复 在线客服 返回列表 返回顶部