Теперь, когда наш контроллер умеет управлять выводом по командам, поступающим по USB. Мы можем создать проект где микроконтроллер и плис будут взаимодействовать. В качестве взаимодействия, выберем простое действие: при поступлении на вход FPGA логической единицы, на экране монитора будет появляться логотип нашей компании, а при появлении логического нуля соответственно пропадать. Сам процесс создания проекта для плис я опущу, он подробно описан в нашей статье Первый проект для VE-EP4CE10E. Часть 2.. Расскажу только об используемых модулях. Схема проекта представлена ниже:
Отдельно стоит обратить внимание на новый модуль ROM: 1-PORT. Создать его можно, вызвав IP Catalog->Installed IP->Library->Basic Functions->On Chip Memory->ROM: 1-PORT.
На этой вкладке настраиваем тип памяти: 8 bits 32768 words. Остальное оставляем без изменений.
В этом месте можно настроить защелку на выходе шины данных, вход асинхронного сброса, сигнал разрешения чтения. Нам этого не надо.
На этом этапе нужно выбрать файл который будет содержаться в нашей памяти. В качестве исходного файла берем любой BMP файл размером меньше 32768 байт. В Quartus можно загрузить файлы типа hex. Поэтому конвертируем файл BMP в HEX. Для этого автор использовал утилиту J-Flash из стандартного набора отладчика SEGGER J-Link. Для того чтобы J-Link смог открыть нашу картинку, меняем расширение с BMP на BIN. Стартовый адрес указываем 0.
Нажимаем Next.
Выбираем пункт Quartus II symbol file. Это приведет к созданию символа для вставки в нашу схему. Теперь приступим к созданию кода:
Verilog Code:
module hvsync_ex( // inputs: input wire char_clock, input wire [3:0]red_in, input wire [3:0]green_in, input wire [3:0]blue_in, // outputs: output reg [11:0]char_count_, //post reg output reg [11:0]line_count_, //post reg output wire [3:0]red, output wire [3:0]green, output wire [3:0]blue, output reg hsync, output reg vsync, output reg blank ); //VGA Standart `define h_visible_area 1024 `define h_front_porch 24 `define h_sync_pulse 136 `define h_back_porch 160 `define v_visible_area 768 `define v_front_porch 3 `define v_sync_pulse 6 `define v_back_porch 29 //variables reg [11:0]char_count; reg [1:0]pixel_state; reg [11:0]line_count; reg [1:0]line_state; reg end_of_line; reg end_of_frame; //permanent comb computations: always @* begin //horizontal processing if(char_count < `h_visible_area) pixel_state = 0; //active video else if(char_count < `h_visible_area + `h_front_porch) pixel_state = 1; //front porch else if(char_count < `h_visible_area + `h_front_porch + `h_sync_pulse) pixel_state = 2; //hsync impuls else pixel_state = 3; //back porch if(char_count < `h_visible_area + `h_front_porch + `h_sync_pulse + `h_back_porch) end_of_line = 0; else end_of_line = 1; //vert processing if(line_count < `v_visible_area) line_state = 0; //active video lines else if(line_count < `v_visible_area + `v_front_porch) line_state = 1; //front porch else if(line_count < `v_visible_area + `v_front_porch + `v_sync_pulse) line_state = 2; //vsync impuls else line_state = 3; //front porch if(line_count < `v_visible_area + `v_front_porch + `v_sync_pulse + `v_back_porch) end_of_frame = 0; else end_of_frame = 1; end //synchronous process always @(posedge char_clock) begin hsync <= (pixel_state!=2'b10); vsync <= (line_state!=2'b10); blank <= (pixel_state==2'b0 && line_state==2'b0); //char_count_ <= char_count; line_count_ <= line_count; if(end_of_line) begin char_count <= 0; char_count_ <= 0; if(end_of_frame) line_count <= 0; else line_count <= line_count + 1'b1; end else begin char_count <= char_count + 1'b1; if (pixel_state==2'b0) char_count_ <= char_count_ + 1'b1; end end assign red = red_in; assign green = green_in; assign blue = blue_in; endmodule
Модуль hvsync_ex генерирует VGA развертку и положение точки на экране (char_count_, line_count_). Следующий код:
Verilog Code:
module picture_gen( //vga input [11:0]char_count, input [11:0]line_count, input wire mcu_data0, input wire blank, input wire char_clock, output reg [3:0]red_out, output reg [3:0]green_out, output reg [3:0]blue_out, //sram memory inout [7:0]data, // SRAM Data Bus output reg [18:0]adress, // SRAM Address output reg cs, // SRAM Chip Select output reg we, // SRAM Write Enable output reg oe, // SRAM Output Enable //built-in memory input [7:0]bmp_data, output reg[14:0]bmp_adress ); reg r_w=0; reg [7:0]out_data; reg [7:0]palit_adr; reg [3:0]r_state = state0; reg [11:0]x_cnt=0; reg [11:0]y_cnt=0; reg [7:0]R; reg [7:0]G; reg [7:0]B; reg [9:0]delay; localparam state0 = 4'b0001, state1 = 4'b0010, state2 = 4'b0011, state3 = 4'b0100, state4 = 4'b0101, state5 = 4'b0110, state6 = 4'b0111, state7 = 4'b1000, state8 = 4'b1001, state9 = 4'b1010;<br /> assign data = r_w ? 8'bzzzzzzzz : out_data; //tristate sram data bus //sram in/out always @(posedge char_clock) begin if (blank) begin if(r_w==1) //read sram to VGA begin if(r_state == state0) begin cs <=1'b0; we <=1'b1; adress <= ((line_count>>1)*512 | char_count>>1); oe <=1'b0; r_state <= state1; end else if(r_state == state1) begin if(mcu_data0==0) begin red_out <= (data >> 4) & 8'b00001110; green_out <= (data >> 1) & 8'b00001100; blue_out <= (data<<1) & 8'b00001110; end else begin red_out <= 0; green_out <= 0; blue_out <= 0; end oe <=1'b1; r_state <= state0; end end else //write sram begin delay <= delay+1; if(delay>1) begin delay <= 0; if(r_state == state0) begin cs <= 1'b0; oe <= 1'b1; we <= 1'b1; adress <= (y_cnt*512 + x_cnt); if(x_cnt <= 246 && y_cnt <= 89) begin bmp_adress<=(y_cnt*248+x_cnt+1078); end r_state <= state1; end else if(r_state == state1) begin if(x_cnt <= 246 && y_cnt <= 89) begin palit_adr <= bmp_data; end else begin palit_adr <= 0; end r_state <= state2; end else if(r_state == state2) begin bmp_adress <= (palit_adr*4+54); r_state <= state3; end else if(r_state == state3) begin B <= bmp_data; bmp_adress <= (palit_adr*4+55); r_state <= state4; end else if(r_state == state4) begin G <= bmp_data; bmp_adress <= (palit_adr*4+56); r_state <= state5; end else if(r_state == state5) begin R <= bmp_data; r_state <= state6; end else if(r_state == state6) begin out_data <= (R & 8'b11100000) | (G>>3 & 8'b00011000) | (B>>5 & 8'b00000111); x_cnt <= x_cnt+1; r_state <= state7; end else if(r_state == state7) begin we <=1'b0; if(x_cnt >= 512) begin x_cnt <= 0; y_cnt <= y_cnt+1; end if(y_cnt >= 384) begin y_cnt <= 0; end if((y_cnt*512 + x_cnt)==196608) begin ino<=1; end r_state <= state0; end end end end else begin red_out <= 0; green_out <= 0; blue_out <= 0; end end endmodule
Сначала копирует наш BMP файл из встроенной памяти во внешнюю SRAM. Причем делает это с учетом палитры 8 битного BMP файла. Затем выводит содержимое памяти на монитор, учитывая состояние входа mcu_data0. Если на входе логический 0, то выводится содержимое внешней памяти, если логическая 1 то черный экран.
Назначение выводов:
Ну и в завершение скриншот:
Архив проекта: test_hardware.zip