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