基于STM32F407(模拟&硬件)I2C通讯

核心板:STM32F407

实验目的:通过I2C来读写EEPROM。 


目录

EEPROM读写相关数据的协议 

I2C基本协议层

I2C基本读写

I2C起始信号与终止信号

I2C数有效性 

I2C响应 与非响应 

模拟I2C代码

模拟I2C代码相关定义

延时代码 

起始信号代码

停止信号代码

发送一个字节代码

接收一个字节代码

等待从机应答代码

主机应答代码

主机非应答代码

等待EEPROM写入完成代码

引脚初始化代码

写一页数据代码

写大量数据代码

读大量数据代码

硬件I2C代码

硬件主发送、接收时序图

硬件I2C代码相关定义

I2C相关配置代码

等待EEPROM写入完成代码

写入一个字节代码

读一个字节代码

写一页数据代码

写大量数据代码

读大量数据代码


EEPROM读写相关数据的协议 

 


I2C基本协议层

I2C基本读写

I2C起始信号与终止信号

I2C数有效性 

I2C响应 与非响应 

模拟I2C代码

模拟I2C代码相关定义

#define EEPROM_I2C_Write              0xA0        //EEPROM的设备地址(1010 000) + 0(写)
#define EEPROM_I2C_Read               0xA1        //EEPROM的设备地址(1010 000) + 1(读)
#define EEPROM_PAGE_SIZE 			  8

/* I2C_SCL——PB8 */	
#define I2C_SCL_PIN  								GPIO_Pin_8
#define I2C_SCL_GPIO_PORT							GPIOB
#define I2C_SCL_GPIO_CLK							RCC_AHB1Periph_GPIOB


/* I2C_SDA——PB9 */
#define I2C_SDA_PIN  								GPIO_Pin_9
#define I2C_SDA_GPIO_PORT							GPIOB
#define I2C_SDA_GPIO_CLK							RCC_AHB1Periph_GPIOB


#define I2C_SCL_1()  GPIO_SetBits(I2C_SCL_GPIO_PORT, I2C_SCL_PIN)		/* SCL = 1 */
#define I2C_SCL_0()  GPIO_ResetBits(I2C_SCL_GPIO_PORT, I2C_SCL_PIN)		/* SCL = 0 */

#define I2C_SDA_1()  GPIO_SetBits(I2C_SDA_GPIO_PORT, I2C_SDA_PIN)		/* SDA = 1 */
#define I2C_SDA_0()  GPIO_ResetBits(I2C_SDA_GPIO_PORT, I2C_SDA_PIN)		/* SDA = 0 */

#define I2C_SDA_READ()  GPIO_ReadInputDataBit(I2C_SDA_GPIO_PORT, I2C_SDA_PIN)	/* 读SDA口线状态 */


void I2C_Buff_Read(uint8_t ADDR , uint8_t *Data,uint16_t size);
void I2C_Buff_Write(uint8_t ADDR , uint8_t *Data,uint16_t size);

void I2C_GPIO_Config(void);

延时代码 

static void I2C_Delay(void)
{
	uint8_t i;

	/* 
		可用逻辑分析仪测量I2C通讯时的频率
    工作条件:CPU主频168MHz ,MDK编译环境,1级优化
  
		经测试,循环次数为20~250时都能通讯正常

	*/
	for (i = 0; i < 40; i++);
}

起始信号代码

void I2C_Start(void)
{
	/* 当SCL高电平时,SDA出现一个下跳沿表示I2C总线启动信号 */
	I2C_SDA_1();
	I2C_SCL_1();
	I2C_Delay();
	I2C_SDA_0();
	I2C_Delay();
	I2C_SCL_0();//钳住I2C总线(防止数据错误)
	I2C_Delay();
}

        注意:最后一定要将SCL拉低,否则数据错误。(因为SCL为高电平SDA数有效,则发送从机设备地址时必然出错) 

停止信号代码

void I2C_Stop(void)
{
	/* 当SCL高电平时,SDA出现一个上跳沿表示I2C总线停止信号 */
	I2C_SDA_0();
	I2C_SCL_1();
	I2C_Delay();
	I2C_SDA_1();
}

发送一个字节代码

void I2C_SendByte(uint8_t data)
{
	uint8_t i;

	/* I2C先发送字节的高位bit7(SCL为高电平时SDA电平有效) */
	for (i = 0; i < 8; i++)
	{		
		if (data & 0x80)
		{
			I2C_SDA_1();
		}
		else
		{
			I2C_SDA_0();
		}
		
		I2C_Delay();
		I2C_SCL_1();
		I2C_Delay();
		I2C_SCL_0();
		
		if (i == 7)
		{
			 I2C_SDA_1(); // 释放总线,交给从机来应答
		}
		data <<= 1;	/* 左移一个bit */
		I2C_Delay();
	}
}

        注意:发送完最后一次时主机要释放对SDA控制权(将SDA拉高),交给从机来发送应答与非应答信号。 

接收一个字节代码

uint8_t I2C_ReadByte(void)
{
	uint8_t i;
	uint8_t value = 0;

	/* EEPROM先发高位 */

	for (i = 0; i < 8; i++)
	{
		value <<= 1;
		I2C_SCL_1();
		I2C_Delay();
		if (I2C_SDA_READ())//如果发来的数据为1
		{
			value++;
		}
		I2C_SCL_0();//切换电平准备接收下一个字节
		I2C_Delay();
	}
	return value;
}

等待从机应答代码

uint8_t I2C_WaitAck(void)
{
	uint8_t re;

	I2C_SDA_1();	/* CPU释放SDA总线,把控制权交给从机 */
	I2C_Delay();
	I2C_SCL_1();	/* CPU驱动SCL = 1, 此时器件会返回ACK应答 */
	I2C_Delay();
	if (I2C_SDA_READ())	/* CPU读取SDA口线状态 */
	{
		re = 1;
	}
	else//应答
	{
		re = 0;
	}
	I2C_SCL_0();//拉低,为了下次数据的正确
	I2C_Delay();
	return re;
}

          注意:最后一定要将SCL拉低,否则数据错误。

主机应答代码

void I2C_Ack(void)
{
	/* SCL为高电平,SDA为低电平为应答 */
	I2C_SDA_0();	/* CPU驱动SDA = 0 */
	I2C_Delay();
	I2C_SCL_1();	/* CPU产生1个时钟 */
	I2C_Delay();
	I2C_SCL_0();
	I2C_Delay();
	I2C_SDA_1();	/* CPU释放SDA总线 *///不释放总线会导致从机发来的数据均为0
}

        注意:要释放总线 (将SDA拉高)。

主机非应答代码

void I2C_NAck(void)
{
	/* SCL为高电平,SDA为高电平为非应答 */
	I2C_SDA_1();	/* CPU驱动SDA = 1 */
	I2C_Delay();
	I2C_SCL_1();	/* CPU产生1个时钟 */
	I2C_Delay();
	I2C_SCL_0();
	I2C_Delay();
}

等待EEPROM写入完成代码

void Wait_For_Writed()
{
	uint32_t TIME_OUT = 0xFFFFF;
	
	while(TIME_OUT--)//一直呼叫从机,只有当从机写完后才会响应我们
	{
		I2C_Start();
		
		I2C_SendByte(EEPROM_I2C_Write);//发送写指令
		
		if(I2C_WaitAck() == 0)//等待从机响应
		{
			I2C_Stop();
			return ;
		}
	}
	I2C_Stop();
}

        注意:一定要等写完,否则会卡死或者数据错误。 

        下图摘自AT24C02手册

 

 

引脚初始化代码

void I2C_GPIO_Config(void)
{
	GPIO_InitTypeDef GPIO_InitStructure;

	RCC_AHB1PeriphClockCmd(I2C_SCL_GPIO_CLK | I2C_SDA_GPIO_CLK, ENABLE);	/* 打开GPIO时钟 */

	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;  	
	GPIO_InitStructure.GPIO_OType = GPIO_OType_OD;  	/* 开漏输出 */
	GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
	
	GPIO_InitStructure.GPIO_Pin = I2C_SCL_PIN;
	GPIO_Init(I2C_SCL_GPIO_PORT, &GPIO_InitStructure);
	
	GPIO_InitStructure.GPIO_Pin = I2C_SDA_PIN;
	GPIO_Init(I2C_SDA_GPIO_PORT, &GPIO_InitStructure);

	/* 给一个停止信号, 复位I2C总线上的所有设备到待机模式 */
	I2C_Stop();
}

写一页数据代码

void I2C_Page_Write(uint8_t ADDR,uint8_t *Data,uint16_t size) 
{
	I2C_Start();
	
	I2C_SendByte(EEPROM_I2C_Write);//发送写指令
	
	while(I2C_WaitAck());//等待从机响应
	
	I2C_SendByte(ADDR);//发送写在哪
	
	while(I2C_WaitAck());//等待从机响应
	
	while(size--)//发送数据
	{
		I2C_SendByte(*Data);
		while(I2C_WaitAck());//等待从机响应
		Data++;
	}
	
	I2C_Stop();
	
	Wait_For_Writed();//等待写入完成
}

        注意:一定要等待一会,要不然数卡死或者数据错误。 

写大量数据代码

void I2C_Buff_Write(uint8_t ADDR , uint8_t *Data,uint16_t size)
{
	uint16_t num_of_page = size / EEPROM_PAGE_SIZE;//算出来有几页
	uint16_t single_byte = size % EEPROM_PAGE_SIZE;//算出来有几个单个的字节数
	uint16_t single_addr = ADDR % EEPROM_PAGE_SIZE;//算出来这一页有几个位置
	
	if(single_addr == 0)//如果地址对齐
	{
		while(num_of_page--)
		{
			I2C_Page_Write(ADDR,Data,EEPROM_PAGE_SIZE) ;
			
			ADDR += EEPROM_PAGE_SIZE;
			Data += EEPROM_PAGE_SIZE;
		}
		I2C_Page_Write(ADDR,Data,single_byte) ;
	}
	else 
	{
		/* 第一次写入的数据字节数(为了让地址对齐) */
		uint16_t first_write_size = EEPROM_PAGE_SIZE - single_addr;//算出来这一页还差几个位置不全一页
		
		I2C_Page_Write(ADDR,Data,first_write_size);//补全
		
		uint16_t surplus = size - first_write_size;//剩余的数据字节数
		Data += first_write_size;
		ADDR += first_write_size;
		
		num_of_page = surplus / EEPROM_PAGE_SIZE;//算出来有几页
		single_byte = surplus % EEPROM_PAGE_SIZE;//算出来有几个单个的字节数
		
		while(num_of_page--)
		{
			I2C_Page_Write(ADDR,Data,EEPROM_PAGE_SIZE) ;
			
			ADDR += EEPROM_PAGE_SIZE;
			Data += EEPROM_PAGE_SIZE;
		}
		I2C_Page_Write(ADDR,Data,single_byte) ;
	}
}

读大量数据代码

void I2C_Buff_Read(uint8_t ADDR , uint8_t *Data,uint16_t size)
{
	I2C_Start();
	
	I2C_SendByte(EEPROM_I2C_Write);//发送写指令
	
	while(I2C_WaitAck());//等待从机响应
	
	I2C_SendByte(ADDR);//发送写在哪
	
	while(I2C_WaitAck());//等待从机响应
	
	I2C_Start();
	
	I2C_SendByte(EEPROM_I2C_Read);//发送读指令
	
	while(I2C_WaitAck());//等待从机响应
	
	while(size--)
	{
		*Data = I2C_ReadByte();//读取数据
		
		if(size == 0)//接收了最后一个数据主机发送非应答
			I2C_NAck();
		else
			I2C_Ack();
		Data++;
	}
	
	I2C_Stop();
}

硬件I2C代码

硬件主发送、接收时序图

        (摘自stm32f407中文手册)

硬件I2C代码相关定义

#define EEPROM_I2C_ADDR                	0xA0       
#define EEPROM_PAGE_SIZE 				8

#define I2C								I2C1
#define I2C_CLK 						RCC_APB1Periph_I2C1
#define I2C_Speed				 		400000
#define I2C_OWN_ADDR					0x78			//地址只需要不同于总线上的其他设备地址即可

/* I2C_SCL——PB8 */	
#define I2C_SCL_PIN  					GPIO_Pin_8
#define I2C_SCL_GPIO_PORT				GPIOB
#define I2C_SCL_GPIO_CLK				RCC_AHB1Periph_GPIOB
#define I2C_SCL_SOURCE					GPIO_PinSource8
#define I2C_SCL_AF						GPIO_AF_I2C1

/* I2C_SDA——PB9 */
#define I2C_SDA_PIN  					GPIO_Pin_9
#define I2C_SDA_GPIO_PORT				GPIOB
#define I2C_SDA_GPIO_CLK				RCC_AHB1Periph_GPIOB
#define I2C_SDA_SOURCE					GPIO_PinSource9
#define I2C_SDA_AF						GPIO_AF_I2C1

void I2C_Config(void);

void I2C_Byte_Write(uint8_t ADDR , uint8_t Data);//向EEPROM中ADDR地址写入一个字节的数据
void I2C_Random_Read(uint8_t ADDR , uint8_t *Data);//随机读取EEPROM中ADDR地址的数据(一个字节)

void I2C_Page_Write(uint8_t ADDR,uint8_t *Data,uint16_t size) ;//写入一页的数据


void I2C_Buffer_Write(uint8_t ADDR , uint8_t *Data,uint16_t size);//批量写入数据
void I2C_Buffer_Read(uint8_t ADDR , uint8_t *Data,uint16_t size);//批量读取数据

I2C相关配置代码

void I2C_Config(void)
{
	GPIO_InitTypeDef GPIO_InitStructure;
	I2C_InitTypeDef I2C_InitStructure;
	
	/* 使能 I2C 引脚的 GPIO 时钟 */
	RCC_AHB1PeriphClockCmd(I2C_SCL_GPIO_CLK | I2C_SDA_GPIO_CLK,ENABLE);
	/* 使能 I2C 时钟 */
	RCC_APB1PeriphClockCmd(I2C_CLK,ENABLE);
	
	/* 将对应的IO口连接到外设,开始启动复用功能 */
	GPIO_PinAFConfig(I2C_SCL_GPIO_PORT,I2C_SCL_SOURCE,I2C_SCL_AF);
	GPIO_PinAFConfig(I2C_SDA_GPIO_PORT,I2C_SDA_SOURCE,I2C_SDA_AF);
	
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF ;//复用功能
	GPIO_InitStructure.GPIO_OType = GPIO_OType_OD ;//开漏输出
	GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP ;//上拉
	GPIO_InitStructure.GPIO_Speed = GPIO_High_Speed ;//高速
	
	GPIO_InitStructure.GPIO_Pin = I2C_SCL_PIN;//配置发送引脚
	GPIO_Init (I2C_SCL_GPIO_PORT,&GPIO_InitStructure);
	
	GPIO_InitStructure.GPIO_Pin = I2C_SDA_PIN;//配置发送引脚
	GPIO_Init (I2C_SDA_GPIO_PORT,&GPIO_InitStructure);
	
	I2C_InitStructure.I2C_Ack = I2C_Ack_Enable ;
	I2C_InitStructure.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit ;
	I2C_InitStructure.I2C_ClockSpeed = I2C_Speed;
	I2C_InitStructure.I2C_DutyCycle = I2C_DutyCycle_2 ;
	I2C_InitStructure.I2C_Mode = I2C_Mode_I2C ;//I2C 模式
	I2C_InitStructure.I2C_OwnAddress1 = I2C_OWN_ADDR;
	
	I2C_Init(I2C, &I2C_InitStructure);
	
	I2C_Cmd(I2C,ENABLE);
}

等待EEPROM写入完成代码

/* 等待RRPROM内部写入完成 */
static void Wait_For_EEPROM(void)
{
	uint32_t check_count = 0xFFFFF;
	uint32_t count_wait  = 0xFFFF; 
	while(check_count--)
	{
			/* 产生一个起始信号 */ 
		I2C_GenerateSTART(I2C ,ENABLE);
		
		/* 等待EV5事件,直到成功 */
		while(I2C_CheckEvent(I2C, I2C_EVENT_MASTER_MODE_SELECT) != SUCCESS);
		
		/* 发送EEPROM设备的地址,设置为写方向 */
		I2C_Send7bitAddress(I2C, EEPROM_I2C_ADDR, I2C_Direction_Transmitter );
		
		/* 重置TIME_OUT */
		count_wait = 0xFFFF;
		/* 等待EV6事件,直到成功,说明EEPROM写完了,可以相应我们了 */
		while(count_wait --)//只有RRPROM写完了,我们寻址,他才会响应我们,否则是不会响应我们的
		{
			/* 若检测到响应,说明内部写时序完成,跳出等待函数 */
			if(I2C_CheckEvent(I2C, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED) == SUCCESS )
			{
				I2C_GenerateSTOP(I2C, ENABLE );//产生一个停止信号
				return ;
			}
		}
	}
	I2

写入一个字节代码

/* 向EEPROM中ADDR地址写入一个字节的数据 */
void I2C_Byte_Write(uint8_t ADDR , uint8_t Data)
{
	I2C_GenerateSTART(I2C, ENABLE);//产生一个起始信号

	while(I2C_CheckEvent(I2C, I2C_EVENT_MASTER_MODE_SELECT) != SUCCESS);//等待EV5事件产生
	
	I2C_Send7bitAddress(I2C, EEPROM_I2C_ADDR, I2C_Direction_Transmitter);//发送地址,为写方向

	while(I2C_CheckEvent(I2C, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED) != SUCCESS);//等待EV6事件产生

	I2C_SendData(I2C,  ADDR);//发送存放数据的地址

	/*(EV8事件是用来检测数据是否正在发送,发送完成会产生一个EV8_2事件,因此我们检测该事件)*/
	while(I2C_CheckEvent(I2C, I2C_EVENT_MASTER_BYTE_TRANSMITTED) != SUCCESS);//等待EV8_2事件产生
	
	I2C_SendData(I2C,  Data);//发送存放数据

	while(I2C_CheckEvent(I2C,I2C_EVENT_MASTER_BYTE_TRANSMITTED) != SUCCESS);//等待EV8_2事件产生

	I2C_GenerateSTOP(I2C, ENABLE);

	Wait_For_EEPROM();//等待EEPROM写入完成
}

读一个字节代码

/* 随机读取EEPROM中ADDR地址的数据,用Data返回 */
void I2C_Random_Read(uint8_t ADDR , uint8_t *Data)
{
	/* 第一个起始信号用于写入我们要读取数据的地址 */
	I2C_GenerateSTART(I2C, ENABLE);//产生一个起始信号

	while(I2C_CheckEvent(I2C, I2C_EVENT_MASTER_MODE_SELECT) != SUCCESS);//等待EV5事件产生
	
	I2C_Send7bitAddress(I2C, EEPROM_I2C_ADDR, I2C_Direction_Transmitter);//发送地址,为写方向

	while(I2C_CheckEvent(I2C, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED) != SUCCESS);//等待EV6事件产生
	
	I2C_SendData(I2C,  ADDR);//发送存放数据的地址

	/*(EV8事件是用来检测数据是否正在发送,发送完成会产生一个EV8_2事件,因此我们检测该事件)*/
	while(I2C_CheckEvent(I2C, I2C_EVENT_MASTER_BYTE_TRANSMITTED) != SUCCESS);//等待EV8_2事件产生
	
	
	/* 产生第二个信号,用于读取数据 */
	I2C_GenerateSTART(I2C, ENABLE);//产生一个起始信号

	while(I2C_CheckEvent(I2C, I2C_EVENT_MASTER_MODE_SELECT) != SUCCESS);//等待EV5事件产生
	
	I2C_Send7bitAddress(I2C, EEPROM_I2C_ADDR, I2C_Direction_Receiver);//发送地址,为写方向
	
	I2C_AcknowledgeConfig(I2C, DISABLE);//非应答信号
	
	while(I2C_CheckEvent(I2C, I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED) != SUCCESS);//等待EV6事件产生

	/* 先发非应答信号,然后在等待数据传送到DR寄存器,否则在停止前会接收到错误的数据 */
	while(I2C_CheckEvent(I2C, I2C_EVENT_MASTER_BYTE_RECEIVED) != SUCCESS);//等待EV7事件产生
	
	*Data = I2C_ReceiveData(I2C);
	
	I2C_GenerateSTOP(I2C, ENABLE);
}

        注意:在接收数据完成时要提前发送非应答数据 。

写一页数据代码

/* 写入一页的数据,size不可大于8,否则数据错误 */
void I2C_Page_Write(uint8_t ADDR,uint8_t *Data,uint16_t size) 
{
	I2C_GenerateSTART(I2C, ENABLE);//产生一个起始信号

	while(I2C_CheckEvent(I2C, I2C_EVENT_MASTER_MODE_SELECT) != SUCCESS);//等待EV5事件产生
	
	I2C_Send7bitAddress(I2C, EEPROM_I2C_ADDR, I2C_Direction_Transmitter);//发送地址,为写方向

	while(I2C_CheckEvent(I2C, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED) != SUCCESS);//等待EV6事件产生

	I2C_SendData(I2C,  ADDR);//发送存放数据的地址

	/*(EV8事件是用来检测数据是否正在发送,发送完成会产生一个EV8_2事件,因此我们检测该事件)*/
	while(I2C_CheckEvent(I2C, I2C_EVENT_MASTER_BYTE_TRANSMITTED) != SUCCESS);//等待EV8_2事件产生
	
	while(size--)
	{
		I2C_SendData(I2C, *Data);//发送存放数据的地址
		
		while(I2C_CheckEvent(I2C, I2C_EVENT_MASTER_BYTE_TRANSMITTED) != SUCCESS);//等待EV8_2事件产生
		
		Data++;
	}
	
	I2C_GenerateSTOP(I2C, ENABLE);

	Wait_For_EEPROM();//等待EEPROM写入完成
}

写大量数据代码

/* 批量写入数据 */
void I2C_Buffer_Write(uint8_t ADDR , uint8_t *Data,uint16_t size)
{
	uint16_t num_of_page = size / EEPROM_PAGE_SIZE;//算出来有几页
	uint16_t single_byte = size % EEPROM_PAGE_SIZE;//算出来有几个单个的字节数
	uint16_t single_addr = ADDR % EEPROM_PAGE_SIZE;//算出来这一页有几个位置
	
	if(single_addr == 0)//说明地址对齐
	{
		while(num_of_page--)
		{
			I2C_Page_Write(ADDR,Data,EEPROM_PAGE_SIZE) ;
			
			ADDR += EEPROM_PAGE_SIZE;
			Data += EEPROM_PAGE_SIZE;
		}
		I2C_Page_Write(ADDR,Data,single_byte) ;
	}
	else 
	{
		/* 第一次写入的数据字节数(为了让地址对齐) */
		uint16_t first_write_size = EEPROM_PAGE_SIZE - single_addr;//算出来这一页还差几个位置不全一页
		
		I2C_Page_Write(ADDR,Data,first_write_size);//补全
		
		uint16_t surplus = size - first_write_size;//剩余的数据字节数
		Data += first_write_size;
		ADDR += first_write_size;
		
		num_of_page = surplus / EEPROM_PAGE_SIZE;//算出来有几页
		single_byte = surplus % EEPROM_PAGE_SIZE;//算出来有几个单个的字节数
		
		while(num_of_page--)
		{
			I2C_Page_Write(ADDR,Data,EEPROM_PAGE_SIZE) ;
			
			ADDR += EEPROM_PAGE_SIZE;
			Data += EEPROM_PAGE_SIZE;
		}
		I2C_Page_Write(ADDR,Data,single_byte) ;
	}
}

读大量数据代码

/* 批量读取数据 */
void I2C_Buffer_Read(uint8_t ADDR , uint8_t *Data,uint16_t size)
{
	I2C_GenerateSTART(I2C, ENABLE);//产生一个起始信号

	while(I2C_CheckEvent(I2C, I2C_EVENT_MASTER_MODE_SELECT) != SUCCESS);//等待EV5事件产生
	
	I2C_Send7bitAddress(I2C, EEPROM_I2C_ADDR, I2C_Direction_Transmitter);//发送地址,为写方向

	while(I2C_CheckEvent(I2C, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED) != SUCCESS);//等待EV6事件产生
	
	I2C_SendData(I2C,  ADDR);//发送存放数据的地址

	/*(EV8事件是用来检测数据是否正在发送,发送完成会产生一个EV8_2事件,因此我们检测该事件)*/
	while(I2C_CheckEvent(I2C, I2C_EVENT_MASTER_BYTE_TRANSMITTED) != SUCCESS);//等待EV8_2事件产生
	
	
	/* 产生第二个信号,用于读取数据 */
	I2C_GenerateSTART(I2C, ENABLE);//产生一个起始信号

	while(I2C_CheckEvent(I2C, I2C_EVENT_MASTER_MODE_SELECT) != SUCCESS);//等待EV5事件产生
	
	I2C_Send7bitAddress(I2C, EEPROM_I2C_ADDR, I2C_Direction_Receiver);//发送地址,为写方向
	
	while(I2C_CheckEvent(I2C, I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED) != SUCCESS);//等待EV6事件产生
	
	while(size--)
	{
		if(size == 0)
			I2C_AcknowledgeConfig(I2C, DISABLE);//非应答信号
		else
			I2C_AcknowledgeConfig(I2C, ENABLE);//应答信号
		/* 读取数据 */
		while(I2C_CheckEvent(I2C, I2C_EVENT_MASTER_BYTE_RECEIVED) != SUCCESS);//等待EV7事件产生
			
		*Data++ = I2C_ReceiveData(I2C);
	}
	
	I2C_GenerateSTOP(I2C, ENABLE);
}

 主函数中(串口通讯请自行配置)

int main(void)
{
	uint8_t Tx_Buff[256];
	uint8_t Rx_Buff[256];
	
	USART_Config();
	
	I2C_Config();
	

	for(int i = 0;i<TEST_SIZE;i++)//给buff赋值
	{
		Tx_Buff[i] = i;
	}
	
	I2C_Buffer_Write(0x00 ,Tx_Buff,256);
	I2C_Buffer_Read(0x00 , Rx_Buff,256);
	
	for(int i = 0;i<TEST_SIZE;i++)//给buff赋值
	{
		printf("0x%02x ",Rx_Buff[i]);
	}

	printf("\n一切OK\n");
	


	while (1)
	{
	}
}

实验结果


 以上仅供自己与大家学习积累,欢迎各位大佬批评与指正!  

  • 4
    点赞
  • 47
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: STM32F407是一款强大的微控制器,可以通过软件模拟I2C总线来读写MPU6050传感器。首先,我们需要设置相关的GPIO引脚来模拟I2C的时钟(SCL)和数据线(SDA)。然后,我们可以通过相应的软件算法来模拟I2C的时序,并使用GPIO引脚来模拟信号的传输。 在进行软件模拟I2C读写MPU6050之前,我们需要先了解MPU6050的寄存器结构和通讯协议。MPU6050内部有多个寄存器,它们存储着各种传感器的原始数据和配置信息。通讯协议使用I2C,我们需要根据MPU6050的地址和寄存器地址来发送读写命令。 首先,发送开始信号。通过GPIO引脚模拟SCL和SDA,在SCL为高电平,SDA从高电平转为低电平时即可认为发送了开始信号。然后,发送MPU6050的设备地址,该地址为7位,由3位固定的值和一个可选择的读写位组成。接下来,发送要访问的寄存器地址,该地址也为7位。再发送读写命令位,0表示写入,1表示读取。 接下来,我们可以进行读写操作。对于写操作,我们发送要写入的数据到寄存器中。对于读操作,我们需要发送重复启动信号,然后再发送MPU6050的设备地址和寄存器地址,并设置读取命令位。此时,我们将把数据读取到SDA引脚上。 最后,发送停止信号。通过GPIO引脚模拟SCL和SDA,在SCL为高电平时将SDA从低电平转为高电平即可认为发送了停止信号。 以上是对于STM32F407软件模拟I2C读写MPU6050传感器的简要步骤介绍。具体实现过程中,需要根据实际需求进行相应的编码和调试。 ### 回答2: stm32f407是一款高性能的ARM Cortex-M4微控制器,可以通过软件模拟I2C总线来读写MPU6050传感器。 首先,我们需要了解MPU6050传感器的通信协议和寄存器地址。MPU6050使用I2C总线进行通信,需要通过开始信号、设备地址、寄存器地址、数据等步骤完成数据的读写。 在stm32f407上,我们可以使用GPIO引脚来模拟I2C总线的SCL(时钟线)和SDA(数据线)。我们需要配置SCL和SDA引脚为GPIO模式,并在代码中编写相应的功能实现软件模拟I2C总线的读写操作。 对于I2C总线的读操作,首先我们需要发送开始信号,然后发送设备地址加上写命令,接着发送寄存器地址,再发送一个重新开始信号,然后发送设备地址加上读命令,最后读取接收到的数据。 对于I2C总线的写操作,首先也是发送开始信号,然后发送设备地址加上写命令,接着发送寄存器地址,最后发送要写入的数据。 在代码中,我们可以按上述过程编写相应的函数来进行软件模拟I2C读写操作,并将读取到的数据存储到相应的变量中。 需要注意的是,软件模拟I2C通信可能会导致一定的时序不准确。为了确保通信的稳定性和可靠性,我们可以使用延时函数来控制时序,并根据MPU6050传感器的参数手册调整延时时间。 总之,通过在stm32f407上进行软件模拟I2C读写操作,我们可以实现对MPU6050传感器的数据读取和写入,从而实现与该传感器的通信和控制。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值