SPWM User Guide


REVISION HISTORY

Revision No.
Description
Date
1.0
  • Initial release
  • 09/06/2024
    1.1
  • Adjust the directory hierarchy and improve FAQ description
  • 04/14/2025

    1. Overview

    SPWM (Sinusoidal Pulse Width Modulation) is a pulse width modulation technology commonly used in AC motor drive and inverter control. It achieves precise control of the speed and power of the AC motor by converting the DC voltage into an AC voltage close to a sine waveform.

    SPWM technology is based on the PWM (Pulse Width Modulation) principle, but unlike traditional square wave PWM, it uses a series of narrow pulses to simulate a sine wave. The width and frequency of these narrow pulses can be reasonably adjusted to produce an output close to a sine wave.

    1.1. Modulation process

    Taking the carrier frequency as 8 times the sine wave frequency (ie modulation index = 8) as an example, the modulation process can be explained as follows:

    Divide the sine wave into eight equal segments and label each segment with a number <1> to <8>;

    According to the principle of equal area in automatic control, the duty cycle of the square wave (the red highlighted part) is adjusted so that its area matches the area under the sine wave (the blue highlighted part) in each interval. For example, as shown in interval <1> in the figure, the control effect on the motor is equivalent. In this way, we can achieve the same control effect as a sine wave by using a series of square waves.

    1.2. Motor Control

    Take a two-phase four-wire motor as an example. There are four terminals: A- A+ B- B+. A- and A+ combine to form a phase A sine wave, while B- and B+ combine to form a phase B sine wave. The phase difference between phase A and phase B is 90°. The higher the frequency of the sine wave, the faster the motor speed.

    A three-phase motor is similar to a two-phase motor, the main difference being that a three-phase motor has three phases with a phase difference of 60° between adjacent phases.

    2. Functional Description

    • The number of channels supporting SPWM is 18, of which channels 0~17 need to be added to their own groups. For details, see Group and Channel Correspondence. Under normal circumstances, 4 channels or 6 channels need to be added to the group. From the perspective of motor control, it supports a maximum of 3 two-phase four-wire motors and 1 three-phase six-wire motor (if 4 two-phase four-wire motors are used, the remaining two channels can be used as PWM)

    • SPWM carrier frequency is 50Hz~50KHz, Supported OSCCLK (Hz): 86M, 24M, 12M, 6M, 3M, 1.5M, 750K

    • The sine wave cannot be distorted, that is, the new configuration of the period and duty cycle will take effect only after the current sine wave period ends.

    • The modulation modes supported by SPWM are Full-wave mode, Half-wave mode, Symmetric half-wave mode, and the corresponding maximum modulation indexes are 64, 128, and 256

    • The same group of SPWMs share a duty table, supporting up to 16bit * 64 duty configurations, that is, one modulation cycle supports up to 64 different duty cycle settings

    • Support phase adjustment, that is, you can select any duty in the duty table as the starting point to start SPWM output

    • Support the scale function, that is, you can configure the scaling ratio for all duties in the duty table, which can be used to adjust the amplitude of the modulation wave

    • Support the clamp function, that is, the duty cycle can be clamped. When duty <= lower limit, the duty cycle is 0, and when duty >= upper limit, the duty cycle is 100%, which can be used for dead zone protection

    • Support the round function, that is, the SPWM in the same group will stop after generating a certain number of pulses

    • Support the stop function, that is, the SPWM in the same group will stop urgently

    3.1. Modulation mode and modulation index

    • Full-wave mode

      The maximum value of a sine cycle is 64 carrier cycles (the maximum modulation index can be set to 64). Duty is effective for each carrier cycle, and the duty cycle is output according to the setting order of the duty table.

    • Half-wave mode

      The maximum value of a sine cycle is 128 carrier cycles (the maximum modulation index can be set to 128). The first 64 carrier cycles are all in duty, and the duty cycle is output according to the setting order of the duty table. The duty of the last 64 carrier cycles is 0.

    • Symmetric half-wave mode

      The maximum value of a sine cycle is 256 carrier cycles (the maximum modulation index can be set to 256). The 1st to 64th carrier cycles are all effective in duty, and the duty cycle is output according to the setting order of the duty table. The 65th to 128th carrier cycles are all effective in duty, and the duty cycle is output in the reverse order of the duty table setting, which is symmetrical with the first 64 carrier cycles. The duty of the 128th to 256th carrier cycles is 0

    • Duty Table

      Each group of spwm shares a duty table, which can be abstractly understood as an array. It can store up to 64 duty values. When spwm is started, it will output the corresponding duty in each carrier cycle in the order of the duty table.

      Taking Full-wave mode as an example, if the modulation index is set to 10 and the initial phase is 0, then spwm will output waveforms according to duty0~duty9 in the duty table; if the modulation index is set to 64, then spwm will output waveforms according to duty0~duty63 in the duty table; The modulation index of Full-wave mode does not support setting values greater than 64;

      Taking Half-wave mode as an example, if the modulation index is set to 10 and the initial phase is 0, then the 1st to 10th carrier cycles of spwm will output waveforms according to duty0~duty9 in the duty table, and the duty of the 11th to 20th carrier cycles is 0; if the modulation index is set to 128, then the 1st to 64th carrier cycles of spwm will output waveforms according to duty0~duty63 in the duty table, and the duty of the 65th to 128th carrier cycles is 0; The modulation index of Half-wave mode does not support setting values greater than 128;

    3.2. scale function

    Duty cycle scaling function, can quickly adjust the amplitude of the modulation wave

    Assuming OSCCLK is 12MHz, carrier period is 50KHz, and the scaling ratio is set to 50% of the original, the duty changes are as follows:

    duty index original duty cycle duty cycle reduced by 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

    Please note that the duty cycle is an integer multiple of 12MHz, so after the scale conversion, the duty cycle that is not an integer multiple will be rounded down.

    3.3. clamp function

    Duty cycle clamp mode, when duty <= lower limit, the duty cycle is 0, when duty >= upper limit, the duty cycle is 100%

    With OSCCLK at 12MHz, carrier period at 50KHz, duty cycle range set in the duty table at 250ns~16000ns, and duty cycle clamp upper and lower limits set at 10000~12000ns, the output waveform is as follows:

    3.4. GROUP相关概念

    3.4.1. Sync mode

    Sync mode can add each spwm channel to its own group to achieve the purpose of controlling multiple spwm at the same time. A group has 4 or 6 spwm channels, and the correspondence between group and channel is as follows:

    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

    Choose whether to add spwm to the Group through DTS Configuration :

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

    If the DTS node is configured with spwm-group = <x>, the spwm function is enabled and added to the spwm group. For example, if pwm0~pwm3 are all configured with spwm-group = <0>, then pwm0~pwm3 will be given the spwm function and added to spwm group0. They need to follow the characteristics and settings of spwm, so spwm-group = <x> can be understood as the function switch of spwm.

    If the DTS node is configured with group = <x>, the pwm function is enabled and added to the pwm group. For example, pwm4~pwm7 are all configured with pwm-group = <1>, then pwm4~pwm7 will be given the pwm function and added to pwm group1. At this time, the spwm function cannot be used.

    If the DTS node is not configured with group = <x> and spwm-group = <x>, such as pwm8 and pwm9, then pwm8 and pwm9 can only be used as ordinary pwm;

    The group and swpm-group attributes determine the channel layout under /sys/class/sstar/pwm

    According to the above configuration, the actual generated interface distribution is as follows:

    3.4.2. Round mode

    The round function will stop after all spwm channels in the same spwm group complete a certain number of pulses. Each spwm group has its own independent round function. As shown in the figure below, spwm group0 uses full wave mode and round is set to 64, so the waveform stops after 64 pulses are actually output.

    3.4.3. Stop mode

    The stop function can stop all spwm channels in the current spwm group immediately (without waiting for the current cycle to complete) and maintain the level at the end. Each spwm group has its own independent stop function.

    Note: It is not recommended to stop for too long, especially when the level is maintained at a high level after stopping.

    4. Hardware connection instructions

    After the PIN pin is multiplexed to PWM MODE, it is already in output state. To protect the circuit, please do not inject external voltage; at the same time, the hardware will output PWM waveform or SPWM waveform according to the register setting, and the load can be connected as needed

    5. Uboot usage introduction

    Uboot does not currently support the use of spwm

    6. Kernel usage introduction

    Driver path: kernel/drivers/sstar/pwm

    6.1. Kernel Config configuration

    The configurations you need to select when compiling the kernel are as follows:

    Device Drivers-->
        [*] SStar SoC platform drivers-->
            [*] Sigmastar PWM driver
                [*] Support SPWM function // Enable SPWM
                [ ] Support dead time generation // Invalid for SPWM
                [*] Support high precision calculation // SPWM only supports high precision mode
    

    6.2. Configuring DTS

    spwm only needs to configure the spwm-group attribute in the pwm node in the corresponding chipname.dtsi . Please refer to DTS Configuration Instructions:

    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 configuration instructions:

    Property Description Value Note
    compatible Match the driver for driver registration, must be consistent with the code "sstar,pwm" Modification prohibited
    reg Set the address of register bank / Modification prohibited
    interrupts Specify the hardware interrupt number and attributes to be used INT_IRQ_PWM_GROUPX Modification prohibited
    clocks Specify the clock source to use CLK_pwm Modification prohibited
    channel Match channel index 0~17, spwm only supports 18 channels Modification prohibited
    spwm-group Specify whether to join the spwm group If this attribute is not configured, it will not join the spwm group. If it joins the spwm group, configure 0/½/3. For details, see Correspondence between Group and channel Can be modified as needed
    clk-select Used to select clock gear pwm0~17 can be selected 0~6 corresponds to 12M/6M/3M/1.5M/750K/86M/24M pwm0~17 selection must be consistent
    status Select whether to enable PWM drive "ok" or "disable" Can be modified as needed

    6.3. PADMUX configuration

    The padmux configuration method of spwm in the kernel environment is the same as that of pwm. You only need to add the following code to the corresponding padmux.dtsi according to the selected pin:

    spwm's padmux configuration:

    <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>,
    

    The first column is the pin index number, which can be found in drivers/sstar/inlcude/{chipname}/gpio.h;

    The second column is the mode definition. In the array hal_gpio_st_padmux_info in drivers/sstar/gpio/{chipname}/hal_pinmux.c, the multiplexing relationship of all pins is listed. You can check which multiplexing functions the pin supports and query the array;

    The third column is the index name of the pin and the matching mode, which can be found in drivers/sstar/include/drv_puse.h.

    6.4. Module usage introduction

    6.4.1. sys/class/sstar/pwm

    The sys/class/sstar/pwm directory structure is as follows:

    • First level directory

      spwm group0/½/... → Synchronize operations on all spwm channels in the same spwm group

    • Second level directory

      Take spwm group0 as an example → Configure the relevant attributes of spwm group0

    spwm configuration pays attention to the order of precedence, please refer to the following steps and instructions

    Step Name Description Parameters Notes
    1 sg_config Set modulation mode and modulation index echo [spwm mode] [duty length] > sg_config Refer to Spwm modulation mode and modulation index, 0: symmetric half-wave mode, 1: half-wave mode, 2: full-wave mode
    2 sg_step Set the initial phase of all spwm channels in the group (i.e. the first output duty is located at the subscript of the duty table) echo [duty index] > sg_step You can specify the step of a channel, echo [channel id] [duty index] > sg_step
    3 sg_period Set the carrier period of spwm echo [period ns] > sg_period The spwm channel period in the same group should be synchronized
    4 sg_duty Configure spwm's duty table echo [duty index] [duty ns] > sg_duty The spwm channels in the same group share a duty table
    5 sg_enable Enable spwm echo [enable] > sg_enable 1: enable, 0: disable
    / sg_scale Set the scaling ratio of spwm duty echo [n] > sg_scale The final ratio is calculated according to n/8. No scaling is performed by default. If the scale function is enabled, this configuration needs to be set before sg_config
    / sg_clamp Set the upper and lower limits of spwm duty clamp echo [upper limit] [lower limit] > sg_clamp Unit: ns
    / sg_stop Enable spwm stop function echo [enable] > sg_stop 1: enable, 0: disable
    / sg_round Enable spwm round function echo [round number] > sg_round round number is the number of pwm cycles

    For example: All spwm channels under spwm group0 are synchronized and the output modulation mode is full wave mode, the modulation index is 64, the carrier cycle is 50KHZ, the duty cycle of the first carrier cycle is 250ns and increases by 250ns:

    1. #Jump to spwm group0 path
    2. cd sys/class/sstar/pwm/spwm_group0
    3.
    4. #Configure 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

    The header file <drv_pwm.h> is located in the /driver/sstar/pwm directory. The struct spwm_gp_cfg structure describes the spwm group attributes.

    • IOCTL_SPWM_SET_GROUP_CFG: Complete the parameter configuration of spwm group

    • IOCTL_SPWM_GET_GROUP_CFG: Get the parameter configuration of the current spwm group

    • IOCTL_SPWM_UPDATE_PERIOD: Update the carrier period of spwm

    • IOCTL_SPWM_UPDATE_DUTY: Update the duty of the spwm specified position

    • IOCTL_SPWM_GROUP_STOP: Trigger the stop function

    • IOCTL_SPWM_GROUP_ROUND: Trigger the round function

    struct spwm_gp_cfg
    {
        u8 mode; //Modulation mode
        u8 step; //Initial phase
        u8 index; //Subscript used when updating duty
        u8 scale; //duty scaling ratio n, calculated as n/8
        u32 group; //spwm group id
        u8 enable; //1:enable
        u64 period; //spwm carrier period, in ns
        u8 stop_en; //Enable stop function
        u8 clamp_en; //Enable duty clamp function
        u8 duty_len; //length used by duty table
        u64 duty[64]; //duty table settings, up to 64
        u64 clamp_max; //Clamp upper limit
        u64 clamp_min; //Clamp lower limit
        u32 round_num; //PWM cycle number, round=64, the actual output is 64 pulses
    };
    
    #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

    Reference code drv_spwm_test.c is located at directory /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; //Can choose to stop after outputting only 64 pulses
            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 interfaces do not exist

    1. Check if the status of the DTS PWM node is ok

    2. Check DTS PWM node spwm-group property, if not configured, no SPWM interface will be generated

    3. Check DTS PWM node group property, if configured, SPWM interface will not be generated

    4. Check if kernel config is configured, see [Kernel Config Configuration]

    Q2:No SPWM waveform is generated after configuration

    1. First confirm whether the measured pin is correct: open the corresponding schematic diagram to confirm. If there is no error, proceed to the next step.

    2. Confirm whether the corresponding PWM mode is effective. There are two main reasons for pin multiplexing failure:

      Reason 1: The pin is not set to PWM mode. For details on how to set it, see PADMUX Configuration

      Reason 2: A padmux mode with a higher priority than PWM mode is enabled. You can enable the padmux readback mechanism CONFIG during compilation: CONFIG_MSYS_PADMUX. Its function is to confirm whether the padmux used has been successfully set. After enabling it, enter the following command in user space to check:

      echo PAD_PWM0 PINMUX_FOR_PWM0_MODE_1 > /sys/class/sstar/msys/mux_verify
      
      cat /sys/class/sstar/msys/mux_verify
      
    3. Check whether the relevant parameters are set successfully

      Taking spwm group0 as an example, enter the following command to view the parameters:

      At the same time, you need to pay attention to the configuration order, see SPWM configuration order for details