# FPGA 第六組 Lab2 結報 [文章網址](https://hackmd.io/nFId4mhoSKWGEaPBM-TYdg?edit) ## 組員 | 姓名 | 學號 | |:------:|:---------:| | 劉永勝 | F94089032 | | 蔡宗瑾 | E14083137 | | 李宇洋 | E24099025 | ## Color Code 解釋 Color code也可稱作hex triplet,而其是由光的三元色RGB(Red, Green, Blue)所組成,每個顏色大小8bit,範圍從0~255,一個顏色有256種組合,透過三元色各個顏色強度的不同可以得到$256*256*256$種顏色組合。Color code就是將每個顏色透過對應的三元色強度編碼而成的code,例如紫色其對應的color code是7F1FFF,代表顏色強度(Red, Green, Blue)=(7F,1F,FF)時,人眼可辨識其顏色為紫色。 ## Problem 1 [[影片連結]](https://youtu.be/us-rQG4W0EQ) 本設計有兩個RGB燈光需要控制,一個是LED4,其為**恆亮**某種指定顏色;一個是LED5,其為以**呼吸燈**的方式亮某種指定顏色。 ### RGB LED4 (Static Lights) - 要利用電路展現出三元色以外的顏色時,我們可以使用counter來模擬三元色顏色的強度,想當然而counter會數256次,如同顏色強度的範圍一樣,數到255時下個cycle重新循環,如以下: ``` verilog= // counter count from 0 ~ 255 always @(posedge clk or posedge rst) begin if (rst) counter_256 <= 8'd0; else begin counter_256 <= counter_256 + 8'd1; end end ``` > R_time_in, G_time_in, B_time_in 分別代表RGB的顏色強度,當counter小於個別強度時,RGB都會亮,一旦counter超過其指定強度時,RGB根據其對應強度各自暗掉。 ``` verilog= always @(*) begin rgb4[0] = (counter_256 < R_time_in) ? 1'b1 : 1'b0; rgb4[1] = (counter_256 < G_time_in) ? 1'b1 : 1'b0; rgb4[2] = (counter_256 < B_time_in) ? 1'b1 : 1'b0; end ``` - 為什麼會恆亮呢? 因為是利用FPGA的system clk來驅動counter_256,其頻率為125MHz,所以對於人眼來說當counter_256大於X_time_in時,LED對人眼而言並未真正熄滅,只是變弱而已,因此這就是現代電腦模擬現實生活中顏色的方式。 ### RGB LED5 (Breathing Lights) 相較於上面直接利用system clk驅動counter_256來顯示顏色,我們呼吸燈採用降頻的方式來呈現,使得人眼可以辨認出呼吸的效果。 透過一個16-bit counter來實現降頻,其範圍是0~0xffff,會根據counter_256每當數到255時才加一,兩種counter如以下: ```verilog= // count 0 ~ 255 always @(posedge clk or posedge rst) begin if (rst) counter_256 <= 8'd0; else begin counter_256 <= counter_256 + 8'd1; end end // count 0 ~ 16777215 always @(posedge clk or posedge rst) begin if (rst) counter_16777215 <= 16'd0; else begin if (counter_256 == 8'd255) counter_16777215 <= counter_16777215 + 16'd1; else counter_16777215 <= counter_16777215; end end ``` 我們呼吸燈的實現方法是將rgb三個PWM **等比例增加**到我們想要的顏色,達到亮度遞增的效果。之後再**等比例遞減**,達到亮度遞減的效果。 - 如何遞增、遞減? 本題目要求`暗 -> 亮 ->暗`至少要32個phase,則可切成`暗->亮`16 phase、`亮->暗`16 phase。依照**等比例**遞增、遞減的方式,將該顏色對應的hex triplet除以16,可得到每個phase需要增加、減少的數值,如下方例子: - 紫燈hex triplet對應的rgb PWM分別為<font color="purple">**7F_1F_FF**</font>: <font color="red">( r )</font> red PWM = 8'h7F <font color="green">( g )</font> green PWM = 8'h1F <font color="blue">( b )</font> blue PWM = 8'hFF - rgb最大值除以16,分成16個phase: <font color="red">( r )</font> 8'h7F / 16 = 8'h7F >> 4 = 4'h7 = $\Delta$ R <font color="green">( g )</font> 8'h1F / 16 = 8'h1F >> 4 = 4'h1 = $\Delta$ G <font color="blue">( b )</font> 8'hFF / 16 = 8'hFF >> 4 = 4'hF = $\Delta$ B 則上方計算出的數值,為每個phase PWM需要增加的量。因此16個phase即可接進數值,因為shift bit的關係,會有餘數問題。 - rgb除以16的餘數: <font color="red">( r )</font> 8'h7F % 16 = 4'hF = mod R <font color="green">( g )</font> 8'h1F % 16 = 4'hF = mod G <font color="blue">( b )</font> 8'hFF % 16 = 4'hF = mod B 根據上方的計算可得下方式子: <font color="red">( r )</font> $\Delta$ R * 16 + mod R = 8'h7F <font color="green">( g )</font> $\Delta$ G * 16 + mod G = 8'h1F <font color="blue">( b )</font> $\Delta$ B * 16 + mod B = 8'hFF 16個phase遞增、遞減,**再加一個phase處理餘數**。 - Dynamic boundary X_breathing不是像上半部的X_time_in是固定的,它是動態改變的邊界,也就是因為這個動態邊界呈現了呼吸燈的效果,讓原先static lights固定的邊界為hex code,在這裡的呼吸燈成為漸漸遞增又遞減的邊界。 ```verilog= // breathing always @(posedge clk or posedge rst) begin if (rst) begin R_breathing <= 8'd0; G_breathing <= 8'd0; B_breathing <= 8'd0; end else if (counter_16777215 == 16'hffff && counter_256 == 8'd0) begin case (mode) INCREASING: begin R_breathing <= R_breathing + R_inc; G_breathing <= G_breathing + G_inc; B_breathing <= B_breathing + B_inc; end ADD_LAST: begin R_breathing <= R_breathing + R_mod; G_breathing <= G_breathing + G_mod; B_breathing <= B_breathing + B_mod; end DECREASING: begin R_breathing <= R_breathing - R_inc; G_breathing <= G_breathing - G_inc; B_breathing <= B_breathing - B_inc; end SUB_LAST: begin R_breathing <= R_breathing - R_mod; G_breathing <= G_breathing - G_mod; B_breathing <= B_breathing - B_mod; end endcase end else begin R_breathing <= R_breathing; G_breathing <= G_breathing; B_breathing <= B_breathing; end end ``` 綜觀counter以及breathing的程式碼,可得知X_breathing改變的頻率是$125MHz/2^{8+16}=7.45Hz$,X_breathing會一路從0加到相對應的hex code(最大值),再從最大值遞減回0。 - FSM 說明 根據上方說明可製作出下方的FSM。 ![](https://i.imgur.com/vZotwRV.png) | state | 說明 | |:------:|:----------:| |INCREASING|rgb PWM從0開始逐步遞增| |ADD_LAST |加上餘數| |DECREASING|rgb PWM從最大值逐步遞減| |SUB_LAST |減去餘數| - 分割模塊 這個實作分成兩個模塊,分別為`PWM_Decoder`和`Breath_LED`個別功能如下: 1. PWM_Decoder 輸入訊號switch,決定輸出顏色的PWM,此PWM輸出訊號會讓Breath_LED接收,**為呼吸燈的最大亮度**。下表為對應顏色: | switch | color | |:------:|:----:| |00|7F_1F_FF - Purple Light | |00|00_FF_FF - Cyan Light| |00|FF_FF_00 - Yellow Light| |00|FF_00_FF - Crimson Light| 2. Breath_LED 接收PWM_Decoder輸出的顏色PWM最大值,執行上方FSM之功能,達到呼吸燈PWM的遞增遞減 - Block Design ![](https://i.imgur.com/WxWrcCz.png) ## Problem 2 [[影片連結]](https://youtu.be/_dSBnKLIC54) Problem 1是手動變換顏色,而**Problem 2是自動變換顏色**。要實現此功能則模塊PWM_Decoder不能從板子上的switch接收訊號,也就是`pynq-z2_v1.0.xdc`不能指名switch腳位。而是要系統自行產生switch訊號切換顏色。 - 各模塊功能 1. PWM_Decoder 與problem 1功能相同,只是顏色從4個變成6個,則輸入`switch`訊號線從2 bit變成3 bit。 2. Breath_LED 內部狀態機與problem 1相同,但當state從INCREASIN跑到SUB_LAST時,完成一次`暗 -> 亮 ->暗`,輸出`switch`訊號給PWM_Decoder,則PWM_Decoder根據`switch`的數值,給出對應顏色。**簡單來說,FSM跑完一圈,則輸出訊號`switch`加一,加到5之後歸零。** switch顏色對應如下: | switch | color | |:------:|:-----:| |0|FF_00_00 - Red Light | |1|FF_61_00 - Orange Light| |2|FF_FF_00 - Yellow Light| |3|00_FF_00 - Green Light| |4|00_00_FF - Blue Light| |5|7F_1F_FF - Purple Light | |default|FF_00_00 - Red Light| - Block Design ![](https://i.imgur.com/Rg6n9Ga.png)