关于AT24C02的学习

一、基本概念

image


二、特性说明

image


三、内存结构

image


四、器件地址

image


五、指令描述

image


/***********************************************************************************
* @file    main.c 
* @author  wvjnuhhail@126.com
* @version V1
* @date    2024-11-13
* @brief   AT24C02存储IC的指令设计
***********************************************************************************/

#include "stm32f4xx.h"
#include <stdio.h>
#include <stdbool.h>

//使用IO口来模拟IIC时序,从而控制从器件
#define  IIC_SDA(n)  (n) ? GPIO_SetBits(GPIOB,GPIO_Pin_9) : GPIO_ResetBits(GPIOB,GPIO_Pin_9)
#define  IIC_SCL(n)  (n) ? GPIO_SetBits(GPIOB,GPIO_Pin_8) : GPIO_ResetBits(GPIOB,GPIO_Pin_8)

#define  IIC_READ		 GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_9)


//延时微秒  注意:24bit计数器  不能798915us
void delay_us(u32 nus)
{
	SysTick->CTRL = 0; 					// 关闭定时器
	SysTick->LOAD = nus*21 - 1; // 设置重载值
	SysTick->VAL 	= 0; 					// 清除计数值
	SysTick->CTRL = 1; 					// 开启定时器  21MHZ  
	while ((SysTick->CTRL & 0x00010000)==0);// 等待时间到达
	SysTick->CTRL = 0; 					// 关闭定时器
}

//延时毫秒  注意:24bit计数器  不能798ms
void delay_ms(u32 nms)
{
	SysTick->CTRL = 0; 					// 关闭定时器
	SysTick->LOAD = nms*21000 - 1; // 设置重载值
	SysTick->VAL 	= 0; 					// 清除计数值
	SysTick->CTRL = 1; 					// 开启定时器  21MHZ  
	while ((SysTick->CTRL & 0x00010000)==0);// 等待时间到达
	SysTick->CTRL = 0; 					// 关闭定时器
}

//串口1的初始化
void USART1_Config(u32 baud)
{
  USART_InitTypeDef USART_InitStructure;
  NVIC_InitTypeDef NVIC_InitStructure;
  GPIO_InitTypeDef GPIO_InitStructure;
  
  //1.打开GPIO端口  PA9 PA10  
  RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);
  
  //2.打开串口时钟  USART1 -- APB2
  RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);
  
  //3.选择引脚的复用功能
  GPIO_PinAFConfig(GPIOA, GPIO_PinSource9  , GPIO_AF_USART1);
  GPIO_PinAFConfig(GPIOA, GPIO_PinSource10 , GPIO_AF_USART1);
  
  //4.配置GPIO引脚参数并初始化
  GPIO_InitStructure.GPIO_Mode 	= GPIO_Mode_AF;			 			//复用模式
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz; 			//输出速度
  GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;		 			//推挽复用
  GPIO_InitStructure.GPIO_PuPd  = GPIO_PuPd_UP;			 			//上拉电阻
  GPIO_InitStructure.GPIO_Pin   = GPIO_Pin_10|GPIO_Pin_9;	//引脚编号
  GPIO_Init(GPIOA, &GPIO_InitStructure);
  
	//5.配置USART1的参数并初始化
  USART_InitStructure.USART_BaudRate 		= baud;										//波特率
  USART_InitStructure.USART_WordLength 	= USART_WordLength_8b;		//数据位
  USART_InitStructure.USART_StopBits 		= USART_StopBits_1;				//停止位
  USART_InitStructure.USART_Parity 			= USART_Parity_No;				//检验位
  USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;	//无硬件流控
  USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx; //收发模式
  USART_Init(USART1, &USART_InitStructure);
  
  //6.配置中断参数并初始化
  NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;								//中断通道编号
  NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;				//抢占优先级
  NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;							//子优先级
  NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;									//打开中断通道
  NVIC_Init(&NVIC_InitStructure);
  
	//7.选择中断源   接收到数据则触发中断
	USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);

  //8.打开USART1
  USART_Cmd(USART1, ENABLE);
}



//发送字符串的函数
void USART1_SendString(char *str)
{ 
	//循环发送字符
  while( *str != '\0' )
	{
		USART_SendData(USART1,*str++);
		while( USART_GetFlagStatus(USART1,USART_FLAG_TXE) == RESET ); //等待发送数据寄存器为空  表示发送完成		
	}
}

//IIC时序: 起始信号 + 发送字节 + 从机应答 + 读取字节 + 主机应答 + 停止信号

//IIC的初始化
void IIC_Config(void)
{
	 GPIO_InitTypeDef GPIO_InitStructure;
		
	 RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE);
	
	 GPIO_InitStructure.GPIO_Mode  = GPIO_Mode_OUT;			 			//输出模式
   GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz; 			//输出速度
   GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;		 			  //推挽复用
   GPIO_InitStructure.GPIO_PuPd  = GPIO_PuPd_UP;			 			//上拉电阻
	
   GPIO_InitStructure.GPIO_Pin   = GPIO_Pin_9;							//引脚编号
   GPIO_Init(GPIOB, &GPIO_InitStructure);
	
	 GPIO_InitStructure.GPIO_Pin   = GPIO_Pin_8;							//引脚编号
   GPIO_Init(GPIOB, &GPIO_InitStructure);
	
	 //确保SDA和SCL都处于空闲状态
	 IIC_SDA(1);
	 IIC_SCL(1);
}	

//SDA设置为输出模式
static void IIC_SDASetOutputMode(void)
{
	 GPIO_InitTypeDef GPIO_InitStructure;
		
	 RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE);
	
	 GPIO_InitStructure.GPIO_Mode  = GPIO_Mode_OUT;			 			//输出模式
   GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz; 			//输出速度
   GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;		 			  //推挽复用
   GPIO_InitStructure.GPIO_PuPd  = GPIO_PuPd_UP;			 			//上拉电阻
	
   GPIO_InitStructure.GPIO_Pin   = GPIO_Pin_9;							//引脚编号
   GPIO_Init(GPIOB, &GPIO_InitStructure);

}

//SDA设置为输入模式
static void IIC_SDASetInputMode(void)
{
	 GPIO_InitTypeDef GPIO_InitStructure;
		
	 RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE);
	
	 GPIO_InitStructure.GPIO_Mode  = GPIO_Mode_IN;			 			//输入模式
   GPIO_InitStructure.GPIO_PuPd  = GPIO_PuPd_UP;			 			//上拉电阻
	
   GPIO_InitStructure.GPIO_Pin   = GPIO_Pin_9;							//引脚编号
   GPIO_Init(GPIOB, &GPIO_InitStructure);

}

//IIC的开始信号
void IIC_Start(void)
{
	//1.SDA引脚设置为输出模式
	IIC_SDASetOutputMode();
	
	//2.确保SDA和SCL为空闲状态  高电平
	IIC_SDA(1);
	IIC_SCL(1);
	delay_us(5);
	
	//3.在SCL高电平期间,把SDA电平拉低
	IIC_SDA(0);
	delay_us(5);
	
	//4.把SCL时钟线的电平拉低
	IIC_SCL(0);
	delay_us(5);
}

//IIC发送字节
void IIC_SendByte(uint8_t data)
{
	uint8_t i = 0;
	
	//1.SDA引脚设置为输出模式
	IIC_SDASetOutputMode();

	//2.把SCL时钟线电平拉低
	IIC_SCL(0);
	delay_us(5);
	
	//3.循环发送8bit数据
	for(i=0;i<8;i++)
	{
		//4.分析待发送数据的最高位 MSB
		if( data & 0x80 )
			IIC_SDA(1);
		else
			IIC_SDA(0);
		
		data <<= 1; //数据左移1bit
		delay_us(5);
		
		//5.把SCL时钟线的电平拉高,此时从机可以读取
		IIC_SCL(1);
		delay_us(5);
		
		//6.把SCL时钟线的电平拉低,此时主机可以准备
		IIC_SCL(0);
		delay_us(5);
	}
}


//IIC的等待从器件应答
bool IIC_WaitSlaveAck(void)
{
	//1.设置SDA数据线为输入模式
	IIC_SDASetInputMode();
	
	//2.把SCL时钟线的电平拉低,此时从机可以准备
	IIC_SCL(0);
	delay_us(5);
	
	//3.把SCL时钟线的电平拉高,此时从机可以读取
	IIC_SCL(1);
	delay_us(5);
	
	//4.主器件读取SDA数据线的电平状态
	if( IIC_READ )
		return false;
	else
		return true;

}

//IIC读取字节
uint8_t IIC_ReadByte(void)
{
	uint8_t data = 0;
	uint8_t i = 0;
	
	//1.设置SDA数据线为输入模式
	IIC_SDASetInputMode();
	
	
	//2.把SCL时钟线的电平拉低,此时从机可以准备
	IIC_SCL(0);
	delay_us(5);
	
	//3.循环读取8bit数据并存储在变量
	for(i=0;i<8;i++)
	{
		//4.把SCL时钟线的电平拉高,此时主机可以读取
		IIC_SCL(1);
		delay_us(5);
		
		//5.读取SDA数据线的电平状态  MSB
		data <<= 1;
		data |= IIC_READ;
		
		//6.把SCL时钟线的电平拉低,此时从机可以准备
	  IIC_SCL(0);
	  delay_us(5);
	}
	
	return data;
}

//IIC主机发送应答
void IIC_MasterSendAck(uint8_t ack)
{
	//1.SDA引脚设置为输出模式
	IIC_SDASetOutputMode();
	
	//2.把SCL时钟线的电平拉低,此时主机可以准备
	IIC_SCL(0);
	delay_us(5);
	
	//3.主机进行应答
	if( ack )
		IIC_SDA(1); //表示不应答
	else
		IIC_SDA(0); //表示应答了
	
	delay_us(5);
	
	//4.把SCL时钟线的电平拉高,此时从机可以读取
	IIC_SCL(1);
	delay_us(5);
}


//IIC发送停止信号
void IIC_Stop(void)
{
	//1.SDA引脚设置为输出模式
	IIC_SDASetOutputMode();
	
	//2.确保SDA和SCL都输出低电平
	IIC_SDA(0);
	IIC_SCL(0);
	delay_us(5);
	
	//3.把SCL时钟线的电平拉高
	IIC_SCL(1);
	delay_us(5);
	
	//4.把SDA时钟线的电平拉高
	IIC_SDA(1);
	delay_us(5);
}

//AT24C02初始化
void AT24C02_Config(void)
{
	IIC_Config();
}

//AT24C02字节写入指令
void AT24C02_ByteWrite(uint8_t Addr,uint8_t Data)
{
	//1.主机发送开始信号
	IIC_Start();
	
	//2.主机发送器件地址
	IIC_SendByte(0xA0);
	
	//3.等待从机应答
	while( IIC_WaitSlaveAck() == false );
	
	//4.主机发送存储地址
	IIC_SendByte(Addr);
	
	//5.等待从机应答
	while( IIC_WaitSlaveAck() == false );
	
	//6.主机发送存储内容
	IIC_SendByte(Data);
	
	//7.等待从机应答
	while( IIC_WaitSlaveAck() == false );
	
	//8.主机发送停止信号
	IIC_Stop();
}



//程序入口
int main()
{

	
	//1.硬件初始化
	USART1_Config(9600); 
	AT24C02_Config();
	

	while(1)
	{	
		
	
	}
}

void USART1_IRQHandler(void)
{
	uint8_t data = 0;
	//判断中断是否触发
	if( USART_GetITStatus(USART1, USART_IT_RXNE) == SET )
	{
		data = USART_ReceiveData(USART1); //一次只能接收1个字节
		USART_SendData(USART1,data);      //通过USART1发送1字节
	}
}




posted @ 2024-11-13 19:57  WJnuHhail  阅读(68)  评论(0)    收藏  举报