项目要求:
1.实现一个可定时时钟的功能,用小脚丫FPGA核心模块的4个按键设置当前的时间,OLED显示数字钟的当前时间,精确到分钟即可,到整点的时候比如8:00,蜂鸣器报警,播放音频信号,最长可持续30秒;
2.实现温度计的功能,小脚丫通过板上的温度传感器实时测量环境温度,并同时间一起显示在OLED的屏幕上;
3.定时时钟整点报警的同时,将温度信息通过UART传递到电脑上,电脑上能够显示当前板子上的温度信息(任何显示形式都可以),要与OLED显示的温度值一致;
4.PC收到报警的温度信号以后,将一段音频文件(自己制作,持续10秒钟左右)通过UART发送给小脚丫FPGA,蜂鸣器播放收到的这段音频文件,OLED屏幕上显示的时间信息和温度信息都停住不再更新;
5.音频文件播放完毕,OLED开始更新时间信息和当前的温度信息
系统实现过程:
第一次参加这种活动,也是第一次接触FPGA,刚开始真的不知道从那里下手,然后就从语言开始学起,在网络上学习一些关于Verilog HDL语言的一些视频,包括一些语法、模块例化等。在.V文件里面一些必须注意到的事情有些语法在该文件里面不适用。学了一些基本的语法知识以后,才开始程序的编写。
作为一个从未接触过FPGA的小萌新来说,刚下载好编译软件真的无从下手,建一个工程,运行一个实验都是会漏洞百出,错误连连。于是,我就只能采用最笨最原始的方法,开始在一些视频网站上面搜集一些资料、视频学习如何完好的运行一个工程。
刚开始大家同学们都会跟着直播老师一起学习如何点灯,如何使用ip核,如何实现时钟分频等一些基本操作,我本以为跟着直播老师一起学习我基本也可以完成任务但后来才发现,老师的直播内容远不及我们对知识的渴求,跟着直播老师学习可以了解代码的大致意思,然后编译,仿真,运行,烧录到开发板芯片上面,便于我们更直观的观察到代码的改变对板的影响,观察每一部分的烧录效果,对代码的理解也会更深刻。
下面就是我使用这块开发板所实现的功能:
根据改开发板项目要求,要完成任务需要用到OLED、温度传感器、蜂鸣器、uart串口和板上的摁键key和sw的功能。
实现的思路:
1、时钟信号的编码及其定时
2、温度计DS18b12温度传感器输出信号度数据
3、无源蜂鸣器的调用
4、OLEDssd1306模块的显示
5、按键消抖和时间调节
6、实现uart数据转换控制蜂鸣器,并通过串口向蜂鸣器发送音乐数据
7、最后综合整理,完成顶层文件的编写
时钟部分:
时钟在我所完成的项目里是一个艰难的开始,虽然它组成都是比较易理解和掌握的,但当编写代码时很容易混淆,这个代码真了浪费了太多不该浪费的时间,好在学到了很多。每当遇到不会的我都会去视频网站去寻找这部分的资源,然后学习和借鉴语法,来完成我的任务。
时钟一小部分源代码:
always @(posedge i_0_1s_clk, negedge i_reset_n)
begin
if(1'b0 == i_reset_n)
begin
r_1s_clk <= 1'b0;
r_1s_clk_falling <= 1'b0;
end
else
begin
r_1s_clk <= i_1s_clk;
r_1s_clk_falling <= r_1s_clk & (~i_1s_clk);
end
end
always @(posedge i_0_1s_clk, negedge i_reset_n)
begin
if(1'b0 == i_reset_n)
begin
r_increment <= 1'b0;
r_increment_falling <= 1'b0;
end
else
begin
r_increment <= i_increment;
r_increment_falling <= r_increment & (~i_increment);
end
end
always @(posedge i_0_1s_clk, negedge i_reset_n)
begin
if(1'b0 == i_reset_n)
r_adjust_cnt <= 2'd0;
else
if(1'b1 == r_adjust_falling)
r_adjust_cnt <= r_adjust_cnt + 1'b1;
end
always @(posedge i_0_1s_clk, negedge i_reset_n)
begin
if(1'b0 == i_reset_n)
begin
r_adjust <= 1'b0;
r_adjust_falling <= 1'b0;
end
else
begin
r_adjust <= i_adjust;
r_adjust_falling <= r_adjust & (~i_adjust);
end
end
//
always @(posedge i_0_1s_clk, negedge i_reset_n)
begin
if(1'b0 == i_reset_n)
r_second_l <= 4'd0;
else
if((2'b01 == r_adjust_cnt) && (1'b1 == r_increment_falling))
r_second_l <= 4'd0;
else if(1'b1 == r_1s_clk_falling)
begin
if(4'd9 == r_second_l)
r_second_l <= 4'd0;
else
r_second_l <= r_second_l + 4'd1;
end
end
always @(posedge i_0_1s_clk, negedge i_reset_n)
begin
if(1'b0 == i_reset_n)
r_second_h <= 4'd0;
else
if((2'b01 == r_adjust_cnt) && (1'b1 == r_increment_falling))
r_second_h <= 4'd0;
else if(1'b1 == r_1s_clk_falling)
begin
if(4'd9 == r_second_l)
if(4'd5 == r_second_h)
r_second_h <= 4'd0;
else
r_second_h <= r_second_h + 4'd1;
end
end
always @(posedge i_0_1s_clk, negedge i_reset_n)
begin
if(1'b0 == i_reset_n)
r_minut_l <= 4'd0;
else
if((2'b10 == r_adjust_cnt) && (1'b1 == r_increment_falling))
if(4'd9 == r_minut_l)
r_minut_l <= 4'd0;
else
r_minut_l <= r_minut_l + 4'd1;
else if(1'b1 == r_1s_clk_falling)
begin
if(4'd9 == r_second_l && 4'd5 == r_second_h)
if(4'd9 == r_minut_l)
r_minut_l <= 4'd0;
else
r_minut_l <= r_minut_l + 4'd1;
end
end
always @(posedge i_0_1s_clk, negedge i_reset_n)
begin
if(1'b0 == i_reset_n)
r_minut_h <= 4'd0;
else
if((2'b10 == r_adjust_cnt) && (1'b1 == r_increment_falling) && (4'd9 == r_minut_l))
begin
if(4'd5 == r_minut_h)
r_minut_h <= 4'd0;
else
r_minut_h <= r_minut_h + 4'd1;
end
else if(1'b1 == r_1s_clk_falling)
begin
if(4'd9 == r_second_l && 4'd5 == r_second_h && 4'd9 == r_minut_l)
begin
if(4'd5 == r_minut_h)
r_minut_h <= 4'd0;
else
r_minut_h <= r_minut_h + 4'd1;
end
end
end
always @(posedge i_0_1s_clk, negedge i_reset_n)
begin
if(1'b0 == i_reset_n)
r_hour_l <= 4'd0;
else
if((2'b11 == r_adjust_cnt) && (1'b1 == r_increment_falling))
begin
if(4'd9 == r_hour_l || (4'd2 == r_hour_h && 4'd3 == r_hour_l))
r_hour_l <= 4'd0;
else
r_hour_l <= r_hour_l + 4'd1;
end
else if(1'b1 == r_1s_clk_falling)
begin
if(4'd9 == r_second_l && 4'd5 == r_second_h && 4'd9 == r_minut_l && 4'd5 == r_minut_h)
begin
if(4'd9 == r_hour_l || (4'd2 == r_hour_h && 4'd3 == r_hour_l))
r_hour_l <= 4'd0;
else
r_hour_l <= r_hour_l + 4'd1;
end
end
end
always @(posedge i_0_1s_clk, negedge i_reset_n)
begin
if(1'b0 == i_reset_n)
r_hour_h <= 4'd0;
else
if((2'b11 == r_adjust_cnt) && (1'b1 == r_increment_falling))
begin
if(4'd2 == r_hour_h && 4'd3 == r_hour_l)
r_hour_h <= 4'd0;
else if(4'd9 == r_hour_l)
r_hour_h <= r_hour_h + 4'd1;
end
else if(1'b1 == r_1s_clk_falling)
begin
if(4'd9 == r_second_l && 4'd5 == r_second_h && 4'd9 == r_minut_l && 4'd5 == r_minut_h && 4'd3 == r_hour_l)
begin
if(4'd2 == r_hour_h )
r_hour_h <= 4'd0;
end
else
begin
if(4'd9 == r_second_l && 4'd5 == r_second_h && 4'd9 == r_minut_l && 4'd5 == r_minut_h && 4'd9 == r_hour_l)
r_hour_h <= r_hour_h + 4'd1;
end
end
end
温度部分:
通过网络搜索和查阅,我们知道DS18b20的工作原理以及信号换算公式,DS18b20中的温度传感器可完成对温度的测量,以12位转化为例,用16位符号扩展的二进制补码读数形式提供。以0.0625℃形式表达,这是12位转换后得到的12位数据存储在18b20的两个八比特的ram中二进制中的前面五位 是符号位,如果测得的温度大于零,这五位为零。只要将测得的数值乘以零点零六二五即可得到实际温度 。
在这里我的代码是参考的电子森林里面所提供的源代码。
OLED部分:
通过网络搜索以及视频网站上面关于OLEDssd1306的介绍,再加上直播老师的一些讲解和电子森林里面所提供的参考,我们对OLED有了初步的认识,这对后面OLED动态显示有了一些基础。
我的OLED代码是通过对电子森林所提供的源代码的基础之上进行修改和运行。
蜂鸣器部分:
了解蜂鸣器的原理,我们需要了解它能在不同频率脉冲下产生不同的音调。我们听到的音调一般在350赫兹左右,而我们的开发版的时钟频率是12M赫兹 所以我们不需要再对时钟进行分频。蜂鸣器运用了PWM控制高低电平时间发出不同声音的原理,不同音调对应的PWM占空比,并将每个音频转换成16位数据,当板子传递给上位机报警信号时,上位机将音调数据提供异步通信串口传递给板子。当音频播放时,板子时间与温度信息不再更新,音频播放完毕,时间和温度重新恢复持续更新状态。
摁键消抖和摁键调时种部分:
摁键消抖我直接调用的是电子森林里面直接提供的代码资源,然后摁键调整时钟部分,我将摁键代码加在时钟代码模块里面,可以最后直接连接运行。
关于摁键的源代码:
always @(posedge i_clk, negedge i_reset_n)
begin
if(1'b0 == i_reset_n)
begin
r_key_1buf <= 1'b0;
r_key_2buf <= 1'b0;
end
else
begin
r_key_1buf <= i_key;
r_key_2buf <= r_key_1buf;
end
end
always @(posedge i_clk, negedge i_reset_n)
begin
if(1'b0 == i_reset_n)
begin
r_key <= 1'b0;
end
else
begin
if(1'b1 == r_key_1buf ~^ r_key_2buf)
r_key <= r_key_1buf;
end
end
uart串口部分:
这部分我是借鉴的野火FPGA视频里面所出现的代码进行改编并与小组成员商讨,很感谢野火视频让我们学到很多。
最后呢就是对各个模块进行连接和总结,编写顶层文件,进行综合和调整。
项目总结:
本次这个项目,这次参加这个实验,耗时一个月的时间 ,前两周都是在学习一些基本语法和研究始终模块儿的正常运行问题 ,后两周就是完成了其他模块儿的代码,然后也开始进行总结和整个项目的调整 。
完成项目过程中所遇到的问题:
1、端口没有显示的问题,这个后来安装了驱动。
2、时钟分频计时刚开始没处理好,数字滚动出现。
3、闹钟定时问题,好再后来得到了解决。
4、温度模块最令人头痛,信号的数据转换,一系列公式,最后采用了电子森林所提供的源代码我进行了修改,完成了温度模块。
5、还有一些细节问题,有时候低级的引脚错误也会犯,这样导致浪费了很多的时间。
6、OLED屏幕的显示,在老师没有直播之前,这个OLED屏幕毫无头绪,后来修改了电子森林所提供的源代码,完成了OLED屏幕的显示。
7、uart串口的传输的数据对蜂鸣器的控制连接,这个是比较困难,然后问了指导老师和团队伙伴,问题得到了解决。
资源消耗:
项目感悟:
这次能够参加电子森林的寒假一起练这个项目活动 我觉得很荣幸 也很感谢这个项目让我学到了很多,从什么都没接触过的小萌新,到一步步获取知识,完成项目,收获满满。很感谢这个平台所提供的多种资源,更便于我们去学习和成长。后面也会积极学习,向各位大佬们靠近。