基于DE2-115的Nios V工程—数码管显示

本教程介绍设计自定义IP组件并添加到Platform Designer系统,设计基于Nios V的软件工程,控制DE2-115开发板上的8个七段数码管循环显示十六进制数0~F。

一、 系统功能实现

本实验的Nios V系统由Nios V/m处理器、On-Chip Memory内存、JTAG接口和自定义七段数码管IP组成。该系统可使用Platform Designer工具来实现,Platform Designer工具不仅包含像Nios V/m Processor、JTAG UART、PIO等组件,还可以借助Platform Designer自定义添加IP组件。

自定义SEG7_IF IP组件

DE2-115 CD-ROM/DE2_115_demonstrations/DE2_115_golden_sopc/ip/TERASIC_SEG7,这是本实验所用的IP组件并再稍作修改(后续会有说明)。

关于七段数码管的控制显示原理,还可以参考《基于FPGA的贪吃蛇游戏设计(二)——数码管驱动模块》。

SEG7_IF组件的顶层信号接口如下表:clk和reset_n为该组件的时钟和复位信号;s_address、s_write、s_writedata、s_read、s_readdata为该组件的Avalon-MM接口,通过该接口实现与Nios V处理器的数据读写;SEG7为该组件的Avalon-Conduit接口,该接口被导出到顶层设计,并与DE2-115开发板上的8个七段数码管相连接。

下图为该组件的模块示意图,每个七段数码管都有一个寄存器用来存放要显示的十六进制数0 ~ F。可以看出SEG7_IF模块主要由模块对外接口和寄存器存储电路组成。

SEG7_IF组件端口

模块端口主要分为Clock/Reset接口、Avalon-MM Slave接口和Avalon Conduit接口,以下是模块的端口信息,定义了一些参数如数码管的个数、Avalon-MM Slave读写地址的宽度、复位后所有七段数码管的状态、数码管是高电平还是低电平点亮,提高了可重用性。   

module SEG7_IF(
    //===== avalon MM s1 slave (read/write)
    // write
    s_clk,
    s_address,
    s_read,
    s_readdata,
    s_write,
    s_writedata,
    s_reset,
    //
    //===== avalon MM s1 to export (read)
    // read/write
    SEG7
)
;
/*****************************************************************************
 *                           Parameter Declarations                          *
 *****************************************************************************/
parameter  SEG7_NUM  =   8;
parameter  ADDR_WIDTH  =  3;
parameter  DEFAULT_ACTIVE  =   1;
parameter  LOW_ACTIVE  =   1;       
......   
/*****************************************************************************
 *                             Port Declarations                             *
 *****************************************************************************/
 // s1
input  s_clk;
input  [(ADDR_WIDTH-1):0]  s_address;
input  s_read;
output  [7:0]  s_readdata;
input  s_write;
input  [7:0]  s_writedata;
input  s_reset;
//===== Interface to export
// s1
output  [(SEG7_NUM*8-1):0]  SEG7;    

SEG7_IF组件寄存器读写

寄存器存储电路定义了SEG7_NUM个8位寄存器reg_file,每个寄存器存储一个数码管显示的字符,如reg_file[0]存储数码管HEX0上要显示的字符,reg_file[1]存储数码管HEX1上要显示的字符。复位时寄存器存储的数据由参数DEFAULT_ACTIVE决定,当DEFAULT_ACTIVE=1时,所有寄存器存储的数据为1'b1,即所有的数码管熄灭不显示;DEFAULT_ACTIVE=0时,所有的寄存器存储的数据为1'b0,即所有的数码管会显示字符“8”。

当进行写操作时,Avalon-MM Slave接口的写数据s_writedata将会存储到地址为s_address的寄存器中;当进行读操作时,会将地址为s_address的寄存器中的数据读取至s_readdata中。

/*****************************************************************************
 *                             Internal Wire/Register                         *
 *****************************************************************************/
reg    [7:0]        base_index;
reg    [7:0]        write_data;
reg    [7:0]        read_data;
reg    [(SEG7_NUM*8-1):0]  reg_file;
/*****************************************************************************
 *                            Sequence logic                                  *
 *****************************************************************************/
always @ (negedge s_clk)
begin
  if (s_reset)
  begin
    integer i;
    for(i=0;i<SEG7_NUM*8;i=i+1)
    begin
      reg_file[i] = (DEFAULT_ACTIVE)?1'b1:1'b0; // trun on or off 
    end
  end
  else if (s_write)
  begin
    integer j;
    write_data = s_writedata;
    base_index = s_address;
    base_index = base_index << 3;
    for(j=0;j<8;j=j+1)
    begin
      reg_file[base_index+j] = write_data[j];
    end
  end
  else if (s_read)
  begin
    integer k;
    base_index = s_address;
    base_index = base_index << 3;
    for(k=0;k<8;k=k+1)
    begin
      read_data[k] = reg_file[base_index+k];
    end
  end  
end
/*****************************************************************************
 *                            Combinational logic                            *
 *****************************************************************************/
assign SEG7 = (LOW_ACTIVE)?reg_file:~reg_file;
assign s_readdata = read_data;

SEG7_IF组件8个寄存器都是8-bit,可以使用IORD/IOWR来读写这8个寄存器。例如若要使数码管HEX0显示字符“F”,则要向第0个寄存器中写入数值0x0F,使用以下语句,其中SEG7_IF_BASE为Seg7_IF组件的基地址:

IOWR(SEG7_IF_BASE, 0, 0x0F)  //七段数码管HEX0显示字符“F”

若要使数码管HEX1显示字符“A”,则要向第1个寄存器中写入数值0x0A,使用以下语句,以此类推:

IOWR(SEG7_IF_BASE, 1, 0x0A)  //七段数码管HEX1显示字符“A”

Nios V .c应用程序

要控制七段数码管,除了需要SEG7_IF组件的驱动程序,还需要完整的Nios V .c应用程序。在应用程序的开头,要包含必要的头文件。​​​​​​​

#include <stdio.h>
#include "system.h"
#include "io.h"
#include "alt_types.h"

stdio.h文件提供了标准输入输出函数比如printf函数等,所以要包含这个头文件。IOWR函数定义在io.h文件内。alt_types.h定义了数据类型,下面是alt_types.h文件内容的片段,比如我们程序里面定义了HEX_display类型为alt_u16 ,实际上就是定义一个无符号字符类型的16位数据。

#ifndef ALT_ASM_SRC
typedef signed char  alt_8;
typedef unsigned char  alt_u8;
typedef signed short alt_16;
typedef unsigned short alt_u16;
typedef signed long alt_32;
typedef unsigned long alt_u32;
typedef long long alt_64;
typedef unsigned long long alt_u64;
#endif

system.h提供了关于Nios V系统硬件信息的描述,是硬件和软件之间的桥梁。

以下代码定义并驱动七段数码管显示十六进制数0~F,比如HEX_display[0]为十进制数64,转换为二进制数为1000000,七段数码管显示即为0,依此类推HEX_display[15]为十进制数14,表示数码管显示十六进制数F。​​​​​​​

/* 数码管控制数组HEX0~HEX7循环显示0~F,低电平点亮*/
const alt_u64 HEX_display[] = {
    64,   //7'b1000000,数码管显示0
    121,  //7'b1111001,数码管显示1
    36,   //7'b0100100,数码管显示2
    48,   //7'b0110000,数码管显示3
    25,   //7'b0011001,数码管显示4
    18,   //7'b0010010,数码管显示5
    2,    //7'b0000010,数码管显示6
    120,  //7'b1111000,数码管显示7
    0,    //7'b0000000,数码管显示8
    24,   //7'b0011000,数码管显示9
    8,    //7'b0001000,数码管显示a
    3,    //7'b0000011,数码管显示b
    70,   //7'b1000110,数码管显示c
    33,   //7'b0100001,数码管显示d
    6,    //7'b0000110,数码管显示E
    14    //7'b0001110,数码管显示F
};

在main()函数中,先使用printf语句打印输出“Realize the display function of eight HEX from 0 to F”信息,然后定义了一些变量。

int main(){
    printf("Realize the display function of eight HEX from 0 to F\n"); 
    alt_u16 i;
    alt_u32 delay;

然后是while()循环语句,对8个七段数码管进行写操作,将HEX_display[0]~HEX_display[15]这十六个值写入8个数码管,使数码管循环显示0-F。​​​​​​​

    while(1){
        for(i = 0; i<16; i++){                          //HEX共有16种状态
            IOWR(SEG7_IF_BASE, 0, HEX_display[i]);      //对HEX0进行写操作
            IOWR(SEG7_IF_BASE, 1, HEX_display[i]);      //对HEX1进行写操作
            IOWR(SEG7_IF_BASE, 2, HEX_display[i]);      //对HEX2进行写操作
            IOWR(SEG7_IF_BASE, 3, HEX_display[i]);      //对HEX3进行写操作
            IOWR(SEG7_IF_BASE, 4, HEX_display[i]);      //对HEX4进行写操作
            IOWR(SEG7_IF_BASE, 5, HEX_display[i]);      //对HEX5进行写操作
            IOWR(SEG7_IF_BASE, 6, HEX_display[i]);      //对HEX6进行写操作
            IOWR(SEG7_IF_BASE, 7, HEX_display[i]);      //对HEX7进行写操作
            delay = 0;
            while(delay < 2000000)
                delay++;
      }
    }
    return 0;
}

二、创建Quartus工程

1. 参考《基于DE1-SOC的Nios V工程—My First Nios V》,使用Quartus Standard Edition v22.1创建Quartus硬件工程,FPGA器件选择DE2-115的Cyclone IV E EP4CE115F29C7,打开Quartus硬件工程。

2. 在Quartus工程路径下创建一个文件夹命名为ip,再将DE2-115 CD-ROM/DE2_115_demonstrations/DE2_115_golden_sopc/ip里的TERASIC_SEG7文件夹复制到刚才创建的ip文件夹里。

3. 打开SEG7_IF.v文件,输入以下代码替换。相对于源码,我们将assign SEG7 = (LOW_ACTIVE)?~reg_file:reg_file修改为assign SEG7 = (LOW_ACTIVE)?reg_file:~reg_file,这样保证后续的main.c程序里数码管驱动代码也以低电平点亮数码管。​​​​​​​

/*******************************************************************************
功能: 控制七段数码管的显示
参数:
  s_address: 七段数码管索引,如0指示第一个七段数码管,1指示第二个七段数码管
  s_writedata: 映射到七段数码的8位二进制数(0:点亮, 1:熄灭)
    0
  ------
  |    |
 5| 6  |1
  ------
  |    |
 4|    |2
  ------  . 7
    3      
映射矩阵:
    unsigned char szMap[] = {
        64, 121, 36, 48, 25, 18, 2, 120, 
        0, 24, 8, 3, 70, 33, 6, 14
    };  //依次表示十进制数0,1,2,....9, a, b, c, d, E, F      
参数:
  SEG7_NUM: 指定七段数码管的个数,默认值为8    
  ADDR_WIDTH: log2(SEG7_NUM),默认值为3    
  DEFAULT_ACTIVE: 
        复位后所有的七段数码管显示状态
        1: 默认值,将所有七段数码管熄灭,即显示0
        0: 将所有七段数码管显示8
  LOW_ACTIVE: 
        1: 共阳数码管,即低电平点亮数码管 
        0: 共阴数码管,即高电平点亮数码管
******************************************************************************/
module SEG7_IF(  
  //===== avalon MM s1 slave (read/write)
  // write
  s_clk,
  s_address,
  s_read,
  s_readdata,
  s_write,
  s_writedata,
  s_reset,
  //
  //===== avalon MM s1 to export (read)
  //read/write
  SEG7
);
        
/*****************************************************************************
 *                           Parameter Declarations                          *
 *****************************************************************************/
parameter  SEG7_NUM    =   8;
parameter  ADDR_WIDTH    =  3;    
parameter  DEFAULT_ACTIVE  =   1;
parameter  LOW_ACTIVE    =   1;
/*****************************************************************************
 *                             Internal Wire/Register                         *
 *****************************************************************************/
reg    [7:0]        base_index;
reg    [7:0]        write_data;
reg    [7:0]        read_data;
reg    [(SEG7_NUM*8-1):0]  reg_file;
/*****************************************************************************
 *                             Port Declarations                             *
 *****************************************************************************/
 // s1
input            s_clk;
input  [(ADDR_WIDTH-1):0]  s_address;
input            s_read;
output  [7:0]        s_readdata;
input            s_write;
input  [7:0]        s_writedata;
input            s_reset;
//===== Interface to export
 // s1
output  [(SEG7_NUM*8-1):0]  SEG7;
/*****************************************************************************
 *                            Sequence logic                                  *
 *****************************************************************************/ 
always @ (negedge s_clk)
begin  
  if (s_reset)
  begin
    integer i;
    for(i=0;i<SEG7_NUM*8;i=i+1)
    begin
      reg_file[i] = (DEFAULT_ACTIVE)?1'b1:1'b0; // trun on or off 
    end
  end
  else if (s_write)
  begin
    integer j;
    write_data = s_writedata;
    base_index = s_address;
    base_index = base_index << 3;
    for(j=0;j<8;j=j+1)
    begin
      reg_file[base_index+j] = write_data[j];
    end
  end
  else if (s_read)
  begin
    integer k;
    base_index = s_address;
    base_index = base_index << 3;
    for(k=0;k<8;k=k+1)
    begin
      read_data[k] = reg_file[base_index+k];
    end
  end  
end
/*****************************************************************************
 *                            Combinational logic                            *
 *****************************************************************************/
assign SEG7 = (LOW_ACTIVE)?reg_file:~reg_file;
assign s_readdata = read_data;
endmodule

三、创建Nios V系统

1. 参考《基于DE1-SOC的Nios V工程——My First Nios V》,添加Nios V/m Processor Intel FPGA IP、On-Chip Memory (RAM or ROM) Intel FPGA IP以及JTAG UART Intel FPGA IP。其中On-Chip Memory (RAM or ROM) Intel FPGA IP的Total Memory Size修改为204800,其余IP等参数不变。

2. 添加自定义SEG7_IF IP组件:在Terasic Technologies Inc./DE2-115里可以看到自定义创建的SEG7_IF IP组件,双击添加该IP,保持默认参数不变。

3. 依次将clk_0、intel_niosv_m_0、onchip_memory2_0、jtag_uart_0以及SEG7_IF_0重命名为clk、intel_niosv_m、onchip_memory2、jtag_uart以及SEG7_IF。

4. 按下图所示连接各IP,最后双击箭头处将SEG7_IF导出。

5. 修改Nios V/m IP的参数设置,勾选Enable Reset from Debug Module,Reset Agent选择onchip_memory2.s1。

6. 点击Platform Designer的菜单System—>Assign Base Addresses给每个模块分配地址空间。

7. Message栏仍然有错误提示,将intel_niosv_m的ndm_reset_in信号与系统reset信号连接起来。最后点击窗口右下角的Generate HDL按钮生成qsys文件。

8. 点击Quartus菜单Project—>Add/remove Files in Project,将niosv.qsys文件就可以将该文件添加进工程里面去。

9. 点击Quartus菜单File—>New—>Verilog HDL File—>OK,创建niosv_hex.v顶层文件。在该.v文件里面输入如下代码并保存。​​​​​​​

module niosv_hex(
  input CLOCK_50,
  output [6:0] HEX0,
  output [6:0] HEX1,
  output [6:0] HEX2,
  output [6:0] HEX3,
  output [6:0] HEX4,
  output [6:0] HEX5,
  output [6:0] HEX6,
  output [6:0] HEX7
);
 7-SEG //
wire HEX0P;
wire HEX1P;
wire HEX2P;
wire HEX3P;
wire HEX4P;
wire HEX5P;
wire HEX6P;
wire HEX7P;
    niosv u0 (
        .clk_clk(CLOCK_50),           //clk.clk
        .seg7_if_conduit_end_export({
          HEX7P,HEX7,HEX6P,HEX6,
          HEX5P,HEX5,HEX4P,HEX4,
          HEX3P,HEX3,HEX2P,HEX2,
          HEX1P,HEX1,HEX0P,HEX0}),   // hex_decoder_x8_conduit_end.new_signal
        .reset_reset_n(1'b1)         //reset.reset_n
    );
endmodule   

10. 点击Analysis & Synthesis进行分析与综合。

11. 按DE2-115开发板的相关引脚进行引脚分配,最后再点击Start Compilation编译Quartus工程,最终生成.sof文件。

四、创建Niov软件工程

1. 参考《基于DE1-SOC的Nios V工程——My First Nios V》,在niosv_hex工程文件夹路径下新建software文件。

2. 运行niosv-bsp -c -t=hal --sopcinfo=niosv.sopcinfo software/hex_bsp/settings.bsp命令在software文件夹创建Nios V BSP。

3. 在software文件夹路径下再创建hex文件夹,并在hex文件夹下创建空白main.c文件。

4.运行niosv-app -a=software/hex -b=software/hex_bsp -s=software/hex/main.c命令在hex文件夹生成CMakeLists.txt文件。

5. 打开Quartus v22.1.2下的RiscFree IDE,workspace指向software文件夹路径。然后点击Launch启动软件。

6. 点击Create a project,在New Project窗口,选择C/C++下的C Project;在打开的C Project窗口,Project name命名为hex,Location指向hex所在文件夹,Project Type选择CMake driven下的Empty Project;接下来的Select Configurations窗口保持默认设置不变,点击Finish即可。

7. 在main.c文件里面输入以下代码并保存。​​​​​​​

#include <stdio.h>
#include "system.h"
#include "io.h"
#include "alt_types.h"
/* 数码管控制数组HEX0~HEX7循环显示0~F,低电平点亮*/
const alt_u64 HEX_display[] = {
    64,   //7'b1000000,数码管显示0
    121,  //7'b1111001,数码管显示1
    36,   //7'b0100100,数码管显示2
    48,   //7'b0110000,数码管显示3
    25,   //7'b0011001,数码管显示4
    18,   //7'b0010010,数码管显示5
    2,    //7'b0000010,数码管显示6
    120,  //7'b1111000,数码管显示7
    0,    //7'b0000000,数码管显示8
    24,   //7'b0011000,数码管显示9
    8,    //7'b0001000,数码管显示a
    3,    //7'b0000011,数码管显示b
    70,   //7'b1000110,数码管显示c
    33,   //7'b0100001,数码管显示d
    6,    //7'b0000110,数码管显示E
    14    //7'b0001110,数码管显示F
};
//
//
// function: main
// description: perform cyclically lighting the eight HEXs from 0 to F
// parameter: none
// return: 0
//
//
int main(){
    printf("Realize the display function of eight HEX from 0 to F\n");   //在Nios V console窗口打印信息
    alt_u16 i;
    alt_u32 delay;
    while(1){
        for(i = 0; i<16; i++){                          //HEX共有16种状态
            IOWR(SEG7_IF_BASE, 0, HEX_display[i]);      //对HEX0进行写操作
            IOWR(SEG7_IF_BASE, 1, HEX_display[i]);      //对HEX1进行写操作
            IOWR(SEG7_IF_BASE, 2, HEX_display[i]);      //对HEX2进行写操作
            IOWR(SEG7_IF_BASE, 3, HEX_display[i]);      //对HEX3进行写操作
            IOWR(SEG7_IF_BASE, 4, HEX_display[i]);      //对HEX4进行写操作
            IOWR(SEG7_IF_BASE, 5, HEX_display[i]);      //对HEX5进行写操作
            IOWR(SEG7_IF_BASE, 6, HEX_display[i]);      //对HEX6进行写操作
            IOWR(SEG7_IF_BASE, 7, HEX_display[i]);      //对HEX7进行写操作
            delay = 0;
            while(delay < 2000000)
                delay++;
      }
    }
    return 0;
}

8. 左键选中hex,点击Build Project编译main.c,编译完成可以看到生成hex.elf文件。

五、 配置FPGA与测试

1. 点击Quartus软件工具栏的Tools--> Programmer,烧录Quartus硬件工程的niosv_hex.sof文件。

2. 在Nios V Command Shell里运行juart-terminal。

3. 在RiscFree IDE窗口左键选中hex,选择菜单里的Run As-->3 Ashling RISC-V(auto-detect)Hardware Debugging,烧录.elf并运行程序。

4. 运行完成后即可在DE2-115开发板上看到8个七段数码管循环显示0~F;同时在Nios V Command Shell会打印输出:Realize the display function of eight HEX from 0 to F。

  • 28
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,以下是基于DE2-115平台VGA显示图片的详细步骤: 1. 打开Quartus II软件,创建一个新工程。 2. 在工程中添加一个VHDL文件,用于实现VGA控制器。下面是一个简单的VGA控制器代码示例: ``` library IEEE; use IEEE.STD_LOGIC_1164.ALL; entity VGA_Controller is Port ( clk : in STD_LOGIC; h_sync : out STD_LOGIC; v_sync : out STD_LOGIC; red : out STD_LOGIC_VECTOR (3 downto 0); green : out STD_LOGIC_VECTOR (3 downto 0); blue : out STD_LOGIC_VECTOR (3 downto 0); x : out STD_LOGIC_VECTOR (9 downto 0); y : out STD_LOGIC_VECTOR (9 downto 0)); end VGA_Controller; architecture Behavioral of VGA_Controller is signal vga_clk : std_logic; signal h_count : integer range 0 to 799 := 0; signal v_count : integer range 0 to 524 := 0; begin vga_clk <= clk; process(vga_clk) begin if rising_edge(vga_clk) then if h_count = 799 then h_count <= 0; if v_count = 524 then v_count <= 0; else v_count <= v_count + 1; end if; else h_count <= h_count + 1; end if; end if; end process; h_sync <= '1' when (h_count >= 656 and h_count <= 752) else '0'; v_sync <= '1' when (v_count >= 490 and v_count <= 492) else '0'; red <= "1111"; green <= "0000"; blue <= "0000"; x <= std_logic_vector(to_unsigned(h_count, 10)); y <= std_logic_vector(to_unsigned(v_count, 10)); end Behavioral; ``` 这个VGA控制器代码示例中,使用了默认的640x480分辨率和60Hz刷新率。其中,h_sync和v_sync分别表示水平同步信号和垂直同步信号,red、green和blue分别表示红、绿、蓝三个颜色通道,x和y表示当前像素的坐标。 3. 在工程中添加一个图片文件,将其转换为灰度图像并调整分辨率为640x480。可以使用Matlab等工具进行图像处理和转换。 4. 在VGA控制器代码中添加一个ROM模块,用于存储转换后的图像数据。下面是一个简单的ROM模块代码示例: ``` library IEEE; use IEEE.STD_LOGIC_1164.ALL; use IEEE.NUMERIC_STD.ALL; entity ROM is Port ( clk : in STD_LOGIC; address : in STD_LOGIC_VECTOR (17 downto 0); data : out STD_LOGIC_VECTOR (7 downto 0)); end ROM; architecture Behavioral of ROM is type ROM_array is array (0 to 307199) of std_logic_vector(7 downto 0); constant ROM_data : ROM_array := ( -- 图像数据 ); begin process(clk) begin if rising_edge(clk) then data <= ROM_data(to_integer(unsigned(address))); end if; end process; end Behavioral; ``` 在ROM模块中,使用一个ROM_array类型的常量存储转换后的图像数据,通过address输入读取对应的像素数据。 5. 在VGA控制器代码中,将ROM模块的输出与红、绿、蓝三个颜色通道连接起来,实现将像素数据输出到VGA显示器。 6. 在Quartus II软件中,进行引脚分配,将VGA控制器的输出信号与DE2-115板子上的VGA接口相连。 7. 编译工程生成.sof文件,并将其下载到DE2-115板子中。 完成以上步骤后,即可在DE2-115的VGA显示器上看到输出的图像。需要注意的是,因为DE2-115FPGA资源有限,可能无法一次性显示完整的640x480分辨率的图像。可以将图像分成若干个区域分别输出,或者降低分辨率以适应FPGA资源。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值