细说STM32F407单片机通过IIC读写EEPROM 24C02

目录

一、操作说明 

二、工程配置

1、时钟、DEBUG、GPIO、USART6、NVIC、Code Generator

2、 IIC2

(1)Master Features组,主设备参数

(2)Slave Features组,从设备参数

三、软件设计

1、KELED

2、EEPROM

(1)24cxx.c

(2)24cxx.h

3、main.c

四、下载与运行


        本文旨在通过使用STM32F407的IIC总线读写 EEPROM 24C02。

        一些细节可以参考本文作者写的其他文章。

        参考文章1:细说STM32F407单片机轮询方式读写SPI FLASH W25Q16BV-CSDN博客  https://wenchm.blog.csdn.net/article/details/144587209

         参考文章2,细说STM32F407单片机IIC总线基础知识-CSDN博客  https://wenchm.blog.csdn.net/article/details/144717358

一、操作说明 

        项目使用旺宝红龙开发板,STM32F407ZGT6 KIT V1.0。 使用开发板上的按键S2、S3、S4、S5,依次按下诸键后执行如下操作,并且使用开发板上的LED灯,依次显示操作状态。S6是复位键。

[S2]KeyUp   = Write a number     LED1
[S3]KeyDown = Read the number    LED2
[S4]KeyLeft = Write a string     LED3
[S5]KeyRight= Read the string    LED4

二、工程配置

1、时钟、DEBUG、GPIO、USART6、NVIC、Code Generator

        与参考文章2相同。

        I2C的中断事件主要是表示传输过程和错误的一些事件,由于I2C通信是一种应答式通信,与其他外设的轮询式操作类似,本例不开启I2C2的中断。I2C也具有DMA功能,但是24C02操作的数据量小,没有使用DMA的必要。如果需要使用I2C接口的中断或DMA数据传输功能,可看参考文章中介绍的中断方式和DMA方式相关函数。 

2、 IIC2

        占用管脚PF0、PF1。默认全部设置。

(1)Master Features组,主设备参数

  • I2C Speed Mode,速度模式。可选标准模式(Standard Mode)或快速模式(Fast Mode)。
  • I2C Clock Speed (Hz),I2C时钟速度。标准模式最大值为100kHz,快速模式最大值为400kHz。
  • Fast Mode Duty Cycle,快速模式占空比。选择快速模式后这个参数会出现,用于设置时钟信号的占空比,是一个周期内低电平与高电平的时间比,有2:1和16:9两种选项。

        本示例中,速度模式选择标准模式。

(2)Slave Features组,从设备参数

  • Clock No Stretch Mode,禁止时钟延长。设置为Disabled表示允许时钟延长。
  • Primary AddressLength selection,设备主地址长度。可选7-bit或10-bit,本例选择7-bit。
  • DualAddress Acknowledge,双地址确认。从设备可以有两个地址,如果设置为Enabled。还会出现一个Secondary slave address参数,用于设置从设备副地址。
  • Primary slave address,从设备主地址。设置从设备主地址,作为I2C从设备时才需要设置。
  • General Call address detection,广播呼叫检测。设置为Disabled表示禁止广播呼叫,不对地址0x00应答;否则,就是允许广播呼叫,对地址0x00应答。

       STM32F407是I2C主设备,无须设置从设备地址。24C02是I2C从设备,其从设备地址是0xA0。 

三、软件设计

1、KELED

        keyled.c和keyled.h与参考文章相同。

2、EEPROM

       在项目中创建EEPROM文件夹,并在其中设计24cxx.c和24cxx.h。

        为便于程序的移植,在文件24cxx.h中定义一个宏I2C_HANDLE替代hi2c2。如果使用了不同的I2C接口,只需修改这个宏定义即可,而I2C接口的外设初始化由CubeMX自动生成的函数完成。在24cxx.h中还定义了表示24C02地址的宏DEV_ADDR_24CXX,如果实际电路中的24C02的I2C地址被修改了,修改这个宏即可

        在文件24cxx.h中定义了5个函数,前面4个都是直接封装I2C的HAL传输函数实现的,最后1个函数EP24C_WriteLongData()能自动将一个长的数据拆分为多个页(每页8字节)写入,这5个函数都使用I2C的阻塞式存储器数据传输函数。

(1)24cxx.c


/* 文件: 24cxx.c* 描述: 24C02驱动程序源程序文件*/#include "24cxx.h"
#define	EP24C_TIMEOUT 200						//超时等待时间,单位:ms
#define	EP24C_MEMADD_SIZE I2C_MEMADD_SIZE_8BIT  //存储器地址大小,8位地址//检查设备是否准备好I2C通讯,返回HAL_OK 表示OK
HAL_StatusTypeDef EP24C_IsDeviceReady(void)
{uint32_t Trials=10;		//尝试次数HAL_StatusTypeDef result=HAL_I2C_IsDeviceReady(&I2C_HANDLE,DEV_ADDR_24CXX,Trials,EP24C_TIMEOUT);return result;
}//向任意地址写入一个字节的数据, memAddr是存储器内部地址,byteData是需要写入的1个字节的数据
HAL_StatusTypeDef EP24C_WriteOneByte(uint16_t memAddress,uint8_t byteData)
{HAL_StatusTypeDef result=HAL_I2C_Mem_Write(&I2C_HANDLE,DEV_ADDR_24CXX,memAddress,EP24C_MEMADD_SIZE,&byteData,1,EP24C_TIMEOUT);return	result;
}//从任意地址读出一个字节的数据, memAddr是存储器内部地址,byteData是读出的1个字节的数据,若返回HAL_OK表示读取成功
HAL_StatusTypeDef EP24C_ReadOneByte(uint16_t memAddress,uint8_t *byteData)
{HAL_StatusTypeDef result=HAL_I2C_Mem_Read(&I2C_HANDLE,DEV_ADDR_24CXX,memAddress,EP24C_MEMADD_SIZE,byteData,1,EP24C_TIMEOUT);return result;
}//连续读取数据,任意地址,任意长度,不受页的限制
HAL_StatusTypeDef EP24C_ReadBytes(uint16_t memAddress,uint8_t *pBuffer,uint16_t bufferLen)
{if(bufferLen>MEM_SIZE_24CXX)	//超过总存储容量return HAL_ERROR;HAL_StatusTypeDef result=HAL_I2C_Mem_Read(&I2C_HANDLE,DEV_ADDR_24CXX,memAddress,EP24C_MEMADD_SIZE,pBuffer,bufferLen,EP24C_TIMEOUT);return result;
}//限定在一个页内写入连续数据,最多8字节。任意起始地址,但是起始地址+数据长度不能超过页边界
HAL_StatusTypeDef EP24C_WriteInOnePage(uint16_t memAddress,uint8_t *pBuffer,uint16_t bufferLen)
{if(bufferLen>PAGE_SIZE_24CXX)	//数据长度不能大于页的大小return HAL_ERROR;HAL_StatusTypeDef result=HAL_I2C_Mem_Write(&I2C_HANDLE,DEV_ADDR_24CXX,memAddress,EP24C_MEMADD_SIZE,pBuffer,bufferLen,EP24C_TIMEOUT);return result;
}//写任意长的数据,可以超过8字节,但数据地址必须从页首开始,即 8*N。自动分解为多次写入
HAL_StatusTypeDef EP24C_WriteLongData(uint16_t memAddress,uint8_t *pBuffer,uint16_t bufferLen)
{if(bufferLen>MEM_SIZE_24CXX)	//超过总存储容量return HAL_ERROR;HAL_StatusTypeDef result=HAL_ERROR;if(bufferLen<=PAGE_SIZE_24CXX)	//不超过1个page,直接写入后退出{result=HAL_I2C_Mem_Write(&I2C_HANDLE,DEV_ADDR_24CXX,memAddress,EP24C_MEMADD_SIZE,pBuffer,bufferLen,EP24C_TIMEOUT);return result;}uint8_t *pt=pBuffer;			//临时指针,不能改变传入的指针uint16_t pageCount=bufferLen/PAGE_SIZE_24CXX;	//Page个数for(uint16_t i=0; i<pageCount; i++)	//一次写入一个page的数据{result=HAL_I2C_Mem_Write(&I2C_HANDLE,DEV_ADDR_24CXX,memAddress,EP24C_MEMADD_SIZE,pt,PAGE_SIZE_24CXX,EP24C_TIMEOUT);pt += PAGE_SIZE_24CXX;memAddress += PAGE_SIZE_24CXX;HAL_Delay(5);					//必须有延时,以等待页写完if (result != HAL_OK)return result;}uint16_t leftBytes=bufferLen % PAGE_SIZE_24CXX;  //余数if (leftBytes>0)					//写入剩余的数据result=HAL_I2C_Mem_Write(&I2C_HANDLE,DEV_ADDR_24CXX,memAddress,EP24C_MEMADD_SIZE,pt,leftBytes,EP24C_TIMEOUT);return result;
}

(2)24cxx.h


/**  文件名:24cxx.h* 	功能描述: 24C02 EEPROM驱动程序,使用HAL库** 	(1)24C02是 256字节EEPROM,可以单个字节读写,连续读写时按页读写,每页8字节。所以,按页读写时最多8字节,页号0--31** 	(2)连续写入数据时要注意不要超过页的边界,否则从页的开始处重新写,会覆盖原来的内容。** 	(3)24C02地址是 0b1010xxxy, 其中xxx由芯片的地址引脚A2,A1,A0决定,一般接地,所以是0xA0, HAL库在读写时自动在最后一位写0或1进行读或写操作** 	(4)24C02的地址数据长度是8位,使用宏定义符号I2C_MEMADD_SIZE_8BIT*/#ifndef __24cxx_H
#define __24cxx_H#include "stm32f4xx_hal.h"
#include "i2c.h" 				//i2c.h中定义了hi2c2
/* 两个与硬件相关的定义,   */
#define I2C_HANDLE hi2c2		//I2C外设对象变量,利用i2c.h中定义的hi2c2
#define	DEV_ADDR_24CXX 0x00A0	//24C02的写地址
// EEPROM存储器参数
#define	PAGE_SIZE_24CXX 0x0008	//24C02的Page大小为8字节
#define	MEM_SIZE_24CXX (uint16_t)256 //24C02总共容量字节数,8*32=256字节//检查设备是否准备好
HAL_StatusTypeDef EP24C_IsDeviceReady(void);//在任意地址写入一个字节
HAL_StatusTypeDef EP24C_WriteOneByte(uint16_t memAddress, uint8_t byteData);//在任意地址读出一个字节
HAL_StatusTypeDef EP24C_ReadOneByte(uint16_t memAddress, uint8_t *byteData);//连续读取数据,任意地址,任意长度,不受页的限制
HAL_StatusTypeDef EP24C_ReadBytes(uint16_t memAddress, uint8_t *pBuffer, uint16_t bufferLen);/*限定在一个页内写入连续数据,最多8字节。任意起始地址,*但是起始地址+数据长度不能超过页边界,即不能超过地址8*N-1 */
HAL_StatusTypeDef EP24C_WriteInOnePage(uint16_t memAddress, uint8_t *pBuffer, uint16_t bufferLen);//写任意长的数据,可以超过8字节,但数据地址必须从页首开始,即 8*N
HAL_StatusTypeDef EP24C_WriteLongData(uint16_t memAddress, uint8_t *pBuffer, uint16_t bufferLen);#endif

3、main.c

/* USER CODE BEGIN Includes */
#include "keyled.h"
#include "24cxx.h"
#include <stdio.h>
/* USER CODE END Includes */
/* USER CODE BEGIN 2 */printf("Demo17_1:I2C Interface\r\n");printf("24C02:EEPROM, 256 bytes\r\n");printf("8 bytes/page, 32 pages\r\n");printf("I2C Device Address=0xA0\r\n");if (EP24C_IsDeviceReady() == HAL_OK)printf("Device is ready.\r\n");//显示菜单printf("[S2]KeyUp   = Write a number\r\n");printf("[S3]KeyDown = Read the number\r\n");printf("[S4]KeyLeft = Write a string\r\n");printf("[S5]KeyRight= Read the string\r\n");// MCU output low level LED light is onLED1_OFF();LED2_OFF();LED3_OFF();LED4_OFF();/* USER CODE END 2 */
  /* USER CODE BEGIN WHILE */uint8_t num1 = 107,num2;uint16_t addr_any = 4;	//任意地址, 0-255uint16_t addr_page = 2*8;	//Page2起始地址//uint8_t infoStr[50];	//用于生成显示信息字符��?while (1){/* USER CODE END WHILE *//* USER CODE BEGIN 3 */KEYS curKey = ScanPressedKey(KEY_WAIT_ALWAYS);switch(curKey){case KEY_UP:{if (EP24C_WriteOneByte(addr_any,num1) == HAL_OK)printf("Write %d at Address %d.\r\n",num1,addr_any);LED1_ON();LED2_OFF();LED3_OFF();LED4_OFF();}break;case KEY_DOWN:{if (EP24C_ReadOneByte(addr_any,&num2) == HAL_OK)printf("Read out %d at Address %d.\r\n",num2,addr_any);LED1_OFF();LED2_ON();LED3_OFF();LED4_OFF();}break;case KEY_LEFT:{uint8_t strIn[] = "University of Petroleum";	//自动加'\0'if (EP24C_WriteLongData(addr_page,strIn,sizeof(strIn)) == HAL_OK)printf("Write string from Page2:%s\r\n",strIn);LED1_OFF();LED2_OFF();LED3_ON();LED4_OFF();}break;case KEY_RIGHT:{uint8_t strOut[50];if (EP24C_ReadBytes(addr_page,strOut,50) == HAL_OK)printf("Read string from Page2:%s\r\n",strOut);LED1_OFF();LED2_OFF();LED3_OFF();LED4_ON();}break;case KEY_NONE:{LED1_OFF();LED2_OFF();LED3_OFF();LED4_OFF();}break;default:break;}printf("** Reselect menu or reset **\r\n");HAL_Delay(500);	//延时500,消除按键后抖动}/* USER CODE END 3 */
}

        上述程序中有一个I2C_HandleTypeDef类型的结构体变量hi2c2,这是表示I2C2接口的外设对象变量,24C02的驱动程序文件24cxx.c中就使用这个外设对象变量访问I2C2接口。函数MX_I2C2_Init()中对hi2c2的各成员变量赋值,各赋值语句与STM32CubeIDE里的设置是对应的。完成hi2c2的赋值后,执行HAL_I2C_Init(&hi2c2)对I2C2接口进行初始化。

        HAL_I2C_MspInit()是I2C接口的MSP初始化函数,在函数HAL_I2C_Init()里被调用。函数HAL_I2C_MspInit()的主要功能是对I2C2接口的复用引脚PF0和PF1进行GPIO引脚配置。

         由STM32CubeIDE自动生成的代码,这里省略。

/* USER CODE BEGIN 4 *///串口打印
int __io_putchar(int ch)
{HAL_UART_Transmit(&huart6,(uint8_t*)&ch,1,0xFFFF);return ch;
}
/* USER CODE END 4 */

四、下载与运行

        下载,运行。依次按下开发板上的S2、S3、S4、S5按键,执行写入1字节,读出1字节、写入页字符串,读出页字符串。按下S6键,执行复位。

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

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

相关文章

神经网络-Inception

Inception网络是由Google开发的一种深度卷积神经网络架构&#xff0c;旨在解决计算机视觉领域中的图像分类和物体识别任务。 Inception网络最初在2014年被提出&#xff0c;并在ImageNet图像分类挑战赛上取得了很好的结果。其设计灵感来自于模块化的思想&#xff0c;将不同尺度…

PyTorch Instance Normalization介绍

Instance Normalization(实例归一化) 是一种标准化技术,与 Batch Normalization 类似,但它对每个样本独立地对每个通道进行归一化,而不依赖于小批量数据的统计信息。这使得它非常适合小批量训练任务以及图像生成任务(如风格迁移)。 Instance Normalization 的原理 对每…

国内独立开发者案例及免费送独立开发蓝图书

独立开发者在国内越来越受到关注&#xff0c;他们追求的是一种自由且自给自足的工作状态。 送这个&#xff1a; 少楠light&#xff08;Flomo、小报童、如果相机&#xff09;&#xff1a;他们是独立开发者的典范&#xff0c;不仅开发了多款产品&#xff0c;还坚信“剩者为王”…

【小程序】自定义组件的data、methods、properties

目录 自定义组件 - 数据、方法和属性 1. data 数据 2. methods 方法 3. properties 属性 4. data 和 properties 的区别 5. 使用 setData 修改 properties 的值 自定义组件 - 数据、方法和属性 1. data 数据 在小程序组件中&#xff0c;用于组件模板渲染的私有数据&…

MATLAB用find函数结合all,any函数高效解决问题

如本节中最后提到的问题&#xff0c;我们输出后还需要判断&#xff0c;不是特别的一目了然&#xff0c;这时候我们可以再加上 f i n d find find函数直接标记序号并输出。首先我们先来了解 f i n d find find的用法&#xff0c; f i n d ( a ) find(a) find(a)表示将矩阵或向量…

2022博客之星年度总评选开始了

作者简介&#xff1a;陶然同学 专注于Java领域开发 熟练掌握Java、js等语言的“Hello World” CSDN原力计划作者、CSDN内容合伙人、Java领域优质作者、Java领域新星作者、51CTO专家、华为云专家、阿里云专家等 &#x1f3ac; 陶然同学&#x1f3a5; 由 陶然同学 原创&#…

vue2 升级为 vite 打包

VUE2 中使用 Webpack 打包、开发&#xff0c;每次打包时间太久&#xff0c;尤其是在开发的过程中&#xff0c;本文记录一下 VUE2 升级Vite 步骤。 安装 Vue2 Vite 依赖 dev 依赖 vitejs/plugin-vue2": "^2.3.3 vitejs/plugin-vue2-jsx": "^1.1.1 vite&…

20241227在ubuntu20.04.6系统中,如何用watch命令每秒钟调用nvidia-smi来监控GPU

watch -n 1 nvidia-smi 20241227在ubuntu20.04.6系统中&#xff0c;如何用watch命令每秒钟调用nvidia-smi来监控GPU 2024/12/27 17:04 缘起&#xff1a;在ubuntu20.04.6系统中&#xff0c;使用M6000显卡来跑whisper&#xff0c;显存拉满/占用巨大&#xff0c;但是CPU占用比低&…

[江科大STM32] 第五集STM32工程模板——笔记

保存&#xff0c;进去选芯片型号&#xff0c;我们是F10C8T6 再添加一些文件&#xff0c;自己看路径 然后去 复习这三文件 打开KEIL add existing那个&#xff0c;添加已经存在的文件 还有5个.c.h文件也要添加进来 回到KEIL 点击旁边的settings 如果你用寄存器开发就建到这里就可…

Bitmap(BMP)图像信息分析主要说明带压缩的形式

文章目录 参考资料Bitmap图片结构Bitmap图片组成实例说明 参考资料 微软官方-位图存储 Bitmap图片结构 序号名称说明1Bitmap File HeaderBitmap文件头2Bitmap Info HeaderBitmap信息头3Color Palette Data调色板数据4Bitmap Image Data图像数据 说明 Bitmap文件头的大小为…

二分和离散化

为什么把二分和离散化放一起&#xff1a;因为离散化其实是一种二分整数的过程。 二分 相信大家都接触过二分查找&#xff08;折半查找&#xff09;&#xff0c;这就是二分的思想。 二分通过每次舍弃一半并不存在答案的区间&#xff0c;进而快速锁定要求的答案&#xff08;二…

ESP-IDF学习记录(2)ESP-IDF 扩展的简单使用

傻瓜式记录一个示例的打开&#xff0c;编译&#xff0c;运行。后面我再一个个运行简单分析每个demo的内容。 1.打开示例代码 2.选择项目&#xff0c;文件夹 3.选择串口 4.选择调试方式 5.根据硬件GPIO口配置menuconfig 6.构建项目 7.烧录设备&#xff0c;选择串口UART方式 运行…

SpringMVC学习(一)——请求与响应处理

目录 一、SpringMVC简介 二、RequestMapping&#xff1a;请求路径映射 三、RestController 四、请求限定 五、请求处理 1.使用普通变量&#xff0c;收集请求参数 2.使用RequestParam明确指定获取参数 3.目标方法参数是一个pojo 4.RequestHeader&#xff1a;获取请求…

数据分析的分类和EDIT思维框架

为了服务于企业不同层次的决策&#xff0c;商业数据分析过程需要提供相应的数据科学产出物。 一般而言&#xff0c;数据分析需要经历从需求层、数据层、分析层到输出层四个阶段。 第一个阶段是需求层——确定目标&#xff0c;具体目标需要依据具体的层次进行分析&#xff1a…

实验五 时序逻辑电路部件实验

一、实验目的 熟悉常用的时序逻辑电路功能部件&#xff0c;掌握计数器、了解寄存器的功能。 二、实验所用器件和仪表 1、双 D触发器 74LS74 2片 2、74LS162 1片 3、74194 1片 4、LH-D4实验仪 1台 1.双…

iOS 中的 nil、Nil、NULL、NSNull 僵尸对象和野指针

iOS 中的 nil、Nil、NULL、NSNull 僵尸对象和野指针-CSDN博客 类型含义使用场景示例nil表示一个指向 Objective - C 对象的空指针。在 Objective - C 和 Swift&#xff08;与 Objective - C 交互时&#xff09;中用于表示对象不存在。当一个对象变量没有指向任何有效的对象实例…

JS面试题|[2024-12-28]

1.JS的设计原理是什么&#xff1f; JS引擎 运行上下文 调用栈 事件循环 回调 执行流程&#xff1a; JS引擎将代码解析为电脑可以执行的代码&#xff0c;调用一些API&#xff08;运行上下文&#xff09;让浏览器执行 JS是单线程的&#xff0c;每次从调用栈里面取出来的代码进行调…

全面了解 SQL Server:功能、优势与最佳实践

SQL Server 是微软公司推出的一款关系型数据库管理系统&#xff08;RDBMS&#xff09;&#xff0c;广泛应用于企业级数据存储、数据分析、应用开发等领域。作为全球最受欢迎的数据库管理系统之一&#xff0c;SQL Server 提供了强大的功能和工具&#xff0c;支持从小型应用到大型…

jdk动态代理和cglib动态代理对比

jdk动态代理和cglib动态代理对比&#xff1a; CGLIB 和 JDK 动态代理都可以用来在运行时生成代理对象 1. 基本概念 JDK 动态代理&#xff1a;只代理接口&#xff08;interface&#xff09;&#xff0c;无法代理类。它使用 java.lang.reflect.Proxy 类和 java.lang.reflect.I…

攻破 Kioptix Level 1 靶机

找教程然后自己练习&#xff0c;论菜狗的自我修养 基本步骤 1.确定目标IP 2.扫描端口&#xff0c;服务&#xff0c;版本信息&#xff0c;漏洞信息 3.查找漏洞可利用脚本 4.运行脚步 一、信息获取 arp-scan -l nmap -sS -p- -sV -sC -A --min-rate5000 192.168.5.130 二、查…