PWM 呼吸灯实验

PWM 呼吸灯实验

FPGA实现一个PWM模块(硬件)来控制灯的亮灭。

实验原理

PWM本质上就是一个输出脉冲的硬件,通过改变一个周期高电平(占空比)的时间来对其他的硬件进行控制,比如电机。
呼吸灯的实现利用了人眼的视觉特性,控制灯亮和暗的间隔时间就形成了对灯亮度的调节。
通过一个N比特的计数器(溢出相当于从0开始)和一个值就可以实现一个占空比可调的脉冲输出。

实验步骤

  1. 设计PWM模块
  2. 用硬件描述语言实现设计
  3. 查看硬件设计,添加管脚约束和时钟约束
  4. 本地仿真
  5. 上板验证

实验记录

  1. 设计PWM模块
信号名称方向说明
clkin时钟输入
rstin异步输入复位,高有效
periodinPWM 脉宽周期(频率)控制。实际是每次计数的步进值,根据系统输入时钟可以计算出pwm输出频率
dutyin占空比,小于该值时输出低电平,否则输出高电平
pwm_outoutpwm输出
  1. 用硬件描述语言实现设计
    ax_pwm.v
`timescale 1ns / 1ps
module ax_pwm
#(
    parameter N = 16
)
(
    input clk,
    input rst,
    input [N-1:0] period,
    input [N-1:0] duty,
    output pwm_out
);


reg[N-1:0] period_r;
reg[N-1:0] duty_r;
reg[N-1:0] period_cnt;
reg pwm_r;

assign pwm_out = pwm_r;

always @(posedge clk or posedge rst)
begin
    if(rst==1)
    begin
        period_r <= {N{1'b0}};
        duty_r <= {N{1'b0}};
    end
    else
    begin
        period_r <= period;
        duty_r <= duty;
    end
end
    
always @(posedge clk or posedge rst)
begin
    if(rst==1)
        period_cnt <= {N{1'b0}};
    else
        period_cnt <= period_cnt + period_r;
end

always @(posedge clk or posedge rst)
begin
    if(rst==1)
    begin
        pwm_r <= 1'b0;
    end
    else
    begin
        if(period_cnt >= duty_r)
            pwm_r <= 1'b1;
       else
            pwm_r <= 1'b0;
    end
end

endmodule

PWM测试模块

`timescale 1ns / 1ps

module pwm_test(
    input  sys_clk_p,    // 时钟是一个200MHz的差分时钟
    input  sys_clk_n,
    input  rst_n,        // 复位由一个按键来控制,按键默认状态为高,所以低复位
    output wire led          // 输出给led
);
localparam CLK_FREQ = 200 ; //200MHz,周期为 1s/200MHz = 1/200 us
localparam US_COUNT = CLK_FREQ ; //1 us counter,200个时钟周期是1us 
localparam MS_COUNT = CLK_FREQ*1000 ; //1 ms counter  1个ms的周期计数

localparam DUTY_STEP = 32'd100000 ; //duty step, 占空比调整大小,单次调整 100000/2^32 = 0.000000000232
localparam DUTY_MIN_VALUE = 32'h6fffffff ; //duty minimum value, 最小占空比 0.437
localparam DUTY_MAX_VALUE = 32'hffffffff ; //duty maximum value, 最大占空比 0.999

localparam IDLE = 0; //IDLE state 
localparam PWM_PLUS = 1; //PWM duty plus state 
localparam PWM_MINUS = 2; //PWM duty minus state 
localparam PWM_GAP = 3; //PWM duty adjustment gap

wire pwm_out; //pwm output 
reg[31:0] period; //pwm step value 
reg[31:0] duty; //duty value 
reg pwm_flag ; //duty value plus and minus flag, 0: plus; 1: minus

reg[3:0] state; 
reg[31:0] timer; //duty adjustment counter 该测试模块计数的

assign led = ~pwm_out ; //led low active

wire clk;
// 差分转单端
IBUFDS IBUFDS_inst(
    .O(clk),
    .I(sys_clk_p),
    .IB(sys_clk_n)
);

always@(posedge clk or negedge rst_n) 
begin 
    if(rst_n == 1'b0) 
    begin  // 复位,设置初始值
        period <= 32'd0; 
        timer <= 32'd0; 
        duty <= 32'd0; 
        pwm_flag <= 1'b0 ; 
        state <= IDLE; 
    end
    else 
    case(state) 
        IDLE: 
        begin 
            // N位计数器,CLK Hz的时钟(周期1/CLK s),要输出n Hz的PWM波,PWM周期为1/n s,要在一个PWM周期计数完一轮,而一个周期会计数((1/n)/(1/CLK))次
            //  2^N/((1/n)/(1/CLK)) = 2^32*n/CLK  
            //  这个地方的时钟写的50M,PWM是800Hz的,周期0.00125s。
            period <= 32'd17179; //The pwm step value, pwm 200Hz(period = 200*2^32/50000000) 
            state <= PWM_PLUS; 
            duty <= DUTY_MIN_VALUE; 
        end 
        PWM_PLUS : 
        begin 
            if (duty > DUTY_MAX_VALUE - DUTY_STEP) //if duty is bigger than DUTY MAX VALUE minus DUTY_STEP , begin to minus duty value 
            begin 
                pwm_flag <= 1'b1 ; 
                duty <= duty - DUTY_STEP ; 
            end 
            else 
            begin 
                pwm_flag <= 1'b0 ; 
                duty <= duty + DUTY_STEP ; 
            end 
            state <= PWM_GAP ; 
        end
        PWM_MINUS : 
        begin 
            if (duty < DUTY_MIN_VALUE + DUTY_STEP) //if duty is little than DUTY MIN VALUE plus duty step, begin to add duty value 
            begin 
                pwm_flag <= 1'b0 ; 
                duty <= duty + DUTY_STEP ; 
            end 
            else 
            begin 
                pwm_flag <= 1'b1 ;
                duty <= duty - DUTY_STEP ; 
            end 
            state <= PWM_GAP ; 
        end 
        PWM_GAP: 
        begin 
            if(timer >= US_COUNT*100) //adjustment gap is 100us 
            begin 
                if (pwm_flag) 
                    state <= PWM_MINUS ; 
                else 
                    state <= PWM_PLUS ; 
                timer <= 32'd0; 
            end 
            else 
                begin 
                    timer <= timer + 32'd1; 
                end 
        end
        default: 
        begin 
            state <= IDLE; 
        end 
    endcase
end

ax_pwm
#(
.N(32)
)
ax_pwm_m0(
    .clk(clk),
    .rst(~rst_n),
    .period(period),
    .duty(duty),
    .pwm_out(pwm_out)
);

endmodule

  1. 查看硬件设计,添加管脚约束和时钟约束
    约束文件
set_property PACKAGE_PIN AE15 [get_ports led]
set_property PACKAGE_PIN AE14 [get_ports rst_n]
set_property PACKAGE_PIN AE5 [get_ports sys_clk_p]
set_property IOSTANDARD LVCMOS33 [get_ports led]
set_property IOSTANDARD LVCMOS33 [get_ports rst_n]
set_property IOSTANDARD DIFF_SSTL12 [get_ports sys_clk_p]
create_clock -period 5.000 -name sys_clk_p -waveform {0.000 2.500} [get_ports sys_clk_p]
  1. 本地仿真
    激励文件
`timescale 1ns / 1ps

module pwm_test_tb;
// Inputs
reg sys_clk_p;
wire sys_clk_n;
reg rst_n;
wire led ;

// Instantiate the Unit Under Test (UUT)
pwm_test uut (
	.sys_clk_p(sys_clk_p), 		
	.sys_clk_n(sys_clk_n), 	
	.rst_n		(rst_n),
	.led       (led)
);

initial begin
	// Initialize Inputs
	sys_clk_p = 0;
	rst_n = 0;

	// Wait 100 ns for global reset to finish
	#100;
      rst_n = 1;      

 end

always #2.5 sys_clk_p = ~ sys_clk_p;   //5ns一个周期,产生200MHz时钟源

assign sys_clk_n = ~ sys_clk_p;
   
endmodule

这里的PWM周期为1.25ms,和前面的分析相符。
在这里插入图片描述

  1. 上板验证
    加载后会产生类似流水灯的效果。

实验总结

  1. 时钟很重要,是硬件系统的心跳,有了时钟硬件才可以动起来。如果PWM模块没有时钟输入的话,就无法被驱动,无法正常工作。另外设计的时候不考虑时钟,将会无从下手。
  2. 逻辑调试和软件调试虽然形式有所不一样,但本质都是相同的,需要一级一级的分析问题出在哪里,不能慌张。
  3. 本地调试很有必要,可以看到更详细的信号,有利于找出问题。

问题记录

  1. 时序约束没让添加时钟的约束,不知道什么原因,手动添加。
  2. 上板的时候流水灯没亮,本地仿真发现led是一个蓝色的线,发现原因是少了一行assign语句,没把pwm的输出接出去。

参考资料

  1. PWM原理 PWM频率与占空比详解
  2. Xilinx 7系列SelectIO结构之IO标准和端接匹配(三)
  3. XILINX 原语的使用之 IBUFDS 差分转单端、OBUFDS 单端转差分
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
好的,我可以帮你解答这个问题。 首先,PWM(Pulse-Width Modulation)是一种调制技术,可以用来控制电路中的电压或电流。在呼吸灯实验中,我们可以使用 PWM 技术来控制 LED 的亮度,实现呼吸灯效果。 在 STM32 中,可以使用定时器模块来实现 PWM 功能。具体步骤如下: 1. 选择一个可用的定时器模块,并配置其时钟源和预分频器。 2. 配置定时器模块为 PWM 模式,并设置 PWM 的周期和占空比。 3. 配置定时器模块的输出通道,将其连接到 LED 灯的控制引脚上。 4. 启动定时器模块,开始 PWM 输出。 下面是一个简单的呼吸灯实验代码示例: ```c #include "stm32f10x.h" #define LED_PIN GPIO_Pin_13 #define LED_PORT GPIOC void delay(uint32_t ms) { uint32_t i; while (ms--) { for (i = 0; i < 1000; i++); } } void led_pwm_breath(void) { TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure; TIM_OCInitTypeDef TIM_OCInitStructure; GPIO_InitTypeDef GPIO_InitStructure; // 使能定时器时钟和 GPIO 时钟 RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM1, ENABLE); RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC, ENABLE); // 配置 LED 控制引脚为推挽输出 GPIO_InitStructure.GPIO_Pin = LED_PIN; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(LED_PORT, &GPIO_InitStructure); // 配置定时器基本参数 TIM_TimeBaseStructure.TIM_Period = 1000; TIM_TimeBaseStructure.TIM_Prescaler = 72 - 1; TIM_TimeBaseStructure.TIM_ClockDivision = 0; TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; TIM_TimeBaseInit(TIM1, &TIM_TimeBaseStructure); // 配置定时器输出通道为 PWM 模式 TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1; TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; TIM_OCInitStructure.TIM_Pulse = 500; TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High; TIM_OC1Init(TIM1, &TIM_OCInitStructure); // 启动定时器 TIM_Cmd(TIM1, ENABLE); while (1) { // 增加占空比 for (int i = 0; i <= 1000; i += 10) { TIM_SetCompare1(TIM1, i); delay(10); } // 减小占空比 for (int i = 1000; i >= 0; i -= 10) { TIM_SetCompare1(TIM1, i); delay(10); } } } int main(void) { led_pwm_breath(); return 0; } ``` 在上面的代码中,我们使用 TIM1 定时器模块来控制 LED 的亮度。首先,我们配置 TIM1 的时钟源和预分频器,使其时钟频率为 1MHz。然后,我们将 LED 控制引脚 PC13 配置为推挽输出模式,将其连接到 TIM1 的通道 1 上。 接着,我们配置 TIM1 的输出通道 1 为 PWM1 模式,并设置 PWM 的周期为 1000(即 1kHz),占空比为 50%。最后,我们启动 TIM1 定时器,并在一个无限循环中不断增加和减小 PWM 的占空比,实现呼吸灯效果。 希望这个例子对你有帮助!

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值