Совершенно случайно, на просторах интернета был запеленгован блог товарища Arlet`а. В данном блоге меня заинтересовала генерация с помощью FPGA динамических, вращающихся плоскостей. Эффект достаточно интересный, в оригинале тип эффекта управляется с помощью переключателей. У нас никаких переключателей нет, поэтому мы будем управлять всем происходящим с помощью нашего ARM контроллера по USB. Для этого изменим код нашей статьи Первый проект для VE-EP4CE10E. Если в первом примере по нажатию кнопки '1' в USB терминале pin B.12 сбрасывался в состояние логического нуля, а по нажатию клавиши '2' устанавливался в состояние логической единицы, то теперь нам нужно сделать управление по четырем линиям. Для этого изменим код файла "main.c":
C++ Code:
/* USER CODE BEGIN PV */ unsigned char rx_buff[64]; unsigned char tx_buff[64]; unsigned char data0, data1, data2, data3; /* USER CODE END PV */
Создаем четыре переменные data0-data3 для контроля выводов нашего контроллера. Вы наверное зададитесь законным вопросом, а почему тип переменных не bool? Ответ, в принципе, достаточно прост: разрешение нашего файла .c- это значит что содержимое файла будет компилироваться C компилятором, а он (в отличие от C++) не поддерживает тип данных bool. Ну это было лирическое отступление. Приступим к управлению нашими эффектами, добавляем код:
C++ Code:
/* USER CODE BEGIN 3 */ if (VCP_read(&rx_buff, 1) != 1) continue; VCP_write("\r\nYou typed ", 12); VCP_write(&rx_buff, 1); VCP_write("\r\n", 2); if(rx_buff[0]=='1') { if(data0==0){data0=1;HAL_GPIO_WritePin(GPIOB, GPIO_PIN_12, 1);} //MCU_DATA0=1 else{data0=0;HAL_GPIO_WritePin(GPIOB, GPIO_PIN_12, 0);} //MCU_DATA0=0 } if(rx_buff[0]=='2') { if(data1==0){data1=1;HAL_GPIO_WritePin(GPIOB, GPIO_PIN_13, 1);} //MCU_DATA1=1 else{data1=0;HAL_GPIO_WritePin(GPIOB, GPIO_PIN_13, 0);} //MCU_DATA1=0 } if(rx_buff[0]=='3') { if(data2==0){data2=1;HAL_GPIO_WritePin(GPIOB, GPIO_PIN_14, 1);} //MCU_DATA2=1 else{data2=0;HAL_GPIO_WritePin(GPIOB, GPIO_PIN_14, 0);} //MCU_DATA2=0 } if(rx_buff[0]=='4') { if(data3==0){data3=1;HAL_GPIO_WritePin(GPIOB, GPIO_PIN_15, 1);} //MCU_DATA3=1 else{data3=0;HAL_GPIO_WritePin(GPIOB, GPIO_PIN_15, 0);} //MCU_DATA3=0 } } /* USER CODE END 3 */
Теперь нажатие клавиши '1' управляет включение/выключением ноги B.12, клавиша '2' - B.13, клавиша '3' - B.14, клавиша '4' - B.15.
В Quartus создаем новый проект. Основной файл графического ввода:
Распределение пинов:
Основной файл Verilog:
Verilog Code:
/* * main module for VGA demo. * * (C) Arlet Ottens */ module main( Clk, Reset, R, G, B, VSync, HSync, SW0, SW1, SW2 ); input Clk; input Reset; // VGA output R; output G; output B; output VSync; output HSync; // Switches input SW0; input SW1; input SW2; // definitions reg R; reg G; reg B; reg VSync; reg HSync; wire HSync0; wire VSync0; wire [9:0] Col; wire [8:0] Row; wire Col0; wire Row0; wire screen_active; wire PixelClock; vga vga( .Reset(Reset), .HSync(HSync0), .VSync(VSync0), .Clk50(Clk), .PClk(PixelClock), .Col(Col), .Row(Row), .Col0(Col0), .Row0(Row0), .Active(screen_active) ); reg [17:0] X0, Y0, X1, Y1, XI; reg [17:0] RX0, RY0, RX1, RY1; reg [17:0] GX0, GY0, GX1, GY1; reg signed [17:0] DY, DX; reg signed [17:0] RDY, RDX; reg signed [17:0] GDY, GDX; reg do_DY; /* * Rotation per line/frame depending on SW2 */ wire Rot = SW2 ? Row0 : Col0; /* * maintain transformation parameters */ always @(posedge Clk) begin if( Reset ) begin DY <= 2000; DX <= 2000; do_DY <= 0; end else if( Rot ) begin DY <= DY - (DX >>> 8); do_DY <= 1; end else if( do_DY ) begin DX <= DX + (DY >>> 8); do_DY <= 0; end if( Reset ) begin RDY <= 2050; RDX <= 1950; end else if( Rot ) begin RDY <= RDY - (RDX >>> 8); end else if( do_DY ) begin RDX <= RDX + (RDY >>> 8); end if( Reset ) begin GDY <= 2000; GDX <= 2000; end else if( Rot ) begin GDY <= GDY - (GDX >>> 8); end else if( do_DY ) begin GDX <= GDX + (GDY >>> 8); end end /* * calculate per-pixel mapping (based on rotozoom). Multipliers can be * avoided by more clever code, but the Spartan-3 has plenty for this * purpose. */ always @(posedge Clk) begin if( PixelClock ) begin if( Row0 ) begin X0 <= -DX * 18'd319; Y0 <= DY * 18'd241; X1 <= -DX * 18'd319; Y1 <= DY * 18'd241; end else if( Col0 ) begin X0 <= X1 + DY; Y0 <= Y1 - DX; X1 <= X1 + DY; Y1 <= Y1 - DX; end else begin X0 <= X0 + DX; Y0 <= Y0 + DY; end if( Row0 ) begin RX0 <= -RDX * 18'd319; RY0 <= RDY * 18'd231; RX1 <= -RDX * 18'd319; RY1 <= RDY * 18'd237; end else if( Col0 ) begin RX0 <= RX1 + RDY; RY0 <= RY1 - RDX; RX1 <= RX1 + RDY; RY1 <= RY1 - RDX; end else begin RX0 <= RX0 + RDX; RY0 <= RY0 + RDY; end if( Row0 ) begin GX0 <= -GDX * 18'd300; GY0 <= GDY * 18'd221; GX1 <= -GDX * 18'd300; GY1 <= GDY * 18'd221; end else if( Col0 ) begin GX0 <= GX1 + GDY; GY0 <= GY1 - GDX; GX1 <= GX1 + GDY; GY1 <= GY1 - GDX; end else begin GX0 <= GX0 + GDX; GY0 <= GY0 + GDY; end end end /* * select bits to XOR based on SW0 */ wire [17:0] SRX1 = SW0 ? RX0 : RX0 << 1; wire [17:0] SGX1 = SW0 ? GX0 : GX0 << 1; wire [17:0] SX1 = SW0 ? X0 : X0 << 1; wire [17:0] SRY1 = SW0 ? RY0 : RY0 << 1; wire [17:0] SGY1 = SW0 ? GY0 : GY0 << 1; wire [17:0] SY1 = SW0 ? Y0 : Y0 << 1; /* * select squares or lines based on SW1 */ wire RB = SW1 ? (SRX1[17] ^ SRY1[17]) : SRX1[17]; wire GB = SW1 ? (SGX1[17] ^ SGY1[17]) : SGX1[17]; wire BB = SW1 ? ( SX1[17] ^ SY1[17]) : SX1[17]; /* * output HSync/VSync and RGB data */ always @(posedge Clk) begin HSync <= HSync0; VSync <= VSync0; if( screen_active ) begin R <= ~BB & (RB | (GB & ~RB)); G <= GB & ~BB & ~RB; B <= BB; end else begin B <= 0; G <= 0; R <= 0; end end endmodule
Файл генератор VGA развертки:
Verilog Code:
/* * VGA timing generator * * (C) Arlet Ottens */ module vga( Reset, VSync, HSync, Clk50, PClk, Row, Col, Row0, Col0, Active ); input Reset; output VSync; output HSync; input Clk50; // outputs to data generator output PClk; // pixel clock output Row0; // pulse when Row == 0 output Col0; // pulse when Col == 0 output [9:0] Col; // next column output [8:0] Row; // next row output Active; // reg PClk; // pixel clock reg Row0; // pulse when Row == 0 reg Col0; // pulse when Col == 0 reg [12:0] HClock; reg [11:0] VClock; assign Col = HClock[9:0]; assign Row = VClock[8:0]; assign Active = HClock[11] & VClock[10]; assign VSync = VClock[9]; assign HSync = HClock[10]; /* * VGA timing information. */ parameter HSYNC = 10'd95, HBACK = 10'd47, HACTIVE = 10'd639, HFRONT = 10'd15; parameter VSYNC = 9'd1, VBACK = 9'd28, VACTIVE = 9'd479, VFRONT = 9'd9; wire HStart = ( HClock == {3'b101, HBACK} ); wire VStart = ( VClock == {3'b101, VBACK} ); /* * make 25 MHz pixelclock */ always @(posedge Clk50) PClk <= Reset ? 0 : ~PClk; /* * Horizontal timing state machine */ always @(posedge Clk50) begin if( Reset ) begin HClock <= 0; Col0 <= 0; end else if( PClk ) begin casex( HClock ) { 3'bxx0, HSYNC } : HClock <= { 3'b101, 10'd0 }; { 3'b101, HBACK } : HClock <= { 3'b011, 10'd0 }; { 3'bx11, HACTIVE } : HClock <= { 3'b001, 10'd0 }; { 3'b001, HFRONT } : HClock <= { 3'b100, 10'd0 }; default: HClock <= { HClock[12:10], HClock[9:0] + 10'd1 }; endcase end Col0 <= HStart; end /* * Vertical timing state machine */ always @(posedge Clk50) begin if( Reset ) begin VClock <= 0; Row0 <= 0; end else if( PClk & HStart ) begin casex ( VClock ) { 3'bxx0, VSYNC } : VClock <= { 3'b101, 9'd0 }; { 3'b101, VBACK } : VClock <= { 3'b011, 9'd0 }; { 3'bx11, VACTIVE } : VClock <= { 3'b001, 9'd0 }; { 3'b001, VFRONT } : VClock <= { 3'b100, 9'd0 }; default: VClock <= { VClock[11:9], VClock[8:0] + 9'd1}; endcase end Row0 <= HStart & VStart; end endmodule
Исходный код для FPGA: vga_demo.zip
Всех с наступающим ДНЕМ РАДИО и ДНЕМ ПОБЕДЫ!