基于SOPC的数字钟设计
学 院: 计控学院
班 级: 计本121
学 号: 2012021001
姓 名: 钟济强
指导老师: 张浩鹏
一、 设计概述
用Nios II DE2开发板的LCD显示电子钟的日期和时间,LCD分两行显示,第一行显示年月日,第二行显示时分秒。用输入按钮BUTTON来控制LCD行修改,通过Nios II IDE进行软件设计。
二、 设计要求
1.掌握Nios II系统的设计和使用方法;
2.学习通过Nios II IDE进行软件设计的方法;
3.实现在液晶屏上显示时间和日期,并可以对其进行设置。
三、 实验环境
DE2 开发板
QuartusII 7.2
SOPC Builder 7.2
NiosII IDE 7.2
四、 设计方案(总体设计、软件设计、硬件设计)
1. 总体设计要实现以下两个功能:
(1). 在液晶屏上显示时间,日期
(2). 对时间、日期能够进行设置
建立新工程clock
2. 硬件系统组成设计
根据系统要实现的功能和开发板配置,需要用到的DE2开发板上的外围器件有:
LCD:电子钟显示屏幕
按钮:电子钟设置功能键
CFI Flash存储器:存储软、硬件程序
SDRAM存储器:程序运行时将其导入SDRAM
根据所用到的外设和器件特性,在SOPC Builder中建立系统要添加的模块包括:NiosII CPU定时器,按键PIO,LCD, 外部存储器总线(Avalon三态桥),外部SDRAM控制器,外部Flash接口。
打开SOPC Buider,系统名称为sopc_clock
加入SDRAM组件
加入Flash Memory 组件
加入外部存储器总线(Avalon三态桥)
加入按键PIO
加入LCD
加入NiosII CPU定时器,选择标准型的
元件添加完后,要为每个外设分配及地址和中断请求优先级(IRQ),在System中选择Auto-Assign Base Address和Auto-Assign IRQs命令,这两个命令可分别简单分配外设基地址和中断优先级。然后生成Nios II系统。
然后生成顶层文件,将Nios II系统模块加入到顶层文件中,如图所示原理图
进行引脚锁定,并编译,然后进行硬件下载
3.软件系统设计
要实现系统所需的功能,大量的工作应该集中在软件设计和优化上。电子钟的软件功能主要分为显示、设置和时间算法三大部分。
显示部分的功能包括:显示时间(小时:分钟:秒)和 显示日期(年-月-日)
设置部分的功能包括:设置小时,设置分钟,设置年份,设置月份,设置日期和退出设置。
设置部分的程序主要用在对按键的响应。在编写程序前要对DE2开发板上的四个按键BUTTON功能进行如下分配:
(1). 主菜单
BUTTON0:设置选择键,可依次选择设置时、分、秒和年、月、日
BUTTON1:显示日期键
BUTTON2:显示时间键
(2). 子菜单(即进入对某个选项设置后的按键功能):
BUTTON1:选项数字增加
BUTTON2:选项数字减少
BUTTON3:退出对选项的设置,返回主菜单
时间算法部分的功能包括:时间累加和确定每个月的最大天数,使年、月、日能正确累加
五、 软件代码
1.count_binary.c的代码:
#include "count_binary.h"
//程序每秒钟检测一次按钮的状态,对日期和时间进行设置
#include "alt_types.h"
#include
#include
#include
#include "system.h"
#include "sys/alt_irq.h"
#include "altera_avalon_pio_regs.h"
#include "lcd.h"
volatile int edge_capture;
void LCD_Init()
{
//LCD初始化
lcd_write_cmd(LCD_BASE,0x38);
usleep(2000);
lcd_write_cmd(LCD_BASE,0x0C);
usleep(2000);
lcd_write_cmd(LCD_BASE,0x01);
usleep(2000);
lcd_write_cmd(LCD_BASE,0x06);
usleep(2000);
lcd_write_cmd(LCD_BASE,0x80);
usleep(2000);
}
void LCD_Show_Text(char* Text)
{
//LCD输出格式
int i;
for(i=0;i
{
lcd_write_data(LCD_BASE,Text[i]);
usleep(2000);
}
}
void LCD_Line1()
{
//向LCD写命令
lcd_write_cmd(LCD_BASE,0x80);
usleep(2000);
}
void LCD_Line2()
{
//向LCD写命令
lcd_write_cmd(LCD_BASE,0xC0);
usleep(2000);
}
static void 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);
}
/*初始化button_pio*/
static void init_button_pio()
{
void* edge_capture_ptr=(void*)&edge_capture;
/*开放全部4个按钮的中断*/
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);
}
void delay(unsigned int x)
{
while(x--);
}
int check_month(int month)
{
//如果是1、3、5、7、8、10、12月,则每月31天,程序返回1
if((month==1)||(month==3)||(month==5)||(month==7)||(month==8)||(month==10)||(month==12))
return 1;
//如果是4、6、9、11月,则每月是30天,程序返回0
if((month==4)||(month==6)||(month==9)||(month==11))
return 0;
//如果是2月,程序返回2,具体多少天还要根据年的判断来决定
if(month==2)
return 2;
}
/*
闰年的计算方法:公元纪年的年数可以被四整数,即为闰年
被100整除而不能被400整除为平年;被100整除也可被400整除的为闰年
如2000年时闰年,而1900年不是
*/
int check_year(int year)
{
if(((year%400)==0)||(((year%4)==0)&&((year%100)!=0)))
return 1;
//是闰年,返回1
else return 0;
//不是闰年,返回0
}
int main(void)
{
int screen=0;//共有两行,一行显示年月日,一行显示时间
int pos=0;//每行都有三个位置,第一行是年月日,第二行是时分秒
int year,month,day,hour,minute,second;
unsigned long sum;//sum要设置为长整型变量,不然会溢出
char date[16];
char time[16];
int year1=6;
int year2=0;
int year3=0;
int year4=2;
int month1=1;
int month2=0;
int day1=1;
int day2=0;
int hour4,hour3,hour2,hour1,minute2,minute1,second2,second1;
unsigned int screenflag;
hour=0;minute=0;second=0;year=2006;month=1;day=1;
//年月日时分秒初始化
init_button_pio();
LCD_Init();
while(1)
{
if(pos>=3)
pos=0;//共有三个位置0、1、2,超过了2要马上清0
if(screen>=2)
screen=0;//共有两行0、1,超过了1要马上清0
//na_LED8->np_piodata=1<
if(screen==0)
screenflag=8;
else screenflag=0;
//IOWR_ALTERA_AVALON_PIO_DATA(LED_GREEN_BASE,(1<
usleep(1000000);//等待1秒的定时时间
if(second<59) second++;
else
{
second=0;
if(minute<59) minute++;
else
{
minute=0;
if(hour<23) hour++;
else
{
hour=0;
if(day<30) day++;
else
{
day=1;
if(month<12) month++;
else
{
month=1;
if(year<9999) year++;
else year=2005;
}
}
}
}
}
switch(edge_capture)//检测按钮
{
case 0x08:pos=pos+1;break;//改变调整位置
/*
对数据进行加减操作的CASE:case 0x02和case 0x04
根据当前调整位置,判断当前屏显示的是年、月、日还是时分、秒
然后决定是对年月日进行加减还是对分时秒进行加减
*/
case 0x02://对当前位置上的数据执行减操作
if(pos==0)
{
if(screen==0)
{
if(day>1)day--;
else
{
if(check_month(month)==0)day=30;
if(check_month(month)==1)day=31;
if(check_month(month)==2)
{
if(check_year(year))day=29;
else day=28;
}
}
}
if(screen==1)
{
if(second>0)second--;
else second=59;
}
}
if(pos==1)
{
if(screen==0)
{
if(month>1)month--;
else month=12;
}
if(screen==1)
{
if(minute>0)minute--;
else minute=59;
}
}
if(pos==2)
{
if(screen==0)
{
if(year>0)year--;
else year=2005;
}
if(screen==1)
{
if(hour>0)hour--;
else hour=23;
}
}
break;
case 0x04://对当前位置上的数据执行加操作
if(pos==0)
{
if(screen==0)
{
if(check_month(month)==0){if(day<30)day++;else day=1;}
if(check_month(month)==1){if(day<31)day++;else day=1;}
if(check_month(month)==2)
{
if(check_year(year)){if(day<29)day++;else day=1;}
else {if(day<28)day++;else day=1;}
}
}
if(screen==1)
{
if(second<59)second++;
else second=0;
}
}
if(pos==1)
{
if(screen==0)
{
if(month<12)month++;
else month=1;
}
if(screen==1)
{
if(minute<59)minute++;
else minute=0;
}
}
if(pos==2)
{
if(screen==0)
{
if(year<9999)year++;
else year=2005;
}
if(screen==1)
{
if(hour<23)hour++;
else hour=0;
}
}
break;
case 0x01:screen++;break;//换屏
}
edge_capture=0;
{
year4=year/1000;year3=(year-year4*1000)/100;
year2=(year-year4*1000-year3*100)/10;year1=year%10;
month2=month/10;month1=month%10;
day2=day/10;day1=day%10;
LCD_Line1();
date[0]=year4+0x30;date[1]=year3+0x30;
date[2]=year2+0x30;date[3]=year1+0x30;
date[4]=' ';date[5]=' ';
date[6]=month2+0x30;date[7]=month1+0x30;
date[8]=' ';date[9]=' ';
date[10]=day2+0x30;date[11]=day1+0x30;
date[12]=' ';date[13]=' ';
date[14]=' ';date[15]=' ';
LCD_Show_Text(date);
}
{
hour4=0;hour3=0;
hour2=hour/10;hour1=hour%10;
minute2=minute/10;minute1=minute%10;
second2=second/10;second1=second%10;
time[0]=' ';time[1]=' ';
time[2]=hour2+0x30;time[3]=hour1+0x30;
time[4]=' ';time[5]=' ';
time[6]=minute2+0x30;time[7]=minute1+0x30;
time[8]=' ';time[9]=' ';
time[10]=second2+0x30;time[11]=second1+0x30;
time[12]=' ';time[13]=' ';
time[14]=' ';time[15]=' ';
LCD_Line2();
LCD_Show_Text(time);
}
}
}
2.lcd.h的代码:
#ifndef _LCD_H_
#define _LCD_H_
//LCD Module 16*2
#define lcd_write_cmd(base,data) IOWR(base,0,data)
#define lcd_read_cmd(base) IORD(base,1)
#define lcd_write_data(base,data) IOWR(base,2,data)
#define lcd_read_data(base) IORD(base,3)
//----------------------------------------------------------------------
void LCD_Init();
void LCD_Show_Text(char* Text);
void LCD_Line2();
void LCD_Test();
//----------------------------------------------------------------------
#endif
六、 实验分析和实验结果
用输入按钮BUTTON[0]可以控制LCD行修改,同时让NiosII DE2开发板上的绿色发光二极管LEDG3的亮灭来表示。用输入按钮BUTTON[3]可以控制日期和时间的修改,BUTTON[1]和BUTTON[2]输入按钮可以减少和增加显示的数字。
实验中开始时SDRAM的BIDIR端口的命名错误导致引脚锁定未成功,此端口的名称应为DRAM_DQ[15..0]。此外,三态桥的设置被改变了,需要重新设置,回到Quartus II中,在assignments中选择settings,Device->Device and Pin Options->Unused Pins,改为As input tri-stated。
七、 总结
通过这12周的SOPC技术综合设计实践的学习,我学会了Quartus II,
SOPC Buider和Nios II DE2 的使用,并通过这些软件可以进行简单的实验操作,强化了我的动手能力,并通过这几次实验更加了解SOPC的设计与应用。在实验中,遇到了各种各样的问题,首先是自己进行检查,查不出来再由老师同学进行检查和讲解,提高了自己解决问题的能力,并且熟悉了整个实验的流程及具体的操作。我觉得这门课程很有意思,实验中是把各个部分的操作整合在一起,形成一个系统,做实验时要有整体感,一旦出现问题整体的重新进行设置和编译,比如如果添加组建时出现错误,要重新添加,重新生成模块,重新倒入模块,重新编译顶层文件,是一个整体的过程。
八、 附录
参考《SOPC技术与应用》,编著:江国强,机械工业出版社。
参考《SOPC技术综合设计实践实验指南》,编著:邱德惠。
思考题:
1. 何谓SOC?
SOC(System On a Chip)称为片上系统,它是指将一个完整产品的功能集成在一个芯片上或芯片组上。SOC中可以包括微处理器CPU、数字信号处理器DSP、存储器(ROM、RAM、Flash等)、总线和总线控制器、外围设备接口等,还可以包括数模混合电路(放大器、比较器、A/D和D/A转换器、锁相环等),甚至传感器、微机电和微光电单元。
2. 何谓SOPC?
SOPC(System On a Programmable Chip)称为可编程片上系统,它是基于可编程逻辑器件(FPGA或CPLD)可重构的SOC。SOPC集成了硬核或软核CPU、DSP、锁相环(PLL)、存储器、I/O接口及可编程逻辑,可以灵活高效地解决SOC方案,而且设计周期短,设计成本低,一般只需要一台配有SOPC开发软件的PC和一台SOPC实验开发系统(或开发板),就可以进行SOPC的设计与开发。
3. 何谓IP核?
IP(Intellectual Property)是知识产权的简称。集成电路IP是指经过预先设计、预先验证、符合产业界普遍认同的设计规范和设计标准,并具有相对独立并可以重复利用的电路模块或子系统,如CPU、运算器、存储器、放大器等。集成电路IP模块具有知识含量高、占用芯片面积小、运行速度快、功耗低、工艺容差性大等特点,可重复用于SOC、SOPC或复杂ASIC设计中。
4. 何谓嵌入式系统,嵌入式系统的CPU核可以分为哪两种类型?
嵌入式系统是指嵌入到对象体系中的专用计算机系统,包括硬件和软件两大部分。硬件包括处理器、存储器、输入输出接口和外部设备等,软件包括系统软件和应用软件。
基于SOPC的嵌入式系统结构主要包括嵌入式微处理器(CPU核)、定时器(Timer)、嵌入式锁相环(PLL)、嵌入式数字信号处理器(DSP)及其他IP模块等部分。
嵌入式系统的CPU核可以分为硬核和软核。
5. 简述CPU软核的特点。
软核通常以可综合的HDL提供,因此具有较高的灵活性,并与具体的实现工艺无关,其主要缺点是缺乏对时序、面积和功耗的预见性。由于软核是以源代码的形式提供,尽管源代码可以采用加密方法,但其知识产权保护问题不容忽视。
6. 简述HAL的用途及基于HAL的外围设备的编程方法。
HAL作为支持Nios II处理器系统的软件包,为用户的嵌入式系统上的外围设备提供了与之相匹配的接口程序。用户不用自己建立或复制HAL文件,也不需要编辑HAL中的任何源代码,使用时只需要在C/C++源程序中指明代表这些接口程序的库函数包含的头文件即可。
在一个基于Nios II的SOPC系统上,外围设备包括通用异步串口UART、发光二极管LED、七段数码管、按钮、LCD、存储器、定时器、鼠标、VGA等。在应用软件的开发中,对于按钮、发光二极管LED、七段数码管等通用输入输出设备的编程比较简单,而对于通用异步串口UART、LCD、VGA等字符模式外围设备的编程就比较复杂。
为了方便用户对字符模式外围设备的编程,HAL支持标准输入、标准输出和标准错误函数,允许在程序中调用stdio.h中的I/O函数来完成对这些设备的访问,也支持字符模式外围设备的通用访问。
7. 简述SOPC的设计流程。
SOPC硬件开发流程
a) 创建Quartus II工程
b) 创建Nios II系统模块
c) 配置Nios II系统
d) 将Nios II系统模块、LPM和用户自定义模块连接起来
e) 编译、引脚分配、编程下载
SOPC软件开发流程
Nios II程序包括一个应用工程、可选的库工程和一个板支持包工程(自动生成)。将Nios II程序编译成一个能在Nios II处理器上运行的.elf文件。
1. 新建Nios II C/C++应用工程与系统库工程
为新的Nios II工程命名、选择目标硬件(硬件系统的PTF文件,IDE根据该文件来建立系统库)、选择新工程的模板、建立C的源文件与头文件(注意一定要加上后缀名.c)、创建新的系统库(为硬件提供相应驱动)
注意:工程文件夹要建在D盘根目录下(其中D盘装有Quartus II软件)
2. 编译与设置工程
对工程进行编译project→build project,编译成功后,在工程下面会出现一个Binaries目录,其中有一个可执行文件.elf。
3. 运行与调试程序
4. 编译整个项目
软件设计完成并调试成功后,可以在Quartus II中将整个项目进行编译,将硬件配置信息和软件初始化信息编译在一起,并固化到EPCS1芯片中。
8. 简述AVALON总线的特点。
Avalon总线是一种相对简单的总线结构,主要用于片内处理器与外设的连接,构成SOPC系统。Avalon总线拥有多种传输模式,以适应不同外设的要求。Avalon总线支持多总线的主外设,并允许在单总线外设之间传输多个数据单元,在构建SOPC系统时拥有极大的灵活性。
9. System.h系统描述文件包含了哪些NIOS II系统的硬件信息?
·处理器配置
·外设列表
·存储器配置
·定制指令宏
·处理器复位和异常地址
·代码段位置
10. 基于altera公司的SOPC系统如何实现上电自动加载程序?
1) 在SOPC Builder中,将CPU的Reset Vector设为cfi_flash
2) 下载硬件时,先将DE2板上液晶左边的开关扳到prog,在下载器窗口中Mode选项选择Active Serial Programing选项,然后单击Add File按钮,选择FlashTest.pof文件,勾选全部选项,点击start按钮即开始下载。下载结束后将上述开关扳回run。以后上电之后就自动将硬件加载到FPGA中,可以直接运行软件。
3) 下载软件时,选择Tool->Flash Programmer,点击Program Flash就可以将程序烧入Flash。
本文来源:https://www.2haoxitong.net/k/doc/97d0ef16c1c708a1294a4472.html
文档为doc格式