简体   繁体   English

STM32F4 作为 I2C 从设备。 为什么在“HAL_I2C_Slave_Transmit_DMA”之后执行“void I2C1_ER_IRQHandler(void)”?

[英]STM32F4 as I2C Slave. Why "void I2C1_ER_IRQHandler(void)" is executed after "HAL_I2C_Slave_Transmit_DMA"?

I am using I2C bus as SLAVE mode in a STM32F411RE.我在 STM32F411RE 中使用 I2C 总线作为从模式。 The master is an arduino board.主人是一个arduino板。

The configuration is working well because I see thougth master serial (arduino) and STstudio (STM32F411) that all the frames are OK and thrue oscilloscope.配置运行良好,因为我看到所有帧都通过示波器正常运行。

I noticed that I2C1_ER_IRQHandler function is triggered every time the slave finishes its TX transmission (Master receive that transmision and finishes with NACK and STOP BIT).我注意到 I2C1_ER_IRQHandler 函数在每次从设备完成其 TX 传输时被触发(主设备接收该传输并以 NACK 和 STOP BIT 完成)。 In the next link:在下一个链接中:

https://drive.google.com/file/d/1-W5Z2nsvLNj6PE1TT9eDCDdYvFpnis8g/view?usp=sharing https://drive.google.com/file/d/14JkeAw2If3v0A71V9-KQasH9rK3PRm3H/view?usp=sharing https://drive.google.com/file/d/1Te2F8aNnvkqUSnfRK5UOO-qKabLXXv1D/view?usp=sharing https://drive.google.com/file/d/1-W5Z2nsvLNj6PE1TT9eDCDdYvFpnis8g/view?usp=sharing https://drive.google.com/file/d/14JkeAw2If3v0A71V9-KQasH9rK3PRm3H/view?usp=sharing https:// drive.google.com/file/d/1Te2F8aNnvkqUSnfRK5UOO-qKabLXXv1D/view?usp=sharing

you can download pictures, you can see the SDA signal and GPIO PIN 2 which toggles within I2C1_ER_IRQHandler function.您可以下载图片,您可以看到在 I2C1_ER_IRQHandler 函数中切换的 SDA 信号和 GPIO PIN 2。 ¿could be related to the Slave (stm32) is receiving NACK at the end of thanssmission? ¿可能与 Slave (stm32) 在thanssmission 结束时收到 NACK 有关? see pictures看图片

The SLAVE main function and calls are as follows: SLAVE主函数和调用如下:

#define BUFFERSIZE_RX      0x03    // Master sends 3 bytes
#define BUFFERSIZE_TX      0x04    //Master is waiting for 4 bytes

uint8_t aRxBuffer[BUFFERSIZE_RX];
uint8_t aTxBuffer[BUFFERSIZE_TX];

int main(void)
{
…uC INITIALIZATION

    if(HAL_I2C_Slave_Receive_DMA(&hi2c1, (uint8_t *)aRxBuffer, BUFFERSIZE_RX) != HAL_OK)
    {
        Error_Handler();
    }
    while (1)
    {}
}

void HAL_I2C_SlaveTxCpltCallback(I2C_HandleTypeDef *hi2c)
{
    if(HAL_I2C_GetState(&hi2c1) == HAL_I2C_STATE_READY)
    {
        if(HAL_I2C_Slave_Receive_DMA(&hi2c1, (uint8_t *)aRxBuffer, BUFFERSIZE_RX) != HAL_OK)
        {
            Error_Handler();
        }
    }
}

void HAL_I2C_SlaveRxCpltCallback(I2C_HandleTypeDef *hi2c)      
{
    if(HAL_I2C_GetState(&hi2c1) == HAL_I2C_STATE_READY)
    {
        if(HAL_I2C_Slave_Transmit_DMA(&hi2c1, (uint8_t*)aTxBuffer, BUFFERSIZE_TX)!= HAL_OK)
        {
           Error_Handler();
        }
    }
}

void HAL_I2C_ErrorCallback(I2C_HandleTypeDef *hi2c)
{
    HAL_GPIO_WritePin(GPIOA, GPIO_PIN_3, GPIO_PIN_SET);
}

void I2C1_ER_IRQHandler(void)
{
 HAL_I2C_ER_IRQHandler(&hi2c1);
 HAL_GPIO_TogglePin(GPIOA, GPIO_PIN_2);
}

I2C and DMA configuration is as the STM32Cube_FW_F4_V1.24.0 examples, but if you need them I can send them as well. I2C 和 DMA 配置如 STM32Cube_FW_F4_V1.24.0 示例,但如果您需要它们,我也可以发送它们。

The Arduino Master only sends the following functions: Arduino Master 只发送以下函数:

void loop()
{       
    Wire.beginTransmission(address);
    Wire.write((uint8_t)M_TX_1);
    Wire.write((uint8_t)M_TX_2);
    Wire.write((uint8_t)M_TX_3);
    Wire.endTransmission();
    delay(1);
    Wire.requestFrom(address, (uint8_t)4);
    M_RX_1 = Wire.read();
    M_RX_2 = Wire.read();      
    M_RX_3 = Wire.read();
    M_RX_4 = Wire.read();
… Serial prints and so on…     
}

I have tested I2C in interruption mode and the same thing happens… communications works but always the I2C1_ER_IRQHandler is called.我已经在中断模式下测试了 I2C 并且发生了同样的事情......通信有效但总是调用 I2C1_ER_IRQHandler。

I am completely lost, any help or comment are really appreciate!!!我完全迷路了,任何帮助或评论都非常感谢!!!

Sorry for the long post.抱歉,帖子太长了。

PD HAL_I2C_ErrorCallback never called, so i suppose that it is OK. PD HAL_I2C_ErrorCallback 从未调用过,所以我认为它没问题。

Best regards.此致。

Alejandro亚历杭德罗

PD2: SPI GPIO and DMA configuration: PD2:SPI GPIO 和 DMA 配置:

static void MX_I2C1_Init(void)
{
    hi2c1.Instance = I2C1;
    hi2c1.Init.ClockSpeed = 100000;
    //hi2c1.Init.DutyCycle = I2C_DUTYCYCLE_2;
    hi2c1.Init.DutyCycle = I2C_DUTYCYCLE_16_9;  // Modificacion
    hi2c1.Init.OwnAddress1 = SLAVEADDRESS << 1; // Modificacion
    hi2c1.Init.AddressingMode = I2C_ADDRESSINGMODE_7BIT;
    hi2c1.Init.DualAddressMode = I2C_DUALADDRESS_DISABLE;
    hi2c1.Init.OwnAddress2 = 0;
    //hi2c1.Init.OwnAddress2 = 0x06;        // Modificacion
    hi2c1.Init.GeneralCallMode = I2C_GENERALCALL_DISABLE;
    hi2c1.Init.NoStretchMode = I2C_NOSTRETCH_DISABLE;
    if (HAL_I2C_Init(&hi2c1) != HAL_OK)
    {
    Error_Handler();
    }
}

static void MX_DMA_Init(void) 
{
    __HAL_RCC_DMA1_CLK_ENABLE();

    HAL_NVIC_SetPriority(DMA1_Stream0_IRQn, 0, 1);
    HAL_NVIC_EnableIRQ(DMA1_Stream0_IRQn);
    HAL_NVIC_SetPriority(DMA1_Stream1_IRQn, 0, 2);
    HAL_NVIC_EnableIRQ(DMA1_Stream1_IRQn);
}

void HAL_I2C_MspInit(I2C_HandleTypeDef* hi2c)
{
  GPIO_InitTypeDef GPIO_InitStruct = {0};
  if(hi2c->Instance==I2C1)
  {
    __HAL_RCC_GPIOB_CLK_ENABLE();

    GPIO_InitStruct.Pin = GPIO_PIN_6|GPIO_PIN_7;
    GPIO_InitStruct.Mode = GPIO_MODE_AF_OD;
    GPIO_InitStruct.Pull = GPIO_PULLUP;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
    GPIO_InitStruct.Alternate = GPIO_AF4_I2C1;
    HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);

    __HAL_RCC_I2C1_CLK_ENABLE();

    hdma_i2c1_rx.Instance = DMA1_Stream0;
    hdma_i2c1_rx.Init.Channel = DMA_CHANNEL_1;
    hdma_i2c1_rx.Init.Direction = DMA_PERIPH_TO_MEMORY;
    hdma_i2c1_rx.Init.PeriphInc = DMA_PINC_DISABLE;
    hdma_i2c1_rx.Init.MemInc = DMA_MINC_ENABLE;
    hdma_i2c1_rx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;
    hdma_i2c1_rx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;
    hdma_i2c1_rx.Init.Mode = DMA_NORMAL;
    hdma_i2c1_rx.Init.Priority = DMA_PRIORITY_LOW;
    hdma_i2c1_rx.Init.FIFOMode = DMA_FIFOMODE_DISABLE;
    hdma_i2c1_rx.Init.FIFOThreshold = DMA_FIFO_THRESHOLD_FULL;  
    hdma_i2c1_rx.Init.MemBurst = DMA_MBURST_INC4;               
    hdma_i2c1_rx.Init.PeriphBurst = DMA_PBURST_INC4;    
    if (HAL_DMA_Init(&hdma_i2c1_rx) != HAL_OK)
    {
        Error_Handler();
    }
    __HAL_LINKDMA(hi2c,hdmarx,hdma_i2c1_rx);

    hdma_i2c1_tx.Instance = DMA1_Stream1;
    hdma_i2c1_tx.Init.Channel = DMA_CHANNEL_0;
    hdma_i2c1_tx.Init.Direction = DMA_MEMORY_TO_PERIPH;
    hdma_i2c1_tx.Init.PeriphInc = DMA_PINC_DISABLE;
    hdma_i2c1_tx.Init.MemInc = DMA_MINC_ENABLE;
    hdma_i2c1_tx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;
    hdma_i2c1_tx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;
    hdma_i2c1_tx.Init.Mode = DMA_NORMAL;
    hdma_i2c1_tx.Init.Priority = DMA_PRIORITY_HIGH;
    hdma_i2c1_tx.Init.FIFOMode = DMA_FIFOMODE_DISABLE;
    hdma_i2c1_tx.Init.FIFOThreshold = DMA_FIFO_THRESHOLD_FULL;              
    hdma_i2c1_tx.Init.MemBurst = DMA_MBURST_INC4;           
    hdma_i2c1_tx.Init.PeriphBurst = DMA_PBURST_INC4;
    if (HAL_DMA_Init(&hdma_i2c1_tx) != HAL_OK)
    {
        Error_Handler();
    }

    __HAL_LINKDMA(hi2c,hdmatx,hdma_i2c1_tx);

    HAL_NVIC_SetPriority(I2C1_EV_IRQn, 0, 3);
    HAL_NVIC_EnableIRQ(I2C1_EV_IRQn);
    HAL_NVIC_SetPriority(I2C1_ER_IRQn, 0, 2);
    HAL_NVIC_EnableIRQ(I2C1_ER_IRQn);
  }
}

To start with, Is it possible that this is an event IRQ (EV) to indicate a slave has finished transferring data and is not an error IRQ?首先,这是否可能是一个事件 IRQ (EV) 来指示从设备已完成数据传输而不是错误 IRQ? You may have configured this to show that the slave is done with data transfer on the bus.您可能已将其配置为显示从站已完成总线上的数据传输。

If that is not the case, definitely take a little better look at the I2C standard of communication.如果不是这种情况,请务必仔细查看 I2C 通信标准。 In your case, a NACK just indicates the stop of the transfer all together from the master, or it could mean that the transfer failed, but if you say all frames are successful, then it is definitely the end of transfer condition.在您的情况下,NACK 仅表示从主设备一起停止传输,或者这可能意味着传输失败,但是如果您说所有帧都成功,那么这绝对是传输结束的条件。 That being said, it is important to know how this was all configured.话虽如此,重要的是要知道这是如何配置的。 I would check the initialization sequence of the I2C on the F4 and make sure that it is in compliance with what is expected from the Arduino.我会检查 F4 上 I2C 的初始化序列,并确保它符合 Arduino 的预期。 It should be something like this.它应该是这样的。

  1. Setup the associated clocks with I2C and GPIOs使用 I2C 和 GPIO 设置相关时钟
  2. Map the GPIOs映射 GPIO
  3. Enable the Interrupt services needed through the Nested Vectored Interrupt Controller (NVIC)通过嵌套向量中断控制器 (NVIC) 启用所需的中断服务
  4. Set the settings of the I2C to the desired framing将 I2C 的设置设置为所需的帧
  5. Enable the I2C peripheral and interrupts启用 I2C 外设和中断

These steps will allow you to make sure you are covering your ground here.这些步骤将允许您确保您在这里覆盖您的地面。

Now you also must make sure you have the right features enabled if you are using DMA with I2C as well.现在,如果您将 DMA 与 I2C 一起使用,您还必须确保启用了正确的功能。 The F4 does some packet error checking after the end of each message. F4 在每条消息结束后进行一些数据包错误检查。 Do you have the PEC enabled?您是否启用了 PEC? If so, make sure there is not something causing this to get the IRQ interrupt to fire.如果是这样,请确保没有任何原因导致 IRQ 中断触发。

You also have event flags to read from in a debugger to see what error specifically triggered the I2C IRQ error line.您还可以在调试器中读取事件标志,以查看具体触发 I2C IRQ 错误行的错误。 According to the STMF4 reference manual, the event flags to generate an interrupt are:根据STMF4参考手册,产生中断的事件标志是:

0: Error interrupt disabled
1: Error interrupt enabled
This interrupt is generated when:
– BERR = 1
– ARLO = 1
– AF = 1
– OVR = 1
– PECERR = 1
– TIMEOUT = 1
– SMBALERT = 1

Although, you mentioned that the Error Callback function was not active, so these might not be present.虽然,您提到错误回调函数未处于活动状态,因此这些可能不存在。

Finally, just to be safe, ensure the IRQ line isn't being used elsewhere in software.最后,为了安全起见,确保 IRQ 线没有在软件的其他地方使用。 it could just be that the line is controlled by something else and being pulled high, causing the IRQ handler function to be called.可能只是线路被其他东西控制并被拉高,导致调用 IRQ 处理程序函数。 This is unlikely if the code you posted is the only thing running, but it is worth a thought.如果您发布的代码是唯一运行的东西,则不太可能,但值得考虑。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM