STM32F1 - I2C读写EEPROM

本文详细描述了使用STM32F103的I2C模块与AT24C02EEPROM进行通信的过程,包括主发送器模式下写入1Byte数据的步骤,以及主接收器模式下数据读取的处理。同时,作者对I2C波形进行了探索和解读。
摘要由CSDN通过智能技术生成


1> 实验概述

通过STM32F103内部I2C硬件模块,
读写EEPROM - AT24C02


2> I2C模块 - 硬件方框图

2


3> I2C模块 - 主发送器模式


3.1> 发送1Byte数据

31

#define AT24C02_ADDR 0xA0

// PB6 - I2C1_SCL
// PB7 - I2C1_SDA
void EEPROM_Init(void)
{
	GPIO_InitTypeDef GPIO_InitStruct;
	I2C_InitTypeDef I2C_InitStruct;
	
/* 首先 开时钟 */	
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C1, ENABLE);
	
/* GPIO参数配置 */
	GPIO_InitStruct.GPIO_Pin	= GPIO_Pin_6 | GPIO_Pin_7;	
	GPIO_InitStruct.GPIO_Mode	= GPIO_Mode_AF_OD;
	GPIO_InitStruct.GPIO_Speed	= GPIO_Speed_50MHz;
	
	GPIO_Init(GPIOB, &GPIO_InitStruct);
	
/* I2C参数配置  */	
	I2C_InitStruct.I2C_Mode 				= I2C_Mode_I2C;
	I2C_InitStruct.I2C_ClockSpeed 			= 40000;
	I2C_InitStruct.I2C_DutyCycle 			= I2C_DutyCycle_2;
	I2C_InitStruct.I2C_Ack 					= I2C_Ack_Enable;
	I2C_InitStruct.I2C_AcknowledgedAddress	= I2C_AcknowledgedAddress_7bit;
	I2C_InitStruct.I2C_OwnAddress1 			= 0X88;
	
	I2C_Init(I2C1, &I2C_InitStruct);
	
/* 使能 I2C1  */		
	I2C_Cmd(I2C1, ENABLE);	
}

	
/**
 * @brief 写1字节数据到AT24C02.
 * @param mem_addr: AT24C02存储地址.
 * @param dat:数据.
 * @retval None
 */
void EEPROM_Write(uint8_t mem_addr, uint8_t dat)
{
	
	//------------ Step 1> 发送起始位[s] -----------------//
	I2C_GenerateSTART(I2C1, ENABLE);
	while (I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_MODE_SELECT) != SUCCESS) {
		/* 检测事件EV5, SB flags */ ;
	}
	
	//---------- Step 2> 发送从机AT24C02地址 ------------//
	I2C_Send7bitAddress(I2C1, AT24C02_ADDR, I2C_Direction_Transmitter);
	while (I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED) != SUCCESS) {
		/* 检测事件EV6, BUSY, MSL, ADDR, TXE and TRA flags  */ ;
	}
	
	//----------- Step 3> 发送存储地址 -----------------//
	I2C_SendData(I2C1, mem_addr); 
	while (I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_TRANSMITTING) != SUCCESS) {
		/* 检测事件EV8, TRA, BUSY, MSL, TXE flags  */ ;
	}
	
	//----------- Step 4> 发送1字节数据 ---------------// 
	I2C_SendData(I2C1, dat); 
	while (I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_TRANSMITTED) != SUCCESS) {
		/* 检测事件EV8, TRA, BUSY, MSL, TXE flags  */ ;
	}

	//----------- Step 5> 发送停止位[P] ---------------//
	I2C_GenerateSTOP(I2C1, ENABLE);	
}


76

311

从机这活,咱也能干,只管ACK


3.2> 抓波形

32

SCL 频率40KHz, 占空比1/2, 周期25us

探索与发现:

每字节数据后面都有个12us左右的窄脉冲是怎么回事?

那是从机发送完ACK信号后,会把SDA拉高释放,
后面有数据0,马上又会把SDA拉低,所以有窄脉冲;

这波形果然比用软件模拟整齐滴多!嘎嘎香!


4> I2C模块 - 主接收器模式

4

void EEPROM_Read(uint8_t *pBuffer, uint8_t mem_addr, uint16_t size)
{
	/*------------ Step 1> 发送起始位[s] -----------------*/
	I2C_GenerateSTART(I2C1, ENABLE);
	while (I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_MODE_SELECT) != SUCCESS) {
		/* 检测事件EV5, SB flags */ ;
	}
	
	/*---------- Step 2> 发送从机AT24C02地址 ------------*/
	I2C_Send7bitAddress(I2C1, AT24C02_ADDR, I2C_Direction_Transmitter);
	while (I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED) != SUCCESS) {
		/* 检测事件EV6, BUSY, MSL, ADDR, TXE and TRA flags  */ ;
	}
	
	/*----------- Step 3> 发送存储地址 -----------------*/
	I2C_SendData(I2C1, mem_addr); 
	while (I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_TRANSMITTED) != SUCCESS) {
		/* 检测事件EV8, TRA, BUSY, MSL, TXE flags  */ ;
	}
	
	
	/* 伪写完成, 正片开始,参考Master receiver流程图 ---------------------------------------*/
	
	
	
	/*------------ Step 1> 发送起始位[s] -----------------*/
	I2C_GenerateSTART(I2C1, ENABLE);
	while (I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_MODE_SELECT) != SUCCESS) {
		/* 检测事件EV5, SB flags */ ;
	}
	
	/*---------- Step 2> 发送从机AT24C02地址(读) ---------*/
	I2C_Send7bitAddress(I2C1, AT24C02_ADDR, I2C_Direction_Receiver); // 读
	while (I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED) != SUCCESS) {
		/* 检测事件EV6, BUSY, MSL and ADDR flags */ ;
	}
	
	/*--------- Step 3> 主机接收数据, 从机发送数据 -------*/
	I2C_AcknowledgeConfig(I2C1, ENABLE);			// 使能应答功能, 在接收到1Bye数据后,输出ACK
	
	while (size) {
		if (size == 1) {
			I2C_AcknowledgeConfig(I2C1, DISABLE);	// 关闭应答功能, 在接收到最后1Bye数据后,输出NACK
			I2C_GenerateSTOP(I2C1, ENABLE);			// 停止位, 接收最后1字节数据前,使能停止位
		}
		
		while (I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_RECEIVED) != SUCCESS) {
			/* 检测事件EV7, BUSY, MSL and RXNE flags */ ;
		}
		*pBuffer = I2C_ReceiveData(I2C1);
		
		pBuffer++;
		size--;
	}
}

98

发现没有:

有了I2C硬件模块,咱也不用管先发高位,还是低位了,
读写数据也是1字节1字节的;


抓波形:

333


5> AT24C02时序图


5.1> 写时序

Byte Write: 任意存储地址写1字节数据;
Page Write: 任意存储地址,连续写;
1


5.2> 写时序

52

  • 3
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
STM32F103ZE I2C1调试手记近日因项目需要,使用100PIN的STM32带FSMC功能驱动3.2"TFTLCD;在使用EEPROM时发现原来的I2C程序居然无法使用,郁闷了! 先说一下我这个I2C的驱动程序是经过STM32的官方库函数整理而成,API支持全系列EEPROM以及8BIT地址的其它I2C设备,一直用的很爽。 赶紧查找问题原因,发现连STA信号都无法启动完成,一直循环在等待状态while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_MODE_SELECT)); 真是见鬼了,居然连STA信号都无法建立,首先怀疑是硬件问题,于是赶紧查找硬件问题,换EEPROM,无果。奇了怪了,其它项目用的好好的程序,问百度吧?百度一下,这个问题还真多啊,而且还一模一样,问题都没结果,基本的认识是说ST芯片的问题,只能用模拟总线解决。 难道真是芯片问题?这可能吗?这可是ST啊,放眼全球有几家公司能与之抗衡啊。我首先觉得不可能,理由是为什么我其它项目用的好好的,虽说不是同PIN的芯片,可是同系列啊。于是我用其它项目的程序稍做修改,只开放IIC功能把程序下载到103ZE,运行一切正常。这就验证了我的判断,芯片不可能有问题。可为什么不行呢?难道是底层库函数的问题,于是升级库函数,移植到3.0版本的,这样捣鼓一下,一上午时间就这样没了,郁闷啊郁闷到差点绝望啊!!!先吃完中午饭,准备下午再战。再问百度吧,找来找去还是没结果,都说是芯片问题。纠结,难不成真的要用模拟I2C总线解决。不甘心啊,土法炼钢吧,把所有初始化程序一个一个注释掉,下载测试,当注释掉LCD初始化程序时,奇迹出现了,I2C总线OK了,天啦难道是LCD造成的?查看LCD初始化程序,FSMC初始化程序,没发现问题啊,再加上LCD初始化程序,I2C又不能工作了,看来确实是LCD的功能造成,先把FSMC注释掉,运行I2C又OK了,终于找到你了,就是FSMC造成的,看程序没有任何错误,还是查手册吧,在手册中看到I2C1的SDA脚与FSMC_NADV脚是共用一个端口,难道是这个引起的,查看手册《STM32F101xC/D/E 和 STM32F103xC/D/E勘误表》终于发现问题的根源。原文描述:2.7.15 FSMC和 I2C1以及TIM4_CH2问题描述如果要使用FSMC功能,NADV被配置成复用输出,该信号会被默认地置位。这样就和TIM4_CH2和I2C1的SDA信号发生冲突。暂时解决办法当使用FSMC的同时,不要使用TIM4_CH2。如果要使用I2C1并且封装允许,就把该功能重映射到PB8/PB9。2.7.13 SDIO 和经过重映射的I2C1配置条件当SDIO配置成1位或者4位模式,会和被重映射到PB8/9引脚上的I2C1发生冲突。冲突发生在以下信号之间:● I2C1_SCL和SDIO_D4● I2C1_SDA和SDIO_D5暂时解决办法当使用SDIO的同时,不要重映射I2C1的功能到PB8/PB9。问题找到了,就是这个原因啊,天杀的ST,映射功能只能到PB8 PB9端口上,不能映射到任意商品上,可我的PB8 PB9端口被SDIO功能占用了。I2C2端口也被USART3功能占用了。问题是找到了,可最后结果只能模拟总线来实现了。调试结论:1. ST的芯片不会有问题。ST官方的库函数是非常好用的,用的很爽的!2. 当使用大容量芯片具有FSMC功能与I2C1功能同时使用时, 需要把I2C1映射到PB9 PB9端口上去。3. 如果要同时使用FSMC I2C1 SDIO功能时,I2C1只能用模拟总线方式实现。因为SDIO功能会占用PB8 PB9端口。以下是I2C模拟总线程序源代码
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值