PWM Development Guide_Rev1.0

中文

1 Revision History

Version

Date

Author

Revision Content

Rev1.0

2023-09-12

WTY

Initial document creation

Rev1.1

2024-03-25

SXX

Changed document name

Rev1.2

2025-02-12

ZLC

Added liot_pwm_set_duty_cycle interface

Rev1.3

2026-03-18

ZLC

Added APWM interface

Rev1.4

2026-04-21

ZXQ

Modified according to review

2 Introduction

2.1 Document Introduction

This document introduces the LTE-EC71X PWM interface APIs. The API interfaces are declared in the file LSDK/components/kernel/lierda_api/liot_pwm/liot_pwm.h.

The LTE-EC71X series modules support 6 PWM outputs, each supporting selection between 26MHz high-speed clock and 40KHz low-speed clock source, with 1~256 clock division.

2.2 PWM Principle Introduction

Pulse Width Modulation (PWM) is a technique that controls average output voltage by adjusting the duty cycle of pulse signals. Duty cycle refers to the ratio of high-level time to the entire period. By changing the duty cycle, the average power of loads (such as motors, LEDs, etc.) connected to PWM output can be controlled.

In the LTE-EC71X module, the PWM controller generates precise PWM waveforms through programmable clock dividers, period registers, and duty cycle registers:

  1. Clock source selection: Can choose 26MHz high-speed clock or 40KHz low-speed clock

  2. Division coefficient: Register value is 0~255 division (actually 1~256), used to adjust base clock frequency

  3. Period register: Determines the total period time of PWM waveform, i.e., PWM wave frequency

  4. Duty cycle register: Determines the duration of low level within the period

Output frequency calculation formula: f = clock source frequency / (division coefficient + 1) / (period value + 1) Duty cycle calculation formula: Duty cycle = (period value + 1 - duty cycle value) / (period value + 1)

2.3 PWM Drive Capability

In this module, drive capability settings vary with different chip platforms and have different default settings. Dynamic setting is currently not supported.

3 API Function Overview

Function

Description

liot_pwm_open()

Open PWM function

liot_pwm_close()

Close PWM function

liot_pwm_enable()

Enable PWM and configure PWM pulse period and duty cycle

liot_pwm_disable()

Pause PWM function

liot_pwm_set_duty_cycle()

Set PWM duty cycle

4 Type Descriptions

4.1 liot_pwm_errcode_e

PWM error codes indicate whether a function executed successfully. If execution fails, the error code indicates the failure reason. Enumeration information is defined as follows:

  • Declaration

typedef enum{    
    LIOT_PWM_SUCCESS = LIOT_SUCCESS,    
    LIOT_PWM_EXECUTE_ERR = 1 | LIOT_PWM_ERRCODE_BASE,    
    LIOT_PWM_INVALID_PARAM_ERR,    
    LIOT_PWM_FUNC_SET_ERR,    
    LIOT_PWM_ACQUIRE_ERR,    
    LIOT_PWM_START_ERR,    
    LIOT_PWM_STOP_ERR,    
    LIOT_PWM_REPEAT_OPEN_ERR,    
    LIOT_PWM_REPEAT_CLOSE_ERR,
} liot_pwm_errcode_e;
  • Parameters

  • LIOT_PWM_SUCCESS: Function executed successfully

  • LIOT_PWM_EXECUTE_ERR: Function execution failed

  • LIOT_PWM_INVALID_PARAM_ERR: Parameter error

  • LIOT_PWM_FUNC_SET_ERR: PWM function setting failed

  • LIOT_PWM_ACQUIRE_ERR: PWM information acquisition failed

  • LIOT_PWM_START_ERR: PWM function enable failed

  • LIOT_PWM_STOP_ERR: PWM function stop failed

  • LIOT_PWM_REPEAT_OPEN_ERR: PWM repeated open error

  • LIOT_PWM_REPEAT_CLOSE_ERR: PWM repeated close error

4.2 liot_pwm_sel_e

PWM channel type enumeration information is defined as follows:

  1. Declaration

typedef enum{    
    LIOT_PWM_0,    
    LIOT_PWM_1,    
    LIOT_PWM_2,    
    LIOT_PWM_3,    
    LIOT_PWM_4,    
    LIOT_PWM_5,    
    LIOT_PWM_MAX,
} liot_pwm_sel_e;
  1. Parameters

  • LIOT_PWM_0: PWM channel 0

  • LIOT_PWM_1: PWM channel 1

  • LIOT_PWM_2: PWM channel 2

  • LIOT_PWM_3: PWM channel 3

  • LIOT_PWM_4: PWM channel 4

  • LIOT_PWM_5: PWM channel 5

  1. Description

For PWM channel corresponding pin numbers, refer to Lierda NT26F OpenCPU Module IO Multiplexing Table

4.3 liot_pwm_clk_e

PWM clock source, enumeration information is defined as follows:

  1. Declaration

typedef enum{    
    LIOT_CLK_RC26M,    
    LIOT_CLK_RTC_40K,
} liot_pwm_clk_e;
  1. Parameters

  • LIOT_CLK_RC26M: RC oscillator clock source, frequency: 26 MHz

  • LIOT_CLK_RTC_40K: RTC clock source, frequency: 40 KHz

4.4 liot_pwm_cfg_s

PWM parameter configuration structure information is defined as follows:

  1. Declaration

typedef struct{    
    liot_pwm_clk_e clk_sel;    
    uint16_t prescaler;    
    uint16_t period;    
    uint16_t duty;
} liot_pwm_cfg_s;
  1. Parameters

  • clk_sel: PWM clock source, see 4.3 liot_pwm_clk_e for details

  • prescaler: Division coefficient, range 0~255

  • period: Auto-load counter value

  • duty: Count value corresponding to high level

  1. Description

Output frequency f = clock source frequency / (prescaler + 1) / (period + 1).

Duty cycle = (period + 1 - duty) / (period + 1).

5 API Function Details

5.1 liot_pwm_open

This function is used to open PWM function.

  1. Declaration

liot_pwm_errcode_e liot_pwm_open(liot_pwm_sel_e pwm_sel);
  1. Parameters

pwm_sel: [In] PWM channel. See 4.2 liot_pwm_sel_e for details.

  1. Return Value

liot_pwm_errcode_e: Execution result code, see section 4.1 for description.

5.2 liot_pwm_close

This function is used to close PWM function.

  1. Declaration

liot_pwm_errcode_e liot_pwm_close(liot_pwm_sel_e pwm_sel);
  1. Parameters

pwm_sel: [In] PWM channel. See 4.2 liot_pwm_sel_e for details.

  1. Return Value

liot_pwm_errcode_e: Execution result code, see section 4.1 for description.

5.3 liot_pwm_enable

This function is used to enable PWM function, and will configure the clock source, prescaler coefficient, pulse period, and duty cycle in liot_pwm_cfg_s.

  1. Declaration

liot_pwm_errcode_e liot_pwm_enable(liot_pwm_sel_e pwm_sel, liot_pwm_cfg_s *pwm_cfg);
  1. Parameters

pwm_sel: [In] PWM channel. See 4.2 liot_pwm_sel_e for details.

pwm_cfg: [In] Parameters to be configured when enabling PWM. See 4.4 liot_pwm_cfg_s for details.

  1. Return Value

liot_pwm_errcode_e: Execution result code, see section 4.1 for description.

5.4 liot_pwm_disable

This function is used to pause PWM function.

  1. Declaration

liot_pwm_errcode_e liot_pwm_disable(liot_pwm_sel_e pwm_sel);
  1. Parameters

pwm_sel: [In] PWM channel. See 4.2 liot_pwm_sel_e for details.

  1. Return Value

liot_pwm_errcode_e: Execution result code, see section 4.1 for description.

5.5 liot_pwm_set_duty_cycle

This function is used to set PWM duty cycle without changing PWM wave frequency.

  1. Declaration

liot_pwm_errcode_e liot_pwm_set_duty_cycle(liot_pwm_sel_e pwm_sel, uint32_t duty_cycle);
  1. Parameters

pwm_sel: [In] PWM channel. See 4.2 liot_pwm_sel_e for details.

duty_cycle: [In] Counter comparison value.

  1. Return Value

liot_pwm_errcode_e: Execution result code, see section 4.1 for description.

6 Code Example

Refer to the LSDK/examples/demo/src/demo_pwm.c file for sample code.

/**
 * @file liot_pwm_demo.c
 * @brief LIoT PWM (Pulse Width Modulation) Demo Application
 * @date 2025-08-26
 * @version 1.0
 * @copyright Copyright (c) 2025 Lierda Technology Co., Ltd.
 */
 /**
 * This demo application demonstrates the usage of PWM (Pulse Width Modulation) peripherals
 * on EC7xx series chips. It initializes multiple PWM channels with different configurations
 * and demonstrates dynamic duty cycle adjustment on one channel.
 */

#include <stdio.h>
#include <string.h>

#include "lierda_app_main.h"
#include "liot_gpio2.h"
#include "liot_os.h"
#include "liot_pwm.h"

#define DEMO_PWM_1_PIN      (20)   /**< PWM1 channel GPIO pin for EC718 */
#define DEMO_PWM_1_FUCN     (5)    /**< PWM1 channel function selector for EC718 */
#define DEMO_PWM_2_PIN      (106)  /**< PWM2 channel GPIO pin for EC718 */
#define DEMO_PWM_2_FUCN     (5)    /**< PWM2 channel function selector for EC718 */
#define DEMO_PWM_3_PIN      (25)   /**< PWM3 channel GPIO pin for EC718 */
#define DEMO_PWM_3_FUCN     (5)    /**< PWM3 channel function selector for EC718 */

/** @} */ // end of PWM_DEMO_HARDWARE_CONFIG

/** @defgroup PWM_DEMO_PARAMETERS PWM Demo Parameters
 *  @brief Configuration parameters for PWM demonstration
 *  @{
 */
#define PWM_UPDATE_INTERVAL_MS 500   /**< Duty cycle update interval in milliseconds */
#define PWM_MAX_DUTY_CYCLE     2000  /**< Maximum duty cycle value for PWM2 */
#define PWM_DUTY_STEP          200   /**< Duty cycle increment step */
/** @} */ // end of PWM_DEMO_PARAMETERS

/**
 * @brief PWM demonstration thread
 * 
 * This thread initializes three PWM channels with different configurations:
 * - PWM1: 10kHz frequency
 * - PWM2: 500Hz frequency with dynamic duty cycle adjustment
 * - PWM3: 100Hz frequency
 * 
 * The thread continuously adjusts the duty cycle of PWM2 from 0 to max value
 * and back to 0 in steps, demonstrating dynamic PWM control.
 * 
 * @param[in] argv Thread argument (not used in this demo)
 */
void liot_pwm_demo_thread(void *argv)
{
    liot_pwm_cfg_s ptr;               /**< PWM configuration structure */
    uint16_t duty_cycle = 0;          /**< Current duty cycle value for PWM2 */

    // Power management: Enable AON (Always-On) domain power
    Liot_AonPowerCtl(true);

    // Configure GPIO pins for PWM functionality  
    // Must set pin func to pwm before use
    Liot_SetPinFunc(DEMO_PWM_1_PIN, DEMO_PWM_1_FUCN);
    Liot_SetPinFunc(DEMO_PWM_2_PIN, DEMO_PWM_2_FUCN);
    Liot_SetPinFunc(DEMO_PWM_3_PIN, DEMO_PWM_3_FUCN);

    /** @brief PWM1 configuration: 10kHz frequency
     *  Calculation: PWM frequency = (Clock frequency) / ((period + 1) * (prescaler + 1))
     *  With RC26M clock (26MHz), period=99, prescaler=25:
     *  Frequency = 26,000,000 / ((99 + 1) * (25 + 1)) = 26,000,000 / 2600 = 10,000Hz = 10kHz
     */
    ptr.clk_sel   = LIOT_CLK_RC26M;   /**< Select 26MHz RC oscillator as clock source */
    ptr.duty      = 10;               /**< Initial duty cycle (10/100 = 10%) */
    ptr.period    = 99;               /**< PWM period value */
    ptr.prescaler = 25;               /**< Clock prescaler value */
    liot_pwm_enable(LIOT_PWM_1, &ptr);/**< Apply configuration to PWM1 */
    liot_pwm_open(LIOT_PWM_1);        /**< Enable PWM1 output */

    /** @brief PWM2 configuration: 500Hz frequency
     *  With RC26M clock (26MHz), period=1999, prescaler=25:
     *  Frequency = 26,000,000 / ((1999 + 1) * (25 + 1)) = 26,000,000 / 52,000 = 500Hz
     */
    ptr.duty      = 500;              /**< Initial duty cycle (500/2000 = 25%) */
    ptr.period    = 1999;             /**< PWM period value */
    ptr.prescaler = 25;               /**< Clock prescaler value */
    liot_pwm_enable(LIOT_PWM_2, &ptr);/**< Apply configuration to PWM2 */
    liot_pwm_open(LIOT_PWM_2);        /**< Enable PWM2 output */

    /** @brief PWM3 configuration: 100Hz frequency
     *  With RC26M clock (26MHz), period=9999, prescaler=25:
     *  Frequency = 26,000,000 / ((9999 + 1) * (25 + 1)) = 26,000,000 / 260,000 = 100Hz
     */
    ptr.duty      = 2000;             /**< Initial duty cycle (2000/10000 = 20%) */
    ptr.period    = 9999;             /**< PWM period value */
    ptr.prescaler = 25;               /**< Clock prescaler value */
    liot_pwm_enable(LIOT_PWM_3, &ptr);/**< Apply configuration to PWM3 */
    liot_pwm_open(LIOT_PWM_3);        /**< Enable PWM3 output */

    // Initial delay to ensure stable PWM output
    liot_rtos_task_sleep_ms(1000);

    // Main loop: Dynamic duty cycle adjustment for PWM2
    while (1)
    {
        liot_trace("LIOT_PWM_2 duty cycle:%d/%d\r\n", duty_cycle, PWM_MAX_DUTY_CYCLE);
        liot_pwm_set_duty_cycle(LIOT_PWM_2, duty_cycle); /**< Update PWM2 duty cycle */
        liot_rtos_task_sleep_ms(PWM_UPDATE_INTERVAL_MS); /**< Wait for next update */
        duty_cycle += PWM_DUTY_STEP;                     /**< Increment duty cycle */

        // Reset duty cycle when exceeding maximum value
        if(duty_cycle > PWM_MAX_DUTY_CYCLE)
        {
            duty_cycle = 0;  /**< Reset to minimum duty cycle */
        }
    }
    liot_pwm_close(LIOT_PWM_1);
    liot_pwm_close(LIOT_PWM_2);
    liot_pwm_close(LIOT_PWM_3);
    
    liot_pwm_disable(LIOT_PWM_1);
    liot_pwm_disable(LIOT_PWM_2);
    liot_pwm_disable(LIOT_PWM_3);

    liot_rtos_task_delete(NULL); // kill itself
}

Execution Result:

_images/pwm-guide/image_1.png

After running the DEMO program, the module outputs three PWM signals. The figure above shows signals captured by a logic analyzer, with frequencies of 10KHz, 500Hz, and 100Hz respectively. The duty cycle of PWM2’s 500Hz PWM wave changes continuously.