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

2024-06-01 11:12

本文主要是介绍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的频率在闪烁。

这篇关于STM32G030C8T6:EEPROM读写实验(I2C通信)--M24C64的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



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

相关文章

Python实现高效地读写大型文件

《Python实现高效地读写大型文件》Python如何读写的是大型文件,有没有什么方法来提高效率呢,这篇文章就来和大家聊聊如何在Python中高效地读写大型文件,需要的可以了解下... 目录一、逐行读取大型文件二、分块读取大型文件三、使用 mmap 模块进行内存映射文件操作(适用于大文件)四、使用 pand

C# 读写ini文件操作实现

《C#读写ini文件操作实现》本文主要介绍了C#读写ini文件操作实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧... 目录一、INI文件结构二、读取INI文件中的数据在C#应用程序中,常将INI文件作为配置文件,用于存储应用程序的

C#实现文件读写到SQLite数据库

《C#实现文件读写到SQLite数据库》这篇文章主要为大家详细介绍了使用C#将文件读写到SQLite数据库的几种方法,文中的示例代码讲解详细,感兴趣的小伙伴可以参考一下... 目录1. 使用 BLOB 存储文件2. 存储文件路径3. 分块存储文件《文件读写到SQLite数据库China编程的方法》博客中,介绍了文

10. 文件的读写

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

系统架构师考试学习笔记第三篇——架构设计高级知识(20)通信系统架构设计理论与实践

本章知识考点:         第20课时主要学习通信系统架构设计的理论和工作中的实践。根据新版考试大纲,本课时知识点会涉及案例分析题(25分),而在历年考试中,案例题对该部分内容的考查并不多,虽在综合知识选择题目中经常考查,但分值也不高。本课时内容侧重于对知识点的记忆和理解,按照以往的出题规律,通信系统架构设计基础知识点多来源于教材内的基础网络设备、网络架构和教材外最新时事热点技术。本课时知识

【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

vue2 组件通信

props + emits props:用于接收父组件传递给子组件的数据。可以定义期望从父组件接收的数据结构和类型。‘子组件不可更改该数据’emits:用于定义组件可以向父组件发出的事件。这允许父组件监听子组件的事件并作出响应。(比如数据更新) props检查属性 属性名类型描述默认值typeFunction指定 prop 应该是什么类型,如 String, Number, Boolean,

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

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

linux中使用rust语言在不同进程之间通信

第一种:使用mmap映射相同文件 fn main() {let pid = std::process::id();println!(

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

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