| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540 |
- /***************************************************************************//**
- * \file cyhal_timer.c
- *
- * \brief
- * Provides a high level interface for interacting with the Infineon Timer/Counter.
- * This interface abstracts out the chip specific details. If any chip specific
- * functionality is necessary, or performance is critical the low level functions
- * can be used directly.
- *
- ********************************************************************************
- * \copyright
- * Copyright 2018-2022 Cypress Semiconductor Corporation (an Infineon company) or
- * an affiliate of 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 <string.h>
- #if !defined(COMPONENT_CAT5)
- #include "cy_device_headers.h"
- #endif
- #include "cyhal_timer_impl.h"
- #include "cyhal_hwmgr.h"
- #include "cyhal_gpio.h"
- #include "cyhal_interconnect.h"
- #include "cyhal_syspm.h"
- #include "cyhal_clock.h"
- #if defined(COMPONENT_CAT5)
- #include "cyhal_irq_impl.h"
- #endif
- #if (CYHAL_DRIVER_AVAILABLE_TIMER)
- #if defined(__cplusplus)
- extern "C" {
- #endif
- static const cy_stc_tcpwm_counter_config_t _cyhal_timer_default_config =
- {
- .period = 32768,
- .clockPrescaler = CY_TCPWM_COUNTER_PRESCALER_DIVBY_1,
- .runMode = CY_TCPWM_COUNTER_CONTINUOUS,
- .countDirection = CY_TCPWM_COUNTER_COUNT_UP,
- .compareOrCapture = CY_TCPWM_COUNTER_MODE_CAPTURE,
- .compare0 = 16384,
- .compare1 = 16384,
- .enableCompareSwap = false,
- .interruptSources = CY_TCPWM_INT_NONE,
- .captureInputMode = 0x3U,
- .captureInput = CY_TCPWM_INPUT_0,
- .reloadInputMode = 0x3U,
- .reloadInput = CY_TCPWM_INPUT_0,
- .startInputMode = 0x3U,
- .startInput = CY_TCPWM_INPUT_0,
- .stopInputMode = 0x3U,
- .stopInput = CY_TCPWM_INPUT_0,
- .countInputMode = 0x3U,
- .countInput = CY_TCPWM_INPUT_1,
- #if (CY_IP_MXTCPWM_VERSION >= 2U)
- .capture1InputMode = 0x3U,
- .capture1Input = CY_TCPWM_INPUT_0,
- .enableCompare1Swap = false,
- .compare2 = 16384,
- .compare3 = 16384,
- .trigger0Event = CY_TCPWM_CNT_TRIGGER_ON_DISABLED,
- .trigger1Event = CY_TCPWM_CNT_TRIGGER_ON_DISABLED,
- #endif
- };
- /** Convert timer direction from the HAL enum to the corresponding PDL constant
- *
- * @param[in] direction The direction, as a HAL enum value
- * @return The direction, as a PDL constant
- */
- static inline uint32_t _cyhal_timer_convert_direction(cyhal_timer_direction_t direction)
- {
- switch (direction)
- {
- case CYHAL_TIMER_DIR_UP:
- return CY_TCPWM_COUNTER_COUNT_UP;
- case CYHAL_TIMER_DIR_DOWN:
- return CY_TCPWM_COUNTER_COUNT_DOWN;
- case CYHAL_TIMER_DIR_UP_DOWN:
- return CY_TCPWM_COUNTER_COUNT_UP_DOWN_2;
- default:
- CY_ASSERT(false);
- return CY_TCPWM_COUNTER_COUNT_UP;
- }
- }
- /*******************************************************************************
- * Timer HAL Functions
- *******************************************************************************/
- cy_rslt_t _cyhal_timer_init_hw(cyhal_timer_t *obj, const cy_stc_tcpwm_counter_config_t *config, const cyhal_clock_t *clk)
- {
- cy_rslt_t result = CY_RSLT_SUCCESS;
- cyhal_resource_inst_t *timer = &obj->tcpwm.resource;
- obj->tcpwm.base = _CYHAL_TCPWM_DATA[_CYHAL_TCPWM_ADJUST_BLOCK_INDEX(timer->block_num)].base;
- en_clk_dst_t pclk = (en_clk_dst_t)(_CYHAL_TCPWM_DATA[_CYHAL_TCPWM_ADJUST_BLOCK_INDEX(timer->block_num)].clock_dst + timer->channel_num);
- if (NULL != clk)
- {
- obj->tcpwm.clock = *clk;
- obj->tcpwm.clock_hz = cyhal_clock_get_frequency(&obj->tcpwm.clock);
- if (CY_SYSCLK_SUCCESS != _cyhal_utils_peri_pclk_assign_divider(pclk, &(obj->tcpwm.clock)))
- {
- result = CYHAL_TIMER_RSLT_ERR_CLOCK_INIT;
- }
- }
- else if (CY_RSLT_SUCCESS == (result = _cyhal_utils_allocate_clock(&(obj->tcpwm.clock), timer, CYHAL_CLOCK_BLOCK_PERIPHERAL_16BIT, true)))
- {
- obj->tcpwm.dedicated_clock = true;
- #if defined(COMPONENT_CAT5)
- // CAT5 lacks hardware required to divide from 96MHz to 1MHz
- result = cyhal_timer_set_frequency(obj, CYHAL_TIMER_DEFAULT_FREQ * 3);
- #else
- result = cyhal_timer_set_frequency(obj, CYHAL_TIMER_DEFAULT_FREQ);
- #endif
- if (CY_RSLT_SUCCESS == result)
- {
- if (CY_SYSCLK_SUCCESS != _cyhal_utils_peri_pclk_assign_divider(pclk, &(obj->tcpwm.clock)))
- {
- result = CYHAL_TIMER_RSLT_ERR_CLOCK_INIT;
- }
- }
- }
- if (CY_RSLT_SUCCESS == result)
- {
- result = Cy_TCPWM_Counter_Init(obj->tcpwm.base, _CYHAL_TCPWM_CNT_NUMBER(obj->tcpwm.resource), config);
- }
- if (result == CY_RSLT_SUCCESS)
- {
- _cyhal_tcpwm_init_data(&obj->tcpwm);
- Cy_TCPWM_Counter_Enable(obj->tcpwm.base, _CYHAL_TCPWM_CNT_NUMBER(obj->tcpwm.resource));
- }
- return result;
- }
- cy_rslt_t cyhal_timer_init(cyhal_timer_t *obj, cyhal_gpio_t pin, const cyhal_clock_t *clk)
- {
- CY_ASSERT(NULL != obj);
- // No support currently for pin connections on this device
- if (CYHAL_NC_PIN_VALUE != pin)
- return CYHAL_TIMER_RSLT_ERR_BAD_ARGUMENT;
- memset(obj, 0, sizeof(cyhal_timer_t));
- cy_rslt_t result = CY_RSLT_SUCCESS;
- #if defined(COMPONENT_CAT5)
- // T2Timer requested
- if((uint32_t)clk == (uint32_t)(CYHAL_CLOCK_T2TIMER))
- {
- result = cyhal_hwmgr_allocate(CYHAL_RSC_T2TIMER, &obj->t2timer.resource);
- if (CY_RSLT_SUCCESS == result)
- {
- obj->t2timer.which_timer = obj->t2timer.resource.block_num;
- result = _cyhal_t2timer_init(&obj->t2timer, NULL);
- if(result == CY_RSLT_SUCCESS)
- {
- obj->is_t2timer = true;
- }
- }
- }
- else
- {
- // T2Timer not specifically requested, use either TCPWM or T2Timer
- #endif
- obj->tcpwm.resource.type = CYHAL_RSC_INVALID;
- result = cyhal_hwmgr_allocate(CYHAL_RSC_TCPWM, &obj->tcpwm.resource);
- if (CY_RSLT_SUCCESS == result)
- {
- result = _cyhal_timer_init_hw(obj, &_cyhal_timer_default_config, clk);
- #if defined(COMPONENT_CAT5)
- obj->is_t2timer = false;
- }
- else
- {
- result = cyhal_hwmgr_allocate(CYHAL_RSC_T2TIMER, &obj->t2timer.resource);
- if (CY_RSLT_SUCCESS == result)
- {
- obj->t2timer.which_timer = obj->t2timer.resource.block_num;
- result = _cyhal_t2timer_init(&obj->t2timer, NULL);
- if(result == CY_RSLT_SUCCESS)
- {
- obj->is_t2timer = true;
- }
- }
- }
- #endif
- }
- if (CY_RSLT_SUCCESS != result)
- {
- cyhal_timer_free(obj);
- }
- return result;
- }
- cy_rslt_t cyhal_timer_init_cfg(cyhal_timer_t *obj, const cyhal_timer_configurator_t *cfg)
- {
- memset(obj, 0, sizeof(cyhal_timer_t));
- obj->tcpwm.resource = *cfg->resource;
- obj->tcpwm.owned_by_configurator = true;
- cy_rslt_t result = _cyhal_timer_init_hw(obj, cfg->config, cfg->clock);
- #if defined(COMPONENT_CAT5)
- // Device configurator doesn't currently support T2Timer
- obj->is_t2timer = false;
- #endif
- if(CY_RSLT_SUCCESS != result)
- {
- cyhal_timer_free(obj);
- }
- return result;
- }
- cy_rslt_t cyhal_timer_configure(cyhal_timer_t *obj, const cyhal_timer_cfg_t *cfg)
- {
- cy_rslt_t rslt;
- #if defined(COMPONENT_CAT5)
- if(obj->is_t2timer)
- {
- if((cfg->direction != CYHAL_TIMER_DIR_DOWN) || (cfg->is_compare != true) || (cfg->value != 0x0))
- {
- // T2Timer can only count down, it has no capture mode, and doesn't support starting values
- return CYHAL_TIMER_RSLT_ERR_UNSUPPORTED;
- }
- cyhal_t2timer_cfg_t t2t_cfg = {
- .mode = T2_ARM_TIMER_MODE_FREERUNNING,
- .counter_mode = T2_ARM_TIMER_COUNTER_MODE_ONESHOT,
- .duration = cfg->compare_value,
- };
- if(cfg->is_continuous)
- {
- t2t_cfg.counter_mode = T2_ARM_TIMER_COUNTER_MODE_WRAPPING;
- }
- rslt = _cyhal_t2timer_configure(&obj->t2timer, &t2t_cfg);
- }
- else
- #endif
- {
- obj->default_value = cfg->value;
- cy_stc_tcpwm_counter_config_t config = _cyhal_timer_default_config;
- config.period = cfg->period;
- config.compare0 = cfg->compare_value;
- config.runMode = cfg->is_continuous ? CY_TCPWM_COUNTER_CONTINUOUS : CY_TCPWM_COUNTER_ONESHOT;
- config.compareOrCapture = cfg->is_compare ? CY_TCPWM_COUNTER_MODE_COMPARE : CY_TCPWM_COUNTER_MODE_CAPTURE;
- config.countDirection = _cyhal_timer_convert_direction(cfg->direction);
- // DeInit will clear the interrupt mask; save it now and restore after we re-nit
- uint32_t old_mask = Cy_TCPWM_GetInterruptMask(obj->tcpwm.base, _CYHAL_TCPWM_CNT_NUMBER(obj->tcpwm.resource));
- Cy_TCPWM_Counter_DeInit(obj->tcpwm.base, _CYHAL_TCPWM_CNT_NUMBER(obj->tcpwm.resource), &config);
- rslt = (cy_rslt_t)Cy_TCPWM_Counter_Init(obj->tcpwm.base, _CYHAL_TCPWM_CNT_NUMBER(obj->tcpwm.resource), &config);
- Cy_TCPWM_Counter_Enable(obj->tcpwm.base, _CYHAL_TCPWM_CNT_NUMBER(obj->tcpwm.resource));
- Cy_TCPWM_SetInterruptMask(obj->tcpwm.base, _CYHAL_TCPWM_CNT_NUMBER(obj->tcpwm.resource), old_mask);
- // This must be called after Cy_TCPWM_Counter_Init
- cyhal_timer_reset(obj);
- }
- return rslt;
- }
- cy_rslt_t cyhal_timer_set_frequency(cyhal_timer_t *obj, uint32_t hz)
- {
- cy_rslt_t result = CY_RSLT_SUCCESS;
- #if defined(COMPONENT_CAT5)
- if(obj->is_t2timer)
- {
- result = _cyhal_t2timer_set_frequency(&obj->t2timer, hz);
- }
- else
- #endif
- {
- if(!obj->tcpwm.dedicated_clock)
- {
- result = CYHAL_TIMER_RSLT_ERR_SHARED_CLOCK;
- }
- #if !defined(COMPONENT_CAT5)
- const cyhal_clock_tolerance_t tolerance = {
- .type = CYHAL_TOLERANCE_PERCENT,
- .value = 2,
- };
- #endif
- if(CY_RSLT_SUCCESS == result)
- {
- #if defined(COMPONENT_CAT5)
- uint32_t current_freq = _cyhal_utils_get_peripheral_clock_frequency(&(obj->tcpwm.resource));
- uint32_t divider = ((current_freq + hz - 1) / hz);
- // _set_divider doesn't use this with CAT5, but to avoid warnings here is what it would use
- en_clk_dst_t clk_dst = (en_clk_dst_t)(_CYHAL_TCPWM_DATA[_CYHAL_TCPWM_ADJUST_BLOCK_INDEX(obj->tcpwm.resource.block_num)].clock_dst + obj->tcpwm.resource.channel_num);
- if(CY_RSLT_SUCCESS == _cyhal_utils_peri_pclk_set_divider(clk_dst, &obj->tcpwm.clock, (divider - 1)))
- #else
- if((CY_RSLT_SUCCESS == cyhal_clock_set_enabled(&obj->tcpwm.clock, false, false)) &&
- (CY_RSLT_SUCCESS == cyhal_clock_set_frequency(&obj->tcpwm.clock, hz, &tolerance)) &&
- (CY_RSLT_SUCCESS == cyhal_clock_set_enabled(&obj->tcpwm.clock, true, false)))
- #endif
- {
- obj->tcpwm.clock_hz = cyhal_clock_get_frequency(&obj->tcpwm.clock);
- }
- else
- {
- result = CYHAL_TIMER_RSLT_ERR_CLOCK_INIT;
- }
- }
- }
- return result;
- }
- cy_rslt_t cyhal_timer_start(cyhal_timer_t *obj)
- {
- CY_ASSERT(NULL != obj);
- cy_rslt_t result = CY_RSLT_SUCCESS;
- #if (CYHAL_DRIVER_AVAILABLE_SYSPM)
- #if defined(COMPONENT_CAT5)
- if (_cyhal_tcpwm_pm_transition_pending() || (obj->is_t2timer && obj->running))
- #else
- if (_cyhal_tcpwm_pm_transition_pending())
- #endif
- {
- return CYHAL_SYSPM_RSLT_ERR_PM_PENDING;
- }
- #endif
- #if defined(COMPONENT_CAT5)
- if(obj->is_t2timer)
- {
- result = _cyhal_t2timer_start(&obj->t2timer);
- if(result == CY_RSLT_SUCCESS)
- {
- obj->running = true;
- }
- }
- else
- #endif
- {
- Cy_TCPWM_Counter_Enable(obj->tcpwm.base, _CYHAL_TCPWM_CNT_NUMBER(obj->tcpwm.resource));
- #if defined(CY_IP_MXTCPWM) && (CY_IP_MXTCPWM_VERSION >= 2)
- Cy_TCPWM_TriggerStart_Single(obj->tcpwm.base, _CYHAL_TCPWM_CNT_NUMBER(obj->tcpwm.resource));
- #else
- Cy_TCPWM_TriggerStart(obj->tcpwm.base, (1 << _CYHAL_TCPWM_CNT_NUMBER(obj->tcpwm.resource)));
- #endif
- }
- return result;
- }
- cy_rslt_t cyhal_timer_stop(cyhal_timer_t *obj)
- {
- CY_ASSERT(NULL != obj);
- cy_rslt_t result = CY_RSLT_SUCCESS;
- #if defined(COMPONENT_CAT5)
- if(obj->is_t2timer)
- {
- result = _cyhal_t2timer_stop(&obj->t2timer);
- if(result == CY_RSLT_SUCCESS)
- {
- obj->running = false;
- }
- }
- else
- #endif
- {
- Cy_TCPWM_Counter_Disable(obj->tcpwm.base, _CYHAL_TCPWM_CNT_NUMBER(obj->tcpwm.resource));
- }
- return result;
- }
- cy_rslt_t cyhal_timer_reset(cyhal_timer_t *obj)
- {
- CY_ASSERT(NULL != obj);
- cy_rslt_t result = CY_RSLT_SUCCESS;
- #if defined(COMPONENT_CAT5)
- if(obj->is_t2timer)
- {
- result = _cyhal_t2timer_reset(&obj->t2timer);
- if(result == CY_RSLT_SUCCESS)
- {
- obj->running = true;
- }
- }
- else
- #endif
- {
- Cy_TCPWM_Counter_SetCounter(obj->tcpwm.base, _CYHAL_TCPWM_CNT_NUMBER(obj->tcpwm.resource), obj->default_value);
- }
- return result;
- }
- uint32_t cyhal_timer_read(const cyhal_timer_t *obj)
- {
- CY_ASSERT(NULL != obj);
- uint32_t read_value = 0;
- #if defined(COMPONENT_CAT5)
- if(obj->is_t2timer)
- {
- read_value = _cyhal_t2timer_read(&obj->t2timer);
- }
- else
- #endif
- {
- read_value = Cy_TCPWM_Counter_GetCounter(obj->tcpwm.base, _CYHAL_TCPWM_CNT_NUMBER(obj->tcpwm.resource));
- }
- return read_value;
- }
- static cyhal_tcpwm_input_t _cyhal_timer_translate_input_signal(cyhal_timer_input_t event)
- {
- switch(event)
- {
- case CYHAL_TIMER_INPUT_START:
- return CYHAL_TCPWM_INPUT_START;
- case CYHAL_TIMER_INPUT_STOP:
- return CYHAL_TCPWM_INPUT_STOP;
- case CYHAL_TIMER_INPUT_RELOAD:
- return CYHAL_TCPWM_INPUT_RELOAD;
- case CYHAL_TIMER_INPUT_COUNT:
- return CYHAL_TCPWM_INPUT_COUNT;
- case CYHAL_TIMER_INPUT_CAPTURE:
- return CYHAL_TCPWM_INPUT_CAPTURE;
- default:
- CY_ASSERT(false);
- return (cyhal_tcpwm_input_t)0;
- }
- }
- static cyhal_tcpwm_output_t _cyhal_timer_translate_output_signal(cyhal_timer_output_t signal)
- {
- switch(signal)
- {
- case CYHAL_TIMER_OUTPUT_OVERFLOW:
- return CYHAL_TCPWM_OUTPUT_OVERFLOW;
- case CYHAL_TIMER_OUTPUT_UNDERFLOW:
- return CYHAL_TCPWM_OUTPUT_UNDERFLOW;
- case CYHAL_TIMER_OUTPUT_COMPARE_MATCH:
- return CYHAL_TCPWM_OUTPUT_COMPARE_MATCH;
- case CYHAL_TIMER_OUTPUT_TERMINAL_COUNT:
- return CYHAL_TCPWM_OUTPUT_TERMINAL_COUNT;
- default:
- CY_ASSERT(false);
- return (cyhal_tcpwm_output_t)0;
- }
- }
- cy_rslt_t cyhal_timer_connect_digital2(cyhal_timer_t *obj, cyhal_source_t source, cyhal_timer_input_t signal, cyhal_edge_type_t edge_type)
- {
- #if defined(COMPONENT_CAT5)
- if(obj->is_t2timer)
- {
- return CYHAL_TIMER_RSLT_ERR_UNSUPPORTED;
- }
- #endif
- cyhal_tcpwm_input_t tcpwm_signal = _cyhal_timer_translate_input_signal(signal);
- return _cyhal_tcpwm_connect_digital(&(obj->tcpwm), source, tcpwm_signal, edge_type);
- }
- cy_rslt_t cyhal_timer_connect_digital(cyhal_timer_t *obj, cyhal_source_t source, cyhal_timer_input_t signal)
- {
- #if defined(COMPONENT_CAT5)
- if(obj->is_t2timer)
- {
- return CYHAL_TIMER_RSLT_ERR_UNSUPPORTED;
- }
- #endif
- #if defined(CY_IP_M0S8PERI_TR) || defined(CY_IP_MXPERI_TR) || defined(CY_IP_MXSPERI)
- /* Signal type just tells us edge vs. level, but TCPWM lets you customize which edge you want. So default
- * to rising edge. If the application cares about the edge type, it can use connect_digital2 */
- cyhal_signal_type_t signal_type = _CYHAL_TRIGGER_GET_SOURCE_TYPE(source);
- cyhal_edge_type_t edge_type = (signal_type == CYHAL_SIGNAL_TYPE_LEVEL) ? CYHAL_EDGE_TYPE_LEVEL : CYHAL_EDGE_TYPE_RISING_EDGE;
- return cyhal_timer_connect_digital2(obj, source, signal, edge_type);
- #else
- CY_UNUSED_PARAMETER(obj);
- CY_UNUSED_PARAMETER(source);
- CY_UNUSED_PARAMETER(signal);
- return CYHAL_TIMER_RSLT_ERR_BAD_ARGUMENT;
- #endif
- }
- cy_rslt_t cyhal_timer_enable_output(cyhal_timer_t *obj, cyhal_timer_output_t signal, cyhal_source_t *source)
- {
- #if defined(COMPONENT_CAT5)
- if(obj->is_t2timer)
- {
- return CYHAL_TIMER_RSLT_ERR_UNSUPPORTED;
- }
- #endif
- cyhal_tcpwm_output_t tcpwm_signal = _cyhal_timer_translate_output_signal(signal);
- return _cyhal_tcpwm_enable_output(&(obj->tcpwm), tcpwm_signal, source);
- }
- cy_rslt_t cyhal_timer_disconnect_digital(cyhal_timer_t *obj, cyhal_source_t source, cyhal_timer_input_t signal)
- {
- #if defined(COMPONENT_CAT5)
- if(obj->is_t2timer)
- {
- return CYHAL_TIMER_RSLT_ERR_UNSUPPORTED;
- }
- #endif
- return _cyhal_tcpwm_disconnect_digital(&(obj->tcpwm), source, _cyhal_timer_translate_input_signal(signal));
- }
- cy_rslt_t cyhal_timer_disable_output(cyhal_timer_t *obj, cyhal_timer_output_t signal)
- {
- #if defined(COMPONENT_CAT5)
- if(obj->is_t2timer)
- {
- return CYHAL_TIMER_RSLT_ERR_UNSUPPORTED;
- }
- #endif
- return _cyhal_tcpwm_disable_output(&(obj->tcpwm), _cyhal_timer_translate_output_signal(signal));
- }
- #if defined(__cplusplus)
- }
- #endif
- #endif /* CYHAL_DRIVER_AVAILABLE_TIMER */
|