STM32F103C6 I2C Slave Interrupt Mode Data Reception Failure and SCL Line Lock
Issue Overview: STM32F103C6 I2C Slave Interrupt Mode Fails After First Byte Reception
The STM32F103C6 microcontroller, when configured as an I2C slave in interrupt mode, fails to receive the complete data stream from the I2C master. The issue manifests as the slave device successfully receiving the address and the first data byte, followed by an acknowledgment (ACK). However, after the ACK, the SCL (Serial Clock) line is held low, effectively stalling the communication. This behavior is consistent with the I2C protocol’s specification, where a slave device can hold the SCL line low to force the master into a wait state, typically indicating that the slave is not ready to receive more data.
The problem occurs specifically when using the HAL_I2C_Slave_Receive_IT() function, which is designed to enable non-blocking data reception via interrupts. In this case, the interrupt service routine (ISR) is not being triggered after the first byte, as evidenced by diagnostic LEDs that fail to illuminate when placed inside the ISR. The I2C peripheral state remains stuck at HAL_I2C_STATE_BUSY_RX_LISTEN, indicating that the peripheral is waiting for further data but is unable to process it due to the interrupt not firing.
Possible Causes: Interrupt Configuration, Peripheral State Management, and Clock Synchronization
The root cause of this issue can be attributed to one or more of the following factors:
-
Interrupt Configuration and Priority Settings: The STM32F103C6 relies on the NVIC (Nested Vectored Interrupt Controller) to manage interrupt priorities and enable/disable interrupts. If the I2C interrupt priority is not configured correctly, or if global interrupts are not enabled, the ISR will not execute. In this case, the user has attempted to manually set the I2C interrupt priority to 5 and explicitly enabled interrupts using _enable_irq(), but the issue persists. This suggests that the problem may lie deeper in the interrupt configuration or the NVIC setup.
-
Peripheral State Management: The HAL_I2C_Slave_Receive_IT() function relies on the proper management of the I2C peripheral state. If the state machine within the HAL library is not transitioning correctly, the peripheral may remain in a BUSY state, preventing further data reception. The HAL_I2C_STATE_BUSY_RX_LISTEN flag indicates that the peripheral is waiting for data but is unable to process it, which could be due to a misconfiguration or a bug in the state machine logic.
-
Clock Synchronization and Timing Issues: I2C communication relies on precise timing and synchronization between the master and slave devices. If the clock configuration for the I2C peripheral is incorrect, or if there are issues with the clock tree (e.g., incorrect PLL settings or clock source selection), the slave device may fail to synchronize with the master, leading to communication breakdowns. Additionally, the STM32F103C6’s I2C peripheral has specific timing requirements for setup and hold times, which must be met for reliable operation.
-
Hardware and Pin Configuration: The I2C pins (SCL and SDA) must be configured correctly in the GPIO initialization function. The pins should be set to open-drain mode with pull-up resistors enabled to comply with the I2C specification. If the GPIO configuration is incorrect, the I2C communication may fail, especially in interrupt mode where timing is critical.
Troubleshooting Steps, Solutions & Fixes: Debugging STM32F103C6 I2C Slave Interrupt Mode
To resolve the issue, follow these detailed troubleshooting steps:
-
Verify Interrupt Configuration and NVIC Settings:
- Ensure that the I2C interrupts (I2C1_EV_IRQn and I2C1_ER_IRQn) are enabled in the NVIC and that their priorities are set correctly. Use the HAL_NVIC_SetPriority() and HAL_NVIC_EnableIRQ() functions to configure the interrupts.
- Confirm that global interrupts are enabled by calling __enable_irq() or equivalent function in your startup code.
- Check the interrupt handler functions (I2C1_EV_IRQHandler and I2C1_ER_IRQHandler) to ensure they are implemented correctly and are being called. Add diagnostic code (e.g., toggling an LED) to verify that the ISR is executing.
-
Inspect Peripheral State Management:
- Review the HAL_I2C_Slave_Receive_IT() function and its interaction with the I2C state machine. Ensure that the state transitions are occurring as expected and that the peripheral is not stuck in a BUSY state.
- Use the HAL_I2C_GetState() function to monitor the I2C peripheral state during operation. If the state remains stuck at HAL_I2C_STATE_BUSY_RX_LISTEN, investigate the conditions under which the state should transition to READY or another appropriate state.
-
Check Clock Configuration and Timing:
- Verify that the I2C clock speed (100 kHz in this case) is correctly configured in the MX_I2C1_Init() function. Ensure that the clock source for the I2C peripheral is stable and meets the timing requirements.
- Use an oscilloscope or logic analyzer to monitor the SCL and SDA lines during communication. Check for proper signal levels, rise/fall times, and timing compliance with the I2C specification.
- Adjust the I2C timing parameters if necessary, using the I2C_TIMINGR register to fine-tune the setup and hold times.
-
Validate Hardware and Pin Configuration:
- Confirm that the GPIO pins used for I2C (PB6 for SCL and PB7 for SDA) are configured correctly in the MX_GPIO_Init() function. The pins should be set to alternate function open-drain mode with pull-up resistors enabled.
- Ensure that external pull-up resistors are present on the SCL and SDA lines, as required by the I2C specification. The resistor values should be chosen based on the bus capacitance and desired rise time.
-
Debugging with Diagnostic Tools:
- Use the STM32CubeMX tool to generate a fresh initialization code for the I2C peripheral and compare it with your existing code to identify any discrepancies.
- Enable debugging in your IDE and set breakpoints in the I2C interrupt handlers to observe the program flow and identify where it deviates from expected behavior.
- Utilize the STM32 HAL error handling mechanism to log and diagnose errors. Implement the Error_Handler() function to provide detailed information about any errors that occur during I2C communication.
-
Firmware Updates and HAL Library Considerations:
- Ensure that you are using the latest version of the STM32 HAL library, as newer versions may contain bug fixes or improvements related to I2C interrupt handling.
- If the issue persists, consider implementing a custom I2C interrupt handler instead of relying on the HAL library. This approach provides greater control over the I2C peripheral and may help identify the root cause of the problem.
By systematically addressing each of these areas, you can identify and resolve the issue with the STM32F103C6 I2C slave interrupt mode. The key is to methodically verify each component of the system, from the hardware configuration to the software state machine, to ensure reliable I2C communication.