cancel
Showing results for 
Search instead for 
Did you mean: 

I2C Slave transmit without HAL

onio
Senior

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

1 ACCEPTED SOLUTION

Accepted Solutions
onio
Senior

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

View solution in original post

1 REPLY 1
onio
Senior

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