1 /*******************************************************************************
2 * File Name: cyhal_quaddec.c
3 *
4 * Description:
5 * Provides a high level interface for interacting with the Quadrature Decoder.
6 * This is a wrapper around the lower level PDL API.
7 *
8 ********************************************************************************
9 * \copyright
10 * Copyright 2020-2022 Cypress Semiconductor Corporation (an Infineon company) or
11 * an affiliate of Cypress Semiconductor Corporation
12 *
13 * SPDX-License-Identifier: Apache-2.0
14 *
15 * Licensed under the Apache License, Version 2.0 (the "License");
16 * you may not use this file except in compliance with the License.
17 * You may obtain a copy of the License at
18 *
19 *     http://www.apache.org/licenses/LICENSE-2.0
20 *
21 * Unless required by applicable law or agreed to in writing, software
22 * distributed under the License is distributed on an "AS IS" BASIS,
23 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
24 * See the License for the specific language governing permissions and
25 * limitations under the License.
26 *******************************************************************************/
27 
28 /**
29  * \addtogroup group_hal_impl_quaddec QuadDec (Quadrature Decoder)
30  * \ingroup group_hal_impl
31  * \{
32  * \section section_hal_impl_quaddec_pins Selecting Pins for Quadrature Decoding
33  * To identify the pins that are available to use with the Quadrature Decoder, open the PSoC
34  * device datasheet and navigate to the 'Multiple Alternate Functions' table.  Any pin that has
35  * the peri.tr_io_input function can be used.
36  *
37  * \} group_hal_impl_quaddec
38  */
39 
40 #if defined(COMPONENT_CAT2)
41 /**
42  * \addtogroup group_hal_impl_quaddec QuadDec (Quadrature Decoder)
43  * \ingroup group_hal_impl
44  * \{
45  * \note The cyhal_quaddec_connect_digital() and cyhal_quaddec_disconnect_digital() functions are
46  * not supported with this device and will return a CYHAL_QUADDEC_RSLT_ERR_NOT_SUPPORTED error if
47  * it is called.  This device does not have the internal circuitry routing that this function is
48  * used to configure.
49  *
50  * \} group_hal_impl_quaddec
51  */
52 #endif
53 
54 #include "cy_tcpwm.h"
55 #include "cy_tcpwm_quaddec.h"
56 #include "cyhal_clock.h"
57 #include "cyhal_gpio.h"
58 #include "cyhal_hwmgr.h"
59 #include "cyhal_interconnect.h"
60 #include "cyhal_quaddec.h"
61 #include "cyhal_syspm.h"
62 #include "cyhal_tcpwm_common.h"
63 #include "cyhal_utils.h"
64 #include "cyhal_hwmgr_impl.h"
65 
66 #include <string.h>
67 
68 #if (CYHAL_DRIVER_AVAILABLE_QUADDEC)
69 
70 #if defined(__cplusplus)
71 extern "C" {
72 #endif
73 
74 #define _CYHAL_CNT_NUM _CYHAL_TCPWM_CNT_NUMBER(obj->tcpwm.resource)
75 
76 #if defined(COMPONENT_CAT1A) || defined(COMPONENT_CAT1B) || defined(COMPONENT_CAT1C) || \
77     defined(COMPONENT_CAT1D) || defined(COMPONENT_CAT5)
78 static cyhal_tcpwm_input_t _cyhal_quaddec_translate_input_signal(cyhal_quaddec_input_t signal);
79 #endif
80 
_cyhal_quaddec_configure_clock(cyhal_tcpwm_t * tcpwm,en_clk_dst_t pclk,uint32_t frequency)81 static inline cy_rslt_t _cyhal_quaddec_configure_clock(cyhal_tcpwm_t *tcpwm, en_clk_dst_t pclk, uint32_t frequency)
82 {
83     cy_rslt_t rslt;
84 
85     #if defined(COMPONENT_CAT5)
86     CY_UNUSED_PARAMETER(pclk);
87     #else
88     const cyhal_clock_tolerance_t tolerance = {
89         .type = CYHAL_TOLERANCE_PERCENT,
90         .value = 2,
91     };
92     #endif
93 
94     #if defined(COMPONENT_CAT1A) || defined(COMPONENT_CAT1B) || defined(COMPONENT_CAT1C) || defined(COMPONENT_CAT1D)
95         rslt = _cyhal_utils_allocate_clock(&tcpwm->clock, &tcpwm->resource, CYHAL_CLOCK_BLOCK_PERIPHERAL_16BIT, true);
96     #elif defined(COMPONENT_CAT2)
97         rslt = cyhal_clock_allocate(&tcpwm->clock, CYHAL_CLOCK_BLOCK_PERIPHERAL_16BIT);
98     #elif defined(COMPONENT_CAT5)
99         rslt = CY_RSLT_SUCCESS; // No need to allocate the TCPWM clock as it is a shared resource
100     #else
101         #warning "No clock allocated for QuadDec"
102         rslt = CYHAL_QUADDEC_RSLT_ERR_CLOCK_INIT;
103     #endif
104 
105     if (rslt == CY_RSLT_SUCCESS)
106     {
107         tcpwm->dedicated_clock = true;
108 
109         #if defined(COMPONENT_CAT5)
110             uint32_t current_freq = _cyhal_utils_get_peripheral_clock_frequency(&(tcpwm->resource));
111             if (frequency <= current_freq)
112             {
113                 uint32_t divider = ((current_freq + frequency - 1) / frequency);
114                 // _set_divider doesn't use this with CAT5, but to avoid warnings here is what it would use
115                 en_clk_dst_t clk_dst = (en_clk_dst_t)(_CYHAL_TCPWM_DATA[_CYHAL_TCPWM_ADJUST_BLOCK_INDEX(tcpwm->resource.block_num)].clock_dst + tcpwm->resource.channel_num);
116 
117                 if (_cyhal_utils_peri_pclk_set_divider(clk_dst, &tcpwm->clock, (divider - 1)) == CY_RSLT_SUCCESS)
118                 {
119                     return CY_RSLT_SUCCESS;
120                 }
121             }
122         #else
123             if (cyhal_clock_set_frequency(&tcpwm->clock, frequency, &tolerance) == CY_RSLT_SUCCESS)
124             {
125                 if (_cyhal_utils_peri_pclk_assign_divider(pclk, &(tcpwm->clock)) == CY_SYSCLK_SUCCESS)
126                 {
127                     cyhal_clock_set_enabled(&tcpwm->clock, true, false);
128                     return CY_RSLT_SUCCESS;
129                 }
130             }
131         #endif
132     }
133 
134     return CYHAL_QUADDEC_RSLT_ERR_CLOCK_INIT;
135 }
136 
137 #if defined(COMPONENT_CAT2)
138 //--------------------------------------------------------------------------------------------------
139 // _cyhal_quaddec_connect_pin
140 //
141 // NOTE: This function should be called after the pin has been initialized with cyhal_gpio_init().
142 //--------------------------------------------------------------------------------------------------
_cyhal_quaddec_connect_pin(cyhal_quaddec_input_t signal,cyhal_gpio_t pin,TCPWM_Type * base,uint8_t channel_num)143 cy_rslt_t _cyhal_quaddec_connect_pin(cyhal_quaddec_input_t signal, cyhal_gpio_t pin,
144                                      TCPWM_Type* base, uint8_t channel_num)
145 {
146 #if defined(CYHAL_PIN_MAP_DRIVE_MODE_TCPWM_TR_IN)
147     bool found = false;
148     uint8_t index;
149     uint8_t array_size = sizeof(cyhal_pin_map_tcpwm_tr_in) / sizeof(cyhal_resource_pin_mapping_t);
150     cyhal_resource_pin_mapping_t mapping;
151 
152     // Search through cyhal_pin_map_tcpwm_tr_in to determine if pin can be
153     // used to drive a trigger line.
154     for (index = 0; index < array_size; index++)
155     {
156         mapping = cyhal_pin_map_tcpwm_tr_in[index];
157 
158         if (mapping.pin == pin)
159         {
160             found = true;
161             Cy_GPIO_SetHSIOM(CYHAL_GET_PORTADDR(pin), CYHAL_GET_PIN(pin), mapping.hsiom);
162             break;
163         }
164     }
165 
166     if (!found)
167     {
168         return CYHAL_GPIO_RSLT_ERR_NO_OUTPUT_SIGNAL;
169     }
170 
171     switch (signal)
172     {
173         case CYHAL_QUADDEC_INPUT_PHI_A:
174             TCPWM_CNT_TR_CTRL0(base, channel_num) &= ~TCPWM_CNT_TR_CTRL0_COUNT_SEL_Msk;
175             TCPWM_CNT_TR_CTRL0(base, channel_num) |= _VAL2FLD(TCPWM_CNT_TR_CTRL0_COUNT_SEL,
176                                                               (uint32_t)(mapping.channel_num) + 2);
177             TCPWM_CNT_TR_CTRL1(base, channel_num) &= ~TCPWM_CNT_TR_CTRL1_COUNT_EDGE_Msk;
178             TCPWM_CNT_TR_CTRL1(base, channel_num) |= _VAL2FLD(TCPWM_CNT_TR_CTRL1_COUNT_EDGE,
179                                                               CYHAL_EDGE_TYPE_RISING_EDGE);
180             break;
181 
182         case CYHAL_QUADDEC_INPUT_PHI_B:
183             TCPWM_CNT_TR_CTRL0(base, channel_num) &= ~TCPWM_CNT_TR_CTRL0_START_SEL_Msk;
184             TCPWM_CNT_TR_CTRL0(base, channel_num) |= _VAL2FLD(TCPWM_CNT_TR_CTRL0_START_SEL,
185                                                               (uint32_t)(mapping.channel_num) + 2);
186             TCPWM_CNT_TR_CTRL1(base, channel_num) &= ~TCPWM_CNT_TR_CTRL1_START_EDGE_Msk;
187             TCPWM_CNT_TR_CTRL1(base, channel_num) |= _VAL2FLD(TCPWM_CNT_TR_CTRL1_START_EDGE,
188                                                               CYHAL_EDGE_TYPE_RISING_EDGE);
189             break;
190 
191         case CYHAL_QUADDEC_INPUT_INDEX:
192             TCPWM_CNT_TR_CTRL0(base, channel_num) &= ~TCPWM_CNT_TR_CTRL0_RELOAD_SEL_Msk;
193             TCPWM_CNT_TR_CTRL0(base, channel_num) |= _VAL2FLD(TCPWM_CNT_TR_CTRL0_RELOAD_SEL,
194                                                               (uint32_t)(mapping.channel_num) + 2);
195             TCPWM_CNT_TR_CTRL1(base, channel_num) &= ~TCPWM_CNT_TR_CTRL1_RELOAD_EDGE_Msk;
196             TCPWM_CNT_TR_CTRL1(base, channel_num) |= _VAL2FLD(TCPWM_CNT_TR_CTRL1_RELOAD_EDGE,
197                                                               CYHAL_EDGE_TYPE_RISING_EDGE);
198             break;
199 
200         default:
201             return CYHAL_GPIO_RSLT_ERR_NO_OUTPUT_SIGNAL;
202     }
203 
204     return CY_RSLT_SUCCESS;
205 #else
206     CY_UNUSED_PARAMETER(signal);
207     CY_UNUSED_PARAMETER(pin);
208     CY_UNUSED_PARAMETER(base);
209     CY_UNUSED_PARAMETER(channel_num);
210 
211     return CYHAL_GPIO_RSLT_ERR_NO_OUTPUT_SIGNAL;
212 #endif /* defined(CYHAL_PIN_MAP_DRIVE_MODE_TCPWM_TR_IN) */
213 }
214 #endif
215 
_cyhal_quaddec_pin_init(cyhal_quaddec_t * obj,cyhal_gpio_t pin,cyhal_gpio_t * obj_pin,cyhal_signal_type_t signal_type,cyhal_quaddec_input_t input)216 static cy_rslt_t _cyhal_quaddec_pin_init(cyhal_quaddec_t *obj, cyhal_gpio_t pin, cyhal_gpio_t *obj_pin, cyhal_signal_type_t signal_type, cyhal_quaddec_input_t input)
217 {
218     if (NC == pin)
219     {
220         return CYHAL_QUADDEC_RSLT_ERR_BAD_ARGUMENT;
221     }
222 
223     #if defined (COMPONENT_CAT5)
224         CY_UNUSED_PARAMETER(signal_type);
225         cy_rslt_t rslt = CYHAL_QUADDEC_RSLT_ERR_BAD_ARGUMENT;
226         const cyhal_resource_pin_mapping_t* pinMap = _CYHAL_UTILS_GET_RESOURCE(pin, cyhal_pin_map_tcpwm_tr_all_cnt_in);
227 
228         if (pinMap != NULL)
229             rslt = _cyhal_utils_reserve_and_connect(pinMap, CYHAL_PIN_MAP_DRIVE_MODE_TCPWM_TR_IN);
230         else
231             rslt = CYHAL_HWMGR_RSLT_ERR_INVALID; // Note: Intentional to match PSoC behavior
232     #else
233         cy_rslt_t rslt = cyhal_gpio_init(pin, CYHAL_GPIO_DIR_INPUT, CYHAL_GPIO_DRIVE_NONE, false);
234     #endif
235 
236     if (rslt == CY_RSLT_SUCCESS)
237     {
238         *obj_pin = pin;
239         #if defined(COMPONENT_CAT1A) || defined(COMPONENT_CAT1B) || defined(COMPONENT_CAT1C) || \
240             defined(COMPONENT_CAT1D) || defined(COMPONENT_CAT5)
241         uint8_t idx = (uint8_t)_cyhal_quaddec_translate_input_signal(input);
242 
243         #if !defined(COMPONENT_CAT5)
244             // Already taken care of in cyhal_connect_pin() for CAT5
245             rslt = cyhal_gpio_enable_output(pin, signal_type, &(obj->tcpwm.inputs[idx]));
246         #endif
247 
248         if (rslt == CY_RSLT_SUCCESS)
249         {
250             rslt = cyhal_quaddec_connect_digital(obj, obj->tcpwm.inputs[idx], input);
251             if (rslt != CY_RSLT_SUCCESS)
252             {
253                 obj->tcpwm.inputs[idx] = CYHAL_TRIGGER_CPUSS_ZERO;
254             }
255         }
256         #elif defined(COMPONENT_CAT2)
257         CY_UNUSED_PARAMETER(signal_type);
258         rslt = _cyhal_quaddec_connect_pin(input, pin, obj->tcpwm.base,
259                                           obj->tcpwm.resource.channel_num);
260         #else
261         #warning "_cyhal_quaddec_pin_init encountered unsupported architecture"
262         #endif
263     }
264     return rslt;
265 }
266 
267 #if defined(COMPONENT_CAT1A) || defined(COMPONENT_CAT1B) || defined(COMPONENT_CAT1C) || defined(COMPONENT_CAT1D)
268 static uint8_t _cyhal_quaddec_get_phy_a_input_dest_trig_idx;
_cyhal_quaddec_get_phy_a_input_dest(uint8_t block_num,uint8_t channel_num)269 static cyhal_dest_t _cyhal_quaddec_get_phy_a_input_dest(uint8_t block_num, uint8_t channel_num)
270 {
271     CY_UNUSED_PARAMETER(channel_num);
272     uint8_t trig_idx = _cyhal_quaddec_get_phy_a_input_dest_trig_idx;
273     if (trig_idx >= _CYHAL_TCPWM_TRIGGER_INPUTS_PER_BLOCK[_CYHAL_TCPWM_GET_IP_BLOCK(block_num)])
274     {
275         /* We cannot use unexisting for current block number index, so this is rather incorrect param situation.
276         There is no possibility to return warning/error code, setting trig index to default 0. */
277         trig_idx = 0;
278     }
279     return _cyhal_tpwm_calculate_dest(block_num, trig_idx);
280 }
281 #endif
282 
_cyhal_quaddec_init_hw(cyhal_quaddec_t * obj,const cy_stc_tcpwm_quaddec_config_t * config,const cyhal_clock_t * clk,uint32_t frequency)283 cy_rslt_t _cyhal_quaddec_init_hw(cyhal_quaddec_t *obj, const cy_stc_tcpwm_quaddec_config_t *config, const cyhal_clock_t *clk,
284                                  uint32_t frequency /* Only required if clk == NULL */)
285 {
286     obj->last_counter_value = 1 << (_CYHAL_TCPWM_DATA[_CYHAL_TCPWM_ADJUST_BLOCK_INDEX(obj->tcpwm.resource.block_num)].max_count - 1);
287 
288     obj->tcpwm.base = _CYHAL_TCPWM_DATA[_CYHAL_TCPWM_ADJUST_BLOCK_INDEX(obj->tcpwm.resource.block_num)].base;
289     _cyhal_tcpwm_init_data(&obj->tcpwm);
290 
291     cy_rslt_t rslt = Cy_TCPWM_QuadDec_Init(obj->tcpwm.base, _CYHAL_CNT_NUM, config);
292 
293     // Clock configuration
294     if (CY_RSLT_SUCCESS == rslt)
295     {
296         en_clk_dst_t pclk =(en_clk_dst_t)(_CYHAL_TCPWM_DATA[
297             _CYHAL_TCPWM_ADJUST_BLOCK_INDEX(obj->tcpwm.resource.block_num)].clock_dst + obj->tcpwm.resource.channel_num);
298 
299         if (clk != NULL)
300         {
301             if (frequency != 0)
302             {
303                 rslt = CYHAL_QUADDEC_RSLT_ERR_BAD_ARGUMENT;
304             }
305             else
306             {
307                 obj->tcpwm.clock = *clk;
308 
309                 if (_cyhal_utils_peri_pclk_assign_divider(pclk, &(obj->tcpwm.clock)) != CY_SYSCLK_SUCCESS)
310                 {
311                     rslt = CYHAL_QUADDEC_RSLT_ERR_CLOCK_INIT;
312                 }
313             }
314         }
315         else
316         {
317             rslt = _cyhal_quaddec_configure_clock(&obj->tcpwm, pclk, frequency);
318         }
319 
320         if (rslt == CY_RSLT_SUCCESS)
321         {
322             obj->tcpwm.clock_hz = cyhal_clock_get_frequency(&obj->tcpwm.clock);
323         }
324     }
325     return rslt;
326 }
327 
cyhal_quaddec_init(cyhal_quaddec_t * obj,cyhal_gpio_t phi_a,cyhal_gpio_t phi_b,cyhal_gpio_t index,cyhal_quaddec_resolution_t resolution,const cyhal_clock_t * clk,uint32_t frequency)328 cy_rslt_t cyhal_quaddec_init(cyhal_quaddec_t *obj, cyhal_gpio_t phi_a, cyhal_gpio_t phi_b,
329                              cyhal_gpio_t index, cyhal_quaddec_resolution_t resolution,
330                              const cyhal_clock_t *clk, uint32_t frequency)
331 {
332     CY_ASSERT(obj != NULL);
333     cy_rslt_t rslt = CY_RSLT_SUCCESS;
334 
335     // Initialize the quadrature object
336     // Explicitly marked not allocated resources as invalid to prevent freeing them.
337     memset(obj, 0, sizeof(cyhal_quaddec_t));
338     obj->phi_a = NC;
339     obj->phi_b = NC;
340     obj->index = NC;
341     obj->tcpwm.resource.type = CYHAL_RSC_INVALID;
342 
343     // Allocate TCPWM resource
344     #if defined(COMPONENT_CAT1A) || defined(COMPONENT_CAT1B) || defined(COMPONENT_CAT1C)  || \
345         defined(COMPONENT_CAT1D)
346     if (NC == phi_a)
347     {
348         return CYHAL_QUADDEC_RSLT_ERR_BAD_ARGUMENT;
349     }
350 
351     cyhal_source_t phy_a_src;
352     uint8_t phy_a_idx = (uint8_t)_cyhal_quaddec_translate_input_signal(CYHAL_QUADDEC_INPUT_PHI_A);
353     rslt = cyhal_gpio_init(phi_a, CYHAL_GPIO_DIR_INPUT, CYHAL_GPIO_DRIVE_NONE, false);
354 
355     if (rslt == CY_RSLT_SUCCESS)
356     {
357         obj->phi_a = phi_a;
358         rslt = cyhal_gpio_enable_output(phi_a, CYHAL_SIGNAL_TYPE_LEVEL, &phy_a_src);
359 
360         uint16_t max_trig_cnt = 0;
361         for (size_t idx = 0; idx < (sizeof(_CYHAL_TCPWM_TRIGGER_INPUTS_PER_BLOCK) / sizeof(_CYHAL_TCPWM_TRIGGER_INPUTS_PER_BLOCK[0])); ++idx)
362         {
363             if (max_trig_cnt < _CYHAL_TCPWM_TRIGGER_INPUTS_PER_BLOCK[idx])
364             {
365                 max_trig_cnt = _CYHAL_TCPWM_TRIGGER_INPUTS_PER_BLOCK[idx];
366             }
367         }
368 
369         if (rslt == CY_RSLT_SUCCESS)
370         {
371             _cyhal_quaddec_get_phy_a_input_dest_trig_idx = 0;
372             do
373             {
374                 rslt = _cyhal_hwmgr_allocate_with_connection(CYHAL_RSC_TCPWM, &phy_a_src,
375                     NULL, NULL, _cyhal_quaddec_get_phy_a_input_dest, &obj->tcpwm.resource);
376                 _cyhal_quaddec_get_phy_a_input_dest_trig_idx++;
377             } while (CY_RSLT_SUCCESS != rslt && _cyhal_quaddec_get_phy_a_input_dest_trig_idx < max_trig_cnt);
378         }
379     }
380     #else
381         rslt = cyhal_hwmgr_allocate(CYHAL_RSC_TCPWM, &obj->tcpwm.resource);
382     #endif
383 
384     // Basic configuration of TCPWM for quadrature functionality
385     if (rslt == CY_RSLT_SUCCESS)
386     {
387         cy_stc_tcpwm_quaddec_config_t config;
388 
389         memset(&config, 0, sizeof(cy_stc_tcpwm_quaddec_config_t));
390 
391         config.resolution = resolution;
392 
393         #if (CY_IP_MXTCPWM_VERSION >= 2U)
394         config.phiAInputMode  = CY_TCPWM_INPUT_LEVEL;                // Pass thorugh (no edge detection)
395         config.phiBInputMode  = CY_TCPWM_INPUT_LEVEL;                // Pass thorugh (no edge detection)
396         config.trigger0Event  = CY_TCPWM_CNT_TRIGGER_ON_DISABLED;    // Disable output trigger0 event generation
397         config.trigger1Event  = CY_TCPWM_CNT_TRIGGER_ON_DISABLED;    // Disable output trigger1 event generation
398         #endif
399 
400         // This function is only used to set the resolution and configure the TCPWM for quadrature mode.
401         // It also configures the inputs, but that will be overwritten by cyhal_quaddec_connect_digital().
402         // So, this must be called before that function is called.
403         rslt = _cyhal_quaddec_init_hw(obj, &config, clk, frequency);
404     }
405 
406     // Pin configuration
407     if (rslt == CY_RSLT_SUCCESS)
408     {
409         // See note above about CAT5 triggers
410         #if defined(COMPONENT_CAT1A) || defined(COMPONENT_CAT1B) || defined(COMPONENT_CAT1C) || \
411             defined(COMPONENT_CAT1D) // already initialized above
412     	    obj->tcpwm.inputs[phy_a_idx] = phy_a_src;;
413             rslt = cyhal_quaddec_connect_digital(obj, obj->tcpwm.inputs[phy_a_idx], CYHAL_QUADDEC_INPUT_PHI_A);
414         #else
415             rslt = _cyhal_quaddec_pin_init(obj, phi_a, &(obj->phi_a), CYHAL_SIGNAL_TYPE_LEVEL, CYHAL_QUADDEC_INPUT_PHI_A);
416         #endif
417 
418         if (rslt == CY_RSLT_SUCCESS)
419         {
420             rslt = _cyhal_quaddec_pin_init(obj, phi_b, &(obj->phi_b), CYHAL_SIGNAL_TYPE_LEVEL, CYHAL_QUADDEC_INPUT_PHI_B);
421         }
422 
423         if (index != NC)
424         {
425             if (rslt == CY_RSLT_SUCCESS)
426             {
427                 rslt = _cyhal_quaddec_pin_init(obj, index, &(obj->index), CYHAL_SIGNAL_TYPE_EDGE, CYHAL_QUADDEC_INPUT_INDEX);
428             }
429         }
430         // See note above about CAT5 triggers
431         #if defined(COMPONENT_CAT1A) || defined(COMPONENT_CAT1B) || defined(COMPONENT_CAT1C) || \
432             defined(COMPONENT_CAT1D)
433         else
434         {
435             uint8_t idx = (uint8_t)_cyhal_quaddec_translate_input_signal(CYHAL_QUADDEC_INPUT_INDEX);
436             obj->tcpwm.inputs[idx] = CYHAL_TRIGGER_CPUSS_ZERO;
437         }
438         #endif
439     }
440 
441     // Enable the quadrature function
442     if (rslt == CY_RSLT_SUCCESS)
443     {
444         Cy_TCPWM_QuadDec_Enable(obj->tcpwm.base, _CYHAL_CNT_NUM);
445     }
446     else
447     {
448         cyhal_quaddec_free(obj);
449     }
450 
451     return rslt;
452 }
453 
cyhal_quaddec_init_cfg(cyhal_quaddec_t * obj,const cyhal_quaddec_configurator_t * cfg)454 cy_rslt_t cyhal_quaddec_init_cfg(cyhal_quaddec_t *obj, const cyhal_quaddec_configurator_t *cfg)
455 {
456     memset(obj, 0, sizeof(cyhal_quaddec_t));
457     /* These pins are probably actually connected, but that is handled by the configurator */
458     obj->phi_a = NC;
459     obj->phi_b = NC;
460     obj->index = NC;
461     obj->tcpwm.resource = *cfg->resource;
462     obj->tcpwm.owned_by_configurator = true;
463     cy_rslt_t result = _cyhal_quaddec_init_hw(obj, cfg->config, cfg->clock, 0u /* Ignored because configurator provides a clock */);
464 
465     /* No need to populate obj->tcpwm.inputs because that is only used to interact with the interconnect
466      * driver, and all of our routing here was configured by the configurator */
467 
468     if (CY_RSLT_SUCCESS == result)
469     {
470         Cy_TCPWM_QuadDec_Enable(obj->tcpwm.base, _CYHAL_CNT_NUM);
471     }
472     else
473     {
474         cyhal_quaddec_free(obj);
475     }
476     return result;
477 }
478 
cyhal_quaddec_free(cyhal_quaddec_t * obj)479 void cyhal_quaddec_free(cyhal_quaddec_t *obj)
480 {
481     CY_ASSERT(obj != NULL);
482     #if defined(COMPONENT_CAT1A) || defined(COMPONENT_CAT1B) || defined(COMPONENT_CAT1C) || \
483         defined(COMPONENT_CAT1D) || defined(COMPONENT_CAT5)
484     uint8_t idx_phi_a = (uint8_t)_cyhal_quaddec_translate_input_signal(CYHAL_QUADDEC_INPUT_PHI_A);
485     if ((obj->phi_a != NC) && (obj->tcpwm.inputs[idx_phi_a] != CYHAL_TRIGGER_CPUSS_ZERO))
486     {
487         cyhal_quaddec_disconnect_digital(obj, obj->tcpwm.inputs[idx_phi_a], CYHAL_QUADDEC_INPUT_PHI_A);
488     }
489     uint8_t idx_phi_b = (uint8_t)_cyhal_quaddec_translate_input_signal(CYHAL_QUADDEC_INPUT_PHI_B);
490     if ((obj->phi_b != NC) && (obj->tcpwm.inputs[idx_phi_b] != CYHAL_TRIGGER_CPUSS_ZERO))
491     {
492         cyhal_quaddec_disconnect_digital(obj, obj->tcpwm.inputs[idx_phi_b], CYHAL_QUADDEC_INPUT_PHI_B);
493     }
494     uint8_t idx_index = (uint8_t)_cyhal_quaddec_translate_input_signal(CYHAL_QUADDEC_INPUT_INDEX);
495     if ((obj->index != NC) && (obj->tcpwm.inputs[idx_index] != CYHAL_TRIGGER_CPUSS_ZERO))
496     {
497         cyhal_quaddec_disconnect_digital(obj, obj->tcpwm.inputs[idx_index], CYHAL_QUADDEC_INPUT_INDEX);
498     }
499     #endif
500     _cyhal_utils_release_if_used(&obj->phi_a);
501     _cyhal_utils_release_if_used(&obj->phi_b);
502     _cyhal_utils_release_if_used(&obj->index);
503     if (obj->tcpwm.resource.type != CYHAL_RSC_INVALID)
504     {
505         _cyhal_tcpwm_free(&obj->tcpwm);
506     }
507 }
508 
cyhal_quaddec_start(cyhal_quaddec_t * obj)509 cy_rslt_t cyhal_quaddec_start(cyhal_quaddec_t *obj)
510 {
511     CY_ASSERT(obj != NULL);
512     if (_cyhal_tcpwm_pm_transition_pending())
513     {
514         return CYHAL_SYSPM_RSLT_ERR_PM_PENDING;
515     }
516     Cy_TCPWM_QuadDec_Enable(obj->tcpwm.base, _CYHAL_CNT_NUM);
517 
518     // Trigger reload/index the QuadDec.
519     // From the TRM, "A reload trigger must be provided through firmware to start the counter if
520     // the hardware reload signal is not enabled."
521     #if defined(CY_IP_MXTCPWM) && (CY_IP_MXTCPWM_VERSION >= 2)
522     Cy_TCPWM_TriggerReloadOrIndex_Single(obj->tcpwm.base, _CYHAL_CNT_NUM);
523     #else
524     Cy_TCPWM_TriggerReloadOrIndex(obj->tcpwm.base, 1 << _CYHAL_CNT_NUM);
525     #endif
526 
527     obj->last_counter_value = 1 << (_CYHAL_TCPWM_DATA[_CYHAL_TCPWM_ADJUST_BLOCK_INDEX(obj->tcpwm.resource.block_num)].max_count - 1);
528 
529     return CY_RSLT_SUCCESS;
530 }
531 
cyhal_quaddec_stop(cyhal_quaddec_t * obj)532 cy_rslt_t cyhal_quaddec_stop(cyhal_quaddec_t *obj)
533 {
534     CY_ASSERT(obj != NULL);
535     Cy_TCPWM_QuadDec_Disable(obj->tcpwm.base, _CYHAL_CNT_NUM);
536     return CY_RSLT_SUCCESS;
537 }
538 
cyhal_quaddec_get_delta(cyhal_quaddec_t * obj)539 int32_t cyhal_quaddec_get_delta(cyhal_quaddec_t *obj)
540 {
541     CY_ASSERT(obj != NULL);
542 
543     uint32_t midpoint = 1 << (_CYHAL_TCPWM_DATA[_CYHAL_TCPWM_ADJUST_BLOCK_INDEX(obj->tcpwm.resource.block_num)].max_count - 1);
544     uint32_t max_delta = midpoint >> 1;
545     uint32_t max_counter_value = (midpoint == 0x8000U) ? 0xFFFFU : 0xFFFFFFFFU;
546     uint32_t current_counter_value = Cy_TCPWM_QuadDec_GetCounter(obj->tcpwm.base, _CYHAL_CNT_NUM);
547     int32_t  delta = current_counter_value - obj->last_counter_value;
548 
549     // Overflow has occurred
550     if (delta < (int32_t)(-max_delta))
551     {
552         delta = max_counter_value - obj->last_counter_value;
553         delta += current_counter_value - midpoint;
554     }
555     // Underflow has occurred
556     else if (delta > (int32_t)max_delta)
557     {
558         delta = (int32_t)(-obj->last_counter_value);
559         delta += current_counter_value - midpoint;
560     }
561 
562     obj->last_counter_value = current_counter_value;
563 
564     return delta;
565 }
566 
cyhal_quaddec_read_counter(const cyhal_quaddec_t * obj)567 uint32_t cyhal_quaddec_read_counter(const cyhal_quaddec_t *obj)
568 {
569     CY_ASSERT(obj != NULL);
570     return Cy_TCPWM_QuadDec_GetCounter(obj->tcpwm.base, _CYHAL_CNT_NUM);
571 }
572 
cyhal_quaddec_read_capture(const cyhal_quaddec_t * obj)573 uint32_t cyhal_quaddec_read_capture(const cyhal_quaddec_t *obj)
574 {
575     CY_ASSERT(obj != NULL);
576     #if defined(CY_IP_MXTCPWM) && (CY_IP_MXTCPWM_VERSION >= 2)
577     return Cy_TCPWM_QuadDec_GetCapture0Val(obj->tcpwm.base, _CYHAL_CNT_NUM);
578     #else
579     return Cy_TCPWM_QuadDec_GetCapture(obj->tcpwm.base, _CYHAL_CNT_NUM);
580     #endif
581 }
582 
583 #if defined(COMPONENT_CAT1A) || defined(COMPONENT_CAT1B) || defined(COMPONENT_CAT1C) || \
584     defined(COMPONENT_CAT1D) || defined(COMPONENT_CAT5)
585 
_cyhal_quaddec_translate_input_signal(cyhal_quaddec_input_t signal)586 static cyhal_tcpwm_input_t _cyhal_quaddec_translate_input_signal(cyhal_quaddec_input_t signal)
587 {
588     switch (signal)
589     {
590         case CYHAL_QUADDEC_INPUT_PHI_A:
591             return CYHAL_TCPWM_INPUT_COUNT;
592         case CYHAL_QUADDEC_INPUT_PHI_B:
593             return CYHAL_TCPWM_INPUT_START;
594         case CYHAL_QUADDEC_INPUT_STOP:
595             return CYHAL_TCPWM_INPUT_STOP;
596         case CYHAL_QUADDEC_INPUT_INDEX:
597             return CYHAL_TCPWM_INPUT_RELOAD;
598         default:
599             CY_ASSERT(false);
600             return (cyhal_tcpwm_input_t)0;
601     }
602 }
603 
cyhal_quaddec_connect_digital2(cyhal_quaddec_t * obj,cyhal_source_t source,cyhal_quaddec_input_t signal,cyhal_edge_type_t edge_type)604 cy_rslt_t cyhal_quaddec_connect_digital2(cyhal_quaddec_t *obj, cyhal_source_t source, cyhal_quaddec_input_t signal, cyhal_edge_type_t edge_type)
605 {
606     cyhal_tcpwm_input_t tcpwm_signal = _cyhal_quaddec_translate_input_signal(signal);
607     return _cyhal_tcpwm_connect_digital(&(obj->tcpwm), source, tcpwm_signal, edge_type);
608 }
609 
cyhal_quaddec_connect_digital(cyhal_quaddec_t * obj,cyhal_source_t source,cyhal_quaddec_input_t signal)610 cy_rslt_t cyhal_quaddec_connect_digital(cyhal_quaddec_t *obj, cyhal_source_t source, cyhal_quaddec_input_t signal)
611 {
612 #if defined(CY_IP_M0S8PERI_TR) || defined(CY_IP_MXPERI_TR) || defined(CY_IP_MXSPERI)
613     /* Signal type just tells us edge vs. level, but TCPWM lets you customize which edge you want. So default
614      * to rising edge. If the application cares about the edge type, it can use connect_digital2 */
615     cyhal_signal_type_t signal_type = _CYHAL_TRIGGER_GET_SOURCE_TYPE(source);
616     cyhal_edge_type_t edge_type = (signal_type == CYHAL_SIGNAL_TYPE_LEVEL) ? CYHAL_EDGE_TYPE_LEVEL : CYHAL_EDGE_TYPE_RISING_EDGE;
617     return cyhal_quaddec_connect_digital2(obj, source, signal, edge_type);
618 #else
619     CY_UNUSED_PARAMETER(obj);
620     CY_UNUSED_PARAMETER(source);
621     CY_UNUSED_PARAMETER(signal);
622     return CYHAL_QUADDEC_RSLT_ERR_BAD_ARGUMENT;
623 #endif
624 }
625 
cyhal_quaddec_disconnect_digital(cyhal_quaddec_t * obj,cyhal_source_t source,cyhal_quaddec_input_t signal)626 cy_rslt_t cyhal_quaddec_disconnect_digital(cyhal_quaddec_t *obj, cyhal_source_t source, cyhal_quaddec_input_t signal)
627 {
628     return _cyhal_tcpwm_disconnect_digital(&(obj->tcpwm), source,
629                                            _cyhal_quaddec_translate_input_signal(signal));
630 }
631 
632 #elif defined(COMPONENT_CAT2)
633 
cyhal_quaddec_connect_digital(cyhal_quaddec_t * obj,cyhal_source_t source,cyhal_quaddec_input_t signal)634 cy_rslt_t cyhal_quaddec_connect_digital(cyhal_quaddec_t *obj, cyhal_source_t source, cyhal_quaddec_input_t signal)
635 {
636     CY_UNUSED_PARAMETER(obj);
637     CY_UNUSED_PARAMETER(source);
638     CY_UNUSED_PARAMETER(signal);
639     return CYHAL_QUADDEC_RSLT_ERR_NOT_SUPPORTED;
640 }
641 
cyhal_quaddec_disconnect_digital(cyhal_quaddec_t * obj,cyhal_source_t source,cyhal_quaddec_input_t signal)642 cy_rslt_t cyhal_quaddec_disconnect_digital(cyhal_quaddec_t *obj, cyhal_source_t source, cyhal_quaddec_input_t signal)
643 {
644     CY_UNUSED_PARAMETER(obj);
645     CY_UNUSED_PARAMETER(source);
646     CY_UNUSED_PARAMETER(signal);
647     return CYHAL_QUADDEC_RSLT_ERR_NOT_SUPPORTED;
648 }
649 
650 #endif
651 
_cyhal_quaddec_translate_output_signal(cyhal_quaddec_output_t signal)652 static cyhal_tcpwm_output_t _cyhal_quaddec_translate_output_signal(cyhal_quaddec_output_t signal)
653 {
654     switch (signal)
655     {
656         case CYHAL_QUADDEC_OUTPUT_COMPARE_MATCH:
657             return CYHAL_TCPWM_OUTPUT_COMPARE_MATCH;
658         default:
659             CY_ASSERT(false);
660             return (cyhal_tcpwm_output_t)0;
661     }
662 }
663 
cyhal_quaddec_enable_output(cyhal_quaddec_t * obj,cyhal_quaddec_output_t signal,cyhal_source_t * source)664 cy_rslt_t cyhal_quaddec_enable_output(cyhal_quaddec_t *obj, cyhal_quaddec_output_t signal, cyhal_source_t *source)
665 {
666     if (signal != CYHAL_QUADDEC_OUTPUT_COMPARE_MATCH)
667     {
668         return CYHAL_QUADDEC_RSLT_ERR_BAD_ARGUMENT;
669     }
670 
671     return _cyhal_tcpwm_enable_output(&(obj->tcpwm), _cyhal_quaddec_translate_output_signal(signal), source);
672 }
673 
cyhal_quaddec_disable_output(cyhal_quaddec_t * obj,cyhal_quaddec_output_t signal)674 cy_rslt_t cyhal_quaddec_disable_output(cyhal_quaddec_t *obj, cyhal_quaddec_output_t signal)
675 {
676     if (signal != CYHAL_QUADDEC_OUTPUT_COMPARE_MATCH)
677         return CYHAL_QUADDEC_RSLT_ERR_BAD_ARGUMENT;
678 
679     return _cyhal_tcpwm_disable_output(&(obj->tcpwm), _cyhal_quaddec_translate_output_signal(signal));
680 }
681 
682 #if defined(__cplusplus)
683 }
684 #endif
685 
686 #endif // CYHAL_DRIVER_AVAILABLE_QUADDEC
687