Hello Kopke,
You are right. To select I2C4 on a NUCLEO-F767ZI board it’s enough to create an I2C object with two arguments - the associated SDA and the SCL pin names:
I2C i2c(D2, D4);
The I2C Mbed documentation is available here.
All the hard work of the initialization and settings is done by Mbed as follows:
The I2C
constructor is implemented in I2C.cpp.
I2C::I2C(PinName sda, PinName scl) :
#if DEVICE_I2C_ASYNCH
_irq(this), _usage(DMA_USAGE_NEVER), _deep_sleep_locked(false),
#endif
_i2c(), _hz(100000)
{
lock();
// The init function also set the frequency to 100000
_sda = sda;
_scl = scl;
recover(sda, scl);
i2c_init(&_i2c, _sda, _scl);
// Used to avoid unnecessary frequency updates
_owner = this;
unlock();
}
The constructor calls the i2c_init(&_i2c, _sda, _scl)
function for the selected target (in your case the STM boards) implemented in i2c_api.c.
void i2c_init(i2c_t *obj, PinName sda, PinName scl)
{
struct i2c_s *obj_s = I2C_S(obj);
// Determine the I2C to use
I2CName i2c_sda = (I2CName)pinmap_peripheral(sda, PinMap_I2C_SDA);
I2CName i2c_scl = (I2CName)pinmap_peripheral(scl, PinMap_I2C_SCL);
obj_s->sda = sda;
obj_s->scl = scl;
obj_s->i2c = (I2CName)pinmap_merge(i2c_sda, i2c_scl);
MBED_ASSERT(obj_s->i2c != (I2CName)NC);
#if defined I2C1_BASE
// Enable I2C1 clock and pinout if not done
if (obj_s->i2c == I2C_1) {
obj_s->index = 0;
__HAL_RCC_I2C1_CLK_ENABLE();
// Configure I2C pins
obj_s->event_i2cIRQ = I2C1_EV_IRQn;
obj_s->error_i2cIRQ = I2C1_ER_IRQn;
}
#endif
#if defined I2C2_BASE
// Enable I2C2 clock and pinout if not done
if (obj_s->i2c == I2C_2) {
obj_s->index = 1;
__HAL_RCC_I2C2_CLK_ENABLE();
obj_s->event_i2cIRQ = I2C2_EV_IRQn;
obj_s->error_i2cIRQ = I2C2_ER_IRQn;
}
#endif
#if defined I2C3_BASE
// Enable I2C3 clock and pinout if not done
if (obj_s->i2c == I2C_3) {
obj_s->index = 2;
__HAL_RCC_I2C3_CLK_ENABLE();
obj_s->event_i2cIRQ = I2C3_EV_IRQn;
obj_s->error_i2cIRQ = I2C3_ER_IRQn;
}
#endif
#if defined I2C4_BASE
// Enable I2C3 clock and pinout if not done
if (obj_s->i2c == I2C_4) {
obj_s->index = 3;
__HAL_RCC_I2C4_CLK_ENABLE();
obj_s->event_i2cIRQ = I2C4_EV_IRQn;
obj_s->error_i2cIRQ = I2C4_ER_IRQn;
}
#endif
#if defined FMPI2C1_BASE
// Enable I2C3 clock and pinout if not done
if (obj_s->i2c == FMPI2C_1) {
obj_s->index = 4;
__HAL_RCC_FMPI2C1_CLK_ENABLE();
obj_s->event_i2cIRQ = FMPI2C1_EV_IRQn;
obj_s->error_i2cIRQ = FMPI2C1_ER_IRQn;
}
#endif
// Configure I2C pins
pinmap_pinout(sda, PinMap_I2C_SDA);
pinmap_pinout(scl, PinMap_I2C_SCL);
pin_mode(sda, OpenDrainNoPull);
pin_mode(scl, OpenDrainNoPull);
// I2C configuration
// Default hz value used for timeout computation
if (!obj_s->hz) {
obj_s->hz = 100000; // 100 kHz per default
}
// Reset to clear pending flags if any
i2c_hw_reset(obj);
i2c_frequency(obj, obj_s->hz);
#if DEVICE_I2CSLAVE
// I2C master by default
obj_s->slave = 0;
obj_s->pending_slave_tx_master_rx = 0;
obj_s->pending_slave_rx_maxter_tx = 0;
#endif
// I2C Xfer operation init
obj_s->event = 0;
obj_s->XferOperation = I2C_FIRST_AND_LAST_FRAME;
#ifdef I2C_IP_VERSION_V2
obj_s->pending_start = 0;
#endif
}
After that you can change the frequency from the default 100 kHz to whatever your need by calling the I2C class’ frequency
method:
i2c.frequency(new_frequency);
which is implemented in I2C.cpp.
void I2C::frequency(int hz)
{
lock();
_hz = hz;
// We want to update the frequency even if we are already the bus owners
i2c_frequency(&_i2c, _hz);
// Updating the frequency of the bus we become the owners of it
_owner = this;
unlock();
}
The frequency
method calls the i2c_frequency(&_i2c, _hz)
function which is for STM targets implemented in i2c_api.c.
void i2c_frequency(i2c_t *obj, int hz)
{
int timeout;
struct i2c_s *obj_s = I2C_S(obj);
I2C_HandleTypeDef *handle = &(obj_s->handle);
// wait before init
timeout = BYTE_TIMEOUT;
while ((__HAL_I2C_GET_FLAG(handle, I2C_FLAG_BUSY)) && (--timeout != 0));
#ifdef I2C_IP_VERSION_V1
handle->Init.ClockSpeed = hz;
handle->Init.DutyCycle = I2C_DUTYCYCLE_2;
#endif
#ifdef I2C_IP_VERSION_V2
/* Only predefined timing for below frequencies are supported */
MBED_ASSERT((hz == 100000) || (hz == 400000) || (hz == 1000000));
handle->Init.Timing = get_i2c_timing(hz);
// Enable the Fast Mode Plus capability
if (hz == 1000000) {
#if defined(I2C1_BASE) && defined(__HAL_SYSCFG_FASTMODEPLUS_ENABLE) && defined (I2C_FASTMODEPLUS_I2C1)
if (obj_s->i2c == I2C_1) {
HAL_I2CEx_EnableFastModePlus(I2C_FASTMODEPLUS_I2C1);
}
#endif
#if defined(I2C2_BASE) && defined(__HAL_SYSCFG_FASTMODEPLUS_ENABLE) && defined (I2C_FASTMODEPLUS_I2C2)
if (obj_s->i2c == I2C_2) {
HAL_I2CEx_EnableFastModePlus(I2C_FASTMODEPLUS_I2C2);
}
#endif
#if defined(I2C3_BASE) && defined(__HAL_SYSCFG_FASTMODEPLUS_ENABLE) && defined (I2C_FASTMODEPLUS_I2C3)
if (obj_s->i2c == I2C_3) {
HAL_I2CEx_EnableFastModePlus(I2C_FASTMODEPLUS_I2C3);
}
#endif
#if defined(I2C4_BASE) && defined(__HAL_SYSCFG_FASTMODEPLUS_ENABLE) && defined (I2C_FASTMODEPLUS_I2C4)
if (obj_s->i2c == I2C_4) {
HAL_I2CEx_EnableFastModePlus(I2C_FASTMODEPLUS_I2C4);
}
#endif
}
#endif //I2C_IP_VERSION_V2
/*##-1- Configure the I2C clock source. The clock is derived from the SYSCLK #*/
#if defined(I2C1_BASE) && defined (__HAL_RCC_I2C1_CONFIG)
if (obj_s->i2c == I2C_1) {
__HAL_RCC_I2C1_CONFIG(I2CAPI_I2C1_CLKSRC);
}
#endif
#if defined(I2C2_BASE) && defined(__HAL_RCC_I2C2_CONFIG)
if (obj_s->i2c == I2C_2) {
__HAL_RCC_I2C2_CONFIG(I2CAPI_I2C2_CLKSRC);
}
#endif
#if defined(I2C3_BASE) && defined(__HAL_RCC_I2C3_CONFIG)
if (obj_s->i2c == I2C_3) {
__HAL_RCC_I2C3_CONFIG(I2CAPI_I2C3_CLKSRC);
}
#endif
#if defined(I2C4_BASE) && defined(__HAL_RCC_I2C4_CONFIG)
if (obj_s->i2c == I2C_4) {
__HAL_RCC_I2C4_CONFIG(I2CAPI_I2C4_CLKSRC);
}
#endif
#ifdef I2C_ANALOGFILTER_ENABLE
/* Enable the Analog I2C Filter */
HAL_I2CEx_ConfigAnalogFilter(handle, I2C_ANALOGFILTER_ENABLE);
#endif
// I2C configuration
handle->Init.AddressingMode = I2C_ADDRESSINGMODE_7BIT;
handle->Init.DualAddressMode = I2C_DUALADDRESS_DISABLE;
handle->Init.GeneralCallMode = I2C_GENERALCALL_DISABLE;
handle->Init.NoStretchMode = I2C_NOSTRETCH_DISABLE;
handle->Init.OwnAddress1 = 0;
handle->Init.OwnAddress2 = 0;
HAL_I2C_Init(handle);
/* store frequency for timeout computation */
obj_s->hz = hz;
}