1 /***************************************************************************//**
2 * \file cyhal_pwm.h
3 *
4 * \brief
5 * Provides a high level interface for interacting with the Infineon PWM.
6 * This interface abstracts out the chip specific details. If any chip specific
7 * functionality is necessary, or performance is critical the low level functions
8 * can be used directly.
9 *
10 ********************************************************************************
11 * \copyright
12 * Copyright 2018-2022 Cypress Semiconductor Corporation (an Infineon company) or
13 * an affiliate of Cypress Semiconductor Corporation
14 *
15 * SPDX-License-Identifier: Apache-2.0
16 *
17 * Licensed under the Apache License, Version 2.0 (the "License");
18 * you may not use this file except in compliance with the License.
19 * You may obtain a copy of the License at
20 *
21 *     http://www.apache.org/licenses/LICENSE-2.0
22 *
23 * Unless required by applicable law or agreed to in writing, software
24 * distributed under the License is distributed on an "AS IS" BASIS,
25 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
26 * See the License for the specific language governing permissions and
27 * limitations under the License.
28 *******************************************************************************/
29 
30 /**
31 * \addtogroup group_hal_pwm PWM (Pulse Width Modulator)
32 * \ingroup group_hal
33 * \{
34  * High level interface for interacting with the pulse width modulator (PWM) hardware resource
35  *
36  * The PWM driver can be used to generate periodic digital waveforms with configurable frequency and duty cycle.
37  * The driver allows assigning the PWM output and an optional inverted output to supplied pins.
38  * The driver supports interrupt generation on PWM terminal count and compare events.
39  *
40  * \section section_pwm_features Features
41  * * Configurable pin assignment for the PWM output
42  * * Optional complementary (inverted) PWM output to a second pin
43  * * Configurable dead time between normal and inverted PWM outputs
44  * * Configurable alignment: left, right or center
45  * * Continuous or One-shot operation
46  * * Option to instantiate and use a new clock or use pre-allocated clock for clock input
47  * * Configurable interrupt and callback assignment on PWM events: terminal count, compare match or combination of both
48  *
49  * \section section_pwm_quickstart Quick Start
50  *
51  * See \ref subsection_pwm_snippet_1 for a code snippet that generates a signal with the specified frequency and duty cycle on the specified pin.
52  *
53  * \section section_pwm_snippets Code snippets
54  *
55  * \subsection subsection_pwm_snippet_1 Snippet 1: Simple PWM initialization and output to pin
56  * The following snippet initializes a PWM resource and assigns the output to the supplied <b>pin</b> using \ref cyhal_pwm_init. <br>
57  * The clock parameter <b>clk</b> is optional and need not be provided (NULL), to generate and use an available clock resource with a default frequency. <br>
58  * The clock frequency and the duty cycle is set using \ref cyhal_pwm_set_duty_cycle. <br>
59  * \ref cyhal_pwm_start starts the PWM output on the pin.
60  *
61  * \snippet hal_pwm.c snippet_cyhal_pwm_simple_init
62  *
63  *
64  * \subsection subsection_pwm_snippet_2 Snippet 2: Starting and stopping the PWM output
65  * \ref cyhal_pwm_start and \ref cyhal_pwm_stop functions can be used after PWM initialization to start and stop the PWM output.
66  *
67  * \snippet hal_pwm.c snippet_cyhal_pwm_start_stop
68  *
69  *
70  * \subsection subsection_pwm_snippet_3 Snippet 3: Advanced PWM output to pin
71  * \ref cyhal_pwm_init_adv can be used to specify advanced PWM options like an additional inverted PWM output, pulse alignment
72  * (left, right, center) and run mode (one-shot or continuous). The following snippet initializes a left-aligned, continuous running PWM
73  * assigned to the supplied pin. The inverted output is assigned to a second pin (<b>compl_pin</b>).
74  *
75  * \snippet hal_pwm.c snippet_cyhal_pwm_adv_init
76  *
77  *
78  * \subsection subsection_pwm_snippet_4 Snippet 4: Interrupts on PWM events
79  * PWM events like hitting the terminal count or a compare event can be used to trigger a callback function. <br>
80  * \ref cyhal_pwm_enable_event() can be used to enable one or more events to trigger the callback function.
81  *
82  * \snippet hal_pwm.c snippet_cyhal_pwm_events
83  */
84 
85 #pragma once
86 
87 #include <stdint.h>
88 #include <stdbool.h>
89 
90 #include "cy_result.h"
91 #include "cyhal_hw_types.h"
92 
93 #if defined(__cplusplus)
94 extern "C" {
95 #endif
96 
97 /** \addtogroup group_hal_results_pwm PWM HAL Results
98  *  PWM specific return codes
99  *  \ingroup group_hal_results
100  *  \{ *//**
101  */
102 
103 /** Bad argument */
104 #define CYHAL_PWM_RSLT_BAD_ARGUMENT                     \
105     (CY_RSLT_CREATE_EX(CY_RSLT_TYPE_ERROR, CY_RSLT_MODULE_ABSTRACTION_HAL, CYHAL_RSLT_MODULE_PWM, 0))
106 /** Failed to initialize PWM clock */
107 #define CYHAL_PWM_RSLT_FAILED_CLOCK_INIT                \
108     (CY_RSLT_CREATE_EX(CY_RSLT_TYPE_ERROR, CY_RSLT_MODULE_ABSTRACTION_HAL, CYHAL_RSLT_MODULE_PWM, 1))
109 /** Failed to initialize PWM */
110 #define CYHAL_PWM_RSLT_FAILED_INIT                      \
111     (CY_RSLT_CREATE_EX(CY_RSLT_TYPE_ERROR, CY_RSLT_MODULE_ABSTRACTION_HAL, CYHAL_RSLT_MODULE_PWM, 2))
112 
113 /**
114  * \}
115  */
116 
117 /** Initialize the PWM out peripheral and configure the pin
118  * This is similar to the \ref cyhal_pwm_init_adv() but uses defaults for some of the
119  * more advanced setup options. See \ref subsection_pwm_snippet_1.
120  *
121  * @param[out] obj  Pointer to a PWM object. The caller must allocate the memory
122  *  for this object but the init function will initialize its contents.
123  * @param[in]  pin  The PWM pin to initialize. This pin is required, it cannot be \ref NC (No Connect).
124  * @param[in]  clk  An optional, pre-allocated clock to use, if NULL a new clock will be allocated
125  * @return The status of the init request.
126  */
127 #define cyhal_pwm_init(obj, pin, clk) (cyhal_pwm_init_adv(obj, pin, NC, CYHAL_PWM_LEFT_ALIGN, true, 0u, false, clk))
128 
129 /** PWM interrupt triggers */
130 typedef enum {
131     CYHAL_PWM_IRQ_NONE            =  0,             /**< No interrupts */
132     CYHAL_PWM_IRQ_TERMINAL_COUNT  =  1 << 0,        /**< Interrupt on terminal count match event */
133     CYHAL_PWM_IRQ_COMPARE         =  1 << 1,        /**< Interrupt on compare match event */
134     CYHAL_PWM_IRQ_ALL             = (1 << 2) - 1,   /**< Interrupt on any events */
135 
136 /** \cond INTERNAL */
137     CYHAL_PWM_IRQ_CAPTURE_COMPARE /* __attribute__ ((deprecated)) */ = CYHAL_PWM_IRQ_COMPARE,
138 /** \endcond */
139 } cyhal_pwm_event_t;
140 
141 /** PWM alignment */
142 typedef enum {
143     CYHAL_PWM_LEFT_ALIGN       = 0, /**< PWM is left aligned (signal starts high and goes low after compare match) */
144     CYHAL_PWM_RIGHT_ALIGN      = 1, /**< PWM is right aligned (signal starts low and goes high after compare match) */
145     CYHAL_PWM_CENTER_ALIGN     = 2, /**< PWM is centered aligned (signal starts and ends low with a center aligned pulse) */
146 } cyhal_pwm_alignment_t;
147 
148 /** PWM input signal */
149 typedef enum
150 {
151     CYHAL_PWM_INPUT_START,    //!< Start signal
152     CYHAL_PWM_INPUT_STOP,     //!< Stop signal
153     CYHAL_PWM_INPUT_RELOAD,   //!< Reload signal
154     CYHAL_PWM_INPUT_COUNT,    //!< Count signal
155     CYHAL_PWM_INPUT_CAPTURE,  //!< Capture/swap signal
156 } cyhal_pwm_input_t;
157 
158 /** PWM output signal */
159 typedef enum
160 {
161     CYHAL_PWM_OUTPUT_OVERFLOW,      //!< Overflow signal
162     CYHAL_PWM_OUTPUT_UNDERFLOW,     //!< Underflow signal
163     CYHAL_PWM_OUTPUT_COMPARE_MATCH, //!< Compare Match signal
164     CYHAL_PWM_OUTPUT_LINE_OUT,      //!< PWM line out signal
165 } cyhal_pwm_output_t;
166 
167 /** Handler for PWM interrupts */
168 typedef void(*cyhal_pwm_event_callback_t)(void *callback_arg, cyhal_pwm_event_t event);
169 
170 /** Initialize the PWM out peripheral and configure the pin.
171  * This is similar to the \ref cyhal_pwm_init() but provides additional setup options. <br>
172  * See \ref subsection_pwm_snippet_3.
173  *
174  * @param[out] obj              Pointer to a PWM object. The caller must allocate the memory
175  *                              for this object but the init function will initialize its contents.
176  * @param[in]  pin              The PWM pin to initialize. This pin is required, it cannot be \ref NC (No Connect).
177  * @param[in]  compl_pin        An optional, additional inverted output pin. <br>
178  *                              If supplied, this must be connected to the same or related PWM instance as <b>pin</b>, see
179  *                              \ref section_hal_impl_pwm_compl_pins for details.<br>
180  *                              If this output is not needed, use \ref NC (No Connect).
181  * @param[in]  pwm_alignment    PWM alignment: left, right, or center.
182  * @param[in]  continuous       PWM run type: continuous (true) or one shot (false).
183  * @param[in]  dead_time_us     The number of micro-seconds for dead time. This is
184  * only meaningful if both <b>pin</b> and <b>compl_pin</b> are provided.
185  * @param[in]  invert           An option for the user to invert the PWM output
186  * @param[in]  clk              An optional, pre-allocated clock to use, if NULL a
187  * new clock will be allocated.
188  * @return The status of the init request
189  *
190  * @note In some cases, it is possible to use a pin designated for non-inverting output as an inverting output and vice versa. Whether this is possible is dependent on the HAL implementation and operating mode. See the implementation specific documentation for details.
191  */
192 cy_rslt_t cyhal_pwm_init_adv(cyhal_pwm_t *obj, cyhal_gpio_t pin, cyhal_gpio_t compl_pin, cyhal_pwm_alignment_t pwm_alignment, bool continuous, uint32_t dead_time_us, bool invert, const cyhal_clock_t *clk);
193 
194 /** Initialize the PWM out peripheral using a configurator generated configuration struct
195   *
196  * @param[out] obj              Pointer to a PWM object. The caller must allocate the memory
197  *                              for this object but the init function will initialize its contents.
198  * @param[in] cfg               Configuration structure generated by a configurator.
199  * @return The status of the init request
200  */
201  cy_rslt_t cyhal_pwm_init_cfg(cyhal_pwm_t *obj, const cyhal_pwm_configurator_t *cfg);
202 
203 /** Deinitialize the PWM object
204  *
205  * @param[in,out] obj The PWM object
206  */
207 void cyhal_pwm_free(cyhal_pwm_t *obj);
208 
209 /** Set the number of microseconds for the PWM period & pulse width
210  *
211  * @param[in] obj            The PWM object
212  * @param[in] period_us      The period in microseconds
213  * @param[in] pulse_width_us The pulse width in microseconds
214  * @return The status of the period request
215  */
216 cy_rslt_t cyhal_pwm_set_period(cyhal_pwm_t *obj, uint32_t period_us, uint32_t pulse_width_us);
217 
218 /** Set the PWM duty cycle and frequency
219  *
220  * @param[in] obj             The PWM object
221  * @param[in] duty_cycle      The percentage of time the output is high (0 - 100%)
222  * @param[in] frequencyhal_hz The frequency of the PWM in Hz
223  * @return                    The status of the duty cycle request
224  */
225 cy_rslt_t cyhal_pwm_set_duty_cycle(cyhal_pwm_t *obj, float duty_cycle, uint32_t frequencyhal_hz);
226 
227 /** Starts the PWM generation and outputs on <b>pin</b> and <b>compl_pin</b>.
228  *
229  * @param[in] obj           The PWM object
230  * @return                  The status of the start request
231  */
232 cy_rslt_t cyhal_pwm_start(cyhal_pwm_t *obj);
233 
234 /** Stops the PWM generation and outputs on <b>pin</b> and <b>compl_pin</b>.
235  *
236  * @param[in] obj          The PWM object
237  * @return                 The status of the stop request
238  */
239 cy_rslt_t cyhal_pwm_stop(cyhal_pwm_t *obj);
240 
241 /** Register a PWM interrupt handler
242  *
243  * This function will be called when one of the events enabled by \ref cyhal_pwm_enable_event occurs.
244  *
245  * @param[in] obj          The PWM object
246  * @param[in] callback     The callback handler which will be invoked when the event occurs
247  * @param[in] callback_arg Generic argument that will be provided to the callback when called
248  */
249 void cyhal_pwm_register_callback(cyhal_pwm_t *obj, cyhal_pwm_event_callback_t callback, void *callback_arg);
250 
251 /** Configure PWM event enablement.
252  *
253  * When an enabled event occurs, the function specified by \ref cyhal_pwm_register_callback will be called.
254  *
255  * @param[in] obj            The PWM object
256  * @param[in] event          The PWM event type
257  * @param[in] intr_priority  The priority for NVIC interrupt events
258  * @param[in] enable         True to turn on events, False to turn off
259  */
260 void cyhal_pwm_enable_event(cyhal_pwm_t *obj, cyhal_pwm_event_t event, uint8_t intr_priority, bool enable);
261 
262 /** Connects a source signal and configures and enables a PWM event to be
263  * triggered from that signal. These PWM events can be configured
264  * independently and connect to the same or different source signals.
265  * @note For "edge" signals, this function will default to rising edge. To control the edge type,
266  * use @ref cyhal_pwm_connect_digital2
267  *
268  * @param[in] obj      PWM obj
269  * @param[in] source   Source signal obtained from another driver's cyhal_<PERIPH>_enable_output
270  * @param[in] signal   The PWM input signal
271  * @return The status of the connection
272  * */
273 cy_rslt_t cyhal_pwm_connect_digital(cyhal_pwm_t *obj, cyhal_source_t source, cyhal_pwm_input_t signal);
274 
275 /** Connects a source signal and configures and enables a PWM event to be
276  * triggered from that signal with a configurable edge type. These PWM events
277  * can be configured independently and connect to the same or different source signals.
278  *
279  * @param[in] obj       PWM obj
280  * @param[in] source    Source signal obtained from another driver's cyhal_<PERIPH>_enable_output
281  * @param[in] signal    The PWM input signal
282  * @param[in] edge_type The edge type that should trigger the event. This must be consistent with the
283  *                      edge type of `source`. If `source` produces a "level" signal, the only valid
284  *                      value is @ref CYHAL_EDGE_TYPE_LEVEL. If `source` produces an "edge" signal, then
285  *                      @ref CYHAL_EDGE_TYPE_LEVEL is not a valid value.
286  * @return The status of the connection
287  * */
288 cy_rslt_t cyhal_pwm_connect_digital2(cyhal_pwm_t *obj, cyhal_source_t source, cyhal_pwm_input_t signal, cyhal_edge_type_t edge_type);
289 
290 /** Enables the specified output signal from a PWM that will be triggered
291  * when the corresponding event occurs. Multiple output signals can be
292  * configured simultaneously.
293  *
294  * @param[in]  obj      PWM obj
295  * @param[in]  signal   The PWM output signal
296  * @param[out] source   Pointer to user-allocated source signal object which
297  * will be initialized by enable_output. \p source should be passed to
298  * (dis)connect_digital functions to (dis)connect the associated endpoints.
299  * @return The status of the output enable
300  * */
301 cy_rslt_t cyhal_pwm_enable_output(cyhal_pwm_t *obj, cyhal_pwm_output_t signal, cyhal_source_t *source);
302 
303 /** Disconnects a source signal and disables the PWM event.
304  *
305  * @param[in] obj      PWM obj
306  * @param[in] source   Source signal from cyhal_<PERIPH>_enable_output to disable
307  * @param[in] signal   The PWM input signal
308  * @return The status of the disconnection
309  * */
310 cy_rslt_t cyhal_pwm_disconnect_digital(cyhal_pwm_t *obj, cyhal_source_t source, cyhal_pwm_input_t signal);
311 
312 /** Disables the specified output signal from a PWM.
313  *
314  * @param[in]  obj      PWM obj
315  * @param[in]  signal   The PWM output signal
316  * @return The status of the output disable
317  * */
318 cy_rslt_t cyhal_pwm_disable_output(cyhal_pwm_t *obj, cyhal_pwm_output_t signal);
319 
320 #if defined(__cplusplus)
321 }
322 #endif
323 
324 #ifdef CYHAL_PWM_IMPL_HEADER
325 #include CYHAL_PWM_IMPL_HEADER
326 #endif /* CYHAL_PWM_IMPL_HEADER */
327 
328 /** \} group_hal_pwm */
329