E2PROM STM32使用I2C协议驱动

news/2024/12/17 17:21:19/

关于E2PROM

使用E2PROM可以保存数据,特点就是掉电不丢失。

在我们CT107D开发板上所使用的的器件是AT24C02,一个容量大小是2Kb/s,也就是256字节的E2PROM。

24C02是一个基于I2C通信协议的器件。

24C01/02/04/08/16 是低工作电压的1K/2K/4K/8K/16K 位串行电可擦除只读存储器,内部组织为128/256/512/1024/2048 个字节,每个字节8 位,该芯片被广泛应用于低电压及低功耗的工商业领域。
在这里插入图片描述
1、串行时钟信号引脚(SCL):在SCL 输入时钟信号的上升沿将数据送入EEPROM器件,并在时钟的下降沿将数据读出。
2、串行数据输入/输出引脚(SDA):SDA 引脚可实现双向串行数据传输。该引脚为开漏输出,可与其它多个开漏输出器件或开集电极器件线或连接。
3、器件/页地址脚(A2,A1,A0):A2、A1 和A0 引脚为24C01与24C02 的硬件连接的器件地址输入引脚。24C01在一个总线上最多可寻址八个1K 器件,24C02 在一个总线上最多可寻址八个2K 器件,A2、A1 和A0 内部必须连接。
4、24C04 仅使用A2、A1 作为硬件连接的器件地址输入引脚,在一个总线上最多可寻址四个4K 器件。A0 引脚内部未连接。
5、24C08 仅使用A2 作为硬件连接的器件地址输入引脚,在一个总线上最多可寻址两个8K 器件。A0和A1 引脚内部未连接。
6、24C16 未使用作为硬件连接的器件地址输入引脚,在一个总线上最多可连接一个16K器件。A0、A1 和A2 引脚内部未连接。
7、写保护(WP)引脚:24C01/02/04/08/16 具有用于硬件数据写保护功能的引脚。当该引脚接GND时,允许正常的读/写操作。当该引脚接VCC 时,芯片启动写保护功能。
在这里插入图片描述

单片机开发步骤

可以发现上述的通信接口类型,都是针对串行通信设计的,所以重点是对通信接口的配置使用,即遵循既定的通信协议标准进行芯片配置。这里以IIC的通信为例,需要完成四种信号的发送(起始位、终止位、数据0/应答、数据1/非应答)。由于采用的是串行的数据传送方式,所以对于数据帧的传送,是一位一位的传送,在接收时也是逐位接收。另外对于EE2PROM而言,存储器配备了写保护,WK引脚,可以对存储器的数据进行一定程度的加密操作。

IIC总线上会外挂很多的从机,所以为了建立主从之间的通信,首先需要传送从机的地址,确认完毕后,才可以发送数据,按照实际的应用场景,可以分为三种不同的数据传送方式,格式分别如下所示,其中灰色部分由主机发出,而白色由从机发出,另外从机地址后的0表示主机向从机发送数据,而1表示从机向主机发送数据;A表示应答,A非表示非应答。

主机只发送数据:
在这里插入图片描述
主机只接受数据:
在这里插入图片描述
主机发送和接收数据:
在这里插入图片描述

程序设计

I2C.C


#include "i2c_hal.h"#define DELAY_TIME	20/*** @brief SDA线输入模式配置* @param None* @retval None*/
void SDA_Input_Mode()
{GPIO_InitTypeDef GPIO_InitStructure = {0};GPIO_InitStructure.Pin = GPIO_PIN_7;GPIO_InitStructure.Mode = GPIO_MODE_INPUT;GPIO_InitStructure.Pull = GPIO_PULLUP;GPIO_InitStructure.Speed = GPIO_SPEED_FREQ_HIGH;HAL_GPIO_Init(GPIOB, &GPIO_InitStructure);
}/*** @brief SDA线输出模式配置* @param None* @retval None*/
void SDA_Output_Mode()
{GPIO_InitTypeDef GPIO_InitStructure = {0};GPIO_InitStructure.Pin = GPIO_PIN_7;GPIO_InitStructure.Mode = GPIO_MODE_OUTPUT_OD;GPIO_InitStructure.Pull = GPIO_NOPULL;GPIO_InitStructure.Speed = GPIO_SPEED_FREQ_HIGH;HAL_GPIO_Init(GPIOB, &GPIO_InitStructure);
}/*** @brief SDA线输出一个位* @param val 输出的数据* @retval None*/
void SDA_Output( uint16_t val )
{if ( val ){GPIOB->BSRR |= GPIO_PIN_7;}else{GPIOB->BRR |= GPIO_PIN_7;}
}/*** @brief SCL线输出一个位* @param val 输出的数据* @retval None*/
void SCL_Output( uint16_t val )
{if ( val ){GPIOB->BSRR |= GPIO_PIN_6;}else{GPIOB->BRR |= GPIO_PIN_6;}
}/*** @brief SDA输入一位* @param None* @retval GPIO读入一位*/
uint8_t SDA_Input(void)
{if(HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_7) == GPIO_PIN_SET){return 1;}else{return 0;}
}/*** @brief I2C的短暂延时* @param None* @retval None*/
static void delay1(unsigned int n)
{uint32_t i;for ( i = 0; i < n; ++i);
}/*** @brief I2C起始信号* @param None* @retval None*/
void I2CStart(void)
{SDA_Output(1);delay1(DELAY_TIME);SCL_Output(1);delay1(DELAY_TIME);SDA_Output(0);delay1(DELAY_TIME);SCL_Output(0);delay1(DELAY_TIME);
}/*** @brief I2C结束信号* @param None* @retval None*/
void I2CStop(void)
{SCL_Output(0);delay1(DELAY_TIME);SDA_Output(0);delay1(DELAY_TIME);SCL_Output(1);delay1(DELAY_TIME);SDA_Output(1);delay1(DELAY_TIME);}/*** @brief I2C等待确认信号* @param None* @retval None*/
unsigned char I2CWaitAck(void)
{unsigned short cErrTime = 5;SDA_Input_Mode();delay1(DELAY_TIME);SCL_Output(1);delay1(DELAY_TIME);while(SDA_Input()){cErrTime--;delay1(DELAY_TIME);if (0 == cErrTime){SDA_Output_Mode();I2CStop();return ERROR;}}SDA_Output_Mode();SCL_Output(0);delay1(DELAY_TIME);return SUCCESS;
}/*** @brief I2C发送确认信号* @param None* @retval None*/
void I2CSendAck(void)
{SDA_Output(0);delay1(DELAY_TIME);delay1(DELAY_TIME);SCL_Output(1);delay1(DELAY_TIME);SCL_Output(0);delay1(DELAY_TIME);}/*** @brief I2C发送非确认信号* @param None* @retval None*/
void I2CSendNotAck(void)
{SDA_Output(1);delay1(DELAY_TIME);delay1(DELAY_TIME);SCL_Output(1);delay1(DELAY_TIME);SCL_Output(0);delay1(DELAY_TIME);}/*** @brief I2C发送一个字节* @param cSendByte 需要发送的字节* @retval None*/
void I2CSendByte(unsigned char cSendByte)
{unsigned char  i = 8;while (i--){SCL_Output(0);delay1(DELAY_TIME);SDA_Output(cSendByte & 0x80);delay1(DELAY_TIME);cSendByte += cSendByte;delay1(DELAY_TIME);SCL_Output(1);delay1(DELAY_TIME);}SCL_Output(0);delay1(DELAY_TIME);
}/*** @brief I2C接收一个字节* @param None* @retval 接收到的字节*/
unsigned char I2CReceiveByte(void)
{unsigned char i = 8;unsigned char cR_Byte = 0;SDA_Input_Mode();while (i--){cR_Byte += cR_Byte;SCL_Output(0);delay1(DELAY_TIME);delay1(DELAY_TIME);SCL_Output(1);delay1(DELAY_TIME);cR_Byte |=  SDA_Input();}SCL_Output(0);delay1(DELAY_TIME);SDA_Output_Mode();return cR_Byte;
}//
void I2CInit(void)
{GPIO_InitTypeDef GPIO_InitStructure = {0};GPIO_InitStructure.Pin = GPIO_PIN_7 | GPIO_PIN_6;GPIO_InitStructure.Mode = GPIO_MODE_OUTPUT_PP;GPIO_InitStructure.Pull = GPIO_PULLUP;GPIO_InitStructure.Speed = GPIO_SPEED_FREQ_HIGH;HAL_GPIO_Init(GPIOB, &GPIO_InitStructure);
}void eeprom_write(uint8_t addr,uint8_t dat)
{I2CStart();I2CSendByte(0xa0);I2CWaitAck();I2CSendByte(addr);I2CWaitAck();I2CSendByte(dat);I2CWaitAck();I2CStop();
}uint8_t eeprom_read(uint8_t addr)
{uint8_t rec;I2CStart();I2CSendByte(0xa0);I2CWaitAck();I2CSendByte(addr);I2CWaitAck();I2CStop();I2CStart();I2CSendByte(0xa1);I2CWaitAck();rec=I2CReceiveByte();I2CSendNotAck();I2CStop();return rec;
}void eeprom_write16(uint8_t addr,uint16_t dat)
{uint8_t dat_h=dat>>8;uint8_t dat_l=dat;eeprom_write(addr,dat_h);HAL_Delay(10);eeprom_write(addr+1,dat_l);HAL_Delay(10);
}uint16_t  eeprom_read16(uint8_t addr)
{uint8_t dat_h=eeprom_read(addr);uint8_t dat_l=eeprom_read(addr+1);return (dat_h<<8)+dat_l;
}void eeprom_write_f(uint8_t addr,float dat)
{uint16_t dat_f=dat*100;uint8_t dat_h=dat_f>>8;uint8_t dat_l=dat_f;eeprom_write(addr,dat_h);HAL_Delay(10);eeprom_write(addr+1,dat_l);HAL_Delay(10);
}float  eeprom_read_f(uint8_t addr)
{uint8_t dat_h=eeprom_read(addr);uint8_t dat_l=eeprom_read(addr+1);return ((dat_h<<8)+dat_l)/100.0f;
}
void mcp_write(uint8_t dat)  //0-127 对应0-100k的阻值
{I2CStart();I2CSendByte(0x5e);I2CWaitAck();I2CSendByte(dat);I2CWaitAck();I2CStop();
}

I2C.H

#ifndef __I2C_HAL_H
#define __I2C_HAL_H#include "stm32g4xx_hal.h"void I2CStart(void);
void I2CStop(void);
unsigned char I2CWaitAck(void);
void I2CSendAck(void);
void I2CSendNotAck(void);
void I2CSendByte(unsigned char cSendByte);
unsigned char I2CReceiveByte(void);
void I2CInit(void);void eeprom_write(uint8_t addr,uint8_t dat);
uint8_t eeprom_read(uint8_t addr);
void eeprom_write16(uint8_t addr,uint16_t dat);
uint16_t  eeprom_read16(uint8_t addr);
void eeprom_write_f(uint8_t addr,float dat);
float  eeprom_read_f(uint8_t addr);void mcp_write(uint8_t dat);
#endif

main.c

	#include "i2c_hal.h"I2CInit();                  //初始化I2Cchar eep_num=0;							//定义存放的数据eep_num=eeprom_read(8);			//放在初始化位置读取if(bkey[1].short_flag==1){eep_num++;eeprom_write(8,eep_num);			//存入数据的地址和存入的数据sprintf(text,"    KEY_1 down!     ");LCD_DisplayStringLine(Line8,(uint8_t *)text);//局部刷新	bkey[1].short_flag=0;}		uint8_t eep_num;            // 0-255uint16_t eep_frq;						//0-65535float eep_adc;							//浮点数eep_num=eeprom_read(8);     //初始化的位置读取数据,上电后只读一次eep_frq=eeprom_read16(10);eep_adc=eeprom_read_f(12);sprintf(text,"EEP:%d,%d,%.2f   ",eep_num,eep_frq,eep_adc);LCD_DisplayStringLine(Line6,(uint8_t *)text);//局部刷新		if(bkey[2].short_flag==1){eep_num+=1;eeprom_write(8,eep_num);HAL_Delay(10);eeprom_write16(10,(uint16_t)frq1);eeprom_write_f(12,adc_data_PB15);sprintf(text,"    KEY_2 down!     ");LCD_DisplayStringLine(Line8,(uint8_t *)text);//局部刷新	bkey[2].short_flag=0;}		

http://www.ppmy.cn/news/1555908.html

相关文章

scala转义序列

在字符串或字符字面值中可以有以下转义序列&#xff1a; \b \t \u0008:退格 BS \u0090:水平制表符 HT 7 空白与注释 \n \f \r \” \‟ \\ \u000a:换行 LF \u000c: 格式进纸FF \u000d:回车 CR \u0022:双引号 ” \u0027:单引号 ‟ \u005c:反斜线 \ 0 到 255 间的 Unicode…

Vue 2 中 v-html 指令的使用详解

目录 Vue 2 中 v-html 指令的使用详解 v-html 简介 基本语法 使用场景与示例 示例 1&#xff1a;基础用法 示例 2&#xff1a;动态更新 HTML 内容 注意事项 XSS 风险 安全建议&#xff1a; 性能考虑 最佳实践 Vue 2 中 v-html 指令的使用详解 Vue.js 是一个用于构建…

Ps:视频动作

Ps菜单&#xff1a;窗口/动作 Window/Actions 快捷键&#xff1a;Alt F9 视频动作 Video Actions组中的动作主要用于 Photoshop 的视频编辑&#xff0c;包括创建和反转 Alpha 通道、调整亮度和饱和度以符合广播标准、格式化 NTSC 和 PAL DVD 幻灯片&#xff0c;以及添加标题安…

代码随想录算法训练营day43|动态规划part10

今天的是子序列问题&#xff0c;第二题难度不大&#xff0c;可以采用类似贪心的思路求解。第一题的思想比较新颖&#xff0c;注意dp[i]是以i为末尾元素的最长递增子序列&#xff0c;可以用两层循环&#xff0c;第二层循环遍历从0到i-1&#xff0c;通过不断更新dp[i]的值求解最长…

游戏引擎学习第52天

仓库 : https://gitee.com/mrxiao_com/2d_game 这节的内容相当多 回顾 在游戏中&#xff0c;实体被分为不同的类别&#xff1a;接近玩家的“高频实体”、距离较远并正在模拟的“低频实体”和不进行更新的“休眠实体”。这些实体会根据它们与玩家的距离进行处理&#xff0c;接…

在Spring Boot中的实现国际化(i18n)

1.什么是国际化&#xff08;i18n&#xff09;&#xff1f; 国际化&#xff08;Internationalization&#xff0c;简称i18n&#xff09;是指在软件应用中支持多种语言和文化的能力。通过国际化&#xff0c;应用可以根据用户的语言和地区设置&#xff0c;动态地显示不同的文本内…

android全局拖拽效果实现startDragAndDrop

有多种方式实现全角拖拽&#xff0c;我们基于系统标准的api startDragAndDrop 来做介绍 实现步骤&#xff1a; 一、应用的的拖拽 1.1 通过长按触发 holder.itemView.setOnLongClickListener(new View.OnLongClickListener() {Overridepublic boolean onLongClick(View v) {C…

【人工智能】基于Python的自然语言处理:深入实现文本相似度计算

解锁Python编程的无限可能:《奇妙的Python》带你漫游代码世界 文本相似度计算是自然语言处理(NLP)中的核心任务,广泛应用于搜索引擎、推荐系统、问答系统等领域。本文全面解析文本相似度计算的核心技术,使用Python中的spaCy和sentence-transformers库实现多种方法,包括基…