SPWM使用参考


REVISION HISTORY

Revision No.
Description
Date
1.0
  • Initial release
  • 09/06/2024
    1.1
  • 调整目录层级和完善FAQ描述
  • 04/14/2025

    1. 概述

    SPWM(Sinusoidal Pulse Width Modulation)是一种脉宽调制技术,常用于交流电机驱动和逆变器控制中。它通过将直流电压转换为接近正弦波形的交流电压,实现对交流电机速度和功率的精确控制。

    SPWM技术基于PWM(Pulse Width Modulation,脉宽调制)原理,但不同于传统的方波PWM,它使用一系列的窄脉冲来模拟正弦波形。这些窄脉冲的脉宽和频率可以通过合理的调节来产生接近正弦波的输出。

    1.1. 调制过程

    以载波频率为正弦波频率的8倍(即调制指数=8)为例,调制过程可以解释如下:

    将正弦波分成八个相等的段,并用数字<1>至<8>标记每个段;

    根据自动控制中面积相等的原理,调整方波(红色突出部分)的占空比,使其面积与每个区间内正弦波(蓝色突出部分)下方的面积相匹配。 例如图中区间<1>所示,对电机的控制效果是等价的。 通过这种方法,我们可以通过使用一系列方波来达到与正弦波相同的控制效果。

    1.2. 电机控制

    以两相四线电机为例,有四个端子:A- A+ B- B+。 A-和A+结合形成A相正弦波,而B-和B+结合形成B相正弦波。 A相和B相之间的相位差为90°。 正弦波的频率越高,电机速度越快。

    三相电机与两相电机类似,主要区别在于三相电机具有三相,且相邻相之间的相位差为60°。

    2. 功能描述

    • 支持SPWM的channel数量为18个,其中channel 0~17需要加入各自的Group,详见Group与channel对应关系,正常情况使用时需要将4个channel或者6个channel均加入group,以电机控制的角度来看,最大支持3个两相四线电机和1个三相六线电机(如果使用4个两相四线电机,那么剩余的两个channel可以当作PWM使用)

    • SPWM载波频率为50Hz~50KHz,支持的OSCCLK(Hz)为:86M、24M、12M、6M、3M、1.5M、750K

    • 正弦波不能失真,即当前正弦波周期结束后周期及占空比等新配置才会生效

    • SPWM支持的调制模式为Full-wave modeHalf-wave modeSymmetric half-wave mode,对应的最大调制指数为64、128、256

    • 同组SPWM共用一份duty table,最大支持16bit * 64个duty的配置,即一个调制周期最大支持64个不同的占空比设定

    • 支持相位调整,即可以选择duty table中的任意一个duty作为起始点开始输出SPWM

    • 支持scale功能,即可以对duty table内的所有duty配置缩放比例,可用于调整调制波的幅度

    • 支持clamp功能,即可以对占空比设置钳位,当duty<=下限时占空比为0,当duty>=上限时占空比为100%,可用于死区保护

    • 支持round功能,即同Group内的SPWM会在产生特定数量的脉冲后停止

    • 支持stop功能,即同Group内的SPWM会紧急停止

    3. 相关概念说明

    3.1. 调制模式和调制指数

    • Full-wave mode,全波模式

      一个正弦周期的最大值为64个载波周期(调制指数最大可设置为64),每个载波周期都生效duty,按照duty table的设定顺序输出占空比

    • Half-wave mode,半波模式

      一个正弦周期的最大值为128个载波周期(调制指数最大可设置为128),前64个载波周期均生效duty,按照duty table的设定顺序输出占空比,后64个载波周期的duty为0

    • Symmetric half-wave mode,对称半波模式

      一个正弦周期的最大值为256个载波周期(调制指数最大可设置为256),第1~64个载波周期均生效duty,按照duty table的设定顺序输出占空比,第65~128个载波周期均生效duty,按照duty table设定的相反顺序输出占空比,与前64个载波周期形成对称,第128~256个载波周期的duty为0

    • Duty Table

      每组spwm共用一份duty table,可以抽象理解为一个数组,最大可以存储64个duty值,spwm启动时会按照duty table的顺序依次在每个载波周期输出对应的duty;

      Full-wave mode举例,调制指数设定为10,初始相位为0,那么spwm就会按照duty table的duty0~duty9输出波形;调制指数设定为64,那么spwm就会按照duty table的duty0~duty63输出波形;Full-wave mode的调制指数不支持设定值大于64;

      Half-wave mode举例,调制指数设定为10,初始相位为0,那么spwm第1~10个载波周期就会按照duty table的duty0duty9输出波形,第1120个载波周期的duty为0;调制指数设定为128,那么spwm第1~64个载波周期就会按照duty table的duty0duty63输出波形,第65128个载波周期的duty为0;Half-wave mode的调制指数不支持设定值大于128;

    3.2. scale功能

    占空比缩放功能,可以快速调整调制波的幅度

    以OSCCLK为12MHz,载波周期为50KHz,scale设定的缩放比例为原先的50%,duty的变化情况如下:

    duty index 原始占空比 缩小50%的占空比
    0 250 ns 83 ns
    1 500 ns 250 ns
    2 750 ns 335 ns
    3 1000 ns 500 ns
    4 1250 ns 585 ns
    5 1500 ns 750 ns
    6 1750 ns 835 ns
    7 2000 ns 1000 ns
    8 2250 ns 1085 ns
    9 2500 ns 1250 ns

    请注意占空比是12MHz的整数倍,因此scale转换后,不是整数倍的占空比会被向下取整

    3.3. clamp功能

    占空比钳位模式,当duty<=下限时占空比为0,当duty>=上限时占空比为100%

    以OSCCLK为12MHz,载波周期为50KHz,duty table设定的占空比范围250ns16000ns,占空比钳位上下限设定为1000012000ns,输出的波形如下:

    3.4. GROUP相关概念

    3.4.1. Sync mode

    sync mode可以将每个spwm channel加入到各自的group群组中,完成同时对多个spwm进行控制的目的,一个group有4个或6个spwm channel,且group与channel对应关系如下:

    Group Group Member
    Group0 SPWM0、SPWM1、SPWM2、SPWM3
    Group1 SPWM4、SPWM5、SPWM6、SPWM7
    Group2 SPWM8、SPWM9、SPWM10、SPWM11
    Group3 SPWM12、SPWM13、SPWM14、SPWM15、SPWM16、SPWM17

    可通过DTS配置选择是否将spwm加入到Group中:

    pwm0: pwm@0x1F203200 {
        compatible = "sstar,pwm";
        ......
        channel = <0>;
        spwm-group = <0>;  //加入spwm group0
        ......
    };
    
    pwm1: pwm@0x1F203240 {
        compatible = "sstar,pwm";
        ......
        channel = <1>;
        spwm-group = <0>;
        ......
    };
    
    pwm2: pwm@0x1F203280 {
        compatible = "sstar,pwm";
        ......
        channel = <2>;
        spwm-group = <0>;  //加入spwm group0
        ......
    };
    
    pwm3: pwm@0x1F2032C0 {
        compatible = "sstar,pwm";
        ......
        channel = <3>;
        spwm-group = <0>;  //加入spwm group0
        ......
    };
    
    pwm4: pwm@0x1F203400 {
        compatible = "sstar,pwm";
        ......
        channel = <4>;
        group = <1>;      //加入pwm group1
        ......
    };
    
    pwm5: pwm@0x1F203440 {
        compatible = "sstar,pwm";
        ......
        channel = <5>;
        group = <1>;      //加入pwm group1
        ......
    };
    
    pwm6: pwm@0x1F203480 {
        compatible = "sstar,pwm";
        ......
        channel = <6>;
        group = <1>;      //加入pwm group1
        ......
    };
    
    pwm7: pwm@0x1F2034C0 {
        compatible = "sstar,pwm";
        ......
        channel = <7>;
        group = <1>;      //加入pwm group1
        ......
    };
    
    pwm8: pwm@0x1F203800 {
        compatible = "sstar,pwm";
        ......
        channel = <8>;
        ......
    };
    
    pwm9: pwm@0x1F203840 {
        compatible = "sstar,pwm";
        ......
        channel = <9>;
        ......
    };
    

    DTS节点配置spwm-group = <x>则使能spwm功能并加入spwm group,比如pwm0pwm3都配置了spwm-group = <0>,那么pwm0pwm3都将赋予spwm功能并加入spwm group0,需要遵循spwm的特性与设定,因此可将spwm-group = <x>理解为spwm的功能开关;

    DTS节点配置group = <x>则使能pwm功能并加入pwm group,比如pwm4pwm7都配置了pwm-group = <1>,那么pwm4pwm7都将赋予pwm功能并加入pwm group1,此时无法使用spwm的功能;

    DTS节点没有配置group = <x>spwm-group = <x>,比如pwm8和pwm9,那么pwm8和pwm9只能当作普通的pwm使用;

    group与swpm-group属性决定了/sys/class/sstar/pwm下的各channel layout

    按上述配置,实际生成的接口分布:

    3.4.2. Round mode

    round功能会在同spwm group内的所有spwm channel完成一定数量的脉冲后停止,每组spwm group都有自己独立的round功能,如下图,spwm group0使用full wave mode,round设定为64,因此实际输出64个脉冲后波形停止

    3.4.3. Stop mode

    stop功能可以让当前spwm group中的所有spwm channel立即停止(不会等当前周期完成)并维持结束时的电平,每组spwm group都有自己独立的stop功能。

    注意:stop 时间不建议太长,尤其是停止后维持的电平为高电平时

    4. 硬件连接说明

    PIN脚复用为PWM MODE后,已经处于output状态,为了保护电路,请不要外灌电压;同时硬件会根据寄存器设定输出PWM波形或者SPWM波形,可根据需要接入负载

    5. Uboot用法介绍

    暂不支持uboot使用spwm

    6. Kernel用法介绍

    驱动路径:kernel/drivers/sstar/pwm

    6.1. Kernel Config配置

    在编译kernel时需要选择的配置如下:

    Device Drivers-->
        [*] SStar SoC platform drivers-->
            [*] Sigmastar PWM driver
                [*] Support SPWM function              // 使能SPWM
                [ ] Support dead time generation       // 对于SPWM无效
                [*] Support high precision calculation // SPWM仅支持高精度模式
    

    6.2. 配置DTS

    spwm的DTS配置只需要在对应的chipname.dtsi中的pwm节点配置spwm-group属性,可参考DTS配置说明

    pwm0: pwm@0x1F203200 {
        compatible = "sstar,pwm";
        reg = <0x0 0x1F203200 0x0 0x40>;
        interrupts=<GIC_SPI INT_IRQ_PWM_GROUP0 IRQ_TYPE_LEVEL_HIGH>;
        clocks = <&CLK_pwm>;
        #pwm-cells = <3>;
        channel = <0>;
        spwm-group = <0>;
        clk-select = <0>;
        status = "okay";
    };
    
    .......
    
    pwm4: pwm@0x1F203400 {
        compatible = "sstar,pwm";
        reg = <0x0 0x1F203400 0x0 0x40>;
        interrupts=<GIC_SPI INT_IRQ_PWM_GROUP1 IRQ_TYPE_LEVEL_HIGH>;
        clocks = <&CLK_pwm>;
        #pwm-cells = <3>;
        channel = <4>;
        spwm-group = <1>;
        clk-select = <0>;
        status = "okay";
    };
    
    ......
    
    pwm8: pwm@0x1F203800 {
        compatible = "sstar,pwm";
        reg = <0x0 0x1F203800 0x0 0x40>;
        interrupts=<GIC_SPI INT_IRQ_PWM_GROUP2 IRQ_TYPE_LEVEL_HIGH>;
        clocks = <&CLK_pwm>;
        #pwm-cells = <3>;
        channel = <8>;
        spwm-group = <2>;
        clk-select = <0>;
        status = "okay";
    };
    
    ......
    
    pwm12: pwm@0x1F205600 {
        compatible = "sstar,pwm";
        reg = <0x0 0x1F205600 0x0 0x40>, <0x0 0x1F203600 0x0 0xC>;
        interrupts=<GIC_SPI INT_IRQ_PWM_GROUP3 IRQ_TYPE_LEVEL_HIGH>;
        clocks = <&CLK_pwm>;
        #pwm-cells = <3>;
        channel = <12>;
        spwm-group = <3>;
        clk-select = <0>;
        status = "okay";
    };
    

    PWM DTS配置说明:

    属性 描述 设定值 备注
    compatible 匹配驱动进行驱动注册,需与代码中一致 "sstar,pwm" 禁止修改
    reg 设定寄存器bank的地址 / 禁止修改
    interrupts 指定使用的硬件中断号及属性 INT_IRQ_PWM_GROUPX 禁止修改
    clocks 指定使用的时钟源 CLK_pwm 禁止修改
    channel 匹配channel index 0~17,spwm仅支持18个channel 禁止修改
    spwm-group 指定是否加入spwm group 不配置该属性则不加入spwm group,加入spwm group配置0/½/3,详见Group与channel对应关系 可根据需要修改
    clk-select 用于选择时钟档位 pwm017可选06分别对应12M/6M/3M/1.5M/750K/86M/ 24M pwm0~17选择必须一致
    status 选择是否使能PWM驱动 "ok" or "disable" 可根据需要修改

    6.3. PADMUX配置

    spwm在Kernel环境下的padmux配置方法与pwm一致,只需要根据选择的引脚在对应的padmux.dtsi中加入如下所示的代码:

    spwm的padmux配置:

    <PAD_GPIOC_00   PINMUX_FOR_PWM0_MODE_1    MDRV_PUSE_PWM0>,
    <PAD_GPIOC_01   PINMUX_FOR_PWM1_MODE_1    MDRV_PUSE_PWM1>,
    <PAD_GPIOC_02   PINMUX_FOR_PWM2_MODE_1    MDRV_PUSE_PWM2>,
    <PAD_GPIOC_03   PINMUX_FOR_PWM3_MODE_1    MDRV_PUSE_PWM3>,
    <PAD_GPIOC_04   PINMUX_FOR_PWM4_MODE_1    MDRV_PUSE_PWM4>,
    <PAD_GPIOC_05   PINMUX_FOR_PWM5_MODE_1    MDRV_PUSE_PWM5>,
    <PAD_GPIOC_06   PINMUX_FOR_PWM6_MODE_1    MDRV_PUSE_PWM6>,
    <PAD_GPIOC_07   PINMUX_FOR_PWM7_MODE_1    MDRV_PUSE_PWM7>,
    <PAD_GPIOC_08   PINMUX_FOR_PWM8_MODE_1    MDRV_PUSE_PWM8>,
    <PAD_OUTP_CH0   PINMUX_FOR_PWM9_MODE_1    MDRV_PUSE_PWM9>,
    <PAD_GPIOA_00   PINMUX_FOR_PWM10_MODE_2   MDRV_PUSE_PWM10>,
    <PAD_GPIOA_01   PINMUX_FOR_PWM11_MODE_2   MDRV_PUSE_PWM11>,
    <PAD_GPIOA_02   PINMUX_FOR_PWM12_MODE_2   MDRV_PUSE_PWM12>,
    <PAD_GPIOA_03   PINMUX_FOR_PWM13_MODE_2   MDRV_PUSE_PWM13>,
    <PAD_GPIOA_04   PINMUX_FOR_PWM14_MODE_2   MDRV_PUSE_PWM14>,
    <PAD_GPIOA_05   PINMUX_FOR_PWM15_MODE_2   MDRV_PUSE_PWM15>,
    <PAD_GPIOA_06   PINMUX_FOR_PWM16_MODE_2   MDRV_PUSE_PWM16>,
    <PAD_GPIOA_07   PINMUX_FOR_PWM17_MODE_2   MDRV_PUSE_PWM17>,
    

    第一列为引脚索引号,可以在drivers/sstar/inlcude/{chipname}/gpio.h中查到;

    第二列为模式定义,在drivers/sstar/gpio/{chipname}/hal_pinmux.chal_gpio_st_padmux_info数组里,罗列了所有引脚的复用关系,查询该引脚支持哪些复用功能可以查询该数组;

    第三列为引脚及搭配模式的索引名称,可在drivers/sstar/include/drv_puse.h里查询。

    6.4. 模块使用介绍

    6.4.1. sys/class/sstar/pwm

    sys/class/sstar/pwm目录结构如下:

    • 一级目录

      spwm group0/½/... → 对同spwm group内的所有spwm channel同步操作

    • 二级目录

      以spwm group0举例 → 配置spwm group0的相关属性

    spwm的配置注重先后顺序,请参考如下步骤和说明

    步骤 名称 描述 参数 备注
    1 sg_config 设定调制模式和调制指数 echo [spwm mode] [duty length] > sg_config 参考spwm调制模式和调制指数,0:symmetric half-wave mode,1:half-wave mode , 2:full-wave mode
    2 sg_step 设定group内所有spwm channel的初始相位(即第一个输出的duty位于duty table的下标) echo [duty index] > sg_step 可以指定某个channel的step,echo [channel id] [duty index] > sg_step
    3 sg_period 设定spwm的载波周期 echo [period ns] > sg_period 同组内的spwm channel period应该保持同步
    4 sg_duty 配置spwm的duty table echo [duty index] [duty ns] > sg_duty 同组内的spwm channel共用一份duty table
    5 sg_enable 使能spwm echo [enable] > sg_enable 1:enable,0:disable
    / sg_scale 设定spwm duty的缩放比例 echo [n] > sg_scale 最终比例按照n/8计算,默认不缩放,如果启用scale功能,那么该配置需要设定在sg_config之前
    / sg_clamp 设定spwm duty钳位的上下限 echo [upper limit] [lower limit] > sg_clamp 单位为ns
    / sg_stop 使能spwm stop功能 echo [enable] > sg_stop 1:enable,0:disable
    / sg_round 使能spwm round功能 echo [round number] > sg_round round number为pwm周期数

    举例:spwm group0下的所有spwm channel同步且输出调制模式为full wave mode、调制指数64、载波周期为50KHZ、第一个载波周期占空比为250ns并以250ns递增的波形:

    1.    #跳转到spwm group0路径
    2.    cd sys/class/sstar/pwm/spwm_group0
    3.
    4.    #配置spwm group0
    4.    echo 2 64     > sg_config
    5.    echo 0        > sg_step
    6.    echo 20000    > sg_period
    7.    echo 0 250    > sg_duty
    8.    echo 1 500    > sg_duty
    9.    echo 2 750    > sg_duty
    10.   echo 3 1000   > sg_duty
    11.   echo 4 1250   > sg_duty
    12.   echo 5 1500   > sg_duty
    13.   ......
    14.   echo 63 16000 > sg_duty
    15.   echo 1        > sg_enable
    

    6.4.2. ioctl

    头文件<drv_pwm.h>位于/driver/sstar/pwm目录下,struct spwm_gp_cfg结构体是对spwm group属性的描述

    • IOCTL_SPWM_SET_GROUP_CFG:完成spwm group的参数配置

    • IOCTL_SPWM_GET_GROUP_CFG:获取当前spwm group的参数配置

    • IOCTL_SPWM_UPDATE_PERIOD:更新spwm的载波周期

    • IOCTL_SPWM_UPDATE_DUTY:更新spwm指定位置的duty

    • IOCTL_SPWM_GROUP_STOP:触发stop功能

    • IOCTL_SPWM_GROUP_ROUND:触发round功能

    struct spwm_gp_cfg
    {
        u8  mode;       //调制模式
        u8  step;       //初始相位
        u8  index;      //更新duty时使用的下标
        u8  scale;      //duty缩放比例n,按n/8计算
        u32 group;      //spwm group id
        u8  enable;     //1:enable
        u64 period;     //spwm载波周期,以ns为单位
        u8  stop_en;    //使能stop功能
        u8  clamp_en;   //使能duty钳位功能
        u8  duty_len;   //duty table使用的length
        u64 duty[64];   //duty table的设定,最大64个
        u64 clamp_max;  //钳位上限
        u64 clamp_min;  //钳位下限
        u32 round_num;  //pwm周期数,round=64,实际输出64个脉冲
    };
    
    #define SPWM_IOC_MAXNR 6
    
    #define IOCTL_SPWM_SET_GROUP_CFG_NR (0)
    #define IOCTL_SPWM_GET_GROUP_CFG_NR (1)
    #define IOCTL_SPWM_UPDATE_PERIOD_NR (2)
    #define IOCTL_SPWM_UPDATE_DUTY_NR   (3)
    #define IOCTL_SPWM_GROUP_STOP_NR    (4)
    #define IOCTL_SPWM_GROUP_ROUND_NR   (5)
    
    #define SPWM_IOC_MAGIC           's'
    #define IOCTL_SPWM_SET_GROUP_CFG _IO(SPWM_IOC_MAGIC, IOCTL_SPWM_SET_GROUP_CFG_NR)
    #define IOCTL_SPWM_GET_GROUP_CFG _IO(SPWM_IOC_MAGIC, IOCTL_SPWM_GET_GROUP_CFG_NR)
    #define IOCTL_SPWM_UPDATE_PERIOD _IO(SPWM_IOC_MAGIC, IOCTL_SPWM_UPDATE_PERIOD_NR)
    #define IOCTL_SPWM_UPDATE_DUTY   _IO(SPWM_IOC_MAGIC, IOCTL_SPWM_UPDATE_DUTY_NR)
    #define IOCTL_SPWM_GROUP_STOP    _IO(SPWM_IOC_MAGIC, IOCTL_SPWM_GROUP_STOP_NR)
    #define IOCTL_SPWM_GROUP_ROUND   _IO(SPWM_IOC_MAGIC, IOCTL_SPWM_GROUP_ROUND_NR)
    

    6.5. Sample code

    参考代码drv_spwm_test.c位于/driver/sstar/pwm/ut目录

    #include <autoconf.h>
    #include <fcntl.h>
    #include <stdio.h>
    #include <errno.h>
    #include <stdlib.h>
    #include <unistd.h>
    #include <string.h>
    #include <drv_pwm.h>
    #include <sys/stat.h>
    #include <sys/types.h>
    #include <sys/ioctl.h>
    
    int main(int argc, char **argv)
    {
        u8   i       = 0;
        int  ret     = 0;
        u32  duty    = 0;
        int  spwm_fd = -1;
        char path_name[24];
    
        struct spwm_gp_cfg gp_cfg;
    
        if (argc != 4 && argc != 5)
        {
            printf("format: ut_spwm <group>  [group_id]  [mode]   [duty_len]\n");
            printf("format: ut_spwm <duty>   [group_id]  [index]  [duty_len]\n");
            printf("format: ut_spwm <stop>   [group_id]  [enable]\n");
            printf("format: ut_spwm <round>  [group_id]  [round_num]\n");
            return -1;
        }
    
        if (!strcmp(argv[1], "group"))
        {
            gp_cfg.group = atoi(argv[2]);
    
            snprintf(path_name, sizeof(path_name), "/dev/spwm_group%u", gp_cfg.group);
            spwm_fd = open((const char *)(char *)path_name, O_RDWR);
            if (spwm_fd < 0)
            {
                printf("open /dev/spwm_group%u fail errno:[%d]\n", gp_cfg.group, spwm_fd);
                return -1;
            }
    
            gp_cfg.step      = 0;
            gp_cfg.scale     = 0;
            gp_cfg.clamp_en  = 0;
            gp_cfg.clamp_max = 12000;
            gp_cfg.clamp_min = 10000;
    
            gp_cfg.enable   = 1;
            gp_cfg.mode     = atoi(argv[3]);
            gp_cfg.duty_len = atoi(argv[4]);
            //gp_cfg.round_num = 64; //可选择只输出64个脉冲后停止
            gp_cfg.period   = 20000;
    
            if (gp_cfg.duty_len > 64)
            {
                printf("spwm group[%u] max duty len is 64\n", gp_cfg.group);
                close(spwm_fd);
                return -1;
            }
    
            duty = 250;
            for (i = 0; i < gp_cfg.duty_len; i++)
            {
                gp_cfg.duty[i] = duty * (i + 1);
            }
    
            ret = ioctl(spwm_fd, IOCTL_SPWM_SET_GROUP_CFG, &gp_cfg);
            if (ret < 0)
            {
                printf("spwm group[%u] set config fail, err[%d]\n", gp_cfg.group, ret);
                close(spwm_fd);
                return ret;
            }
    
            /*spwm period change from 50Khz to 25Khz */
            gp_cfg.period = 40000;
            ret           = ioctl(spwm_fd, IOCTL_SPWM_UPDATE_PERIOD, &gp_cfg);
            if (ret < 0)
            {
                printf("spwm group[%u] update period fail\n", gp_cfg.group);
                close(spwm_fd);
                return ret;
            }
    
            usleep(6000);
            /*spwm period change from 25Khz to 40Khz */
            gp_cfg.period = 25000;
            ret           = ioctl(spwm_fd, IOCTL_SPWM_UPDATE_PERIOD, &gp_cfg);
            if (ret < 0)
            {
                printf("spwm group[%u] update period fail\n", gp_cfg.group);
                close(spwm_fd);
                return ret;
            }
        }
        else if (!strcmp(argv[1], "duty"))
        {
            gp_cfg.group    = atoi(argv[2]);
            gp_cfg.index    = atoi(argv[3]);
            gp_cfg.duty_len = atoi(argv[4]);
    
            snprintf(path_name, sizeof(path_name), "/dev/spwm_group%u", gp_cfg.group);
            spwm_fd = open((const char *)(char *)path_name, O_RDWR);
            if (spwm_fd < 0)
            {
                printf("open /dev/spwm_group%u fail errno:[%d]\n", gp_cfg.group, spwm_fd);
                return -1;
            }
    
            for (i = 0; i < gp_cfg.duty_len; i++)
            {
                gp_cfg.duty[i] = 16000;
            }
    
            ret = ioctl(spwm_fd, IOCTL_SPWM_UPDATE_DUTY, &gp_cfg);
            if (ret < 0)
            {
                printf("spwm group[%u] update duty fail\n", gp_cfg.group);
                close(spwm_fd);
                return ret;
            }
        }
        else if (!strcmp(argv[1], "stop"))
        {
            gp_cfg.group   = atoi(argv[2]);
            gp_cfg.stop_en = atoi(argv[3]);
            snprintf(path_name, sizeof(path_name), "/dev/spwm_group%u", gp_cfg.group);
            spwm_fd = open((const char *)(char *)path_name, O_RDWR);
            if (spwm_fd < 0)
            {
                printf("open /dev/spwm-group%u fail errno:[%d]\n", gp_cfg.group, spwm_fd);
                return -1;
            }
    
            ret = ioctl(spwm_fd, IOCTL_SPWM_GROUP_STOP, &gp_cfg);
            if (ret < 0)
            {
                printf("spwm group[%u] stop config fail\n", gp_cfg.group);
                close(spwm_fd);
                return ret;
            }
        }
        else if (!strcmp(argv[1], "round"))
        {
            gp_cfg.group     = atoi(argv[2]);
            gp_cfg.round_num = atoi(argv[3]);
            snprintf(path_name, sizeof(path_name), "/dev/spwm_group%u", gp_cfg.group);
            spwm_fd = open((const char *)(char *)path_name, O_RDWR);
            if (spwm_fd < 0)
            {
                printf("open /dev/spwm-group%u fail errno:[%d]\n", gp_cfg.group, spwm_fd);
                return -1;
            }
            ret = ioctl(spwm_fd, IOCTL_SPWM_GROUP_ROUND, &gp_cfg);
            if (ret < 0)
            {
                printf("spwm group[%u] round config fail\n", gp_cfg.group);
                close(spwm_fd);
                return ret;
            }
        }
        else
        {
            printf("erro command\n");
        }
    
        close(spwm_fd);
    
        return 0;
    }
    

    7. FAQ

    Q1:SPWM各接口不存在

    1. 检查DTS PWM节点的status是否为ok

    2. 检查DTS PWM节点spwm-group属性,如果未配置,则不会生成SPWM接口

    3. 检查DTS PWM节点group属性,如果已配置,则不会生成SPWM接口

    4. 检查kernel config是否配置,详见[Kernel Config配置]

    Q2:配置后SPWM无波形产生

    1. 首先确认测量的引脚是否正确:打开对应的原理图确认即可,如果没有错误的话,则进行下一步。

    2. 确认对应的PWM mode是否生效,引脚复用失败主要有两个原因:

      原因一:该引脚没有设置为PWM mode,设置方法详见[PADMUX配置]

      原因二:有优先级比PWM mode更高级别的padmux mode被开启,可以在编译的时候打开padmux回读机制的CONFIG:CONFIG_MSYS_PADMUX,它的功能是用于确认使用到的padmux是否有被成功设定,开启后在user space输入如下命令查看:

      echo PAD_PWM0 PINMUX_FOR_PWM0_MODE_1 > /sys/class/sstar/msys/mux_verify
      
      cat /sys/class/sstar/msys/mux_verify
      
    3. 检查相关参数是否设置成功

      以spwm group0为例,输入如下命令查看参数:

      同时需要注意配置顺序,详见SPWM配置顺序