FPGA:Verilog实现pid算法控制pwm

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档

FPGA:Verilog实现pid算法控制pwm

前言

本次使用verilog编写pid算法,并用于生成pwm波形,分别进行仿真和上板并用示波器验证

提示:以下是本篇文章正文内容,下面案例可供参考

一、PID公式

原理自行学习,网上资料多,主要就是下面的这个公式
pid算法
其中Kp,Ki,Kd就是后续需要调整的参数,通常为固定值,而在本次使用中只是用到了p和i,没有使用到d。

二、verilog实现pid

主要思路就是固定个期望值,随便赋予个实际初值,然后再对这个实际值进行pid算法处理,pid计算的值与原来的实际值相加作为新的实际值

1.pid模块

由于Kp,Ki,Kd三个参数通常为小数或者浮点数,所以一般都是经过放大处理减小误差, 计算完后还需要通过位移缩小。
代码如下(示例):

parameter period_num    =   1024;//pwm周期频率
reg [15:0] period_cnt;//pwm频率计数器
reg signed [15:0]inter_data;//寄存当前实际值
reg signed [15:0]error;//误差寄存器
reg signed [15:0]prev_error;//上次误差寄存器
reg signed [15:0]sum_error;//误差累加
reg signed [15:0] p ;     //P算法计算结果
reg signed [15:0] d ;     //I算法计算结果
reg signed [15:0] i ;     //D算法计算结果(尽管未使用到)
reg signed [15:0] kp_reduce;//缩小计算结果
reg signed [15:0] ki_reduce;
reg signed [15:0] kd_reduce;
reg signed [16:0] prev_out;//上一次输出(未使用到)
reg [5:0]flow_cnt;//状态机计数器,用于控制状态跳转
wire [15:0]error_jian;//误差之差,即本次误差和上次误差的差值
assign error_jian = error - prev_error;
always @(posedge clk or negedge rst_n)begin
    if (!rst_n) begin
        out             <=  'b0;
        pid_vld         <=  'b0;//输出使能
//---------状态机跳转计数器------------
        flow_cnt        <=  'b0;
        xianfu_out      <=  'b0;
        error           <=  'b0;
 //       prev_error      <=  'b0;
        sum_error       <=  'b0;
        inter_data      <=  'b0;
        kp_reduce       <=  'b0;
        ki_reduce       <=  'b0;
//      kd_reduce       <=  'b0;
        
        p               <=  'b0;
        i               <=  'b0;
//      d               <=  'b0;
    end
    else begin
      case(flow_cnt)
            6'd0: begin
                if(pid_en == 1'b1)begin 
                    inter_data  <= actual_value;//寄存当前实际值,用于和pid计算值相加
                    prev_error  <= error;  //将当前误差赋给上次误差 
//                    prev_out    <= out;          
                    flow_cnt    <= flow_cnt + 1'b1;//接收到数据,并完成误差累加和上次误差的赋值后进入下一个状态
                    //---------------------------------------积分限幅-----------------------------------------
                    if( sum_error > $signed('d3000) && ( expect_value - actual_value ) > $signed('d0) )
                        sum_error <= sum_error;
                    else if( sum_error < $signed(-'d3000) && ( expect_value - actual_value ) < $signed('d0) )
                        sum_error <= sum_error;
                    else
                        sum_error <= sum_error + ( expect_value - actual_value );//误差累加
                end
                else begin
                    inter_data  <= inter_data;
                    prev_error  <= prev_error;
                    sum_error   <= sum_error;
                end
            end
            6'd1: begin//该状态给当前误差赋值
                    error       <= expect_value - actual_value ;
                    flow_cnt    <= flow_cnt + 1'b1;
                end     
            6'd2: begin//三种误差赋值完后,需要进行P I D三个算法的计算,公式怎么来的自行百度学习
            /***************************************************************************
            直接用*进行乘法在实际中容易出问题,本次使用只是为了用于仿真验证,实际使用时,
            使用乘法器,则这个状态就只需要添加一个使能信号用于输出给乘法器。
            ***************************************************************************/
                    p <= Kp * error;
 //                 d <= Kd * error_jian;//
                    i <= Ki * sum_error;
                    flow_cnt <= flow_cnt + 1'b1;         
            end
            6'd3:begin  //P I D三种算法计算完后还需要根据参数的放大倍数进行缩小,本次将Kp放大了100倍,Ki放大了1024倍,采用右移完成
                    kp_reduce   <= (p >>> 7) + (p >>> 9);// p/128 + p/512 = 5*p/512 = p/102.4 有误差但影响小
                    ki_reduce   <= i >>> 10;//(i >>> 7) + (i >>> 9);
//                  kd_reduce   <= (d >>> 7) + (d >>> 9);
                    flow_cnt <= flow_cnt + 1'b1;
            end
            6'd4:begin //给pid输出赋值,将当前值以及三个算法的缩小结果相加
                    //pid_ack <= 1'b1;
                    flow_cnt <= flow_cnt + 1'b1;                   
                    out <= inter_data + kp_reduce + ki_reduce + kd_reduce;         
            end
            6'd5:begin //对输出数据进行限幅处理,避免超过限制(根据实际情况使用,可有可无),并拉高输出有效信号使能
                   pid_vld <= 1'b1; 
                   flow_cnt <= flow_cnt + 1'b1;
                   
                   if(out > $signed(32'd1023))
				        xianfu_out <= $signed(32'd1023);
			       else if(out < $signed(-32'd1023)) 
				        xianfu_out <= $signed(-32'd1023);			 
			       else 
				        xianfu_out <= out;                
            end
            6'd6:begin
                    pid_vld <= 1'b0;//输出有效拉低,表示pid计算完成
                    flow_cnt <= 'b0;
            end
            default: ;
      endcase
    end
end

2.仿真文件

always #20 clk = ~clk;

    initial begin
    
        clk = 1'b0;
        rst_n = 1'b0;
        #100
        rst_n = 1'b1;
        expect_value = 'd200;
    end

3.仿真波形

本次给Kp赋20,Ki赋1,Kd为0,如果使用Kd,则波形不会出现这种平滑不断接近期望值的波形,而是会超过期望值,然后在不断逼近,而我在使用中不能出现超过的情况

在这里插入图片描述

4.PWM生成

PWM生成就是通过定义一个pwm频率计数器,然后在将pid三种算法计算后和实际值相加所得的值作为PWM占空比的比较值,添加在pid模块内,状态机后面,最终得到的仿真波形如下
在这里插入图片描述
示波器采集到的波形如下(不一样是因为期望值不同,仿真懒得改)
在这里插入图片描述


总结

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值