尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

FPGA基于NIOS II软核的电子钟设计:从硬件搭建到上板调试全解析

FPGA基于NIOS II软核的电子钟设计:从硬件搭建到上板调试全解析 简介一套基于NIOS II软核处理器与FPGA的电子钟设计完整工程适合FPGA初学者、嵌入式爱好者和电子设计竞赛队伍学习参考。工程针对数字钟的常见功能需求给出了从硬件驱动到软件控制的整体方案底层使用Verilog编写数码管驱动上层基于Qsys构建的SOPC系统完成计时控制通过定时器产生基准时基驱动八位数码管显示时分秒三个按键分别实现模式切换、数值加一和减一调节中的对应位会闪烁提示操作LED同步点亮反馈闹钟时间到后蜂鸣器报警并联动流水灯指示。资源共602个文件压缩包22.66MB包含设计源码、C程序、Qsys系统配置、SDC约束以及可直接下载的SOF与ELF镜像各类型文件用途清晰目录结构利于按模块排查和二次修改。目前已有2274人学习下载适合希望从头完整搭建NIOS II硬件系统、掌握软核处理器与FPGA逻辑协同开发的读者。 前阵子帮朋友做课程设计选的题目就是“FPGA基于NIOS II的电子钟设计”做完之后发现这个项目特别适合分享出来。它麻雀虽小五脏俱全既要用Verilog把NIOS软核、定时器、GPIO这些组件在FPGA里搭起来又要写C语言让处理器跑起来还要处理数码管动态扫描、按键消抖这些硬件相关的细节非常适合想入门SoC方向的朋友。市面上很多电子钟都是纯逻辑状态机做的但用NIOS II软核做思路完全不一样更像是“在FPGA里嵌入一颗CPU来控制外围设备”这个思维转换对后面学Zynq、学嵌入式都很有帮助。顺便说一句如果你手头是EGO1或者其他Altera/Intel系列的板子这套流程直接照着走就行。如果用的是Xilinx的板子NIOS换成MicroBlaze思路完全一样。这篇文章不以具体板卡型号为限制重点把系统设计、硬件搭建、软件开发、上板调试这几个环节讲透。1. 电子钟软硬件方案的整体拆解1.1 纯逻辑方案与NIOS软核方案的取舍先说说为什么选NIOS II。传统电子钟用纯Verilog写状态机把计数、进位、译码、扫描全部用硬件逻辑实现好处是延时确定、不依赖代码执行流程坏处也很明显改时间格式、加闹钟、调整显示模式每一处改动都要动RTL代码改完还要重新综合布线调试周期很长。NIOS II方案的本质是“在FPGA内部生成一颗32位处理器软核”外围接上定时器、GPIO、UART这些标准外设然后像写单片机程序一样用C语言控制整个系统。这样做有三个实际好处第一计时逻辑、按键扫描、显示刷新全部变成软件逻辑改起来无非是改C代码重新编译几分钟就能迭代一版第二NIOS II的开发流程和单片机很接近对于学过STM32的同学学习曲线非常平缓第三NIOS II外设都提供HAL库和寄存器级驱动不用自己造轮子省下大量时间。我也必须说两句实在话。NIOS II方案在资源占用上比纯逻辑大不少还要占用不少内部存储做程序和数据空间但对于EGO1这种Cyclone IV级别板子资源完全够用。另外纯逻辑方案的实时性天然更好而NIOS II依赖中断和主循环调度只要程序设计得当做电子钟的计时精度毫无问题。1.2 系统整体架构的划分思路整个电子钟系统的架构可以分成两大部分FPGA硬件逻辑部分和NIOS II软件部分。硬件部分通过Platform Designer老版本叫QSys搭建一个完整的最小系统包括NIOS II处理器软核、片上RAM、定时器IP、PIO控制器。软件部分跑在NIOS II内部通过外设寄存器地址访问硬件。我把架构画出来你感受下CPUNIOS II/e经济型内核适合做控制类任务时钟由FPGA外部50MHz晶振统一供给存储片上RAM存放程序指令和数据定时器Interval Timer IP产生1ms周期中断作为计时基准PIO若干组GPIO分别控制数码管段选、位选和独立按键UART可选调试打印信息用这个架构说白了就是一个微型计算机。硬件上的连线相当于把CPU、内存、定时器、GPIO用总线连接起来软件上的逻辑相当于在CPU上跑一个裸机程序。这个模型一旦建立后面做任何基于NIOS II的项目基本都是在往这个骨架上添砖加瓦。2. Platform Designer下的硬件平台搭建2.1 在Platform Designer里搭一个最小NIOS系统打开Quartus新建工程后在Tools菜单找到Platform Designer并打开。这一步是在图形化环境下把各个IP拖出来连线操作起来像搭积木但每根线的含义都要清楚。第一步添加NIOS II处理器。双击添加NIOS II Processor选择Nios II/e经济型。这里有个关键选择JTAG调试模块要不要加我建议选上JTAG调试模块因为上板调试的时候很多问题要依赖它查看内部变量。如果空间紧张可以在最后阶段把调试模块去掉以省资源。第二步添加片上存储。选择On-Chip Memory设置总容量。我用的是64KB对电子钟这种程序量极小的项目32KB其实也够但64KB更从容Quartus综合的时候也不会因为容量不足报错。存储类型选RAM不要选ROM。第三步添加串口调试选择JTAG UART。这个IP很有用可以把printf的输出映射到Quartus的调试终端我调试时习惯在代码里加一串打印信息观察状态机变化没有串口会很不方便。第四步添加Interval Timer定时器也就是核心的计时器IP。这里有个比较重要的配置项后面单独讲。第五步添加PIO控制器根据外设引脚需求创建。我用了两组PIO一组输出给数码管段选和位选一组输入读按键。所有组件添加完后最关键的步骤来了在System Contents里双击各个IP的时钟和复位连接确保它们都在System 50MHz这个时钟总线上。然后点击Assign Base Address让系统自动分配基地址。最后点击Generate生成整个硬件系统。生成的.v文件会在工程目录下自动关联到顶层模块。2.2 定时器与PIO外设的参数规划定时器的参数规划是这个项目硬件设计的重头戏。我使用的是Full-featured定时器模式计数器宽度设为32位。为什么是32位而不是16位因为系统时钟是50MHz也就是一个时钟周期20ns如果我要实现1ms定时需要的计数次数是50000这个数字16位能放下但考虑到以后可能要扩展成更长的计时周期32位更保险省得以后返工。定时器的工作方式选择往下计数模式Count down当计数值从初始值减到0会输出一个脉冲并触发中断。初始值设置在软件里通过寄存器写入这里硬件上只需要把周期值预留出来。PIO参数的规划更加琐碎但不能错。我把段选引脚配置为8位输出位选配置为6位输出按键配置为4位输入。PIO的方向设置是个经典坑在Platform Designer里Output Only和Input Only都会自动配置方向但如果你选的是Bidirectional一定要在代码里写方向寄存器否则输入输出方向是乱的。我的习惯是用Input Only和Output Only把方向控制交给硬件软件少操心。另外每个PIO都要单独设置中断条件。按键PIO我选择了“上升沿触发中断”和“下降沿触发中断”都打开这样按键按下和松开都能感知到。定时器PIO不需要中断PIO对应的是中断控制器里的中断号这个中断号要记下来后面写软件ISR要用。2.3 硬件生成后的顶层集成方法Platform Designer生成的是一个带固定接口名称的模块名称通常是my_system之类的名字加上你生成时填写的实例名。回到Quartus工程需要新建一个顶层Verilog文件把这个模块例化进去。顶层模块要解决两件事一是把Platform Designer生成的NIOS系统各个接口连接到FPGA的真实引脚上二是把没有被NIOS系统占用的引脚比如数码管的公共端根据位选信号进行分配。这里有个非常实用的经验先把Platform Designer生成的模块源码添加进工程然后在顶层文件里例化用wire定义好各接口信号再把与引脚相关的信号通过Quartus的Pin Planner绑定到具体引脚。绑定引脚时务必在Pin Planner里确认每个信号的IO标准我用的板子大多是3.3V所以IO Standard选3.3V-LVTTL选错会导致信号不稳定甚至损坏引脚。3. Nios II软件的编写与逻辑实现3.1 BSP工程创建与代码结构约定硬件平台生成后进入Nios II SBT for Eclipse或更新版本的Nios II Command Line Shell创建BSP包和软件工程。BSP包相当于板级支持包会自动根据sopcinfo文件生成对每个外设的寄存器定义和驱动函数。我的做法是先用File New Nios II Board Support Package选中生成的.sopcinfo使用默认设置直接生成再把软件工程和它关联起来。这里有个重要选择BSP的设置中关于stdout设备的选项。默认会指向jtag_uart这个不用改方便printf调试。另外在BSP Editor里确认timer的驱动支持是打开的如果没有生成对应的timer驱动代码里访问寄存器会报错。工程建好后代码建议分成三个文件main.c放主循环和初始化逻辑timer.c放定时器中断服务程序display.c放数码管扫描和按键响应。这样结构清晰调试也方便。如果你之后要往工程里加闹钟、秒表功能也是在这三个文件上继续扩展。3.2 定时器中断电子钟的核心计时逻辑计时逻辑是整个软件的核心我把它全部写在定时器中断服务程序里。思路是把1ms作为最小时间粒度中断每触发一次就对ms计数加1累加到1000说明过了1秒然后秒计数加1。秒满60进分钟分钟满60进小时小时满24自动归零。中断服务程序里有个细节硬件定时器触发中断后需要清除中断标志位否则中断会反复进入。清除方式是在状态寄存器里写0。代码上我习惯用IOWR_ALTERA_AVALON_TIMER_STATUS宏把寄存器值为0写入状态寄存器这条语句不能漏。很多人做NIOS的时候定时器一直狂触发中断原因多半就是没清标志位。另外在ISR中尽量不要做耗时操作。我早期版本在ISR里直接做数码管刷新结果计时明显变慢主循环和ISR抢资源。后来把ISR只负责计数和置标志位主循环里检查标志位再做刷新问题立刻解决。这下你明白为什么ISR要短小精悍了吧。3.3 数码管动态扫描与按键输入的工程实现数码管的动态扫描原理是一个共阴或共阳数码管靠位选信号选通某一位然后用段选信号决定这一位显示什么数字。因为人眼有视觉暂留效应只要循环扫描每一位点亮的频率足够高通常1ms切换一次看起来就像所有位同时亮着。实现上我在主循环里用一个scan_index变量从0到5循环每次循环做两件事先把前一位的位选信号拉低避免显示重叠再往段选寄存器写入当前位要显示的字符对应的段码最后把当前位的位选信号拉高。整个过程本质上是一个小型状态机状态切换靠主循环轮询不阻塞。段码表的编写要特别留意共阳共阴的区别。共阳数码管公共端接高电平某个段要点亮对应段信号必须为低电平。比如要显示数字0共阳段码是0xC0。共阴极正好相反对应位为高电平。我用的是六位共阳数码管所以段码表做了注释以免换板子时搞混。按键输入方面我用PIO中断加软件消抖的方式来处理。按键按下时PIO会产生一次中断我在ISR里只记录当前按键状态并置一个按键事件标志真正执行功能调整放在主循环。消抖采用延时法检测到按键状态改变后延时20ms再读取一次如果状态一致就确认是有效按键。这样做的好处是简单可靠缺点是需要主循环及时处理但对电子钟这种低频应用完全够用。4. 上板调试的完整流程与排查技巧4.1 从烧写硬件配置到运行软件程序的步骤上板调试的第一步是综合编译Quartus硬件工程生成.sof文件。打开Programmer选择JTAG模式添加.sof文件勾选Program/Configure点击Start。这一步会把FPGA的硬件配置烧到板子上的FPGA配置RAM中掉电就丢失适合调试阶段使用。注意如果NIOS II核里配置了JTAG调试模块烧写完.sof之后需要把软件工程也编译好生成.elf文件然后在Eclipse里右键工程名选择Run As Nios II Hardware。这一步会自动连接JTAG先下载.sof也可以选择跳过如果你已经烧过.sof再下载.elf最后复位并运行程序。我建议分开操作先用Programmer烧.sof再用Eclipse运行.elf这样能明确区分到底是硬件问题还是软件问题。一个常见的连锁现象是只烧了.sof忘记运行.elf板子上数码管不亮或者只运行了.elf没有烧.sofJTAG找不到设备。所以每次上电后第一件事先烧.sof再确认Nios II的JTAG调试模块能被识别最后才运行软件。4.2 通过调试器和串口定位异常的实战思路调试NIOS II的时候最大的优势是可以看到变量设置断点。我用的是Eclipse自带的Debug配置断点打在ISR里或主循环中观察sec、min、hour变量是否正确累加。如果发现秒数不正常优先检查定时器初始值是否算错以及标志位有没有清除。另一个调试法宝是JTAG UART打印。我在初始化完成后打印一行System Ready然后在按键响应里打印按键编号。有次按键功能失效我打印后发现按键中断一直在触发原因是PIO中断使能后在ISR里没有清除PIO的中断捕获标志。这个通过打印很快就能定位。数码管显示异常的排查思路稍微复杂一些。如果显示乱码优先怀疑段码表错误或共阳共阴搞反如果只有个别位不亮优先检查位选驱动如果整体闪烁优先检查扫描延时是否过大。我用示波器量了一下位选波形发现扫描周期大约6ms刷新一遍视觉上正好稳定。这里也分享一个技巧如果不方便量频率可以用肉眼观察闪烁程度来估算一般1到2ms刷新一位整体刷新周期不要超过8ms。4.3 常见问题速查表现象可能原因解决办法编译通过但上板无反应未烧.sof或.elf按顺序烧写.sof并运行.elf定时中断不停触发ISR未清除定时器状态标志在ISR首行写状态寄存器清0按键无响应PIO中断捕获标志未清除读取并清除PIO捕获寄存器数码管乱码共阳共阴段码表弄反核对段码表极性个别位不亮位选信号方向或电平不对检查PIO方向和位选驱动极性计时明显偏慢ISR耗时太长把耗时操作移到主循环程序跑飞或硬件报错片上RAM容量不足增大On-Chip Memory容量5. 从基础功能到扩展玩法的优化路线5.1 显示刷新算法和计时精度的进一步优化如果觉得动态扫描的主循环占CPU太多可以做一个简单的优化把扫描放到定时器中断里用1ms中断切换一位。这样主循环就解放出来了可以做更复杂的事情。我当时实测把扫描放进中断后主循环CPU占用几乎为零按键响应也更灵敏。计时精度的优化要关注晶振频率。板载晶振标称50MHz但实际可能有些偏差长期运行会累积误差。NIOS II提供了altera_avalon_timer驱动可以在软件里做一个简单的校正用秒表看一小时差多少秒然后在Cnt子寄存器里补偿Tick数。这个思路和机械表调快慢是一个道理。5.2 闹钟、整点报时和扩展外设的加入思路有了NIOS II软核加功能主要就是软件层面的工作。加闹钟只需要定义一个闹钟变量在秒进位后比较当前时间和闹钟时间一致则触发蜂鸣器整点报时同理在分进位时判断当前分钟数是否为0即可。如果要用到蜂鸣器或继电器只需要在Platform Designer里再加一组PIO然后在软件里控制高低电平。这个工作量比纯逻辑方案小太多也是软核方案最大的价值所在。不过要注意新增外设后硬件工程要重新综合生成.sof软件的BSP也要重新生成否则地址映射会不一致导致访问到错误的外设地址。我自己做完电子钟后又把显示从六位数码管换成了LCD1602加了温湿度读取只是多加了一个I2C的IP和相应驱动一周不到就弄完了。可见架构搭好之后扩展性是相当大的。5.3 给新手学习者的实践建议如果你第一次接触NIOS II建议按这个顺序推进第一步什么都不改只跑通Platform Designer自带的hello_world工程确认JTAG下载、软件运行、printf打印都正常第二步在这个基础上加一个LED闪烁程序理解PIO输出第三步对照本文加定时器、数码管显示、按键中断最后再整合成完整电子钟。每步都生成一个独立版本的工程方便回溯。代码维护上建议用Git管理工程每次能稳定运行就打个tag因为FPGA工程文件多随便改个底层可能就编译不通过没有版本管理容易心态崩溃。这个建议不仅适用于本项目做任何FPGA大项目都值得遵循。我实际操作中最深刻的体会是NIOS II电子钟这个项目真正的难点不在代码量而在于理解“硬件生成软件、软件控制硬件”的边界。硬件上少一个IP、少连接一条总线软件层面就多一次崩溃软件上少清一次标志位硬件上就多一次异常。所以遇到问题不要慌先从硬件配置检查起再从软件驱动程序查起一步步缩小范围最终一定能解决。以后你往Zynq这类ARMFPGA异构架构切换时这个项目的经验会给你很扎实的底气。本文还有配套的精品资源点击获取
返回列表