In this blog, I will explain you how to use I2C Master interface on NXP MCXA Microcontroller. I2C is a simple two wire interface which can be used to connect different I2C devices on the bus. One of the most common uses of I2C is to connect a microcontroller to a peripheral device, such as a memory chip (EEPROM) or a display (character LCD, OLED, etc.) or any other I2C based sensor. I2C can also be used to connect multiple microcontrollers together, allowing them to communicate with each other and share data.
What is I2C Interface?
The Inter-Integrated Circuit (I2C) interface is a serial communication protocol that allows two or more integrated circuits (ICs) to communicate with each other. It is a two-wire interface, meaning that it only requires two wires to communicate: a clock line (SCL) and a data line (SDA).
I2C is a relatively simple protocol, making it easy to implement in hardware and software. It is also a very versatile protocol, and can be used to connect a wide variety of devices, including microcontrollers, sensors, and actuators.

Using I2C Interface on NXP MCXA Microcontroller is similar to using any other interface.
Example code can imported and referred. You can import from File > New > Import SDK Examples.

In order to use I2C, first we have to configure the system clock, port pins, then configure I2C port settings and then do I2C write and I2C read operations.
Configuring Clock and I2C Pins
System clock is set by calling BOARD_InitBootClocks(); which internally calls BOARD_BootClockFRO96M(); to set MCU frequency to 96Mhz.
For I2C clock Peripheral clock is attached to I2C. After clock configuration, Port and Pins are configured
/* Write to PORT0: Peripheral clock is enabled */
CLOCK_EnableClock(kCLOCK_GatePORT1);
RESET_ReleasePeripheralReset(kPORT1_RST_SHIFT_RSTn);
/* LPI2C0 peripheral is released from reset */
RESET_ReleasePeripheralReset(kLPI2C0_RST_SHIFT_RSTn);
/* Attach peripheral clock to LPI2C0*/
CLOCK_SetClockDiv(kCLOCK_DivLPI2C0, 1u);
CLOCK_AttachClk(kFRO12M_to_LPI2C0);
I2C SDA is configured on port P1_8 and I2C SCL is configured on P1_9
const port_pin_config_t port1_8_config = {/* Internal pull-up resistor is enabled */
kPORT_PullUp,
/* Low internal pull resistor value is selected. */
kPORT_LowPullResistor,
/* Fast slew rate is configured */
kPORT_FastSlewRate,
/* Passive input filter is disabled */
kPORT_PassiveFilterDisable,
/* Open drain output is enabled */
kPORT_OpenDrainEnable,
/* Low drive strength is configured */
kPORT_LowDriveStrength,
/* Normal drive strength is configured */
kPORT_NormalDriveStrength,
/* Pin is configured as LPI2C0_SCL */
kPORT_MuxAlt3,
/* Digital input enabled */
kPORT_InputBufferEnable,
/* Digital input is not inverted */
kPORT_InputNormal,
/* Pin Control Register fields [15:0] are not locked */
kPORT_UnlockRegister};
/* PORT1_8 (pin 34) is configured as LPI2C0_SDA */
PORT_SetPinConfig(PORT1, 8U, &port1_8_config);
const port_pin_config_t port1_9_config = {/* Internal pull-up resistor is enabled */
kPORT_PullUp,
/* Low internal pull resistor value is selected. */
kPORT_LowPullResistor,
/* Fast slew rate is configured */
kPORT_FastSlewRate,
/* Passive input filter is disabled */
kPORT_PassiveFilterDisable,
/* Open drain output is enabled */
kPORT_OpenDrainEnable,
/* Low drive strength is configured */
kPORT_LowDriveStrength,
/* Normal drive strength is configured */
kPORT_NormalDriveStrength,
/* Pin is configured as LPI2C0_SDA */
kPORT_MuxAlt3,
/* Digital input enabled */
kPORT_InputBufferEnable,
/* Digital input is not inverted */
kPORT_InputNormal,
/* Pin Control Register fields [15:0] are not locked */
kPORT_UnlockRegister};
/* PORT1_9 (pin 33) is configured as LPI2C0_SCL */
PORT_SetPinConfig(PORT1, 9U, &port1_9_config);
I2C Port Configuration is done with the following code where you can change the baud rate, etc.
#define LPI2C_MASTER_CLOCK_FREQUENCY CLOCK_GetLpi2cClkFreq()
lpi2c_master_config_t masterConfig;
/*
* masterConfig.debugEnable = false;
* masterConfig.ignoreAck = false;
* masterConfig.pinConfig = kLPI2C_2PinOpenDrain;
* masterConfig.baudRate_Hz = 100000U;
* masterConfig.busIdleTimeout_ns = 0;
* masterConfig.pinLowTimeout_ns = 0;
* masterConfig.sdaGlitchFilterWidth_ns = 0;
* masterConfig.sclGlitchFilterWidth_ns = 0;
*/
LPI2C_MasterGetDefaultConfig(&masterConfig);
/* Change the default baudrate configuration */
masterConfig.baudRate_Hz = 400000U;
/* Initialize the LPI2C master peripheral */
LPI2C_MasterInit(LPI2C0, &masterConfig, LPI2C_MASTER_CLOCK_FREQUENCY);
UTick is used for introducing Delay.
RESET_PeripheralReset(kUTICK0_RST_SHIFT_RSTn);
UTICK_Init(UTICK0);
static void UTickCallback(void)
{
utickExpired = true;
}
static void UTickDelay(uint32_t usec)
{
/* Set the UTICK timer to wake up the device from reduced power mode */
UTICK_SetTick(EXAMPLE_UTICK, kUTICK_Onetime, usec - 1, UTickCallback);
while (!utickExpired)
{
}
utickExpired = false;
}
We will see how to interface a Sensirionโs Temperature Humidity Sensor SHTC3 which has I2C Interface. I have an Adafruit SHTC3 breakout board which I will connect for the testing.
You can also refer the SHTC3 interface example I have previously done using CH32V003 MCU.

The only connection we need with NXP MCXA Microcontroller development board are 3.3V, GND, I2C SCL, I2C SDA.
I would suggest you go through the Sensirionโs datasheet to understand how the sensor works and how we will get the data out of sensor, which commands we need to send and what is the response I am going to get out of it. Please donโt jump directly to checking the code and testing it.
Reading I2C Temperature Humidity Sensor
It is recommended to reset the sensor after powerup by sending RESET command.
How to execute I2C write / Read on NXP MCXA Microcontroller
For I2C Write Data, various APIs are used. LPI2C_MasterStart() is used for starting I2C with write or read flag. This will basically send the device address with read/write flag and wait for acknowledgement from the device and return the status.
After that we need to check if NACK flag was received by checking return of LPI2C_MasterGetStatusFlags(). If ACK is received, we can proceed with sending data by using LPI2C_MasterSend() API which take buffer pointer input and buffer length. We will check the return value of the function if it is success or not and then finally we will call LPI2C_MasterStop() to complete one I2C transaction.
The only different in read operation is you need to call LPI2C_MasterReceive() after LPI2C_MasterStart().
See the code below how various commands are sent while interfacing SHT3C sensor and how data is received.
status_t SHTC3_Software_Reset(uint8_t address)
{
status_t reVal = kStatus_Success;
if (kStatus_Success == LPI2C_MasterStart(EXAMPLE_I2C_MASTER, address, kLPI2C_Write))
{
if ((LPI2C_MasterGetStatusFlags(EXAMPLE_I2C_MASTER)) & kLPI2C_MasterNackDetectFlag)
{
return kStatus_LPI2C_Nak;
}
else
{
reVal = LPI2C_MasterSend(EXAMPLE_I2C_MASTER,soft_reset_cmd_data, 2);
if(reVal != kStatus_Success)
{
if(reVal == kStatus_LPI2C_Nak)
{
LPI2C_MasterStop(EXAMPLE_I2C_MASTER);
return kStatus_LPI2C_Nak;
}
}
else if(reVal == kStatus_Success)
{
LPI2C_MasterStop(EXAMPLE_I2C_MASTER);
return kStatus_Success;
}
}
}else
{
return kStatus_LPI2C_Busy;
}
}
COMMAND is 0x80, 0x5D. The I2C Sensor address is 0x70.

After powerup reset command, Wakeup command needs to be sent then measurement trigger command and then temperature and humidity are read.
See the functions below.
status_t SHTC3_WakeUpCmd(uint8_t address)
{
status_t reVal = kStatus_Success;
if (kStatus_Success == LPI2C_MasterStart(EXAMPLE_I2C_MASTER, address, kLPI2C_Write))
{
if((LPI2C_MasterGetStatusFlags(EXAMPLE_I2C_MASTER)) & kLPI2C_MasterNackDetectFlag)
{
return kStatus_LPI2C_Nak;
}
else
{
reVal = LPI2C_MasterSend(EXAMPLE_I2C_MASTER, wakeup_cmd_data, 2);
if(reVal != kStatus_Success)
{
if(reVal == kStatus_LPI2C_Nak)
{
LPI2C_MasterStop(EXAMPLE_I2C_MASTER);
return kStatus_LPI2C_Nak;
}
}
else if(reVal == kStatus_Success)
{
LPI2C_MasterStop(EXAMPLE_I2C_MASTER);
return kStatus_Success;
}
}
}else
{
return kStatus_LPI2C_Busy;
}
}
status_t SHTC3_MeasureCmdTFCE(uint8_t address)
{
status_t reVal = kStatus_Success;
if (kStatus_Success == LPI2C_MasterStart(EXAMPLE_I2C_MASTER, address, kLPI2C_Write))
{
if ((LPI2C_MasterGetStatusFlags(EXAMPLE_I2C_MASTER)) & kLPI2C_MasterNackDetectFlag)
{
return kStatus_LPI2C_Nak;
}
else
{
reVal = LPI2C_MasterSend(EXAMPLE_I2C_MASTER, measure_cmd_data, 2);
if (reVal != kStatus_Success)
{
if(reVal == kStatus_LPI2C_Nak)
{
LPI2C_MasterStop(EXAMPLE_I2C_MASTER);
return kStatus_LPI2C_Nak;
}
}
else if(reVal == kStatus_Success)
{
LPI2C_MasterStop(EXAMPLE_I2C_MASTER);
return kStatus_Success;
}
}
}else
{
return kStatus_LPI2C_Busy;
}
}
status_t SHTC3_ReadTempFirstCE(uint8_t address, uint8_t *rec_Data,size_t length)
{
status_t reVal = kStatus_Success;
if (kStatus_Success == LPI2C_MasterStart(EXAMPLE_I2C_MASTER, address, kLPI2C_Read))
{
if ((LPI2C_MasterGetStatusFlags(EXAMPLE_I2C_MASTER)) & kLPI2C_MasterNackDetectFlag)
{
return kStatus_LPI2C_Nak;
}
else
{
reVal = LPI2C_MasterReceive(EXAMPLE_I2C_MASTER,rec_Data, length);
if(reVal != kStatus_Success)
{
if(reVal == kStatus_LPI2C_Nak)
{
LPI2C_MasterStop(EXAMPLE_I2C_MASTER);
return kStatus_LPI2C_Nak;
}
}
else if(reVal == kStatus_Success)
{
LPI2C_MasterStop(EXAMPLE_I2C_MASTER);
return kStatus_Success;
}
}
}else
{
return kStatus_LPI2C_Busy;
}
}
status_t SHTC3_WakeUpCmd(uint8_t address)
{
status_t reVal = kStatus_Success;
if (kStatus_Success == LPI2C_MasterStart(EXAMPLE_I2C_MASTER, address, kLPI2C_Write))
{
if((LPI2C_MasterGetStatusFlags(EXAMPLE_I2C_MASTER)) & kLPI2C_MasterNackDetectFlag)
{
return kStatus_LPI2C_Nak;
}
else
{
reVal = LPI2C_MasterSend(EXAMPLE_I2C_MASTER, wakeup_cmd_data, 2);
if(reVal != kStatus_Success)
{
if(reVal == kStatus_LPI2C_Nak)
{
LPI2C_MasterStop(EXAMPLE_I2C_MASTER);
return kStatus_LPI2C_Nak;
}
}
else if(reVal == kStatus_Success)
{
LPI2C_MasterStop(EXAMPLE_I2C_MASTER);
return kStatus_Success;
}
}
}else
{
return kStatus_LPI2C_Busy;
}
}
status_t SHTC3_MeasureCmdTFCE(uint8_t address)
{
status_t reVal = kStatus_Success;
if (kStatus_Success == LPI2C_MasterStart(EXAMPLE_I2C_MASTER, address, kLPI2C_Write))
{
if ((LPI2C_MasterGetStatusFlags(EXAMPLE_I2C_MASTER)) & kLPI2C_MasterNackDetectFlag)
{
return kStatus_LPI2C_Nak;
}
else
{
reVal = LPI2C_MasterSend(EXAMPLE_I2C_MASTER, measure_cmd_data, 2);
if (reVal != kStatus_Success)
{
if(reVal == kStatus_LPI2C_Nak)
{
LPI2C_MasterStop(EXAMPLE_I2C_MASTER);
return kStatus_LPI2C_Nak;
}
}
else if(reVal == kStatus_Success)
{
LPI2C_MasterStop(EXAMPLE_I2C_MASTER);
return kStatus_Success;
}
}
}else
{
return kStatus_LPI2C_Busy;
}
}
status_t SHTC3_ReadTempFirstCE(uint8_t address, uint8_t *rec_Data,size_t length)
{
status_t reVal = kStatus_Success;
if (kStatus_Success == LPI2C_MasterStart(EXAMPLE_I2C_MASTER, address, kLPI2C_Read))
{
if ((LPI2C_MasterGetStatusFlags(EXAMPLE_I2C_MASTER)) & kLPI2C_MasterNackDetectFlag)
{
return kStatus_LPI2C_Nak;
}
else
{
reVal = LPI2C_MasterReceive(EXAMPLE_I2C_MASTER,rec_Data, length);
if(reVal != kStatus_Success)
{
if(reVal == kStatus_LPI2C_Nak)
{
LPI2C_MasterStop(EXAMPLE_I2C_MASTER);
return kStatus_LPI2C_Nak;
}
}
else if(reVal == kStatus_Success)
{
LPI2C_MasterStop(EXAMPLE_I2C_MASTER);
return kStatus_Success;
}
}
}else
{
return kStatus_LPI2C_Busy;
}
}
Following are the waveform captures using Saleae logic analyzer.
Wakeup command

Measurement Trigger Command

Measurement Read Command. 6 bytes are read, 2 each are for temperature and humidity.

The main code looks like this
/* declaration of arrays for SHTC3 sensor commands */
uint8_t wakeup_cmd_data[2] = {0x35,0x17};
uint8_t measure_cmd_data[2] = {0x78,0x66};
uint8_t soft_reset_cmd_data[2] = {0x80,0x5D};
if((reVal = SHTC3_Software_Reset(LPI2C_MASTER_SLAVE_ADDR_7BIT)) == kStatus_Success)
{
PRINTF("\r\n Software reset done\r\n");
}
/* Send master blocking data to slave */
while(1){
// Wake up command transmitted by this function
SHTC3_WakeUpCmd(LPI2C_MASTER_SLAVE_ADDR_7BIT);
//give a delay of 1ms after Wake up command
UTickDelay(1000);
// measurement command is transmitted
SHTC3_MeasureCmdTFCE(LPI2C_MASTER_SLAVE_ADDR_7BIT);
// delay of 50 millisec
UTickDelay(50000);
// read 6 bytes from sensor which has temp and humidity readings
SHTC3_ReadTempFirstCE(LPI2C_MASTER_SLAVE_ADDR_7BIT, rec_Data,6);
//calculation of temperature and humidity readings
temp = (((rec_Data[0]*256 + rec_Data[1]) * 1750)/65536)-450;
Hum = ((rec_Data[3]*256 + rec_Data[4])*1000)/65536;
PRINTF("Temp: %d, Humidity: %d\r\n",temp, Hum);
//delay of 1 Sec.
UTickDelay(1000000);
}
Temperature and humidity measurements are as shown below UART debug prints

I hope with this example you are able ti understand how to use I2C Master Interface of NXP MCXA Microcontroller.
In my next blog I will explain how to use SPI interface. Stay tuned.
I am running an Embedded Design House, CAPUF Embedded Pvt. Ltd, located in Bangalore, India. At CAPUF, we help companies build embedded products with our hardware and firmware design services.
We also help in design optimizations for power consumption, cost, mass manufacturing, and performance. Additionally, we develop PCB testing jigs and provide cloud-based monitoring solutions.