STM32G030C8T6:EEPROM读写实验(I2C通信)--M24C64

本专栏记录STM32开发各个功能的详细过程,方便自己后续查看,当然也供正在入门STM32单片机的兄弟们参考;

本小节的目标是,系统主频64 MHZ,采用高速外部晶振,实现PB11,PB10 引脚模拟I2C 时序,对M24C08 的EEPROM 进行读。
原理:通过模拟I2C接(PB10:CLK,PB11:DTA)与M24C08 EEPROM进行读写实验。
涉及到的知识:配置I2C通信,STM32CubeMX的使用

文章目录

  • 1 新建工程
  • 2 配置SWD下载引脚
  • 3 配置RCC
  • 4 设置系统主频
  • 5 生成工程
  • 6 增加代码实现PB10,PB11 模拟I2C 时序,从而实现EEPROM数据读写

1 新建工程

点击File 菜单下的New Project

在这里插入图片描述

选择芯片型号,如下图所示先输入芯片型号,目前这边输入STM32G030C8,

在这里插入图片描述

双击选择,就确定了芯片型号,界面会变成如下图所示

在这里插入图片描述

2 配置SWD下载引脚

如下图所示,在Pinout&Configuration 栏目的System Core 下,先点击SYS,再勾选Serial Wire 框,
配置好SWD 下载引脚设置:

在这里插入图片描述

3 配置RCC

如下图,先点击RCC,在HSE 配置中选择Crystal/Ceramic Resonator 外部晶振设

在这里插入图片描述

4 设置系统主频

如下图, 先点击Clock Configuration 栏目,按下图的1,2,3,4 步骤完成系统64MHZ 主频设置:

在这里插入图片描述

5 生成工程

按照下图的步骤,进行项目配置,项目名称和路径设置等,生成项目的类型选择STM32CubeIDE(我这里以STM32CubeIDE为例,如果你要试用keil5,那就选择MDK-RAM,如果要使用makefile,就选择Makefile),注意项目名称和路径不要有中文名;
在这里插入图片描述

最后全部设置完毕后点击create code,生成项目代码:

在这里插入图片描述

生成的工程如下图所示:
在这里插入图片描述

6 增加代码实现PB10,PB11 模拟I2C 时序,从而实现EEPROM数据读写

如下图所示,在Core/Src下面增加24C64.c 文件,里面是用I/O 口模拟I2C 总线实现EEPROM读写驱动。

在这里插入图片描述

24C64.h:
#ifndef M24C64_H
#define M24C64_H#include "main.h"#define EE_ADDR 0xA0 // EEPROM地址,地址管脚全接地,为0xA0
#define EE_SCL_PIN  GPIO_PIN_10   //模拟IIC的SCL信号
#define EE_SDA_PIN  GPIO_PIN_11   //模拟IIC的SDA信号
#define EE_IIC_SCL(val)         HAL_GPIO_WritePin(GPIOB, GPIO_PIN_10,val)                    //SCL 输出高或者低
#define EE_IIC_SDA(val)         HAL_GPIO_WritePin(GPIOB, GPIO_PIN_11,val)                    //SDA 输出高或者低void EE_SDA_IN(void);  // PB11配置成输入
void EE_SDA_OUT(void); // PB11配置成开漏输出
void EE_SCK_OUT(void); // PB10配置成开漏输出
unsigned char EE_READ_SDA(void); // 读DATA引脚状态void EE_IIC_Delay(uint16_t us); // IIC延时
void EE_IIC_Init(void); // IIC初始化
void EE_IIC_Start(void); // 开始
void EE_IIC_Stop(void); // 停止
uint8_t EE_IIC_WaitAck(void); // 等待应答
void EE_IIC_Ack(void); // 发送应答
void EE_IIC_NAck(void); // 发送非应答
void EE_IIC_SendByte(uint8_t data); // 发送一个字节
uint8_t EE_IIC_ReadByte(uint8_t ack); // 读取1字节// M24C64特定函数
uint8_t EE_IIC_ReadByteFromSlave(uint8_t I2C_Addr, uint16_t reg, uint8_t *buf);
uint8_t EE_IIC_SendByteToSlave(uint8_t I2C_Addr, uint16_t reg, uint8_t data);#endif // M24C64_H/*********************************************************************************************************END FILE
*********************************************************************************************************/
24C08.c
#include "24C64.h"void EE_SDA_IN(void) 	// PB11配置成输入
{__HAL_RCC_GPIOB_CLK_ENABLE(); // GPIO时钟使能GPIO_InitTypeDef GPIO_InitStruct = {0};GPIO_InitStruct.Pin = GPIO_PIN_11;GPIO_InitStruct.Mode = GPIO_MODE_INPUT;GPIO_InitStruct.Pull = GPIO_NOPULL;GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
}void EE_SDA_OUT(void) // PB11配置成开漏输出
{__HAL_RCC_GPIOB_CLK_ENABLE(); // GPIO时钟使能GPIO_InitTypeDef GPIO_InitStruct = {0};GPIO_InitStruct.Pin = GPIO_PIN_11;GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_OD;GPIO_InitStruct.Pull = GPIO_NOPULL;GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
}void EE_SCK_OUT(void) // PB10配置成开漏输出
{__HAL_RCC_GPIOB_CLK_ENABLE(); // GPIO时钟使能GPIO_InitTypeDef GPIO_InitStruct = {0};GPIO_InitStruct.Pin = GPIO_PIN_10;GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_OD;GPIO_InitStruct.Pull = GPIO_NOPULL;GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
}// 读DATA引脚状态
unsigned char EE_READ_SDA(void)
{return HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_11);
}// IIC延时
void EE_IIC_Delay(uint16_t us)
{uint16_t j;for (j = 0; j < us; j++){for (int i = 0; i < 20; i++){__asm("NOP"); // 等待1个指令周期,系统主频16M}}
}// IIC初始化
void EE_IIC_Init(void)
{EE_SCK_OUT(); // CLK引脚配置成输出EE_SDA_OUT(); // DATA引脚配置成输出EE_IIC_SCL(1); // CLK引脚输出高EE_IIC_SDA(1); // DATA引脚输出高
}// 开始
void EE_IIC_Start(void)
{EE_SDA_OUT(); // DATA引脚配置成输出EE_IIC_SDA(1); // DATA引脚输出高EE_IIC_SCL(1); // CLK引脚输出高EE_IIC_Delay(4); // 等待大约40usEE_IIC_SDA(0); // DATA引脚输出低EE_IIC_Delay(4); // 等待大约40usEE_IIC_SCL(0); // CLK引脚输出低,钳住I2C总线,准备发送或接收数据
}// 停止
void EE_IIC_Stop(void)
{EE_SDA_OUT(); // DATA引脚配置成输出EE_IIC_SCL(0); // CLK引脚输出低EE_IIC_SDA(0); // DATA引脚输出低EE_IIC_Delay(4); // 等待大约40usEE_IIC_SCL(1); // CLK引脚输出高EE_IIC_SDA(1); // DATA引脚输出高,发送I2C总线结束信号EE_IIC_Delay(4); // 等待大约40us
}// 等待应答
uint8_t EE_IIC_WaitAck(void)
{uint8_t ucErrTime = 0;EE_SDA_IN(); // DATA引脚配置成输入(从机给一个低电平做为应答)EE_IIC_SDA(1);EE_IIC_Delay(1);EE_IIC_SCL(1);EE_IIC_Delay(1); // 等待约10uswhile (EE_READ_SDA()) // 一直读,直到读取到低电平应答{ucErrTime++;if (ucErrTime > 250){EE_IIC_Stop();return 1;}}EE_IIC_SCL(0); // 时钟输出0return 0;
}// 发送应答
void EE_IIC_Ack(void)
{EE_IIC_SCL(0);EE_SDA_OUT();EE_IIC_SDA(0);EE_IIC_Delay(1);EE_IIC_SCL(1);EE_IIC_Delay(2);EE_IIC_SCL(0);
}// 发送非应答
void EE_IIC_NAck(void)
{EE_IIC_SCL(0);EE_SDA_OUT();EE_IIC_SDA(1);EE_IIC_Delay(1);EE_IIC_SCL(1);EE_IIC_Delay(1);EE_IIC_SCL(0);
}// 发送一个字节
void EE_IIC_SendByte(uint8_t data)
{uint8_t t;EE_SDA_OUT();EE_IIC_SCL(0); // 拉低时钟开始数据传输for (t = 0; t < 8; t++){EE_IIC_SDA((data & 0x80) >> 7); // 发送数据EE_IIC_Delay(1);EE_IIC_SCL(1);data <<= 1;EE_IIC_Delay(1);EE_IIC_SCL(0);}EE_IIC_Delay(1);
}// 读取1字节
uint8_t EE_IIC_ReadByte(uint8_t ack)
{uint8_t i, receive = 0;EE_SDA_IN(); // SDA设置为输入模式 等待接收从机返回数据for (i = 0; i < 8; i++){EE_IIC_SCL(0);EE_IIC_Delay(1);EE_IIC_SCL(1);receive <<= 1;if (EE_READ_SDA()) receive++; // 读取从机发送的电平,如果是高,就记录高EE_IIC_Delay(1);}if (ack)EE_IIC_Ack(); // 发送ACKelseEE_IIC_NAck(); // 发送nACKreturn receive;
}// 从EE指定地址读取一个字节
uint8_t EE_IIC_ReadByteFromSlave(uint8_t I2C_Addr, uint16_t reg, uint8_t *buf)
{EE_IIC_Start();EE_IIC_SendByte(I2C_Addr); // 发送从机地址if (EE_IIC_WaitAck()) // 如果从机未应答则数据发送失败{EE_IIC_Stop();return 1;}EE_IIC_SendByte((reg >> 8) & 0xFF); // 发送寄存器高位地址EE_IIC_WaitAck();EE_IIC_SendByte(reg & 0xFF); // 发送寄存器低位地址EE_IIC_WaitAck();EE_IIC_Start();EE_IIC_SendByte(I2C_Addr + 1); // 进入接收模式EE_IIC_WaitAck();*buf = EE_IIC_ReadByte(0);EE_IIC_Stop(); // 产生一个停止条件return 0;
}// 发送一个字节内容到EE指定地址
uint8_t EE_IIC_SendByteToSlave(uint8_t I2C_Addr, uint16_t reg, uint8_t data)
{EE_IIC_Start();EE_IIC_SendByte(I2C_Addr); // 发送从机地址if (EE_IIC_WaitAck()){EE_IIC_Stop();return 1; // 从机地址写入失败}EE_IIC_SendByte((reg >> 8) & 0xFF); // 发送寄存器高位地址EE_IIC_WaitAck();EE_IIC_SendByte(reg & 0xFF); // 发送寄存器低位地址EE_IIC_WaitAck();EE_IIC_SendByte(data);if (EE_IIC_WaitAck()){EE_IIC_Stop();return 1; // 数据写入失败}EE_IIC_Stop(); // 产生一个停止条件return 0;
}

然后如下图所示,24C08.c 文件,主要是把SCL 引脚改成PB10,SDA引脚改成PB11,还有EEPROM 驱动所需的基本I/O 操作函数,还有实现时序所需的等待函数。

然后打开main.c文件,按照下图操作添加代码:

在这里插入图片描述

extern void EE_IIC_Init(void);
extern uint8_t EE_EE_IIC_SendByteToSlave(uint8_t I2C_Addr,uint8_t reg,uint8_t data);
extern uint8_t EE_IIC_ReadByteFromSlave(uint8_t I2C_Addr,uint8_t reg,uint8_t *buf);
unsigned char EEDATA;//存放EEPROM读取出来的数据,1个字节

在这里插入图片描述

  HAL_GPIO_WritePin(GPIOB, GPIO_PIN_12, GPIO_PIN_RESET);#if 1//M24C64代码// 测试写入和读取EEPROMEE_IIC_Init();uint16_t test_addr = 0x0000;uint8_t test_data = 0x55;uint8_t read_data = 0;// 写入测试数据EE_IIC_SendByteToSlave(EE_ADDR, test_addr, test_data);HAL_Delay(10); // 确保写入完成// 读取测试数据EE_IIC_ReadByteFromSlave(EE_ADDR, test_addr, &read_data);
#endif/* USER CODE END 2 *//* Infinite loop *//* USER CODE BEGIN WHILE */while (1){/* USER CODE END WHILE */// 循环检测读取的数据if (read_data == test_data) {// 成功读取while(1){HAL_GPIO_TogglePin(GPIOB,GPIO_PIN_9);HAL_Delay(50);}} else {// 读取失败while(1){HAL_GPIO_TogglePin(GPIOB,GPIO_PIN_9);HAL_Delay(500);}}/* USER CODE BEGIN 3 */}/* USER CODE END 3 */
}

然后编译,调试,查看EEDATA变量的值,结果如下图所示:

在这里插入图片描述
我们写进去的值时0x55,最后读出来的值是85'U',查一下ASCII码:

在这里插入图片描述
0x55对应的就是85'U',证明我们的结果是对的,证明此EEPROM读写实验成功。

此时单片机上PB9对应的小灯在以50hz的频率在闪烁。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/diannao/20225.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

javascript DOM 设置样式

No.内容链接1Openlayers 【入门教程】 - 【源代码示例300】 2Leaflet 【入门教程】 - 【源代码图文示例 150】 3Cesium 【入门教程】 - 【源代码图文示例200】 4MapboxGL【入门教程】 - 【源代码图文示例150】 5前端就业宝典 【面试题详细答案 1000】 文章目录 一、直接…

【易错题】数据统计补充习题(选择题 )#CDA Level 1

本文整理了数据统计相关的易错题&#xff0c;部分可作为备考CDA Level 1统计学部分的补充习题。来源&#xff1a;如荷学题库&#xff08;CFDP第三部分&#xff09; 1&#xff09; 2&#xff09; 3&#xff09; 4&#xff09; 5&#xff09; 6&#xff09; 7&#xff09; 8&…

shopee签名x-sap-ri、x-sap-sec算法还原

最新版签名&#xff0c;免账号登录成功率百分百&#xff0c;需要可d 两种方式base64 MTQzMDY0OTc3OA QXVndXN0MjItZnF4

【项目经理】什么是领导

引言&#xff1a;        项目管理是一个不断发展的领域&#xff0c;它要求项目经理不断学习、适应和创新。通过本系列文章&#xff0c;我们希望能够帮助每一位项目经理提升自己的专业能力&#xff0c;成为引领项目成功的舵手。 持续更新。。。。。。。。。。。。。。。…

Blueprints - Collision Presets相关

一些以前的学习笔记归档&#xff1b; 在Static Mesh或SkeletalMesh等的属性中&#xff0c;都有Collision Presets&#xff1a; 其中Oject Type只是一个枚举参数&#xff0c;代表设置该Actor为什么类型&#xff0c;Collision Responses代表该Actor对各种类型的Actor有什么反应&a…

DataGrip 数据库连接客户端

I DataGrip 安装 1.1安装 打开dmg镜像&#xff0c;将“DataGrip.app”拖入应用程序中&#xff1b; 1.2 Act 打开应用程序,点击试用模式启动软件&#xff0c;然后将“jetbrains-agent-latest”拖到任意位置&#xff0c;然后拖入&#xff0c;弹出对话框&#xff0c;点击“Rest…

Java web应用性能分析之【压测工具ab】

常用的性能测试工具有&#xff1a;JMeter、loadRunner、ab&#xff1b;对于开发人员来说用的多的是免费的Jmeter和ab&#xff0c;对于测试来说可能用收费的商业软件loadRunner多。在这里我们就说说ab压测工具&#xff0c;因为ab基本满足web接口测试要求&#xff0c;jmeter后面再…

LabVIEW车体静强度试验台测控系统

LabVIEW车体静强度试验台测控系统 开发了一种基于LabVIEW的车体静强度试验台测控系统&#xff0c;通过自动化技术提高试验的精度和效率。系统采用LabVIEW软件与S7-200 SMART PLC硬件平台相结合&#xff0c;实现了对液压缸作用力的精确控制和试验数据的实时采集及管理。 传统的…

通用代码生成器应用场景四,跨编程语言翻译

通用代码生成器应用场景四&#xff0c;跨编程语言翻译 如果您有一个Java工程&#xff0c;想把它移植到Rust或Golang语言中去&#xff0c;希望尽可能加快研发速度。 如果您的系统是通用代码生成器开发的&#xff0c;保留了系统的SGS源文件或者SGS2的Excel模板&#xff0c;您可…

结构体 基础知识

本笔记为观看64 结构体-结构体定义和使用_哔哩哔哩_bilibili 的学习笔记 1.结构体概念 结构体属于用户自定义的数据类型&#xff0c;允许用户存储不同的数据类型。 2.结构体定义和使用 ​ 结构体定义 ​ 通过结构体创建变量的方式 2.1 Struct 结构体名 变量名 ​ 2…

【算法】MT2 棋子翻转

✨题目链接&#xff1a; MT2 棋子翻转 ✨题目描述 在 4x4 的棋盘上摆满了黑白棋子&#xff0c;黑白两色棋子的位置和数目随机&#xff0c;其中0代表白色&#xff0c;1代表黑色&#xff1b;左上角坐标为 (1,1) &#xff0c;右下角坐标为 (4,4) 。 现在依次有一些翻转操作&#…

“迎七一、学党史、祭英烈”活动在孙善师孙善帅烈士故居启动

临沂信息联播讯&#xff08;张春兄、冯爱云&#xff09; 5月30日&#xff0c;山东省著名烈士孙善师孙善帅故居迎来了山东全味时间企业管理咨询服务有限公司、志林丽虹沂蒙文化传播&#xff08;临沂&#xff09;有限公司、山东志林搏击健身有限公司的参观团队&#xff0c;标志着…

【WEEK14】 【DAY4】Swagger第二部分【中文版】

2024.5.30 Thursday 接上文【WEEK14】 【DAY3】Swagger第一部分【中文版】 目录 16.4.配置扫描接口16.4.1.修改SwaggerConfig.java16.4.1.1.使用.basePackage()方法指定扫描的包路径16.4.1.2.其他扫描方式均可在RequestHandlerSelectors.class中查看源码 16.4.2.仍然是修改Swag…

这个夏天,凶险如昨?

回望2023年三季度的“美债风暴”&#xff0c;当时美债收益率狂飙突破5%&#xff0c;阴霾笼罩下全球风险资产一片惨淡&#xff0c;这一场景会在今夏再度上演吗&#xff1f; 本周美债遭遇抛售&#xff0c;10年期收益率上破4.6%&#xff0c;2年期收益率逼近5%关口&#xff0c;收益…

linux磁盘满了,如何查找大文件清除?

将整个Linux中文件按照文件大小排序&#xff0c;从大到小排序 只显示前100条数据 命令&#xff1a; find / -type f -exec du -h {} | sort -rh | head -n 100结果&#xff1a;

全栈工程师需要具备哪些技能?

概论&#xff1a; 全栈工程师是一位能够从头到尾构建 Web 应用程序的工程师&#xff0c;能独立完成产品。技术包括前端部分、后端部分和应用程序所在的基础架构。他们在整个技术栈中工作&#xff0c;并了解其中的每个部分。从需求分析开始&#xff0c;到概要设计&#xff0c;详…

HarmonyOS鸿蒙学习笔记(25)相对布局 RelativeContainer详细说明

RelativeContainer 简介 前言核心概念官方实例官方实例改造蓝色方块改造center 属性说明参考资料 前言 RelativeContainer是鸿蒙的相对布局组件&#xff0c;它的布局很灵活&#xff0c;可以很方便的控制各个子UI 组件的相对位置&#xff0c;其布局理念有点类似于android的约束…

270 基于matlab的模糊自适应PID控制

基于matlab的模糊自适应PID控制&#xff0c;具有10页报告。传统PID在对象变化时&#xff0c;控制器的参数难以自动调整。将模糊控制与PID控制结合&#xff0c;利用模糊推理方法实现对PID参数的在线自整定。使控制器具有较好的自适应性。使用MATLAB对系统进行仿真&#xff0c;结…

win10系统下WPS工具显示灰色全部用不了,提示登录

如果你在写文档或使用excel时发现导航栏的工具全部使用不了&#xff0c;弹出是需要您登录&#xff0c;可以通过以下操作不用登录。 按照 1&#xff08;搜索框&#xff09;—> 2&#xff08;应用&#xff09;—> 3&#xff08;WPS Office&#xff09;点鼠标左键—> 4&a…

大规模 Transformer 模型 8 比特矩阵乘

本文基于 Hugging Face Transformers、Accelerate 以及 bitsandbytes库。 Transformers&#xff1a;Hugging Face 提供的一个开源库&#xff0c;包含了多种预训练的 Transformer 模型&#xff0c;方便用户进行各种 NLP 任务。Accelerate&#xff1a;Hugging Face 开发的一个库…