跳转至

脉冲宽度调制输出(PWM)


1、简介

  • PWM(Pulse Width Modulation)是一种通过调节数字脉冲的占空比(高电平时间占整个周期的比例)来等效模拟信号电平的技术,广泛应用于电机控制、电源管理、LED调光等领域。
  • CI13XX支持6个专用PWM:PWM0~PWM5。
  • 每个PWM输出信号的频率通过API(pwm_init)进行配置,每个PWM输出信号的占空比通过API(pwm_init、pwm_set_duty)进行配置,不支持100%占空比(常高),若需使用100%占空比,通过配置GPIO来实现。

2、特性

  • 计数时钟分频,支持1、2、4、16分频,通过API(pwm_init)进行配置;
  • 支持两个32位递减计数器;
  • 可变占空比PWM脉冲宽度波形输出;

3、API列表

函数名 描述
pwm_init 初始化PWM设备
pwm_start 启动PWM设备
pwm_stop 停止PWM设备
pwm_set_duty 设置PWM占空比
pwm_set_restart_md 重新计数生效模式的选择


4、普通IO示例

(1) 以下代码配置并启动PWM5,50%占空比,1000频率

#include "ci130x_scu.h"
#include "ci130x_dpmu.h"
#include "ci130x_pwm.h"

void pwm_test()
{
    /*PWM5控制器时钟配置*/
    scu_set_device_gate(HAL_PWM5_BASE,ENABLE);

    /*PWM5的引脚初始化*/
    dpmu_set_io_reuse(PB4,SECOND_FUNCTION);              //设置引脚功能复用
    dpmu_set_io_direction(PB4,DPMU_IO_DIRECTION_OUTPUT); //设置引脚功能为输出模式
    dpmu_set_io_pull(PB4,DPMU_IO_PULL_DISABLE);          //设置关闭上下拉

    /*PWM5频率、占空比初始化*/
    pwm_init_t init;
    init.clk_sel = 0;    //计数时钟来源于PCLK
    init.freq = 1000;    //频率为1K
    init.duty = 50;      //占空比50%的分子
    init.duty_max = 100; //占空比50%的分母
    pwm_init(PWM5,init);

    /*PWM5启动*/
    pwm_stop(PWM5);      //PWM关闭
    pwm_start(PWM5);     //PWM打开
}

(2) 该芯片IP增加了重新计数生效模式的选择,例如初始化后,需要在不同占空比之间切换,希望前一个占空比的PWN波完整输出后,后一个占空比重新计数才生效,可以参考下列示例代码。

#include "ci130x_scu.h"
#include "ci130x_dpmu.h"
#include "ci130x_pwm.h"

void pwm_test()
{
    /*PWM5控制器时钟配置*/
    scu_set_device_gate(HAL_PWM5_BASE,ENABLE);

    /*PWM5的引脚初始化*/
    dpmu_set_io_reuse(PB4,SECOND_FUNCTION);              //设置引脚功能复用
    dpmu_set_io_direction(PB4,DPMU_IO_DIRECTION_OUTPUT); //设置引脚功能为输 出模式
    dpmu_set_io_pull(PB4,DPMU_IO_PULL_DISABLE);          //设置关闭上下拉

    /*PWM5频率、占空比初始化*/
    pwm_init_t init;
    init.clk_sel = 0;      //计数时钟来源于PCLK
    init.freq = 1000;      //频率为1K
    init.duty = 50;        //占空比50%的分子
    init.duty_max = 100;   //占空比50%的分母
    pwm_init(PWM5,init);
    pwm_set_restart_md(PWM5,1);   //配置等待正在进行的PWM波完整输出后重新计数才生效

    /*PWM5启动*/
    pwm_stop(PWM5);        //PWM关闭
    pwm_start(PWM5);       //PWM打开

    /*PWM5中途切换占空比*/
    pwm_set_duty(PWM5,30,100)    //切换到占空比30%
}

配置等待正在进行的PWM波完整输出后重新计数才生效,波形结果如下:

PWM波形图

配置重新计数立即生效,pwm_set_restart_md(PWM0,0),波形结果如下:

PWM波形图


(3) 2组PWM实现互补输出功能

#include "ci130x_scu.h"
#include "ci130x_dpmu.h"
#include "ci130x_pwm.h"
#include "task.h"

void pwm_test()
{
    /*PWM5控制器时钟配置*/
    scu_set_device_gate(HAL_PWM5_BASE,ENABLE);

    /*PWM5的引脚初始化*/
    dpmu_set_io_reuse(PB4,SECOND_FUNCTION);                //设置引脚功  能复用
    dpmu_set_io_direction(PB4,DPMU_IO_DIRECTION_OUTPUT);   //设置引脚功  能为输出模式
    dpmu_set_io_pull(PB4,DPMU_IO_PULL_DISABLE);            //设置关闭上  下拉

    /*PWM5频率、占空比初始化*/
    pwm_init_t init;
    init.clk_sel = 0;     //计数时钟来源于PCLK
    init.freq = 16000;    //频率为1.6K
    init.duty = 50;       //占空比50%的分子
    init.duty_max = 100;  //占空比50%的分母
    pwm_init(PWM5,init);
    pwm_stop(PWM5);       //PWM关闭

    /*PWM0控制器时钟配置*/
    scu_set_device_gate(HAL_PC_BASE,ENABLE);

    /*PWM0的引脚初始化*/
    scu_set_device_gate(HAL_PWM0_BASE,ENABLE);
    dpmu_set_io_reuse(PC4,FORTH_FUNCTION);                 //设置引脚功能复用
    dpmu_set_io_direction(PC4,DPMU_IO_DIRECTION_OUTPUT);   //设置引脚功能为输出模式
    dpmu_set_io_pull(PC4,DPMU_IO_PULL_DISABLE);            //设置关闭上下拉

    /*PWM5频率、占空比初始化*/
    init.clk_sel = 0;    //计数时钟来源于PCLK
    init.freq = 16000;   //频率为1.6K
    init.duty = 50;      //占空比50%的分子
    init.duty_max = 100; //占空比50%的分母
    pwm_init(PWM0,init);
    pwm_stop(PWM0);      //PWM关闭

    /*PWM0和PWM5错开时间开启,实现互补输出*/
    {
        volatile uint32_t i = 0;
        taskENTER_CRITICAL();
        pwm_start(PWM5);
        for (i = 0;i < 0x210;i++);
        pwm_start(PWM0);
        taskEXIT_CRITICAL();
    }
}

5、晶振IO示例

(1) PA0是连接晶振的IO,默认是模拟功能,以下代码配置并启动PA0复用成PWM5,50%占空比,1000频率

#include "ci130x_scu.h"
#include "ci130x_dpmu.h"
#include "ci130x_pwm.h"

void pwm_test()
{
    /*使能晶振IO用作普通IO功能*/
    dpmu_osc_pad_for_gpio(ENABLE);                       //PA0用作PWM,必须把晶振功能关闭

    /*PWM5控制器时钟配置*/
    scu_set_device_gate(HAL_PWM5_BASE,ENABLE);

    /*PWM5的引脚初始化*/
    dpmu_set_io_reuse(PA0,SECOND_FUNCTION);              //设置引脚功能复用
    dpmu_set_adio_reuse(PA0,DIGITAL_MODE);               //初始化为数字功能,默认是模拟功能
    dpmu_set_io_direction(PA0,DPMU_IO_DIRECTION_OUTPUT); //设置引脚功能为输出模式
    dpmu_set_io_pull(PA0,DPMU_IO_PULL_DISABLE);          //设置关闭上下拉

    /*PWM5频率、占空比初始化*/
    pwm_init_t init;
    init.clk_sel = 0;     //计数时钟来源于PCLK
    init.freq = 1000;     //频率为1K
    init.duty = 50;       //占空比50%的分子
    init.duty_max = 100;  //占空比50%的分母
    pwm_init(PWM5,init);

    /*PWM5启动*/
    pwm_stop(PWM5);    //PWM关闭
    pwm_start(PWM5);   //PWM打开
}

6、模拟IO示例

(1)PC1、PC2、PC3、PC4这4个引脚默认是模拟功能,以下代码配置并启动PC1复用成PWM3,50%占空比,1000频率

#include "ci130x_scu.h"
#include "ci130x_dpmu.h"
#include "ci130x_pwm.h"

void pwm_test()
{
    /*PWM3控制器时钟配置*/
    scu_set_device_gate(HAL_PWM3_BASE,ENABLE);

    /*PWM3的引脚初始化*/
    dpmu_set_io_reuse(PC1,FORTH_FUNCTION);               //设置引脚功能复用
    dpmu_set_adio_reuse(PC1,DIGITAL_MODE);               //初始化为数字功能,默认是模拟功能
    dpmu_set_io_direction(PC1,DPMU_IO_DIRECTION_OUTPUT); //设置引脚功能为输出模式
    dpmu_set_io_pull(PC1,DPMU_IO_PULL_DISABLE);          //设置关闭上下拉

    /*PWM3频率、占空比初始化*/
    pwm_init_t init;
    init.clk_sel = 0;     //计数时钟来源于PCLK
    init.freq = 1000;     //频率为1K
    init.duty = 50;       //占空比50%的分子
    init.duty_max = 100;  //占空比50%的分母
    pwm_init(PWM3,init);

    /*PWM3启动*/
    pwm_stop(PWM3);    //PWM关闭
    pwm_start(PWM3);   //PWM打开
}

7、常见问题

PWM模块基础时钟为主频的一半,例如CLK_S = 100000000Hz(100M)。

PWM频率与PWM最大占空比的关系:

  • CLK_S 大于等于(freq * duty_max)
  • 当freq为10000000Hz(10M)时,此时duty_max最大可配置为10
  • 当freq为1000000Hz(1M)时,此时duty_max最大可配置为100
  • 当freq为100000Hz(100K)时,此时duty_max最大可配置为1000

最大频率支持:

  • freq = 50000000Hz(50M)
  • duty_max = 2;

8、API 参考