Визуальная электроника

Совершенно случайно, на просторах интернета был запеленгован блог товарища Arlet`а. В данном блоге меня заинтересовала генерация с помощью FPGA динамических, вращающихся плоскостей. Эффект достаточно интересный, в оригинале тип эффекта управляется с помощью переключателей. У нас никаких переключателей нет, поэтому мы будем управлять всем происходящим с помощью нашего ARM контроллера по USB. Для этого изменим код нашей статьи Первый проект для VE-EP4CE10E. Если в первом примере по нажатию кнопки '1' в USB терминале pin B.12 сбрасывался в состояние логического нуля, а по нажатию клавиши '2' устанавливался в состояние логической единицы, то теперь нам нужно сделать управление по четырем линиям. Для этого изменим код файла "main.c":

C++ Code:
  1. /* USER CODE BEGIN PV */
  2. unsigned char rx_buff[64];
  3. unsigned char tx_buff[64];
  4. unsigned char data0, data1, data2, data3;
  5. /* USER CODE END PV */

Создаем четыре переменные data0-data3 для контроля выводов нашего контроллера. Вы наверное зададитесь законным вопросом, а почему тип переменных не bool? Ответ, в принципе, достаточно прост: разрешение нашего файла .c- это значит что содержимое файла будет компилироваться C компилятором, а он (в отличие от C++) не поддерживает тип данных bool. Ну это было лирическое отступление. Приступим к управлению нашими эффектами, добавляем код:

C++ Code:
  1. /* USER CODE BEGIN 3 */
  2. if (VCP_read(&rx_buff, 1) != 1)
  3. continue;
  4. VCP_write("\r\nYou typed ", 12);
  5. VCP_write(&rx_buff, 1);
  6. VCP_write("\r\n", 2);
  7. if(rx_buff[0]=='1')
  8. {
  9. if(data0==0){data0=1;HAL_GPIO_WritePin(GPIOB, GPIO_PIN_12, 1);} //MCU_DATA0=1
  10. else{data0=0;HAL_GPIO_WritePin(GPIOB, GPIO_PIN_12, 0);} //MCU_DATA0=0
  11. }
  12. if(rx_buff[0]=='2')
  13. {
  14. if(data1==0){data1=1;HAL_GPIO_WritePin(GPIOB, GPIO_PIN_13, 1);} //MCU_DATA1=1
  15. else{data1=0;HAL_GPIO_WritePin(GPIOB, GPIO_PIN_13, 0);} //MCU_DATA1=0
  16. }
  17. if(rx_buff[0]=='3')
  18. {
  19. if(data2==0){data2=1;HAL_GPIO_WritePin(GPIOB, GPIO_PIN_14, 1);} //MCU_DATA2=1
  20. else{data2=0;HAL_GPIO_WritePin(GPIOB, GPIO_PIN_14, 0);} //MCU_DATA2=0
  21. }
  22. if(rx_buff[0]=='4')
  23. {
  24. if(data3==0){data3=1;HAL_GPIO_WritePin(GPIOB, GPIO_PIN_15, 1);} //MCU_DATA3=1
  25. else{data3=0;HAL_GPIO_WritePin(GPIOB, GPIO_PIN_15, 0);} //MCU_DATA3=0
  26. }
  27. }
  28. /* USER CODE END 3 */

Теперь нажатие клавиши '1' управляет включение/выключением ноги B.12, клавиша '2' - B.13, клавиша '3' - B.14, клавиша '4' - B.15.

 В Quartus создаем новый проект. Основной файл графического ввода:

Распределение пинов:

Основной файл Verilog:

Verilog Code:
  1. /*
  2.  * main module for VGA demo.
  3.  *
  4.  * (C) Arlet Ottens
  5.  */
  6. module main( Clk, Reset, R, G, B, VSync, HSync, SW0, SW1, SW2 );
  7.  
  8. input Clk;
  9. input Reset;
  10.  
  11. // VGA
  12. output R;
  13. output G;
  14. output B;
  15. output VSync;
  16. output HSync;
  17.  
  18. // Switches
  19. input SW0;
  20. input SW1;
  21. input SW2;
  22.  
  23. // definitions
  24. reg R;
  25. reg G;
  26. reg B;
  27. reg VSync;
  28. reg HSync;
  29.  
  30. wire HSync0;
  31. wire VSync0;
  32. wire [9:0] Col;
  33. wire [8:0] Row;
  34. wire Col0;
  35. wire Row0;
  36. wire screen_active;
  37. wire PixelClock;
  38.  
  39.  
  40. vga vga( .Reset(Reset),
  41. .HSync(HSync0),
  42. .VSync(VSync0),
  43. .Clk50(Clk),
  44. .PClk(PixelClock),
  45. .Col(Col),
  46. .Row(Row),
  47. .Col0(Col0),
  48. .Row0(Row0),
  49. .Active(screen_active) );
  50.  
  51. reg [17:0] X0, Y0, X1, Y1, XI;
  52. reg [17:0] RX0, RY0, RX1, RY1;
  53. reg [17:0] GX0, GY0, GX1, GY1;
  54. reg signed [17:0] DY, DX;
  55. reg signed [17:0] RDY, RDX;
  56. reg signed [17:0] GDY, GDX;
  57. reg do_DY;
  58.  
  59. /*
  60.  * Rotation per line/frame depending on SW2
  61.  */
  62.  
  63. wire Rot = SW2 ? Row0 : Col0;
  64.  
  65. /*
  66.  * maintain transformation parameters
  67.  */
  68. always @(posedge Clk) begin
  69. if( Reset ) begin
  70. DY <= 2000;
  71. DX <= 2000;
  72. do_DY <= 0;
  73. end else if( Rot ) begin
  74. DY <= DY - (DX >>> 8);
  75. do_DY <= 1;
  76. end else if( do_DY ) begin
  77. DX <= DX + (DY >>> 8);
  78. do_DY <= 0;
  79. end
  80.  
  81. if( Reset ) begin
  82. RDY <= 2050;
  83. RDX <= 1950;
  84. end else if( Rot ) begin
  85. RDY <= RDY - (RDX >>> 8);
  86. end else if( do_DY ) begin
  87. RDX <= RDX + (RDY >>> 8);
  88. end
  89.  
  90. if( Reset ) begin
  91. GDY <= 2000;
  92. GDX <= 2000;
  93. end else if( Rot ) begin
  94. GDY <= GDY - (GDX >>> 8);
  95. end else if( do_DY ) begin
  96. GDX <= GDX + (GDY >>> 8);
  97. end
  98. end
  99.  
  100. /*
  101.  * calculate per-pixel mapping (based on rotozoom). Multipliers can be
  102.  * avoided by more clever code, but the Spartan-3 has plenty for this
  103.  * purpose.
  104.  */
  105. always @(posedge Clk) begin
  106. if( PixelClock ) begin
  107. if( Row0 ) begin
  108. X0 <= -DX * 18'd319;
  109. Y0 <= DY * 18'd241;
  110. X1 <= -DX * 18'd319;
  111. Y1 <= DY * 18'd241;
  112. end else if( Col0 ) begin
  113. X0 <= X1 + DY;
  114. Y0 <= Y1 - DX;
  115. X1 <= X1 + DY;
  116. Y1 <= Y1 - DX;
  117. end else begin
  118. X0 <= X0 + DX;
  119. Y0 <= Y0 + DY;
  120. end
  121.  
  122. if( Row0 ) begin
  123. RX0 <= -RDX * 18'd319;
  124. RY0 <= RDY * 18'd231;
  125. RX1 <= -RDX * 18'd319;
  126. RY1 <= RDY * 18'd237;
  127. end else if( Col0 ) begin
  128. RX0 <= RX1 + RDY;
  129. RY0 <= RY1 - RDX;
  130. RX1 <= RX1 + RDY;
  131. RY1 <= RY1 - RDX;
  132. end else begin
  133. RX0 <= RX0 + RDX;
  134. RY0 <= RY0 + RDY;
  135. end
  136.  
  137. if( Row0 ) begin
  138. GX0 <= -GDX * 18'd300;
  139. GY0 <= GDY * 18'd221;
  140. GX1 <= -GDX * 18'd300;
  141. GY1 <= GDY * 18'd221;
  142. end else if( Col0 ) begin
  143. GX0 <= GX1 + GDY;
  144. GY0 <= GY1 - GDX;
  145. GX1 <= GX1 + GDY;
  146. GY1 <= GY1 - GDX;
  147. end else begin
  148. GX0 <= GX0 + GDX;
  149. GY0 <= GY0 + GDY;
  150. end
  151. end
  152. end
  153.  
  154. /*
  155.  * select bits to XOR based on SW0
  156.  */
  157. wire [17:0] SRX1 = SW0 ? RX0 : RX0 << 1;
  158. wire [17:0] SGX1 = SW0 ? GX0 : GX0 << 1;
  159. wire [17:0] SX1 = SW0 ? X0 : X0 << 1;
  160.  
  161. wire [17:0] SRY1 = SW0 ? RY0 : RY0 << 1;
  162. wire [17:0] SGY1 = SW0 ? GY0 : GY0 << 1;
  163. wire [17:0] SY1 = SW0 ? Y0 : Y0 << 1;
  164.  
  165. /*
  166.  * select squares or lines based on SW1
  167.  */
  168. wire RB = SW1 ? (SRX1[17] ^ SRY1[17]) : SRX1[17];
  169. wire GB = SW1 ? (SGX1[17] ^ SGY1[17]) : SGX1[17];
  170. wire BB = SW1 ? ( SX1[17] ^ SY1[17]) : SX1[17];
  171.  
  172. /*
  173.  * output HSync/VSync and RGB data
  174.  */
  175. always @(posedge Clk) begin
  176. HSync <= HSync0;
  177. VSync <= VSync0;
  178. if( screen_active ) begin
  179. R <= ~BB & (RB | (GB & ~RB));
  180. G <= GB & ~BB & ~RB;
  181. B <= BB;
  182. end else begin
  183. B <= 0;
  184. G <= 0;
  185. R <= 0;
  186. end
  187. end
  188.  
  189. endmodule

Файл генератор VGA развертки:

Verilog Code:
  1. /*
  2.  * VGA timing generator
  3.  *
  4.  * (C) Arlet Ottens
  5.  */
  6.  
  7. module vga( Reset, VSync, HSync, Clk50, PClk, Row, Col, Row0, Col0, Active );
  8. input Reset;
  9. output VSync;
  10. output HSync;
  11. input Clk50;
  12.  
  13. // outputs to data generator
  14. output PClk; // pixel clock
  15. output Row0; // pulse when Row == 0
  16. output Col0; // pulse when Col == 0
  17. output [9:0] Col; // next column
  18. output [8:0] Row; // next row
  19. output Active; //
  20.  
  21. reg PClk; // pixel clock
  22. reg Row0; // pulse when Row == 0
  23. reg Col0; // pulse when Col == 0
  24. reg [12:0] HClock;
  25. reg [11:0] VClock;
  26. assign Col = HClock[9:0];
  27. assign Row = VClock[8:0];
  28. assign Active = HClock[11] & VClock[10];
  29. assign VSync = VClock[9];
  30. assign HSync = HClock[10];
  31.  
  32. /*
  33.  * VGA timing information.
  34.  */
  35. parameter
  36. HSYNC = 10'd95,
  37. HBACK = 10'd47,
  38. HACTIVE = 10'd639,
  39. HFRONT = 10'd15;
  40.  
  41. parameter
  42. VSYNC = 9'd1,
  43. VBACK = 9'd28,
  44. VACTIVE = 9'd479,
  45. VFRONT = 9'd9;
  46.  
  47. wire HStart = ( HClock == {3'b101, HBACK} );
  48. wire VStart = ( VClock == {3'b101, VBACK} );
  49.  
  50. /*
  51.  * make 25 MHz pixelclock
  52.  */
  53. always @(posedge Clk50)
  54. PClk <= Reset ? 0 : ~PClk;
  55.  
  56. /*
  57.  * Horizontal timing state machine
  58.  */
  59. always @(posedge Clk50) begin
  60. if( Reset ) begin
  61. HClock <= 0;
  62. Col0 <= 0;
  63. end else if( PClk ) begin
  64. casex( HClock )
  65. { 3'bxx0, HSYNC } : HClock <= { 3'b101, 10'd0 };
  66. { 3'b101, HBACK } : HClock <= { 3'b011, 10'd0 };
  67. { 3'bx11, HACTIVE } : HClock <= { 3'b001, 10'd0 };
  68. { 3'b001, HFRONT } : HClock <= { 3'b100, 10'd0 };
  69.  
  70. default:
  71. HClock <= { HClock[12:10], HClock[9:0] + 10'd1 };
  72. endcase
  73. end
  74. Col0 <= HStart;
  75. end
  76.  
  77. /*
  78.  * Vertical timing state machine
  79.  */
  80. always @(posedge Clk50) begin
  81. if( Reset ) begin
  82. VClock <= 0;
  83. Row0 <= 0;
  84. end else if( PClk & HStart ) begin
  85. casex ( VClock )
  86. { 3'bxx0, VSYNC } : VClock <= { 3'b101, 9'd0 };
  87. { 3'b101, VBACK } : VClock <= { 3'b011, 9'd0 };
  88. { 3'bx11, VACTIVE } : VClock <= { 3'b001, 9'd0 };
  89. { 3'b001, VFRONT } : VClock <= { 3'b100, 9'd0 };
  90.  
  91. default:
  92. VClock <= { VClock[11:9], VClock[8:0] + 9'd1};
  93. endcase
  94. end
  95. Row0 <= HStart & VStart;
  96. end
  97.  
  98. endmodule

Исходный код для FPGA: vga_demo.zip

Всех с наступающим ДНЕМ РАДИО и ДНЕМ ПОБЕДЫ!

Добавить комментарий