当前位置:   article > 正文

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

基于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读写地址的宽度、复位后所有七段数码管的状态、数码管是高电平还是低电平点亮,提高了可重用性。   

  1. module SEG7_IF(
  2.    //===== avalon MM s1 slave (read/write)
  3.    // write
  4.    s_clk,
  5.    s_address,
  6.    s_read,
  7.    s_readdata,
  8.    s_write,
  9.    s_writedata,
  10.    s_reset,
  11.    //
  12.    //===== avalon MM s1 to export (read)
  13.    // read/write
  14.    SEG7
  15. )
  16. ;
  17. /*****************************************************************************
  18. *                           Parameter Declarations                          *
  19. *****************************************************************************/
  20. parameter  SEG7_NUM  =   8;
  21. parameter  ADDR_WIDTH  =  3;
  22. parameter  DEFAULT_ACTIVE  =   1;
  23. parameter  LOW_ACTIVE  =   1;      
  24. ......  
  25. /*****************************************************************************
  26. *                             Port Declarations                             *
  27. *****************************************************************************/
  28. // s1
  29. input  s_clk;
  30. input  [(ADDR_WIDTH-1):0]  s_address;
  31. input  s_read;
  32. output  [7:0]  s_readdata;
  33. input  s_write;
  34. input  [7:0]  s_writedata;
  35. input  s_reset;
  36. //===== Interface to export
  37. // s1
  38. 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中。

  1. /*****************************************************************************
  2. *                             Internal Wire/Register                         *
  3. *****************************************************************************/
  4. reg    [7:0]        base_index;
  5. reg    [7:0]        write_data;
  6. reg    [7:0]        read_data;
  7. reg    [(SEG7_NUM*8-1):0]  reg_file;
  8. /*****************************************************************************
  9. *                            Sequence logic                                  *
  10. *****************************************************************************/
  11. always @ (negedge s_clk)
  12. begin
  13.  if (s_reset)
  14.  begin
  15.    integer i;
  16.    for(i=0;i<SEG7_NUM*8;i=i+1)
  17.    begin
  18.      reg_file[i] = (DEFAULT_ACTIVE)?1'b1:1'b0; // trun on or off
  19.    end
  20.  end
  21.  else if (s_write)
  22.  begin
  23.    integer j;
  24.    write_data = s_writedata;
  25.    base_index = s_address;
  26.    base_index = base_index << 3;
  27.    for(j=0;j<8;j=j+1)
  28.    begin
  29.      reg_file[base_index+j] = write_data[j];
  30.    end
  31.  end
  32.  else if (s_read)
  33.  begin
  34.    integer k;
  35.    base_index = s_address;
  36.    base_index = base_index << 3;
  37.    for(k=0;k<8;k=k+1)
  38.    begin
  39.      read_data[k] = reg_file[base_index+k];
  40.    end
  41.  end  
  42. end
  43. /*****************************************************************************
  44. *                            Combinational logic                            *
  45. *****************************************************************************/
  46. assign SEG7 = (LOW_ACTIVE)?reg_file:~reg_file;
  47. 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应用程序。在应用程序的开头,要包含必要的头文件。​​​​​​​

  1. #include <stdio.h>
  2. #include "system.h"
  3. #include "io.h"
  4. #include "alt_types.h"

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

  1. #ifndef ALT_ASM_SRC
  2. typedef signed char  alt_8;
  3. typedef unsigned char  alt_u8;
  4. typedef signed short alt_16;
  5. typedef unsigned short alt_u16;
  6. typedef signed long alt_32;
  7. typedef unsigned long alt_u32;
  8. typedef long long alt_64;
  9. typedef unsigned long long alt_u64;
  10. #endif

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

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

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

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

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

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

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

二、创建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程序里数码管驱动代码也以低电平点亮数码管。​​​​​​​

  1. /*******************************************************************************
  2. 功能: 控制七段数码管的显示
  3. 参数:
  4.  s_address: 七段数码管索引,如0指示第一个七段数码管,1指示第二个七段数码管
  5.  s_writedata: 映射到七段数码的8位二进制数(0:点亮, 1:熄灭)
  6.    0
  7.  ------
  8.  |    |
  9. 5| 6  |1
  10.  ------
  11.  |    |
  12. 4|    |2
  13.  ------  . 7
  14.    3      
  15. 映射矩阵:
  16.    unsigned char szMap[] = {
  17.        64, 121, 36, 48, 25, 18, 2, 120,
  18.        0, 24, 8, 3, 70, 33, 6, 14
  19.    };  //依次表示十进制数0,1,2,....9, a, b, c, d, E, F      
  20. 参数:
  21.  SEG7_NUM: 指定七段数码管的个数,默认值为8    
  22.  ADDR_WIDTH: log2(SEG7_NUM),默认值为3    
  23.  DEFAULT_ACTIVE:
  24.        复位后所有的七段数码管显示状态
  25.        1: 默认值,将所有七段数码管熄灭,即显示0
  26.        0: 将所有七段数码管显示8
  27.  LOW_ACTIVE:
  28.        1: 共阳数码管,即低电平点亮数码管
  29.        0: 共阴数码管,即高电平点亮数码管
  30. ******************************************************************************/
  31. module SEG7_IF(  
  32.  //===== avalon MM s1 slave (read/write)
  33.  // write
  34.  s_clk,
  35.  s_address,
  36.  s_read,
  37.  s_readdata,
  38.  s_write,
  39.  s_writedata,
  40.  s_reset,
  41.  //
  42.  //===== avalon MM s1 to export (read)
  43.  //read/write
  44.  SEG7
  45. );
  46.        
  47. /*****************************************************************************
  48. *                           Parameter Declarations                          *
  49. *****************************************************************************/
  50. parameter  SEG7_NUM    =   8;
  51. parameter  ADDR_WIDTH    =  3;    
  52. parameter  DEFAULT_ACTIVE  =   1;
  53. parameter  LOW_ACTIVE    =   1;
  54. /*****************************************************************************
  55. *                             Internal Wire/Register                         *
  56. *****************************************************************************/
  57. reg    [7:0]        base_index;
  58. reg    [7:0]        write_data;
  59. reg    [7:0]        read_data;
  60. reg    [(SEG7_NUM*8-1):0]  reg_file;
  61. /*****************************************************************************
  62. *                             Port Declarations                             *
  63. *****************************************************************************/
  64. // s1
  65. input            s_clk;
  66. input  [(ADDR_WIDTH-1):0]  s_address;
  67. input            s_read;
  68. output  [7:0]        s_readdata;
  69. input            s_write;
  70. input  [7:0]        s_writedata;
  71. input            s_reset;
  72. //===== Interface to export
  73. // s1
  74. output  [(SEG7_NUM*8-1):0]  SEG7;
  75. /*****************************************************************************
  76. *                            Sequence logic                                  *
  77. *****************************************************************************/
  78. always @ (negedge s_clk)
  79. begin  
  80.  if (s_reset)
  81.  begin
  82.    integer i;
  83.    for(i=0;i<SEG7_NUM*8;i=i+1)
  84.    begin
  85.      reg_file[i] = (DEFAULT_ACTIVE)?1'b1:1'b0; // trun on or off
  86.    end
  87.  end
  88.  else if (s_write)
  89.  begin
  90.    integer j;
  91.    write_data = s_writedata;
  92.    base_index = s_address;
  93.    base_index = base_index << 3;
  94.    for(j=0;j<8;j=j+1)
  95.    begin
  96.      reg_file[base_index+j] = write_data[j];
  97.    end
  98.  end
  99.  else if (s_read)
  100.  begin
  101.    integer k;
  102.    base_index = s_address;
  103.    base_index = base_index << 3;
  104.    for(k=0;k<8;k=k+1)
  105.    begin
  106.      read_data[k] = reg_file[base_index+k];
  107.    end
  108.  end  
  109. end
  110. /*****************************************************************************
  111. *                            Combinational logic                            *
  112. *****************************************************************************/
  113. assign SEG7 = (LOW_ACTIVE)?reg_file:~reg_file;
  114. assign s_readdata = read_data;
  115. 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文件里面输入如下代码并保存。​​​​​​​

  1. module niosv_hex(
  2.  input CLOCK_50,
  3.  output [6:0] HEX0,
  4.  output [6:0] HEX1,
  5.  output [6:0] HEX2,
  6.  output [6:0] HEX3,
  7.  output [6:0] HEX4,
  8.  output [6:0] HEX5,
  9.  output [6:0] HEX6,
  10.  output [6:0] HEX7
  11. );
  12. 7-SEG //
  13. wire HEX0P;
  14. wire HEX1P;
  15. wire HEX2P;
  16. wire HEX3P;
  17. wire HEX4P;
  18. wire HEX5P;
  19. wire HEX6P;
  20. wire HEX7P;
  21.    niosv u0 (
  22.        .clk_clk(CLOCK_50),           //clk.clk
  23.        .seg7_if_conduit_end_export({
  24.          HEX7P,HEX7,HEX6P,HEX6,
  25.          HEX5P,HEX5,HEX4P,HEX4,
  26.          HEX3P,HEX3,HEX2P,HEX2,
  27.          HEX1P,HEX1,HEX0P,HEX0}),   // hex_decoder_x8_conduit_end.new_signal
  28.        .reset_reset_n(1'b1)         //reset.reset_n
  29.    );
  30. 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文件里面输入以下代码并保存。​​​​​​​

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

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。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/羊村懒王/article/detail/601369
推荐阅读
相关标签
  

闽ICP备14008679号