作者:包春,胡建東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”,歷史消息里面有,大家可以查看作者和日期,是純原創,希望大家支持!
評論
查看更多