当前位置: 首页 > news >正文

速通蓝桥杯嵌入式省一教程:(九)AT24C02芯片(E2PROM存储器)读写操作与I2C协议

AT24C02芯片(又叫E2PROM存储器、EEPROM存储器),是一种通过I2C(IIC)协议通信的掉电保存存储器芯片,其内部含有256个8位字节在介绍这款芯片之前,我们先来粗略了解一下I2C协议。

I2C总线是一种双向二线制的同步串行总线,它只需要两根线即可在连接于总线上的器件之间传送信息(分别为SDA和SCL)。在I2C总线上,可以有若干个从机(如AT24C02芯片),但只能有一个主机(如单片机)。像不同的通信协议一样,I2C协议规定了一些SDA、SCL的行为(如什么时候谁置高、什么时候谁置低,用以代表什么含义),连在I2C总线上的器件则依靠这个规则来传输数据与接收数据。

(想要深入了解I2C协议,可以参考嵌入式硬件入门——EEPROM(AT24C02+I2C协议))

通常,单片机上内置了硬件I2C。开启硬件I2C,并使用相应的HAL库函数,单片机中的硬件I2C就会按照其已经设定好的I2C协议与别的I2C器件进行通信。与硬件I2C相对的,还有软件I2C。软件I2C是指按照I2C协议,自行用两个GPIO端口置高或低模拟SDA和SCL的行为。在比赛方提供的资源数据包——底层驱动代码参考中,有用HAL库函数实现的软件I2C的库,我们就基于这个库来使用AT24C02芯片(E2PROM存储器)进行读写操作。

打开电路原理图(CT117E-M4产品手册),可以看到STM32G431RBT6的24C02芯片被挂到了PB6、PB7上(相当于I2C总线的SDA和SCL):

因此,我们需要在Cube中将PB6和PB7设置为GPIO输出模式。

设置完成后,需要调用比赛方提供的软件I2C库:i2c_hal.c和i2c_hal.h,我们需要用到的函数如下:

/*** @brief I2C起始信号* @param None* @retval None*/
void I2CStart(void);/*** @brief I2C结束信号* @param None* @retval None*/
void I2CStop(void);/*** @brief I2C等待确认信号* @param None* @retval None*/
unsigned char I2CWaitAck(void);/*** @brief I2C发送一个字节* @param cSendByte 需要发送的字节* @retval None*/
void I2CSendByte(unsigned char cSendByte);/*** @brief I2C接收一个字节* @param None* @retval 接收到的字节*/
unsigned char I2CReceiveByte(void);

下面,我们来看看如何利用这些函数来进行E2PROM的读写操作。

打开AT24C02芯片手册(在选手资源数据包——芯片资料中),可以找到向E2PROM写入数据与读取数据(读取内存)的流程图:

 

对应流程图,我们就可以编写E2PROM的读写操作函数如下:

/* e2prom.c */#include "e2prom.h"/* E2PROM写操作,对应Figure 8. Btye Write */
void e2prom_write(unsigned char address, unsigned char info)
{I2CStart();              //1.I2C起始信号(START)I2CSendByte(0xa0);       //2.发送设备地址与“写”信号(DEVICE ADDRESS+WRITE),将在下文解释I2CWaitAck();            //3.IC2等待确认信号(ACK)I2CSendByte(address);    //4.发送数据存储地址(WORD ADDRESS)(可以为0~255,对应256个)I2CWaitAck();            //5.I2C等待确认信号(ACK)I2CSendByte(info);       //6.发送数据(DATA)I2CWaitAck();            //7.I2C等待确认信号(ACK)I2CStop();               //8.I2C结束信号(STOP)
}/* E2PROM读操作,对应Figure 11. Random Read */
unsigned char e2prom_read(unsigned char address)
{unsigned char val;I2CStart();              //1.I2C起始信号(START)I2CSendByte(0xa0);       //2.发送设备地址与“写”信号(DEVICE ADDRESS+WR-TE),将在下文解释I2CWaitAck();            //3.IC2等待确认信号(ACK)I2CSendByte(address);    //4.发送数据存储地址(WORD ADDRESS)(可以为0~255,对应256个)I2CWaitAck();            //5.I2C等待确认信号(ACK)I2CStart();              //6.I2C起始信号(START)I2CSendByte(0xa1);       //7.发送设备地址与“读”信号(DEVICE ADDRESS+READ),将在下文解释I2CWaitAck();            //8.I2C等待确认信号(ACK)val = I2CReceiveByte();  //9.接收数据(DATA)I2CWaitAck();            //10.I2C等待确认信号(NO ACK)I2CStop();               //11.I2C结束信号(STOP)return (val);
}
/* e2prom.h */#ifndef __E2PROM_H
#define __E2PROM_H#include "main.h"
#include "i2c_hal.h"void e2prom_write(unsigned char address, unsigned char info);
unsigned char e2prom_read(unsigned char address);#endif /* __E2PROM_H */

下面来解释一下为什么第二步和第七步发送的是0xa0和0xa1。同样在芯片手册中,可以找到AT24C02芯片的地址:

AT24C01/02/04/08/16分别对应1K/2K/4K/8K/16K,A2、A1、A0分别对应电路原理图的E3、E2、E1。在下图中可以看到,E1、E2、E2均接地,为0。最后一位的R(Read)为1,W(Write)为0。因此,在STM32G431RBT6中,AT24C02的地址为1010000_(即0xa_),最后一位视读或写操作为1或0。

通过调用我们自己编写的e2prom的库,就可以使用E2PROM存储器进行简单的8位数据(unsigned char或uint8_t类型)的存储操作了。

附录

i2c_hal.c

/*程序说明: CT117E-M4嵌入式竞赛板GPIO模拟I2C总线驱动程序软件环境: MDK-ARM HAL库硬件环境: CT117E-M4嵌入式竞赛板日    期: 2020-3-1
*/#include "i2c_hal.h"#define DELAY_TIME	20/*** @brief SDA线输入模式配置* @param None* @retval None*/
void SDA_Input_Mode()
{GPIO_InitTypeDef GPIO_InitStructure = {0};GPIO_InitStructure.Pin = GPIO_PIN_7;GPIO_InitStructure.Mode = GPIO_MODE_INPUT;GPIO_InitStructure.Pull = GPIO_PULLUP;GPIO_InitStructure.Speed = GPIO_SPEED_FREQ_HIGH;HAL_GPIO_Init(GPIOB, &GPIO_InitStructure);
}/*** @brief SDA线输出模式配置* @param None* @retval None*/
void SDA_Output_Mode()
{GPIO_InitTypeDef GPIO_InitStructure = {0};GPIO_InitStructure.Pin = GPIO_PIN_7;GPIO_InitStructure.Mode = GPIO_MODE_OUTPUT_OD;GPIO_InitStructure.Pull = GPIO_NOPULL;GPIO_InitStructure.Speed = GPIO_SPEED_FREQ_HIGH;HAL_GPIO_Init(GPIOB, &GPIO_InitStructure);
}/*** @brief SDA线输出一个位* @param val 输出的数据* @retval None*/
void SDA_Output( uint16_t val )
{if ( val ){GPIOB->BSRR |= GPIO_PIN_7;}else{GPIOB->BRR |= GPIO_PIN_7;}
}/*** @brief SCL线输出一个位* @param val 输出的数据* @retval None*/
void SCL_Output( uint16_t val )
{if ( val ){GPIOB->BSRR |= GPIO_PIN_6;}else{GPIOB->BRR |= GPIO_PIN_6;}
}/*** @brief SDA输入一位* @param None* @retval GPIO读入一位*/
uint8_t SDA_Input(void)
{if(HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_7) == GPIO_PIN_SET){return 1;}else{return 0;}
}/*** @brief I2C的短暂延时* @param None* @retval None*/
static void delay1(unsigned int n)
{uint32_t i;for ( i = 0; i < n; ++i);
}/*** @brief I2C起始信号* @param None* @retval None*/
void I2CStart(void)
{SDA_Output(1);delay1(DELAY_TIME);SCL_Output(1);delay1(DELAY_TIME);SDA_Output(0);delay1(DELAY_TIME);SCL_Output(0);delay1(DELAY_TIME);
}/*** @brief I2C结束信号* @param None* @retval None*/
void I2CStop(void)
{SCL_Output(0);delay1(DELAY_TIME);SDA_Output(0);delay1(DELAY_TIME);SCL_Output(1);delay1(DELAY_TIME);SDA_Output(1);delay1(DELAY_TIME);}/*** @brief I2C等待确认信号* @param None* @retval None*/
unsigned char I2CWaitAck(void)
{unsigned short cErrTime = 5;SDA_Input_Mode();delay1(DELAY_TIME);SCL_Output(1);delay1(DELAY_TIME);while(SDA_Input()){cErrTime--;delay1(DELAY_TIME);if (0 == cErrTime){SDA_Output_Mode();I2CStop();return ERROR;}}SDA_Output_Mode();SCL_Output(0);delay1(DELAY_TIME);return SUCCESS;
}/*** @brief I2C发送确认信号* @param None* @retval None*/
void I2CSendAck(void)
{SDA_Output(0);delay1(DELAY_TIME);delay1(DELAY_TIME);SCL_Output(1);delay1(DELAY_TIME);SCL_Output(0);delay1(DELAY_TIME);}/*** @brief I2C发送非确认信号* @param None* @retval None*/
void I2CSendNotAck(void)
{SDA_Output(1);delay1(DELAY_TIME);delay1(DELAY_TIME);SCL_Output(1);delay1(DELAY_TIME);SCL_Output(0);delay1(DELAY_TIME);}/*** @brief I2C发送一个字节* @param cSendByte 需要发送的字节* @retval None*/
void I2CSendByte(unsigned char cSendByte)
{unsigned char  i = 8;while (i--){SCL_Output(0);delay1(DELAY_TIME);SDA_Output(cSendByte & 0x80);delay1(DELAY_TIME);cSendByte += cSendByte;delay1(DELAY_TIME);SCL_Output(1);delay1(DELAY_TIME);}SCL_Output(0);delay1(DELAY_TIME);
}/*** @brief I2C接收一个字节* @param None* @retval 接收到的字节*/
unsigned char I2CReceiveByte(void)
{unsigned char i = 8;unsigned char cR_Byte = 0;SDA_Input_Mode();while (i--){cR_Byte += cR_Byte;SCL_Output(0);delay1(DELAY_TIME);delay1(DELAY_TIME);SCL_Output(1);delay1(DELAY_TIME);cR_Byte |=  SDA_Input();}SCL_Output(0);delay1(DELAY_TIME);SDA_Output_Mode();return cR_Byte;
}//
void I2CInit(void)
{GPIO_InitTypeDef GPIO_InitStructure = {0};GPIO_InitStructure.Pin = GPIO_PIN_7 | GPIO_PIN_6;GPIO_InitStructure.Mode = GPIO_MODE_OUTPUT_PP;GPIO_InitStructure.Pull = GPIO_PULLUP;GPIO_InitStructure.Speed = GPIO_SPEED_FREQ_HIGH;HAL_GPIO_Init(GPIOB, &GPIO_InitStructure);
}

i2c_hal.h

#ifndef __I2C_HAL_H
#define __I2C_HAL_H#include "stm32g4xx_hal.h"void I2CStart(void);
void I2CStop(void);
unsigned char I2CWaitAck(void);
void I2CSendAck(void);
void I2CSendNotAck(void);
void I2CSendByte(unsigned char cSendByte);
unsigned char I2CReceiveByte(void);
void I2CInit(void);#endif

相关文章:

速通蓝桥杯嵌入式省一教程:(九)AT24C02芯片(E2PROM存储器)读写操作与I2C协议

AT24C02芯片&#xff08;又叫E2PROM存储器、EEPROM存储器&#xff09;&#xff0c;是一种通过I2C(IIC)协议通信的掉电保存存储器芯片&#xff0c;其内部含有256个8位字节。在介绍这款芯片之前&#xff0c;我们先来粗略了解一下I2C协议。 I2C总线是一种双向二线制的同步串行总线…...

负载均衡:优化性能与可靠性的关键

在现代互联网时代&#xff0c;数以万计的用户访问着各种在线服务&#xff0c;从即时通讯、社交媒体到电子商务和媒体流媒体&#xff0c;无不需要应对海量的请求和数据传输。在这个高并发的环境下&#xff0c;负载均衡成为了关键的技术&#xff0c;它旨在分散工作负载&#xff0…...

T113-S3-TCA6424-gpio扩展芯片调试

目录 前言 一、TCA6424介绍 二、原理图连接 三、设备树配置 四、内核配置 五、gpio操作 总结 前言 TCA6424是一款常用的GPIO&#xff08;通用输入输出&#xff09;扩展芯片&#xff0c;可以扩展微控制器的IO口数量。在T113-S3平台上&#xff0c;使用TCA6424作为GPIO扩展芯…...

奥威BI数据可视化工具:个性化定制,打造独特大屏

每个人都有自己独特的审美&#xff0c;因此即使是做可视化大屏&#xff0c;也有很多人希望做出不一样的报表&#xff0c;用以缓解审美疲劳的同时提高报表浏览效率。因此这也催生出了数据可视化工具的个性化可视化大屏制作需求。 奥威BI数据可视化工具&#xff1a;个性化定制&a…...

13 秒插入 30 万条数据,批量插入!

数据库表 CREATE TABLE t_user (id int(11) NOT NULL AUTO_INCREMENT COMMENT 用户id,username varchar(64) DEFAULT NULL COMMENT 用户名称,age int(4) DEFAULT NULL COMMENT 年龄,PRIMARY KEY (id) ) ENGINEInnoDB DEFAULT CHARSETutf8 COMMENT用户信息表; User实体 /*** …...

Nginx代理转发地址不正确问题

使用ngix前缀去代理转发一个地址&#xff0c;貌似成功了&#xff0c;但是进不到正确的页面&#xff0c;能够访问&#xff0c;但是一直404远处出来nginx会自动拼接地址在后面 后面才知道要将这段代码加上去&#xff0c;去除前缀转发...

HyperMotion高度自动化云迁移至华为HCS8.1解决方案

项目背景 2020 年以来&#xff0c;金融证券已经成为信创落地最快的领域。2021 年证监会发布的《证券期货业科技发展十四五规划》中&#xff0c;将“加强信创规划与实施”作为证券行业重点建设任务之一。为了符合国家信创标准&#xff0c;某证券企业计划将网管系统、呼叫中心管…...

pbootcms系统安全防护设置大全

PbootCMS系统简介 PbootCMS是全新内核且永久开源免费的PHP企业网站开发建设管理系统&#xff0c;是一套高效、简洁、 强悍的可免费商用的PHP CMS源码&#xff0c;能够满足各类企业网站开发建设的需要。系统采用简单到想哭的模板标签&#xff0c;只要懂HTML就可快速开发企业网站…...

【环境】docker时间与宿主同步

1.容器创建后 docker cp /etc/localtime 容器名:/etc/2.容器创建时 加入 -v /ect/localtime/:/etc/localtime:ro参考链接...

亮点!视频云存储/安防监控视频智能分析平台睡岗离岗检测

在生产过程中&#xff0c;未经领导允许的擅自离岗、睡岗会带来很多的潜在危害。TSINGSEE青犀推出的视频云存储/安防监控视频智能分析平台得睡岗离岗检测根据AI视频分析技术建立人工智能算法&#xff0c;对视频画面展开分析与识别。自动识别出人员睡岗、离岗、玩手机与抽烟等动作…...

编程锦囊妙计——快速创建本地Mock服务

点击上方&#x1f446;蓝色“Agilean”&#xff0c;发现更多精彩。 前情提要 在本系列上一篇文章《全文干货&#xff1a;打破前后端数据传递鸿沟&#xff0c;高效联调秘笈》中我们分享了使用Zod这一运行时类型校验库来对后端服务响应结果进行验证达到增加项目质量的方式。 这次…...

简单认识镜像底层原理详解和基于Docker file创建镜像

文章目录 一、镜像底层原理1.联合文件系统(UnionFS)2.镜像加载原理3.为什么Docker里的centos的大小才200M? 二、Dockerfile1.简介2.Dockerfile操作常用命令 三、创建Docker镜像1.基于已有镜像创建2.基于本地模板创建3.基于Dockerfile创建4.Dockerfile多阶段构建镜像 一、镜像底…...

加速乐(__jsl_clearance_s)动态cookie生成分析实战

文章目录 一、写在前面二、抓包分析三、逆向分析 一、写在前面 加速乐&#xff08;JSL&#xff09;是阿里推出的一项反爬虫服务&#xff0c;其生成cookie的原理基于浏览器的行为特征 我们知道普通网站生成cookie是在请求时生成&#xff0c;而它先生成cookie&#xff0c;然后向服…...

启动Vue项目踩坑记录

前言 在启动自己的Vue项目时&#xff0c;遇到一些报错&#xff0c;当时很懵&#xff0c;解决了以后豁然开朗&#xff0c;特写此博客记录一下。 一、<template>里多加了个div标签 [vite] Internal server error: At least one <template> or <script> is req…...

vue-pc上传优化-uni-app上传优化

vue-pc上传优化 当我们使用自己搭建的文档服务器上传图片时候&#xff0c;在本地没问题&#xff0c;上线上传会比较慢 这时候我们最简单的方法就是写一个加载组件&#xff0c;上传之前打开组件&#xff0c;掉完接口关闭组件 或者不想写直接使用element的loading写一个遮罩层加…...

【计算机视觉|生成对抗】StackGAN:使用堆叠生成对抗网络进行文本到照片逼真图像合成

本系列博文为深度学习/计算机视觉论文笔记&#xff0c;转载请注明出处 标题&#xff1a;StackGAN: Text to Photo-realistic Image Synthesis with Stacked Generative Adversarial Networks 链接&#xff1a;[1612.03242] StackGAN: Text to Photo-realistic Image Synthesis…...

跟随角色镜头时,解决地图黑线/白线缝隙的三种方案

下面一共三个解决方案&#xff0c;这里我推荐第二个方案解决&#xff0c;因为够快速和简单。 现象&#xff1a; 解决方案一&#xff1a; 参考【Unity2D】去除地图中的黑线_unity选中后有线_香菇CST的博客-CSDN博客&#xff0c;博主解释是因为抗锯齿采样导致的问题。 具体到这…...

redis7高级篇2 redis的BigKey的处理

一 Bigkey的处理 1.1 模拟造数 1.截图 2.代码 &#xff1a;使用pipe 批量插入10w的数据量 cat /root/export/monidata.txt | redis-cli -h 127.0.0.1 -a 123456 -p 6379 --pipe [rootlocalhost export]# for((i1;i<10*10;i)); do echo "set k$i v$i" >>…...

启英泰伦通话降噪方案,采用深度学习降噪算法,让通话更清晰

生活中的通话应用场景无处不在&#xff0c;如电话、对讲机、远程会议、在线教育等。普遍存在的问题是环境噪音、干扰声导致通话声音不清晰&#xff0c;语音失真等。 为了解决这一问题&#xff0c;启英泰伦基于自适应线性滤波联合非线性滤波的回声消除方案和基于深度学习的降噪…...

将SonarLint集成到Git

1、搭建SonarQube服务器 下载SonarQube安装包 访问SonarQube官网&#xff08;https://www.sonarqube.org/downloads/&#xff09;下载最新版本的SonarQube Community Edition。解压安装包 将下载的压缩包解压到一个目录&#xff0c;例如&#xff1a;D:\sonarqube-community-7.…...

网络六边形受到攻击

大家读完觉得有帮助记得关注和点赞&#xff01;&#xff01;&#xff01; 抽象 现代智能交通系统 &#xff08;ITS&#xff09; 的一个关键要求是能够以安全、可靠和匿名的方式从互联车辆和移动设备收集地理参考数据。Nexagon 协议建立在 IETF 定位器/ID 分离协议 &#xff08;…...

Android Wi-Fi 连接失败日志分析

1. Android wifi 关键日志总结 (1) Wi-Fi 断开 (CTRL-EVENT-DISCONNECTED reason3) 日志相关部分&#xff1a; 06-05 10:48:40.987 943 943 I wpa_supplicant: wlan0: CTRL-EVENT-DISCONNECTED bssid44:9b:c1:57:a8:90 reason3 locally_generated1解析&#xff1a; CTR…...

iOS 26 携众系统重磅更新,但“苹果智能”仍与国行无缘

美国西海岸的夏天&#xff0c;再次被苹果点燃。一年一度的全球开发者大会 WWDC25 如期而至&#xff0c;这不仅是开发者的盛宴&#xff0c;更是全球数亿苹果用户翘首以盼的科技春晚。今年&#xff0c;苹果依旧为我们带来了全家桶式的系统更新&#xff0c;包括 iOS 26、iPadOS 26…...

《Qt C++ 与 OpenCV:解锁视频播放程序设计的奥秘》

引言:探索视频播放程序设计之旅 在当今数字化时代,多媒体应用已渗透到我们生活的方方面面,从日常的视频娱乐到专业的视频监控、视频会议系统,视频播放程序作为多媒体应用的核心组成部分,扮演着至关重要的角色。无论是在个人电脑、移动设备还是智能电视等平台上,用户都期望…...

关于iview组件中使用 table , 绑定序号分页后序号从1开始的解决方案

问题描述&#xff1a;iview使用table 中type: "index",分页之后 &#xff0c;索引还是从1开始&#xff0c;试过绑定后台返回数据的id, 这种方法可行&#xff0c;就是后台返回数据的每个页面id都不完全是按照从1开始的升序&#xff0c;因此百度了下&#xff0c;找到了…...

【解密LSTM、GRU如何解决传统RNN梯度消失问题】

解密LSTM与GRU&#xff1a;如何让RNN变得更聪明&#xff1f; 在深度学习的世界里&#xff0c;循环神经网络&#xff08;RNN&#xff09;以其卓越的序列数据处理能力广泛应用于自然语言处理、时间序列预测等领域。然而&#xff0c;传统RNN存在的一个严重问题——梯度消失&#…...

Linux简单的操作

ls ls 查看当前目录 ll 查看详细内容 ls -a 查看所有的内容 ls --help 查看方法文档 pwd pwd 查看当前路径 cd cd 转路径 cd .. 转上一级路径 cd 名 转换路径 …...

【python异步多线程】异步多线程爬虫代码示例

claude生成的python多线程、异步代码示例&#xff0c;模拟20个网页的爬取&#xff0c;每个网页假设要0.5-2秒完成。 代码 Python多线程爬虫教程 核心概念 多线程&#xff1a;允许程序同时执行多个任务&#xff0c;提高IO密集型任务&#xff08;如网络请求&#xff09;的效率…...

Java入门学习详细版(一)

大家好&#xff0c;Java 学习是一个系统学习的过程&#xff0c;核心原则就是“理论 实践 坚持”&#xff0c;并且需循序渐进&#xff0c;不可过于着急&#xff0c;本篇文章推出的这份详细入门学习资料将带大家从零基础开始&#xff0c;逐步掌握 Java 的核心概念和编程技能。 …...

06 Deep learning神经网络编程基础 激活函数 --吴恩达

深度学习激活函数详解 一、核心作用 引入非线性:使神经网络可学习复杂模式控制输出范围:如Sigmoid将输出限制在(0,1)梯度传递:影响反向传播的稳定性二、常见类型及数学表达 Sigmoid σ ( x ) = 1 1 +...