mbed library sources. Supersedes mbed-src.

Dependents:   Nucleo_Hello_Encoder BLE_iBeaconScan AM1805_DEMO DISCO-F429ZI_ExportTemplate1 ... more

targets/TARGET_Cypress/TARGET_PSOC6/i2c_api.c

Committer:
AnnaBridge
Date:
2019-02-20
Revision:
189:f392fc9709a3
Parent:
188:bcfe06ba3d64

File content as of revision 189:f392fc9709a3:

/*
 * mbed Microcontroller Library
 * Copyright (c) 2017-2018 Future Electronics
 * Copyright (c) 2018-2019 Cypress Semiconductor Corporation
 * SPDX-License-Identifier: Apache-2.0
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "cmsis.h"
#include "mbed_assert.h"
#include "mbed_error.h"
#include "PeripheralPins.h"
#include "pinmap.h"
#include "i2c_api.h"
#include "psoc6_utils.h"

#include "cy_sysclk.h"
#include "cy_gpio.h"
#include "cy_scb_i2c.h"
#include "cy_sysint.h"

#define I2C_DEFAULT_SPEED               100000
#define NUM_I2C_PORTS                   8
#define I2C_DEFAULT_IRQ_PRIORITY        3
#define I2C_NUM_DIVIDERS                3
#define MIN_I2C_CLOCK_FREQUENCY         CY_SCB_I2C_SLAVE_STD_CLK_MIN
// Default timeout in milliseconds.
#define I2C_DEFAULT_TIMEOUT             1000

#define PENDING_NONE                    0
#define PENDING_RX                      1
#define PENDING_TX                      2
#define PENDING_TX_RX                   3

typedef enum {
    I2C_DIVIDER_LOW = 0,
    I2C_DIVIDER_MID,
    I2C_DIVIDER_HIGH,
    I2C_INVALID_DIVIDER = 0xff
} I2cDividerType;


typedef struct {
    uint32_t                div_num;
    cy_en_divider_types_t   div_type;
    uint32_t                clk_frequency;
} I2cDividerInfo;


static const cy_stc_scb_i2c_config_t default_i2c_config = {
    .i2cMode                = CY_SCB_I2C_MASTER,
    .useRxFifo              = true,
    .useTxFifo              = true,
    .slaveAddress           = 0,
    .slaveAddressMask       = 0,
    .acceptAddrInFifo       = false,
    .ackGeneralAddr         = false,
    .enableWakeFromSleep    = false
};


static I2cDividerInfo i2c_dividers[I2C_NUM_DIVIDERS] = {
    { I2C_INVALID_DIVIDER, CY_SYSCLK_DIV_8_BIT, CY_SCB_I2C_SLAVE_STD_CLK_MIN }, // Low divider uses lowest possible frequency.
    { I2C_INVALID_DIVIDER, CY_SYSCLK_DIV_8_BIT, CY_SCB_I2C_SLAVE_FST_CLK_MIN },
    { I2C_INVALID_DIVIDER, CY_SYSCLK_DIV_8_BIT, CY_SCB_I2C_SLAVE_FSTP_CLK_MIN }
};


typedef struct i2c_s i2c_obj_t;

#if DEVICE_I2C_ASYNCH
#define OBJ_P(in)     (&(in->i2c))
#else
#define OBJ_P(in)     (in)
#endif


#if DEVICE_I2C_ASYNCH

static IRQn_Type i2c_irq_allocate_channel(i2c_obj_t *obj)
{
#if defined (TARGET_MCU_PSOC6_M0)
    obj->cm0p_irq_src = scb_0_interrupt_IRQn + obj->i2c_id;
    return cy_m0_nvic_allocate_channel(CY_SERIAL_IRQN_ID + obj->i2c_id);
#else
    return (IRQn_Type)(scb_0_interrupt_IRQn + obj->i2c_id);
#endif /* (TARGET_MCU_PSOC6_M0) */
}

static int i2c_irq_setup_channel(i2c_obj_t *obj)
{
    cy_stc_sysint_t irq_config;

    if (obj->irqn == unconnected_IRQn) {
        IRQn_Type irqn = i2c_irq_allocate_channel(obj);
        if (irqn < 0) {
            return (-1);
        }
        // Configure NVIC
        irq_config.intrPriority = I2C_DEFAULT_IRQ_PRIORITY;
        irq_config.intrSrc = irqn;
#if defined (TARGET_MCU_PSOC6_M0)
        irq_config.cm0pSrc = obj->cm0p_irq_src;
#endif
        if (Cy_SysInt_Init(&irq_config, (cy_israddress)(obj->handler)) != CY_SYSINT_SUCCESS) {
            return (-1);
        }

        obj->irqn = irqn;
        NVIC_EnableIRQ(irqn);
    }
    return 0;
}

#endif // DEVICE_I2C_ASYNCH

static int allocate_divider(I2cDividerType divider)
{
    I2cDividerInfo *p_div = &i2c_dividers[divider];

    if (p_div->div_num == CY_INVALID_DIVIDER) {
        p_div->div_num = cy_clk_allocate_divider(CY_SYSCLK_DIV_8_BIT);
        if (p_div->div_num != CY_INVALID_DIVIDER) {
            p_div->div_type = CY_SYSCLK_DIV_8_BIT;
        } else {
            p_div->div_num = cy_clk_allocate_divider(CY_SYSCLK_DIV_16_BIT);
            if (p_div->div_num != CY_INVALID_DIVIDER) {
                p_div->div_type = CY_SYSCLK_DIV_16_BIT;
            }
        }
    }

    if (p_div->div_num != CY_INVALID_DIVIDER) {
        // Set up proper frequency;
        uint32_t div_value = cy_PeriClkFreqHz / p_div->clk_frequency;
        p_div->clk_frequency = cy_PeriClkFreqHz / div_value;
        if (Cy_SysClk_PeriphSetDivider(p_div->div_type, p_div->div_num, div_value) == CY_SYSCLK_SUCCESS) {
            Cy_SysClk_PeriphEnableDivider(p_div->div_type, p_div->div_num);
        } else {
            p_div->div_num = CY_INVALID_DIVIDER;
        }
    }

    return (p_div->div_num == CY_INVALID_DIVIDER) ? -1 : 0;
}

/*
 * Select one of the 3 dividers used depending on the required frequency.
 */
static I2cDividerType select_divider(uint32_t frequency)
{
    if (frequency <= (MIN_I2C_CLOCK_FREQUENCY / CY_SCB_I2C_DUTY_CYCLE_MAX)) {
        // Required speed lower than min supported.
        return I2C_INVALID_DIVIDER;
    } else if (frequency <= CY_SCB_I2C_STD_DATA_RATE) {
        return I2C_DIVIDER_LOW;
    } else if (frequency <= CY_SCB_I2C_FST_DATA_RATE) {
        return I2C_DIVIDER_MID;
    } else if (frequency <= CY_SCB_I2C_FSTP_DATA_RATE) {
        return I2C_DIVIDER_HIGH;
    } else {
        // Required speed too high;
        return I2C_INVALID_DIVIDER;
    }
}

/*
 * Initializes i2c clock for the required speed
 */
static cy_en_sysclk_status_t i2c_init_clock(i2c_obj_t *obj, uint32_t speed)
{
    I2cDividerInfo *p_div = NULL;
    cy_en_sysclk_status_t status = CY_SYSCLK_INVALID_STATE;
    I2cDividerType divider = select_divider(speed);

    if (divider == I2C_INVALID_DIVIDER) {
        error("I2C: required speed/frequency is out of valid range.");
    }

    if (allocate_divider(divider) < 0) {
        error("I2C: cannot allocate clock divider.");
    }

    obj->divider = divider;
    p_div = &i2c_dividers[divider];

    status = Cy_SysClk_PeriphAssignDivider(obj->clock, p_div->div_type, p_div->div_num);
    if (status != CY_SYSCLK_SUCCESS) {
        error("I2C: cannot assign clock divider.");
    }

    /* Set desired speed/frequency */
    obj->actual_speed = Cy_SCB_I2C_SetDataRate(obj->base, speed, p_div->clk_frequency);
    return (obj->actual_speed != 0) ? CY_SYSCLK_SUCCESS : CY_SYSCLK_BAD_PARAM;
}

/*
 * Initializes i/o pins for i2c sda/scl.
 */
static void i2c_init_pins(i2c_obj_t *obj)
{
    /* MBED driver reserves pins for I2C as Ditigal IO while doing I2C error
     * recovery in constructor.
     */
    pin_function(obj->pin_sda, pinmap_function(obj->pin_sda, PinMap_I2C_SDA));
    pin_function(obj->pin_scl, pinmap_function(obj->pin_scl, PinMap_I2C_SCL));
}


/*
 * Initializes and enables I2C/SCB.
 */
static void i2c_init_peripheral(i2c_obj_t *obj)
{
    cy_stc_scb_i2c_config_t i2c_config = default_i2c_config;
    I2cDividerInfo *p_div = &i2c_dividers[obj->divider];

    Cy_SCB_I2C_Init(obj->base, &i2c_config, &obj->context);
    Cy_SCB_I2C_SetDataRate(obj->base, obj->actual_speed, p_div->clk_frequency);
    Cy_SCB_I2C_Enable(obj->base);
}

/*
 * Coverts PDL status into Mbed status.
 */
static int i2c_convert_status(cy_en_scb_i2c_status_t status)
{
    switch (status) {
        case CY_SCB_I2C_MASTER_NOT_READY:
        case CY_SCB_I2C_MASTER_MANUAL_ARB_LOST:
        case CY_SCB_I2C_MASTER_MANUAL_BUS_ERR:
        case CY_SCB_I2C_MASTER_MANUAL_ABORT_START:
            return I2C_ERROR_BUS_BUSY;

        case CY_SCB_I2C_MASTER_MANUAL_TIMEOUT:
        case CY_SCB_I2C_MASTER_MANUAL_ADDR_NAK:
        case CY_SCB_I2C_MASTER_MANUAL_NAK:
            return I2C_ERROR_NO_SLAVE;

        case CY_SCB_I2C_SUCCESS:
        case CY_SCB_I2C_BAD_PARAM:
        default:
            return 0;
    }
}

/*
 * Callback function to handle into and out of deep sleep state transitions.
 */
#if DEVICE_SLEEP && DEVICE_LPTICKER
static cy_en_syspm_status_t i2c_pm_callback(cy_stc_syspm_callback_params_t *callback_params, cy_en_syspm_callback_mode_t mode)
{
    cy_stc_syspm_callback_params_t params = *callback_params;
    i2c_obj_t *obj = (i2c_obj_t *)params.context;
    params.context = &obj->context;

    return Cy_SCB_I2C_DeepSleepCallback(&params, mode);
}
#endif // DEVICE_SLEEP && DEVICE_LPTICKER


void i2c_init(i2c_t *obj_in, PinName sda, PinName scl)
{
    i2c_obj_t *obj = OBJ_P(obj_in);

    uint32_t i2c = pinmap_peripheral(sda, PinMap_I2C_SDA);
    i2c = pinmap_merge(i2c, pinmap_peripheral(scl, PinMap_I2C_SCL));

    if (i2c != (uint32_t) NC) {
        /* Initialize configuration */
        obj->base    = (CySCB_Type *) i2c;
        obj->i2c_id  = ((I2CName) i2c - I2C_0) / (I2C_1 - I2C_0);
        obj->clock   = CY_PIN_CLOCK(pinmap_function(scl, PinMap_I2C_SCL));
        obj->divider = I2C_INVALID_DIVIDER;
        obj->already_reserved = (0 != cy_reserve_scb(obj->i2c_id));
        obj->pin_sda = sda;
        obj->pin_scl = scl;

        obj->mode    = CY_SCB_I2C_MASTER;
        obj->timeout = I2C_DEFAULT_TIMEOUT;

#if DEVICE_I2C_ASYNCH
        obj->irqn    = unconnected_IRQn;
        obj->pending = PENDING_NONE;
        obj->events  = 0;
#endif /* DEVICE_I2C_ASYNCH */

        /* Check if resource severed */
        if (obj->already_reserved) {

            /* SCB pins and clocks are connected */

            /* Disable block and get it into the default state */
            Cy_SCB_I2C_Disable(obj->base, &obj->context);
            Cy_SCB_I2C_DeInit(obj->base);

            /* The proper clock will be connected by i2c_init_clock(obj, I2C_DEFAULT_SPEED) */
            obj->divider = I2C_DIVIDER_LOW;
        } else {
#if DEVICE_SLEEP && DEVICE_LPTICKER
            /* Register callback once */
            obj->pm_callback_handler.callback = i2c_pm_callback;
            obj->pm_callback_handler.type = CY_SYSPM_DEEPSLEEP;
            obj->pm_callback_handler.skipMode = 0;
            obj->pm_callback_handler.callbackParams = &obj->pm_callback_params;
            obj->pm_callback_params.base = obj->base;
            obj->pm_callback_params.context = obj;
            if (!Cy_SysPm_RegisterCallback(&obj->pm_callback_handler)) {
                error("PM callback registration failed!");
            }
#endif /* DEVICE_SLEEP && DEVICE_LPTICKER */
        }

        /* Configure hardware resources */
        i2c_init_clock(obj, I2C_DEFAULT_SPEED);
        i2c_init_pins(obj);
        i2c_init_peripheral(obj);

    } else {
        error("I2C pinout mismatch. Requested pins SDA and SCL can't be used for the same I2C communication.");
    }
}

void i2c_frequency(i2c_t *obj_in, int hz)
{
    i2c_obj_t *obj = OBJ_P(obj_in);
    Cy_SCB_I2C_Disable(obj->base, &obj->context);
    i2c_init_clock(obj, hz);
    Cy_SCB_I2C_Enable(obj->base);
}

int  i2c_start(i2c_t *obj_in)
{
    i2c_obj_t *obj = OBJ_P(obj_in);

    if (CY_SCB_I2C_IDLE == obj->context.state) {
        /* Set the read or write direction */
        obj->context.state = CY_SCB_I2C_MASTER_ADDR;

        /* Clean up the hardware before a transfer. Note RX FIFO is empty at here */
        Cy_SCB_ClearMasterInterrupt(obj->base, CY_SCB_I2C_MASTER_INTR_ALL);
        Cy_SCB_ClearRxInterrupt(obj->base, CY_SCB_RX_INTR_NOT_EMPTY);
        Cy_SCB_ClearTxFifo(obj->base);

        /* Generate a Start and send address byte */
        SCB_I2C_M_CMD(obj->base) = SCB_I2C_M_CMD_M_START_ON_IDLE_Msk;

        /* Wait until Start is generated */
        while (0 != (SCB_I2C_M_CMD(obj->base) & SCB_I2C_M_CMD_M_START_ON_IDLE_Msk)) {
        }

        return 0;
    }

    return (-1);
}

int  i2c_stop(i2c_t *obj_in)
{
    i2c_obj_t *obj = OBJ_P(obj_in);
    cy_en_scb_i2c_status_t status = Cy_SCB_I2C_MasterSendStop(obj->base, obj->timeout, &obj->context);

    return i2c_convert_status(status);
}

int i2c_read(i2c_t *obj_in, int address, char *data, int length, int stop)
{
    cy_en_scb_i2c_status_t status = CY_SCB_I2C_SUCCESS;
    i2c_obj_t *obj = OBJ_P(obj_in);
    cy_en_scb_i2c_command_t ack = CY_SCB_I2C_ACK;
    int byte_count = 0;
    address >>= 1;

    // Start transaction, send address.
    if (obj->context.state == CY_SCB_I2C_IDLE) {
        status = Cy_SCB_I2C_MasterSendStart(obj->base, address, CY_SCB_I2C_READ_XFER, obj->timeout, &obj->context);
    }
    if (status == CY_SCB_I2C_SUCCESS) {
        while (length > 0) {
            if (length == 1) {
                ack = CY_SCB_I2C_NAK;
            }
            status = Cy_SCB_I2C_MasterReadByte(obj->base, ack, (uint8_t *)data, obj->timeout, &obj->context);
            if (status != CY_SCB_I2C_SUCCESS) {
                break;
            }
            ++byte_count;
            --length;
            ++data;
        }
        // SCB in I2C mode is very time sensitive. In practice we have to request STOP after
        // each block, otherwise it may break the transmission.
        Cy_SCB_I2C_MasterSendStop(obj->base, obj->timeout, &obj->context);
    }

    if (status != CY_SCB_I2C_SUCCESS) {
        Cy_SCB_I2C_MasterSendStop(obj->base, obj->timeout, &obj->context);
        byte_count = i2c_convert_status(status);
    }

    return byte_count;
}

int i2c_write(i2c_t *obj_in, int address, const char *data, int length, int stop)
{
    cy_en_scb_i2c_status_t status = CY_SCB_I2C_SUCCESS;
    i2c_obj_t *obj = OBJ_P(obj_in);
    int byte_count = 0;
    address >>= 1;

    // Start transaction, send address.
    if (obj->context.state == CY_SCB_I2C_IDLE) {
        status = Cy_SCB_I2C_MasterSendStart(obj->base, address, CY_SCB_I2C_WRITE_XFER, obj->timeout, &obj->context);
    }
    if (status == CY_SCB_I2C_SUCCESS) {
        while (length > 0) {
            status = Cy_SCB_I2C_MasterWriteByte(obj->base, *data, obj->timeout, &obj->context);
            if (status != CY_SCB_I2C_SUCCESS) {
                break;;
            }
            ++byte_count;
            --length;
            ++data;
        }
        // SCB in I2C mode is very time sensitive. In practice we have to request STOP after
        // each block, otherwise it may break the transmission.
        Cy_SCB_I2C_MasterSendStop(obj->base, obj->timeout, &obj->context);
    }

    if (status != CY_SCB_I2C_SUCCESS) {
        Cy_SCB_I2C_MasterSendStop(obj->base, obj->timeout, &obj->context);
        byte_count = i2c_convert_status(status);
    }

    return byte_count;
}

void i2c_reset(i2c_t *obj_in)
{
    i2c_obj_t *obj = OBJ_P(obj_in);

    /* Back block into default state */
    Cy_SCB_FwBlockReset(obj->base);
    obj->context.state = CY_SCB_I2C_IDLE;
}

int i2c_byte_read(i2c_t *obj_in, int last)
{
    i2c_obj_t *obj = OBJ_P(obj_in);
    uint8_t tmp_byte = 0;
    cy_en_scb_i2c_command_t ack = last ? CY_SCB_I2C_NAK : CY_SCB_I2C_ACK;

    /* i2c_start and i2c_byte_write was called. Update state to receive data */
    if (CY_SCB_I2C_MASTER_TX == obj->context.state) {
        obj->context.state = CY_SCB_I2C_MASTER_RX0;
    }

    cy_en_scb_i2c_status_t status = Cy_SCB_I2C_MasterReadByte(obj->base, ack, &tmp_byte, obj->timeout, &obj->context);

    if (status == CY_SCB_I2C_SUCCESS) {
        return tmp_byte;
    } else {
        return (-1);
    }
}

int i2c_byte_write(i2c_t *obj_in, int data)
{
    i2c_obj_t *obj = OBJ_P(obj_in);

    /* i2c_start was called update state to receive data */
    if (CY_SCB_I2C_MASTER_ADDR == obj->context.state) {
        obj->context.state = CY_SCB_I2C_MASTER_TX;
    }

    cy_en_scb_i2c_status_t status = Cy_SCB_I2C_MasterWriteByte(obj->base, (uint8_t)data, obj->timeout, &obj->context);

    switch (status) {
        case CY_SCB_I2C_MASTER_MANUAL_TIMEOUT:
            return 2;
        case CY_SCB_I2C_MASTER_MANUAL_ADDR_NAK:
        case CY_SCB_I2C_MASTER_MANUAL_NAK:
            return 0;
        case CY_SCB_I2C_SUCCESS:
            return 1;
        default:
            /* Error has occurred */
            return (-1);
    }
}

#if DEVICE_I2C_ASYNCH

void i2c_transfer_asynch(i2c_t *obj_in,
                         const void *tx,
                         size_t tx_length,
                         void *rx, size_t rx_length,
                         uint32_t address,
                         uint32_t stop,
                         uint32_t handler,
                         uint32_t event,
                         DMAUsage hint)
{
    i2c_obj_t *obj = OBJ_P(obj_in);

    (void)hint; // At the moment we do not support DMA transfers, so this parameter gets ignored.

    if (obj->pending != PENDING_NONE) {
        return;
    }

    obj->rx_config.slaveAddress = address >> 1;
    obj->tx_config.slaveAddress = address >> 1;
    obj->events = event;
    obj->handler = handler;
    if (i2c_irq_setup_channel(obj) < 0) {
        return;
    }

    obj->rx_config.buffer = rx;
    obj->rx_config.bufferSize = rx_length;
    obj->rx_config.xferPending = !stop;

    obj->tx_config.buffer = (void *)tx;
    obj->tx_config.bufferSize = tx_length;
    obj->tx_config.xferPending = rx_length || !stop;

    if (tx_length) {
        // Write first, then read, or write only.
        if (rx_length > 0) {
            obj->pending = PENDING_TX_RX;
        } else {
            obj->pending = PENDING_TX;
        }
        Cy_SCB_I2C_MasterWrite(obj->base, &obj->tx_config, &obj->context);
    } else if (rx_length) {
        // Read transaction;
        obj->pending = PENDING_RX;
        Cy_SCB_I2C_MasterRead(obj->base, &obj->rx_config, &obj->context);
    }
}


static void i2c_gen_stop(i2c_t *obj_in)
{
    i2c_obj_t *obj = OBJ_P(obj_in);

    // Enable master interrupts and generate Stop
    Cy_SCB_SetMasterInterruptMask(obj->base, CY_SCB_I2C_MASTER_INTR);
    SCB_I2C_M_CMD(obj->base) = (SCB_I2C_M_CMD_M_STOP_Msk | SCB_I2C_M_CMD_M_NACK_Msk);
}


uint32_t i2c_irq_handler_asynch(i2c_t *obj_in)
{
    i2c_obj_t *obj = OBJ_P(obj_in);
    uint32_t event = 0;
    // Process actual interrupt.
    Cy_SCB_I2C_Interrupt(obj->base, &obj->context);

    if (false == (CY_SCB_I2C_MASTER_BUSY & obj->context.masterStatus)) {
        // Transfer was completed
        event = I2C_EVENT_TRANSFER_COMPLETE;

        // Parse results of single transfer
        if (CY_SCB_I2C_MASTER_ERR & obj->context.masterStatus) {
            if (CY_SCB_I2C_MASTER_ADDR_NAK & obj->context.masterStatus) {
                event |= I2C_EVENT_ERROR_NO_SLAVE;
            } else if (CY_SCB_I2C_MASTER_DATA_NAK & obj->context.masterStatus) {
                event |= I2C_EVENT_TRANSFER_EARLY_NACK;
            } else {
                // CY_SCB_I2C_MASTER_ARB_LOST || CY_SCB_I2C_MASTER_BUS_ERR || CY_SCB_I2C_MASTER_ABORT_START
                event |= I2C_EVENT_ERROR;
            }
        }

        // Check if a read phase is pending after write.
        if (obj->pending == PENDING_TX_RX) {
            obj->pending = PENDING_RX;

            if (event == I2C_EVENT_TRANSFER_COMPLETE) {
                // Send ReStart and continue with RX transfer
                event = 0;
                Cy_SCB_I2C_MasterRead(obj->base, &obj->rx_config, &obj->context);
            } else {
                // NACK - generate Stop  (do not execute RX transfer)
                // Error - do not execute RX transfer and report transfer complete
                if (false == (event & I2C_EVENT_ERROR)) {
                    // Report events after Stop generation
                    event = 0;
                    obj->context.state       = CY_SCB_I2C_MASTER_WAIT_STOP;
                    obj->context.masterPause = false;

                    i2c_gen_stop(obj_in);
                }
            }
        } else {
            obj->pending = PENDING_NONE;
        }
    }

    return (event & obj->events);
}

uint8_t i2c_active(i2c_t *obj_in)
{
    i2c_obj_t *obj = OBJ_P(obj_in);
    return (obj->pending != PENDING_NONE);
}

void i2c_abort_asynch(i2c_t *obj_in)
{
    i2c_obj_t *obj = OBJ_P(obj_in);
    if (obj->pending != PENDING_NONE) {
        if (obj->pending == PENDING_RX) {
            Cy_SCB_I2C_MasterAbortRead(obj->base, &obj->context);
        } else {
            Cy_SCB_I2C_MasterAbortWrite(obj->base, &obj->context);
        }
    }
}

#endif // DEVICE_ASYNCH