wujian100 的 PWM 周期问题

wujian100 Edwin ⋅ 于 2020-07-28 11:02:50 ⋅ 277 阅读

1、pwm波形发生器实验结果

在wujian100的样例程序代码如下:

int32_t  pwm_signal_test(uint32_t pwm_idx, uint8_t pwm_ch)
{
    int32_t ret;
    pwm_handle_t pwm_handle;

    example_pin_pwm_init();

    pwm_handle = csi_pwm_initialize(pwm_idx);

    if (pwm_handle == NULL) {
        printf("csi_pwm_initialize error\n");
        return -1;
    }

    ret = csi_pwm_config(pwm_handle, pwm_ch, 3000, 1500); //设置pwm周期3ms

    if (ret < 0) {
        printf("csi_pwm_config error\n");
        return -1;
    }

    csi_pwm_start(pwm_handle, pwm_ch);
    mdelay(20);

    ret = csi_pwm_config(pwm_handle, pwm_ch, 200, 150); //设置pwm周期200us

    if (ret < 0) {
        printf("csi_pwm_config error\n");
        return -1;
    }

    mdelay(20);
    csi_pwm_stop(pwm_handle, pwm_ch);

    csi_pwm_uninitialize(pwm_handle);

    return 0;

}

其中有两句配置函数

ret = csi_pwm_config(pwm_handle, pwm_ch, 3000, 1500); //设置pwm周期3ms,占空比1500/3000
ret = csi_pwm_config(pwm_handle, pwm_ch, 200, 150); //设置pwm周期200us占空比50/200

这两句都是用来配置pwm周期的,是实际测试中pwm周期和占空比都没有问题。但是如果设置pwm周期为5ms时,需要修改这句话为:

ret = csi_pwm_config(pwm_handle, pwm_ch, 5000, 2500);

在实际测试过程中会发现,他的周期实际是2.5ms并不是预期的5ms,如果设置为100ms会发现pwm的实际输出周期是2.5ms。实际输出是有问题的。

2、查找问题

1、软件问题:

跟踪csi_pwm_config 函数,发现在函数中会根据设置的周期进行时钟配置:
drv_pwm_config_clockdiv(handle, channel, cnt_div[count_div]);由于pwm计数器是16位,所以软件会根据设置的周期值计算出要配置的数值,如果该数值超过0xffff,就会设置分频系数,直到需要计数的值小于0xffff为止。

继续跟踪
drv_pwm_config_clockdiv(handle, channel, cnt_div[count_div]);
我们可以看到

void drv_pwm_config_clockdiv(pwm_handle_t handle, uint8_t channel, uint32_t div)
{
    PWM_NULL_PARAM_CHK_NORETVAL(handle);

    wj_pwm_priv_t *pwm_priv = handle;
    wj_pwm_reg_t *addr = (wj_pwm_reg_t *)(pwm_priv->base);
    addr->PWMCFG &= ~(7 << 24);

    switch (div) {
        case 1:
            addr->PWMCFG &= ~(PWM_CFG_CNTDIV_EN);
            break;

        case 2:
            addr->PWMCFG |= PWM_CFG_CNTDIV_EN | PWM_CFG_CNTDIV_2;
            break;

        case 5:
            addr->PWMCFG |= PWM_CFG_CNTDIV_EN | PWM_CFG_CNTDIV_5;
            break;
    ........

这个函数会对寄存器PWMCFG的第25位到第27位进行了赋值操作,查找数据手册发现

file

第28位是分频使能,第26到24位是分频系数设置。

在csi库里提供了一个函数可以读取这几位的值来查看分频的设置:

/**
  \brief       get pwm clock division.
  \param[in]   handle   pwm handle to operate.
  \param[in]   channel  channel num.
  \return      div      clock div.
*/
uint33_t drv_pwm_get_clockdiv(pwm_handle_t handle, uint8_t channel)

通过这个函数可以得到PWMCFG寄存器里的分频系数cntdiv的值。通过实验我们发现分频系数的值配置到寄存器里了,并且读回来的值也是配置的值。

总结:所以软件上对于pwm的配置是没有问题的,配置pwm周期不是预期值不是软件问题。

3、硬件问题

排除软件问题,那么出现pwm周期非预期值他的问题就只可能是pwm外设在设计时的硬件问题了。我们查找wujian100内部设计的问题,为了方便查看我们使用verdi来查看跟踪模块设计,这样效率高。

首先第一步配置wujian100工作路径:在linux系统中利用source将wujian100工作目录添加到系统环境变量。

第二步tb目录下的tb_file.list文件,这个文件里加载的顶层文件是wujian100_open_top.v,并不是我们生成bit文件时的wujian100_open_fpga_top.v文件(该文件在fpga目录下),我们首先赋值fpga目录下的wujian100_open_fpga_top.v到soc目录下。修改文件tb_file.list里的第3行,将wujian100_open_top.v替换为wujian100_open_fpga_top.v

第三步进入tb目录。使用verdi -f tb_file.list打开软件verdi并加载tb_file.list里列出的文件。(前提是你的linux系统安装了verdi软件)。

以上三步正确就会打开wujian100设计的模块图。

file

查看选中打开该文件,查看文件名是不是wujian100_open_fpga_top.v

file

然后打开原理图。点击如图按钮:

file)

打开wujian100的设计如图:

file

接下来可以按文件查找相应模块,也可以双击原理图上的模块一层一层进入。

pwm模块位于PDU下的ahb1上。一层一层进入查看下,定位到pwm:

file

继续进入pwm_sec_top,再进入pwm就是pwm外设的内部了。

file

在这里有两部分,一个是aphif负责总线,ctrl就是pwm的实际实现了。
进入pwm_ctrl:

file

我们发现有六个pwm_gen。和数据手册上介绍的一样。

file

我们现在定位到左下角,放大看,这部分就是pwm的时钟部分:

file
我们在view菜单下打开端口名称显示和模块内部端口名称显示。在图上我们可以看到cntdiv[3:0]控制了分频系数,分频系数会通过分频器(图上的f)对pclk系统时钟进行分频,然后通过gated_clk_cell控制时钟的通过然后通过clk_mux2选择器送到后续的pwm发生器上作为发生器的时钟。我们双击模块就能够查看各自对应的verilog代码。我们在逐个查找时发现gated_clk_cell(如下图)它的结构有问题:

file

双击它,查看它内部结构:

file

如图这样一个很奇怪的结构,直接就是clk_in输入,直接送到clk_out输出上去了。结合上一张图我们可以看出clk_in就是pclk系统时钟,这样导致前面的分频没有任何作用,pclk将会直接送到pwm发生器上作为pwm的时钟。这样不论你有没有设置分频,pwm就只有系统时钟pclk(21M)。这样pwm的信号周期无法修改。

反过来推到下,我们之前的实验设置周期6ms是得到的结果是2.5ms,其实我们的分频系数是2,由于时钟没有分频,导致我们的输出周期是2.5ms,如果分频成功我们的周期就会是2.5*2=5ms,同理,10ms时的分频系数是4。有兴趣的可以根据sdk提供的代码推到下,也可以用

uint33_t drv_pwm_get_clockdiv(pwm_handle_t handle, uint8_t channel)

这个函数查看分频系数,然后分析下。

我们直到了问题出在模块gated_clk_cell上了,我们定位到模块对应的verilog文件,查找到代码位于pwm.v文件的4186行例化了一个叫做gated_clk_cell的模块,我们双击gated_clk_cell进入内部,双击模块定位到了common.v文件的66行,代码如下:

`ifdef FPGA
assign clk_out = clk_in;
`else
Standard_Cell_CLK_GATE x_gated_clk_cell(
             .CK  (clk_in),
             .SE  (SE),
             .EN   (clk_en_bf_latch),
             .Q   (clk_out)
             );
`endif

这下一目了然了,由于定义了FPGA这个量,导致assign clk_out=clk_in;而下面的模块没有实现,所以最主要原因就是定义了FPGA这个量,这个量在哪里定义的呢,就是在wujian100_open_fpga_top.v这个文件的最开头定义的(第37行):

file

将这行用//注释,然后在verdi中点击file下选择reload设计,重新加载文件,我们再查看geted_clk_cell模块,得到如下图:

file

这样clk_in不会直接送到clk_out输出了。

修改后用vivado重新分析综合生成bit,下载到开发板上,我们发现输出的pwm的周期输出正确了。6ms,10ms的周期也能生成了。

这里有一点注意,修改后源码用vivado建立工程,用vivado去Synthesis,不要用官方方法,用Synplify_pro去Synthesis。不然生成的bit下载开发板,输出的pwm周期会小一半。例如10ms周期只能输出5ms。有兴趣的可以试下。

4、后续问题:

查看这张结构图

file

仔细查看,不难发现,它的六个pwm_gen都是用的一个时钟源,都是pclk通过分频之后的时钟直接连接在了每一个pwn_gen的时钟上,六个时钟都是一个源,那么就会造成一个问题,他的六组pwm发生器只能同时产生一个频率(周期)的信号,例如ch1产生了5ms,那个这时ch2也只能产生5ms,没法产生10ms周期的pwm,所有通道的周期都会被最后那个设置改成同一个频率,就是因为他们的时钟是同一个。:cry: :worried:。这将怎么解决,有一个思路,就是每一个pwm_gen有各自的分频模块。怎么解决下一篇介绍。

有问题欢迎留言指出,一起讨论。

本帖已被设为精华帖!
本帖由 YX 于 2月前 加精
成为第一个点赞的人吧 :bowtie:
回复数量: 0
    暂无评论~~
    • 请注意单词拼写,以及中英文排版,参考此页
    • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`, 更多语法请见这里 Markdown 语法
    • 支持表情,使用方法请见 Emoji 自动补全来咯,可用的 Emoji 请见 :metal: :point_right: Emoji 列表 :star: :sparkles:
    • 上传图片, 支持拖拽和剪切板黏贴上传, 格式限制 - jpg, png, gif
    • 发布框支持本地存储功能,会在内容变更时保存,「提交」按钮点击时清空
    Ctrl+Enter