A PWM (Pulse-Width Modulator) is a component used for controlling power to inertial electrical devices. It generates a periodic waveform with positive width that can be controlled, which allows the waveform’s average value to be modified.
The average value of voltage fed to the load is controlled by rapidly turning the switch between the supply and the load on and off. The longer the switch is on compared to the off, the higher the power supplied to the load will be.
The ConnectCore platforms have several PWM interfaces. You can find more information in Hardware reference manuals and Pulse-width Modulation (PWM).
Digi adds an API to Linux to manage these PWM interfaces. To use this API, include the following header file:
#include <libdigiapix/pwm.h>
You can configure the interfaces and set the working frequency and duty cycle.
Request a PWM
You can request a PWM with one of the following functions:
Function | Description |
---|---|
|
Requests a PWM by its chip number and channel number It returns a pointer to |
|
Requests a PWM by its alias name.
The PWM alias mapping must be defined in the It returns a pointer to |
The requested PWM must be freed once it is no longer needed. See Free a PWM. |
Both functions may fail and return NULL
for the following reasons:
-
The provided chip and channel numbers, or the associated chip and channel numbers to the given alias cannot be exported.
-
The API encountered problems allocating memory to initialize the PWM. Your system may have run out of resources.
-
The request mode is configured to REQUEST_WEAK and the PWM is already exported.
When requesting a PWM, you can select the request mode from the following request_mode_t values:
-
REQUEST_SHARED: If the PWM is already exported, it will not be unexported on free. If it is not exported, it will be unexported on free.
-
REQUEST_GREEDY: The PWM will be always unexported on free.
-
REQUEST_WEAK: If the PWM is already exported, the request will fail. It will always be unexported on free.
Once you have requested the PWM, you can enable it or verify whether it is enabled or not using the following functions:
Function | Description |
---|---|
|
|
|
|
[...]
/* Request a PWM using its alias */
pwm_t pwm1 = ldx_pwm_request_by_alias("DEFAULT_PWM", REQUEST_SHARED);
/* Request a PWM located in the chip = 1 channel = 0 */
pwm_t pwm2 = ldx_pwm_request(1, 0, REQUEST_SHARED);
/* Enable pwm1 */
ldx_pwm_enable(pwm1, PWM_ENABLED);
/* Disable pwm2 */
ldx_pwm_enable(pwm2, PWM_ENABLED);
printf("The PWM 1 (chip %d, channel %d) is %s\n",
pwm1->pwm_chip, pwm1->pwm_channel,
ldx_pwm_is_enabled(pwm1) == PWM_ENABLED ? "enabled" : "disabled");
printf("The PWM 2 (chip %d, channel %d) is %s\n",
pwm2->pwm_chip, pwm2->pwm_channel,
ldx_pwm_is_enabled(pwm2) == PWM_ENABLED ? "enabled" : "disabled");
[...]
Establish PWM aliases
To help you identify the PWMs of your design, you can assign aliases to your PWM Linux IDs.
Map the assigned chip number, channel, and name in the /etc/libdigiapix.conf
file:
-
Add a section called [PWM], if one doesn’t already exist.
-
Below the section name, add the list of mapped PWMs following the format:
<alias> = <pwm_chip>,<pwm_channel>
Where:
-
<alias> is the human-readable name for the PWM.
-
<pwm_chip> is the PWM chip.
-
<pwm_channel> is the PWM channel used by that PWM chip.
-
[PWM]
# PWM1 on Expansion connector (pin 27).
DEFAULT_PWM = 0,0
For example, using the configuration above, you can request a PWM with the API using DEFAULT_PWM alias. See Request a PWM.
You can get the chip and the channel associated with an alias using these functions:
int ldx_pwm_get_chip(const char * const pwm_alias)
int ldx_pwm_get_channel(const char * const pwm_alias)
For information on including libdigiapix.conf in your Digi Embedded Yocto images, see Define interface aliases.
|
Free a PWM
You must free a requested PWM when it is no longer required.
To do so, use the ldx_pwm_free()
function.
int ldx_pwm_free(pwm_t *pwm)
[...]
pwm_t *pwm = ...;
[...]
/* Free PWM once it is not required anymore */
ldx_pwm_free(pwm);
[...]
Configure the PWM frequency
You can read and set the PWM frequency value:
Function | Description |
---|---|
|
|
|
|
The PWM frequency range is:
-
Minimum value: 1 Hz.
-
Maximum value: 1000000000 Hz (1 GHz).
If you try to set a frequency value less than the current duty cycle for that PWM channel, a PWM_CONFIG_ERROR_INVALID
error is returned.
The minimum PWM period that can be get/set (1 ns) can create a mismatch between the configured and the desired frequencies, particularly with values greater than 1 MHz. |
For a more accurate configuration, see Configure the PWM period.
Calculate the final configured frequency:
ns = [1000000000 / f] F = [1000000000 / ns] [x] means the nearest integer to x, for example [1.6] = 2, [3.4] = 3
Desired frequency (MHz) | Configured frequency (MHz) |
---|---|
1000 |
1000 |
750 |
1000 |
650 |
500 |
500 |
500 |
350 |
333.33 |
250 |
250 |
100 |
100 |
75 |
76.92 |
65 |
66.67 |
50 |
50 |
35 |
34.48 |
15 |
14.93 |
7 |
6.99 |
4.5 |
4.504 |
1.5 |
1.499 |
[...]
pwm_t pwm = ...;
/* Configure a frequency of 1000 Hz */
ldx_pwm_set_freq(pwm, 1000);
printf("The frequency for PWM %d.%d is %l Hz\n", pwm->pwm_chip, pwm->pwm_channel, ldx_pwm_get_freq(pwm));
[...]
Configure the PWM period
You can read and set the PWM period value:
Function | Description |
---|---|
|
|
|
|
The PWM period range is:
-
Minimum value: 1 ns.
-
Maximum value: 2147483647 ns.
If you try to set a period value less than the current duty cycle for that PWM channel, a PWM_CONFIG_ERROR_INVALID
error is returned.
[...]
pwm_t pwm = ...;
/* Configure a period of 1000000 ns, 1 ms */
ldx_pwm_set_period(pwm, 1000000);
printf("The period for PWM %d.%d is %d ns\n", pwm->pwm_chip, pwm->pwm_channel, ldx_pwm_get_period(pwm));
[...]
Manage the duty cycle
You can manage and work with the duty cycle using one of two methods: nanoseconds or percentage.
Work with percentage
You can read and set the PWM duty cycle in percentage with the following functions:
Function | Description |
---|---|
|
|
|
|
[...]
pwm_t pwm = ...;
/* Configure a period of 1000000 ns (1 ms) */
ldx_pwm_set_period(pwm, 1000000);
/* Configure a duty cycle of 50% (0.5 ms) */
ldx_pwm_set_duty_cycle_percentage(pwm, 50);
printf("The duty cycle for PWM %d.%d is %d%%\n", pwm->pwm_chip, pwm->pwm_channel, ldx_pwm_get_duty_cycle_percentage(pwm));
[...]
Work with nanoseconds
You can read and set the PWM duty cycle in nanoseconds with the following functions:
Function | Description |
---|---|
|
|
|
Sets the duty cycle in nanoseconds for the selected PWM channel. It returns a
|
The PWM duty cycle range is:
-
Minimum value: 1 ns.
-
Maximum value: less than the current period.
If you try to set a duty cycle value greater than the current period for that PWM channel, a PWM_CONFIG_ERROR_INVALID
error is returned.
[...]
pwm_t pwm = ...;
/* Configure a period of 1000000 ns (1 ms) */
ldx_pwm_set_period(pwm, 1000000);
/* Configure a duty cycle of 500000 ns (0.5 ms) */
ldx_pwm_set_duty_cycle(pwm, 500000);
printf("The duty cycle for PWM %d.%d is %d ns\n", pwm->pwm_chip, pwm->pwm_channel, ldx_pwm_get_duty_cycle(pwm));
[...]
Change the polarity
You can read and set the PWM polarity with the following functions:
Function | Description |
---|---|
|
|
|
|
You can only change the polarity if the PWM is not enabled; otherwise, EXIT_FAILURE is returned.
|
Not all platforms support changing the PWM polarity. See Pulse-width Modulation (PWM) for more information.
[...]
pwm_t pwm = ...;
ldx_pwm_set_polarity(pwm, PWM_INVERSED);
printf("The polarity for PWM %d.%d is %d\n", pwm->pwm_chip, pwm->pwm_channel, ldx_pwm_get_polarity(pwm));
[...]
PWM example
In this example, one PWM line of the board is enabled using a frequency of 1000Hz. Then, the duty cycle is progressively modified in a loop from 10% to 90% and vice-versa.
You can import the example in Eclipse using the Digi Embedded Yocto plugin. For more information, see Create a new DEY sample project. This example is included in Digi Embedded Yocto. Go to GitHub to see the application source code.