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标志位会置位

stm8si2c程序,调试通过 INTERRUPT_HANDLER(I2C_IRQHandler, 19) { /* In order to detect unexpected events during development, it is recommended to set a breakpoint on the following instruction. */ struct SCB_T *system=&system_process; unsigned char i,i2c_sr1,i2c_sr2,i2c_event,i2c__status=0,temp,sr1_analysis_int_resource[8],sr2_analysis_int_resource[8]; char i2c_interrupt_type=0,p;//在一次I2中断中,I2C中断中的中断标志个数; disableInterrupts();//关总中断 i2c_sr1=I2C->SR1; p=I2C->SR3; i2c_sr2=I2C->SR2; //temp=I2C->SR3; //analysis interrupt resource in i2c->sr1 register sr1_analysis_int_resource[0]=i2c_sr1&I2C_SR1_SB; sr1_analysis_int_resource[1]=i2c_sr1&I2C_SR1_ADDR; sr1_analysis_int_resource[2]=i2c_sr1&I2C_SR1_BTF; sr1_analysis_int_resource[3]=i2c_sr1&I2C_SR1_ADD10; sr1_analysis_int_resource[4]=i2c_sr1&I2C_SR1_STOPF; // sr1_i2c__state[5]=i2c_state&((u8)I2C_SR1_BIT6); sr1_analysis_int_resource[6]=i2c_sr1&I2C_SR1_RXNE; sr1_analysis_int_resource[7]=i2c_sr1&I2C_SR1_TXE; //analysis interrupt resource in i2c->sr2 register sr2_analysis_int_resource[0]=i2c_sr2&I2C_SR2_BERR; sr2_analysis_int_resource[1]=i2c_sr2&I2C_SR2_ARLO; sr2_analysis_int_resource[2]=i2c_sr2&I2C_SR2_AF; sr2_analysis_int_resource[3]=i2c_sr2&I2C_SR2_OVR; sr2_analysis_int_resource[5]=i2c_sr2&I2C_SR2_WUFH; if(sr1_analysis_int_resource[0]==I2C_SR1_SB) {i2c__status=0x01;i2c_interrupt_type++;} if(sr1_analysis_int_resource[1]==I2C_SR1_ADDR) {i2c__status=0x02;i2c_interrupt_type++;} if(sr1_analysis_int_resource[2]==I2C_SR1_BTF) {i2c__status=0x03;i2c_interrupt_type++;} if(sr1_analysis_int_resource[3]==I2C_SR1_ADD10) {i2c__status=0x04;i2c_interrupt_type++;} if(sr1_analysis_int_resource[4]==I2C_SR1_STOPF) {i2c__status=0x05;i2c_interrupt_type++;} if(sr1_analysis_int_resource[6]==I2C_SR1_RXNE) {i2c__status=0x06;i2c_interrupt_type++;} if(sr1_analysis_int_resource[7]==I2C_SR1_TXE) {i2c__status=0x07;i2c_interrupt_type++;} if(sr2_analysis_int_resource[0]==I2C_SR2_BERR) {i2c__status=0x08;i2c_interrupt_type++;} if(sr2_analysis_int_resource[1]==I2C_SR2_ARLO) {i2c__status=0x09;i2c_interrupt_type++;} if(sr2_analysis_int_resource[2]==I2C_SR2_AF) {i2c__status=0x0a;i2c_interrupt_type++;} if(sr2_analysis_int_resource[3]==I2C_SR2_OVR) {i2c__status=0x0b;i2c_interrupt_type++;} if(sr2_analysis_int_resource[5]==I2C_SR2_WUFH) {i2c__status=0x0c;i2c_interrupt_type++;} if(i2c_interrupt_type>=2) /*there are more than one interrupt resource in the time*/ { if(i2c_interrupt_type==2) { if((sr1_analysis_int_resource[1]==I2C_SR1_ADDR)&&(sr1_analysis_int_resource[7]==I2C_SR1_TXE)) { I2C->DR=system->i2c.send_frame.data[system->i2c.send_frame.proc]; system->i2c.send_frame.proc++; system->i2c.debug.reserve[system->i2c.int_debug_count].type=0x62; } else if((sr1_analysis_int_resource[7]==I2C_SR1_TXE)&&(sr1_analysis_int_resource[2]==I2C_SR1_BTF)) { system->i2c.send_frame.terminate=0; //set I2C transfer terminate bit; system->i2c.send_frame.mod=0; system->i2c.send_frame.write=0; system->i2c.debug.reserve[system->i2c.int_debug_count].type=0x64; } else if((sr1_analysis_int_resource[7]==I2C_SR1_TXE)&&(sr2_analysis_int_resource[2]==I2C_SR2_AF)) { I2C->CR2|=I2C_CR2_STOP; I2C->SR2&=(~I2C_SR2_AF);//clear AF bit; system->i2c.error=1; system->i2c.debug.reserve[system->i2c.int_debug_count].type=0x64; } else { system->i2c.error=1; I2C_ITConfig(I2C_IT_EVT, DISABLE); system->i2c.debug.reserve[system->i2c.int_debug_count].type=0x37; } } else { system->i2c.error=1; I2C_ITConfig(I2C_IT_EVT, DISABLE); system->i2c.debug.reserve[system->i2c.int_debug_count].type=0x37; } } else { switch(i2c__status) { case I2C_SR1_SB_proc: //如果是发送模式 if(system->i2c.send_frame.mod==1)//说明本次中断之前是从模式,说明这是在从模式下发的起始; { //EV5 p=I2C->SR1; I2C->DR=system->i2c.send_frame.add__L; //自动清除I2C_SR1_SB标志 system->i2c.debug.reserve[system->i2c.int_debug_count].type=0x38; } else { if(system->i2c.rev_frame.mod==1) //说明本次中断之间是主模式,这次发的是重复起始; { //EV6如果是接收模式 p=I2C->SR1; I2C->DR=system->i2c.rev_frame.add__L;//自动清除I2C_SR1_SB标志; system->i2c.debug.reserve[system->i2c.int_debug_count].type=0x51; } else { system->i2c.error=1; system->i2c.debug.reserve[system->i2c.int_debug_count].type=0x36; } } break; case I2C_SR1_ADDR_proc: p=I2C->SR1; temp=I2C->SR3;//软件读取SR1寄存器后,对SR3寄存器的读操作将清除 temp&=(u8)I2C_SR3_TRA; I2C->CR2|=(u8)I2C_CR2_ACK;// 使能应答 I2C->CR2&=(u8)(~I2C_CR2_POS);//设置接受到当字节应答 //如果是发送模式 if(system->i2c.send_frame.mod==1) { if(temp==(u8)I2C_SR3_TRA) {; } else { system->i2c.error=1; } } else { if(system->i2c.rev_frame.mod==1) { if(temp==0)//machine at a master-receive mod { system->i2c.rev_frame.proc=0; } else { system->i2c.error=1; } } else { system->i2c.error=1; } } system->i2c.debug.reserve[system->i2c.int_debug_count].type=0x52; break; case I2C_SR1_RXNE_proc: if(system->i2c.rev_frame.proci2c.rev_frame.num-3)) { system->i2c.rev_frame.data[system->i2c.rev_frame.proc]=I2C->DR; system->i2c.rev_frame.proc++; system->i2c.debug.reserve[system->i2c.int_debug_count].type=0x57; } else if(system->i2c.rev_frame.proc==(u8)(system->i2c.rev_frame.num-2)) { system->i2c.rev_frame.data[system->i2c.rev_frame.proc]=I2C->DR; system->i2c.rev_frame.proc++; system->i2c.debug.reserve[system->i2c.int_debug_count].type=0x58; I2C->CR2&=(u8)(~I2C_CR2_ACK);//不返回应答 I2C->CR2|=I2C_CR2_STOP; //发停止结束这次数据接收; } else if(system->i2c.rev_frame.proc>=(u8)(system->i2c.rev_frame.num-1)) { system->i2c.rev_frame.data[system->i2c.rev_frame.proc]=I2C->DR; system->i2c.rev_frame.proc++; system->i2c.debug.reserve[system->i2c.int_debug_count].type=0x59; system->i2c.rev_frame.terminate=0; //set I2C transfer terminate bit; system->i2c.rev_frame.mod=0; system->i2c.rev_frame.read=0; } else { system->i2c.error=1; system->i2c.debug.reserve[system->i2c.int_debug_count].type=0xfb; } break; case I2C_SR1_TXE_proc: if(system->i2c.send_frame.proci2c.send_frame.num-1)) { I2C->DR=system->i2c.send_frame.data[system->i2c.send_frame.proc]; system->i2c.send_frame.proc++; system->i2c.debug.reserve[system->i2c.int_debug_count].type=0x61; } else if(system->i2c.send_frame.proc=(u8)(system->i2c.send_frame.num)) { I2C->CR2|=I2C_CR2_STOP; // 发停止结束 这次数据接收; I2C_ITConfig(I2C_IT_BUF, DISABLE); system->i2c.debug.reserve[system->i2c.int_debug_count].type=0x60; } else { system->i2c.error=1; system->i2c.debug.reserve[system->i2c.int_debug_count].type=0xfc; } break; case I2C_SR2_AF_proc: I2C->CR2|=I2C_CR2_STOP; I2C->SR2&=(~I2C_SR2_AF);//clear AF bit; system->i2c.error=1; system->i2c.debug.reserve[system->i2c.int_debug_count].type=0x63; break; default: system->i2c.error=1; system->i2c.debug.reserve[system->i2c.int_debug_count].type=0xfd; break; } } system->i2c.int_debug_count++; enableInterrupts();//开总中断 }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值