Skip to main content
Visitor II
December 20, 2024
Solved

STM32l0 stop mode and I2C slave: over consumption when disconnecting SDA (before scl)

  • December 20, 2024
  • 1 reply
  • 1491 views

Hi everyone,

I get stuck with an issue and can't figure what to do.
I implemented on a STM32l071 a I2C slave and using DMA with circular buffer, I am using HAL with CubeMX generated code.
The microcontroller is by default in stop mode, so when something communicates with the microcontroller with I2C, the microcontroller wakes up by I2c address match, then do what it has to do, then return in stop mode.
All that I have implemented seems to work well.
The custom board we are using consume around 22µA when the microcontroller is in stop mode.

The problem is:
When I disconnect only the SDA line before SCL line, the board consume arround 321µA .
(I can then disconnect and reconnect all lines it doesn't change the result).
Then If a send a read or write I2C frame with the master with SCL connected, (SDA connected or not), the board consumption go back to normal at 22µA.

Before entering stop mode, I clear all power flag and I2c flag just in case.
I also activate an output pin in order to see if some I2C callback function are called but it does'nt semm to be the case.

I attach the code and capture of SCL, SDA and output pin

Thanks in advance for your help
Yohann

 

 

// ================================================================================================
// ========================================== Includes ============================================
// ================================================================================================

// HAL headers
#include "main.h"

// ================================================================================================
// ======================================= External data ==========================================
// ================================================================================================

// Layer Hal
extern "C" I2C_HandleTypeDef hi2c1;

// ================================================================================================
// ===================================== Private variables ========================================
// ================================================================================================

#define I2C_RX_BUFFER_SIZE 255
#define I2C_TX_BUFFER_SIZE 255
uint8_t _rxBufferI2C[I2C_RX_BUFFER_SIZE] = {0};
uint8_t _txBufferI2C[I2C_TX_BUFFER_SIZE] = {0x10, 0x11, 0x12, 0x13, 0x14, 0x15};

uint8_t countAddrI2C = 0;
uint8_t countRxCpltI2C = 0;
uint8_t countTxCpltI2C = 0;
uint8_t countErrorI2C = 0;
uint8_t countlistenCpltI2C = 0;

volatile uint8_t i2cSlaveTransferDirection = I2C_DIRECTION_TRANSMIT;
volatile bool i2cSlaveCommunicating = false;
volatile bool processReceivedMessage = false;

// ================================================================================================
// ======================================= Implementation =========================================
// ================================================================================================

extern "C" void HAL_I2C_ListenCpltCallback(I2C_HandleTypeDef *hi2c)
{
 HAL_GPIO_WritePin(GPIOA, GPIO_PIN_9, GPIO_PIN_SET);

 countlistenCpltI2C++;

 if (i2cSlaveTransferDirection == I2C_DIRECTION_TRANSMIT)
 {
 processReceivedMessage = true;
 }

 // I2C slave has finished to communicate
 i2cSlaveCommunicating = false;

 // slave is ready again
 HAL_I2C_EnableListen_IT(hi2c);

 HAL_GPIO_WritePin(GPIOA, GPIO_PIN_9, GPIO_PIN_RESET);
}

extern "C" void HAL_I2C_AddrCallback(I2C_HandleTypeDef *hi2c, uint8_t TransferDirection, uint16_t AddrMatchCode)
{
 HAL_GPIO_WritePin(GPIOA, GPIO_PIN_9, GPIO_PIN_SET);

 countAddrI2C++;

 i2cSlaveTransferDirection = TransferDirection;

 // I2C slave is communicating
 i2cSlaveCommunicating = true;

 // Master is transmitting data
 if(TransferDirection == I2C_DIRECTION_TRANSMIT)
 {
 HAL_I2C_Slave_Sequential_Receive_DMA(hi2c, _rxBufferI2C, I2C_RX_BUFFER_SIZE, I2C_FIRST_AND_LAST_FRAME);
 }
 else
 {
 HAL_I2C_Slave_Sequential_Transmit_DMA(hi2c, _txBufferI2C, I2C_TX_BUFFER_SIZE, I2C_FIRST_AND_LAST_FRAME);
 }

 HAL_GPIO_WritePin(GPIOA, GPIO_PIN_9, GPIO_PIN_RESET);
}

extern "C" void HAL_I2C_SlaveRxCpltCallback(I2C_HandleTypeDef *hi2c)
{
 // Count how many times we enter this function
 countRxCpltI2C++;

 HAL_GPIO_WritePin(GPIOA, GPIO_PIN_9, GPIO_PIN_SET);
 HAL_GPIO_WritePin(GPIOA, GPIO_PIN_9, GPIO_PIN_RESET);
}

extern "C" void HAL_I2C_SlaveTxCpltCallback(I2C_HandleTypeDef *hi2c)
{
 // Count how many times we enter this function
 countTxCpltI2C++;

 HAL_GPIO_WritePin(GPIOA, GPIO_PIN_9, GPIO_PIN_SET);
 HAL_GPIO_WritePin(GPIOA, GPIO_PIN_9, GPIO_PIN_RESET);
}

extern "C" void HAL_I2C_ErrorCallback(I2C_HandleTypeDef *hi2c)
{
 // Count how many times we enter this function
 countErrorI2C++;

 HAL_GPIO_WritePin(GPIOA, GPIO_PIN_9, GPIO_PIN_SET);
 HAL_GPIO_WritePin(GPIOA, GPIO_PIN_9, GPIO_PIN_RESET);
}

// ================================================================================================
// ======================================= Implementation =========================================
// ================================================================================================

void enterStopMode()
{
 // I2C clear all flags before entering stop mode
 __HAL_I2C_CLEAR_FLAG(&hi2c1, I2C_FLAG_TXE); // Transmit data register empty
 __HAL_I2C_CLEAR_FLAG(&hi2c1, I2C_FLAG_ADDR); // Address matched (slave mode)
 __HAL_I2C_CLEAR_FLAG(&hi2c1, I2C_FLAG_AF); // Acknowledge failure received flag
 __HAL_I2C_CLEAR_FLAG(&hi2c1, I2C_FLAG_STOPF); // STOP detection flag
 __HAL_I2C_CLEAR_FLAG(&hi2c1, I2C_FLAG_BERR); // Bus error
 __HAL_I2C_CLEAR_FLAG(&hi2c1, I2C_FLAG_ARLO); // Arbitration lost
 __HAL_I2C_CLEAR_FLAG(&hi2c1, I2C_FLAG_OVR); // Overrun/Underrun
 __HAL_I2C_CLEAR_FLAG(&hi2c1, I2C_FLAG_PECERR); // PEC error in reception
 __HAL_I2C_CLEAR_FLAG(&hi2c1, I2C_FLAG_TIMEOUT); // Timeout or Tlow detection flag
 __HAL_I2C_CLEAR_FLAG(&hi2c1, I2C_FLAG_ALERT); // SMBus alert

 // POWER clear all flag before entering stop mode
 __HAL_PWR_CLEAR_FLAG(PWR_FLAG_WU);
 __HAL_PWR_CLEAR_FLAG(PWR_FLAG_SB);
 __HAL_PWR_CLEAR_FLAG(PWR_FLAG_PVDO);
 __HAL_PWR_CLEAR_FLAG(PWR_FLAG_VREFINTRDY);
 __HAL_PWR_CLEAR_FLAG(PWR_FLAG_VOS);
 __HAL_PWR_CLEAR_FLAG(PWR_FLAG_REGLP);

 // Enable Power Control clock
 __HAL_RCC_PWR_CLK_ENABLE();

 // Ensure that MSI is wake-up system clock -> Doesn't work to wakeup from I2C
 //__HAL_RCC_WAKEUPSTOP_CLK_CONFIG(RCC_STOP_WAKEUPCLOCK_MSI);

 // Need to put HSI as wake-up system clock in order to wakeup from I2C
 // (RCC -> Reset and Clock Control)
 __HAL_RCC_WAKEUPSTOP_CLK_CONFIG(RCC_STOP_WAKEUPCLOCK_HSI);

 // Suspend Tick increment to prevent wakeup by Systick interrupt.
 // Otherwise the Systick interrupt will wake up the device within 1ms (HAL time base)
 HAL_SuspendTick();

 // Enter Stop Mode
 HAL_PWR_EnterSTOPMode(PWR_LOWPOWERREGULATOR_ON, PWR_STOPENTRY_WFI);
}

void leaveStopMode()
{
 // Set system clock back to ... oscillator because when exiting Stop mode by using an interrupt or a wake up event,
 // ... oscillator is selected as system clock
 SystemClock_Config ();

 //Resume Tick interrupt if disabled prior to Stop mode entry
 HAL_ResumeTick();
}

extern "C" void main_app()
{
 // ##### I2C #####
 HAL_StatusTypeDef status = HAL_OK;

 // Enable the Analog I2C Filter
 status = HAL_I2CEx_ConfigAnalogFilter(&hi2c1 ,I2C_ANALOGFILTER_ENABLE);

 // Enable I2C peripheral in wake up from stop mode
 status = HAL_I2CEx_EnableWakeUp(&hi2c1);

 status = HAL_I2C_EnableListen_IT(&hi2c1);

 HAL_Delay(1000);

 bool enableEnterStopMode = true;

 while (1)
 {
 if (processReceivedMessage == true)
 {
 // Do something
 HAL_GPIO_WritePin(GPIOA, GPIO_PIN_9, GPIO_PIN_SET);
 HAL_GPIO_WritePin(GPIOA, GPIO_PIN_9, GPIO_PIN_RESET);

 processReceivedMessage = false;
 }

 if (enableEnterStopMode == true && processReceivedMessage == false && i2cSlaveCommunicating == false)
 {
 enterStopMode();

 leaveStopMode();
 }
 }
}

 

 

    This topic has been closed for replies.
    Best answer by TDK

    When SDA drops with SCL high, that is a start condition and the bus is no longer idle. It makes sense that current consumption would increase as it's now waiting for something to happen.

    You should pull up SDA/SCL on the STM32 side of the disconnection. That way they stay high and the chip doesn't even know it's disconnected.

    1 reply

    TDKAnswer
    Super User
    December 20, 2024

    When SDA drops with SCL high, that is a start condition and the bus is no longer idle. It makes sense that current consumption would increase as it's now waiting for something to happen.

    You should pull up SDA/SCL on the STM32 side of the disconnection. That way they stay high and the chip doesn't even know it's disconnected.

    Visitor II
    December 26, 2024

    I will try that and let you know.
    Thanks for your support