一、前言
学习NIOS的必备条件: 具有一定的单片机基础; 具有一定的C语言编程能力; 了解Quartus II的开发流程; 一块开发板。
二、建立工程
首先,打开 Quartus II 9.0 软件。
接下来,建立一个工程File->New Project Wizard
第一行是工程的路径,你选择你放置的路径即可,第二,第三行都是工程名(注意:工程名不要用中文,路径中也不要含有中文字符,否则后面编译时会出错),写好以后如下,点击Next,
点击后,如下图所示,这个不需要改动,接着点击Next,
点击后,如下图所示,Family 里选择Cyclone II,在Available devices中选择EP2C8Q208C8(具体内容根据你的芯片所定)。
接着点击Next,不需要修改,点击Finish,显示如下图所示。
至此为止,工程已经建立完成。接下来,需要建立一个Block Diagram/Schematic File,点击File->New,如下图所示:
点击OK,完成建立,工程中出现了一个 Block1.bdf 文件。
三、AD7606的工作原理
1.工作原理
AD7606是8通道16位逐次逼近型ADC,有2种接口模式:串行接口模式和高速的并行接口模式,并行接口模式又分为8位和16位传送方式。在数据转换时,2个转换信号CONVSTA/B,用来控制每4个或每8个ADC同时采样。如果将2个CONVST引脚连接在一起,就可对8个ADC同时进行采样。在CONVSTX的上升沿,被选择的ADC对被置为保持模式,转换开始。CONVSTX的上升沿过后,BUSY信号变为高电平表明转换正在进行,转换时间是2μs,BUSY信号返回低电平表明转换结束。在BUSY信号的下降沿,ADC回到跟踪模式,数据可以通过并行或串行接口从输出寄存器读出。16位并行接口读取数据时可以用CS和RD信号通过并行数据总线从AD7606读取数据。通过并行总线读取数据时,需将PAR/SER/BYTE SEL引脚和低电平相连。通过内部选通CS和RD输人信号,可以将转换结果输出到数据总线。当CS和RD同时处于逻辑低电平时,数据线DB15至DBO不再呈高阻态。
CS输入信号的上升沿使总线进入三态,其下降沿使总线脱离高阻抗状态,CS是使能数据线的控制信号,RD引脚用来从输出转换结果寄存器读取数据。
对AD7606的RD引脚施加一个RD脉冲序列,可使各通道的转换结果按升序逐个输出到并行总线DB[15:0]。BUSY变为低电平后的第一个RD下降沿输出通道V1的转换结果,下一个RD下降沿则用V2转换结果更新总线,依此类推。在AD7606上,RD的第八个下降沿输出通道V8的转换结果。
当系统板上只有一个AD7606且它不共享并行总线时,可以仅用数字主机的一个控制信号来读取数据。CS和RD信号可以连在一起,如图错误!文档中没有指定样式的文字。.4所示,这种情况下,数据总线在CS和RD的下降沿时脱离三态。利用CS和RD合并信号,可以从AD7606输出数据,并由数字主机读取。这种情况下,CS用来使能各数据通道的数据帧传输。
A/D7606内部转换16位并行接口模式转换时序如图错误!文档中没有指定样式的文字。.1所示[i]。
图错误!文档中没有指定样式的文字。.1 并行接口模式转换时序
AD7606并行接口模式数据读取时序如图错误!文档中没有指定样式的文字。.2所示。
图错误!文档中没有指定样式的文字。.2 并行接口模式数据读取时序
(3)ADC传递函数
AD7606的输出编码方式为二进制补码,其最高位为符号位。所设计的码转换在连续LSB整数值的中间(即1/2LSB和3/2LSB)进行。AD7606的LSB的大小为FSR/65536。AD7606的理想传递特性如图错误!文档中没有指定样式的文字。.3所示。
图错误!文档中没有指定样式的文字。.3 AD7606传递特性
2. 硬件设计
AD7606硬件电路如图错误!文档中没有指定样式的文字。.4所示。
图错误!文档中没有指定样式的文字。.4 AD7606硬件电路
如图错误!文档中没有指定样式的文字。.4所示,A/D7606的第8引脚(RANGE)为模拟输入选择引脚,此引脚的极性决定模拟输人通道的输人范
围。如果此引脚与逻辑高电平相连,则所有通道的模拟输人范围为l0V。如果此引脚与逻辑低电平相连,则所有通道的模拟输人范围为5V。此引脚的逻辑状态改变会立即影响模拟输人范围。在硬件设计时将此引脚连接到3.3V逻辑高电平,选择模拟输人范围为l0V。
A/D7606的第6引脚(SER/PAR)为并行/串行/字节接口选择输入。如果此引脚与逻辑低电平相连,则选择并行接口。如果此引脚与逻辑高电平相连,则选择串行接口。如果此引脚为逻辑高电平且DB15/BYTE SEL为逻辑高电平,则选择并行字节接口模式。
接口模式选择如表错误!文档中没有指定样式的文字。.1所示。
表错误!文档中没有指定样式的文字。.1 接口模式选择
PAR/SER/BYTE SEL 0 1 1 DB15 0 0 1 接口模式 并行接口模式 串行接口模式 并行字节接口模式 在本设计中将第6引脚接在逻辑高电平上,选择并行接口模式。
A/D7606的第14引脚(BUSY)是输出繁忙引脚,在CONVST A和CONVST B均达到上升沿之后,此引脚变为逻辑高电平,表示转换过程已开始。BUSY输出保持高电平,直到所有通道的转换过程完成为止。BUSY下降沿表示转换数据正被锁存至输出数据寄存器,经过时间t之后便可供读取。在BUSY为高电平时执行的数据读取操作应当在BUSY下降沿之前完成。当BUSY信号为高电
平时,CONVST A或CONVST B的上升沿不起作用。在硬件设计时将此引脚与LPC1752的第26引脚(CAP1.1)相连,使用LPC1752的定时器捕获功能捕获BUSY的下降沿,并产生中断读取转换数据。
A/D7606的第34引脚为内部/外部基准电压选择输入。如果此引脚设为逻辑高电平,则选择并使能内部基准电压模式。如果此引脚设为逻辑低电平,则内部基准电压禁用,必须将外部基准电汪施加到REFIN/REFOU下引脚。在本设计中使用内部基准电压,所以此引脚通过一个10K电阻连接到3.3V电压上。
A/D7606的第42引脚为基准电压输入(REFIN)/基准电压输出(REFOUT)。如果REF SELECT引脚设置为逻辑高电平,此引脚将提供2.5 V片内基准电压供外部使用。或者,可将REF SELECT引脚设置为逻辑低电平以禁用内部基准电压,并将2.5V外部基准电压施加到此输人端。在设计中使用的是内部基准电
压,所以此引脚与地之间连接一个10uF的去藕电容,不需要施加外部基准电压。
四、构建NIOS II 软核
接下来,我们迕入了构建软核环节,点击 Tools->SOPC Builder或者下图红圈标示的图标:
点击以后,SOPC Builder运行,界面如下图示:
System Name中输入软核的名字:将其命名为AD_CPU。点击OK后,如下图所示
按图中标注的,clk_0为时钟名称,50.0为时钟值(单位为MHz),我们可以对他们进行修改。用鼠标点击50.0,将其改为80.0(注意:这里的时钟是根据具体需要进行设置的,设置的越大CPU的运行越快,但是也不是无限大的)。这时候,我们的软核时钟就为80.0MHz了。这是软核建立的第一步,接下来,我们要建立Nios II Processor。
1.构建CPU 模块
用鼠标点击左侧边框的红圈处Nios II Processor,如下图示
点击后,将出现下图,这一步我们来选择软核的类型。这里给我们提供了三种类型,Nios II/e占用资源最少600-800LEs,功能也最简单,速度最慢。Nios II/s占资源比前者多一些,功能也多了,速度也快一些。Nios II/f占资源最多,功能也最多,速度最快。选择的时候要根据你的需求和你的芯片资源来决定。在这里,我选择 Nios II/f,功能和速度都可以得到满足。下面的 Reset Vector是复位后启动时的Memory 类型和偏移量, Exception Vector 是异常情况时的Memory 类型和偏移量。现在还不能配置,需要SDRAM 和FLASH 设置好以后才能修改这里,这两个地方很重要。
接下来连续点击 Next,一直到下图为止
这里设置JTAG Debug Module,即JTAG调试时所用到的功能模块。功能越多,需要的资源越多,这里,我们选择 Level 1即可,不需要过多其他的功能。点击Finish,结束 Nios II Processor的建立后,如下图所示
2.建立SDRAM 模块
大家都知道电脑工作时,程序运行需要内存,FPGA工作程序运行时也需要空间,在这里SDRAM作为程序的运行空间。
接下来,我们要建立 SDRAM控制器,点击下图红框所示的地方。
点击后,如下图所示
在 Presets中选择第一项Custom,在Bits中选择16,其他项不动,点击Next,点击Finish,完成SDRAM控刢器的设置。在这里之所以选择16,是因为我们用的SDRAM(HY57V641620)是16位的。
3.建立EPCS控制器
首先在软核中添加 EPCS Serial Flash controller,如下图所示红圈处
双击红圈处后,没什么需要修改的,直接点击 Finish完成添加。
4.建立SYSTEM ID
System ID就是一种标示符,类似校验和的这么个东西,在你下载程序之前或者重启之后,都会对它进行检验,以防止 Quartus 和 NIOS 程序版本不一致的错误发生。点击下图所示红圈处
之后直接点击Finish,完成System ID 的建立。
5.建立JTAG UART
JTAG UART是实现PC和Nios II系统间的串行通信接口,它用于字符的输入输出,在 Nios II 的开发调试过程中扮演了重要的角色,接下来我们开始建立它的模块。点击下图所示红圈处,
或者下面红圈出查找JTAG,就可以找到了(其他模块同样的方法都可以找到)
点击后什么都不用修改,直接点击 Next->Finish完成JTAG UART模块的建立。到此为止,最基本的NIOS 系统模块就建立完成了,如下图所示
6.构建AD7606引脚模块
在前面我们已经对AD7606的工作原理进行了介绍,在这里需要为CPU添加一些IO模块来对AD7606进行操作。具体模块如下
1、ad_busy
1位 1位 1位 1位
输入 输出 输出 输出 输入
2、ad_convest 3、ad_cs 4、ad_rest 5、ad_data
16位
具体设置方法如下
在查找栏中输入PIO,点击上图中的PIO模块,点击后如下图
设置所要添加IO的位宽及输入输出方式。例如添加ad_cs时设置如下 位宽width 为1
输入输出方式为 output ports only 添加后如下图
然后右击pio_0 选择rename 将其重命名为ad_cs。其他同理。
注意:在设置ad_busy时要对其进行如下设置,因为对AD读数是采用中断方式,其他引脚不用设置这里。
7.构建串口模块
点击下图所示红圈处
点击后,如下图所示,红圈1处为波特率,我们设置为115200;红圈2处是否允讲通过软件改变波特率,我们选中,便是允讲,这样我们就可以通过软件来随时更改波特率,如果软件不设置,默认值就是上面设置的115200;红框3中是设置一些与串口有关的参数,校验方式,数据位,停止位,后面那个基本不用,大家根据实际情况来修改。讴置好以后,点击Next,Finish,完成构建。
到此为止,ad7606数据采集系统就建立完成了,如下图所示
8.配置及编译NIOS II
接下来我们需要对cpu进行设置一下,双击 cpu,Reset Vector处的Memeory选择cfi_flash,Exceptioni Vector选择sdram,其他不变,如下图所示
点击Finish,完成cpu设置。
最后一步设置就是对地址自动分配,这样做是为了不浪费空间,如果有特殊要求的,也可以手动设置,点击相应的地址,就可以手动修改了,大家自己试试吧。地址自动分配操作如下,点击下图所示红圈处,SYSTEM->Auto-assign Basic Addresses
接下来是中断的自动分配,和地址自动分配一样,SYSTEM-> Auto-assign IRQs,如果有什么特殊需要,可以手动更改中断得优先级。之所以要自动分配一下,是因为这个软件咳不够智能,当模块建好以后,有出现重复中断号的现象,编译的过程就会出现问题,自动分配了以后就会从上自下按顺序排列了。
这时候我们就可以编译了。点击Next,出现下图,如果需要仿真,点击红圈处,将其选中,我一般进行仿真,此处就不选了。
点击Generate,我们就开始编译了。(时间比较长,需要耐心等待) 当看到如下图红圈处所示的successful时表示没问题了,软核已经编译好了,点击Exit,回到了Quartus界面。
9.分配管脚
回到 Quartus 界面以后,在 Block1.bdf 界面里在空白处双击左键,会出现下图,看多了下图红圈处,这个就是我们做好得 NIOS II 软核了,用鼠标双击它(或者选中以后点击 OK)后,将其放到Block.bdf的空白处。
放好以后,如下图所示
在NIOS软核ad_cpu上点击右键后点击 Generate Pins for symbol ports,这一步作用就是生成管脚,通过命名以后分配真实的引脚。点击后如下图所示
五、建立锁相环 PLL 模块
接下来的工作我们还需要建立一个锁相环,对时钟进行倍频,如我们板子上是20MHz的有源晶振,我们要将其倍频到80MHz满足我们之前设定的NOIS 软核的时钟,还需要为SDRAM提供80MHz的时钟。下面我们就开始建立锁相环PLL模块。
在Block.bdf的空白处双击鼠标,点击下图所示的红圈处
出现下图后点击Next
出现下图所示,用鼠标选中红圈1所在的位置ALTPLL,在红圈2所在的位置处,加上PLL,目的是将我们所要建立的锁相环命名为PLL。
一切弄好以后,点击 Next,如下图所示,将红圈处设置为20,即我们输入的晶振为20MHz
设置好以后,点击Next,将下图中选中的地方去掉后,
连续点击 Next,等出现下图以后,将红圈处设置为4,这时c0 输出的频率就设置为了80MHz,可以从Actual settings处看出来。
设置好以后点击 Next,出现下图,将红圈1处选中,红圈2处修改为4,红圈3处修改为-75。
设置好以后,连续点击 Next,中间没有需要修改的,最后点击 Finish,完成 PLL的建立,然后点击OK,回到Quartus界面,将我们建好的PLL放到空白处。接下来的工作就是将 PLL连接到 NIOS 软核上。如下图所示
六、分配引脚
点击工具栏中
红圈所标注的图标进入如下界面
根据电路板上FPGA与ad7606引脚的连接件引脚编号分配到指定引脚上如上图红圈标注的。都修改好以后,点击红圈处进行编译
七、建立软件工程
首先,将NIOS II 9.0 IDE软件打开,我们需要做的就是首先建立一个软件工程,操作方法如下图所示,File->New->Project
点击后,会出现工程向导界面,如下图所示,选中红圈处的内容,Nios II C/C++ Application
点击Next,会出现下图所示内容,红圈1处是工程名,我将其修改为hello_world,红圈 2 处是目标硬件文件,点击 Browse,找到我们上一节生成的 NIOS 软核的位置,这个文件是以.ptf 为后缀的,在红圈3 处选中Hello World,这个地方是工程模版。再说说红圈 4,这个地方是改变工程所放位置的,如果不修改,软件工程的位置就在 Quartus工程目录下的software 下面。
点击Next,这里不用修改,点击 Finish,完成工程向导。
介绍三个部分,其他部分用处不大。我按功能将这三部分命名为代码区,工程目录区,和观察区
八、编译
接下来我们就要开始编译了,第一次的编译时间比较长,因为编译过程中会生成一个非常重要的文件 system.h,这个文件是根据我们构建的NIOS II 软核产生的,也就是说,system.h 的内容与软核的模块一一对应。一旦软核发生发化,就需要重新编译,重新产生 system.h文件。现在就开始编译,如下图所示,在工程目录栏中单击右键后,点击红圈处Build Project,或者直接按快捷键 Ctrl + b。
编译好以后,大家可以看到下面界面,红圈处说明了,编译完成。
九、运行
将仿真器与开发板的JTAG口相连。安装好以后,我们进行下面的操作,点击红圈处Nios II Hardware。这样就可已运行了。
十、程序流程图及主要程序
对AD7606数据进行读取是采用中断方式,当AD7606转换结束时,BUSY引脚会输出下降沿,FPGA 捕获中断对数据进行读取。
开始清除中断标志位读取转换数据将数据通过串口发送到上位机返回
/************************************************************* * 文件名:ad_test.c
* 功 能:控制ad7606采集电压信号
* 说 明:当ad7606转换结束,触发中断,对数据进行读取 ************************************************************/ #include #include \"system.h\" //包含基本的硬件描述信息 #include \"altera_avalon_timer_regs.h\" //定义内核寄存器的映射,提供对底层硬件的符号化访问 #include \"altera_avalon_pio_regs.h\" //包含基本的I/O口信息 #include \"alt_types.h\" //Altera定义的数据类型 #include \"sys/alt_irq.h\" #include \"sys/alt_alarm.h\" //系统时钟服务头文件 #include \"priv\\alt_busy_sleep.h\" //延时函数alt_busy_sleep() alt_u8 led_state = 0; alt_u16 ad_code[8]; /*************************************************************** ** 名 称: uart_SendByte ** 功 能: 向串口发送字节 ***************************************************************/ void uart_SendByte (alt_u8 ucDat) { while(!(IORD_ALTERA_AVALON_UART_STATUS(UART_BASE)&0x0040));//等待数据发送完毕 IOWR_ALTERA_AVALON_UART_TXDATA(UART_BASE,ucDat); } /*************************************************************** * 名称:uart_SendStr * 功能:向串口发送字符串 * ************************************************************/ void uart_SendStr (alt_u8 const *puiStr) { while (1) { if (*puiStr == '\\0') { break; /* 遇到结束符,退出 */ } uart_SendByte (*puiStr++); } } /************************************************************* * 名 称:ad_data() * 功 能:将读取的数字量转换成相应的电压值 ************************************************************/ float ad_data(alt_u16 code) { float ad_data; if(code<=0x7FFF) ad_data=code*0.00030517578125; else if (code>=0x8000) ad_data=(code*0.00030517578125)-20; return ad_data; } /************************************************************* * 名 称:ad7606_irq_Init() * 功 能:初始化ad7606中断,开中断,清边沿捕获寄存器 * 入口参数:无 * 出口参数:无 ************************************************************/ void ad7606_irq_Init() { IOWR_ALTERA_AVALON_PIO_IRQ_MASK(AD_BUSY_BASE, 0x01); //开AD_BUSY中断 IOWR_ALTERA_AVALON_PIO_EDGE_CAP(AD_BUSY_BASE, 0x00); //清中断捕获寄存器 alt_irq_register(AD_BUSY_IRQ,0,AD7606_ISR); //注册中断函数 } /************************************************************* * 名 称:void ad7606_Init() * 功 能:初始化ad7606 对ad7606进行复位 * 入口参数:无 * 出口参数:无 ************************************************************/ void AD7606_Init() { IOWR_ALTERA_AVALON_PIO_DATA(AD_REST_BASE,1); alt_busy_sleep(1); IOWR_ALTERA_AVALON_PIO_DATA(AD_REST_BASE,0); AD7606_irq_Init(); } /************************************************************* * 名 称:AD7606_ISR() * 功 能:AD7606中断服务程序 ************************************************************/ void AD7606_ISR(void* context, alt_u32 id) { alt_u8 i; char chars[1]; float voltage[8]; IOWR_ALTERA_AVALON_PIO_DATA(AD_CONVEST_BASE,0); for(i=0;i<8;i++) { IOWR_ALTERA_AVALON_PIO_DATA(AD_CS_BASE,0); //使能ad7606 ad_code[i] = IORD_ALTERA_AVALON_PIO_DATA(AD_DATA_BASE); //读取ad7606转换值 voltage[i] = ad_data(ad_code[i]); //得到电压值 IOWR_ALTERA_AVALON_PIO_DATA(AD_CS_BASE,1); } for(i=0;i<8;i++) { sprintf(chars,\"%f\ uart_SendStr(chars); uart_SendStr(\" \"); } uart_SendStr(\"\\n\"); IOWR_ALTERA_AVALON_PIO_EDGE_CAP(AD_BUSY_BASE, 0x00);//清中断捕获寄存器 IOWR_ALTERA_AVALON_PIO_DATA(LED_BASE,led_state); led_state=~led_state; alt_busy_sleep(1000000); IOWR_ALTERA_AVALON_PIO_DATA(AD_CONVEST_BASE,1); //开启转换 } /************************************************************* * 名 称:main() * 功 能:等待ad7606中断 ************************************************************/ int main() { ad7606_Init(); while(1); return 0; } //通过串口发送转换数据 因篇幅问题不能全部显示,请点此查看更多更全内容