nios ii step by step - pudn.comread.pudn.com/downloads119/doc/505597/nios_step_by_step.pdf · nios...

42
Nios Nios Nios II II II Step Step Step By By By Step Step Step NIOS NIOS NIOS II II II Step Step Step By By By Step(1)---NIOS Step(1)---NIOS Step(1)---NIOS II II II 自定义指令 可编程软核处理器最大的特点是灵活,灵活到我们可以方便的增加指令,这在其他 SOC统中做不到的,增加用户指令可以把我们系统中用软件处理时耗 费时间多的关键算法用硬逻辑 电路来实现,大大提高系统的效率,更突出的一点是:我们通过下面的逐步操作会认识到,这 是一个听起来高深,其实比较容易实现的 功能(我们站在EDA工具这个巨人肩上,风光无限 啊:),通过这一文档的介绍可以增强我们掌握 NIOS II 所有的技术手段的信心,这也是我把 NIOS II 用户指令放在最前面的用意。 用户指令就是我们让NIOS II软核完成的一个功能,这个功能由电路模块来实现,这个电路 模块是用HDL 语言描述的。它被连接到NIOS II 软核的算术逻辑部件上,下面就是示意图: 用户指令分多种,有组合逻辑指令、多周期指令、扩展指令等等,学明白一个,也就举一反三 了, Altera 提供了用户模块HDL 的模板, 通过裁减就 可以适应多种指令类型了。下面就是这个 模版的Verilog 形式,很简单吧,我想看到这里很多人会说:oh,thats easy。模块里面的内容不 要我说了,也说不尽,是你智慧表演的舞台了,八仙过海,各显神通的机会来了:) //Verilog Custom Instruction Template module __module_name(

Upload: others

Post on 24-Feb-2020

94 views

Category:

Documents


3 download

TRANSCRIPT

NiosNiosNios

Nios

IIIIII

II

StepStepStep

Step

ByByBy

By

StepStepStep

Step

NIOSNIOSNIOS

NIOS

IIIIII

II

StepStepStep

Step

ByByBy

By

Step(1)---NIOSStep(1)---NIOSStep(1)---NIOS

Step(1)---NIOS

IIIIII

II

自定义指令

可编程软核处理器最大的特点是灵活,灵活到我们可以方便的增加指令,这在其他SOC系统中做不到的,增加用户指令可以把我们系统中用软件处理时耗 费时间多的关键算法用硬逻辑

电路来实现,大大提高系统的效率,更突出的一点是:我们通过下面的逐步操作会认识到,这

是一个听起来高深,其实比较容易实现的 功能(我们站在EDA工具这个巨人肩上,风光无限

啊:),通过这一文档的介绍可以增强我们掌握 NIOS II所有的技术手段的信心,这也是我把 NIOSII 用户指令放在最前面的用意。

用户指令就是我们让NIOS II软核完成的一个功能,这个功能由电路模块来实现,这个电路

模块是用HDL语言描述的。它被连接到NIOS II软核的算术逻辑部件上,下面就是示意图:

用户指令分多种,有组合逻辑指令、多周期指令、扩展指令等等,学明白一个,也就举一反三

了, Altera提供了用户模块HDL的模板,通过裁减就 可以适应多种指令类型了。下面就是这个

模版的Verilog形式,很简单吧,我想看到这里很多人会说:oh,that's easy。模块里面的内容不

要 我说了,也说不尽,是你智慧表演的舞台了,八仙过海,各显神通的机会来了:)

//Verilog Custom Instruction Templatemodule __module_name(

clk, // CPU's master-input clk <required for multi-cycle>reset, // CPU's master asynchronous reset <required for multi-cycle>clk_en, // Clock-qualif ier <required for multi-cycle>start, // True when this instr. issues <required formulti-cycle>done, // True when instr. completes <required for variable muli-cycle>dataa, // operand A <always required>datab, // operand B <optional>n, // N-field selector <required for extended>a, // operand A selector <used for Internal register file access>b, // operand b selector <used for Internal register file access>c, // result destination selector <used for Internal register file access>readra, // register file index <used for Internal register file access>readrb, // register file index <used for Internal register file access>writerc,// register file index <used for Internal register file access>result // result <always required>);input clk;input reset;input clk_en;input start;input readra;input readrb;input writerc;input [7:0] n;input [4:0] a;input [4:0] b;input [4:0] c;input [31:0]dataa;input [31:0]datab;output[31:0]result;output done;

// Port Declaration

// Wire Declaration// Integer Declaration// ConcurrentAssignment//Always Constructendmodule

下面两张图和表可以帮助我们更深入的理解和总结:

万事开头难,那我们就挑个软的捏,来做一条组合逻辑指令吧,下面就是模块示意图:

这个模块只有两个输入,A,B,都是32BIT的,输出也是32BIT的。你可以让他们是C=A+B,

也可以C=A+1。我们给出一个HDL范例:

//Verilog Custom Instruction Templatemodule test_custom_instruction(dataa, // operand A <always required>datab, // operand B <optional>result // result <always required>);input [31:0]dataa;input [31:0]datab;output[31:0]result;

assign result=dataa+1;

endmodule

上面的模块实现A+1,很简单。

其他指令类型也就是增加了PORT口,里面处理更复杂一点。下面是多周期指令的块图:

有了以上概念,和HDL 模块的准备,我们可以进入实际操作了,打开 QUARTUS II,建一个工

程,再进入 SOPCBUILDER,建好相关的 SOPC 环境,下图是 FreeDev2.1 EP1C12+100M开发

板的一个配置

双击 CPU项就进入 CPU配置,设置用户指令我们要选最后一个表单(Custom Instructions TAB)。

见下图

在该表单中我们点击 IMPORT,来加入我们的 HDL 模块,见下图:

在IMPORT操作窗口上,点击ADD增加用户指令模块,点击read port list from files显示模块PORT。

最后点击Add to System完成用户指令的添加。

在SOPC BUILDER 中生成系统,并在QUARTUS II中重新编译整个工程,并program编程开发板

器件,这样硬件部分就完成了。下面我们就可以在NIOS II IDE中来编程使用我们的指令了。

由于我们在SOPC BUILDER 中增加了用户指令,SOPC BUILDER 生成的PTF文件中包含

了相关信息,我们在 NIOS II IDE 中增加新软件工程项目编译时,系统根据SOPC BUILDER生成的PTF文件生成了硬件系统描述的system.h。在该头 文件中系统已经为用户指令生成了相关

的宏,我们以上定制的用户指令在system.h中相关部分如下:

/** custom instruction macros**/

#define ALT_CI_TEST_CUSTOM_INSTRUCTION_N 0x00000000

#define ALT_CI_TEST_CUSTOM_INSTRUCTION(A,B)__builtin_custom_inii(ALT_CI_TEST_CUSTOM_INSTRUCTION_N,(A),(B))

注意__builtin_custom_inii是Altera 移植GCC后的built_in Function。

最后,我们创建一个Hello World工程,稍微改动一下:

#include <system.h>#include <stdio.h>

int main()

{int a,b,c;

a=1;

c=ALT_CI_TEST_CUSTOM_INSTRUCTION(a,b);printf("c=%d\n",c);

return 0;}

主要把system.h头文件包含进来。

好了,马上可以进行调试了。

NIOSNIOSNIOS

NIOS

IIIIII

II

StepStepStep

Step

ByByBy

By

StepStepStep

Step

2--2--2--

2--

理解HALHALHAL

HAL

前一讲我们实现了一个简单的用户指令,在本系列文档中我们假定用户已经正确安装NiosII IDE开发环境并能创建和调试程序,对Nios II IDE开发环境不再介绍。

目前Nios II 的开发都是在HAL(hardwareabstraction layer)的基础上进行的。在进一步介绍其

他内容之前有必要介绍HAL。HAL系统库是一个轻量级的运行环境,提供了简单的和硬件通讯的设备驱动程序。HAL API集成了ANSI C标准库,这些API允许你用标准C函数(例如:printf,fopen,fwrite等等)去存取设备。

HAL类似于ARM系统中的BSP(board-support package),提供了一个一致的设备存取界面。由

于SOPC Builder和 NIOS II IDE紧密的集成,在SOPC Builder生成硬件系统以后,NIOS II IDE 能

够自动生成对应的客户HAL系统库。更新硬 件系统设置以后,NIOS II IDE能自动更新HAL的驱动设置。在创建软件项目的时候,NIOS II IDE自动生成并管理HAL系统库。

HAL体系结构:

HAL系统库提供了下列系统服务:

1、 集成newlib。一个ANSI C标准库。(许多人都要求提供NIOS II中C库的说明,具体

可以参考 http://sources.redhat.com/newlib)

2、 设备驱动。这些设备驱动程序提供了常用设备的驱动。同时也是我们学习设备驱动

程序开发的范例。

3、 HAL API。提供了一个一致的设备存取、中断处理以及ALARM等工具。

4、 系统初始化。在main执行前完成相关的初始化任务。注意这里包含了BOOTLOAD以及程序重定位等工作。所以NIOS II开发中没有象ARM系统开发中涉及BOOTLOAD等问题。

5、 设备初始化。在main前分配设备空间,并初始化设备。

在NIOS II软件系统开发中,程序员划分为应用程序开发和设备驱动开发,从而为提高开发效率

给出一条途径(实际开发中并不一定如此划分,实际我们都身兼数职披挂上阵J)。

1、 应用开发人员使用HAL API去完成开发工作

2、 设备驱动开发人员完成设备驱动的开发并融合到HAL体系中,供应用开发人员使用

设备类型:

在以后的文档中我们会给出 NIOS II HAL设备被组织加入到双向设备链表中(在我的

BLOG:http://www.pld.com.cn/blog/blog?name=jhljs中有设备组织的分析文挡)。并不是所有的设

备被添加到同一个设备链表中而是分成了几个类型:

1、 字符模式设备

2、 时间模式设备

3、 文件系统设备

4、 以太网设备

5、 DMA设备

6、 FLASH设备

不同的设备有大体一致的模式,但内提供的设备驱动函数不同类型不一致。

在NIOS II 安装完毕以后,NIOS II IDE 中提供了上述类型的外设和相应的驱动程序。我们在

以后的文档中会分析这些设备驱动程序,从而为开发自己的设备驱动提供有力的支持和坚实的

基础。

OK,HAL就介绍这些了。点到为止,革命尚未成功,同志仍需努力。

NiosNiosNios

Nios

IIIIII

II

StepStepStep

Step

ByByBy

By

StepStepStep

Step

3--Nios3--Nios3--Nios

3--Nios

IIIIII

II

中的DMADMADMA

DMA

有了上一讲HAL的基础,我们来关注一下DMA在NIOS 中的实现和编程。DMA是个老问题

了,从8086/8088一直到现在,完成不需要CPU参与的数据搬家,源和目标可以是内存也可以是

设备,在NIOS II中通过基于HAL编程完成。

下图是三中基本的DMA传输:

在NIOS II的HAL DMA设备模式中,DMA传输被分为两类:transmit 和 receive。NIOS提供两

种设备驱动实现 transmit channels和receive channels,transmit channels把缓冲区数据发送到目标

设备, receive channels读取设备数据存放到缓冲区。

为了适应大家不同的开发环境,下面我们完成一个相对简单的DMA操作,复制SDRAM内存

缓冲区到on_chip_memory中,如果我们在库工 程属性中设置了SDRAM为主内存,那么程序中

分配的数组缓冲区就在SDRAM中,我们用指针赋值让指针指向on_chip_memory。这个操作完全

可以在程序中用memcpy来实现,我们趋简就繁,就是为了尝试一下DMAJ。首先我们在 SOPCBuilder中增加一个名字为 dma_0 的DMA设备。两个表单设置都选默认。

第二步,DMA设备有三个 PORT,两个MASTER PORT:read_master、write_master,一个 SLAVEPORT: control_port_slave。需要在 SOPC BUILDER中设置AVALONE交换总线,设置 read_master和 sdram连接, write_master 和 on_chip_memory 连接,具体见下图(交叉点为黑色)。

在sopc builder中生成系统,并在Quartus II中编译下载,硬件部分就OK了。如果你的DMA操作

不是内存到内存的,而是内存到设备,或者设备到内存,那么你需要在上面这一步中加以设置,设备只支持读写,是CPU读写还是DMA读写设备不加以区分。

在程序中,我们要使用DMA必须包含:sys/alt_dma.h。因为是内存DMA操作,所以我们必须实现transmit channels和receive channels,这在NIOS II中就

是打开两个设 备。在NIOS II IDE中生成一个以Hello World为模板的memory_dma工程项目修改

一下程序如下:

#include <stdio.h>#include <stdlib.h>#include <sys/alt_dma.h>#include "system.h"

static volatile int rx_done = 0;

/*

* Callback function that obtains notification that the data has* been received.*/static void done (void* handle, void* data){rx_done++;

}

int main (int argc, char* argv[], char* envp[])

{int rc;static char buff[256];alt_dma_txchan txchan;alt_dma_rxchan rxchan;

void* tx_data = (void*) buff; /* pointer to data to send */

void* rx_buffer = (void*) 0x01000000; /* on_chip_memory addr*/

/* Create the transmit channel */if ((txchan = alt_dma_txchan_open("/dev/dma_0")) == NULL){printf ("Failed to open transmit channel\n");exit (1);

}

/* Create the receive channel */

if ((rxchan = alt_dma_rxchan_open("/dev/dma_0")) == NULL){printf ("Failed to open receive channel\n");exit (1);

}/* Post the transmit request */if ((rc = alt_dma_txchan_send (txchan,

tx_data,128,NULL,NULL)) < 0)

{printf ("Failed to post transmit request, reason = %i\n", rc);exit (1);

}

/* Post the receive request */if ((rc = alt_dma_rxchan_prepare (rxchan,

rx_buffer,128,done,NULL)) < 0)

{printf ("Failed to post read request, reason = %i\n", rc);exit (1);

}

/* wait for transfer to complete */while (!rx_done);

printf ("Transfer successful!\n");return 0;

}我们很多人对DMA理解的很深入,在其他嵌入式领域有丰富的经验,在其他系统上的实现问题

很自然会想在NIOS II中是怎么完成的呢,比如DMA完成以 后需要中断吗?如何知道DMA传输

完成等等,在上面的程序中,实际上是通过回调函数完成的,回调函数在Windows系统的WINAPI中以及驱动开发 中被大量使用。

好了,DMA就是如此,还有一些相关的函数需要去尝试一下。尝试非常重要,在资料欠缺

的时候,需要创建环境去实验,你的理解是这样的,按这样的理解会有这样的结果,实际做一

下到底是怎样的,不符合?是理解错了吗?不断尝试,收益无限

NiosIINiosIINiosII

NiosII

StepStepStep

Step

ByByBy

By

StepStepStep

Step

(4)--PIO(4)--PIO(4)--PIO

(4)--PIO

--本篇由Mr_Don版主撰写

俗话说柿子拣软的捏 : )。我也抖胆放一篇NiosII Step by Step,给大家讲讲PIO。这只是我个

人的理解,如果写得有什么问题或不足,欢迎大家跟帖指正交流。

PIO(Parallel Input/Output)是SOPCBuilder里面一个最常用的IP之一,而且相对简单,容易上手

使用。个人感觉PIO 可类比于单片机的GPIO管脚,用于用户自己定义的外设操作。一般来说,

我们在SOPC系统中用PIO主要是因为方便,因为FPGA的管脚越来越多,内部 逻辑资源越来越

多,所以设计时几乎不用担心管脚的数目或者逻辑资源是否不够;相比之下单片机的GPIO可能

会让用户感到数目不够,有时不够方便。所以在 SOPC系统我们可以同时用很多使用PIO接口的

设备。大体来说,这些设备主要分输入和输出两类:

输入类:按钮,自定义键盘等

输出类:LED,LCD定义接口连线等

标准件:如pwm步进电机等

如上图所示,在Nios/NiosII系统中,PIO接口IP可用于输入、输出、以及双向口三种类型。另外 ,

SOPCBuilder中的PIO还支持中断 检测。不过中断检测及处理只在其作为输入设备时可用。如果

是用PIO连接一个输出设备(例如LED),则系统自动禁止中断。

这里只举一个button_pio(4位)作为输入接口的例子,如下图

可以看到,作为输入接口时,另外两个标签项也可选,先看看第二个标签项(作输出接口时另

外两个标签选项是自动禁止的):

Edge Capture Register下面的三个选项是指捕获输入动作时是检测哪一种,用户可以选择只检测

上升沿(Rising Edge),这样 PIO内部的Edge Capture Register只在有上升沿动作时接收数据。同

样道理,另外的两个选项一看便知。对于Button,我们一般倾 向于选双向沿均可监测到数据变

化。

下面的Interrupt选项是指用户选择以什么方式作为中断的输入,一种是电平变化产生中断,一种

是沿的变化产生中断。第一种方式只有在输入由低电平变 化为高电平时才有效(不过用户可以

根据需要,在做好的Nios模块外面的相应管脚上加上非门,使之由高电平变电平时生效);第二

种方式只有当 Edge Capture Register位是1(高电平)时才有用。

再来看一看第三个标签下面的东东:

这个标签下面的东西是用于仿真的。例如如果我们在测试输入设备时想给他一个激励,这里我

用的是0x0001。什么意思呢?即表示最低位的 button位为 高电平1。如果我用的激励数据是

0x0003,这又是什么意思呢?聪明的你一定会发现这表示激励使低两位的button位为1,呵呵。

下面我们来分析一个SOPCBuilder生成的HDL文件:

module led_pio (// inputs:address,chipselect,clk,reset_n,write_n,writedata,

// outputs:

out_port);

output [ 3: 0] out_port;

input [ 1: 0] address;

input chipselect;input clk;input reset_n;input write_n;input [ 3: 0] writedata;

wire clk_en;

reg [ 3: 0] data_out;wire [ 3: 0] out_port;assign clk_en = 1;//s1, which is an e_avalon_slavealways @(posedge clk or negedge reset_n)beginif (reset_n == 0)

data_out <= 0;else if (chipselect && ~write_n && (address = = 0))

data_out <= writedata[3 : 0];end

assign out_port = data_out;

endmodule

你一定立刻注意到这段描述的输入端口怎么有点似曾相识?没错,avalon总线的要求就是这样!

回忆一下在SOPCBuilder下加入用户自定义逻辑的 要求是不是必须得有这几个端口?呵呵。上

面的代码并不难看懂,大家可以看到led_pio是如何被avalon总线驱动的。

关于PIO的软件编程,NiosII的IDE的样板工程里有很多程序可以参考,这里我就不赘述了,只

是还想捎带介绍一下altera_avalon_pio_regs.h和另外一个细节。

这个头文件比较奇怪,它和其它设备的驱动文件独立开来,专门控制PIO设备。所以一定要记住

在你对PIO外设进行编程时一定要包含这个头文件,否则会出错。

include <io.h>

#define IOADDR_ALTERA_AVALON_PIO_DATA(base)__IO_CALC_ADDRESS_NATIVE(base, 0)

#define IORD_ALTERA_AVALON_PIO_DATA(base) IORD(base, 0)#define IOWR_ALTERA_AVALON_PIO_DATA(base, data) IOWR(base, 0, data)

#define IOADDR_ALTERA_AVALON_PIO_DIRECTION(base)__IO_CALC_ADDRESS_NATIVE(base, 1)

#define IORD_ALTERA_AVALON_PIO_DIRECTION(base) IORD(base, 1)#define IOWR_ALTERA_AVALON_PIO_DIRECTION(base, data) IOWR(base, 1, data)

#define IOADDR_ALTERA_AVALON_PIO_IRQ_MASK(base)__IO_CALC_ADDRESS_NATIVE(base, 2)

#define IORD_ALTERA_AVALON_PIO_IRQ_MASK(base) IORD(base, 2)#define IOWR_ALTERA_AVALON_PIO_IRQ_MASK(base, data) IOWR(base, 2, data)

#define IOADDR_ALTERA_AVALON_PIO_EDGE_CAP(base)__IO_CALC_ADDRESS_NATIVE(base, 3)

#define IORD_ALTERA_AVALON_PIO_EDGE_CAP(base) IORD(base, 3)#define IOWR_ALTERA_AVALON_PIO_EDGE_CAP(base, data) IOWR(base, 3, data)

可以看出,这里主要有DATA、DIRECTION、IRQ_MASK、EDGE_CAP几个寄存器或位的宏定

义,而你可以从SOPCBuilder生成 的文件中找到相对应的硬件定义。这也说明了PIO实际上主要

是由以上几个寄存器控制工作。因此,软件中只要对相应的寄存器针对基地址进行操作写入数

据就可 以了。

再来看看另一个值得注意的细节。我们可以从样板工程里发现类似于这样的语句:

volatile int edge_capture_button;

#ifdef BUTTON_PIO_BASEvoid handle_button_interrupts(void* context, alt_u32 id){volatile int* edge_capture_ptr = (volatile int*) context;*edge_capture_ptr = IORD_ALTERA_AVALON_PIO_EDGE_CAP(BUTTON_PIO_BASE);IOWR_ALTERA_AVALON_PIO_EDGE_CAP(BUTTON_PIO_BASE, 0);

}

void init_button_pio()

{void* edge_capture_ptr = (void*) &edge_capture_button;IOWR_ALTERA_AVALON_PIO_IRQ_MASK(BUTTON_PIO_BASE, 0xf);IOWR_ALTERA_AVALON_PIO_EDGE_CAP(BUTTON_PIO_BASE, 0x0);alt_irq_register( BUTTON_PIO_IRQ, edge_capture_ptr, handle_button_interrupts );

}#endif

第一个函数是中断处理,这里暂不介绍。第二个函数是button的初始化函数。我们可以看到主要

是三个步骤:将 IRQ_MASK置为1(因为这个寄存器位 在相应的硬件文件里要取 and操作),

EDGE_CAP则置为0,因为此时并没有输入设备的沿状态发生变化,因此表示按钮尚未按下;第

三步则是中断注册, 只要按照软件开发手册上说的去做就好了,不用在乎细节也是可以的,只

要注册完毕,你的PIO外设就可以接收外部的输入啦,然后Nios会自动跳到相应的处 理程序。

好了,应该差不多就是这些内容了,先写到这里吧。

NiosIINiosIINiosII

NiosII

StepStepStep

Step

bybyby

by

StepStepStep

Step

(5)(5)(5)

(5)

---------

---

UARTUARTUART

UART

(partpartpart

part

111

1

、222

2

--本篇由Mr_Don版主撰写

常常在不同的论坛上看到很多新学Nios/NiosII的朋友们问几乎同样的问题,

形如:

“请问NiosII下应该如何编写串口程序呢?有没有例子能让我参考一下?”

每次见到这样的帖子我都想回复他,但又觉得一言难尽,软件开发手册里面是有例子的,但是

遗憾的是不够详细,所以即使回复一个人又会有很多人跟帖,感觉一句 一句好像总是说不完。

现在就在NiosII Step by Step这一部分里面给大家讲讲UART。注意,这只是我个人使用UART IP的经验,可 能关于这个IP的很多底层细节都没有涉及到,或者还有一些特性我不知道或尚未使

用过。所以这里只是给大家开个如何使用UART IP的头,具体更广泛更深 层的应用还得靠大家

继续分享你们的经验。毕竟,这只是入门的文档,不过,对于不是很复杂的应用(例如参加竞赛)应该差不多了,呵呵。

首先要清楚UART模块的应用并不难,要对自己有信心。

本文将分为四个部分:

1)UART IP介绍调试串口通信的需要的工具;

2)软件开发手册上的程序分析和两个最简单的例子;

3)实战部分:我个人用这个IP调过的两个不同模块及部分程序分析;

4)关于仿真以及目前使用该IP发现的问题(---啊???做好心理准备去接受哦,呵呵)

(注:四个部分将分为两个part来介绍,本文是part 1 ,介绍前两个部分内容)

第1)部分:UART IP介绍及调试串口通信的工具

UART(Universal Asynchronous Receiver/Transmitter),通用异步收发器,是嵌入式系统上很常用

的一个串 行接口。这么说的原因是因为RS-232串口本身由于数据速率比较慢而且误码率相对偏

高,正在从笔记本电脑的接口配置中慢慢消失,逐渐被USB接口所代 替。但是另一方面,由于

其方便、简单、易用等特性,它在嵌入式系统中依然扮演着十分重要的角色。所以Altera才把

UART作为一个连接 Nios/NosII和其相关外设的IP放在SOPCBuilder里面供用户使用。Nios一代

调试甚至用的就是直接是串口。

首先要注意的是:Altera所提供的UART IP虽然实现了RS-232接口的异步时序逻辑,但是不能直

接连接或驱动一个RS-232串口。原因在于 Altera大多数的FPGA器件不符合RS-232的逻辑电平规

则,如果直接连上获驱动可能会损害器件。所以连接通过RS-232串口通信的一个设备 时,在

Nios/NiosII和外设接口之间要加电压转换芯片(例如 Maxim的 Max232),这样才能首先保证电

气上没问题。所以大家在自制电路 板时,一定别忘了这一点。

关于串口通信用到的其他一些位,例如奇偶校验位等,这里不打算涉及,一般按照默认就可以

了。另外,数据一桢的位数一般也都是8位,停止位一般也就1位,按默认走,不用改。还有一

些选项属于高级一些的应用,这里不打算深究。本文的目标只要教会大家如何用串口就够了,

呵呵。

串口调试时建议大家使用串口调试的小工具,这样的工具网上有很多,大家可以随便找找,实

在不行我在这个帖子后面给大家附上一个,很好用。在PC上只用这个小工具就够了,可以测试

待测模块的收、发。

调试时建议采取的方法:先将待调外设的串口和PC主机的串口连上,然后先看看外设和主机之

间能不能通信,如果可以那么就可以继续下一步了;如果不行,那么 你的问题多半是出在外设

本身上,看看是不是波特率不匹配什么的还是别的什么问题。经过这一步,下来就是验证Nios系统将要使用的串口(已经经过电压转换 芯片的转接)是否正常。我们可以通过把它也连接到

PC的串口上看看是否能正常通信来验证。怎么验证是否能通信呢?请看下一部分介绍:

第2)部分:软件开发手册上的程序分析和两个最简单的例子

先看第一个:

#include <stdio.h>

int main ()

{printf("He llo world.\n");return 0;}

真是熟悉得不能再熟悉了。但是这个程序很值得说一说。

首先看它的头文件<stdio.h>,这说明两个问题。第一,NiosII的编程是支持ANSI C库的;第二,

这里使用的是有关标准IO的 库,这就说明程序里的printf语句是把输出结果送到标准输出流上。

那么这个标准输出流是什么,又在哪里呢?请看下图:

打开一个 hello_world 的样板工程,再打开 System Libraries Properties 选项,你会看到:

看见了正中间的三个 “jtag_uart”了么?现在的stdout\ stdin\ stderr都是导向jtag_uart的,所以当你

运行这个工程(Run As NiosII Hardware)时,你才可以看到 “Hello from NiosII!”显示在NiosII IDE下面的 console 显示框里。因为那里就是jtag_uart接口的用户界面。

那么如果我们改变stdout\ stdin\ stderr的位置会怎样呢?请看下图:

现在我已经将stdout\stdin改成了pc_uart。这是我在SOPCBuilder里面自己添加的一个UART IP接口,用于连接NiosII和PC,主要就是用来调试NiosII能否和PC之间通信。

这个时候你再次运行该工程(Run As NiosII Hardware)你会发现“Hello from NiosII!”不显示了!

呵呵,注意,结 果只是不显示在NiosII IDE下的console里面了,但不代表就不显示了。相反,

结果转而送往pc_uart这个接口,而不是 jtag_uart接口了。也就是说这时候你在PC上的串口调试

工具下才可以看见相应的结果(前提是必须波特率及其它的一些串口基本设置必须匹配)。

以上就是一个输出字符流的重定向过程。输入流的控制也可类比于此。如果把 stdin重新改成

jtag_uart,那么你在NiosII IDE的 console下面就可以对硬件芯片里面运行着的NiosII处理器发送

接受自用户的自符(可以是普通的字符,也可以是某种命令字符)。同样,如果 stdin还是用

pc_uart,那么输送自符只能在PC机上的串口工具里完成。

怎么样,一个这么简单的程序其实还是比你想的要复杂一些吧?呵呵,不用怕,经过上面的介

绍,你已经对于NiosII下的字符流输入输出有了一定的了解了。 其实这是一种很重要的字符流

重定向的方法。还有一种自符流定向的方法我很快就在下面介绍。这里想就这种方法再多说上

两句。如果你在SOPCBuilder 里面添加了要用的串口通信模块的UART接口(例如最典型的GPS设备),你不用编写程序就可以直接从设备中接收数据,方法就是把stdin定向成 gps_uart(名字

随便起),stdout设为jtag_uart,你就会发现只需要对收到的数据编写程序进行包分解处理就行了 ,

而接收数据的过程 NiosII IDE已经自动帮你搞定了,呵呵。

不过这个方法有个很大的局限性。很明显,System Libraries Properties选项下给出的只有stdout\stdin\ stderr三个选项,而一般stderr用的不多,所以实际上你只能最多同时使用两个不同的串

口通信的外设模块。除去调试NiosII本身用的 jtag_uart(把stdout设为jtag_uart),你只能用一个

模块了,而且该模块只能实现输入一种功能。这是不够的,如果遇到要调试GSM 这样交互性

很强的模块,你就真没办法了,因为你不仅需要向它发送命令,还需要接收该模块的反馈(例

如你发送“AT”,它应该反馈回来“OK”),可是还需 要在jtag_uart console下观察输出和反馈回来

的结果啊。而stdin/stdout都被GSM已经占了,jtag_uart形同虚设,根 本看不到发出去和收到的

字符(甚至不可能发出去),所以这样是肯定不行的。

建议:以上的方法只适用于刚学习NiosII下串口编程的朋友,如果你想在你的系统上集成两个或

者两个以上的串口外设(包括jtag_uart),那么请用第二种方法。

下面介绍第二种串口编程的方法,我们还是从一个简单的例子看起吧:

#include <stdio.h>

#include <string.h>

int main (void)

{char* msg = “hello world”;FILE* fp;fp = fopen (“/dev/jtag_uart”, “r+”);if (fp){

fprintf(fp, “%s”,msg);fclose (fp);}return 0;}

这种方法才是标准的串口操作方法。说实话,NiosII下的串口编程比Nios下的已经简化了很多。

Nios下的串口操作要调用Altera自定义的函 数,而NiosII下,由于有了HAL(详见NiosII step bystep 2关于HAL的介绍)的存在,串口编程已经成为标准的文件操作了。

我们知道,在Unix/Linux等操作系统里所有的流操作都可以看成是文件,NiosII吸收了这一点(可

能是因为Nios吸收了Cygwin的很多特性吧,呵呵),把对串口的流操作也当成了一种文件操作。

操作一个串口时,只需要对它相应的设备驱动读写数据就好了。

fp = fopen (“/dev/jtag_uart”, “r+”);这个语句的意思就是打开jtag_uart的驱动,并且可以对其进行读写(r+),

if (fp)

{fprintf(fp, “%s”,msg);fclose (fp);}

这段语句的意思就是如果文件打开没问题,指针句柄有效,就可以用fprintf语句向这个串口写数

据了,这里要写的数据是一个字符串。写完之后关闭文件。 你看,所有的操作几乎与C语言下

的文件操作没什么区别,呵呵,唯一不同的就是待操作的文件是你要操作的外设,而不是一个“文件”。

利用这种方法,你可以立刻举一反三,只要改动一点点,就可以操作所有的串口外设了。要改

动的就是/dev/jtag_uart,把 /dev/后面的外设名改改就是了。例如我要操作NiosII与PC之间的通信 ,

只要改为/dev/pc_uart就行啦。

再延伸一点,如果你的系统里面要添加四个使用串口流的设备,分别是JTAG UART、PCUART、

GSM UART、GPS UART,那么对于每个外设,只要编写四个相应的文件操作程序就行了:

fp = fopen (“/dev/jtag_uart”, “r+”);

fp = fopen (“/dev/pc_uart”, “r+”);fp = fopen (“/dev/gps_uart”, “r+”);fp = fopen (“/dev/gsm_uart”, “r+”);

当然,不要把所有外设的操作都写在一个程序里,应该分开写,最后分成不同的函数,再最后

集成到一个main函数里。这是编程细节方面的问题了,不多说了。

以上介绍的方法就是我们一般会采用的方法,具有很大的灵活性,可随时更改、添加外设程序,

克服了第一种方法的局限性。

说到这里,你应该知道其实第一种方法就是第二种方法的在NiosII IDE里面的隐含集成,其实图

形用户界面背后也不过就是第二种方法的程序,只是 stdin/stdout/stder都是由你指定罢了,呵呵 。

(估计Altera设计这三个选项就是为了方便懒人和初学者,呵呵)

好了,这就是part 1 的内容了,先写到这里,后面我会为大家继续添加part 2 的内容。相信看到

这里,你已经对NiosII下的串口编程有了一定的认识了吧,加油,呵呵。

NiosII Step by Step (5) --- UART(part 2)(续)--进阶篇

上一次的NiosII Step by Step (5) --- UART(part 1)已经给大家讲了如何使用UART这个IP和两种

常用的编程技巧。这次的part 2里将会给大家讲两个实际的应用例子,以及过去使用该IP时发现

的问题。

第3)部分:实战部分:个人用这个IP调过的两个不同模块及部分程序分析

这部分将向大家举两个串口程序的例子,一个是关于GPS信息接收的程序分析,一个是GSM模

块操作的程序分析。程序都是自己以前编的,所以水平有限,可能没有过多的考虑程序性能的

问题,还请大家谅解。

只要是串口通信的设备都可以通过PC机上的串口调试程序来调试,所以建议初次调试从设备本

身和PC的串口开始。当然不用串口调试程序,用Windows自带的超级终端也可以,只是个人感

觉不太好用,呵呵。

实战1:关于GPS

GPS(Global Positioning System),是现今高智能化的嵌入式设备中越来越重要的功能设备之一,

而且应用前景广阔。所以能在你 的NiosII嵌入式系统上挂上一个GPS模块还是很不错的(虽然

感觉有点模式化和老套,呵呵),而且很多市售的GPS模块都采用串口通信,所以实现起来 也

相对其它接口(如USB)简单。下面就来介绍如何编写NiosII与GPS模块通信的程序:

这里我使用的GPS设备是Motorola的M12 模块,软件配置QuartusII5.0+NiosII5.0。注意,这款GPS模块可以由用户发送 相应的命令改变一些功能设置,实现的方法就是通过串口发送命令。不过

这里并不打算讲如何发送命令这一模块,如何发送数据这一部分我们将放在操作GSM模块 里

面来讲(因为GSM必须进行双向调试)。就GPS本身来说,其实只考虑接受它由串口发送过来的

数据就够用了。

首先我们需要知道的是,GPS的数据是以“包”(也可以看作是“祯”)的形式送到接受端的串口的 。

每一包数据的格式都是一样的,这也就是我们对数据报进行解析的基础。我们来看看一包的数

据是什么样的:

(Rx)@@Ha081907D50F023B00031765075977F1175F49B30000A2490000000007597849175F4B270000A24900000000001200120A440026080310000000000001082BC408A11900000000001400000000000E0827C308A11700000000000800000000000308234A08A01C00000000000700000000001A0000000000090000000000C0000000FFDE00014828002DCD0008005030394F4A58

这是一个完整的数据包。对于不同的GPS设备可能数据包的格式可能不同,这些区别大家可以

参考你正在使用的GPS的用户手册。下面我们就来解析一下到底这些数据都是什么意思,限于

一行的长度,我只解析前面部分位数的数据,后面的数据解析方法也是一样的:

(Rx)@@Ha 08 19 07D5 0F 03 00 0003E7B1 075977F2 175F49B5 0000A249

m d yy h m s ffff aaaa oooo hhhh

m(月),d(日),yy(年),h(小时),m(分钟),s(秒)……

呵呵,这就是一包的数据实际含义,更多的数据位,例如经纬度、速度什么的我就不多说了,

具体在数据包中的位置大家可以查用户手册,准有。

下面就来进行这段串口程序的分析:

…….

FILE* fp;

fp = fopen ("/dev/gps_uart", "r+");

while(1)

{for(i=0;i<5;i++){

while((c = getc(fp))== -1);number\[\i\]=c;

}for(i=5;i<size;i+=2){

while((c = getc(fp))== -1);

temp = c;temp1= temp>>4;temp = c &0x0f;number[\i\] = temp1;number[\i+1] = temp;

}……

这两个语句:

FILE* fp;

fp = fopen ("/dev/gps_uart", "r+");

正是我们在本文part1里面提到过的标准串口设备操作方法:先定义一个文件指针,再把它当作

句柄( handle)打开相应的串口设备就可以啦。这里我自己定义的GPS串口UART IP在SOPCBuilder下的名字叫做gps_uart。

通过观察原始的数据包,我采用的一种方法是等待(还有很多方法,大家可以自己去尝试,比

如你可以发现“@”是每包都会出现的一个字符,而且只在开头出现一 次,该怎么做,呵呵,自

己去试试吧)。也就是说要做的就是等待有效数据的出现,从有效数据的位的位置就可以知道一

包新的数据就要开始了。所以前一个 for 循环就是从端口文件中读入字符,并等待开头的用于

同步的数据字符 (Rx)@@Ha 过去。

while((c = getc(fp))== -1)

的作用是一定要等待到实际数据的到来,没有数据则会一直循环等待。接下来,很明显,第二

个for循环则是用来接收真正的有效数据。循环中有一部分

temp = c;

temp1= temp>>4;temp = c &0x0f;number[\i] = temp1;number[\i+1] = temp;

是用来对原始数据进行数制处理的(因为原始的数据采用的是Motorola定义的十六进制数,所

以应该转变成十进制才能给用户看)。

好了,看到这里,你应该对GPS的串口数据接收程序编写有个大体的认识了吧,还有一些细节

需要自己实践中才能发现,光说不做是不行的,呵呵。

=============================================================

实战2:关于GSM

GSM(Global System for Mobile Communications),是手机等移动设备上一种常见的通信标准,在

很多的嵌入式系 统中都可能会有移动通信方面的应用。由于GSM网络在全国范围内实现了联

网和漫游,具有网络能力强的特点。同时,它对用户的数量也没有限制,克服了传统的 专网通

信系统投资成本大,维护费用高,且网络监控的覆盖范围和用户数量有限的缺陷。比起传统的

集群系统在无线网络覆盖上具有无法比拟的优势,加上GSM的 SMS本身具备的数据传送功能,

都使得这些应用得到迅速的普及。利用GSM短信息系统进行无线通信还具有双向数据传输功

能,性能稳定,为远程数据传送和监 控设备的通信提供了一个强大的支持平台。

所以在NiosII上挂上一个GSM模块也是很不错的,会给你的设计增色不少。下面我们就来看看

怎样编写一个可以向GSM模块发送命令并且接受模块的反馈信息的程序。这里我使用的是

Siemens公司的TC35i模块。

首先我们要清楚的是GSM的通信采用AT命令,这是一种命令标准。输入不同的命令给模块,模

块会有不同的反应,并执行不同的功能。

例如,

输入“AT”,如果模块正常则会反馈回“OK”;

输入“ATI”,如果模块正常则会反馈回模块当前的信息;

输入“ATDxxxxxxxxxxx”,是让模块拨打相应的电话号码;

输入“AT+CMGS=xxxxxxxxxx”,是让模块向这个号码发短信等等。

AT命令集非常庞大,所以建议大家自己去参阅完整的命令资料。事实上,一般通常使用的命令

不会超过20条,所以还是不难掌握的。

NiosII与GSM的通信就是采用AT命令向GSM模块发送命令。下面我们就来看看这个完整不过很

简单的通信程序。这个程序包括了发送和接收数据两部分功能:

#include <stdio.h>

int main()

{int i=0,j=0;char c;char at[3] ={'A','T',0x0d};char reply[10]="";

FILE* fp;

fp = fopen ("/dev/gsm_uart", "r+");

while(1){if (fp){printf("\nSuccessfully Opened!!!\n");for(i=0;i<3;i++){fprintf(fp,"%x",at[\i]);printf("%x",at[\i]);

}

printf("\nPlease REPLY a char...\n");

j=0;while(j<2){

fscanf(fp,"%c",&c);reply[j++]=c;

}

for(i=0;i<2;i++)printf("%c",reply[\i]);

}}

}

怎么样?有了我前面讲述的知识铺垫,基本上能看懂吧?呵呵,不过还是要给没完全看懂的xdjm们说说。

char at[3] ={'A','T',0x0d};

这就是在程序里面AT命令应该存在的定义形式。完全是采用字符,因为串口本来就是字符流收

发设备嘛。所以其他的命令定义也可以如法炮制。0x0d是必需的,这是表示一条AT命令结束的

标志,千万不要忘记。它其实就是ASCII码里面的“CR”换行。

FILE* fp;

fp = fopen ("/dev/gsm_uart", "r+");

不用多说了吧,呵呵。

if (fp)

{printf("\nSuccessfully Opened!!!\n");for(i=0;i<3;i++){fprintf(fp,"%x",at[\i]);printf("%x",at[\i]);

}

这段程序是用fprinf函数把上面定义的AT字符命令发出去,发到GSM设备上。同时把这一命令

在标准输出流上送出,以便于调试观察。这里的标准输出设备就是NiosII IDE的jtag uart控制台。

你可以在那里看见结果。

printf("\nPlease REPLY a char...\n");

这句是什么意思?呵呵,我在祈求GSM来点儿反应(“求求了,看在NiosII Step by Step的份上说

句话吧...”)。

那么如果模块真的良心发现,给了NiosII的串口一句反馈信息,我又该怎么看呢?就是这段程序 :

while(j<2)

{fscanf(fp,"%c",&c);reply[j++]=c;

}

这里让j<2是有原因的。因为我知道发送 “AT”的返回信息是“OK”,所以我的目的就是为了收到

这两个字符。

收到以后,用

for(i=0;i<2;i++)

printf("%c",reply[\i]);

把“OK”这两个字符发送到标准输出上,这时候,你就可以看到来自GSM的问候了!呵呵…

好啦,第三部分关于串口程序的实战就写到这里,希望对大家的程序编写有帮助。

第3)部分:关于仿真以及目前使用该IP发现的问题

仿真时,你可以选择软件仿真或者实际仿真,一般在SOPCBuilder下面按照这样的设置就可以了 :

倒数第二个选项选中是为了使仿真速度加快。然后就可以在仿真过程中收到你在上面文本框里

输入的信息了。

关于这个IP的问题

Altera的IP不是完美的,这个IP也存在着一些问题,有时问题感觉莫名其妙,不过它的确发生了 ,

我们只好去接受或者等待更新版本的IP出现。

以下是我从NiosForum的对话里面摘录的一些人对于UART这个IP的帖子摘要,大家可以看一看,

原来没有什么是完美的,呵呵:

一个人说:We use the UART in our Nios- II design that comes with Quartus 5.0SP1 and Nios-II5.0.We face the problem that sometimes the Uart reports wrong RX Bytes from correct RX streams.Theserial stream starts with a 0x02 byte but the UART delivers a 0x04 byte. Monitoring the serial signaloutside the FPGA and inside the FPGA shortly before it enters the UART shows that the streamcorrectly contains the 0x02. So no electrical problem. a perfect signal.

第二个人说:We are finding the same or a similar problem. We have 2 devices, we program one tojust transmit. Every four seconds we send a blast of data 0-255. If we enable Rx and listen to ourself onthat device, we hear all 256 characters nice and clean. We then plug into a second device and enable itto Rx. We loose about one byte in 16. We have tried blocking, non-blocking, read only, read write,disabling the transmitter. We have tried open(), fopen(), wehave used getc() and read(), and more.

第三个人说:

We had the same problem - I tried to use the UART together with DMA . At lower baudrates (up toabout 115k2) it worked more or less. But we looked for baudrates up to 2Mbit/s. Finally we wrote ourown UARTwith 512 Byte deep fifos - for rx and tx.

第四个人说:(这个人是我,因为我在调试串口时也遇到了类似的问题)

I met the similar problem when I use the UART in NiosII5.0 + QuartusII 5.0.But I tried an alternativeapproach via some code programmed by myself to tackle this problem and it seems that it worksnormal now. The way I program is to capture some fixed digit in my code. But the side effect is evidenttoo, that is the time for processing are prolonged about 1 time.

第五个人说:

There's no buffering of the data being sent or received in the UART, so if you can't keep up, it willdrop data... Could this possibly be what you're seeing?后来有人否定了问题出在BUFFER的问题上

第六个人说:

It certainly sounds like a hw problem, but to prove that it is not SW/timing related, you should use thesimplest possible single-threaded test code. Just take the hello_world example and modify it to echoback everything it receives. Open the uart twice, once for read and once for write. Use a Y cable so aPC (not a scope) can be used to independantly monitor what is really going into the uart. For multi-tasking apps in particular, you should increase the default 64 char buffer size in altera_avalon_uart.h

呵呵,看到这里,你是不是对Altera的这个IP有点失望?

那么结果呢?这个话题的结果是没有结果:最后第一个提问的人换了一个自己的UART IP集成

到SOPCBuilder里面,一切都不了了之了…

注意上面范例中[\i]中\是为了不和HTML标注混淆而加的。

NiosIINiosIINiosII

NiosII

StepStepStep

Step

bybyby

by

StepStepStep

Step

——————

——

MicroC/OSMicroC/OSMicroC/OS

MicroC/OS

起步

本篇有cameral撰写,为我们丰富了学习内容,在此表示感谢。

看了由论坛成员写的几篇Step by Step受益匪浅,今天也写一篇,请大家斧正。

μC/OSII是著名的、源码公开的实时内核,是专为嵌入式应用设计的。&micro;C/OS-II 是基于抢

占式的实时多任务内核,可固化、可剪裁、具有高 稳定性和可靠性,除此以外,&micro;C/OS-II的鲜明特点就是源码公开,便于移植和维护。

本文挡将指导你在fpga实验板上搭建一个基于MicroC/OS-II实时操作系统的工程。

硬件环境:实验板(任何一款RAM容量要大于120k的fpga开发板,自制或购买)

软件环境: Quartus&reg; II5.1版sp2版本

NiosII 嵌入式处理器5.1版版本

一、 创建一个新的Nios II IDE工程

执行以下步骤:

1.选择 程序>Altera > Nios II 5.1 > Nios II IDE (Windows 开始菜单).

2.选择 New > C/C++ Application (File menu). 出现工程向导。如图1。在 Project Template(工

程模板)选择中, 选择 the MicroC/OS-II Tutorial。

3.工程名与工程路径已为你自动填好,请保留这些默认值。

4.Click Browse under Select TargetHardware(单击浏览选择目标硬件).5.浏览你正在使用的Nios扩展板的标准例程目录。

6.选择std_<device name>.ptf文件。

7.单击Open。你返回到New Project.如图1-2所示,SOPC Builder系统框下Select TargetHardware栏 中包含的指向标准设计例程 .ptf文件的路径。另外CPU栏包含在SOPC Builder示例系统中的

CPU的名称。

8.单击Next至New Project第二页。

9. 开启Select or create a system library选项。

10.单击New System Library Project打开系统库页面。

见图3。

图3. New System Library Dialog Box

11.在Name栏中输入std_system_lib_012.在Select Type of system library栏中选择MicroC/OS-II。13.单击Finish返回New Project。见图4

图 4. New Project Wizard Page 2

14.单击Finish完成你的新工程的创建。该范例创建了两个工程在C/C++ Projects视图中

Nios II device drivers (niosII设备驱动程序)

MicroC/OS-II system library for the standard hardware (用于标准硬件的MicroC/OS-II systemlibrary)Application project (应用设计)

二、系统库设定

通常,在你创建了一个新的系统库以后你必须设定它,例如定义 stdin, stdout, stderr,等。在设置

期间Nios II IDE保存适配参数到os_cfg.h文件中。执行以下步骤设定MicroC/OS-II核。

1. 在Nios II IDE的C/C++ Projects视图中,右键在系统库上单击std_system_lib。2. 在弹出的菜单中选择Properties打开Properties对话框。

3. 单击System Library显示system library选项,如图5所示。

图 5. System Library Options

4.单击在RTOS 下面的RTOS Options。弹出MicroC/OS-II RTOS Options对话框,如图6所示。

图 6. MicroC/OS-II RTOS Options

5.单击“+”在在左边的面板中,展开MicroC/OS-II目录。MicroC/OS-II是高度映射可设定的。你

选定的对话框中的选项被保存在 os_cfg.h文件中。选定的MicroC/OS-II选项被包含在二进制中。

通过单击MicroC/OS-II下每一个选项检查你所能选择的选项。

关于MicroC/OS-II 的各种详细特点,请参见Nios II Software Developer’s Handbook的“MicroC/OS-

II Real Time Operating System”章节。

6.选择默认设置单击OK。你将返回系统库选项对话框。

7.单击OK完成设置。

三、运行Nios II软件设计

在这一部分,你将要在扩展板上运行一个设计示例。

使用Nios II IDE,你将要创建一个应用程序,为扩展板设定一个合法的目标文件 (.sof),并且下

载执行与连接文件(.elf)1.在Nios II IDE的 C/C++ Projects视图中,选择ucosii_tutorial_0工程。

2.打开Quartus II程序选择Quartus II Programmer (Tools menu中)。见图7。

图7Quartus II Programmer

3.检查Program/Configure选项。

4.单击Start将SOF下载到你的实验板上。

5.选择Exit (File menu中)关闭Quartus II程序。你将返回Nios II IDE。6.如果你被问及是否想保存chain1.cdf文件单击No。7.选择RunAs > Nios II Hardware (Run menu中)创建程序,下载它到试验板中并运行。

关于Nios II IDE创建和运行设置,参见在Nios II IDE 中的在线帮助Nios II Software DevelopmentTutorial下载完成后,Nios II IDE通过print_status_task()函数使控制台视图周期性更新,如下所示:

****************************************************************Hello FromMicroC/OS-II Running on Nios II. Here is the status:The number of messages sent by the send_task: 55The number of messages received by the receive_task1: 20The number of messages received by the receive_task2: 7

The shared resource is owned by: getsem_task2The Number of times getsem_task1 acquired the semaphore 66The Number of times getsem_task2 acquired the semaphore 52****************************************************************祝贺你成功地设定,构建和运行了一个MicroC/OS-II程序

四、让你的操作系统执行跑马灯任务

在这一部分,你将要在扩展板上运行一个你自己设计的MicroC/OS-II程序。

1、 打开ucosii_tutorial.c2、 头文件加入如下代码

#include "system.h"#include "altera_avalon_pio_regs.h"#include "alt_types.h"

3、 加入定义任务优先级代码

OS_STK my_task1_stk[TASK_STACKSIZE];#define MY_TASK1_PRIORITY 13

4、 在初始化函数 initCreateTasks函数中加入一下代码

return_code = OSTaskCreateExt(my_task1,NULL,(void *)&my_task1_stk[TASK_STACKSIZE],MY_TASK1_PRIORITY,MY_TASK1_PRIORITY,my_task1_stk,TASK_STACKSIZE,NULL,0);

alt_ucosii_check_return_code(return_code);

5、 增加执行跑马灯的任务的代码

void my_task1(void* pdata){alt_u8 led = 0x2;alt_u8 dir = 0;volatile int i;while (1){if (led & 0x81){dir = (dir ^ 0x1);

}

if (dir)

{led = led >> 1;

}else{led = led << 1;

}IOWR_ALTERA_AVALON_PIO_DATA(LED_PIO_BASE, led);

OSTimeDlyHMSM(0, 0, 1, 0);//延迟1秒}

}6、 选择RunAs > Nios II Hardware (Run menu中)创建程序,下载它到试验板中并运行。下载完

成后,可以看到除了函数使控制台视图周期性更新,你试验板上的LED灯也周期性的亮灭。

恭喜你,你设计构建并编写的MicroC/OS-II程序如愿以偿了!

NiosII 的中断处理方式带有典型的RISC处理器的特征,所有的中断处理都从同一入 口进入,

然后由软件加以分配。负责分配工作的软件叫系统ISR,它是由开发系统提供的,自动的连接到

可执行程序上。系统ISR维护着一个中断向量表,表中 的每一项代表着一个专项处理程序的入

口。所有的专项处理程序都是由用户定义然后注册到中断向量表中的,叫做用户ISR。系统ISR的入口地址是在 SOPC_Builder中定义的,叫Exception Address。和中断有关的CPU寄存器有:

ctl0、ctl1、ctl3、ctl4。 Ctl0 是程序状态字,它的bit0位是全局中断允许位,1代表允许,0代表

禁止。Ctl0是程序状字的堆栈,当发生中断时,由它保留一个程序状态字的 备份。Ctl3是中断

允许寄存器,其中每一位控制着一个中断源,1代表允许,0代表禁止,共计32位。Ctl4是中断

申请寄存器,每一位对应着一个中断源 的中断请求,1代表有中断,0代表没有******计32位 。

NiosII的中断处理过程是这样的:

1、 考备一份程序状态字到ctl1;2、 清除全局中断允许位PIE,禁止中断;

3、 将下一条将执行的指令的地址存入R29,以便中断返回之用;

4、 跳转到中断入口地址,进入系统ISR;5、 系统ISR保护现场;

6、 系统ISR检测ctl1的PIE位,如为0则进入软中断处理程序从11继续,否则由7继续;

7、 系统ISR检测Ctl4,如果有中断申请,则转到硬中断处理和序,否则进入软中断处理程序;

8、 硬中断处理程序将检测中断申请号,并检索中断向量表,跳转到用户中断处理程序;

9、 用户中断处理程序做出具体的处理,最后返回系统ISR;10、 系统ISR恢复现场,并返回;

11、 软中断处理程序进行陷井指令、模拟指令判断,并做相应处理,然后返回系统ISR;12、 系统ISR恢复现场并返回;

软中断处理程序是用来处理由软件发起的中断事件的,包括调试指令引起的中断及未定义

指令引起的中断。目前未定义指令的处理主要为乘、除法运算指令的处 理,不支持用自定义的

操作码,除用户自己修改系统程序。如果软中断处理程序遇到了一个不识别的操作码,将返回

一个不确定的结果。

与用户编程相关API函数有:

1、 alt_irq_register();2、 alt_irq_disable();3、 alt_irq_enble();4、 alt_irq_disable_all();5、 alt_irq_enable_all();alt_irq_register()是向系统ISR注册用户ISR的API函数。其原形为:

int alt_irq_register( alt_u32 id,void *context,void (* isr)(void *, alt_u32))

id 代表被服务的中断向量号;

context 是运行参数指针,将来作为第一个参数传给用户ISR;Isr 是一个函数指针,指向用户ISR入口;

如果注册成功,函数返回0,并允许全局中断及被服务中断;不成功返回非0值。

Alt_irq_disable()用来禁止某个中断服务。原形为:

Int alt_irq_disable(alt_u32 id)Id 为对应的中断号;

返回值为0;alt_irq_enable()与alt_irq_disable()对应,用来开启某个中断服务。原形为:

int alt_irq_enable(alt_u32 id)alt_irq_disable_all()用于关闭全局中断,原形为:

alt_irq_context alt_irq_disable_all(void)返回值为中断控制寄存器的值。

Alt_irq_enable_all()用于开启全局中断,原形为:

void alt_irq_enable_all( alt_irq_context context)context 代表中断控制寄存器的值。

用户定义的用户ISR程序要符合统一的原形定义,即:

void isr(void * context, alt_u32 id)函数名没特别的要求,与一般函数一样。入口参数与返回值要严格按标准形式定义,否则系统

ISR将不能正确的对其调用。