STM32G030C8T6:EEPROM读写实验(I2C通信)
本专栏记录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下面增加24C08.c 文件,里面是用I/O 口模拟I2C 总线实现EEPROM读写驱动。

24C08.h:
/*
**------------------------------------------------------------------------------------------------------
** Modified by:
** Modified date:
** Version:
** Descriptions:
********************************************************************************************************/
#ifndef __24C02_H
#define __24C02_H/* Includes ------------------------------------------------------------------*//* Private define ------------------------------------------------------------*/
#define AT24C01A /* 24C01A,I2C时序和往后的24C02一样 */
//#define AT24C01 /* 24C01,I2C的时序和普通的有点不同 */#define ADDR_24LC02 0xA0
#define I2C_PAGESIZE 4 /* 24C01/01A页缓冲是4个 *//* Private function prototypes -----------------------------------------------*/
void I2C_Configuration(void);
uint8_t I2C_Read(I2C_TypeDef *I2Cx,uint8_t I2C_Addr,uint8_t addr,uint8_t *buf,uint16_t num);
uint8_t I2C_Write(I2C_TypeDef *I2Cx,uint8_t I2C_Addr,uint8_t addr,uint8_t *buf,uint16_t num);#endif /*********************************************************************************************************END FILE
*********************************************************************************************************/
24C08.c/***************************************************************************//**文件: 24C08.c作者: Zhengyu https://gzwelink.taobao.com版本: V1.0.0时间: 20200401平台:MINI-G030C8T6*******************************************************************************/#include "main.h"#define EE_ADDR 0xa0//EEPROM地址,地址管脚全接地,为0xA0
#define EE_SCL_PIN GPIO_PIN_10 //模拟IIC的SCL信号 1.修改引脚即可修改IIC接口
#define EE_SDA_PIN GPIO_PIN_11 //模拟IIC的SDA信号
#define EE_IIC_SCL(val) HAL_GPIO_WritePin(GPIOB, GPIO_PIN_10,val) //SCL 输出高或者低 2.修改引脚即可修改IIC接口
#define EE_IIC_SDA(val) HAL_GPIO_WritePin(GPIOB, GPIO_PIN_11,val) //SDA 输出高或者低 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);
} /******************************************************************************
*函 数:void EE_IIC_Delay(void)
*功 能:IIC延时
*参 数:无
*返回值:无
*备 注: 移植时只需要将EE_IIC_Delay()换成自己的延时即可,目前是在16M主频下运行,大约等待10us
*******************************************************************************/
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}}}
/******************************************************************************
*函 数:void IIC_Init(void)
*功 能: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);//等待约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 elseEE_IIC_NAck(); //发送nACK return receive;
}//从EE指定地址读取一个字节
uint8_t EE_IIC_ReadByteFromSlave(uint8_t I2C_Addr,uint8_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); //发送寄存器地址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_EE_IIC_SendByteToSlave(uint8_t I2C_Addr,uint8_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); //发送寄存器地址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个字节

EE_IIC_Init();//EEPROM管脚初始化,配置管脚
EE_EE_IIC_SendByteToSlave(0xA0,0x00,0x55);//0地址存储0x55
HAL_Delay(10);//等待10ms
EE_IIC_ReadByteFromSlave(0xA0,0x00,&EEDATA);//从0地址读取1字节内容到EEDATA变量
然后编译,调试,查看EEDATA变量的值,结果如下图所示:

我们写进去的值时0x55,最后读出来的值是85'U',查一下ASCII码:

0x55对应的就是85'U',证明我们的结果是对的,证明此EEPROM读写实验成功。
相关文章:
STM32G030C8T6:EEPROM读写实验(I2C通信)
本专栏记录STM32开发各个功能的详细过程,方便自己后续查看,当然也供正在入门STM32单片机的兄弟们参考; 本小节的目标是,系统主频64 MHZ,采用高速外部晶振,实现PB11,PB10 引脚模拟I2C 时序,对M24C08 的EEPRO…...
使用Git管理github的代码库-上
1、下载安装Git https://download.csdn.net/download/notfindjob/11451730?spm1001.2014.3001.5503 2、注册一个github的账号(已经注册的,可略过这一步) 3、打开git命令行,配置github账号 git config --global user.name &quo…...
经典文献阅读之--D-Map(无需射线投射的高分辨率激光雷达传感器的占据栅格地图)
0. 简介 占用地图是机器人系统中推理环境未知和已知区域的基本组成部分。《Occupancy Grid Mapping without Ray-Casting for High-resolution LiDAR Sensors》介绍了一种高分辨率LiDAR传感器的高效占用地图框架,称为D-Map。该框架引入了三个主要创新来解决占用地图…...
开源免费的定时任务管理系统:Gocron
Gocron:精准调度未来,你的全能定时任务管理工具!- 精选真开源,释放新价值。 概览 Gocron是github上一个开源免费的定时任务管理系统。它使用Go语言开发,是一个轻量级定时任务集中调度和管理系统,用于替代L…...
从零开始详解OpenCV车道线检测
前言 车道线检测是智能驾驶和智能交通系统中的重要组成部分,对于提高道路安全、交通效率和驾驶舒适性具有重要意义。在本篇文章中将介绍使用OpenCV进行车道线的检测 详解 导入包 import cv2 import matplotlib.pyplot as plt import numpy as np读入图像并灰度化…...
【Java代码审计】逻辑漏洞篇
【Java代码审计】逻辑漏洞篇 逻辑漏洞概述常见逻辑漏洞点 逻辑漏洞概述 逻辑漏洞一般是由于源程序自身逻辑存在缺陷,导致攻击者可以对逻辑缺陷进行深层次的利用。逻辑漏洞出现较为频繁的地方一般是登录验证逻辑、验证码校验逻辑、密码找回逻辑、权限校验逻辑以及支…...
SSH简介
SSH,全名叫Secure Shell,你可以想象它是一个超级安全的管道,专门用来远程操控电脑的。就好比你在家用遥控器指挥远处的电视换台,但比这高级多了,因为它是专门为电脑设计的。 为什么需要SSH? 在互联网的早期…...
Oracle的高级分组函数grouping和grouping_id
在网上对Oracle的高级分组函数grouping和grouping_id的讲解并不多,特别是grouping_id,还有解说有误的。经过1天研究,已经完全掌握了两个函数的作用和用法,下面简单的讲述即可明白。下面给大家分享。 GROUPING 函数 语法:grouping(表达式) 作用: GROUPING将超聚…...
SqlServer 查询数据库 和 数据表 大小的语句
–Sqlserver 查询数据库 大小 SELECT * FROM (SELECT DB_NAME(database_id) AS DatabaseName,type_desc AS FileType,name AS FileName,size * 8 / 1024/1024 AS FileSizeGBFROM sys.master_filesWHERE type 0 -- 数据文件AND state 0 -- 在线状态 ) T1 ORDER BY FileSizeG…...
特殊类的设计与单例模式
1、特殊类的设计 如何设计出一个创建出的对象只能在堆上的类?将类的默认构造函数设置为私有,再将类的拷贝构造函数设置为delete,设置静态函数GetObj,内部调用new HeapOnly,这样就只能在堆上开辟空间。 class HeapOnly…...
MySQL从入门到高级 --- 6.函数
文章目录 第六章:6.函数6.1 聚合函数6.2 数学函数6.3 字符串函数6.4 日期函数6.4.1 日期格式 6.5 控制流函数6.5.1 if逻辑判断语句6.5.2 case when语句 6.6 窗口函数6.6.1 序号函数6.6.2 开窗聚合函数6.6.3 分布函数6.6.4 前后函数6.6.5 头尾函数6.6.6 其他函数6.7 …...
Qt---信号和槽
一、信号和槽机制 所谓信号槽,实际就是观察者模式。当某个事件发生之后,比如,按钮检测到自己被点击了一下,它就会发出一个信号(signal)。这种发出是没有目的的,类似广播。如果有对象对这个信号…...
POCEXP编写—文件上传案例
POC&EXP编写—文件上传案例 1. 前言2. 文件上传案例2.1. Burp抓包2.2. 基础代码实践2.2.1. 优化代码 2.3. 整体代码2.3.1. 木马测试 1. 前言 之前的文章基本上都是一些相对来说都是验证类的或者说是一些代码执行类的,相对来说都不是太复杂,而这篇会…...
C#知识|上位机UI设计-详情窗体设计思路及流程(实例)
哈喽,你好啊,我是雷工! 上两节练习记录了登录窗体和主窗体的实现过程,本节继续练习内容窗体的实现,以下为练习笔记。 01 详情窗体效果展示: 02 添加窗体并设置属性 在之前练习项目的基础上添加一个Windows窗体,设置名称为:FrmIPManage.cs 设置窗体的边框和标题栏的外…...
目标检测——印度车辆数据集
引言 亲爱的读者们,您是否在寻找某个特定的数据集,用于研究或项目实践?欢迎您在评论区留言,或者通过公众号私信告诉我,您想要的数据集的类型主题。小编会竭尽全力为您寻找,并在找到后第一时间与您分享。 …...
Zotero Word中插入带超链接的参考文献
Zotero 超链接 找了好多原代码,最接近能实施的为: https://blog.csdn.net/weixin_47244593/article/details/129072589 但是,就是向他说的一样会报错,我修改了代码,遇见报错的地方会直接跳过不执行,事后找…...
如何在服务器上下载,解压github上的代码
在github上找到对应仓库,找到平时download zip的地方,右键它,复制链接。在远程的终端里使用wget 链接 命令就可以得到zip了。 解压方法: -c :新建打包文件 -t :查看打包文件的内容含有哪些文件名 -x &…...
BGP学习二:BGP通告原则,BGP反射器,BGP路径属性细致讲解,新手小白无负担
目录 一.AS号 二.BGP路由生成 1.network 2.import-route引入 三.BGP通告原则 1.只发布最优且有效的路由 2.从EBGP获取的路由,会发布给所有对等体 3.水平分割原则 4.IBGP学习BGP默认不发送给EBGP,但如果也从IGP学习到了这条路由,就发…...
Docker学习(带图详细)
一、安装docker 参考官方文档:https://docs.docker.com/engine/install/centos/ 查看系统版本 [rootlocalhost ~]# cat /etc/redhat-release CentOS Linux release 7.9.2009 (Core) [rootlocalhost ~]# [rootlocalhost ~]# uname -a Linux localhost.localdomai…...
RabbitMQ 如何使用延迟队列
RabbitMQ 如何使用延迟队列 目录 前置条件场景描述RabbitMQ 延迟队列机制实现步骤 1. 安装 RabbitMQ 延迟队列插件2. 创建延迟队列和交换机3. 发布延迟消息4. 消费延迟消息 示例代码 1. 延迟队列配置2. 发布消息的 Producer 代码3. 消费消息的 Consumer 代码 注意事项 前置条…...
java_网络服务相关_gateway_nacos_feign区别联系
1. spring-cloud-starter-gateway 作用:作为微服务架构的网关,统一入口,处理所有外部请求。 核心能力: 路由转发(基于路径、服务名等)过滤器(鉴权、限流、日志、Header 处理)支持负…...
React第五十七节 Router中RouterProvider使用详解及注意事项
前言 在 React Router v6.4 中,RouterProvider 是一个核心组件,用于提供基于数据路由(data routers)的新型路由方案。 它替代了传统的 <BrowserRouter>,支持更强大的数据加载和操作功能(如 loader 和…...
【解密LSTM、GRU如何解决传统RNN梯度消失问题】
解密LSTM与GRU:如何让RNN变得更聪明? 在深度学习的世界里,循环神经网络(RNN)以其卓越的序列数据处理能力广泛应用于自然语言处理、时间序列预测等领域。然而,传统RNN存在的一个严重问题——梯度消失&#…...
Linux简单的操作
ls ls 查看当前目录 ll 查看详细内容 ls -a 查看所有的内容 ls --help 查看方法文档 pwd pwd 查看当前路径 cd cd 转路径 cd .. 转上一级路径 cd 名 转换路径 …...
学校招生小程序源码介绍
基于ThinkPHPFastAdminUniApp开发的学校招生小程序源码,专为学校招生场景量身打造,功能实用且操作便捷。 从技术架构来看,ThinkPHP提供稳定可靠的后台服务,FastAdmin加速开发流程,UniApp则保障小程序在多端有良好的兼…...
【ROS】Nav2源码之nav2_behavior_tree-行为树节点列表
1、行为树节点分类 在 Nav2(Navigation2)的行为树框架中,行为树节点插件按照功能分为 Action(动作节点)、Condition(条件节点)、Control(控制节点) 和 Decorator(装饰节点) 四类。 1.1 动作节点 Action 执行具体的机器人操作或任务,直接与硬件、传感器或外部系统…...
2021-03-15 iview一些问题
1.iview 在使用tree组件时,发现没有set类的方法,只有get,那么要改变tree值,只能遍历treeData,递归修改treeData的checked,发现无法更改,原因在于check模式下,子元素的勾选状态跟父节…...
ESP32 I2S音频总线学习笔记(四): INMP441采集音频并实时播放
简介 前面两期文章我们介绍了I2S的读取和写入,一个是通过INMP441麦克风模块采集音频,一个是通过PCM5102A模块播放音频,那如果我们将两者结合起来,将麦克风采集到的音频通过PCM5102A播放,是不是就可以做一个扩音器了呢…...
Rust 异步编程
Rust 异步编程 引言 Rust 是一种系统编程语言,以其高性能、安全性以及零成本抽象而著称。在多核处理器成为主流的今天,异步编程成为了一种提高应用性能、优化资源利用的有效手段。本文将深入探讨 Rust 异步编程的核心概念、常用库以及最佳实践。 异步编程基础 什么是异步…...
Swagger和OpenApi的前世今生
Swagger与OpenAPI的关系演进是API标准化进程中的重要篇章,二者共同塑造了现代RESTful API的开发范式。 本期就扒一扒其技术演进的关键节点与核心逻辑: 🔄 一、起源与初创期:Swagger的诞生(2010-2014) 核心…...
