2020-04-15 12:04 PM
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);
}
}
Solved! Go to Solution.
2020-04-15 12:08 PM
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
2020-04-15 12:08 PM
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