本文将接着《基于DE2-115的Nios V工程—按键和定时器中断控制的时钟(一)》继续介绍实验的详细操作步骤,包括创建Quartus硬件工程、Nios V系统、Nios V软件工程以及配置FPGA上板测试。
一、创建Quartus工程
1. 参考《基于DE1-SOC的Nios V工程——My First Nios V》的四、Quartus工程设计这一节的步骤,使用Quartus Standard Edition v22.1创建Quartus硬件工程KEY_Timer,FPGA器件选择DE2-115的Cyclone IV E EP4CE115F29C7,打开Quartus硬件工程。
2. 在Quartus工程路径下创建一个文件夹命名为ip,再将《基于DE2-115的Nios V工程—数码管显示》Quartus工程里的TERASIC_SEG7文件夹复制到刚才创建的ip文件夹里。
二、创建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. 搜索PIO Intel FPGA IP并双击添加,width改为4,direction为Input;并勾选Synchronously capture,Edge Type为FALLING,勾选Generate IRQ,IRQ Type为EDGE。
4. 搜索并添加Interval Timer IP组件,保持默认参数设置不变。
5. 依次将clk_0、intel_niosv_m_0、onchip_memory2_0、jtag_uart_0、SEG7_IF_0、pio_0以及timer_0重命名为clk、intel_niosv_m、onchip_memory2、jtag_uart、SEG7、key以及timer。
6. 按下图所示连接各IP,最后双击箭头处将SEG7以及key导出。
7. 修改Nios V/m IP的参数设置,勾选Enable Reset from Debug Module,Reset Agent选择onchip_memory2.s1。
8. 点击Platform Designer的菜单System—— Assign Base Addresses给每个模块分配地址空间。
9. Message栏仍然有错误提示,将intel_niosv_m的ndm_reset_in信号与系统rest信号连接起来。最后点击窗口右下角的Generate HDL按钮生成qsys文件。
10. 将Nios V系统添加到顶层设计,点击Quartus菜单Project——Add/remove Files in Project...,将niosv.qsys文件就可以将该文件添加进工程里面去。
11. 点击Quartus菜单File——>New——>Verilog HDL File——>OK,创建niosv_hex.v顶层文件。在该.v文件里面输入如下代码并保存。
module KEY_Timer(
input CLOCK_50,
input [3:0] KEY,
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
.reset_reset_n(1'b1), //reset.reset_n
.seg7_conduit_end_export({
HEX7P,HEX7,HEX6P,HEX6,
HEX5P,HEX5,HEX4P,HEX4,
HEX3P,HEX3,HEX2P,HEX2,
HEX1P,HEX1,HEX0P,HEX0}), //seg7_conduit_end.export
.key_external_connection_export (KEY) // key_external_connection.export
);
endmodule
12. 点击Analysis & Synthesis进行分析与综合。
13. 按DE2-115开发板的相关引脚进行引脚分配,具体内容如下;最后再点击Start Compilation图标编译Quartus工程,最终生成.sof文件。
三、创建Nios V软件工程
1. 参考《基于DE1-SOC的Nios V工程——My First Nios V》的五、创建Nios V 软件工程这一节步骤,在KEY_Timer工程文件夹路径下新建software文件。
2. 运行niosv-bsp -c -t=hal --sopcinfo=niosv.sopcinfo software/interrupt_clock_bsp/settings.bsp命令在software文件夹创建Nios V BSP。
3. 在software文件夹路径下再创建interrupt_clock文件夹,并在interrupt_clock文件夹下创建空白main.c文件。
4. 运行niosv-app -a=software/interrupt_clock -b=software/interrupt_clock_bsp -s=software/interrupt_clock/main.c命令在interrupt_clock文件夹生成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命名为interrupt_clock,Location指向interrupt_clock所在文件夹,Project Type选择CMake driven下的Empty Project;接下来的Select Configurations窗口保持默认设置不变,点击Finish即可。
7. 在main.c文件里面添加上一期1.3.2小节的代码1并保存。如下所示代码:
#include <stdio.h>
#include "system.h"
#include "alt_types.h"
#include "sys/alt_irq.h"
#include "altera_avalon_pio_regs.h"
#include "altera_avalon_timer_regs.h"
alt_u16 flag_second; //计时器1s标志位
alt_u8 edge_capture_value; //存储按键边沿捕获寄存器中的值
alt_u16 second; //计时60s判断
alt_u16 minute; //计时60分钟
alt_u16 hour; //计时24小时
alt_u16 running=1; //时钟运行状态,1表示运行,0表示暂停
alt_u8 seg[10] = {0x40,0x79,0x24,0x30,0x19,0x12,0x02,0x78,0x00,0x10}; //数码管段显示0~9定义
/*按键中断服务程序*/
static void key_interrupts(void* isr_context){
edge_capture_value = IORD(KEY_BASE, 3); //读取边沿捕获寄存器中的值,存到edge_capture_value
IOWR(KEY_BASE, 3, 0x0); //清除边沿捕获寄存器
if (edge_capture_value) { //edge_capture_value为非0
running = !running; //切换Timer运行状态
}
}
/*按键I/O初始化*/
void init_key(){
edge_capture_value = 0x00; //edge_capture_value清0
IOWR(KEY_BASE, 2, 0xf); //使能4个按键中断
IOWR(KEY_BASE, 3, 0x0); //清除边沿捕获寄存器
alt_ic_isr_register( //注册中断函数
KEY_IRQ_INTERRUPT_CONTROLLER_ID,
KEY_IRQ,
key_interrupts,
NULL, 0x0);
}
//定时器中断服务程序
static void handle_Timer_interrupts(void* isr_context)
{
IOWR(TIMER_BASE, 0, 0x00); //清除状态寄存器
if(running){
if(flag_second < 1000)
flag_second++; //计时1s,定时器中断默认周期值为1ms,则计数1000次就为1s
else
{
flag_second = 0;
second++;
} //每计时1s,second递增1
if(second > 59)
{
second = 0;
minute++;
} //计时60秒
if(minute > 59)
{
minute = 0;
hour++;
} //计时60分钟
if(hour > 23)
{
hour = 0;
} //计时24小时
}
}
//定时器初始化
void init_timer(void)
{
IOWR(TIMER_BASE, 0, 0x00); //清除状态寄存器
IOWR(TIMER_BASE, 1, 0x07); //使能中断,启动计数器开始计数并连续运行
alt_ic_isr_register( //注册中断函数
TIMER_IRQ_INTERRUPT_CONTROLLER_ID,
TIMER_IRQ,
handle_Timer_interrupts,
NULL, 0x00);
}
//主函数
int main (void)
{
printf("Use the Interval Timer to implement a 24-hour clock and display on the 7-segment displays.\n"); //在Nios V Command Shell窗口打印信息
flag_second = 0;
init_timer(); //初始化定时器
init_key(); //初始化按键I/O
while(1)
{
IOWR(SEG7_BASE, 0, seg[second%10]); //将时钟的秒钟的个位显示在HEX0上
IOWR(SEG7_BASE, 1, seg[second/10]); //将时钟的秒钟的十位显示在HEX1上
IOWR(SEG7_BASE, 2, seg[minute%10]); //将时钟的分钟的个位显示在HEX2上
IOWR(SEG7_BASE, 3, seg[minute/10]); //将时钟的分钟的十位显示在HEX3上
IOWR(SEG7_BASE, 4, seg[hour%10]); //将时钟的小时的个位显示在HEX4上
IOWR(SEG7_BASE, 5, seg[hour/10]); //将时钟的小时的十位显示在HEX5上
}
return 0;
}
8. 左键选中interrupt_clock,点击Build Project编译main.c,编译完成可以看到生成interrupt_clock.elf文件。
四、配置FPGA与测试
1. 点击Quartus软件工具栏的Tools--> Programmer,烧录Quartus硬件工程的KEY_Timer.sof文件。
2. 在Nios V Command Shell里运行juart-terminal。
3. 在RiscFree IDE窗口左键选中interrupt_clock,选择菜单里的Run As-->3 Ashling RISC-V(auto-detect)Hardware Debugging,烧录.elf并运行程序。
4. 运行完成后在Nios V Command Shell会打印输出:Use the Interval Timer to implement a 24-hour clock and display on the 7-segment displays.
5. 在DE2-115开发板会观察到以下现象:
-
HEX[1:0]显示时钟的秒,每隔一秒计数值加1,从0~59循环计数;
-
HEX[3:2]显示时钟的分,秒钟计数每计数60秒,分钟的值加1,从0~59循环计数;
-
HEX[5:4]显示时钟的时,分钟每计数60分,时钟的值加1,从0~24循环计数。
-
按KEY[3:0]中的任意一个,时钟暂停运行;再按任意一个,时钟从暂停的时间开始恢复运行。