STM32 硬件I2C采用DMA发送时ADDR的清除问题

从机I2C DMA中断接收需注意的点:

1、开启DMA时钟

2、这里要开启I2C EVT事件中断,当地址匹配后清除地址中断标志才能继续发送数据
 

RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1,ENABLE);

/******* I2C1 DMA Channel Configuration **********/
	DMA_DeInit(DMA1_Channel7);
	DMA_InitStructure.DMA_BufferSize = 10;
	DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;
	DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
	DMA_InitStructure.DMA_MemoryBaseAddr = (u32)read_buffer;
	DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;
	DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
	DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;
	DMA_InitStructure.DMA_PeripheralBaseAddr = (u32)(&I2C1->DR);
	DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;
	DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
	DMA_InitStructure.DMA_Priority = DMA_Priority_Medium;
	DMA_Init(DMA1_Channel7,&DMA_InitStructure);
	
	/******** DMA NVIC Configuration *****************/
	NVIC_InitStructure.NVIC_IRQChannel = DMA1_Channel7_IRQn;
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
	NVIC_Init(&NVIC_InitStructure);

	I2C_ITConfig(I2C1,I2C_IT_EVT,ENABLE);
	
    DMA_ITConfig(DMA1_Channel7,DMA_IT_TC,ENABLE);
	DMA_Cmd(DMA1_Channel7,ENABLE);
	
	I2C_DMACmd(I2C1,ENABLE);
	I2C_Cmd(I2C1,ENABLE);

 

从机I2C DMA中断发送

void I2C_Configuration(void)
{
	RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1,ENABLE);

    /******* I2C1 DMA Transmit Configuration **********/
	DMA_DeInit(DMA1_Channel6);
	DMA_InitStructure.DMA_BufferSize = 10;
	DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST;
	DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
	DMA_InitStructure.DMA_MemoryBaseAddr = (u32)write_buffer;
	DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;
	DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
	DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;
	DMA_InitStructure.DMA_PeripheralBaseAddr = (u32)(&I2C1->DR);
	DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;
	DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
	DMA_InitStructure.DMA_Priority = DMA_Priority_Medium;
	DMA_Init(DMA1_Channel6,&DMA_InitStructure);
	
	/******** DMA NVIC Configuration *****************/
	NVIC_InitStructure.NVIC_IRQChannel = DMA1_Channel6_IRQn;
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
	NVIC_Init(&NVIC_InitStructure);

	I2C_ITConfig(I2C1,I2C_IT_EVT,ENABLE);
    DMA_ITConfig(DMA1_Channel6,DMA_IT_TC,ENABLE);
	DMA_Cmd(DMA1_Channel6,ENABLE);
	
	I2C_Cmd(I2C1,ENABLE);
	I2C_DMACmd(I2C1,ENABLE);
}


void I2C1_EV_IRQHandler(void)
{
	if(I2C_GetITStatus(I2C1,I2C_IT_ADDR) != RESET)
	{
		assert_param(I2C1->SR2);
		
		DMA_ClearITPendingBit(DMA1_IT_TC6);
		DMA_Cmd(DMA1_Channel6,DISABLE);
	    DMA_SetCurrDataCounter(DMA1_Channel6, 10);
	    DMA_Cmd(DMA1_Channel6,ENABLE);
	}
	switch(I2C_GetLastEvent(I2C1))
	{
		case I2C_EVENT_SLAVE_RECEIVER_ADDRESS_MATCHED:
		case I2C_EVENT_SLAVE_TRANSMITTER_ADDRESS_MATCHED:
			I2C_ClearITPendingBit(I2C1,I2C_IT_ADDR);
		  test++;
		  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,ENABLE);
		   break;
  }
}


void DMA1_Channel6_IRQHandler(void)
{
	if(DMA_GetITStatus(DMA1_IT_TC6) != RESET)
	{
		DMA_ClearITPendingBit(DMA1_IT_TC6);
	}
}

关于I2C的DMA发送,如果是从机DMA发送,最好配置DMA为单次传输,然后在地址匹配成功后再开启新的传输,而这个过程有几个需要注意的点:

1、DMA正确的重新开启方法:先Disable,用库函数SetCurrDataCounter重新配置DMA缓冲区大小(因为DMA传输时这个大小值会递减至0所以需要重新配置),最后再使能DMA通道

       DMA_Cmd(DMA1_Channel6,DISABLE);
       DMA_SetCurrDataCounter(DMA1_Channel6, 10);
       DMA_Cmd(DMA1_Channel6,ENABLE);

2、关于I2C的地址匹配中断,经测试发现,如果采用case I2C_EVENT_SLAVE_RECEIVER_ADDRESS_MATCHED:和case I2C_EVENT_SLAVE_TRANSMITTER_ADDRESS_MATCHED:这两个事件判断,然后I2C_ClearITPendingBit(I2C1,I2C_IT_ADDR);这种方式下,ADDR地址匹配中断只进一次

查阅void I2C_ClearITPendingBit(I2C_TypeDef* I2Cx, uint32_t I2C_IT)库函数的说明有这样一段
 / *   
  * @note
  *   - STOPF (STOP detection) is cleared by software sequence: a read operation 
  *     to I2C_SR1 register (I2C_GetITStatus()) followed by a write operation to 
  *     I2C_CR1 register (I2C_Cmd() to re-enable the I2C peripheral).
  *   - ADD10 (10-bit header sent) is cleared by software sequence: a read 
  *     operation to I2C_SR1 (I2C_GetITStatus()) followed by writing the second 
  *     byte of the address in I2C_DR register.
  *   - BTF (Byte Transfer Finished) is cleared by software sequence: a read 
  *     operation to I2C_SR1 register (I2C_GetITStatus()) followed by a 
  *     read/write to I2C_DR register (I2C_SendData()).
  *   - ADDR (Address sent) is cleared by software sequence: a read operation to 
  *     I2C_SR1 register (I2C_GetITStatus()) followed by a read operation to 
  *     I2C_SR2 register ((void)(I2Cx->SR2)).

  *   - SB (Start Bit) is cleared by software sequence: a read operation to 
  *     I2C_SR1 register (I2C_GetITStatus()) followed by a write operation to 
  *     I2C_DR register (I2C_SendData()).
  * @retval None
  */

这里指出了ADDR标志位清除的正确方法,先用I2C_GetITStatus()对SR1寄存器进行读操作,之后再对SR2寄存机进行读操作,才能正确清除ADDR标志

所以正确的清除方法应该是在EVT中断中作如下处理:

   if(I2C_GetITStatus(I2C1,I2C_IT_ADDR) != RESET)
    {
        assert_param(I2C1->SR2);
    }

猜你喜欢

转载自blog.csdn.net/yhl_sophia/article/details/88993878