利用IO口模拟IIC时序控制AT24C02

点击查看代码
/**
   *********************************************************************************
   * @file    main.c 
   * @author  
   * @version 
   * @date    2024/07/18
   * @brief   利用IO口模拟
						  
					SCL  --  PB8  输出模式
					SDA  --  PB9  输出模式/输入模式
					GND  --  接地
					VCC  --  3.3V
   *********************************************************************************
**/

#include "stm32f4xx.h"  //必须包含
#include <stdio.h>
#include <stdbool.h>
#include "delay.h"


#define  SDA_SET(n)  (n) ? GPIO_SetBits(GPIOB,GPIO_Pin_9) : GPIO_ResetBits(GPIOB,GPIO_Pin_9) 
#define  SCL_SET(n)  (n) ? GPIO_SetBits(GPIOB,GPIO_Pin_8) : GPIO_ResetBits(GPIOB,GPIO_Pin_8) 

#define  SDA_READ    GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_9)

//前台程序就是中断服务程序,该程序是不需要手动调用的,当中断触发之后CPU会自动跳转过来执行该函数
void USART1_IRQHandler(void)
{
	uint8_t data;
  //判断中断是否发生
  if (USART_GetITStatus(USART1, USART_IT_RXNE) == SET)
  {   
		//从USART1中接收一个字节
		data = USART_ReceiveData(USART1);  //一次只能接收一个字节   
		
		//把接收到的数据转发出去
		USART_SendData(USART1,data);
  }
}

void USART1_Config(u32 baud)
{
	USART_InitTypeDef USART_InitStructure;
	NVIC_InitTypeDef NVIC_InitStructure;
	GPIO_InitTypeDef GPIO_InitStructure;
	
	//打开了GPIO端口时钟  PA9和PA10
	RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);
	
	//打开USART1的时钟
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);
	
	//选择GPIO引脚的复用功能
	GPIO_PinAFConfig(GPIOA, GPIO_PinSource9 , GPIO_AF_USART1);
  GPIO_PinAFConfig(GPIOA, GPIO_PinSource10, GPIO_AF_USART1);
	
	//配置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_9|GPIO_Pin_10;
	GPIO_Init(GPIOA, &GPIO_InitStructure);

	//配置串口参数+对串口初始化
	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);

	//配置NVIC参数 + 对NVIC初始化
	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);
	
	//选择USART1的中断源,接收到数据则触发中断
	USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);
	
	//打开串口
	USART_Cmd(USART1, ENABLE);
}


//利用串口发送一个字符串
void  USART1_SendString(const char *str)
{
	while(*str)
	{
		 USART_SendData(USART1,*str++);
		 while( USART_GetFlagStatus(USART1,USART_FLAG_TXE) == RESET );		
	}
}

//SCL引脚设置
void IIC_SCLConfig(void)
{
	GPIO_InitTypeDef GPIO_InitStructure;

	//打开GPIO端口的时钟
	RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB,ENABLE);

	//SCL
	GPIO_InitStructure.GPIO_Mode 	= GPIO_Mode_OUT;					//输出模式
	GPIO_InitStructure.GPIO_Speed 	= GPIO_Speed_50MHz;
	GPIO_InitStructure.GPIO_OType 	= GPIO_OType_PP;
	GPIO_InitStructure.GPIO_PuPd  	= GPIO_PuPd_UP;
	GPIO_InitStructure.GPIO_Pin 	= GPIO_Pin_8;
	GPIO_Init(GPIOB, &GPIO_InitStructure);
}

//SDA引脚设置
void IIC_SDAConfig(GPIOMode_TypeDef GPIO_Mode)
{
	GPIO_InitTypeDef GPIO_InitStructure;

	//打开GPIO端口的时钟
	RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB,ENABLE);

	//SCL
	GPIO_InitStructure.GPIO_Mode 	= GPIO_Mode;					
	GPIO_InitStructure.GPIO_Speed 	= GPIO_Speed_50MHz;
	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);
}

//IIC的初始化
void IIC_Config(void)
{
	//1.设置SDA和SCL为输出模式
	IIC_SCLConfig();
	IIC_SDAConfig(GPIO_Mode_OUT);

	//2.确保SDA和SCL处于空闲状态
	SDA_SET(1);
	SCL_SET(1);
	delay_us(5);
}



//IIC的开始信号
void IIC_Start(void)
{
	//1.设置SDA引脚为输出模式
	IIC_SDAConfig(GPIO_Mode_OUT);

	//2.确保SDA和SCL处于空闲状态
	SDA_SET(1);
	SCL_SET(1);
	delay_us(5);

	//3.把SDA引脚电平拉低
	SDA_SET(0);
	delay_us(5);

	//4.把SCL引脚电平拉低,此时准备数据
	SCL_SET(0);
	//delay_us(5);
	
}

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

	//2.把SCL引脚电平拉低,此时主机准备数据
	SCL_SET(0);
	delay_us(5);

	//3.循环发送8bit,遵循MSB高位先出
	for(i=0;i<8;i++)
	{
		//4.判断待发送的字节的最高位
		if( Byte & 0x80 )
		{
			SDA_SET(1);
		}
		else
			SDA_SET(0);

		Byte <<= 1;
		delay_us(5);

		//5.把SCL电平拉高,此时从机读取bit
		SCL_SET(1);
		delay_us(5);

		//6.把SCL引脚电平拉低,此时主机准备下一个bit
		SCL_SET(0);
		delay_us(5);
	}
}

//判断从机是否应答
bool  IIC_IsSlaveACK(void)
{
	bool ack;
	
	//1.设置SDA引脚为输入模式
	IIC_SDAConfig(GPIO_Mode_IN);

	//2.把SCL引脚电平拉低,此时为第9个脉冲的低电平,从机准备bit
	SCL_SET(0);
	delay_us(5);

	//3.把SCL引脚电平拉高,此时为第9个脉冲的高电平,主机读取状态
	SCL_SET(1);
	delay_us(5);

	//4.主机读取状态  1 表示未应答  0  表示已应答
	if(SDA_READ)
		ack = false;
	else
		ack = true;
	
	//5.把SCL引脚电平拉低
	SCL_SET(0);
	delay_us(5);
	
	return ack;
}


//IIC读取字节
uint8_t IIC_ReadByte(void)
{
	uint8_t i = 0,data = 0;

	//1.设置SDA引脚为输入模式
	IIC_SDAConfig(GPIO_Mode_IN);

	//2.把SCL引脚电平拉低,此时从机准备数据
	SCL_SET(0);
	delay_us(5);

	//3.循环读取8bit,遵循MSB高位先出
	for (i = 0; i < 8; ++i)
	{
		//4.把SCL电平拉高,此时主机读取bit
		SCL_SET(1);
		delay_us(5);

		//5.主机读取bit
		data <<= 1;
		data |= SDA_READ;
		//delay_us(5);

		//6.把SCL引脚电平拉低,此时从机准备下一个bit数据
		SCL_SET(0);
		delay_us(5);
	}

	//7.返回结果
	return data;
}

//ack=1 表示不应答  ack=0 表示要应答
void IIC_MasterACK(uint8_t ack)
{
	//1.设置SDA引脚为输出模式
	IIC_SDAConfig(GPIO_Mode_OUT);

	//2.把SCL引脚电平拉低,此时主机准备
	SCL_SET(0);
	delay_us(5);

	//3.判断ack的状态,从而对SDA操作
	if(ack)
		SDA_SET(1);
	else
		SDA_SET(0);

	delay_us(5);

	//4.把SCL电平拉高,此时从机读取bit
	SCL_SET(1);
	delay_us(5);
	
	//5.把SCL引脚电平拉低
	SCL_SET(0);
	delay_us(5);
}

//IIC的停止信号
void IIC_Stop(void)
{
	//1.设置SDA引脚为输出模式
	IIC_SDAConfig(GPIO_Mode_OUT);

	//2.设置SDA和SCL均为低电平
	SDA_SET(0);
	SCL_SET(0);
	delay_us(5);

	//3.把SCL电平拉高
	SCL_SET(1);
	delay_us(5);

	//4.把SDA电平拉高
	SDA_SET(1);
	delay_us(5);
}

void AT24C02_Config(void)
{
	IIC_Config();
}

//AT24C02字节写入的指令
bool AT24C02_ByteWrite(uint8_t Addr,uint8_t Byte)
{
	//1.开始信号
	IIC_Start();
	
	//2.器件地址
	IIC_SendByte(0xA0);
	
	//3.从机应答
	if( IIC_IsSlaveACK() == false) 
	{
		  IIC_Stop();
			USART1_SendString("Slave Is Not ACK:Device Address Error\r\n");
		  return false;
	}
	
	//4.存储地址
	IIC_SendByte(Addr);
	
	//5.从机应答
	if( IIC_IsSlaveACK() == false) 
	{
		  IIC_Stop();
			USART1_SendString("Slave Is Not ACK:Memory Address Error\r\n");
		  return false;
	}
	//6.发送数据
	IIC_SendByte(Byte);
	
	//7.从机应答
	if( IIC_IsSlaveACK() == false) 
	{
		  IIC_Stop();
			USART1_SendString("Slave Is Not ACK:Data Error\r\n");
		  return false;
	}
	//8.停止信号
	IIC_Stop();
	
	//9.延时5ms
	delay_ms(5);
	
	return true;
}

//AT24C02的当前地址读指令
uint8_t  AT24C02_CurrentAddressRead(void)
{
	uint8_t recv = 0;
	
	//1.开始信号
	IIC_Start();
	
	//2.器件地址
	IIC_SendByte(0xA1);
	
	//3.从机应答
	if( IIC_IsSlaveACK() == false) 
	{
		  IIC_Stop();
			USART1_SendString("Slave Is Not ACK:Device Address Error\r\n");
		  return false;
	}
	
	//4.读取字节
	recv = IIC_ReadByte();
	
	//5.发送应答
	IIC_MasterACK(0x01);
	
	//6.停止信号
	IIC_Stop();
	
	return recv;
}

//AT24C02的随机地址读指令
uint8_t  AT24C02_RandomAddressRead(uint8_t DestAddr)
{
	uint8_t recv = 0;
	
	//1.开始信号
	IIC_Start();
	
	//2.器件地址
	IIC_SendByte(0xA0);
	
	//3.从机应答
	if( IIC_IsSlaveACK() == false) 
	{
		  IIC_Stop();
			USART1_SendString("Slave Is Not ACK:Device Address Error\r\n");
		  return false;
	}
	
	//4.存储地址
	IIC_SendByte(DestAddr);
	
	//5.从机应答
	if( IIC_IsSlaveACK() == false) 
	{
		  IIC_Stop();
			USART1_SendString("Slave Is Not ACK:Memory Address Error\r\n");
		  return false;
	}
	
	//6.开始信号
	IIC_Start();
	
	//7.器件地址
	IIC_SendByte(0xA1);
	
	//8.从机应答
	if( IIC_IsSlaveACK() == false) 
	{
		  IIC_Stop();
			USART1_SendString("Slave Is Not ACK:Device Address Error\r\n");
		  return false;
	}
	
	//9.读取字节
	recv = IIC_ReadByte();
	
	//10.发送应答
	IIC_MasterACK(0x01);
	
	//11.停止信号
	IIC_Stop();
	
	return recv;

}


int main()
{
	uint8_t data = 0;
	uint8_t buf[128] ={0};
	//1.硬件的初始化
	USART1_Config(9600);
	AT24C02_Config();
	
	//2.向AT24C02的0x10地址下写入字符'A'
	AT24C02_ByteWrite(0x00,'A');

	
	//3.读取AT4C02的0x00地址下的数据
	data = AT24C02_RandomAddressRead(0x00);
	sprintf((char *)buf,"read byte is %c\r\n",data);
	USART1_SendString((char *)buf);
	
	while(1)
	{
		
	}
}





posted @ 2025-08-30 12:32  w1888  阅读(0)  评论(0)    收藏  举报