【GD32F303红枫派使用手册】第二十一节 I2C-EEPROM读写实验

本文主要是介绍【GD32F303红枫派使用手册】第二十一节 I2C-EEPROM读写实验,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

21.1 实验内容

通过本实验主要学习以下内容:

  • AT24C16 EEPROM的工作原理;
  • IIC模块原理以及IIC驱动原理。

21.2 实验原理

21.2.1 AT24C16 EEPROM的工作原理

下图为AT24CXX系列EEPROM相关参数,由该图可知,AT24C16的存储容量为16Kbit,共2048字节,共128页,每页为16字节。

由下图可知,AT24C16由8块组成,每块256字节。

I2C开始信号后,第一个字节为器件地址,由1010+3位块地址+1位读写标志组成, 3位块地址刚好可以表示 8个块, 所以一次写完256字节,换到下一下块的时候,要重新更改器件地址。

AT24C16支持页写入模式,一次最多可支持写入16字节。主机每发送一个字节,24c16收到确认,内部地址递增(仅限低4bit,所以1次可写16字节)。

21.2.2 IIC接口原理

GD32F30X系列MCU的I2C 接口模块实现了I2C 协议的标速模式,快速模式以及快速+模式,具备CRC 计算和校验功能、支持 SMBus(系统管理总线)和PMBus(电源管理总线),此外还支持多主机 I2C 总线架构,其主要特性如下:

◼ 并行总线至 I2C 总线协议的转换及接口;
◼ 同一接口既可实现主机功能又可实现从机功能;
◼ 主从机之间的双向数据传输;
◼ 支持 7 位和 10 位的地址模式和广播寻址;
◼ 支持 I2C 多主机模式;
◼ 支持标速(最高 100 KHz),快速(最高 400 KHz)和快速+ 模式(最高 1MHz);
◼ 从机模式下可配置的 SCL 主动拉低;
◼ 支持 DMA 模式;
◼ 兼容 SMBus 2.0 和 PMBus;
◼ 两个中断:字节成功发送中断和错误事件中断;
◼ 可选择的 PEC(报文错误校验)生成和校验。  

IIC模块结构框图如下所示。

 

21.3 硬件设计

EEPROM硬件电路图如下所示,IIC引脚使用PB10和PB11引脚,SDA和SCL总线通过4.7K电阻上拉,且对地接30pf电容以及100欧姆串阻滤波。

21.4 代码解析

21.4.1 EEPROM初始化配置函数

EEPROM初始化配置函数如下,主要实现对IIC总线引脚配置以及IIC模块配置。

C
void bsp_eeprom_init_AT24C16(void)
{driver_i2c_init(&EEPROM_I2C);
}
void driver_i2c_init(typdef_i2c_struct *i2cx)
{rcu_periph_clock_enable(i2cx->rcu_i2c_x);i2c_deinit(i2cx->i2c_x);driver_gpio_general_init(i2cx->i2c_scl_gpio);driver_gpio_general_init(i2cx->i2c_sda_gpio);        /* I2C clock configure */i2c_clock_config(i2cx->i2c_x, i2cx->frequency, I2C_DTCY_2);/* I2C address configure */i2c_mode_addr_config(i2cx->i2c_x, I2C_I2CMODE_ENABLE, I2C_ADDFORMAT_7BITS, i2cx->slave_addr);/* enable I2C0 */i2c_enable(i2cx->i2c_x);/* enable acknowledge */i2c_ack_config(i2cx->i2c_x, I2C_ACK_ENABLE);    
}

21.4.2 EEPROM buf写入接口函数

EEPROM buf写入接口函数实现如下,通过该函数可实现对AT24C16任意地址的多字节写入。内部已根据地址和写入长度自动识别从机地址以及对应的块,然后写入正确的地址空间。

C
EEPROM_STATE eeprom_buffer_write_AT24C16(uint8_t* p_buffer, uint16_t write_address, uint16_t number_of_byte)
{uint8_t number_of_page = 0, number_of_single = 0, address = 0, count = 0;uint8_t deviceId;address = write_address % I2C_PAGE_SIZE;count = I2C_PAGE_SIZE - address;number_of_page =  number_of_byte / I2C_PAGE_SIZE;number_of_single = number_of_byte % I2C_PAGE_SIZE;if(write_address+write_address>EEPROM_SIZE){return EEPROM_ERROR;}/* if write_address is I2C_PAGE_SIZE aligned  */if(0 == address){while(number_of_page--){deviceId=(write_address>>8)>0 ? (EEPROM_ADDR | (uint8_t)((write_address>>7)&0x0E)):EEPROM_ADDR ;if(driver_i2c_mem_poll_write(&EEPROM_I2C,deviceId,write_address,MEM_ADDRESS_8BIT,p_buffer,I2C_PAGE_SIZE) == DRV_ERROR){return EEPROM_ERROR;}                                                        if(eeprom_wait_standby_state(&EEPROM_I2C) == EEPROM_ERROR){return EEPROM_ERROR;}write_address +=  I2C_PAGE_SIZE;p_buffer += I2C_PAGE_SIZE;}if(0 != number_of_single){deviceId=(write_address>>8)>0 ? (EEPROM_ADDR | (uint8_t)((write_address>>7)&0x0E)):EEPROM_ADDR ;if(driver_i2c_mem_poll_write(&EEPROM_I2C,deviceId,write_address,MEM_ADDRESS_8BIT,p_buffer, number_of_single)==DRV_ERROR){return EEPROM_ERROR;}if(eeprom_wait_standby_state(&EEPROM_I2C) == EEPROM_ERROR){return EEPROM_ERROR;}}return         EEPROM_SUCCESS;                        }else{/* if write_address is not I2C_PAGE_SIZE aligned */if(number_of_byte < count){ deviceId=(write_address>>8)>0 ? (EEPROM_ADDR | (uint8_t)((write_address>>7)&0x0E)):EEPROM_ADDR ;if(driver_i2c_mem_poll_write(&EEPROM_I2C,deviceId,write_address,MEM_ADDRESS_8BIT,p_buffer, number_of_byte)==DRV_ERROR){return EEPROM_ERROR;}if(eeprom_wait_standby_state(&EEPROM_I2C)==EEPROM_ERROR){return EEPROM_ERROR;}}else{number_of_byte -= count;number_of_page =  number_of_byte / I2C_PAGE_SIZE;number_of_single = number_of_byte % I2C_PAGE_SIZE;if(0 != count){deviceId=(write_address>>8)>0 ? (EEPROM_ADDR | (uint8_t)((write_address>>7)&0x0E)):EEPROM_ADDR ;if(driver_i2c_mem_poll_write(&EEPROM_I2C,deviceId,write_address,MEM_ADDRESS_8BIT,p_buffer, count)==DRV_ERROR){return EEPROM_ERROR;}if(eeprom_wait_standby_state(&EEPROM_I2C)==EEPROM_ERROR){return EEPROM_ERROR;}write_address += count;p_buffer += count;} /* write page */while(number_of_page--){deviceId=(write_address>>8)>0 ? (EEPROM_ADDR | (uint8_t)((write_address>>7)&0x0E)):EEPROM_ADDR ;if(driver_i2c_mem_poll_write(&EEPROM_I2C,deviceId,write_address,MEM_ADDRESS_8BIT,p_buffer, I2C_PAGE_SIZE)==DRV_ERROR){return EEPROM_ERROR;}if(eeprom_wait_standby_state(&EEPROM_I2C)==EEPROM_ERROR){return EEPROM_ERROR;}write_address +=  I2C_PAGE_SIZE;p_buffer += I2C_PAGE_SIZE;}/* write single */if(0 != number_of_single){deviceId=(write_address>>8)>0 ? (EEPROM_ADDR | (uint8_t)((write_address>>7)&0x0E)):EEPROM_ADDR ;if(driver_i2c_mem_poll_write(&EEPROM_I2C,deviceId,write_address,MEM_ADDRESS_8BIT,p_buffer, number_of_single)==DRV_ERROR){return EEPROM_ERROR;}if(eeprom_wait_standby_state(&EEPROM_I2C)==EEPROM_ERROR){return EEPROM_ERROR;}}}return         EEPROM_SUCCESS;        }  
}

 21.4.3 EEPROM buf读取接口函数

EEPROM buf读取接口函数实现如下,通过该函数可实现对EEPROM任意地址的多字节数据读取,内部也对读取的地址进行自动识别从机地址。

C
EEPROM_STATE eeprom_buffer_read_AT24C16(uint8_t* p_buffer, uint16_t read_address, uint16_t number_of_byte)
{uint8_t rNum=0; //读取的数据长度uint16_t lenLeft=number_of_byte;//剩余的数据长度uint8_t deviceId;//读取的器件地址if(read_address+number_of_byte>EEPROM_SIZE)//如果读取的长度加上读取地址超过了EEPROM的空间大小,则报错误{return EEPROM_ERROR;}/*calculate the current read position to know how many word can read continully*/rNum=16-read_address & 0x0F;if(rNum == 0)  rNum=16;rNum = lenLeft>=rNum ? rNum : lenLeft;//剩余未读字节数如果大于rNum, 则读rNum个,如果小于rNum,则一次读完了/*read the data from e2prom*/while(lenLeft){//这里计算页地址,当地址小于256时,右移8位会小于0,所以器件地址为基地址A1//如果读取的地址大于256时,右移8位则不会小于0,所以器件地址为 基地址A1 | 3位页地址deviceId=(read_address>>8)>0 ? (EEPROM_ADDR | (uint8_t)((read_address>>7)&0x0E)):EEPROM_ADDR ;if(driver_i2c_mem_poll_read(&EEPROM_I2C,deviceId,read_address,MEM_ADDRESS_8BIT,p_buffer,rNum)==DRV_ERROR){
//                        printf("i2c read error\r\n");return EEPROM_ERROR;}read_address+=rNum;//已经读了rNum个了,所以地址后移rNum个lenLeft-=rNum;//剩余未读数据减少rNum个p_buffer+=rNum;rNum=lenLeft>16? 16 : lenLeft;//如果剩余大于16个,则下次再读16个,如果小于,则一次读完}return EEPROM_SUCCESS;
}

21.4.4 EEPROM读写实验主函数

EEPROM读写实验主函数如下所示。通过该实验实现对AT24C16任意地址256字节的写入、读取以及校验测试。

C
int main(void)
{
          uint16_t i;
    uint8_t i2c_buffer_write[BUFFER_SIZE];
    uint8_t i2c_buffer_read[BUFFER_SIZE];
        
    bsp_eeprom_init_AT24C16();
    /* initialize i2c_buffer_write */
    for(i = 0;i < BUFFER_SIZE;i++){
        i2c_buffer_write[i]=i;
//        printf("0x%02X ",i2c_buffer_write[i]);
//        if(15 == i%16){
//            printf("\r\n");
//        }
    }
                
                if(eeprom_buffer_write_AT24C16(i2c_buffer_write,0x0153,BUFFER_SIZE)==EEPROM_SUCCESS)
                {
                        __nop();
                }
                if(eeprom_buffer_read_AT24C16(i2c_buffer_read,0x0153,BUFFER_SIZE)==EEPROM_SUCCESS)
                {
                        __nop();
                }
                    /* compare the read buffer and write buffer */
    for(i = 0;i < BUFFER_SIZE;i++){
        if(i2c_buffer_read[i] != i2c_buffer_write[i]){
                                        __nop();
//            printf("0x%02X ", i2c_buffer_read[i]);
//            printf("Err:data read and write aren't matching.\n\r");
//            return I2C_FAIL;
        }
        //printf("0x%02X ", i2c_buffer_read[i]);
//        if(15 == i%16){
//            printf("\r\n");
//        }
    }
                __nop();
//    printf("I2C-AT24C02 test passed!\n\r");
        while (1)
        {
        }
}

21.5 实验结果

将本实验历程烧录到红枫派开发板中,运行后,可通过串口打印测试结果,可实现对于AT24C16任意地址写入、读取以及校验。

由聚沃科技原创,来源于【红枫派开发板】第二十一讲 I2C-EEPROM读写实验 - 苏州聚沃电子科技有限公司 (gd32bbs.com) 

 

 

 

这篇关于【GD32F303红枫派使用手册】第二十一节 I2C-EEPROM读写实验的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1082070

相关文章

10. 文件的读写

10.1 文本文件 操作文件三大类: ofstream:写操作ifstream:读操作fstream:读写操作 打开方式解释ios::in为了读文件而打开文件ios::out为了写文件而打开文件,如果当前文件存在则清空当前文件在写入ios::app追加方式写文件ios::trunc如果文件存在先删除,在创建ios::ate打开文件之后令读写位置移至文件尾端ios::binary二进制方式

【STM32】SPI通信-软件与硬件读写SPI

SPI通信-软件与硬件读写SPI 软件SPI一、SPI通信协议1、SPI通信2、硬件电路3、移位示意图4、SPI时序基本单元(1)开始通信和结束通信(2)模式0---用的最多(3)模式1(4)模式2(5)模式3 5、SPI时序(1)写使能(2)指定地址写(3)指定地址读 二、W25Q64模块介绍1、W25Q64简介2、硬件电路3、W25Q64框图4、Flash操作注意事项软件SPI读写W2

衡石分析平台使用手册-单机安装及启动

单机安装及启动​ 本文讲述如何在单机环境下进行 HENGSHI SENSE 安装的操作过程。 在安装前请确认网络环境,如果是隔离环境,无法连接互联网时,请先按照 离线环境安装依赖的指导进行依赖包的安装,然后按照本文的指导继续操作。如果网络环境可以连接互联网,请直接按照本文的指导进行安装。 准备工作​ 请参考安装环境文档准备安装环境。 配置用户与安装目录。 在操作前请检查您是否有 sud

STM32(十一):ADC数模转换器实验

AD单通道: 1.RCC开启GPIO和ADC时钟。配置ADCCLK分频器。 2.配置GPIO,把GPIO配置成模拟输入的模式。 3.配置多路开关,把左面通道接入到右面规则组列表里。 4.配置ADC转换器, 包括AD转换器和AD数据寄存器。单次转换,连续转换;扫描、非扫描;有几个通道,触发源是什么,数据对齐是左对齐还是右对齐。 5.ADC_CMD 开启ADC。 void RCC_AD

HNU-2023电路与电子学-实验3

写在前面: 一、实验目的 1.了解简易模型机的内部结构和工作原理。 2.分析模型机的功能,设计 8 重 3-1 多路复用器。 3.分析模型机的功能,设计 8 重 2-1 多路复用器。 4.分析模型机的工作原理,设计模型机控制信号产生逻辑。 二、实验内容 1.用 VERILOG 语言设计模型机的 8 重 3-1 多路复用器; 2.用 VERILOG 语言设计模型机的 8 重 2-1 多

关于使用cspreadsheet读写EXCEL表格数据的问题

前几天项目有读写EXCEL表格的需求,我就找了大概有几种,大致分为:COM方法、ODBC方法、OLE方法、纯底层格式分析方法。由于COM方法要求必须安装有OFFICE的EXCEL组件,纯底层格式分析方法又很多功能需要自行去完善,所有最终选择了数据库的方法,用数据库的方法去存取xls格式的数据。网上有一个高手写的CSpreedSheet,看了一下提供的接口,感觉挺好用的。在使用的过程中发现几个

linux 内核提权总结(demo+exp分析) -- 任意读写(四)

hijack_modprobe_path篇 本文转自网络文章,内容均为非盈利,版权归原作者所有。 转载此文章仅为个人收藏,分享知识,如有侵权,马上删除。 原文作者:jmpcall 专栏地址:https://zhuanlan.kanxue.com/user-815036.htm     原理同hijack_prctl, 当用户执行错误格式的elf文件时内核调用call_usermod

linux 内核提权总结(demo+exp分析) -- 任意读写(三)

hijack_prctl篇 本文转自网络文章,内容均为非盈利,版权归原作者所有。 转载此文章仅为个人收藏,分享知识,如有侵权,马上删除。 原文作者:jmpcall 专栏地址:https://zhuanlan.kanxue.com/user-815036.htm   prctl函数: 用户态函数,可用于定制进程参数,非常适合和内核进行交互 用户态执行prctl函数后触发prctl系统

linux 内核提权总结(demo+exp分析) -- 任意读写(二)

hijack_vdso篇 本文转自网络文章,内容均为非盈利,版权归原作者所有。 转载此文章仅为个人收藏,分享知识,如有侵权,马上删除。 原文作者:jmpcall 专栏地址:https://zhuanlan.kanxue.com/user-815036.htm     vdso: 内核实现的一个动态库,存在于内核,然后映射到用户态空间,可由用户态直接调用 内核中的vdso如果被修改

linux 内核提权总结(demo+exp分析) -- 任意读写(一)

cred篇 本文转自网络文章,内容均为非盈利,版权归原作者所有。 转载此文章仅为个人收藏,分享知识,如有侵权,马上删除。 原文作者:jmpcall 专栏地址:https://zhuanlan.kanxue.com/user-815036.htm   每个线程在内核中都对应一个线程结构块thread_infothread_info中存在task_struct类型结构体 struct t