cancel
Showing results for 
Search instead for 
Did you mean: 

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

YohannDrc2
Associate II

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();
        }
    }
}

 

 

1 REPLY 1
TDK
Guru

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.

If you feel a post has answered your question, please click "Accept as Solution".