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:
Clock source selection: Can choose 26MHz high-speed clock or 40KHz low-speed clock
Division coefficient: Register value is 0~255 division (actually 1~256), used to adjust base clock frequency
Period register: Determines the total period time of PWM waveform, i.e., PWM wave frequency
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 |
|---|---|
|
Open PWM function |
|
Close PWM function |
|
Enable PWM and configure PWM pulse period and duty cycle |
|
Pause PWM function |
|
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:
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;
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
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:
Declaration
typedef enum{
LIOT_CLK_RC26M,
LIOT_CLK_RTC_40K,
} liot_pwm_clk_e;
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:
Declaration
typedef struct{
liot_pwm_clk_e clk_sel;
uint16_t prescaler;
uint16_t period;
uint16_t duty;
} liot_pwm_cfg_s;
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
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.
Declaration
liot_pwm_errcode_e liot_pwm_open(liot_pwm_sel_e pwm_sel);
Parameters
pwm_sel: [In] PWM channel. See 4.2 liot_pwm_sel_e for details.
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.
Declaration
liot_pwm_errcode_e liot_pwm_close(liot_pwm_sel_e pwm_sel);
Parameters
pwm_sel: [In] PWM channel. See 4.2 liot_pwm_sel_e for details.
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.
Declaration
liot_pwm_errcode_e liot_pwm_enable(liot_pwm_sel_e pwm_sel, liot_pwm_cfg_s *pwm_cfg);
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.
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.
Declaration
liot_pwm_errcode_e liot_pwm_disable(liot_pwm_sel_e pwm_sel);
Parameters
pwm_sel: [In] PWM channel. See 4.2 liot_pwm_sel_e for details.
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.
Declaration
liot_pwm_errcode_e liot_pwm_set_duty_cycle(liot_pwm_sel_e pwm_sel, uint32_t duty_cycle);
Parameters
pwm_sel: [In] PWM channel. See 4.2 liot_pwm_sel_e for details.
duty_cycle: [In] Counter comparison value.
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:
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.
