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

pwm_t *ldx_pwm_request(unsigned int pwm_chip, unsigned int channel, request_mode_t request_mode)

Requests a PWM by its chip number and channel number

It returns a pointer to pwm_t on success, NULL on error.

pwm_t *ldx_pwm_request_by_alias(char const * const pwm_alias, request_mode_t request_mode)

Requests a PWM by its alias name. The PWM alias mapping must be defined in the /etc/libdigiapix.conf file under the [PWM] section. See Establish PWM aliases.

It returns a pointer to pwm_t on success, NULL on error.

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

pwm_enabled_t ldx_pwm_is_enabled(pwm_t *pwm)

  • Retrieves the enable status of the PWM:

    • PWM_ENABLED if the PWM is enabled

    • PWM_DISABLED if the PWM is disabled

    • PWM_ENABLED_ERROR if there is an error

int ldx_pwm_enable(pwm_t *pwm, pwm_enabled_t enabled)

  • Changes the enable status of the PWM:

    • Enable the PWM interface: PWM_ENABLED

    • Disable the PWM interface: PWM_DISABLED

  • It returns EXIT_SUCCESS on success, EXIT_FAILURE otherwise.

Request a PWM
[...]

/* 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:

  1. Add a section called [PWM], if one doesn’t already exist.

  2. 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.

Example PWM section
[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)
Free a requested 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

long ldx_pwm_get_freq(pwm_t *pwm)

  • Retrieves the value of the PWM channel frequency in Hz.

  • If there is an error, the function returns -1.

pwm_config_error_t ldx_pwm_set_freq(pwm_t *pwm, unsigned long freq_hz)

  • Sets the frequency of the PWM channel in Hz.

  • It returns a pwm_config_error_t code:

    • PWM_CONFIG_ERROR_INVALID if the frequency to set is invalid or out of range

    • PWM_CONFIG_ERROR if there is an error configuring the frequency

    • PWM_CONFIG_ERROR_NONE if the operation succeeds

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

Get and set the PWM frequency
[...]

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

int ldx_pwm_get_period(pwm_t *pwm)

  • Retrieves the value of the PWM channel period in nanoseconds.

  • If there is some error, the function returns -1.

pwm_config_error_t ldx_pwm_set_period(pwm_t *pwm, unsigned int period)

  • Sets the period of the PWM channel in nanoseconds.

  • It returns a pwm_config_error_t code:

    • PWM_CONFIG_ERROR_INVALID if the period to set is invalid or out of range

    • PWM_CONFIG_ERROR if there is an error configuring the period

    • PWM_CONFIG_ERROR_NONE if the operation succeeds

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.

Get and set the PWM period
[...]

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

int ldx_pwm_get_duty_cycle_percentage(pwm_t *pwm)

  • Retrieves the duty cycle percentage of a PWM signal.

  • If there is some error, the function returns -1.

pwm_config_error_t ldx_pwm_set_duty_cycle_percentage(pwm_t *pwm, unsigned int percentage)

  • Sets the duty cycle percentage for the selected PWM.

  • It returns a pwm_config_error_t code:

    • PWM_CONFIG_ERROR_INVALID if the duty cycle percentage to set is invalid or out of range

    • PWM_CONFIG_ERROR if there is an error setting the duty cycle

    • PWM_CONFIG_ERROR_NONE if the operation successes

Get and set the PWM duty cycle percentage
[...]

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

int ldx_pwm_get_duty_cycle(pwm_t *pwm)

  • Retrieves the value of the PWM channel duty cycle in nanoseconds.

  • If there is an error, the function returns -1.

pwm_config_error_t ldx_pwm_set_duty_cycle(pwm_t *pwm, unsigned int duty)

Sets the duty cycle in nanoseconds for the selected PWM channel.

It returns a pwm_config_error_t code:

  • PWM_CONFIG_ERROR_INVALID if the duty cycle to set is invalid or out of range

  • PWM_CONFIG_ERROR if there is an error setting the duty cycle

  • PWM_CONFIG_ERROR_NONE if the operation successes

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.

Get and set the PWM duty cycle
[...]

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

pwm_polarity_t ldx_pwm_get_polarity(pwm_t *pwm)

  • Retrieves the value of the PWM polarity.

    • PWM_NORMAL for normal polarity

    • PWM_INVERSED for inverted polarity

    • PWM_POLARITY_ERROR if there is an error

int ldx_pwm_set_polarity(pwm_t *pwm, pwm_polarity_t polarity)

  • Changes the polarity of the selected PWM channel:

    • Normal polarity: PWM_NORMAL

    • Inverted polarity: PWM_INVERSED

  • It returns EXIT_SUCCESS on success, EXIT_FAILURE otherwise.

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.

Get and set the PWM polarity
[...]

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.