基于STM32F401RET6智能锁项目(AT24C0X存储芯片)

一、IIC基本介绍

1、IIC通信接口

• I2C(Inter IC Bus)是由Philips公司开发的一种通用数据总线
• 两根通信线:SCL(串行时钟线)、SDA(串行数据线)
• 同步,半双工
• 带数据应答
• 支持总线挂载多设备(一主多从、多主多从)
一主多从:单片机作为主机,主导I2C总线的运行,挂载在I2C总线的所有外部模块都是从机,从机只有被主机点名之后才能控制I2C总线,不能在未经允许的情况下去碰I2C总线,防止冲突。
多主多从:在总线上的任何一个模块都可以是主机

2、IIC通信接口的特点

①.双线制。一根是SCL,作为时钟同步线;一根是SDA,作为数据传输线。

②.半双工。因为真正做数据传输的只有SDA,所以使用IIC通信的双方能双向通信,但不能同时

③.同步通信。有同步信号线SCL,通信协议时序严格,SDA在通信双方初步同步后在SCL的时序下按位传输数据。

④.通信为主从机模式。支持一主多从模式,通信过程中SCL一直由主机控制,而SDA则不固定

⑤.从属设备地址。因为支持主从机模式,所以IIC总线中主机在通信过程中为了确保找到具体的从机,需要先知道该从机的设备地址(Device Address)而主机则不需要设备地址。

二、IIC从设备的地址

1、设备地址的组成

①从机地址是7位,读写位是1位。

②读写位

因为lIC是半双工通信接口,所以在主机开始与从机通信前,主机需要告诉从机此次通信是谁做为发送方,谁做为接收方。故有了读写位。

其中最低位为0表示写模式----主机作为发送方;1表示读模式----主机作为接收方

2、设备地址的作用

用于在IIC总线上唯一标识每个从设备,以便主设备能够正确地与特定从设备进行通信。通过设备地址,主设备可以向特定的从设备发送数据或请求数据,从而实现设备之间的通信和数据交换。设备地址通常是一个唯一的数字或编码,每个从设备都有一个独特的地址,主设备根据这个地址来识别和访问相应的从设备。

三、AT24COX

1、 AT24C02介绍

AT24C02是一种可以实现掉电不丢失的存储器,可用于保存单片机运行时想要永久保存的数据信息
存储介质:E2PROM
通讯接口:I2C总线
容量:256字节

2、硬件原理图

3、设备地址

写模式设备地址:OxA0

读模式设备地址:OxA1

2、时序单元

1、起始条件

在I2C 总线处于空闲状态时:SCL和SDA都处于高电平状态,是由SCL和SDA由外挂的上拉电阻拉高至高电平,总线处于平静的高电平状态。
主机需要进行数据收发时,当从机捕获到SCL高电平,SDA下降沿信号时,就会进行自身的复位,等待主机的召唤。然后在SDA下降沿之后,主机要再把SCL拽下来,拽下SCL:1、占用总线,2、方便拼接。

2、终止条件

SCL先放手,回到高电平;SDA再放手,回到高电平,产生一个上升沿。上升沿触发终止条件,同时终止条件之后,SCL和SDA都是高电平。

3、起始条件和终止代码实现

这些是封装的函数,还是对SCL、SDA的操作,根据时序看配置低电平还是高电平。

4、发送一个字节

主机如果想发送0,就拉底SDA到低电平;如果想发送1,就放手,SDA回到高电平,在SCL低电平期间,允许改变SDA的电平。当这一位的数据放好之后,主机就松手时钟线,SCL回到高电平。在高电平期间,是从机读取SDA的时候,所以高电平期间,SDA不允许变化。SCL处于高电平之后,从机需要尽快地读取SDA,一般在上升沿这个时刻,从机就已经读取完成了。主机在放手SCL一段时间后,就可以继续拉低SCL,然后传输下一位。

5、接收一个字节

SCL低电平期间,从机将数据位依次到SDA线上(高位先行),然后释放SCL,主机在SCL高电平期间读取数据位,所以SCL高电平期间SDA不允许有数据变化。

6、发送应答和接收应答

• 发送应答:主机在接收完一个字节之后,在下一个时钟发送一位数据,数据0表示应答,数据1表示非应答

• 接收应答:主机在发送完一个字节之后,在下一个时钟接收一位数据,判断从机是否应答,数据0表示应答,数据1表示非应答(主机在接收之前,需要释放SDA

四、AT24COX代码实现

指定地址写

对于指定设备(Slave Address),在指定地址(Reg Address)下,
写入指定数据(Data)
void AT24C02_W_REG(uint8_t adress,uint8_t data)   //指定地址写
{
	MyI2C_Start();  //起始条件
	MyI2C_SendByte(AT24C02_ADDR_W);  //指定从机设备写的地址:10100000
	MyI2C_ReceiveAck();   //主机接收从机的应答  判断从机是否收到
	MyI2C_SendByte(adress);//指定内部地址
	MyI2C_ReceiveAck(); 
	MyI2C_SendByte(data); //写入的数据
	MyI2C_ReceiveAck();
	MyI2C_Stop();
}

指定地址读

对于指定设备(Slave Address),在当前地址指针指示的地址下,
读取从机数据(Data)
uint8_t AT24C02_R_REG(uint8_t adress)  //指定地址读
{
	uint8_t data;
	MyI2C_Start();  //起始条件
	MyI2C_SendByte(AT24C02_ADDR_W);  //指定从机设备写的地址:10100000
	MyI2C_ReceiveAck();   //主机接收从机的应答  判断从机是否收到
	MyI2C_SendByte(adress);//指定内部地址
	
	MyI2C_Start();  //起始条件
	MyI2C_SendByte(AT24C02_ADDR_R);//读从机的数据
	MyI2C_ReceiveAck();  //接收应答,从机开始发数据
	data = MyI2C_ReceiveByte();  //主机接收数据
	MyI2C_SendAck(1);//参数1:不给从机应答
	MyI2C_Stop();
	
	return data;
}

五、整体代码实现

main.c

#include "led.h"
#include "delay.h"
#include "key.h"
#include "usart.h"
#include "stdio.h"
#include "myiic.h"
#include "at24c0x.h"

int main(void)
{
//	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
//	SysTick_Config(SystemCoreClock/1000);   //配置1ms的中断
//	Led_Config();
	USART1_Config();
	At24c02_Config();
	
	AT24C02_W_REG(0x12,0xAA);
	uint8_t ID = AT24C02_R_REG(0x12);
	printf("%x\r\n",ID);

//	while(1)
//	{	
//		if(LED_Period[0]>=LED_Period[1])
//		{
//			LED_Period[0]=0;
//			led3_T();
//			led4_T();
//		}
//	}
}

IIC.C

#include "myiic.h"
#include "delay.h"

/**
  * 函    数:I2C写SCL引脚电平
  * 参    数:BitValue 协议层传入的当前需要写入SCL的电平,范围0~1
  * 返 回 值:无
  * 注意事项:此函数需要用户实现内容,当BitValue为0时,需要置SCL为低电平,当BitValue为1时,需要置SCL为高电平
  */
void MyI2C_W_SCL(uint8_t BitValue)
{
	GPIO_WriteBit(GPIOA, GPIO_Pin_8, (BitAction)BitValue);		//根据BitValue,设置SCL引脚的电平
	Delay_us(10);												//延时10us,防止时序频率超过要求
}

/**
  * 函    数:I2C写SDA引脚电平
  * 参    数:BitValue 协议层传入的当前需要写入SDA的电平,范围0~0xFF
  * 返 回 值:无
  * 注意事项:此函数需要用户实现内容,当BitValue为0时,需要置SDA为低电平,当BitValue非0时,需要置SDA为高电平
  */
void MyI2C_W_SDA(uint8_t BitValue)
{
	GPIO_WriteBit(GPIOC, GPIO_Pin_9, (BitAction)BitValue);		//根据BitValue,设置SDA引脚的电平,BitValue要实现非0即1的特性
	Delay_us(10);												//延时10us,防止时序频率超过要求
}

/**
  * 函    数:I2C读SDA引脚电平
  * 参    数:无
  * 返 回 值:协议层需要得到的当前SDA的电平,范围0~1
  * 注意事项:此函数需要用户实现内容,当前SDA为低电平时,返回0,当前SDA为高电平时,返回1
  */
uint8_t MyI2C_R_SDA(void)
{
	uint8_t BitValue;
	BitValue = GPIO_ReadInputDataBit(GPIOC, GPIO_Pin_9);		//读取SDA电平
	Delay_us(10);												//延时10us,防止时序频率超过要求
	return BitValue;											//返回SDA电平
}


/***************
AT24XX  SCL  PA8  开漏输出
				SDA  PC9  开漏输出
***************/
void MyI2C_config(void)
{
	//开启GPIOA和GPIOC的时钟
	RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA|RCC_AHB1Periph_GPIOC,ENABLE);
	
	//定义结构体
	GPIO_InitTypeDef GPIO_InitStruct;
	//给结构体内容赋值
	GPIO_InitStruct.GPIO_Mode = GPIO_Mode_OUT;   //输出模式
	GPIO_InitStruct.GPIO_OType = GPIO_OType_OD;  //开漏模式
	GPIO_InitStruct.GPIO_Pin = GPIO_Pin_8;
	GPIO_InitStruct.GPIO_Speed = GPIO_Fast_Speed;
	
	GPIO_Init(GPIOA,&GPIO_InitStruct);
	GPIO_InitStruct.GPIO_Pin = GPIO_Pin_9;
	GPIO_Init(GPIOC,&GPIO_InitStruct);
	
	/*设置默认电平*/
	GPIO_SetBits(GPIOA, GPIO_Pin_8);
	GPIO_SetBits(GPIOC, GPIO_Pin_9);
}

/*I2C起始*/
void MyI2C_Start(void)
{
	MyI2C_W_SDA(1);			//释放SDA,确保SDA为高电平
	MyI2C_W_SCL(1);			//释放SCL,确保SCL为高电平
	MyI2C_W_SDA(0);			//在SCL高电平期间,拉低SDA,产生起始信号
	MyI2C_W_SCL(0);			//起始后把SCL也拉低,即为了占用总线,也为了方便总线时序的拼接
}

/**
  * 函    数:I2C终止
  * 参    数:无
  * 返 回 值:无
  */
void MyI2C_Stop(void)
{
	MyI2C_W_SDA(0);							//拉低SDA,确保SDA为低电平
	MyI2C_W_SCL(1);							//释放SCL,使SCL呈现高电平
	MyI2C_W_SDA(1);							//在SCL高电平期间,释放SDA,产生终止信号
}

/**
  * 函    数:I2C发送一个字节
  * 参    数:Byte 要发送的一个字节数据,范围:0x00~0xFF
  * 返 回 值:无
  */
void MyI2C_SendByte(uint8_t Byte)
{
	uint8_t i;
	for (i = 0; i < 8; i ++)				//循环8次,主机依次发送数据的每一位
	{
		MyI2C_W_SDA(Byte & (0x80 >> i));	//使用掩码的方式取出Byte的指定一位数据并写入到SDA线
		MyI2C_W_SCL(1);						//释放SCL,从机在SCL高电平期间读取SDA
		MyI2C_W_SCL(0);						//拉低SCL,主机开始发送下一位数据
	}
}

/**
  * 函    数:I2C接收一个字节
  * 参    数:无
  * 返 回 值:接收到的一个字节数据,范围:0x00~0xFF
  */
uint8_t MyI2C_ReceiveByte(void)
{
	uint8_t i, Byte = 0x00;					//定义接收的数据,并赋初值0x00,此处必须赋初值0x00,后面会用到
	MyI2C_W_SDA(1);							//接收前,主机先确保释放SDA,避免干扰从机的数据发送
	for (i = 0; i < 8; i ++)				//循环8次,主机依次接收数据的每一位
	{
		MyI2C_W_SCL(1);						//释放SCL,主机机在SCL高电平期间读取SDA
		if (MyI2C_R_SDA() == 1){Byte |= (0x80 >> i);}	//读取SDA数据,并存储到Byte变量
														//当SDA为1时,置变量指定位为1,当SDA为0时,不做处理,指定位为默认的初值0
		MyI2C_W_SCL(0);						//拉低SCL,从机在SCL低电平期间写入SDA
	}
	return Byte;							//返回接收到的一个字节数据
}

/**
  * 函    数:I2C发送应答位
  * 参    数:Byte 要发送的应答位,范围:0~1,0表示应答,1表示非应答
  * 返 回 值:无
  */
void MyI2C_SendAck(uint8_t AckBit)
{
	MyI2C_W_SDA(AckBit);					//主机把应答位数据放到SDA线
	MyI2C_W_SCL(1);							//释放SCL,从机在SCL高电平期间,读取应答位
	MyI2C_W_SCL(0);							//拉低SCL,开始下一个时序模块
}

/**
  * 函    数:I2C接收应答位
  * 参    数:无
  * 返 回 值:接收到的应答位,范围:0~1,0表示应答,1表示非应答
  */
uint8_t MyI2C_ReceiveAck(void)
{
	uint8_t AckBit;							//定义应答位变量
	MyI2C_W_SDA(1);							//接收前,主机先确保释放SDA,避免干扰从机的数据发送
	MyI2C_W_SCL(1);							//释放SCL,主机机在SCL高电平期间读取SDA
	AckBit = MyI2C_R_SDA();					//将应答位存储到变量里
	MyI2C_W_SCL(0);							//拉低SCL,开始下一个时序模块
	return AckBit;							//返回定义应答位变量
}



IIC.h

#ifndef _MYIIC_H_
#define _MYIIC_H_

#include "stm32f4xx.h"

void MyI2C_config(void);
void MyI2C_Start(void);
void MyI2C_Stop(void);
void MyI2C_SendByte(uint8_t Byte);
uint8_t MyI2C_ReceiveByte(void);
void MyI2C_SendAck(uint8_t AckBit);
uint8_t MyI2C_ReceiveAck(void);

#endif

AT24C02.C

#include "at24c0x.h"
#include "myiic.h"
#include "delay.h"

void At24c02_Config(void)
{
	MyI2C_config();
}

void AT24C02_W_REG(uint8_t adress,uint8_t data)   //指定地址写
{
	MyI2C_Start();  //起始条件
	MyI2C_SendByte(0xA0);  //指定从机设备写的地址:10100000
	MyI2C_ReceiveAck();   //主机接收从机的应答  判断从机是否收到
	MyI2C_SendByte(adress);//指定内部地址
	MyI2C_ReceiveAck(); 
	MyI2C_SendByte(data); //写入的数据
	MyI2C_ReceiveAck();
	MyI2C_Stop();
	
	Delay_ms(5);
}

uint8_t AT24C02_R_REG(uint8_t adress)  //指定地址读
{
	uint8_t NUM;
	
	MyI2C_Start();  //起始条件
	MyI2C_SendByte(0xA0);  //指定从机设备写的地址:10100000
	MyI2C_ReceiveAck();   //主机接收从机的应答  判断从机是否收到
	MyI2C_SendByte(adress);//指定内部地址
	MyI2C_ReceiveAck(); 
	MyI2C_Stop();
	
	MyI2C_Start();  //起始条件
	MyI2C_SendByte(0xA1);//读从机的数据
	MyI2C_ReceiveAck();  //接收应答,从机开始发数据
	NUM = MyI2C_ReceiveByte();  //主机接收数据
	MyI2C_SendAck(1);//发送应答,给从机非应答,终止从机的数据输出
	MyI2C_Stop();
	
	return NUM;
}




AT24C02.h

#ifndef _AT24C0X_H_
#define _AT24C0X_H_

#define AT24C02_ADDR    0x50  //1010000
#define AT24C02_ADDR_W  0xA0  //10100000
#define AT24C02_ADDR_R  0xA1  //10100001

#include "stm32f4xx.h"
void At24c02_Config(void);
void AT24C02_W_REG(uint8_t adress,uint8_t data);   //指定地址写
uint8_t AT24C02_R_REG(uint8_t adress);  //指定地址读



#endif

  • 24
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值