×
单片机 > 其他资讯 > 详情

STM32CubeMX GPIO模拟I2C读写M24C64

发布时间:2020-06-23 发布时间:
|

一、先了解一下硬件的连接,I2C_SDA和I2C_SCL分别接STM32的PB9、PB6

二、粗阅一下M24C64的数据手册,得知器件地址和存储器地址,器件地址是8bit,而存储器地址是16bit

三、下面是M24C64的写时序

四、下面是M24C64的读时序

五、下面是程序编写流程

六、看看时序参数
七、好啦!需要的知识点差不多都提到了开始搬砖
1、用STM32CubeMX配置生成工程,并打开工程。(具体怎么用这个软件这里不讲)
2、在我的工程里是这样配置的
《1》配置USART3,用打印读出来的数据与写入的是否一致
《2》配置PB6、PB9为开漏输出模式,配置如下:

void MX_GPIO_Init(void)
{

  GPIO_InitTypeDef GPIO_InitStruct;

  /* GPIO Ports Clock Enable */
  __HAL_RCC_GPIOH_CLK_ENABLE();
  __HAL_RCC_GPIOG_CLK_ENABLE();
  __HAL_RCC_GPIOC_CLK_ENABLE();
  __HAL_RCC_GPIOB_CLK_ENABLE();

  /*Configure GPIO pin Output Level */
  HAL_GPIO_WritePin(GPIOG, GPIO_PIN_7, GPIO_PIN_RESET);

  /*Configure GPIO pin Output Level */
  HAL_GPIO_WritePin(GPIOB, GPIO_PIN_6|GPIO_PIN_9, GPIO_PIN_RESET);

  /*Configure GPIO pin : PG7 */
  GPIO_InitStruct.Pin = GPIO_PIN_7;
  GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
  GPIO_InitStruct.Pull = GPIO_NOPULL;
  GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
  HAL_GPIO_Init(GPIOG, &GPIO_InitStruct);

  /*Configure GPIO pins : PB6 PB9 */
  GPIO_InitStruct.Pin = GPIO_PIN_6|GPIO_PIN_9;						//PB6    PB9
  GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_OD;				//开漏输出
  GPIO_InitStruct.Pull = GPIO_NOPULL;											//上下拉模式配置为既不上拉也不下拉
  GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;//IO口速度配置
  HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);									//初始化
}
在这里插入代码片1234567891011121314151617181920212223242526272829303132

八、编写读程序,下面的代码是(安富莱电子 www.armfly.com)串行EEPROM 24xx驱动模块的代码,
代码如下:

/***********************************************************************************************
*
*
*
*
*
*
*
*
***************************************************************************************************/
#include "stm32f4xx_hal.h"


#define EE_MODEL_NAME		"AT24C64"
#define EE_DEV_ADDR			0xA0			/* 设备地址 */
#define EE_PAGE_SIZE		32				/* 页面大小(字节) */
#define EE_SIZE				(8*1024)		/* 总容量(字节) */
#define EE_ADDR_BYTES		2			 	/* 地址字节个数 */

// 定义I2C总线连接的GPIO端口, 用户只需要修改下面3行代码即可任意改变SCL和SDA的引脚 
#define GPIO_PORT_I2C	GPIOB			     // GPIO端口 
#define I2C_SCL_PIN		GPIO_PIN_6			 // 连接到SCL时钟线的GPIO 
#define I2C_SDA_PIN		GPIO_PIN_9			 // 连接到SDA数据线的GPIO 

/* 定义读写SCL和SDA的宏 */
#define I2C_SCL_1()  GPIO_PORT_I2C->BSRR = I2C_SCL_PIN							// SCL = 1 
#define I2C_SCL_0()  GPIO_PORT_I2C->BSRR = (uint32_t)I2C_SCL_PIN BSRR = (uint32_t)GPIO_PIN_9 IDR & I2C_SCL_PIN)						// 读SCL口线状态 


static void i2c_Delay(void)
{
	uint8_t i;
	for (i = 0; i > 7) & 0x0E));	/* 此处是写指令 */
//				#else				
					i2c_SendByte(EE_DEV_ADDR | I2C_WR);
//				#endif
        
				/* 第3步:发送一个时钟,判断器件是否正确应答 */
				if (i2c_WaitAck() == 0)
				{
					break;
				}
			}
			if (m  == 1000)
			{
				goto cmd_fail;	/* EEPROM器件写超时 */
			}
			/* 第4步:发送字节地址,24C02只有256字节,因此1个字节就够了,如果是24C04以上,那么此处需要连发多个地址 */
			if (EE_ADDR_BYTES == 1)
			{
				i2c_SendByte((uint8_t)usAddr);
				if (i2c_WaitAck() != 0)
				{
					goto cmd_fail;	/* EEPROM器件无应答 */
				}
			}
			else
			{
				i2c_SendByte(usAddr >> 8);
				if (i2c_WaitAck()!= 0)
				{
					goto cmd_fail;	/* EEPROM器件无应答 */
				}

				i2c_SendByte(usAddr);
				if (i2c_WaitAck() != 0)
				{
					goto cmd_fail;	/* EEPROM器件无应答 */
				}
			}
		}

		/* 第6步:开始写入数据 */
		i2c_SendByte(_pWriteBuf[i]);

		/* 第7步:发送ACK */
		if (i2c_WaitAck() != 0)
		{
			goto cmd_fail;	/* EEPROM器件无应答 */
		}
		usAddr++;	/* 地址增1 */
	}

	/* 命令执行成功,发送I2C总线停止信号 */
	i2c_Stop();

	/* 通过检查器件应答的方式,判断内部写操作是否完成, 一般小于 10ms
		CLK频率为200KHz时,查询次数为30次左右
	*/
	for (m = 0; m > 7) & 0x0E));	/* 此处是写指令 */
		#else		
			i2c_SendByte(EE_DEV_ADDR | I2C_WR);	/* 此处是写指令 */
		#endif

		/* 第3步:发送一个时钟,判断器件是否正确应答 */
		if (i2c_WaitAck() == 0)
		{
			break;
		}
	}
	if (m  == 1000)
	{
		goto cmd_fail;	/* EEPROM器件写超时 */
	}

	/* 命令执行成功,发送I2C总线停止信号 */
	i2c_Stop();	

	return 1;

cmd_fail: /* 命令执行失败后,切记发送停止信号,避免影响I2C总线上其他设备 */
	/* 发送I2C总线停止信号 */
	i2c_Stop();
	return 0;
}
uint8_t ee_ReadBytes(uint8_t *_pReadBuf, uint16_t _usAddress, uint16_t _usSize)
{
	uint16_t i;

	/* 采用串行EEPROM随即读取指令序列,连续读取若干字节 */

	/* 第1步:发起I2C总线启动信号 */
	i2c_Start();

	/* 第2步:发起控制字节,高7bit是地址,bit0是读写控制位,0表示写,1表示读 */
	
	i2c_SendByte(EE_DEV_ADDR | I2C_WR);	/* 此处是写指令 */

	/* 第3步:发送ACK */
	if (i2c_WaitAck() != 0)
	{	
		goto cmd_fail;	/* EEPROM器件无应答 */
	}
	/* 第4步:发送字节地址,24C02只有256字节,因此1个字节就够了,如果是24C04以上,那么此处需要连发多个地址 */
	if (EE_ADDR_BYTES == 1)
	{
		i2c_SendByte((uint8_t)_usAddress);
		if (i2c_WaitAck() != 0)
		{
			goto cmd_fail;	/* EEPROM器件无应答 */
		}
	}
	else
	{
		i2c_SendByte(_usAddress >> 8);
		if (i2c_WaitAck() != 0)
		{
			goto cmd_fail;	/* EEPROM器件无应答 */
		}

		i2c_SendByte(_usAddress);
		if (i2c_WaitAck() != 0)
		{
			goto cmd_fail;	/* EEPROM器件无应答 */
		}
	}

	/* 第6步:重新启动I2C总线。下面开始读取数据 */
	i2c_Start();

	/* 第7步:发起控制字节,高7bit是地址,bit0是读写控制位,0表示写,1表示读 */	
	
	i2c_SendByte(EE_DEV_ADDR | I2C_RD);	/* 此处是写指令 */

	/* 第8步:发送ACK */
	if (i2c_WaitAck() != 0)
	{
		goto cmd_fail;	/* EEPROM器件无应答 */
	}

	/* 第9步:循环读取数据 */
	for (i = 0; i 



『本文转载自网络,版权归原作者所有,如有侵权请联系删除』

热门文章 更多
液晶电视的电磁兼容设计方案