使用cubemx工具的stm32用AT24C02实现简单密码(一点点面向对象的思想编程)

20 篇文章 2 订阅
11 篇文章 3 订阅
本文详细介绍了STM32通过I2C协议与AT24C02 EEPROM进行通信的过程,包括I2C的基础知识、配置步骤以及四种信号和五种速率模式。同时,展示了如何使用CubeMX配置STM32F1芯片的I2C接口,以及AT24C02的地址选择。此外,还讨论了如何利用AT24C02实现密码管理,包括密码的写入、读取和管理函数。
摘要由CSDN通过智能技术生成

I2C

I2C简介

I2C是一种两线的协议,一条使SCL,一条是SDA。
他们两个分别承担时钟线和数据线的作用,

  1. 时钟线SCL用来保持与主机的同步通信;
  2. 数据线SDA用于数据的发送与接收。
    SCL的英文是 Slave Clock ,意思是给从机的时钟
    SDA的英文是 Slave Data ,意思是给从机的数据

I2C的4种信号与5种速率

4种信号

I2C协议最基础的几种信号:起始、停止、应答和非应答信号

1、起始信号与停止信号

在一个时间区间内:

  • 先把SDA给拉低电平,再把SCL拉底电平就是起始信号(两个信号原先默认是高电平);
  • 先把SCL给拉高电平,再把SCL拉高电平就是终止信号;(两个信号原先默认是低电平)
    在这里插入图片描述
2、发送应答与接收应答

如图所示:
在这里插入图片描述

3、发送一个字节

在这里插入图片描述

4、接收一个字节

在这里插入图片描述

5种速率

I2C协议可以工作在以下5种速率模式下,不同的器件可能支持不同的速率。

  • 标准模式(Standard):100kbps
  • 快速模式(Fast):400kbps
  • 快速模式+(Fast-Plus):1Mbps
  • 高速模式(High-speed):3.4Mbps
  • 超快模式(Ultra-Fast):5Mbps(单向传输)

在cubemx中配置f1芯片的时候只有前面两种,标准模式和快速模式。

I2C与多设备通信

一组I2C总线上可以挂载多个设备,如图所示:
在这里插入图片描述
当主机要与某一个I2C设备进行通信的时候,因为当前的线上挂载了多个设备,所以此时的信号相当于是以广播的形式发送出去的,所有设备都会接收到这个信号。
但是当前我只想和其中一个设备通信该怎么办呢?为了针对这种情况,于是每个设备都有了一个对应的设备地址(这个设备地址是由厂家设定的,同一种设备的设备地址是一样的。如,有两个AT24C02的芯片,他们的设备地址就是一样的)

我们要与设备进行通信会先发一个起始信号,再发送一个设备地址字节信号,所有的设备都会接收到这串消息,但是只有与自己的设备号对应上了,才能算是开始通信。

对于AT24C02的设备地址信号就是0xA0和0xA1(在A0、A1、A2引脚都拉地的情况下),这两个字节的设备地址的区别就是在与写和读的区别,0xA0是写设备信号,而0xA1是读设备信号。

AT24C02

AT24C02简介

AT24C02是ATMAL公司开发的串行 COMS 型 EEPROM存储类芯片,与他同类型的芯片还有AT24C01A、AT24C04、AT24C08A、AT24C016A,
这些芯片唯一的区别就是存储容量不同,后面是1的就是1Kbit、后面是2的就是2Kbit,以此类推。

芯片引脚介绍

在这里插入图片描述

AT24C02有8个引脚:

  • A0、A1、A2:这三个是设备地址选择引脚
  • WP:写保护引脚,高低平有效,一般给他拉底电平
  • SCL:I2C时钟线
  • SDA:I2C数据线

AT24C02地址选择

对于AT24C02来说,如果把A0、A1、A2都拉到地上,那么器件地址就是0xA0或0xA1,一般来说也是把A0、A1、A2都拉到地上
在这里插入图片描述

CubeMX的配置

这里用的芯片是野火的指南者STM32F103VET6

新建工程

1. 打开 STM32CubeMX 软件,点击“新建工程”

在这里插入图片描述

2. 选择 MCU 和封装

在这里插入图片描述

3.配置时钟

RCC 设置,选择 HSE(外部高速时钟) 为 Crystal/Ceramic Resonator(晶振/陶瓷谐振器)
在这里插入图片描述
选择 Clock Configuration,配置系统时钟 SYSCLK 为 72MHz
修改 HCLK 的值为 72 后,输入回车,软件会自动修改所有配置
在这里插入图片描述

4. 配置调试模式

非常重要的一步,否则会造成第一次烧录程序后续无法识别调试器
SYS 设置,选择 Debug 为 Serial Wire
在这里插入图片描述

5. I2C参数配置

选择I2C1中打开I2C,右边会出现PB7为SDA、PB6为SCL。
在这里插入图片描述
在这里插入图片描述

这些配置从上到下分别是:

  • I2C Speed Mode:配置I2C速率,这里有标准模式、快速模式。
  • I2C Clock Speed(Hz):这里不用配置,上面,那个Speed Mode配置好了,这里会默认变成对应的速率。
  • Clock No Stretch Mode:时钟不拉伸,这里选择disabled,意思是选择不拉伸时钟,感觉这里就是为了匹配一些速度慢设备的,而专门搞了一个配置。
  • Primary Address Length selection:设备地址长度选择,有7位的有10位的,默认7位即可。
  • 后面的其他配置我不懂,但我知道配置默认即可。

6_1. 工程配置1

6_2. 工程配置2

这里要注意,工程名一定不能有中文,并且路径下也一定不能有中文
在这里插入图片描述

7生成工程

在这里插入图片描述

代码部分

AT24C02.h

#ifndef __AT24C02_H_
#define __AT24C02_H_

#include "i2c.h"

//修改对应的I2C句柄
#define AT24C02_I2C hi2c1

void EEPOM_Clear_Single(uint16_t i);						//使第i个字节的值归0
void EEPOM_Empty(void);													//使清空EEPOM内存
void Wrinte_EEPOM(uint16_t i, uint8_t data);		//在第i个字节的位置写入SIZE__大小的data
uint8_t Read_EEPOM(uint16_t i);									//读取第个字节的数据


#endif

AT24C02.c

#include "stdarg.h"
#include "AT24C02.h"


#define ADDR_24LCxx_Write 0xA0
#define ADDR_24LCxx_Read 0xA1


#define AT24C02_NUM_Byte 256	//AT24C02的大小

/*清除单个位置的数据*/
void EEPOM_Clear_Single(uint16_t i)
{
	uint8_t clear__ = 0;
	HAL_I2C_Mem_Write(&AT24C02_I2C, ADDR_24LCxx_Write, i, I2C_MEMADD_SIZE_8BIT, &clear__, 1, 1000);
}

/*清除全部数据*/
void EEPOM_Empty(void)
{
	uint8_t clear__ = 0;
	for(uint16_t i=0; i<256; i++)
	{
		EEPOM_Clear_Single(i);
	}
}

void Wrinte_EEPOM(uint16_t i, uint8_t data)
{
	/* wrinte date to EEPROM */
	HAL_I2C_Mem_Write(&AT24C02_I2C, ADDR_24LCxx_Write, i, I2C_MEMADD_SIZE_8BIT, &data, 1, 1000);
}

uint8_t Read_EEPOM(uint16_t i)
{
	uint8_t data;
	/* read date from EEPROM */
  HAL_I2C_Mem_Read(&AT24C02_I2C, ADDR_24LCxx_Read, i, I2C_MEMADD_SIZE_8BIT, &data, 1, 1000);
	return data;
}

Login.h

#ifndef LOGIN__H___
#define LOGIN__H___

#include "usart.h"

/*修改对应串口句柄*/
#define		PASS_UART_HAND 			huart1
#define		PASS_UART 					USART1


/*****密码长度选择*****/
typedef enum{
	PASS_LENGTH_4  = 4,
	PASS_LENGTH_5  = 5,
	PASS_LENGTH_6  = 6,
	PASS_LENGTH_7  = 7,
	PASS_LENGTH_8  = 8,
}PASSWORD_LENGTH;

/*****AT中分给密码区的空间*****/
typedef enum{
	AT_DOWN_PA_RE = 0,//密码下限区
	AT_UP___PA_RE = 7,//密码上限区
	AT_PASS__SIZE = 8
}PASSWORD_REGION;

/*****密码类型*****/
typedef enum{
	LOGGED_NOT  	= 0,//未登入
	LOGGING_IN		= 1,//登入中
}LOGIN_STATUS;

/*****密码选择确认或者撤销*****/
typedef enum{
	PASS_REVOKE  = 11,		//撤销
	PASS_CONFIRM = 12,		//确认
	PASS_EMPTY	 = 13,		//清空
}PASS_YES_OR_NO;

/*****登入后其他操作*****/
typedef enum{
	CHANGE_PASS	= 14,		//修改密码
	RESET_PASS 	= 15,		//重置密码
	EXIT_LOGIN	= 16		//退出登入
}LOGIN_IN_OPERATION;



/*密码设定长度*/
#define 	PASSLEN 	PASS_LENGTH_4

/*****密码句柄*****/
typedef struct Pass_Hand{
	uint8_t Login_Status;												//登入状态,取值在LOGIN_STATUS中
	uint8_t * Password_Buf;											//密码缓冲区
	uint8_t a_Password_Buf;											//密码中转站
	uint8_t	Password_Buf_Cnt;										//密码缓存计数
	
	ErrorStatus (*Init_PossWord)(void);					//初始化函数
	ErrorStatus (*Reset_PossWord)(void);				//复位清除原来的密码,并重新初始化密码
	ErrorStatus (*Revise_PossWord)(uint8_t*);		//修改密码,用新的密码替换原来的密码
	ErrorStatus (*Match_PossWord)(uint8_t*);		//密码匹配,拿用户输入的密码和设定的密码进行匹配
	
//	struct Pass_Hand* yxypass;									
}Password_hand;




extern Password_hand yxypassword;		//句柄

/*函数区*/

/*输入密码提示*/
void Password_Promot(void);


/****未登入回调函数****/
/*
	未登入:
--	撤销密码
--	确认密码
--	清空密码
--	其他:
		--	判断密码是否超值:
				--	超值,舍弃
				--	没超值,输入一位密码
*/
void Not_Login_in(void);


/****已登入回调函数****/
/*
	已登入:
--	退出登入
--	确认密码
--	清空密码
--	其他:
		--	判断密码是否超值:
				--	超值,舍弃
				--	没超值,输入一位密码
*/
void Login_in(void);




#endif	/*LOGIN__H___*/

Login.c

#include "AT24C02.h"
#include "Login.h"

#include "redirect.h"														//试着删

#define D_MS_Len 	2








/*内部函数声明区*/
static ErrorStatus Password_Init(void);
static ErrorStatus Password_Reset(void);
static ErrorStatus Password_Revise(uint8_t* User_Pass);
static ErrorStatus Password_Match(uint8_t* User_Input_Pass);


static uint8_t PasswordBuf[PASS_LENGTH_8] = {0};	//密码缓冲区,按最大密码量来设置大小


/*密码延时*/
#define Pass_Delay_Ms yxy_delay_ms

Password_hand yxypassword = {
	LOGGED_NOT,			//登入状态(未登入)
	PasswordBuf,		//密码缓存区
	0,							//密码中转站
	0,							//密码计数
	
	Password_Init,		//初始化密码函数
	Password_Reset,		//重置密码函数
	Password_Revise,	//修改密码
	Password_Match		//密码匹配
};


/*
	AT24C02中的密码区(个人设定):0~7字节的位置
*/
void yxy_delay_us(uint32_t us)
{
		SysTick->CTRL =0;	
		SysTick->LOAD =72*us-1;
		SysTick->VAL =0;	
		SysTick->CTRL =5;	
		while((SysTick->CTRL & 0x10000) == 0);
	SysTick->CTRL =0;	
}

void yxy_delay_ms(uint32_t ms)
{
	while(ms--)
	{
		SysTick->CTRL =0;	
		SysTick->LOAD =72000-1;
		SysTick->VAL =0;	
		SysTick->CTRL =5;	
		while((SysTick->CTRL & 0x10000) == 0);
		
	}
	SysTick->CTRL =0;	
}

/****初始密码****/
/*
	初始一个默认密码:1234……
*/
/*
	(SUCCESS)0 是重置成功的
	(ERROR  )1 是重置失败的
*/
static ErrorStatus Password_Init(void)
{
	/*默认密码*/
	uint8_t DefaultPassword[PASSLEN] = {1,1,1,1};
	
	/*在密码区中对密码初始化*/
	for(uint8_t i=AT_DOWN_PA_RE; i<AT_UP___PA_RE; i++)
	{
		if(i>=PASSLEN)	//判断当前数值是否超过密码设定值
		{
			break;
		}
		Wrinte_EEPOM(i, DefaultPassword[i]);		//写入默认密码
		Pass_Delay_Ms(D_MS_Len);
		if(Read_EEPOM(i) != DefaultPassword[i])	//判断是否清除成功
		{
			return ERROR;
		}
		Pass_Delay_Ms(D_MS_Len);
	}
	return SUCCESS;
}


/****密码重置****/
/*
	(SUCCESS)0 是重置成功的
	(ERROR  )1 是重置失败的
*/
static ErrorStatus Password_Reset(void)
{
	/*在密码区中对密码重置*/
	for(uint8_t i=AT_DOWN_PA_RE; i<AT_UP___PA_RE; i++)
	{
		if(i>=PASSLEN)	//判断密码是否超过密码设定值
		{
			break;
		}
		Wrinte_EEPOM(i, 0);			//把使用密码区的密码清零
		Pass_Delay_Ms(D_MS_Len);
		if(Read_EEPOM(i) != 0)	//判断是否清除成功
		{
			return ERROR;
		}
		Pass_Delay_Ms(D_MS_Len);
	}
	Password_Init();	//复位后初始化密码
	return SUCCESS;
}


/****修改密码****/
/*
	(SUCCESS)0 是重置成功的
	(ERROR  )1 是重置失败的
*/
static ErrorStatus Password_Revise(uint8_t* User_Pass)
{
	/*在密码区中修改密码*/
	for(uint8_t i=AT_DOWN_PA_RE; i<AT_UP___PA_RE; i++)
	{
		if(i>=PASSLEN)	//判断密码是否超过密码设定值
		{
			break;
		}
		Wrinte_EEPOM(i, User_Pass[i]);//在EEPOM中写入密码
		Pass_Delay_Ms(D_MS_Len);
		if(Read_EEPOM(i) != User_Pass[i])	//不等于就直接退出
		{
			return ERROR;
		}
		Pass_Delay_Ms(D_MS_Len);
	}
	return SUCCESS;
}

/****匹配密码****/
/*
	(SUCCESS)0 是重置成功的
	(ERROR  )1 是重置失败的
*/
static ErrorStatus Password_Match(uint8_t* User_Input_Pass)
{
	/*在密码区中匹配密码*/
	for(uint8_t i=AT_DOWN_PA_RE; i<AT_UP___PA_RE; i++)
	{
		if(i>=PASSLEN)	//判断密码是否超过密码设定值
		{
			break;
		}
		if(User_Input_Pass[i] != Read_EEPOM(i))//密码一个一个比较
		{
			return ERROR;
		}
	}
	return SUCCESS;
}


void Password_Input_sigle(void)
{

}

/****密码提示****/
void Password_Promot(void)														//试着删
{
	printf("请输入密码:\r\n");
}

/****未登入回调函数****/
/*
	未登入:
--	撤销密码
--	确认密码
--	其他:
		--	判断密码是否超值:
				--	超值,舍弃
				--	没超值,输入一位密码
*/
void Not_Login_in(void)
{
	switch(yxypassword.a_Password_Buf)
	{
		case PASS_REVOKE:		//撤销输入一位
			if(yxypassword.Password_Buf_Cnt > 0)	//减少计数后直接退出
			{
				printf("清除1位\r\n");														//试着删
				yxypassword.Password_Buf_Cnt--;
			}
			break;
		case PASS_CONFIRM:	//确认密码
			printf("12的进入\r\n");														//试着删
			if(yxypassword.Password_Buf_Cnt == PASSLEN)	//判断是否密码输入完
			{
				if(yxypassword.Match_PossWord(yxypassword.Password_Buf) == SUCCESS)	//判断是否匹配正确
				{
					yxypassword.Login_Status = LOGGING_IN;		//登入成功,进入已登入状态
					yxypassword.Password_Buf_Cnt=0;						//登入成功,清零计数位
					printf("登入成功\r\n");														//试着删
					break;
				}
			}
			printf("登入失败\r\n");														//试着删

			break;
		case PASS_EMPTY:	//清空输入密码
				printf("清空\r\n");														//试着删
				yxypassword.Password_Buf_Cnt = 0;	//清空输入计数
			break;
		default:
			if(yxypassword.Password_Buf_Cnt >= PASSLEN)		//判断是否密码输入超值,超值丢弃,不超存入
			{
				printf("超值舍弃\r\n");														//试着删
			}
			else
			{
				//把中转站的值赋给缓冲区
				yxypassword.Password_Buf[yxypassword.Password_Buf_Cnt] = yxypassword.a_Password_Buf;
				yxypassword.Password_Buf_Cnt++;	//密码数量加1
			}
			break;
	}
}

/****已登入回调函数****/
/*
	已登入:
--	退出登入
--	确认密码
--	其他:
		--	判断密码是否超值:
				--	超值  ,舍弃
				--	没超值,输入一位密码
*/

void Login_in(void)
{
	static uint8_t Change_Flag = 0;
	switch(yxypassword.a_Password_Buf)
	{
		case CHANGE_PASS:	//修改密码
			printf("开始修改密码\r\n");														//试着删
			Change_Flag=1;	//修改密码标志位
			break;
		case RESET_PASS:	//复位密码
			if(yxypassword.Reset_PossWord() == SUCCESS)
			{
				printf("复位成功\r\n");														//试着删
			}
			else
			{
				printf("复位失败r\n");														//试着删
			}
			break;
		case EXIT_LOGIN:	//退出登入	
			yxypassword.Login_Status = 0;
			printf("退出成功\r\n");														//试着删
			break;
		default:
			if(Change_Flag == 1)//判断修改密码标志位
			{
//				printf("进入修改密码界面\r\n");														//试着删
				switch(yxypassword.a_Password_Buf)
				{
					case PASS_REVOKE:		//撤销输入一位
						if(yxypassword.Password_Buf_Cnt > 0)	//减少计数后直接退出
						{
							printf("清除1位\r\n");
							yxypassword.Password_Buf_Cnt--;
						}
						break;
					case PASS_CONFIRM:	//确认修改密码
						if(yxypassword.Password_Buf_Cnt == PASSLEN)	//判断是否密码输入完
						{
							printf("满值的确认修改\r\n");														//试着删
							if(yxypassword.Revise_PossWord(yxypassword.Password_Buf) == SUCCESS)	//判断修改密码是否成功
							{
								yxypassword.Login_Status = LOGGING_IN;		//修改成功,进入已登入状态
								yxypassword.Password_Buf_Cnt=0;						//修改成功,清零计数位
								printf("修改成功\r\n");														//试着删
								break;
							}
						}
						printf("修改失败\r\n");														//试着删
					
						break;
					case PASS_EMPTY:	//清空输入修改
							printf("清空\r\n");															//试着删
							yxypassword.Password_Buf_Cnt = 0;	//清空输入计数
						break;
					default:
						
						if(yxypassword.Password_Buf_Cnt >= PASSLEN)		//判断是否密码输入超值,超值丢弃,不超存入
						{
							printf("超值舍弃\r\n");														//试着删
						}
						else
						{
							//把中转站的值赋给缓冲区
							yxypassword.Password_Buf[yxypassword.Password_Buf_Cnt] = yxypassword.a_Password_Buf;
							yxypassword.Password_Buf_Cnt++;	//密码数量加1
						}
						break;
				}
			}
			break;
	}
}



void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
	static uint8_t cnt=0;														//试着删
	
//	printf("cnt%d: %d\r\n", cnt, yxypassword.a_Password_Buf);//试着删

	if(huart->Instance == PASS_UART)
	{
		
		if((cnt%2==1))														//试着删
		{
			goto yyy;
		}
		if(yxypassword.a_Password_Buf>=16)														//试着删
		{
			yxypassword.a_Password_Buf -= 6;
		}
		
		
		switch(yxypassword.Login_Status)
		{
			case LOGGED_NOT:		//未登入状态
				Not_Login_in();		//未登入函数
				break;
			case LOGGING_IN:		//登入状态
				Login_in();
				break;
			default:
				break;
		}
		
		//HAL_UART_Transmit(&PASS_UART_HAND, &yxypassword.Password_Buf[yxypassword.Password_Buf_Cnt-1], 1, 10);	//阻塞发送
		
		printf("键入:%d\r\n", yxypassword.a_Password_Buf);														//试着删
		
yyy:														//试着删

		HAL_UART_Receive_IT(&PASS_UART_HAND, &(yxypassword.a_Password_Buf), 1);		//再次开启接收中断
		cnt++;														//试着删
	}
}

main.c

头文件包含

在这里插入图片描述

主函数代码试用

在这里插入图片描述

效果图

登入

框起来的是一个输入,打印出来的
在这里插入图片描述

修改密码重新登入

框起来的是一个输入,打印出来的
在这里插入图片描述

7 参考资料

STM32CubeMX学习笔记(9)——I2C接口使用(读写EEPROM AT24C02).

AT24C02 Datasheet.

一文看懂I2C协议.

I2C通信协议介绍.

  • 5
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

嵌入一下?

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值