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

news2024/11/18 11:42:35

本专栏记录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); // 等待大约40us
 	EE_IIC_SDA(0); // DATA引脚输出低
	EE_IIC_Delay(4); // 等待大约40us
	EE_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); // 等待大约40us
	EE_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); // 等待约10us
	while (EE_READ_SDA()) // 一直读,直到读取到低电平应答
	{
		ucErrTime++;
		if (ucErrTime > 250)
		{
			EE_IIC_Stop();
			return 1;
		}
	}
	EE_IIC_SCL(0); // 时钟输出0
	return 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(); // 发送ACK
	else
		EE_IIC_NAck(); // 发送nACK
	return 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代码
  // 测试写入和读取EEPROM
	EE_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.coloradmin.cn/o/1721117.html

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

相关文章

低代码开发平台(Low-code Development Platform)的模块组成部分

低代码开发平台&#xff08;Low-code Development Platform&#xff09;的模块组成部分主要包括以下几个方面&#xff1a; 低代码开发平台的模块组成部分可以按照包含系统、模块、菜单组织操作行为等维度进行详细阐述。以下是从这些方面对平台模块组成部分的说明&#xff1a; …

翻译《The Old New Thing》- What a drag: Dragging a virtual file (HGLOBAL edition)

What a drag: Dragging a virtual file (HGLOBAL edition) - The Old New Thing (microsoft.com)https://devblogs.microsoft.com/oldnewthing/20080318-00/?p23083 Raymond Chen 2008年03月18日 拖拽虚拟文件&#xff08;HGLOBAL 版本&#xff09; 现在我们已经对简单的数据…

MySQL之创建高性能的索引(七)

创建高性能的索引 InnoDB和MyISAM的数据分布对比 聚簇索引和非聚簇索引欸度数据分布有区别&#xff0c;以及对应的主键索引和二级索引的数据分布也有区别&#xff0c;通常会让人感到困扰和意外。来看看InnoDB和MyISAM时如何存储下面这个表的: CREATE TABLE layout_test ( co…

R语言ggplot2包绘制世界地图

数据和代码获取&#xff1a;请查看主页个人信息&#xff01;&#xff01;&#xff01; 1. 数据读取与处理 首先&#xff0c;从CSV文件中读取数据&#xff0c;并计算各国每日收入的平均签证成本。 library(tidyverse) ​ df <- read_csv("df.csv") %>% group_…

自监督表示学习和神经音频合成实现语音修复

关键词&#xff1a;语音修复、自监督模型、语音合成、语音增强、神经声码器 语音和/或音频修复的目标是增强局部受损的语音和/或音频信号。早期的工作基于信号处理技术&#xff0c;例如线性预测编码、正弦波建模或图模型。最近&#xff0c;语音/音频修复开始使用深度神经网络&a…

用万界星空科技低代码平台能快速搭建一个云MES系统

一、低代码平台与MES:智能制造的新篇章 随着工业4.0和智能制造的兴起&#xff0c;企业对于生产过程的数字化、智能化需求日益迫切。传统的MES系统实施周期长、成本高&#xff0c;成为许多企业数字化转型的瓶颈。而低代码开发平台的出现为这一问题提供了新的解决思路。 二、万界…

Linux常用环境Docker安装

一、mysql安装 简单安装 docker run -d \--name mysql \-p 3306:3306 \-e TZAsia/Shanghai \-e MYSQL_ROOT_PASSWORD123 \mysql mysql容器本地挂载 cd /usr mkdir mysql cd mysql/ mkdir data mkdir conf mkdir init可以手动导入自己的数据库信息 docker run -d \--name mys…

MES系统的功能、架构及应用价值

MES系统生产过程控制的主要方面涵盖了生产计划与控制、生产调度与排程、数据采集与监控、质量控制与管理、物料管理与控制以及设备管理与维护等多个方面。这些功能共同构成了MES系统的核心价值&#xff0c;帮助企业实现生产过程的数字化、智能化和精细化管理。 一、工厂使用MES…

Opera 浏览器与Google联手,推出由Gemini驱动的全新AI功能

每周跟踪AI热点新闻动向和震撼发展 想要探索生成式人工智能的前沿进展吗&#xff1f;订阅我们的简报&#xff0c;深入解析最新的技术突破、实际应用案例和未来的趋势。与全球数同行一同&#xff0c;从行业内部的深度分析和实用指南中受益。不要错过这个机会&#xff0c;成为AI领…

JavaScript 动态网页实例 —— 窗口控制

除了打开和关闭窗口之外,还有很多其他控制窗口的方法。例如,可以使用 window.focus()方法使窗口获得焦点,也可以利用与其相对的window.blur 方法使窗口失去焦点。本节介绍移动窗口、改变窗口大小、窗口滚动、窗口超时操作、常用窗口事件、常用窗口扩展等窗口控制的方法和手段。…

AI办公自动化:用通义千问Qwen-Long批量总结PDF长文档内容

Qwen-Long是在通义千问针对超长上下文处理场景的大语言模型&#xff0c;支持中文、英文等不同语言输入&#xff0c;支持最长1000万tokens(约1500万字或1.5万页文档)的超长上下文对话。配合同步上线的文档服务&#xff0c;可支持word、pdf、markdown、epub、mobi等多种文档格式的…

HCIP-Datacom-ARST自选题库__BGP/MPLS IP VPN判断【10道题】

1.部署BGP/MPLSIP VPN时,当两个VPN有共同的站点,则该共同站点一定不能与两个VPN其他站点使用重叠的地址空间。 2.如图所示&#xff0c;运营商BGP/MPLSIP VPN骨干网通过LDP构建LSP&#xff0c;若想实现用户X两个站点之间通过BGP/MPLSIP VPN网络互通&#xff0c;则PE1和PE2之间必…

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

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

【STM32】定时器与PWM的LED控制

目录 一、定时器控制LED周期性亮灭&#xff08;一&#xff09;定时器1.STM32F103定时器分类及区别2.通用定时器主要功能3.通用定时器工作过程 &#xff08;二&#xff09;STM32CubeMX创建工程&#xff08;三&#xff09;代码实现&#xff08;四&#xff09;实验结果 二、PWM模式…

7.1 Go 错误的概念

&#x1f49d;&#x1f49d;&#x1f49d;欢迎莅临我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:「stormsha的主页」…

富士摄像机X-H2S MOV格式化后重新写入后的恢复方法

X-H2S是富士数码的一款旗舰机型&#xff0c;支持4K/6K高清&#xff0c;视频编码为最新的HVC。下面我们来看下富士数码摄像机恢复案例。 故障存储:512G存储卡 Exfat文件系统 故障现象: 512G的卡误格式化后又进行了拍摄&#xff0c;卡使用了120G不到的空间&#xff0c;其它底…

深入分析 Android Service (五)

文章目录 深入分析 Android Service (五)1. 深入分析 Service 与 Activity 之间的通信2. Messenger 的内部工作原理2.1 服务端实现2.2 客户端实现 3. AIDL 的内部工作原理3.1 定义 AIDL 接口3.2 服务端实现3.3 客户端实现 4. Service 的优化建议和最佳实践4.1 异步操作4.2 资源…

Vite项目构建chrome extension,实现多入口

本项目使用Vite5 Vue3进行构建。 要使用vite工程构建浏览器插件&#xff0c;无非就是要实现popup页面和options页面。这就需要在项目中用到多入口打包&#xff08;生成多个html文件&#xff09;。 实现思路&#xff1a; 通过配置vite工程&#xff0c;使得项目打包后有两个h…

每天学点小知识:WSL安装Ubuntu 22.04 LTS

前言 本章教会你在不使用虚拟机下使用linux&#xff0c;但是这里建议还是使用虚拟机&#xff0c;或者装一双系统&#xff0c;wsl使用linux还是有很多问题的。 1. 简介WSL WSL&#xff08;Windows Subsystem for Linux&#xff09;是微软为Windows 10及以上版本开发的一项功能…

如何在IDEA中实现类似Linux命令那样的外部传参

【背景说明】 IDEA中执行一个程序时&#xff0c;如何就在程序一开始执行给传入你给的参数呢&#xff1f; 【说明】 public static void main(String[] args) throws Exception {} 说明&#xff1a;其实java中main方法里的args这个参数&#xff0c;就是用于接收外部传参的。…