PWM使用参考


REVISION HISTORY

Revision No.
Description
Date
1.0
  • Initial release
  • 04/25/2024

    1. 概述

    PWM (Pulse Width Modulation) 模块用于产生脉宽波形,可通过改变频率和占空比来改变输出的电流、电压进而实现控制电机转速、液晶屏调光等功能;SigmaStar的PWM模块同时具备Group功能(细分为sync 、round、hold、stop),即每4个channel的PWM可绑定成一组,实现4路PWM同步产生或停止。

    2. 功能描述

    • 支持PWM的channel数量为11个,支持加入各自的Group

    • 支持的OSCCLK(Hz)为:86M、12M、6M、3M、1.5M、1M、32K、8K

    • 频率的工作范围是(OSCCLK/2)到(OSCCLK/2^34) ,例如OSCCLK = 12MHz,则范围是(0.0007Hz~6MHz);

    • 支持double buffer来防止产生错误波形,即波形更新会等待当前波形完全生成之后;

    • 支持sync功能,即Group内的PWM在同一个时刻产生波形,一般一组Group中会包含4个channel的PWM

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

    • 支持hold功能,即同Group内的PWM会在完成当前周期的波形后暂停,常用于改变波形的设定,可设定暂停时间内的高低电平

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

    3. 相关概念说明

    3.1. 频率和占空比

    • 频率(frequency)

      每秒钟信号从高电平到低电平再回到高电平的次数。

    • 占空比(duty cycle)

      高电平持续时间和低电平持续时间之间的比例。

    • 举例说明

      假设PWM的OSCCLK频率为12M,那么可设置的频率范围为:0.0007Hz~6MHz;

      设定PWM0和PWM1为频率120Hz,占空比25%的波形,且PWM1相对于PWM0有180°的相位偏移,那么各参数配置如下:

      period duty shift
      PWM0 120Hz 25% 0%
      PWM1 120Hz 75% 50%

      产生的波形如下图:

    3.2. 普通精度模式

    kernel config中不开启CONFIG_SSTAR_PWM_EXTEND(Support high precision calculation),则使用普通精度模式,该模式下配置PWM的period的单位为Hz,duty cycle的单位为百分比。

    假设要设置PWM0频率为10000HZ,占空比为50%,则:

    period = 10000

    duty cycle = 50

    以sys/class/pwm/下的接口举例,配置方法如下:

        # 跳转到PWM控制目录
        cd sys/class/pwm/pwmchip0/
    
        # 创建PWM0节点
        echo 0 > export
    
        # 进入PWM0控制目录
        cd pwm0
    
        # 设置频率为10000Hz
        echo 10000 > preiod
    
        # 设置占空比为50%
        echo 50 > duty_cycle
    
        # 设置极性为反向(不设置反向为echo normal > polarity)
        echo inversed > polarity
    
        # 使能PWM0
        echo 1 > enable
    

    3.3. 高精度模式

    kernel config中开启CONFIG_SSTAR_PWM_EXTEND(Support high precision calculation)选项时,则使用高精度模式,该模式下配置PWM的period和duty_cycle均使用纳秒为单位,所以要先计算周期和占空比的值。

    假设要设置PWM0频率为10000.5HZ,占空比为49.5%,则:

    period = 10^9 ÷ 10000.5 = 99995

    duty cycle = 99995 * 49.5% = 49498

    以sys/class/pwm/下的接口举例,配置方法如下:

        # 跳转到PWM控制路径
        cd sys/class/pwm/pwmchip0/
    
        # 创建PWM0节点
        echo 0 > export
    
        # 进入PWM0控制路径
        cd pwm0
    
        # 设置频率为10000.5Hz
        echo 99995> period
    
        # 设置占空比为49.5%
        echo 49498> duty_cycle
    
        # 使能PWM0
        echo 1 >enable
    

    由此可见,高精度模式的优势在于参数设定支持到小数。

    3.4. GROUP相关概念

    3.4.1. Sync mode

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

    Group Group Member
    Group0 PWM0、PWM1、PWM2、PWM3
    Group1 PWM4、PWM5、PWM6、PWM7
    Group2 PWM8、PWM9、PWM10

    可通过以下方式选择是否将pwm加入到Group中:

    方式一:各channel DTS配置group属性

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

    以channel 0 举例,DTS配置group = <0>,选择加入group0

    channel 0的目录在/sys/class/sstar/pwm/group0/目录下:

    以channel 1 举例,DTS不配置group属性,选择不加入group0各channel的目录

    channel 1在/sys/class/sstar/pwm目录下:

    方式二:/sys/class/sstar/group下使用join接口,echo [channel] [enable] > join

    加入: echo 0 1 > join

    退出: echo 0 0 > join

    注意:方式一中DTS不配置group属性,就无法在方式二自由加入group

    3.4.2. Hold mode

    Group的Hold功能会在pwm完成当前周期的波形后停止,并触发中断,此时可以改变各channel波形的配置以保持同步,完成修改后会关闭hold 功能,pwm就会重新产生新的波形,每组group都有自己独立的hold功能。

    举例:高精度模式下,group0下的所有channel已经输出100HZ,50%占空比,极性normal的波形,在此基础上将周期修改成1000HZ。

        #跳转到group路径
        cd sys/class/sstar/pwm/group0
    
        #设置group0下所有channal输出100HZ,50%占空比,极性normal的波形
        echo 10000000  > g_period
        echo 7500000   > g_duty
        echo 2500000   > g_shift
        echo 0         > g_polarity
        echo 1         > update // 触发hold mode
        echo 1         > g_enable
    
        #将周期修改为1000HZ
        echo 1000000 > g_period
        ehco 1       > update // 触发hold mode
    

    3.4.3. Round mode

    round功能会在同group内的所有channel完成一定数量的脉冲后停止,每组group都有自己独立的round功能。

    举例:group0下的所有channel已经输出1000HZ,50%占空比,极性normal的波形,在此基础上分别触发100,200,300个脉冲后停止。

        #跳转到group路径
        cd sys/class/sstar/pwm/group0
    
        #设置group0下所以channal输出1000HZ,50%占空比,极性normal的波形
        echo 1000000 > g_period
        echo 750000 > g_duty
        echo 250000 > g_shift
        echo 0      > g_polarity
        echo 1      > update
        echo 1      > g_enable
    
        #触发100个脉冲
        echo 100 > round
    
        #触发200个脉冲
        echo 200 > round
    
        #触发300个脉冲
        echo 300 > round
    

    3.4.4. Stop mode

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

    举例:使用stop暂停一段时间后再恢复波形

        #跳转到group路径
        cd sys/class/sstar/pwm/group0
    
        #设置group0下所以channel输出1000HZ,50%占空比,极性normal的波形
        echo 1000000 > g_period
        echo 750000  > g_duty
        echo 250000  > g_shift
        echo 0       > g_polarity
        echo 1       > update
        echo 1       > g_enable
    
        #急停波形
        echo 1 > stop
    
        #恢复波形
        echo 0 > stop
    

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

    4.PADMUX配置

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

    <PAD_PWM0     PINMUX_FOR_PWM0_MODE_1    MDRV_PUSE_PWM0 >;
    <PAD_PWM1     PINMUX_FOR_PWM1_MODE_1    MDRV_PUSE_PWM1 >;
    <PAD_GPIO2    PINMUX_FOR_PWM2_MODE_4    MDRV_PUSE_PWM2 >;
    <PAD_GPIO3    PINMUX_FOR_PWM3_MODE_4    MDRV_PUSE_PWM3 >;
    <PAD_GPIO4    PINMUX_FOR_PWM4_MODE_3    MDRV_PUSE_PWM4 >;
    <PAD_GPIO5    PINMUX_FOR_PWM5_MODE_3    MDRV_PUSE_PWM5 >;
    <PAD_GPIO6    PINMUX_FOR_PWM6_MODE_3    MDRV_PUSE_PWM6 >;
    <PAD_GPIO7    PINMUX_FOR_PWM7_MODE_3    MDRV_PUSE_PWM7 >;
    <PAD_GPIO0    PINMUX_FOR_PWM8_MODE_1    MDRV_PUSE_PWM8 >;
    <PAD_GPIO14   PINMUX_FOR_PWM9_MODE_3    MDRV_PUSE_PWM9 >;
    <PAD_GPIO15   PINMUX_FOR_PWM10_MODE_3   MDRV_PUSE_PWM10>;
    

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

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

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

    5. Uboot用法介绍

    5.1. Uboot Config配置

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

    SigmaStar drivers->
    
        <*> SigmaStar PWM->
    
            <*> Support high precision calculation // 切换普通精度与高精度模式
    

    5.2. 配置DTS

    PWM的DTS配置只需要在对应的chipname.dtsi中配置如下信息(可以根据需求选择性配置PWM channel的数量,最多可以同时配置11个channel):

    pwm0: pwm@0x1F203200{
        compatible = "sstar,pwm";
        reg = <0x1F203200 0x37>;
        channel = <0>;
        clock-freq = <12000000>;
        status = "okay";
    };
    

    PWM DTS配置说明:

    属性 描述 设定值 备注
    compatible 匹配驱动进行驱动注册 "sstar,pwm" 禁止修改
    reg 设定寄存器bank地址 / 禁止修改
    channel 匹配channel index 0~10 不需要更改
    clock-freq 设定OSCCLK 详见支持的OSCCLK 可根据需要修改
    status 选择是否使能PWM驱动 "ok" or "disable" 可根据需要修改

    5.3. Uboot cmd参数说明

    1. 配置PWM 频率及占空比:

      pwm config <pwm_dev_num> <channel> <period_ns> <duty_ns> - config PWM
      
      pwm_dev_num: 输入0~13选择想要配置的channel
      
      channel: 输入0~13选择想要配置的channel
      
      period_ns: 输入频率
      
      duty_ns: 输入占空
      
    2. 配置PWM极性:

      pwm invert <pwm_dev_num> <channel> <polarity> - invert polarity
      
      pwm_dev_num: 输入0~13选择想要配置的channel
      
      channel: 输入0~13选择想要配置的channel
      
      polarity: 输入0表示极性正常,输入1表示极性相反
      
    3. enbale PWM:

      pwm enable <pwm_dev_num> <channel> - enable PWM output
      
      pwm_dev_num: 输入0~13选择想要配置的channel
      
      channel: 输入0~13选择想要配置的channel
      
    4. disable PWM:

      pwm disable <pwm_dev_num> <channel> - eisable PWM output
      
      pwm_dev_num: 输入0~13选择想要配置的channel
      
      channel: 输入0~13选择想要配置的channel
      

    5.4. Uboot cmd使用实例截图

    6. Kernel用法介绍

    6.1. Kernel Config配置

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

    Device Drivers-->
        [*] SStar SoC platform drivers-->
            [*] Sigmastar PWM driver
                [*] Support high precision calculation // 切换普通精度与高精度模式
    

    6.2. 配置DTS

    PWM的dts配置只需要在对应的chipname.dtsi中配置如下信息(可以根据需求选择性配置PWM channel的数量,最多可以同时配置11个channel):

        pwm0: pwm@0x1F003400{
            compatible = "sstar,pwm";
            reg = <0x1F003400 0x37>;
            #pwm-cells = <3>;
            channel = <0>;
            group = <0>;
            clocks = <&CLK_pm_pwm>;
            clk-select = <0>;
            interrupts=<GIC_SPI INT_IRQ_PWM_INT_ALL IRQ_TYPE_LEVEL_HIGH>;
            status = "ok";
        };
        .......
    
        pwm9: pwm@0x1F003880 {
            compatible = "sstar,pwm";
            reg = <0x1F003880 0x37>;
            #pwm-cells = <3>;
            channel = <9>;
            group = <2>;
            clocks = <&CLK_pm_pwm>;
            clk-select = <0>;
            interrupts=<GIC_SPI INT_IRQ_PWM_INT_ALL IRQ_TYPE_LEVEL_HIGH>;
            status = "ok";
        };
    
        pwm10: pwm@0x1F003900 {
            compatible = "sstar,pwm";
            reg = <0x1F003900 0x37>;
            #pwm-cells = <3>;
            channel = <10>;
            group = <2>;
            clocks = <&CLK_pm_pwm>;
            clk-select = <0>;
            interrupts=<GIC_SPI INT_IRQ_PWM_INT_ALL IRQ_TYPE_LEVEL_HIGH>;
            status = "ok";
        };
    

    PWM DTS配置说明:

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

    6.3. 模块使用介绍

    6.3.1. sys/class/pwm

    DTS配置N个pwm channel节点,sys/class/pwm目录下对应生成N个pwmchip0,在pwmchip0目录下执行echo 0 > export,即可生成对应的pwm0目录,(由于每个pwmchip0下的npwm数值均为1,因此export的传参参数只能是0),之后可在pwm0目录下配置pwm相关属性:period、duty_cycle、polarity、enable,详见3.2. 普通精度模式或者3.3. 高精度模式

    6.3.2. sys/class/sstar/pwm

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

    • 一级目录

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

      pwm1 → 对未加入group内的channel单独操作

    • 二级目录

      group0 → 对同group内的所有channel同时设定period/shift/duty/polarity/enable,并触发hold/stop/round功能

    • 三级目录

      pwm1 → 对同group内的某一个channel进行操作,可单独设定period/shift/duty/polarity/enable

    sys/class/sstar/pwm接口,在二级目录group下可针对所有属于group的channel进行统一设置,也可在该目录下独立设置各channel的属性;在三级目录下可独立设置各channel的属性,二级目录与三级目录设置channel的属性区别在于:二级目录下独立设置channel的属性后,需要echo 1 > update触发hold功能去更新参数,三级目录下独立设置channel的属性后,会立即生效

    举例:group0下的所有channel同步且输出100HZ,50%占空比,极性normal的波形,然后将channel0的period修改为200HZ

        #跳转到group路径
        cd sys/class/sstar/pwm/group0
    
        #设置group0下所以channal输出100HZ,50%占空比,极性normal的波形
        echo 10000000  > g_period //group内所有channel的period都设为100HZ
        echo 7500000   > g_duty
        echo 2500000   > g_shift //占空比为(7500000-2500000)/ 10000000 = 50%
        echo 0         > g_polarity
        echo 1         > update //触发hold mode
        echo 1         > g_enable
    
        #修改channel0的period为200HZ,方法一:
        echo 0 20000000 > g_period  //group内只设定channel 0的period为100HZ
        echo 1          > update    //触发hold更新波形,仍然同步
    
        #修改channel0的period为200HZ,方法二:
        cd /sys/class/sstar/pwm/group0/pwm0
        echo 20000000 > period
    

    6.3.3. ioctl

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

    • IOCTL_PWM_SET_CHAN_CFG:完成channel的参数配置

    • IOCTL_PWM_GET_CHAN_CFG:获取当前channel的参数配置

    • IOCTL_PWM_SET_GROUP_CFG:完成group的参数配置

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

    • IOCTL_PWM_GROUP_STOP:触发stop功能

    • IOCTL_PWM_GROUP_ROUND:触发round功能

      #ifndef __DRV_PWM_H__
      #define __DRV_PWM_H__
      
      #include <cam_os_wrapper.h>
      
      struct pwm_ch_cfg
      {
          u64 duty;     //设定占空比,实际占空比=duty-shift
          u64 shift;    //设定起始相位
          u64 period;   //设定周期
          u8  enable;   //波形使能
          u32 channel;  //指定channel
          u32 polarity; //极性设置:0-正常,1-极性取反
      
      #ifdef CONFIG_SSTAR_PWM_DDT //ifado不支持
          u64 p_ddt;   //正向波形的死区时间,以ns为单位
          u64 n_ddt;   //反向波形的死区时间,以ns为单位
          u8  ddt_en;  //死区时间使能
      #endif
      };
      
      struct pwm_gp_cfg
      {
          u64 duty;
          u64 shift;
          u32 group;
          u64 period;
          u8  enable;
          u8  stop_en;   //stop功能使能
          u32 polarity;
          u32 round_num; //设定round mode下的脉冲数量
      };
      
      #define PWM_IOC_MAXNR 6
      
      #define IOCTL_PWM_SET_CHAN_CFG_NR  (0)
      #define IOCTL_PWM_GET_CHAN_CFG_NR  (2)
      #define IOCTL_PWM_SET_GROUP_CFG_NR (1)
      #define IOCTL_PWM_GET_GROUP_CFG_NR (3)
      #define IOCTL_PWM_GROUP_STOP_NR    (4)
      #define IOCTL_PWM_GROUP_ROUND_NR   (5)
      
      #define PWM_IOC_MAGIC           'p'
      #define IOCTL_PWM_SET_CHAN_CFG  _IO(PWM_IOC_MAGIC, IOCTL_PWM_SET_CHAN_CFG_NR)
      #define IOCTL_PWM_GET_CHAN_CFG  _IO(PWM_IOC_MAGIC, IOCTL_PWM_GET_CHAN_CFG_NR)
      #define IOCTL_PWM_SET_GROUP_CFG _IO(PWM_IOC_MAGIC, IOCTL_PWM_SET_GROUP_CFG_NR)
      #define IOCTL_PWM_GET_GROUP_CFG _IO(PWM_IOC_MAGIC, IOCTL_PWM_GET_GROUP_CFG_NR)
      #define IOCTL_PWM_GROUP_STOP    _IO(PWM_IOC_MAGIC, IOCTL_PWM_GROUP_STOP_NR)
      #define IOCTL_PWM_GROUP_ROUND   _IO(PWM_IOC_MAGIC, IOCTL_PWM_GROUP_ROUND_NR)
          ......
      
      #endif
      

    Sample code:

        #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)
        {
            int  ret    = 0;
            int  pwm_fd = -1;
            char path_name[24];
    
            struct pwm_ch_cfg pwm_channel;
            struct pwm_gp_cfg pwm_group;
    
            if (argc != 4)
            {
                printf("format: ut_pwm <channel> [channel_id] [enable]\n");
                printf("format: ut_pwm <group>   [group_id]   [enable]\n");
                printf("format: ut_pwm <stop>    [group_id]   [enable]\n");
                printf("format: ut_pwm <round>   [group_id]   [round_num]\n");
                printf("format: ut_pwm <ddt>     [channel_id] [dead_time]\n");
                return -1;
            }
    
            if (!strcmp(argv[1], "channel"))
            {
                pwm_channel.period   = 1000000;
                pwm_channel.duty     = 500000;
                pwm_channel.shift    = 200000;
                pwm_channel.polarity = 1;
                pwm_channel.channel  = atoi(argv[2]);
                pwm_channel.enable   = atoi(argv[3]);
    
                snprintf(path_name, sizeof(path_name), "/dev/pwm%u", pwm_channel.channel);
                pwm_fd = open((const char *)(char *)path_name, O_RDWR);
                if (pwm_fd < 0)
                {
                    printf("open /dev/pwm%u fail errno:[%d]\n", pwm_channel.channel, pwm_fd);
                    return -1;
                }
    
                ret = ioctl(pwm_fd, IOCTL_PWM_SET_CHAN_CFG, &pwm_channel);
                if (ret < 0)
                {
                    printf("pwm channel[%u] set config fail\n", pwm_channel.channel);
                    return ret;
                }
    
                ret = ioctl(pwm_fd, IOCTL_PWM_GET_CHAN_CFG, &pwm_channel);
                if (ret < 0)
                {
                    printf("pwm channel[%u] get config fail\n", pwm_channel.channel);
                    return ret;
                }
    
                usleep(500000);
                pwm_channel.duty = 700000;
                ret              = ioctl(pwm_fd, IOCTL_PWM_SET_CHAN_CFG, &pwm_channel);
                if (ret < 0)
                {
                    printf("pwm channel[%u] set config again fail\n", pwm_channel.channel);
                    return ret;
                }
            }
            else if (!strcmp(argv[1], "group"))
            {
                pwm_group.period   = 1000000;
                pwm_group.duty     = 500000;
                pwm_group.shift    = 200000;
                pwm_group.polarity = 0;
                pwm_group.group    = atoi(argv[2]);
                pwm_group.enable   = atoi(argv[3]);
    
                snprintf(path_name, sizeof(path_name), "/dev/pwm_group%u", pwm_group.group);
                pwm_fd = open((const char *)(char *)path_name, O_RDWR);
                if (pwm_fd < 0)
                {
                    printf("open /dev/pwm-group%u fail errno:[%d]\n", pwm_group.group, pwm_fd);
                    return -1;
                }
    
                ret = ioctl(pwm_fd, IOCTL_PWM_SET_GROUP_CFG, &pwm_group);
                if (ret < 0)
                {
                    printf("pwm group[%u] set config fail\n", pwm_group.group);
                    return ret;
                }
    
                ret = ioctl(pwm_fd, IOCTL_PWM_GET_GROUP_CFG, &pwm_group);
                if (ret < 0)
                {
                    printf("pwm group[%u] get config fail\n", pwm_group.group);
                    return ret;
                }
    
                pwm_group.duty = 800000;
                usleep(500000);
                ret = ioctl(pwm_fd, IOCTL_PWM_SET_GROUP_CFG, &pwm_group);
                if (ret < 0)
                {
                    printf("pwm group[%u] set config again fail\n", pwm_group.group);
                    return ret;
                }
            }
            else if (!strcmp(argv[1], "stop"))
            {
                pwm_group.group = atoi(argv[2]);
                snprintf(path_name, sizeof(path_name), "/dev/pwm_group%u", pwm_group.group);
                pwm_fd = open((const char *)(char *)path_name, O_RDWR);
                if (pwm_fd < 0)
                {
                    printf("open /dev/pwm-group%u fail errno:[%d]\n", pwm_group.group, pwm_fd);
                    return -1;
                }
    
                pwm_group.stop_en = atoi(argv[3]);
                ret               = ioctl(pwm_fd, IOCTL_PWM_GROUP_STOP, &pwm_group);
                if (ret < 0)
                {
                    printf("pwm group[%u] stop config fail\n", pwm_group.group);
                    return ret;
                }
            }
            else if (!strcmp(argv[1], "round"))
            {
                pwm_group.group = atoi(argv[2]);
                snprintf(path_name, sizeof(path_name), "/dev/pwm_group%u", pwm_group.group);
                pwm_fd = open((const char *)(char *)path_name, O_RDWR);
                if (pwm_fd < 0)
                {
                    printf("open /dev/pwm-group%u fail errno:[%d]\n", pwm_group.group, pwm_fd);
                    return -1;
                }
                pwm_group.round_num = atoi(argv[3]);
                ret                 = ioctl(pwm_fd, IOCTL_PWM_GROUP_ROUND, &pwm_group);
                if (ret < 0)
                {
                    printf("pwm group[%u] round config fail\n", pwm_group.group);
                    return ret;
                }
            }
        #ifdef CONFIG_SSTAR_PWM_DDT
            else if (!strcmp(argv[1], "ddt"))
            {
                pwm_channel.channel = atoi(argv[2]);
                snprintf(path_name, sizeof(path_name), "/dev/pwm%u", pwm_channel.channel);
                pwm_fd = open((const char *)(char *)path_name, O_RDWR);
                if (pwm_fd < 0)
                {
                    printf("open /dev/pwm%u fail errno:[%d]\n", pwm_channel.channel, pwm_fd);
                    return -1;
                }
    
                ret = ioctl(pwm_fd, IOCTL_PWM_GET_CHAN_CFG, &pwm_channel);
                if (ret < 0)
                {
                    printf("pwm channel[%u] get config fail\n", pwm_channel.channel);
                    return ret;
                }
    
                pwm_channel.p_ddt  = atoi(argv[3]);
                pwm_channel.n_ddt  = atoi(argv[3]);
                pwm_channel.ddt_en = 1;
    
                ret = ioctl(pwm_fd, IOCTL_PWM_SET_CHAN_CFG, &pwm_channel);
                if (ret < 0)
                {
                    printf("pwm channel[%u] set ddt config fail\n", pwm_channel.channel);
                    return ret;
                }
            }
        #endif
            else
            {
                printf("erro pwm command\n");
            }
    
            close(pwm_fd);
    
            return 0;
        }
    

    6.4 Debug方法

    6.4.1 PWM各接口不存在

    6.4.2 配置后PWM无波形产生

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

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

    原因一:该引脚没有设置为PWM mode,设置方法详见4.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
    

    可以从log的提示得知:若要将PAD_PWM0设为PWM0_MODE_1,需要将Bank:0x103C,Offset:0x68的bit[10:8]写0;将Bank:0x103C,Offset:0x6D的bit[6:4]写1。

    Step3:检查相关参数是否设置成功

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

    cd /sys/class/sstar/pwm/group0/
    
    cat g_info
    

    如下图所示,channel 0 和channel 1频率和占空比设置成功,channel 0 enable是在正常输出波形,channel 1 disable,无波形产生,而channel 2 和 channel 3没有配置频率和占空比。