Skip to main content
onio
Associate III
April 15, 2020
Solved

I2C Slave transmit without HAL

  • April 15, 2020
  • 1 reply
  • 1746 views

I am writing a C++ I2C driver that would operate as a SLAVE device operating under

interrupt and without CLOCK STRETCHING using STM32H732zi nucleo board

My problem is that the first byte that I tried to transmit seems to always be

0xFF no matter what I do. I have tried to show code snippet here.

Can anyone see anything I might be doing wrong in the way I am handling the

interrupt service routine. I have tried different implementation and this seems

to show the most consistent behavior.

Even though the code seems to be partially work I noticed the odd behavior

the interrupt firing about 200 times which does not make sense to me especially

when I am the slave device is the only one on the bus with the master.

I found that if I test and clear the I2C_ISR_ADDR flag in the ISR then the

interrupt fires about 7 times since I am only send about 7 bytes but then

it only output all 0xFF.

//
// i2cinterface.h
//
#ifndef I2C_INTERFACE_H
#define I2C_INTERFACE_H
 
enum I2CEnum {
 i2c1 = 0,
 i2c2 = 1,
 i2c3 = 2,
 i2c4 = 3,
 max_instances
};
 
class I2CInterface // : public CommsInterface
{
public:
 I2CInterface(I2C_HandleTypeDef& i2c);
 ~I2CInterface();
 bool Open(void);
 static I2CInterface* instances[max_instances];
 static void ISRHandler(I2CInterface* param);
 void ProcessIncommingCommands(uint8_t);
private:
 int state;
 int txcount;
 uint8_t *txdataptr;
 I2C_HandleTypeDef m_hi2c;
};
 
#endif
 
/************************************************************/
 
///
/// \file - i2c_interface.cpp
/// 
/// \brief - Modbule used for impkement i2c interface
///
/// This is the main interface file for i2c
#include "main.h"
#include "i2c_interface.h"
 
extern I2C_HandleTypeDef hi2c2;
 
// test variables
uint8_t frequency[4] = {3,2,1,9};
uint8_t power[48] = {10,11,12,13,14,15,16,17,18,19,20}; // assume real code would have 48 bytes
 
uint8_t txbuf[64];
uint32_t g_isr[64];
uint32_t g_isr_count = 0;
// end test variables
 
I2CInterface* I2CInterface::instances[] = {nullptr,nullptr,nullptr,nullptr}; 
 
I2CInterface::I2CInterface (I2C_HandleTypeDef& hi2c)
{
 m_hi2c = hi2c;
}
 
I2CInterface::~I2CInterface()
{
}
 
bool I2CInterface::Open(void)
{
 bool retval = true;
 
 if ( m_hi2c.Instance == I2C1 )
 {
 instances[i2c1] = this;
 }
 else if ( m_hi2c.Instance == I2C2 )
 {
 instances[i2c2] = this;
 }
 else if ( m_hi2c.Instance == I2C3 )
 {
 instances[i2c3] = this;
 }
 else if ( m_hi2c.Instance == I2C4 )
 {
 instances[i2c4] = this;
 }
 else
 {
 retval = false;
 }
 if ( retval )
 {
 return (retval && (HAL_I2C_EnableListen_IT(&m_hi2c) == HAL_OK));
 }
 return retval;
}
 
//
// Handle I2C interrupt events
//
uint8_t trace[128];
int trace_count = 0;
void I2CInterface::ISRHandler(I2CInterface* param)
{
 I2C_TypeDef* Instance = param->m_hi2c.Instance;
 trace_count++;
 
 if ((( Instance->ISR & I2C_ISR_TXIS) == I2C_ISR_TXIS ) && 
 (( Instance->ISR & I2C_ISR_DIR) == I2C_ISR_DIR ) )
 {
 g_isr[g_isr_count++] = Instance->ISR;
 uint8_t data = param->txdataptr[param->txcount];
 txbuf[param->txcount] = data;
 Instance->TXDR = data;
 param->txcount++;
 }
 if (( Instance->ISR & I2C_ISR_RXNE) == I2C_ISR_RXNE )
 {
 param->ProcessIncommingCommands(Instance->RXDR);
 }
 if (( Instance->ISR & I2C_ISR_NACKF) == I2C_ISR_NACKF )
 {
 Instance->ICR |= I2C_ICR_ADDRCF; // clear flag once only when nack received 
 Instance->ICR |= I2C_ICR_NACKCF; 
 Instance->ISR |= I2C_ISR_TXE; // flush TXDR
 Instance->ICR |= I2C_ICR_OVRCF;
 g_isr_count = param->state = 0;
 }
 if (( Instance->ISR & I2C_ISR_STOPF) == I2C_ISR_STOPF )
 {
 Instance->ICR |= I2C_ICR_STOPCF;
 }
 if (( Instance->ISR & I2C_ISR_OVR) == I2C_ISR_OVR )
 {
 Instance->ICR |= I2C_ICR_OVRCF;
 }
}
 
void I2CInterface::ProcessIncommingCommands(uint8_t ch)
{
 switch ( state ) 
 {
 case 0:
 if ( ch == 2 ) 
 {
 state++;
 txcount = 0;
 }
 break;
 case 1:
 switch ( ch )
 {
 case 0x31:
 txdataptr = frequency; // point to buffer to transmit
 break;
 
 case 0x32:
 txdataptr = power;
 break;
 }
 break;
 }
}
 
extern "C"
{
void I2C1_EV_IRQHandler(void)
{
 I2CInterface::ISRHandler(I2CInterface::instances[i2c1]);
}
void I2C2_EV_IRQHandler(void)
{
 I2CInterface::ISRHandler(I2CInterface::instances[i2c2]);
}
void I2C3_EV_IRQHandler(void)
{
 I2CInterface::ISRHandler(I2CInterface::instances[i2c3]);
}
void I2C4_EV_IRQHandler(void)
{
 I2CInterface::ISRHandler(I2CInterface::instances[i2c4]);
}
}
 
/************************************************************/
 
// mainTask.cpp
 
#include "main.h"
#include "app_main.h"
 
 
extern I2C_HandleTypeDef hi2c1;
 
void impTask(void *argument)
{
 I2CInterface i2c(hi2c1);
 i2c.Open();
	
 while (1) 
 {
 osDelay(500);
 HAL_GPIO_TogglePin(GPIOB, LD2_Pin);
 }
}

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

    Solved the problem in my above code. The fix can be found in code shown. I have also removed all the debug code that I put in the initial example. I thought to leave it here just in case someone is trying to do something similar using the STM32 as a slave device without HAL.

    If there are better and cleaner solution I would also like to know. So please post here as I would periodically log in to check. Many thanks in advance.

    #ifndef I2C_INTERFACE_H
    #define I2C_INTERFACE_H
     
    enum I2CEnum {
     i2c1 = 0,
     i2c2 = 1,
     i2c3 = 2,
     i2c4 = 3,
     max_instances
    };
     
    class I2CInterface // : public CommsInterface
    {
    public:
     enum STOPF{STOPF_START = 0, STOPF_REPEAT_START = 1};
     I2CInterface(I2C_HandleTypeDef& i2c);
     ~I2CInterface();
     
     bool Open(void);
     static I2CInterface* instances[max_instances];
     static void ISRHandler(I2CInterface* param);
     void ProcessIncommingCommands(uint8_t);
    private:
     int state;
     int txcount;
     int stopf;
     uint8_t *txdataptr;
     I2C_HandleTypeDef m_hi2c;
    };
    #endif
     
    /************************************************/
    ///
    /// \file - i2c_interface.cpp
    /// 
    /// \brief - Modbule used for impkement i2c interface
    ///
    /// This is the main interface file for i2c
    #include "main.h"
    #include "i2c_interface.h"
     
    extern I2C_HandleTypeDef hi2c2;
     
    uint8_t frequency[4] = {3,2,1,9};
    uint8_t power[48] = {10,11,12,13,14,15,16,17,18,19,20};
     
    I2CInterface* I2CInterface::instances[] = {nullptr,nullptr,nullptr,nullptr}; 
     
    I2CInterface::I2CInterface (I2C_HandleTypeDef& hi2c)
    {
     m_hi2c = hi2c;
    }
     
    I2CInterface::~I2CInterface()
    {
    }
     
    bool I2CInterface::Open(void)
    {
     bool retval = true;
     
     if ( m_hi2c.Instance == I2C1 )
     {
     instances[i2c1] = this;
     }
     else if ( m_hi2c.Instance == I2C2 )
     {
     instances[i2c2] = this;
     }
     else if ( m_hi2c.Instance == I2C3 )
     {
     instances[i2c3] = this;
     }
     else if ( m_hi2c.Instance == I2C4 )
     {
     instances[i2c4] = this;
     }
     else
     {
     retval = false;
     }
     if ( retval )
     {
     return (retval && (HAL_I2C_EnableListen_IT(&m_hi2c) == HAL_OK));
     }
     return retval;
    }
     
    //
    // Handle I2C interrupt events
    //
    void I2CInterface::ISRHandler(I2CInterface* param)
    {
     I2C_TypeDef* Instance = param->m_hi2c.Instance;
     
     if ((( Instance->ISR & I2C_ISR_TXIS) == I2C_ISR_TXIS ) && 
     (( Instance->ISR & I2C_ISR_DIR) == I2C_ISR_DIR ) )
     {
     Instance->TXDR = param->txdataptr[param->txcount];
     param->txcount++;
     }
     if (( Instance->ISR & I2C_ISR_RXNE) == I2C_ISR_RXNE )
     {
     param->ProcessIncommingCommands(Instance->RXDR);
     }
     if (( Instance->ISR & I2C_ISR_NACKF) == I2C_ISR_NACKF )
     {
     Instance->ICR |= I2C_ICR_ADDRCF; // clear flags once only when nack received 
     Instance->ICR |= I2C_ICR_NACKCF; 
     Instance->ISR |= I2C_ISR_TXE; // flush TXDR
     param->state = 0;
     }
     
     if (( Instance->ISR & I2C_ISR_OVR) == I2C_ISR_OVR )
     {
     Instance->ICR |= I2C_ICR_OVRCF;
     } 
     
     if (( Instance->ISR & I2C_ISR_STOPF) == I2C_ISR_STOPF )
     {
     Instance->ICR |= I2C_ICR_STOPCF;
     if (param->stopf == STOPF_REPEAT_START)
     {
     Instance->TXDR = param->txdataptr[param->txcount];
     param->txcount++;
     }
     param->stopf++;
     }
     
    }
     
    void I2CInterface::ProcessIncommingCommands(uint8_t ch)
    {
     switch ( state ) 
     {
     case 0:
     if ( ch == 2 ) 
     {
     state++;
     txcount = 0;
     stopf = STOPF_START;
     }
     break;
     
     case 1:
     switch ( ch )
     {
     case 0x31:
     txdataptr = frequency; // point to buffer to transmit
     break;
     
     case 0x32:
     txdataptr = power;
     break;
     }
     break;
     }
    }
     
    extern "C"
    {
    void I2C1_EV_IRQHandler(void)
    {
     I2CInterface::ISRHandler(I2CInterface::instances[i2c1]);
    }
    void I2C2_EV_IRQHandler(void)
    {
     I2CInterface::ISRHandler(I2CInterface::instances[i2c2]);
    }
    void I2C3_EV_IRQHandler(void)
    {
     I2CInterface::ISRHandler(I2CInterface::instances[i2c3]);
    }
    void I2C4_EV_IRQHandler(void)
    {
     I2CInterface::ISRHandler(I2CInterface::instances[i2c4]);
    }
    }
     
    /**************************************/
    #include "main.h"
    #include "app_main.h"
     
    extern I2C_HandleTypeDef hi2c1;
     
    void impTask(void *argument)
    {
     I2CInterface i2c(hi2c1);
     i2c.Open();
     
     while (1) 
     {
     osDelay(500);
     HAL_GPIO_TogglePin(GPIOB, LD2_Pin);
     }
    }

    below

    1 reply

    onio
    onioAuthorBest answer
    Associate III
    April 15, 2020

    Solved the problem in my above code. The fix can be found in code shown. I have also removed all the debug code that I put in the initial example. I thought to leave it here just in case someone is trying to do something similar using the STM32 as a slave device without HAL.

    If there are better and cleaner solution I would also like to know. So please post here as I would periodically log in to check. Many thanks in advance.

    #ifndef I2C_INTERFACE_H
    #define I2C_INTERFACE_H
     
    enum I2CEnum {
     i2c1 = 0,
     i2c2 = 1,
     i2c3 = 2,
     i2c4 = 3,
     max_instances
    };
     
    class I2CInterface // : public CommsInterface
    {
    public:
     enum STOPF{STOPF_START = 0, STOPF_REPEAT_START = 1};
     I2CInterface(I2C_HandleTypeDef& i2c);
     ~I2CInterface();
     
     bool Open(void);
     static I2CInterface* instances[max_instances];
     static void ISRHandler(I2CInterface* param);
     void ProcessIncommingCommands(uint8_t);
    private:
     int state;
     int txcount;
     int stopf;
     uint8_t *txdataptr;
     I2C_HandleTypeDef m_hi2c;
    };
    #endif
     
    /************************************************/
    ///
    /// \file - i2c_interface.cpp
    /// 
    /// \brief - Modbule used for impkement i2c interface
    ///
    /// This is the main interface file for i2c
    #include "main.h"
    #include "i2c_interface.h"
     
    extern I2C_HandleTypeDef hi2c2;
     
    uint8_t frequency[4] = {3,2,1,9};
    uint8_t power[48] = {10,11,12,13,14,15,16,17,18,19,20};
     
    I2CInterface* I2CInterface::instances[] = {nullptr,nullptr,nullptr,nullptr}; 
     
    I2CInterface::I2CInterface (I2C_HandleTypeDef& hi2c)
    {
     m_hi2c = hi2c;
    }
     
    I2CInterface::~I2CInterface()
    {
    }
     
    bool I2CInterface::Open(void)
    {
     bool retval = true;
     
     if ( m_hi2c.Instance == I2C1 )
     {
     instances[i2c1] = this;
     }
     else if ( m_hi2c.Instance == I2C2 )
     {
     instances[i2c2] = this;
     }
     else if ( m_hi2c.Instance == I2C3 )
     {
     instances[i2c3] = this;
     }
     else if ( m_hi2c.Instance == I2C4 )
     {
     instances[i2c4] = this;
     }
     else
     {
     retval = false;
     }
     if ( retval )
     {
     return (retval && (HAL_I2C_EnableListen_IT(&m_hi2c) == HAL_OK));
     }
     return retval;
    }
     
    //
    // Handle I2C interrupt events
    //
    void I2CInterface::ISRHandler(I2CInterface* param)
    {
     I2C_TypeDef* Instance = param->m_hi2c.Instance;
     
     if ((( Instance->ISR & I2C_ISR_TXIS) == I2C_ISR_TXIS ) && 
     (( Instance->ISR & I2C_ISR_DIR) == I2C_ISR_DIR ) )
     {
     Instance->TXDR = param->txdataptr[param->txcount];
     param->txcount++;
     }
     if (( Instance->ISR & I2C_ISR_RXNE) == I2C_ISR_RXNE )
     {
     param->ProcessIncommingCommands(Instance->RXDR);
     }
     if (( Instance->ISR & I2C_ISR_NACKF) == I2C_ISR_NACKF )
     {
     Instance->ICR |= I2C_ICR_ADDRCF; // clear flags once only when nack received 
     Instance->ICR |= I2C_ICR_NACKCF; 
     Instance->ISR |= I2C_ISR_TXE; // flush TXDR
     param->state = 0;
     }
     
     if (( Instance->ISR & I2C_ISR_OVR) == I2C_ISR_OVR )
     {
     Instance->ICR |= I2C_ICR_OVRCF;
     } 
     
     if (( Instance->ISR & I2C_ISR_STOPF) == I2C_ISR_STOPF )
     {
     Instance->ICR |= I2C_ICR_STOPCF;
     if (param->stopf == STOPF_REPEAT_START)
     {
     Instance->TXDR = param->txdataptr[param->txcount];
     param->txcount++;
     }
     param->stopf++;
     }
     
    }
     
    void I2CInterface::ProcessIncommingCommands(uint8_t ch)
    {
     switch ( state ) 
     {
     case 0:
     if ( ch == 2 ) 
     {
     state++;
     txcount = 0;
     stopf = STOPF_START;
     }
     break;
     
     case 1:
     switch ( ch )
     {
     case 0x31:
     txdataptr = frequency; // point to buffer to transmit
     break;
     
     case 0x32:
     txdataptr = power;
     break;
     }
     break;
     }
    }
     
    extern "C"
    {
    void I2C1_EV_IRQHandler(void)
    {
     I2CInterface::ISRHandler(I2CInterface::instances[i2c1]);
    }
    void I2C2_EV_IRQHandler(void)
    {
     I2CInterface::ISRHandler(I2CInterface::instances[i2c2]);
    }
    void I2C3_EV_IRQHandler(void)
    {
     I2CInterface::ISRHandler(I2CInterface::instances[i2c3]);
    }
    void I2C4_EV_IRQHandler(void)
    {
     I2CInterface::ISRHandler(I2CInterface::instances[i2c4]);
    }
    }
     
    /**************************************/
    #include "main.h"
    #include "app_main.h"
     
    extern I2C_HandleTypeDef hi2c1;
     
    void impTask(void *argument)
    {
     I2CInterface i2c(hi2c1);
     i2c.Open();
     
     while (1) 
     {
     osDelay(500);
     HAL_GPIO_TogglePin(GPIOB, LD2_Pin);
     }
    }

    below