当前位置: 首页 > news >正文

可配置的PWM外设模块

🔧 可配置的PWM外设模块

基于FPGA的PWM信号发生器,支持 动态周期与占空比配置,无需外部控制信号,适用于 LED 呼吸灯、舵机控制、电机驱动等场景。

仿真波形

参数修改后会晚一个pwm周期才生效
在这里插入图片描述


📌 模块功能

  • 🧮 支持以微秒为单位动态设置周期 i_period_us
  • 💡 支持以微秒为单位动态设置高电平时间 i_high_us
  • 🧠 自动检测参数变化,内部锁存、乘法,仅更新一次

⚙️ 参数定义

名称默认值说明
P_CLK_FREQ_MHZ50时钟频率(单位 MHz)
P_COUNTER_WIDTH 32位宽
P_DEFAULT_PERIOD_US1000默认周期(1ms)
P_DEFAULT_HIGH_US500默认高电平时间(0.5ms)

🧷 接口定义

信号名方向位宽说明
i_clkin1 bit系统时钟
i_rst_nin1 bit异步复位(启停PWM)
i_period_usin32 bitPWM 周期(单位:微秒)
i_high_usin32 bit高电平时间(单位:微秒)
o_pwm_outout1 bitPWM 输出信号
o_param_err out1 bit参数错误标志

⚠️ 使用注意事项

  1. 参数更新顺序要求:

    • ⚠️ 在动态修改 PWM 参数时,务必先更新 i_period_us,再更新 i_high_us,并保持一个pwm周期。
    • 原因是模块在每个pwm周期结束时检测参数变更并更新内部寄存器。
    • 若先设置高电平时间,可能导致短暂出现 i_high_us > i_period_us 将导致该周期输出恒为高电平,可能不是你想要的占空比
  2. 参数变化生效时机:

    • 模块在pwm周期结束时检测到参数变更后立即生效,无需显式控制信号。
    • 建议在两个周期之间或计数归零时刻修改参数,以避免中途跳变导致输出毛刺。
  3. 默认输出行为:

    • 若输入未初始化或刚上电,模块内部默认配置:
      • i_period_us = 1000 → 周期 1ms
      • i_high_us = 500 → 高电平 0.5ms(50% 占空比)
  4. 停止输出:

    • i_rst_n 拉低,可立即停止 PWM 输出并复位内部状态。
    • 拉高 i_rst_n,PWM 将按当前配置重新开始输出。

pwm.v

module pwm #(parameter P_CLK_FREQ_MHZ      = 50,    // 输入时钟频率(单位 MHz)parameter P_COUNTER_WIDTH     = 32,    // 计数器位宽parameter P_DEFAULT_PERIOD_US = 1000,  // 默认周期(单位 us)parameter P_DEFAULT_HIGH_US   = 500    // 默认高电平时间(单位 us)
)(input  wire                         i_clk        , // 时钟input  wire                         i_rst_n      , // 异步复位,低有效input  wire [P_COUNTER_WIDTH-1:0]   i_period_us  , // PWM 周期(单位:us)input  wire [P_COUNTER_WIDTH-1:0]   i_high_us    , // 高电平时间(单位:us)output reg                          o_pwm_out    , // PWM 输出output reg                          o_param_err    // 参数错误标志
);// ==================================================// 常量与默认值(以 clock cycles 表示)// ==================================================localparam L_CYCLE_MAX_VAL         = (1 << P_COUNTER_WIDTH) - 1;localparam L_DEFAULT_PERIOD_CYCLES = P_CLK_FREQ_MHZ * P_DEFAULT_PERIOD_US;localparam L_DEFAULT_HIGH_CYCLES   = P_CLK_FREQ_MHZ * P_DEFAULT_HIGH_US;// ==================================================// 输入 us → clock cycles 转换(组合逻辑)// ==================================================wire [P_COUNTER_WIDTH-1:0] w_period_cycles = P_CLK_FREQ_MHZ * i_period_us;wire [P_COUNTER_WIDTH-1:0] w_high_cycles   = P_CLK_FREQ_MHZ * i_high_us;// ==================================================// 寄存器定义// ==================================================reg [P_COUNTER_WIDTH-1:0] r_cnt;             // 主计数器reg [P_COUNTER_WIDTH-1:0] r_period_cycles;   // 周期最大值(锁存)reg [P_COUNTER_WIDTH-1:0] r_high_cycles;     // 高电平持续周期数(锁存)reg [P_COUNTER_WIDTH-1:0] r_period_us_d;     // 上一次周期值(us)reg [P_COUNTER_WIDTH-1:0] r_high_us_d;       // 上一次高电平值(us)// ==================================================// 参数合法性检查// ==================================================always @(posedge i_clk or negedge i_rst_n) beginif (!i_rst_n) begino_param_err <= 1'b0;end else begino_param_err <= (i_period_us == 0) ||(w_period_cycles > L_CYCLE_MAX_VAL) ||(w_high_cycles   > L_CYCLE_MAX_VAL);endend// ==================================================// 周期结束时更新锁存周期/高电平(单位 clk)// ==================================================always @(posedge i_clk or negedge i_rst_n) beginif (!i_rst_n) beginr_period_us_d   <= P_DEFAULT_PERIOD_US;r_high_us_d     <= P_DEFAULT_HIGH_US;r_period_cycles <= L_DEFAULT_PERIOD_CYCLES;r_high_cycles   <= L_DEFAULT_HIGH_CYCLES;end else if (r_cnt == r_period_cycles - 1 && !o_param_err) beginif ((i_period_us != r_period_us_d) || (i_high_us != r_high_us_d)) beginr_period_us_d   <= i_period_us;r_high_us_d     <= i_high_us;r_period_cycles <= w_period_cycles;r_high_cycles   <= w_high_cycles;endendend// ==================================================// 主计数器逻辑// ==================================================always @(posedge i_clk or negedge i_rst_n) beginif (!i_rst_n) beginr_cnt <= 0;end else if (o_param_err) beginr_cnt <= r_cnt;  // 可省略end else if (r_cnt == r_period_cycles - 1) beginr_cnt <= 0;end else beginr_cnt <= r_cnt + 1;endend// ==================================================// PWM输出逻辑// ==================================================always @(posedge i_clk or negedge i_rst_n) beginif (!i_rst_n)o_pwm_out <= 1'b0;else if (o_param_err)o_pwm_out <= 1'b0;else if (r_high_cycles >= r_period_cycles)o_pwm_out <= 1'b1;else if (r_cnt < r_high_cycles)o_pwm_out <= 1'b1;elseo_pwm_out <= 1'b0;endendmodule

tb.v

`timescale 1ns/1nsmodule tb;// ==================================================// 参数定义// ==================================================parameter P_CLK_FREQ_MHZ = 50;parameter CLK_PERIOD_NS  = 1000 / P_CLK_FREQ_MHZ;// ==================================================// 信号定义// ==================================================reg         i_clk;reg         i_rst_n;reg  [31:0] i_period_us;reg  [31:0] i_high_us;wire        o_pwm_out;wire        o_param_err;// ==================================================// 实例化 DUT// ==================================================pwm #(.P_CLK_FREQ_MHZ(P_CLK_FREQ_MHZ),.P_COUNTER_WIDTH(32),.P_DEFAULT_PERIOD_US(1000),.P_DEFAULT_HIGH_US(500)) dut (.i_clk       (i_clk),.i_rst_n     (i_rst_n),.i_period_us (i_period_us),.i_high_us   (i_high_us),.o_pwm_out   (o_pwm_out),.o_param_err (o_param_err));// ==================================================// 时钟生成// ==================================================always #(CLK_PERIOD_NS / 2) i_clk = ~i_clk;// ==================================================// 初始过程// ==================================================initial begin$display(">>> Start PWM Testbench");i_clk       = 0;i_rst_n     = 0;i_period_us = 1000;  // 1ms周期i_high_us   = 200;   // 初始20%#500;i_rst_n = 1;// 维持一段时间后改变占空比为 50%#(3000 * 1000);i_high_us = 500;// 再一段时间后改为 80%#(3000 * 1000);i_high_us = 800;// 再观察一段时间#(3000 * 1000);$display(">>> Finish PWM Testbench");$finish;endendmodule
http://www.dtcms.com/a/301992.html

相关文章:

  • Java Collections工具类
  • RocketMQ入门实战详解
  • 【MySQL学习|黑马笔记|Day1】数据库概述,SQL|通用语法、SQL分类、DDL
  • 【数据标注】详解使用 Labelimg 进行数据标注的 Conda 环境搭建与操作流程
  • 【unitrix】 6.20 非零整数特质(non_zero.rs)
  • 做了一款小而美的本地校验器
  • 【保姆级喂饭教程】Python依赖管理工具大全:Virtualenv、venv、Pipenv、Poetry、pdm、Rye、UV、Conda、Pixi等
  • 【el-table滚动事件】el-table表格滚动时,获取可视窗口内的行数据
  • 电磁兼容五:仿真技术
  • 数智驱动的「库存管理」:从风险系数、ABC分类到OMS和ERP系统的协同优化策略
  • 前端静态资源优化
  • WD5030A芯片24降12V,15A以内,应用于路由器、交换机和网络服务器,成本低大电流
  • 枚举策略模式实战:优雅消除支付场景的if-else
  • 6种将iPhone照片传输到Windows 10电脑的方法
  • Vue 正在热映模块
  • 安宝特案例丨AR+AI+SOP?3大技术融合革新军工航天领域
  • 组件化(一):重新思考“组件”:状态、视图和逻辑的“最佳”分离实践
  • 中兴云电脑W101D2-晶晨S905L3A-2G+8G-安卓9-线刷固件包
  • react前端样式如何给元素设置高度自适应
  • 四、计算机组成原理——第7章:输入/输出系统
  • Mac查看本机ip地址
  • 六轴机械臂cad【11张】三维图+设计说明书
  • GPU训练日志 (下)
  • Redis 服务挂掉排查与解决
  • STL学习(?、set容器)
  • 计算机毕业设计java在线二手系统的设计与实现 基于Java的在线二手交易平台开发 Java技术驱动的二手物品管理系统
  • 如何创建 Google 翻译桌面快捷方式
  • qt 心跳包
  • 【Linux篇】进程间通信:进程IPC
  • 搜索引擎高级搜索指令大全(Google、百度等浏览器通用)