AnsweredAssumed Answered

STM32F030CC SMBus

Question asked by karlis77 on Feb 24, 2016
Latest reply on Feb 25, 2016 by karlis77
Hello all.
Do anyone here have experience with STM32F0 series micro-controllers using SMBus?
I have serious problem that should be dealt with ASAP.

I use MCU to communicate with BQ3060(fuel gauge chip from Texas instruments), and sometimes ( one time out of 100) this BQ3060 chip just hungs after first few packets when I connect BQ3060 and NACKs my reads and writes and answers with its own address 0x17. 

Now I try to recreate this happening while recording everything with Saleae digital logic analyzer, but it has not happened now for many hours, it's just so random.

So here is my code(only parts which refers to SMBus) . I know it is trash, but the thing is  - my last code which is trash too has not triggered this BQ3060 hung up. I guess this is the worst way to learn why one should use version-control.

#include <stm32f0xx_gpio.h>
#include <stm32f0xx_rcc.h>
#include <stm32f0xx_i2c.h>
#include <stm32f0xx.h>
#include <stm32f0xx_adc.h>
#include <stm32f0xx_exti.h>
#include <stm32f0xx_syscfg.h>
 
 
 
 
 
 
#define BAT_PLUGIN_PIN GPIO_Pin_7
#define LI_CHARGE_STATUS_PIN GPIO_Pin_0
#define LI_BAT_OUT_STM_PIN GPIO_Pin_1
#define AC_ADAPT_STATUS_PIN GPIO_Pin_2
#define SMBUS_CLK_PIN GPIO_Pin_9
#define SMBUS_DATA_PIN GPIO_Pin_10
#define LI_CHRG_SHUTDOWN_PIN GPIO_Pin_5
#define LI_PRECHARGE_CHARGE_PIN GPIO_Pin_11 //    6 jauna , 7 vecajaa
#define BATTERY_STATUS_GOOD_LED GPIO_Pin_8
#define BATTERY_STATUS_BAD_LED GPIO_Pin_4
#define CHARGING_STATUS_LED GPIO_Pin_12
int m;
int x;
int ciklu_skaits;
unsigned int t;
unsigned int i;
int temp_mode;
unsigned char volt_A;
unsigned char volt_B;
float voltage;
int volti_desmiti;
int volti_vieni;
int volti_desmitdalas;
int volti_simtdalas;
  
int maska;
 
unsigned char cycle_A;
unsigned char cycle_B;
int cycle;
int cycle_1;
int cycle_2;
int cycle_3;
int cycle_4;
int cycle_5;
 
int aizture = 1;
int laiks;
unsigned char current_A;
unsigned char current_B;
float current;
int current_int;
int current_int_5;
int current_int_4;
int current_int_3;
int current_int_2;
int current_int_1;
uint32_t current_HEX;
 
int relative_SOC;
int SOC_desmiti;
int SOC_vieni;
 
unsigned char temp_A;
unsigned char temp_B;
float temp;
int temp_desmiti;
int temp_vieni;
int temp_desmitdalas;
int temp_simtdalas;
 
unsigned char time_to_full_A;
unsigned char time_to_full_B;
int time_to_full;
int time_to_full_5;
int time_to_full_4;
int time_to_full_3;
int time_to_full_2;
int time_to_full_1;
 
int time_to_empty;
unsigned char time_to_empty_A;
unsigned char time_to_empty_B;
int time_to_empty_5;
int time_to_empty_4;
int time_to_empty_3;
int time_to_empty_2;
int time_to_empty_1;
 
float dalitajs_10 = 10;
float dalitajs_100 = 100;
float dalitajs_1000 = 1000;
 
int current_mode;
// visi bija int a-dh
char a;
char ah;
char b;
char bh;
char c;
char ch;
char d;
char dh;
 
 
 
int plugin_time;
int state;
float LI_bat_node;
float LI_bat_adc_value;
 
void SMBUS_FLAG_BUSY() // when SMBus is busy nothing is being done, if it is busy too long then I2C is reseted
{
    while (I2C_GetFlagStatus(I2C1, I2C_FLAG_BUSY))
    {
        t++; // vnk ; te bija bez nekaa
         
        if (t == 100000)
        {
            RCC->APB1RSTR |= 0x200000; // resets i2c2 400000 , i2c1 200000
            for (aizture = 1;aizture < 10000;aizture++)
            {
                m++;
            }
            m = 0;
            RCC->APB1RSTR &= 0xFFDFFFFF; // iesledz i2c2 FFBFFFFF
            //I2C_TimeoutAConfig(I2C1, 0x63);// 0x61(TIDLE =0  , TIMEOUTEN=1) vai 0x63(TIDLE =1  , TIMEOUTEN=1)
            //I2C_TimeoutBConfig(I2C1, 0x1F); // TEXTEN=1
        }
 
         
    }
    t = 0;
         
}
 
void read_SMBus()  //  waits for message in SMBus , if nothing is being read for too long time, it breaks this waiting cycle
{
    while (I2C_GetFlagStatus(I2C1, I2C_FLAG_RXNE) == 0)
    {
        i++;
        if (i == 10000)
        {
            i = 0;
            break;
        }
    }
    i = 0;
}
 
 
void PIN_CONFIGURATION(void) // GPIO, I2C config
{
    GPIO_InitTypeDef GPIO_InitStructure;
    I2C_InitTypeDef I2C_InitStructure;
    //RCC_ClocksTypeDef RCC_InitStructure;
    EXTI_InitTypeDef EXTI_InitStruct;
     
 
    RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOA, ENABLE);
    RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOB, ENABLE);
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C1, ENABLE);
     
    // reset i2c1
    RCC->APB1RSTR |= 0x200000; // resets i2c2 400000 , i2c1 200000
    for (aizture = 1;aizture < 10000;aizture++)
    {
        m++;
    }
    m = 0;
    RCC->APB1RSTR &= 0xFFDFFFFF; //
     
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_SYSCFG, ENABLE);
     
     
         //OUTPUTI uz GPIOA
    GPIO_InitStructure.GPIO_Pin =   LI_PRECHARGE_CHARGE_PIN | BATTERY_STATUS_GOOD_LED | CHARGING_STATUS_LED;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz;
    GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
    GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
    GPIO_Init(GPIOA, &GPIO_InitStructure);
     
     
    // DIGITAL INPUTS GPIOA
    GPIO_InitStructure.GPIO_Pin =  BAT_PLUGIN_PIN;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz;
    GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
    GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
    GPIO_Init(GPIOA, &GPIO_InitStructure);
     
     
 
    // INTERUPTI PRIEKS BAT PLUGIN INPUTA
     /* Tell system that you will use PA0 for EXTI_Line_0 */
    SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOA, BAT_PLUGIN_PIN); // EXTI_PinSource0 var rakstit BAT_PLUGIN_PIN
     
     /* PA0 is connected to EXTI_Line0 */
    EXTI_InitStruct.EXTI_Line = EXTI_Line7;
    /* Enable interrupt */
    EXTI_InitStruct.EXTI_LineCmd = ENABLE;
    /* Interrupt mode */
    EXTI_InitStruct.EXTI_Mode = EXTI_Mode_Interrupt;
    /* Triggers on rising and falling edge */
    EXTI_InitStruct.EXTI_Trigger = EXTI_Trigger_Falling;
    /* Add to EXTI */
    EXTI_Init(&EXTI_InitStruct);
     
    NVIC_SetPriority(EXTI4_15_IRQn, 0);
    NVIC_EnableIRQ(EXTI4_15_IRQn);
     
     
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
    GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_Level_1;
    GPIO_InitStructure.GPIO_OType = GPIO_OType_OD;
    GPIO_InitStructure.GPIO_Pin = SMBUS_DATA_PIN;
    GPIO_Init(GPIOA, &GPIO_InitStructure);
    GPIO_InitStructure.GPIO_Pin =  SMBUS_CLK_PIN;
    GPIO_Init(GPIOA, &GPIO_InitStructure);
     
    /* Configure the I2C clock source. The clock is derived from the HSI */
    RCC_I2CCLKConfig(RCC_I2C1CLK_SYSCLK);// vai HSI/ SYSCLK
     
     
    /* AF - alternate function */
    GPIO_PinAFConfig(GPIOA, GPIO_PinSource10, GPIO_AF_4); // vai SMBUS_DATA_PIN
    GPIO_PinAFConfig(GPIOA, GPIO_PinSource9, GPIO_AF_4);  // vai SMBUS_CLK_PIN
     
     
     
    // I2C configuration
     
    I2C_DeInit(I2C1);
    I2C_Cmd(I2C1, DISABLE);
    I2C_InitStructure.I2C_Timing = 0xB0420F13; //B0420F13 vai     moshk sitā 0x00901D23
    I2C_InitStructure.I2C_AnalogFilter = I2C_AnalogFilter_Enable;// analogaistroksnu filtrs ieslegts
    I2C_InitStructure.I2C_DigitalFilter = 0x00; // digitalais troksnu filtrs izslegts
    I2C_InitStructure.I2C_Mode = I2C_Mode_I2C; // pec tam buus siten I2c uz SMBus modi janomaina
    I2C_InitStructure.I2C_OwnAddress1 = 0x40;// kaa tiek izveeleeta adrese
    I2C_InitStructure.I2C_Ack = I2C_Ack_Enable; // sitam jaabut bus ieslegtam ari tad kad SMBus buus laikam
    I2C_InitStructure.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit;
 
 
    I2C_Cmd(I2C1, ENABLE);
    I2C_Init(I2C1, &I2C_InitStructure);
     
    I2C_TimeoutAConfig(I2C1, 0x63);
    I2C_TimeoutBConfig(I2C1, 0x1F);
     
     
}
 
 
extern "C"
{
    void EXTI4_15_IRQHandler(void)
    {
        /* Make sure that interrupt flag is set */
        if (EXTI_GetITStatus(EXTI_Line7) != RESET) {
            /* Do your stuff when PA0 is changed */
             
            while (state == 0)
            {
                if ((GPIOA->IDR & BAT_PLUGIN_PIN) == 0) // vai baterija ir piesprausta
                {
                    plugin_time++;
                }
             
                if ((GPIOA->IDR & BAT_PLUGIN_PIN) != 0) // vai baterija ir norauta
                {
                    plugin_time--;
                }  
                 
                if (plugin_time == 10000)
                {
                    state = 1;
                }
                if (plugin_time == -10000)
                {
                    state = 1;
                    current_mode = 2;
                    GPIOB->BRR = LI_CHRG_SHUTDOWN_PIN;
                         
                }
            }
            plugin_time = 0;
            state = 0;
            /*
            for (delay_laiks = 1;delay_laiks < 100000;delay_laiks++)  // 0.25s
            {
                 
            }
            current_mode = 2;
            */
        /* Clear interrupt flag */
            EXTI_ClearITPendingBit(EXTI_Line7);
        }  
    }
}
 
 
 
int main(void)
{
     
    PIN_CONFIGURATION();
     
    while (1)
    {
        GPIOA->BSRR = PRECHARGE_BULKCHARGE_PIN;
        GPIOA->BSRR = CHARGER_SHUTDOWN_PIN;
        while (1)
        {
            while (ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC) == RESET)
                ;
         
            LI_bat_adc_value = (ADC_GetConversionValue(ADC1));
         
            LI_bat_node = (LI_bat_adc_value * 3.3) / 4096;
        }
         
    */
     
    while (1)  
    {
        if ((GPIOA->IDR & BAT_PLUGIN_PIN) == 0) // parbauda vai baterija piesprausta
        {
             
             
            /// NOLASA NO BATERIJAS NEPIECIESAMOS DATUS/////
//////////////////////////// SPRIEGUMS //////////////////////////////
            SMBUS_FLAG_BUSY();
             
            I2C_TransferHandling(I2C1, 0x16, 1, I2C_SoftEnd_Mode, I2C_Generate_Start_Write);
            //while ((I2C1->CR2 & 0x200) == 0x200) {    ;}
            //while (I2C_GetFlagStatus(I2C1, I2C_FLAG_TXE) == 0) {;}
            I2C_SendData(I2C1, 0x9);
            I2C_ExtendedClockTimeoutCmd(I2C1, ENABLE);
            SMBUS_FLAG_BUSY();
            //while (I2C_GetFlagStatus(I2C1, I2C_FLAG_TXE) == 0) {  ;}
            I2C_TransferHandling(I2C1, 0x16, 2, I2C_SoftEnd_Mode, I2C_Generate_Start_Read);
            //while ((I2C1->CR2 & 0x200) == 0x200) {;}
            I2C_SlaveByteControlCmd(I2C1, ENABLE);// In order to allow byte ACK control in slave reception mode, Slave Byte Control mode must be enabled by setting the SBC bit in the I2C_CR1 register. This is required to be compliant with SMBus standards.
            while (I2C_GetFlagStatus(I2C1, I2C_FLAG_RXNE) == 0) // tas pats kas read_SMBus, bet pievienots klaa tas ka vertibas tiek nomestas uz 0. nomeshanu uz 0 var likt 1 funkcijaa, jo izies cauri visiem while loopiem tapat
            {
                i++;
                if (i == 10000)
                {
                    i = 0;
                    volt_A = 0;
                    volt_B = 0;
                    temp_A = 0;
                    temp_B = 0;
                    temp = 0;
                    relative_SOC = 0;
                    time_to_full_A = 0;
                    time_to_full_B = 0;
                    time_to_empty_A = 0;
                    time_to_empty_B = 0;
                    current_A = 0;
                    current_B = 0;
                    cycle_A = 0;
                    cycle_B = 0;
                    break;
                }
            }
            i = 0;
            volt_A = I2C_ReceiveData(I2C1);
            read_SMBus();
            volt_B = I2C_ReceiveData(I2C1);
         
 
             
        ////////////////////////////////     TEMPERATURA          ///////////////////////////////
         
     
            SMBUS_FLAG_BUSY();
            I2C_TransferHandling(I2C1, 0x16, 1, I2C_SoftEnd_Mode, I2C_Generate_Start_Write);
            //while ((I2C1->CR2 & 0x200) == 0x200) {    ;}
            //while (I2C_GetFlagStatus(I2C1, I2C_FLAG_TXE) == 0) {  ;}
            I2C_SendData(I2C1, 0x8);
            I2C_ExtendedClockTimeoutCmd(I2C1, ENABLE);
            SMBUS_FLAG_BUSY();
            //while (I2C_GetFlagStatus(I2C1, I2C_FLAG_TXE) == 0) {  ;}
            I2C_TransferHandling(I2C1, 0x16, 2, I2C_SoftEnd_Mode, I2C_Generate_Start_Read);
            //while ((I2C1->CR2 & 0x200) == 0x200) {    ;}
            I2C_SlaveByteControlCmd(I2C1, ENABLE);// In order to allow byte ACK control in slave reception mode, Slave Byte Control mode must be enabled by setting the SBC bit in the I2C_CR1 register. This is required to be compliant with SMBus standards.
            read_SMBus();
            temp_A = I2C_ReceiveData(I2C1);
            read_SMBus();
            temp_B = I2C_ReceiveData(I2C1);
         
     
         
        ///////////////////////////////// STATE OF CHARGE ////////////////
 
            SMBUS_FLAG_BUSY();
            I2C_TransferHandling(I2C1, 0x16, 1, I2C_SoftEnd_Mode, I2C_Generate_Start_Write);
            //while ((I2C1->CR2 & 0x200) == 0x200) {    ;}
            //while (I2C_GetFlagStatus(I2C1, I2C_FLAG_TXE) == 0) {  ;}
            I2C_SendData(I2C1, 0x0d);
            I2C_ExtendedClockTimeoutCmd(I2C1, ENABLE);
            SMBUS_FLAG_BUSY();
//  while (I2C_GetFlagStatus(I2C1, I2C_FLAG_TXE) == 0) {    ;}
            I2C_TransferHandling(I2C1, 0x16, 1, I2C_SoftEnd_Mode, I2C_Generate_Start_Read);
            //while ((I2C1->CR2 & 0x200) == 0x200) {;}
            I2C_SlaveByteControlCmd(I2C1, ENABLE);// In order to allow byte ACK control in slave reception mode, Slave Byte Control mode must be enabled by setting the SBC bit in the I2C_CR1 register. This is required to be compliant with SMBus standards.
            read_SMBus();
            relative_SOC = I2C_ReceiveData(I2C1);
 
         
         
            //////////////////////////// current //////////////////////////////
            SMBUS_FLAG_BUSY();
            I2C_TransferHandling(I2C1, 0x16, 1, I2C_SoftEnd_Mode, I2C_Generate_Start_Write);
            //while ((I2C1->CR2 & 0x200) == 0x200) {;}
            //while (I2C_GetFlagStatus(I2C1, I2C_FLAG_TXE) == 0) {  ;}
            I2C_SendData(I2C1, 0x0a);
            I2C_ExtendedClockTimeoutCmd(I2C1, ENABLE);
            SMBUS_FLAG_BUSY();
            //while (I2C_GetFlagStatus(I2C1, I2C_FLAG_TXE) == 0) {;}
            I2C_TransferHandling(I2C1, 0x16, 2, I2C_SoftEnd_Mode, I2C_Generate_Start_Read);
            //while ((I2C1->CR2 & 0x200) == 0x200) {    ;}
            I2C_SlaveByteControlCmd(I2C1, ENABLE);// In order to allow byte ACK control in slave reception mode, Slave Byte Control mode must be enabled by setting the SBC bit in the I2C_CR1 register. This is required to be compliant with SMBus standards.
            read_SMBus();
            current_A = I2C_ReceiveData(I2C1);
            read_SMBus();
            current_B = I2C_ReceiveData(I2C1);
         
         
            //////////////////////////// cycle count //////////////////////////////
            SMBUS_FLAG_BUSY();
            I2C_TransferHandling(I2C1, 0x16, 1, I2C_SoftEnd_Mode, I2C_Generate_Start_Write);
            //while ((I2C1->CR2 & 0x200) == 0x200) {;}
            //while (I2C_GetFlagStatus(I2C1, I2C_FLAG_TXE) == 0) {;}
            I2C_SendData(I2C1, 0x17);
            I2C_ExtendedClockTimeoutCmd(I2C1, ENABLE);
            SMBUS_FLAG_BUSY();
            //while (I2C_GetFlagStatus(I2C1, I2C_FLAG_TXE) == 0) {;}
            I2C_TransferHandling(I2C1, 0x16, 2, I2C_SoftEnd_Mode, I2C_Generate_Start_Read);
            //while ((I2C1->CR2 & 0x200) == 0x200) {;}
            I2C_SlaveByteControlCmd(I2C1, ENABLE);// In order to allow byte ACK control in slave reception mode, Slave Byte Control mode must be enabled by setting the SBC bit in the I2C_CR1 register. This is required to be compliant with SMBus standards.
            read_SMBus();
            cycle_A = I2C_ReceiveData(I2C1);
            read_SMBus();
            cycle_B = I2C_ReceiveData(I2C1);
         
         
        }
        else
        {
            GPIOB->BRR = LI_CHRG_SHUTDOWN_PIN; // izsleedz chargeri
            current_mode = 2; // unplugged mode
             
        // Nav piesprausta baterija, deg sarkans LEDs
            GPIOA->BRR = BATTERY_STATUS_GOOD_LED;
            GPIOB->BSRR = BATTERY_STATUS_BAD_LED;
            GPIOA->BSRR = CHARGING_STATUS_LED;
     
             
        }
    }
}
     
         
        

Maybe someone can point out major errors in code that may trigger this hung up? I can't even connect to BQ3060 with EV2300, it just NACKs everything. I added my logic analyzer file so you can see, what is going on on SMBus while it is working fine.

Outcomes