作者:包春,胡建东QQ:1758931664
很多小伙伴说控制原理真**难学,小编深有同感。于是发布此文帮助在控制原理里面不停挣扎的小伙伴们,帮你们拨开迷雾。此文专门告诉你怎么实现数字负反馈控制,一切高深理论均不涉及!
首先我们做一个模块,输入信号T,代表输出时钟周期,输出时钟周期严格等于T,对于熟悉FPGA的小伙伴应该很容易。下面是小编的代码。简单的说就是建立一个状态机,对输入周期参数在产生时钟的每个周期,第一时刻更新值,然后进入下一个状态计数,计数到和输入周期一致的时候又回到初始状态。
1. module clk_gen(
2. input clk,
3. input [15:0] T ,//it can not be 0!
4. output reg clk_gen
5. );
6.
7. reg [15:0] cnt_div = 0;
8. reg [15:0] period = 0;
9. reg [ 1:0] now_s = 0;
10. reg [ 1:0] next_s = 0;
11.
12. always@(posedge clk)
13. begin
14. now_s <= next_s;
15. end
16.
17. always@(*)
18. begin
19. case(now_s)
20. 2'h0 : next_s = (T > 0) ? 1 : 0;
21. 2'h1 : next_s = (cnt_div < (period>>1)) ? 1: 3;//跑前半周期
22. 2'h3 : next_s = (cnt_div < (period-1)) ? 3: 0; //跑后半周期
23. default: next_s =0;
24. endcase
25. end
26.
27. always@(posedge clk)
28. begin
29. case(next_s)
30. 2'h0 : begin period <= T; cnt_div <= 0; clk_gen <= 1'b0; end
31. 2'h1 : begin period <= (now_s == 0) ? T : period; cnt_div <= cnt_div + 1; clk_gen <= 1'b0; end//刷新T
32. 2'h3 : begin cnt_div <= cnt_div + 1; clk_gen <= 1'b1; end
33. default: begin period <= 10; cnt_div <= 0; clk_gen <= 1'b0; end
34. endcase
35. end
36. endmodule
上面这个模块就是我们的控制对象,也就是说,我们要想办法让他的时钟频率Fo = 系统给定时钟频率Fi*N,也就是说: 输出时钟周期To = 系统时钟周期Ti/N。
小编采用如下环路实现。从中可以看出,小编的方法反馈控制的是周期,所以一切参数都转化到周期上,输入和反馈,输出都是通过周期控制。从图中可以得出结论输出时钟周期To=N/(M*N+1)
输入时钟要求稳定,并且比系统时钟慢的越多越好,因为这样测量更准确。下面就是我们测量时钟周期的代码。反馈非常重要,因为他涉及到整个反馈环路的精度和运作效率。除以N的部分我们在另一个模块实现,因为测量时钟周期的模块是相对独立的输入和反馈都要用到,独立出来可以加强代码可重用性。这段代码就是在每个输入时钟上升沿电脑时候更新周期值。以保证周期的准确性。
1. module figure_T(
2. input clk,
3. input clk_in,
4. output reg[15:0] T = 0
5. );
6.
7. localparam PE = 2'b01;
8.
9. reg [ 1:0] clk_buf = 0;
10. reg [15:0] cnt_T = 0;
11. reg state = 0;
12.
13. always@(posedge clk) begin clk_buf <= {clk_buf[0],clk_in}; end
14.
15. always@(posedge clk)
16. begin
17. case(state)
18. 0:begin state <= (clk_buf == PE) ? 1 : 0; cnt_T <= (clk_buf == PE) ? 1 : 0; end
19. 1:begin state <= (clk_buf == PE) ? 0 : 1; cnt_T <= cnt_T + 1; T <= (clk_buf == PE) ? cnt_T : T; end
20. default: state <= 0;
21. endcase
22. end
23. endmodule
控制对象和输入,反馈参数产生都已经好了,剩下的就是按照控制框图搭建反馈环路。代码如下图,代码中的Ti是在顶层产生的,输入时钟被测量后才把Ti给到环路输入。第34行是将反馈的时钟周期乘以反馈增益1/N,35行是将反馈误差乘以开环增益1/M。至此整个环路已经搭建完成!
1. module feedback(
2. input clk,
3. input [15:0] Ti,
4. output clk_out
5. );
6.
7. parameter M = 10;//参数
8. parameter N = 10;//参数
9.
10. wire [15:0] T_clk_out;
11. reg [15:0] T;
12. reg [15:0] err;
13. //反馈误差
14. always@(posedge clk) begin err <= Ti - (T_clk_out/N); end
15. always@(posedge clk) begin T <= err/M; end
16. //被控制对象
17. clk_gen u_clk_gen(
18. .clk ( clk ),
19. .T ( T ),
20. .clk_gen( clk_out )
21. );
22. //输出时钟周期反馈
23. figure_T uT(
24. .clk ( clk ),
25. .clk_in( clk_out ),
26. .T ( T_clk_out )
27. );
28. endmodule
这里附上小编顶层代码,这里从30行开始的模块就是产生上文说的Ti用的,输入时钟直接测量后,变成是种病周期T输入到feedback模块。
1. module dll(
2. input clk,
3. input clk_in,
4. output clk_out
5. );
6.
7. wire [15:0] T;
8.
9. figure_T uT(
10. .clk ( clk ),
11. .clk_in( clk_in ),
12. .T ( T )
13. );
14. //
15. feedback #(
16. .M ( 9 ),
17. .N ( 1 )
18. ) u_fb(
19. .clk ( clk ),
20. .Ti ( T ),
21. .clk_out( clk_out )
22. );
23.
24. endmodule
那么实际效果咋样呢,能工作吗?工作起来是啥样子?小编接下来就测试给大家看。测试代码如下,输出时钟周期在反馈的代码里面找,输入时钟周期是给定的,这两个找出来对比看看与理论差多远就知道了,这里令M = 10,N=10。 这两个参数完全决定了输出时钟频率。
1. module tb_dll;
2. parameter HT = 100;
3. // Inputs
4. reg clk;
5. reg rst;
6. reg clk_in;
7.
8. // Outputs
9. wire clk_out;
10. wire [15:0] Tclk_out;
11. wire [15:0] Tclk_in;
12.
13.
14. initial begin
15. // Initialize Inputs
16. clk = 0;
17. rst = 0;
18. clk_in = 0;
19.
20. // Wait 100 ns for global reset to finish
21. #100;
22.
23. // Add stimulus here
24.
25. end
26.
27. always begin clk = 0;#1 clk = 1; #1; end//主时钟产生
28. always begin clk_in = 0;#HT clk_in = 1; #HT; end//输入时钟产生
29. // Instantiate the Unit Under Test (UUT)
30. dll uut (
31. .clk ( clk ),
32. .clk_in ( clk_in ),
33. .clk_out( clk_out)
34. );
35.
35.
36.
37. endmodule仿真结果如下,看图1可知输出时钟周期稳定后为To = 10*2ns,输入时钟为Ti=100*2ns,所以To/Ti = 0.1。而理论值为N/(M*N+1)=10/(10*10+1)=0.099。
误差err = |0.1-0.099|/0.1=1% !
我们看看下图的波形,输出时钟周期的震荡是不是很像连续系统的二阶震荡环节的阶跃响应?只不过超调不大,震荡周期比较少。和控制原理书上很近似(看看离散控制部分会更觉得近似)。小编今天讲到这里,有任何疑问请对公众号“玩儿转FPGA”发消息“提问”,就可以获得小编联系方式并提问,或者加QQ1758931664 一起探讨共同进步。
声明:此文是作者原创内容,未经允许严禁抄袭!!原文出自微信公众号“玩儿转FPGA”,历史消息里面有,大家可以查看作者和日期,是纯原创,希望大家支持!
评论
查看更多