求教STM32硬件I2C EV5和EV6错误问题

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了求教STM32硬件I2C EV5和EV6错误问题相关的知识,希望对你有一定的参考价值。

EV5、EV6并不是错误。

EV5事件:

程序中是这样调用的

//检测 EV5 事件并清除标志

while(! I2C_CheckEvent(EEPROM_I2C,I2C_EVENT_MASTER_MODE_SELECT)) 

if ((I2CTimeout--) == 0)  return I2C_TIMEOUT_UserCallback(1);


可以看到这里有一个宏I2C_EVENT_MASTER_MODE_SELECT,它定义在stm32f4xx_i2c.h头文件中(答主用的STM32F4单片机)

它的值为((uint32_t)0x00030001)。

这里还有一个函数I2C_CheckEvent(); 

这个函数将I2C的两个16位寄存器SR1和SR2合并成一个32位数据再与FLAG_MASK (这也是一个宏,在stm32f4xx_i2c.c中,它的值为((uint32_t)0x00FFFFFF)) 做按位与运算,得到SR1的低8位数据和SR2的全部16位数据,这个数据再与宏 I2C_EVENT_MASTER_MODE_SELECT 做按位与运算这个宏的值为((uint32_t)0x00030001) ,取出了SR1寄存器的位0、位1,SR2寄存器的位0 的值。

我们来看看 SR1寄存器的位0、位1,SR2寄存器的位0  表示的什么含义?

在STM32F4xx中文参考手册中查到:

SR1:

SR2:

可见,EV5事件是判断起始位有没有产生、判断是主模式还是从模式,地址有没有发送的? 判断完成,无异常接下来就开始发送从设备地址了。

EV6事件同理,如果不懂,说明还没有理解上面的EV5事件,继续看EV5。

参考技术A 这两个不是错误。当你配置I2C为主模式式,会有EV5时间产生。主模式配置为发送模式的时候会有EV6

I2C_EVENT_MASTER_MODE_SELECT: EV5
I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED: EV6本回答被提问者和网友采纳

硬件实现IIC协议读取EEPROM

我TMD也是服了,反正我板子搞了半天也不成功我也不知道为什么,野火STM32-MINI,一直卡EV5,不管了 先代码沾上

1

2 工程目录(板子为野火STM32 MINI)

串口相关代码:

bsp_usart.h

#ifndef __USART_H
#define	__USART_H


#include "stm32f10x.h"
#include <stdio.h>

/**
  * 串口宏定义,不同的串口挂载的总线和IO不一样,移植时需要修改这几个宏
	* 1-修改总线时钟的宏,uart1挂载到apb2总线,其他uart挂载到apb1总线
	* 2-修改GPIO的宏
  */

// 串口1-USART1
#define  DEBUG_USARTx                   USART1
#define  DEBUG_USART_CLK                RCC_APB2Periph_USART1
#define  DEBUG_USART_APBxClkCmd         RCC_APB2PeriphClockCmd
#define  DEBUG_USART_BAUDRATE           115200

// USART GPIO 引脚宏定义
#define  DEBUG_USART_GPIO_CLK           (RCC_APB2Periph_GPIOA)
#define  DEBUG_USART_GPIO_APBxClkCmd    RCC_APB2PeriphClockCmd

#define  DEBUG_USART_TX_GPIO_PORT         GPIOA
#define  DEBUG_USART_TX_GPIO_PIN          GPIO_Pin_9
#define  DEBUG_USART_RX_GPIO_PORT       GPIOA
#define  DEBUG_USART_RX_GPIO_PIN        GPIO_Pin_10

#define  DEBUG_USART_IRQ                USART1_IRQn
#define  DEBUG_USART_IRQHandler         USART1_IRQHandler

void USART_Config(void);

#endif /* __USART_H */

bsp_usart.c

 

#include "./usart/bsp_usart.h"


 /**
  * @brief  USART GPIO 配置,工作参数配置
  * @param  无
  * @retval 无
  */
void USART_Config(void)
{
	GPIO_InitTypeDef GPIO_InitStructure;
	USART_InitTypeDef USART_InitStructure;

	// 打开串口GPIO的时钟
	DEBUG_USART_GPIO_APBxClkCmd(DEBUG_USART_GPIO_CLK, ENABLE);

	// 打开串口外设的时钟
	DEBUG_USART_APBxClkCmd(DEBUG_USART_CLK, ENABLE);

	// 将USART Tx的GPIO配置为推挽复用模式
	GPIO_InitStructure.GPIO_Pin = DEBUG_USART_TX_GPIO_PIN;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(DEBUG_USART_TX_GPIO_PORT, &GPIO_InitStructure);

  // 将USART Rx的GPIO配置为浮空输入模式
	GPIO_InitStructure.GPIO_Pin = DEBUG_USART_RX_GPIO_PIN;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
	GPIO_Init(DEBUG_USART_RX_GPIO_PORT, &GPIO_InitStructure);

	// 配置串口的工作参数
	// 配置波特率
	USART_InitStructure.USART_BaudRate = DEBUG_USART_BAUDRATE;
	// 配置 针数据字长
	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(DEBUG_USARTx, &USART_InitStructure);

	// 使能串口
	USART_Cmd(DEBUG_USARTx, ENABLE);
}


///重定向c库函数printf到串口,重定向后可使用printf函数
int fputc(int ch, FILE *f)
{
		/* 发送一个字节数据到串口 */
		USART_SendData(DEBUG_USARTx, (uint8_t) ch);

		/* 等待发送完毕 */
		while (USART_GetFlagStatus(DEBUG_USARTx, USART_FLAG_TXE) == RESET);

		return (ch);
}

///重定向c库函数scanf到串口,重写向后可使用scanf、getchar等函数
int fgetc(FILE *f)
{
		/* 等待串口输入数据 */
		while (USART_GetFlagStatus(DEBUG_USARTx, USART_FLAG_RXNE) == RESET);

		return (int)USART_ReceiveData(DEBUG_USARTx);
}

bsp_i2c_ee.h

#ifndef __I2C_EE_H
#define	__I2C_EE_H


#include "stm32f10x.h"
#include "bsp_usart.h"

#define  EEPROM_ADDR  0xA0


void I2C_EE_Config(void);
void EEPROM_Byte_Write(uint8_t addr,uint8_t data);
void EEPROM_Page_Write(uint8_t addr,uint8_t *data,uint8_t numByteToWrite);
void EEPROM_Read(uint8_t addr,uint8_t *data,uint8_t numByteToRead);
void EEPROM_WaitForWriteEnd(void);


#endif /* __I2C_EE_H */

bsp_i2c_ee.c

#include "bsp_i2c_ee.h"



void I2C_EE_Config(void)
{
	GPIO_InitTypeDef GPIO_InitStructure;
	I2C_InitTypeDef  I2C_InitStructure;

	// 打开IIC GPIO的时钟
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);

	// 打开IIC 外设的时钟
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C1, ENABLE);

	// 将IIC SCL SDA的GPIO配置为推挽复用模式
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2|GPIO_Pin_3;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_OD;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA, &GPIO_InitStructure);



	// 配置IIC的工作参数
	I2C_InitStructure.I2C_Ack = I2C_Ack_Enable  ;//使能应答
	I2C_InitStructure.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit ;//使用7位地址模式
	I2C_InitStructure.I2C_ClockSpeed = 400000; //配置SCL时钟频率
	I2C_InitStructure.I2C_DutyCycle = I2C_DutyCycle_2 ;
	I2C_InitStructure.I2C_Mode = I2C_Mode_I2C ;
	I2C_InitStructure.I2C_OwnAddress1 = 0x5f; //这是STM32 IIC自身设备地址,只要是总线上唯一即可

	I2C_Init(I2C1,&I2C_InitStructure);

	// 使能串口
	I2C_Cmd (I2C1, ENABLE);
}


//向EEPROM写入一个字节

void EEPROM_Byte_Write(uint8_t addr,uint8_t data)
{
	//产生起始信号
	I2C_GenerateSTART(I2C1,ENABLE);

	while(I2C_CheckEvent(I2C1,I2C_EVENT_MASTER_MODE_SELECT) == ERROR);

	//EV5事件被检测到,发送设备地址
	I2C_Send7bitAddress(I2C1,EEPROM_ADDR,I2C_Direction_Transmitter);

  while(I2C_CheckEvent(I2C1,I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED ) == ERROR);

	//EV6事件被检测到,发送要操作的存储单元地址
	I2C_SendData (I2C1,addr);

	while(I2C_CheckEvent(I2C1,I2C_EVENT_MASTER_BYTE_TRANSMITTING ) == ERROR);

  //EV8事件被检测到,发送要存储的数据
	I2C_SendData (I2C1,data);

	while(I2C_CheckEvent(I2C1,I2C_EVENT_MASTER_BYTE_TRANSMITTED ) == ERROR);

	//数据传输完成
	I2C_GenerateSTOP(I2C1,ENABLE);

}



//向EEPROM写入多个字节(页写入),每次写入不能超过8个字节

void EEPROM_Page_Write(uint8_t addr,uint8_t *data,uint8_t numByteToWrite)
{
	//产生起始信号
	I2C_GenerateSTART(I2C1,ENABLE);

	while(I2C_CheckEvent(I2C1,I2C_EVENT_MASTER_MODE_SELECT) == ERROR);

	//EV5事件被检测到,发送设备地址
	I2C_Send7bitAddress(I2C1,EEPROM_ADDR,I2C_Direction_Transmitter);

  while(I2C_CheckEvent(I2C1,I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED ) == ERROR);

	//EV6事件被检测到,发送要操作的存储单元地址
	I2C_SendData (I2C1,addr);

	while(I2C_CheckEvent(I2C1,I2C_EVENT_MASTER_BYTE_TRANSMITTING ) == ERROR);


	while(numByteToWrite)
	{
		//EV8事件被检测到,发送要存储的数据
		I2C_SendData (I2C1,*data);

		while(I2C_CheckEvent(I2C1,I2C_EVENT_MASTER_BYTE_TRANSMITTED ) == ERROR);

		data++;
		numByteToWrite--;

	}
	//数据传输完成
	I2C_GenerateSTOP(I2C1,ENABLE);

}



//从EEPROM读取数据

void EEPROM_Read(uint8_t addr,uint8_t *data,uint8_t numByteToRead)
{
	//产生起始信号
	I2C_GenerateSTART(I2C1,ENABLE);

	while(I2C_CheckEvent(I2C1,I2C_EVENT_MASTER_MODE_SELECT) == ERROR);

	//EV5事件被检测到,发送设备地址
	I2C_Send7bitAddress(I2C1,EEPROM_ADDR,I2C_Direction_Transmitter);

  while(I2C_CheckEvent(I2C1,I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED ) == ERROR);

	//EV6事件被检测到,发送要操作的存储单元地址
	I2C_SendData (I2C1,addr);

	while(I2C_CheckEvent(I2C1,I2C_EVENT_MASTER_BYTE_TRANSMITTING ) == ERROR);


	//第二次起始信号
	//产生起始信号
	I2C_GenerateSTART(I2C1,ENABLE);

	while(I2C_CheckEvent(I2C1,I2C_EVENT_MASTER_MODE_SELECT) == ERROR);

	//EV5事件被检测到,发送设备地址
	I2C_Send7bitAddress(I2C1,EEPROM_ADDR,I2C_Direction_Receiver);

  while(I2C_CheckEvent(I2C1,I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED ) == ERROR);


	while(numByteToRead)
	{
		if(numByteToRead == 1)
		{
			//如果为最后一个字节
			I2C_AcknowledgeConfig (I2C1,DISABLE);
		}

		//EV7事件被检测到	
		while(I2C_CheckEvent(I2C1,I2C_EVENT_MASTER_BYTE_RECEIVED ) == ERROR);

		//EV7事件被检测到,即数据寄存器有新的有效数据	
		*data = I2C_ReceiveData(I2C1);

		data++;

		numByteToRead--;

	}


	//数据传输完成
	I2C_GenerateSTOP(I2C1,ENABLE);

	//重新配置ACK使能,以便下次通讯
	I2C_AcknowledgeConfig (I2C1,ENABLE);

}


//等待EEPROM内部时序完成
void EEPROM_WaitForWriteEnd(void)
{

	do
	{
		//产生起始信号
		I2C_GenerateSTART(I2C1,ENABLE);

		while(I2C_GetFlagStatus (I2C1,I2C_FLAG_SB) == RESET);

		//EV5事件被检测到,发送设备地址
		I2C_Send7bitAddress(I2C1,EEPROM_ADDR,I2C_Direction_Transmitter);
	}
	while(I2C_GetFlagStatus (I2C1,I2C_FLAG_ADDR) == RESET );

	//EEPROM内部时序完成传输完成
	I2C_GenerateSTOP(I2C1,ENABLE);
}

main.c

#include "stm32f10x.h"
#include "./usart/bsp_usart.h"
#include "./i2c/bsp_i2c_ee.h"


uint8_t readData[10]={0};
uint8_t writeData[8]={4,5,6,7,8,9,10,11};
 /**
  * @brief  主函数
  * @param  无
  * @retval 无
  */
int main(void)
{
	 uint8_t i=0;
   USART_Config();
	 printf("这是一个IIC通讯实验\\n");

	 I2C_EE_Config();
   //向 EEPROM 11地址写入0x55,
   EEPROM_Byte_Write(11,0x55);
	 EEPROM_WaitForWriteEnd();
	 //读出 EEPROM 11地址的内容,保存到readData中(数组的第一个址位)
	 EEPROM_Read(11,readData,1);
	 printf("%x",readData[0]);

		//addr%8 == 0 ,即为地址对齐
	EEPROM_Page_Write(16,writeData,8);

	//等待写入操作完成
	EEPROM_WaitForWriteEnd();

	//读取数据
	EEPROM_Read(16,readData,8);
	for(i=0;i<8;i++)
	{
		printf("%d ",readData[i]);
	}
    while(1);
}
/*********************************************END OF FILE**********************/

以上是关于求教STM32硬件I2C EV5和EV6错误问题的主要内容,如果未能解决你的问题,请参考以下文章

stm32硬件i2c缺陷解决方法

stm32F407的硬件I2C稳定吗

STM32模拟I2C读写铁电无应答

关于stm32中iic与fsmc冲突问题

STM32 硬件I2C 死循环 自动退出

关于STM32的I2C硬件DMA实现