SOPC之NiosⅡ系统(五)

NIOS Ⅱ系统实例 

目录

2.创建BSP工程

2.1 创建BSP工程

2.2 BSP Editor

2.3 创建C代码文件

3.Nios Ⅱ实例

3.1 Hello NIOS Ⅱ

3.2 System ID与Timestamp

3.3 蜂鸣器定时鸣叫

3.4 拨码开关输入GIO控制

4.FPGA器件的代码固化

4.1 嵌入式软件HEX文件生成

4.2 程序存储器初始化文件加载

4.3 JIC文件生成和烧录配置


2.创建BSP工程

2.1 创建BSP工程

进行Nios Ⅱ嵌入式软件开发

点击Tool->Nios Ⅱ Software Buid Tools for Eclipse,创建工作空间

点击File->New->Nios Ⅱ Application and BSP from Template新建项目

点击Target hardware information中的[...] 按钮选择Quartus工程下的.sopcinfo文件,Nios II SBT for Eclipse软件会自动识别Qsys系统中CPU的名称

然后给创建的 Nios Ⅱ工程命名(名称需要以英文字母开头,可以包含字母、数字和下划线,不能有中文及特殊字符)

在取消勾选Use default location后可以修改然后将工程存放的位置

在Project template一栏中列出了一些工程模板,也可以选择创建空白工程 (Bland Project)

点击下方Next系统会自动创建一个BSP(Board Support Package,板级支持包)工程,提供了访问底层硬件(Qsys 系统)的函数库

 .sopcinfo文件是Qsys系统生成时一同产生,包含了所有Qsys系统的硬件信息,将它导入到BSP工程使得BSP获得全部硬件信息

在Project Explorer下出现了nios2bsp_bsp工程,可以看到这个文件夹下包含了各种和当前Qsys系统相关的板级动源文件和头文件,供应用软件调用。

头文件system.h将Qsys系统中的 Nios Ⅱ处理器和所有外设的名称、基地址、中断有无以及优先级号码等相关硬件信息进行了定义。

其中nios2bsp是C/C++应用工程,nios2bsp_bsp是函数库

2.2 BSP Editor

在BSPEditor中可以对板级驱动层进行一些定制化的配置,比如代码裁剪、标准输入/输出外设和定时器外设的设置等。

在Project Explorer下工程名右键点击Nios Ⅱ->BSP Editor

在左侧一栏选中Common,然后在右侧勾选两个选项:

enable_reduced_device_drivers: BSP为处理器的外设提供了两个版本的驱动库:一种是执行速度快但代码量比较大的版本:另一种是封装小的版本。默认使用的是代码量大的版本,这里通过[enable reduced device drivers]选项来选择封装小的版本,从而减少代码量。

enalbe_small_c_library: 完整的ANSIC标准库通常不适用于嵌入式系统,BSP提供了一系列经过裁剪的ANSIC标准库,占用资源比较少,通过[enalbe small c lbrary]选项来选择精简的ANSIC标准库。

在左侧一栏选中Settings,将右侧两个选项(默认勾选) 取消勾选:

enable_c_plus_plus: 使用 C 语言来编写软件程序,因此不需要使能 C+。

enable clean exit: 当选中该选项时,系统库在主函数main()返回时会调用exit()。调用 exit0时,首先会清理I/O的缓冲区,然后再调用exit()。当不选中该选项时,系统库会只调用exit(),这样将会节省程序空间。对于嵌入式系统程序来说,一般都不会从 main()返回,所以可以不勾选该选项。

设置完成后先点击Generate然后再点击Exit

2.3 创建C代码文件

在Project Explorer下工程名右键点击New->Source File新建C代码源文件

 

 在Project Explorer下工程名右键Build Project (CTRL+B)进行软件工程编译

3.Nios Ⅱ实例

3.1 Hello NIOS Ⅱ

通过JTAG UART在Nios Console中每隔3s打印一串“Hello NIOS II”的字符串

#include "system.h"     //定义Qsys中各个外设的地址、中断优先级等基本硬件信息
#include <stdio.h>      //定义标准输入、输出函数
#include <unistd.h>     //包含了延时函数usleep()函数的生命

///
//功能:每隔3s通过JTAG UART打印一条字符串“Hello NIOS Ⅱ”
///

int main(void)
{
	while(1)
	{
		printf("Hello NIOS II!\n");
		usleep(3000000);
	}
	return 0;
}

printf对应的设备在BSP Editor中设定

下载验证

先把.sof文件下载到开发板中,下载完成后开发板上没有任何实验现象

再下载.elf 文件(注意一定要先下载 sof 文件,再下载 elf 文件),Nios Ⅱ SBT for Eclipse中工具栏Run->Run AS->Nios II Hardware,将编译生成可执行文件.elf下载到硬件系统中。 

如果在程序下载的过程中弹出了“Run Configurations”窗口,提示找不到与Nios II硬件系统的连接

那么在这个窗口中点击Target Connection标签,点击Refresh Connections按钮,软件使会自动识别开发板上的 Qsys 系统,并显示 Qsys 系统的相关信息,接着点击[Apply] [Run],软件会把.elf 文件下载至开发板中

 下载结束后,程序自动开始运行,在软件下方的“Nios II Console”中会打印“Hello,Nios Ⅱ!”信息

3.2 System ID与Timestamp

读取 System ID外设的两个寄存器值,一个是id 值,另一个是Testamp值

地址偏移寄存器名称读/写功能描述
0id基于Qsys系统定义的唯一的32位数值,该id值类似于校验和;不同组件、不同选项配置的Qsys系统产生不同的id值
1timestamp基于系统生成时间的唯一32位值,该值等效于从1970年1月1日以来所经过的总秒数
#include "alt_types.h"                      //对altera定义的数据类型进行宏定义声明,alt_u32表示32位的无符号整型
#include "system.h"
#include <stdio.h>  
#include "altera_avalon_sysid_qsys_regs.h"  //定义了System ID硬件寄存器访问的接口函数

///
//功能:读取System ID的ID值核timestamp值,通过JTAG UART打印
///

int main(void)
{
	//读取%sy_id值
	alt_u32 hardware_id = IORD_ALTERA_AVALON_SYSID_QSYS_ID(SYSID_BASE);                //读取定义的System ID外设的id值,SYSID_BASE是定义的System ID外设的基址
	//读取%sy_id的timestap值
	alt_u32 hardware_timestamp = IORD_ALTERA_AVALON_SYSID_QSYS_TIMESTAMP(SYSID_BASE);  //读取定义的System ID外设的Timestamp值
	
	printf("System ID is 0x%8x\n",hardware_id);
	printf("System timestamp is 0x%8x\n",hardware_timestamp);
	
	while(1);
	return 0;
}

3.3 蜂鸣器定时鸣叫

使用Timer定时器产生秒中断信号,驱动蜂鸣器发出响声

Timer 定时器组件内部有可编程的32位定时计数器,通过编程访问该组件的控制和状态寄存器实现定时中断功能。

Timer定时器外设寄存器定义

地址偏移寄存器名称

读/写

功能描述
0status读写

bit 15~2:保留

bit 1:运行指示位。当计数寄存器运行时,该位置高。

bit 0:定时结束指示位。当计数回零时,该位置高。一旦定时结束事件发生,该位置高,直到对该寄存器读写后该位清零

1control读写

bit 15~4:保留

bit 3:停止位。该位写1将停止当前定时计数功能。

bit 2:启动位。该位写1将启动定时计数功能。

bit 1:运行指示位。该位置1后,计数器清零后将继续计数,即执行连续计数;该位清0后,计数器清零后将停止计数,即只执行一次计数。

bit 0:中断表示位。该位提高后,一旦status寄存器的bit 0拉高,即产生IRQ中断。

2periodl读写定时计数周期值-1(高16位)
3periodh读写定时计数周期值-1(低16位)
4snapl读写当前计数值(高16位)
5snaph读写当前计数值(低16位)

#include "alt_types.h"                      
#include "altera_avalon_pio_regs.h"     //声明PIO外设函数,如IOWR_ALTERA_AVALON_PIO_DATA()函数
#include "altera_avalon_timer_regs.h"   //声明Timer定时器外设函数,如IOWR_ALTERA_AVALON_TIMER_STATUS()/CONTROL()
#include "sys/alt_irq.h"                //声明中断相关的函数,如all_irq_register
#include "system.h"

int flag,second;

///
//功能:Timer定时器初始化函数
///
void init_timer(void)
{
	//注册定时器中断函数
	//TIMER_IRQ是system.h中定义的Timer定时器组件的中断号,TIMER_BASE是Timer定时器组件的基址,handle_time_interrupts为中断函数
	alt_irq_register(TIMER_IRQ,TIMER_BASE,handle_time_interrupts);
	//对Timer定时器组件的control寄存器写数据7,即启动timer定时计数功能,持续循环计数产生IRQ中断中断
	IOWR_ALTERA_AVALON_TIMER_CONTROL(TIMER_BASE,7);
	//清除标志位
	flag = 0;
	//初始化函数没有对periodl和periodh寄存器进行设置,使用默认的计数值,即在Qsys中定义的1s计数周期
}


///
//功能:秒定时中断处理函数
///
static void handle_time_interrupts(void)
{
	//函数写Timer定时器组件的status寄存器,即清TO标志
	IOWR_ALTERA_AVALON_TIMER_STATUS(TIMER_BASE,0); 
	flag = 1;
	second++;
}


///
//功能:秒定时中断处理函数
///
int main(void)
{
	IOWR_ALTERA_AVALON_PIO_DATA(PIO_BEEP_BASE,0);  //拨码开关OFF
	init_timer();                                  //Timer定时器初始化函数
	
	while(1)
	{
		if(flag)
		{
			flag = 0;
			//该函数对基址为PIO_BEEP_BASE的PIO外设写数据1/0
			if(second & 0x01) IOWR_ALTERA_AVALON_PIO_DATA(PIO_BEEP_BASE,1);  //拨码开关ON
			else IOWR_ALTERA_AVALON_PIO_DATA(PIO_BEEP_BASE,0);               //拨码开关OFF
		}
	}

	return 0;
}

3.4 拨码开关输入GIO控制

使用4个拨码开关产生中断,对不同拨码开关值进行判断,相应驱动蜂鸣器发出1~4此响声

PIO组件寄存器定义

地址偏移寄存器名称

读/写

功能描述
0data读写

作为输入PIO时,读控制获取当前输入PIO的电平值;

作为输出PIO时,写控制将数据输出到PIO上

1direction读写

每个PIO引脚单独的方向控制。电平0设置PIO为输入;电平1设置PIO为输出

2interruptmask读写每个PIO引脚对应的IRQ中断使能。电平1设置IRQ中断使能
3edgecapture读写每个PIO引脚的边沿变化状态捕获

#include "alt_types.h"
#include "altera_avalon_pio_regs.h"
#include "sys/alt_irq.h"
#include "system.h"
#include <stdio.h>
#include <unistd.h>
#include <io.h>

/
//函数申明
void init_switch_pio(void);	//switch GIO初始化函数
void beep_didi(alt_u8 time);	//蜂鸣器发出“滴滴”响声函数

/
//宏定义

/
//变量申明
alt_u8 edge_capture_value;	//当前拨码开关值

/
//功  能:	拨码开关中断服务函数
/
static void handle_switch_interrupts(void)
{
    //捕获当前PIO值
    edge_capture_value = IORD_ALTERA_AVALON_PIO_DATA(PIO_SWITCH_BASE);
    //清除边沿中断标志位
    IOWR_ALTERA_AVALON_PIO_EDGE_CAP(PIO_SWITCH_BASE,0x00);
}

/
//功  能:	主函数,拨码开关从off到on拨动时,蜂鸣器发出几声清脆的“滴”响声
/
int main(void)
{

	IOWR_ALTERA_AVALON_PIO_DATA(PIO_BEEP_BASE,0);	//beep off
	init_switch_pio();	//switch GIO初始化函数
	
    while(1)
    {
    	if(~edge_capture_value & 0x01)	//蜂鸣器发出1声“滴”
        {
			beep_didi(1);
        }
    	else if(~edge_capture_value & 0x02)	//蜂鸣器发出2声“滴”
        {
			beep_didi(2);	
        }
    	else if(~edge_capture_value & 0x04)	//蜂鸣器发出3声“滴”
    	{
			beep_didi(3);
    	}
    	else if(~edge_capture_value & 0x08)	//蜂鸣器发出4声“滴”
    	{
			beep_didi(4);
    	}
    }
    return 0;
}

/
//功  能:	switch GIO初始化函数
/
void init_switch_pio(void)
{
    //初始化拨码开关状态值
    edge_capture_value = 0xff;
    //使能3个拨码开关中断
    IOWR_ALTERA_AVALON_PIO_IRQ_MASK(PIO_SWITCH_BASE, 0xff);
    //复位拨码开关边沿状态
    IOWR_ALTERA_AVALON_PIO_EDGE_CAP(PIO_SWITCH_BASE, 0x00);
    //拨码开关输入GIO中断复位申明
    alt_irq_register(PIO_SWITCH_IRQ,PIO_SWITCH_BASE,handle_switch_interrupts);
}

/
//功  能:	蜂鸣器发出“滴滴”响声函数
//参  数:	alt_u8 time 发出响声的次数
/
void beep_didi(alt_u8 time)
{
	alt_u8 i;
	for(i=0;i<time;i++)
	{
		IOWR_ALTERA_AVALON_PIO_DATA(PIO_BEEP_BASE,1);	//beep on
		usleep(20000);	//delay 100ms
		IOWR_ALTERA_AVALON_PIO_DATA(PIO_BEEP_BASE,0);	//beep off
		usleep(20000);	//delay 100ms	
	}
}

其他实例可参照参考书籍进行

4.FPGA器件的代码固化

4.1 嵌入式软件HEX文件生成

基于Nios Ⅱ处理器的FPGA器件代码固化和一般只有 FPGA 逻辑的代码固化不同

除了正常的FPGA逻辑部分代码需要固化外,还有Nios Ⅱ处理器的程序代码也需要固化。

使用FPGA片内存储器作为Nios Ⅱ处理器的代码存储器,在生成逻辑代码部分的烧录文件时,将 Nios Ⅱ处理器的代码集成在一起,就可以使用一个烧录文件完成 FPGA 器件的固化

打开工程的BSP Editor页面,选择Setting->Advanced->hal->linker,勾选allow_code_at_reset选项,然后重新编译C/C++软件工程文件和函数库文件

设置该选项的目的是在确保FPGA使用了片内存储器作Nios Ⅱ处器代码存储器时,在FPGA逻辑运行起来以后,Nios Ⅱ处理器复位后直接可以从片内存储器开始运行代码

 然后右键软件工程文件->Make Targets->Build,选择men_init-generate,点击Build

然后就可以在软件工程文件夹下发现.hex文件,为软件工程代码对应的十六进制HEX文件,即Nios Ⅱ处理器的软件代码

4.2 程序存储器初始化文件加载

进入Qsys界面,双击onchip_men组件进入配置界面的Memery initialization

勾选Initialize memory content和Enable non-default initialization file,并在User created initialization file中添加刚才生成的.hex文件

重新生成Qsys工程并且重新编译整个Quartus工程并产生新的.sof 文件

此时.sof 文件通过JTAG在线烧录到FPGA器件中则会直接运行软件程序

但.sof 文件只能在线烧录,因此需要将.sof文件转换为jic文件,实现SPI Flash的固化

4.3 JIC文件生成和烧录配置

在Quartus中选择File->Convert Programming Files

在Programming file type中选择JTAG Indirect Configuration File(.jic)

Configuration device选择配置设备,File name输入转换后的文件名(output_files 文件夹下)

在Input files to convert中

单击Flash Loader所在的行,然后单击右侧Add Device按钮在Select Devices 窗口中选择设备

单击SOF Data所在行,然后单击右侧的Add File按钮,在弹出的窗口中选择output files文件夹下的 .sof文件

完成设置后,单击 Generate 生成 *.jic,弹出如图所示的提示信息,表示成功生成jic文件

打开 Quartus的 Programmer 页面,单击Add File按钮加载的jic 文件(File下若有其他文件,请删除),并且确保勾选 Program/Configure 所在列

单击 Start 按钮执行下载操作,完成下载后,开发板默认处于不工作状态,需要重启开发板,重启后就能看到最新下载的代码已经固化到 SPI Flash 中并且掉电后重启仍然可以运行。

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值