STM32 I2C调试过程心得记录

又花了好几天的时间调I2C,前前后后出现了很多问题,有一些是不仔细的问题,有一些是对于I2C协议不够了解,手册看的不够认真,总之继续学习,这里将遇到的问题记录下,以便日后查阅

  错误一:HardFault硬件错误,逻辑分析仪上显示数据发送到一半就中断了,但是总线上并没有检测到停止信号

  原因:这个错误我犯了两次了,乱用指针,直接定义uint8_t * Rxbuffer来接收I2C数据,也没有指定它的大小,造成野指针以及数组越界的问题

 

 错误二:硬件IO口上没有数据输出,检测到总线一直处于busy状态

 原因:I2C的IO口的复用功能没有配置正确,PB6和PB7应该对应的是GPIO_AF_1而不是GPIO_AF_2(看手册不够仔细)

 

 错误三:主机循环发送数据,但从机中断接收完一次数据后就不继续接收

 解决办法:从机中断中检测到StopF信号后重新使能一次I2C,I2C_Cmd(I2C1,ENABLE)

 

 错误四:主机循环查询接收数据,但从机中断发送发送完一次数据后不继续发送

 原因:

在103的中文参考手册p511有关于STOPF的描述如下:

    STOPF:停止条件检测位(从模式) (Stop detection (slave mode))
       0:没有检测到停止条件;
       1:检测到停止条件。
      – 在一个应答之后(如果ACK=1),当从设备在总线上检测到停止条件时,硬件将该位置’1’。
      – 软件读取SR1寄存器后,对CR1寄存器的写操作将清除该位,或当PE=0时,硬件清除该位。
     注:在收到NACK后,STOPF位不被置位。

     最关键的是这个注,我们知道主机接收数据时,每接收到一个字节都会发回一个ACK信号(前提是主机配置要使能Ack_Enable),而当发送完最后一个字节时,主机这时发送的NACK。也就是说从机如果使能了AF中断,当监测到总线上有NACK信号时就会进入中断,而之后的Stop信号就算从机检测到了也不会产生置位Stopf不会进入相应的中断。

    而且,AF中断是属于I2C1_ER_IRQHandler这个中断通道,在EV_IRQHandler中是检测不到的,并且千万记得要开启I2C_IT_ERR中断以及配置它的NVIC,不然中断也不会响应

解决:开启I2C_IT_ERR中断并配置它的NVIC通道,在I2C1_ER_IRQHandler中处理AF中断,清除AF标志

 

代码整理

STM32F070RB I2C硬件配置

    I2C_DeInit(I2C1);
	RCC_I2CCLKConfig(RCC_I2C1CLK_SYSCLK);
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C1, ENABLE);
	RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOB, ENABLE);


    /********I2C1 GPIO_Pins configuration***********/
	GPIO_PinAFConfig(GPIOB,GPIO_PinSource6,GPIO_AF_1);
	GPIO_PinAFConfig(GPIOB,GPIO_PinSource7,GPIO_AF_1);
	
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7; //PB6 - SCL PB7 - SDA
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
	GPIO_InitStructure.GPIO_OType = GPIO_OType_OD;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;
	GPIO_Init(GPIOB,&GPIO_InitStructure);

	/********I2C1 InitStruct configuration***********/
	I2C_InitStruct.I2C_Mode = I2C_Mode_I2C;
	I2C_InitStruct.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit;
	I2C_InitStruct.I2C_AnalogFilter = I2C_AnalogFilter_Enable;
	I2C_InitStruct.I2C_DigitalFilter = 0x00;
	I2C_InitStruct.I2C_Ack = I2C_Ack_Enable;
	/*ÉèÖÃI2Cʱ¼ä¼Ä´æÆ÷Öµ,Ò²ÅäÖÃÁËI2C_Speed_Frequency ²Î¿¼ÅäÖù¤¾ßI2C_Timing_Config_Tool*/
	I2C_InitStruct.I2C_Timing = I2C_TIMING;  
	I2C_InitStruct.I2C_OwnAddress1 = 0x00;
	I2C_Init(I2C1,&I2C_InitStruct);
	
	//I2C_ClearFlag(I2C1,I2C_FLAG_BUSY);
	I2C_Cmd(I2C1,ENABLE);

 

I2C主机查询发送数据

/******************************************************************
* Function Name : I2C_Write
* Description   : Write more than one byte to the Device with a 
*                 single WRITE cycle
* Input         : - DevAddr : Device Address
                  - RegAddr : Register Address
                  - pBuffer : pointer to the buffer containing the
                              data to be written to the Device
                  - NumByteToWrite : number of bytes to write
* Return        : I2C_Status
******************************************************************/
I2C_Status I2C_Write(uint8_t DevAddr, uint8_t* pBuffer, uint8_t NumByteToWrite)
{
	uint16_t timeout = I2C_TIMEOUT;
	
	/* while the bus is busy */
	while(I2C_GetFlagStatus(I2C1,I2C_FLAG_BUSY) != RESET)
	{
		if((timeout--) == 0)
		{
			return I2C_FAIL;
		}
	}
	
  I2C_TransferHandling(I2C1,DevAddr,NumByteToWrite ,I2C_AutoEnd_Mode,I2C_Generate_Start_Write);

		
	for(write_Num = 0;write_Num < NumByteToWrite;write_Num++)
	{
		I2C_SendData(I2C1,pBuffer[write_Num]);
		timeout = I2C_TIMEOUT;
	  while(I2C_GetFlagStatus(I2C1,I2C_FLAG_TXE) == RESET)
	  {
		  if((timeout--) == 0)
			{
				return1++;
				return I2C_FAIL;
			}
	  }	
  }
	
	timeout = I2C_TIMEOUT;
	while(I2C_GetFlagStatus(I2C1,I2C_FLAG_STOPF) == RESET)
	{
		if((timeout--) == 0)
		{
			return2++;
			return I2C_FAIL;
		}
	}
   	return I2C_OK;
}

 

STM32F103的从机中断接收

void IIC_Init(void)
{
	GPIO_InitTypeDef      GPIO_InitStructure;
	I2C_InitTypeDef       I2C_InitStructure;
	NVIC_InitTypeDef      NVIC_InitStructure;
	
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C1,ENABLE);
	
	/******** I2C1 GPIO_Pins Configuration***********/
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_OD;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOB,	&GPIO_InitStructure);
	
	/******** I2C1 NVIC Configuration ***************/
	NVIC_InitStructure.NVIC_IRQChannel = I2C1_EV_IRQn;  //I2C Event IRQ
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2;
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
	NVIC_Init(&NVIC_InitStructure);
	
	/********** I2C1 initial Configuration***********/
	I2C_InitStructure.I2C_Mode = I2C_Mode_I2C;
	I2C_InitStructure.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit;
	I2C_InitStructure.I2C_Ack = I2C_Ack_Enable;
	I2C_InitStructure.I2C_OwnAddress1 = SLAVE_ADDRESS;
	I2C_InitStructure.I2C_DutyCycle = I2C_DutyCycle_2;
	I2C_InitStructure.I2C_ClockSpeed = 100000;
	I2C_Init(I2C1,&I2C_InitStructure);
	
	I2C_ITConfig(I2C1,I2C_IT_EVT | I2C_IT_BUF | I2C_IT_ERR,ENABLE);
	I2C_Cmd(I2C1,ENABLE);  
}

void I2C1_EV_IRQHandler(void)
{
	switch(I2C_GetLastEvent(I2C1))
	{
		case I2C_EVENT_SLAVE_RECEIVER_ADDRESS_MATCHED:
			break;
		/******* Slave receive mode *************/
		case I2C_EVENT_SLAVE_BYTE_RECEIVED:
			 read_buffer[Rx++] = I2C_ReceiveData(I2C1);
		   break;
		case I2C_EVENT_SLAVE_STOP_DETECTED:
             Rx = 0;
			 I2C_Cmd(I2C1,ENABLE);   //持续接收改成 I2C_Cmd(I2C1,ENABLE)
		   break;
 }
}

 

主机中断发送

I2C_Status I2C_INT_Write(uint8_t DevAddr, uint8_t* pBuffer, uint8_t NumByteToWrite)
{
	
	uint16_t timeout = I2C_TIMEOUT;
	uint8_t write_Num  = 0;
	Txptr = pBuffer;
	
	/* while the bus is busy */
	while(I2C_GetFlagStatus(I2C1,I2C_FLAG_BUSY) != RESET)
	{
		if((timeout--) == 0)
		{
			return I2C_FAIL;
		}
	}
	
	I2C_TransferHandling(I2C1,DevAddr,NumByteToWrite,I2C_AutoEnd_Mode,I2C_Generate_Start_Write);
	I2C_ITConfig(I2C1,I2C_IT_TXI,ENABLE);
	return I2C_OK;
}

 

主机查询接收

I2C_Status I2C_Read(uint8_t DevAddr,uint8_t* pBuffer, uint8_t NumByteToRead)
{
	uint16_t timeout = I2C_TIMEOUT;
	
	/* while the bus is busy */
	while(I2C_GetFlagStatus(I2C1,I2C_FLAG_BUSY) != RESET)
	{
		if((timeout--) == 0)
			return I2C_FAIL;
	}
	
	/* Send Device address for read */
	I2C_TransferHandling(I2C1,DevAddr,NumByteToRead,I2C_AutoEnd_Mode,I2C_Generate_Start_Read);
		
	for(uint8_t read_Num = 0;read_Num < NumByteToRead;read_Num++)
	{
		timeout = I2C_TIMEOUT;
		while(I2C_GetFlagStatus(I2C1,I2C_FLAG_RXNE) == RESET)
	  {
		  if((timeout--) == 0)
			  return I2C_FAIL;
	  }
		pBuffer[read_Num] = I2C_ReceiveData(I2C1);
	}
	
	timeout = I2C_TIMEOUT;
	while(I2C_GetFlagStatus(I2C1,I2C_FLAG_STOPF) == RESET)
	{
		if((timeout--) == 0)
		{
			return I2C_FAIL;
		}
	}
	
	return I2C_OK;
}

从机中断发送 

void I2C1_EV_IRQHandler(void)
{
	switch(I2C_GetLastEvent(I2C1))
	{
		case I2C_EVENT_SLAVE_RECEIVER_ADDRESS_MATCHED:
		case I2C_EVENT_SLAVE_TRANSMITTER_ADDRESS_MATCHED:
			I2C_ClearITPendingBit(I2C1,I2C_IT_ADDR);
		  break;
		/******* Slave transmit mode ************/
		case I2C_EVENT_SLAVE_BYTE_TRANSMITTED:
		case I2C_EVENT_SLAVE_BYTE_TRANSMITTING:
			I2C_SendData(I2C1,write_buffer[Tx++]);
			break;
		/******* Slave receive mode *************/
		case I2C_EVENT_SLAVE_BYTE_RECEIVED:
			 read_buffer[Rx++] = I2C_ReceiveData(I2C1);
		   break;
		case I2C_EVENT_SLAVE_STOP_DETECTED:
			 Rx = 0;
			 I2C_Cmd(I2C1,DISABLE);
		   break;
  }
}

void I2C1_ER_IRQHandler(void)
{
	switch(I2C_GetLastEvent(I2C1))
	{
			case I2C_EVENT_SLAVE_ACK_FAILURE:
			Tx = 0; 
		  I2C_ClearITPendingBit(I2C1,I2C_IT_AF);
	}
}

 

补充:

1、关于工作模式的切换

硬件I2C默认工作在Slave模式,当其产生一个Start信号时自动切换为Master模式

而当仲裁丢失或者产生一个Stop信号时则自动由Slave模式切换为Master模式

2、数据传输

在Master模式下,数据的传输在产生Start信号后开始,产生Stop信号后结束,而无论是Start信号或是Stop信号,都由软件产生。

Start信号后紧跟的第一个字节(8 bit)包含着Slave的地址和读写位(0 write / 1 read)

3、7 bit地址和10 bit地址

7 bit地址模式下,Start信号后一个字节表示从机地址和读写位 Slave address + W/R );10 bit地址模式下,Start信号后的两个字节表示从机地址和读写位(W/R + 1111 0 + Slave address

4、关于I2C的几个模式解析:

   I2C_AutoEnd_Mode  ——  这种模式下,I2C传输完NBytes个字节后会自动产生停止信号以终止信号的传输,所以这个模式下的设备肯定是主机模式

   I2C_Reload_Mode  —— 自动装载模式,在这种模式下传输完NBytes个字节后,I2C的TXR又会重新装载需要传输的数据,每传输完NBytes个字节后TCR标志位会置位

你可能感兴趣的:(单片机)