Newer
Older
mbed-os / targets / TARGET_Cypress / TARGET_PSOC6 / mtb-hal-cat1 / source / cyhal_comp_lp.c
@Dustin Crossman Dustin Crossman on 4 Jun 2021 11 KB Fix file modes.
/*******************************************************************************
* \file cyhal_comp_lp.c
*
* \brief
* Provides an implementation of the comp HAL on top of the LP (low power) comp.
*
********************************************************************************
* \copyright
* Copyright 2018-2021 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 "cyhal_comp_lp.h"
#include "cyhal_analog_common.h"
#include "cyhal_hwmgr.h"
#include "cyhal_system.h"

#define _CYHAL_COMP_PER_LP (2u)

#if defined(CY_IP_MXLPCOMP_INSTANCES) && (CY_IP_MXLPCOMP_INSTANCES > 0)

static cyhal_comp_t* _cyhal_comp_lp_config_structs[CY_IP_MXLPCOMP_INSTANCES * _CYHAL_COMP_PER_LP];

LPCOMP_Type *const _cyhal_lpcomp_base[] =
{
#if (CY_IP_MXLPCOMP_INSTANCES == 1)
    LPCOMP,
#endif
#if (CY_IP_MXLPCOMP_INSTANCES > 1)
    #error Unhandled LP_COMP instance count
#endif
};

static const IRQn_Type _cyhal_lp_comp_irq_n[] =
{
#if (CY_IP_MXLPCOMP_INSTANCES == 1)
    lpcomp_interrupt_IRQn,
#endif
#if (CY_IP_MXLPCOMP_INSTANCES > 1)
    #error Unhandled LP_COMP instance count
#endif
};

static uint8_t _cyhal_lpcomp_get_block_from_irqn(IRQn_Type irqn) {
    switch (irqn)
    {
#if (CY_IP_MXLPCOMP_INSTANCES == 1)
    case lpcomp_interrupt_IRQn:
        return 0;
#endif
#if (CY_IP_MXLPCOMP_INSTANCES > 1)
    #error Unhandled LP_COMP instance count
#endif
    default:
        CY_ASSERT(false); // Should never be called
        return 0;
    }
}

static uint32_t _cyhal_comp_lp_convert_power(cyhal_power_level_t hal_power)
{
    switch(hal_power)
    {
        case CYHAL_POWER_LEVEL_OFF:
            return (uint32_t)CY_LPCOMP_MODE_OFF;
        case CYHAL_POWER_LEVEL_LOW:
            return (uint32_t)CY_LPCOMP_MODE_ULP;
        case CYHAL_POWER_LEVEL_MEDIUM:
            return (uint32_t)CY_LPCOMP_MODE_LP;
        case CYHAL_POWER_LEVEL_HIGH:
            return (uint32_t)CY_LPCOMP_MODE_NORMAL;
        case CYHAL_POWER_LEVEL_DEFAULT:
            return (uint32_t)CY_LPCOMP_MODE_LP;
        default:
            CY_ASSERT(false);
            return CY_LPCOMP_MODE_OFF;
    }
}

static cyhal_comp_event_t _cyhal_comp_lp_get_enabled_events(cyhal_comp_t * obj)
{
    switch((cy_en_lpcomp_int_t)obj->irq_cause)
    {
        case CY_LPCOMP_INTR_DISABLE:
            return (cyhal_comp_event_t)0u;
        case CY_LPCOMP_INTR_RISING:
            return CYHAL_COMP_RISING_EDGE;
        case CY_LPCOMP_INTR_FALLING:
            return CYHAL_COMP_FALLING_EDGE;
        case CY_LPCOMP_INTR_BOTH:
            return (cyhal_comp_event_t)(CYHAL_COMP_RISING_EDGE | CYHAL_COMP_FALLING_EDGE);
        default:
            CY_ASSERT(false);
            return (cyhal_comp_event_t)0u;
    }
}

static cy_en_lpcomp_int_t _cyhal_comp_lp_convert_hal_event(cyhal_comp_event_t event)
{
    switch((uint8_t)event)
    {
        case 0u:
            return CY_LPCOMP_INTR_DISABLE;
        case (uint8_t)CYHAL_COMP_RISING_EDGE:
            return CY_LPCOMP_INTR_RISING;
        case (uint8_t)CYHAL_COMP_FALLING_EDGE:
            return CY_LPCOMP_INTR_FALLING;
        case (uint8_t)(CYHAL_COMP_RISING_EDGE | CYHAL_COMP_FALLING_EDGE):
            return CY_LPCOMP_INTR_BOTH;
        default:
            CY_ASSERT(false);
            return CY_LPCOMP_INTR_DISABLE;
    }
}

static void _cyhal_comp_lp_irq_handler(void)
{
    IRQn_Type irqn = _CYHAL_UTILS_GET_CURRENT_IRQN();
    uint8_t block = _cyhal_lpcomp_get_block_from_irqn(irqn);
    uint32_t instance_num = (Cy_LPComp_GetInterruptStatusMasked(_cyhal_comp_lp_config_structs[block * _CYHAL_COMP_PER_LP]->base_lpcomp) >> 1) ? 1 : 0;

    cyhal_comp_t* obj = _cyhal_comp_lp_config_structs[block * _CYHAL_COMP_PER_LP + instance_num];

    Cy_LPComp_ClearInterrupt(obj->base_lpcomp, (obj->resource.block_num % _CYHAL_COMP_PER_LP) ? 2 : 1);

    cyhal_comp_event_callback_t callback = (cyhal_comp_event_callback_t)obj->callback_data.callback;
    if(NULL != callback)
    {
        cyhal_comp_event_t event = _cyhal_comp_lp_get_enabled_events(obj);
        callback(obj->callback_data.callback_arg, event);
    }
}

cy_rslt_t _cyhal_comp_lp_init(cyhal_comp_t *obj, cyhal_gpio_t vin_p, cyhal_gpio_t vin_m, cyhal_gpio_t output, cyhal_comp_config_t *cfg)
{
    CY_ASSERT(NULL != obj);

    /* Initial values */
    cy_rslt_t result = CY_RSLT_SUCCESS;
    memset(obj, 0, sizeof(cyhal_comp_t));
    obj->irq_cause = 0;

    /* Mark pins in obj NC until they are successfully reserved */
    obj->pin_vin_p = NC;
    obj->pin_vin_m = NC;
    obj->pin_out = NC;

    obj->base_lpcomp = NULL;
    obj->resource.type = CYHAL_RSC_INVALID;

    /* Validate pins. vin_p and vin_m are mandatory pins, output is optional. */
    if ((NC == vin_p) || (NC == vin_m))
    {
        result = CYHAL_COMP_RSLT_ERR_INVALID_PIN;
    }

    /* Get mapping for pins */
    const cyhal_resource_pin_mapping_t *vin_p_map  = (NC != vin_p)     ? CY_UTILS_GET_RESOURCE(vin_p, cyhal_pin_map_lpcomp_inp_comp)     : NULL;
    const cyhal_resource_pin_mapping_t *vin_m_map  = (NC != vin_m)     ? CY_UTILS_GET_RESOURCE(vin_m, cyhal_pin_map_lpcomp_inn_comp)     : NULL;
    const cyhal_resource_pin_mapping_t *output_map = (NC != output)    ? CY_UTILS_GET_RESOURCE(output, cyhal_pin_map_lpcomp_dsi_comp)    : NULL;

    /* Verify if mapping successful */
    if((NULL == vin_p_map) || (NULL == vin_m_map) || ((NC != output) && (NULL == output_map)))
    {
        result = CYHAL_COMP_RSLT_ERR_INVALID_PIN;
    }

    if(CY_RSLT_SUCCESS == result &&
        ((NULL != vin_m_map  && false == _cyhal_utils_resources_equal(vin_p_map->inst, vin_m_map->inst))
      || (NULL != output_map && false == _cyhal_utils_resources_equal(vin_p_map->inst, output_map->inst))))
    {
        result = CYHAL_COMP_RSLT_ERR_INVALID_PIN;
    }

    /* Reserve resources */
    if(CY_RSLT_SUCCESS == result)
    {
        result = cyhal_hwmgr_reserve(vin_p_map->inst);
    }

    if(CY_RSLT_SUCCESS == result)
    {
        obj->resource = *(vin_p_map->inst);
        result = _cyhal_utils_reserve_and_connect(vin_p, vin_p_map);
    }

    if(CY_RSLT_SUCCESS == result)
    {
        obj->pin_vin_p = vin_p;
        result = _cyhal_utils_reserve_and_connect(vin_m, vin_m_map);
    }

    if(CY_RSLT_SUCCESS == result)
    {
        obj->pin_vin_m = vin_m;
        if(NC != output)
        {
            result = _cyhal_utils_reserve_and_connect(output, output_map);
        }
    }

    if(CY_RSLT_SUCCESS == result)
    {
        obj->pin_out = output;
    }

    if (result == CY_RSLT_SUCCESS)
    {
        obj->base_lpcomp = _cyhal_lpcomp_base[obj->resource.block_num / _CYHAL_COMP_PER_LP];

        /* Configure LPComp output mode and hysteresis for channel 0 or 1 */
        cy_stc_lpcomp_config_t comp_lp_config;

        comp_lp_config.outputMode = CY_LPCOMP_OUT_DIRECT;
        comp_lp_config.intType = CY_LPCOMP_INTR_DISABLE;
        comp_lp_config.hysteresis = cfg->hysteresis ? CY_LPCOMP_HYST_ENABLE : CY_LPCOMP_HYST_DISABLE;
        comp_lp_config.power = (cy_en_lpcomp_pwr_t)_cyhal_comp_lp_convert_power(cfg->power);

        result = Cy_LPComp_Init(obj->base_lpcomp, (obj->resource.block_num % _CYHAL_COMP_PER_LP) ? CY_LPCOMP_CHANNEL_1 : CY_LPCOMP_CHANNEL_0, &comp_lp_config);

        /* Set both terminals to GPIO inputs */
        Cy_LPComp_SetInputs(obj->base_lpcomp, (obj->resource.block_num % _CYHAL_COMP_PER_LP) ? CY_LPCOMP_CHANNEL_1 : CY_LPCOMP_CHANNEL_0, CY_LPCOMP_SW_GPIO, CY_LPCOMP_SW_GPIO);

        /* Set channel 0 or 1 power mode - Ultra Low Power mode */
        Cy_LPComp_SetPower(obj->base_lpcomp, (obj->resource.block_num % _CYHAL_COMP_PER_LP) ? CY_LPCOMP_CHANNEL_1 : CY_LPCOMP_CHANNEL_0, comp_lp_config.power);

        Cy_LPComp_SetInterruptMask(obj->base_lpcomp, (obj->resource.block_num % _CYHAL_COMP_PER_LP) ? 2 : 1);

        /* Initialize the programmable analog */
        Cy_LPComp_Enable(obj->base_lpcomp, (obj->resource.block_num % _CYHAL_COMP_PER_LP) ? CY_LPCOMP_CHANNEL_1 : CY_LPCOMP_CHANNEL_0);

        _cyhal_comp_lp_config_structs[obj->resource.block_num] = obj;
        cy_stc_sysint_t irqCfg = { _cyhal_lp_comp_irq_n[obj->resource.block_num / _CYHAL_COMP_PER_LP], CYHAL_ISR_PRIORITY_DEFAULT };
        Cy_SysInt_Init(&irqCfg, _cyhal_comp_lp_irq_handler);
        NVIC_EnableIRQ(_cyhal_lp_comp_irq_n[obj->resource.block_num / _CYHAL_COMP_PER_LP]);
    }

    /* Free LP_COMP in case of failure */
    if (result != CY_RSLT_SUCCESS)
    {
        _cyhal_comp_lp_free(obj);
    }
    return result;
}

void _cyhal_comp_lp_free(cyhal_comp_t *obj)
{
    CY_ASSERT(NULL != obj);

    if(CYHAL_RSC_INVALID != obj->resource.type)
    {
        _cyhal_comp_lp_config_structs[obj->resource.block_num] = NULL;

        /* Free the programmable analog */
        uint8_t lpcomp_num = obj->resource.block_num / _CYHAL_COMP_PER_LP;
        if((NULL == _cyhal_comp_lp_config_structs[(lpcomp_num * _CYHAL_COMP_PER_LP)])
                && (NULL == _cyhal_comp_lp_config_structs[(lpcomp_num * _CYHAL_COMP_PER_LP) + 1]))
        {
            NVIC_DisableIRQ(_cyhal_lp_comp_irq_n[obj->resource.block_num / _CYHAL_COMP_PER_LP]);
        }

        if(NULL != obj->base_lpcomp)
        {
            Cy_LPComp_Disable(obj->base_lpcomp, (obj->resource.block_num % _CYHAL_COMP_PER_LP) ? CY_LPCOMP_CHANNEL_1 : CY_LPCOMP_CHANNEL_0);
        }
        cyhal_hwmgr_free(&(obj->resource));
        obj->base_lpcomp = NULL;
        obj->resource.type = CYHAL_RSC_INVALID;
    }

    _cyhal_utils_release_if_used(&(obj->pin_vin_p));
    _cyhal_utils_release_if_used(&(obj->pin_vin_m));
    _cyhal_utils_release_if_used(&(obj->pin_out));
}

cy_rslt_t _cyhal_comp_lp_configure(cyhal_comp_t *obj, cyhal_comp_config_t *cfg)
{
    CY_ASSERT(NULL != obj);
    cy_rslt_t result = CY_RSLT_SUCCESS;

    result = _cyhal_comp_lp_set_power(obj, cfg->power);
    if (result == CY_RSLT_SUCCESS)
    {
        Cy_LPComp_SetHysteresis(obj->base_lpcomp, (obj->resource.block_num % _CYHAL_COMP_PER_LP) ? CY_LPCOMP_CHANNEL_1 : CY_LPCOMP_CHANNEL_0, cfg->hysteresis ? CY_LPCOMP_HYST_ENABLE : CY_LPCOMP_HYST_DISABLE);
    }
    return result;
}

cy_rslt_t _cyhal_comp_lp_set_power(cyhal_comp_t *obj, cyhal_power_level_t power)
{
    CY_ASSERT(NULL != obj);
    cy_en_lpcomp_pwr_t power_level = (cy_en_lpcomp_pwr_t)_cyhal_comp_lp_convert_power(power);

    Cy_LPComp_SetPower(obj->base_lpcomp, (obj->resource.block_num % _CYHAL_COMP_PER_LP) ? CY_LPCOMP_CHANNEL_1 : CY_LPCOMP_CHANNEL_0, power_level);
    return CY_RSLT_SUCCESS;
}

bool _cyhal_comp_lp_read(cyhal_comp_t *obj)
{
    CY_ASSERT(NULL != obj);
    return (1UL == Cy_LPComp_GetCompare(obj->base_lpcomp, (obj->resource.block_num % _CYHAL_COMP_PER_LP) ? CY_LPCOMP_CHANNEL_1 : CY_LPCOMP_CHANNEL_0));
}

void _cyhal_comp_lp_enable_event(cyhal_comp_t *obj, cyhal_comp_event_t event, uint8_t intr_priority, bool enable)
{
    if(enable)
    {
        obj->irq_cause |= event;
    }
    else
    {
        obj->irq_cause &= (~event);
    }

    IRQn_Type irqn = _cyhal_lp_comp_irq_n[obj->resource.block_num / _CYHAL_COMP_PER_LP];
    NVIC_SetPriority(irqn, intr_priority);

    cy_en_lpcomp_int_t pdl_event = _cyhal_comp_lp_convert_hal_event((cyhal_comp_event_t)obj->irq_cause);
    Cy_LPComp_SetInterruptTriggerMode(obj->base_lpcomp, (obj->resource.block_num % _CYHAL_COMP_PER_LP) ? CY_LPCOMP_CHANNEL_1 : CY_LPCOMP_CHANNEL_0, pdl_event);
}

#endif /* defined(CY_IP_MXLPCOMP_INSTANCES) && (CY_IP_MXLPCOMP_INSTANCES > 0) */