寒假一起练 FPGA
小脚丫FPGA的综合技能训练平台,基于Altera MAX10 FPGA的一个完成定时、测温、报警、控制的项目。完成各项任务。
标签
FPGA
阿萨姆
更新2021-02-24
1550

任务:

1.实现一个可定时时钟的功能,用小脚丫FPGA核心模块的4个按键设置当前的时间,OLED显示数字钟的当前时间,精确到分钟即可,到整点的时候比如8:00,蜂鸣器报警,播放音频信号,最长可持续30秒;

2.实现温度计的功能,小脚丫通过板上的温度传感器实时测量环境温度,并同时间一起显示在OLED的屏幕上;

3.定时时钟整点报警的同时,将温度信息通过UART传递到电脑上,电脑上能够显示当前板子上的温度信息(任何显示形式都可以),要与OLED显示的温度值一致;

4.PC收到报警的温度信号以后,将一段音频文件(自己制作,持续10秒钟左右)通过UART发送给小脚丫FPGA,蜂鸣器播放收到的这段音频文件,OLED屏幕上显示的时间信息和温度信息都停住不再更新;

5.音频文件播放完毕,OLED开始更新时间信息和当前的温度信息

资源报告:FjOQW72XZbgwccnXqA_mATYsGLDs


任务实现:

这里记录一下我对于fpga的一些理解,和做这个项目的一些感受,我也是才刚上手fpga,对于一些细节甚至是基础知识点都有很多理解错误的地方,还请大家见谅。

我对于fpga的期待是直接去编程操作硬件,故名思意嘛,fpga中文名叫可编程逻辑门阵列,那我们编写代码的时候是不是直接在操作一个又一个的逻辑门,搭积木一样地把他们堆砌起来呢,其实不然,至少我初学时用的这个verilog语言不是这样的逻辑,它和c语言很像,但很多地方比起c语言还有低级,在学verilog之前我以为c语言就已经是倒数第二低级的语言了,倒数第一当然是汇编,这里低级不是贬义词,仅仅是形容这种语言的语义密度低,写了很多但是干的事少。不得不承认,我自恃有单片机的基础,做这个项目的态度不够严谨,很多东西没有学的太明白就草草上战场,去完成项目,我把任务各个模块的代码找来,觉得只要简单的把他们拼接到一起,项目就完成了,结果这个拼接的过程异常痛苦,自己都觉得写的极差,很多地方都只是勉强能用,这样做的后果是逻辑门使用率直逼100%,后来才反应过来,原来是自己只是学会了verilog的语法,并没有完全了解fpga的特点,而是去生搬硬套单片机的经验,导致写的代码人不人鬼不鬼的。一如中国革命一定要结合中国国情,照搬照抄外国的成功经验是行不通的。我打算接下来再去学习一下时钟分频、加法器、状态机这类看起来不怎么有实际意义的课时,希望在亲身接触到实际项目之后,在受过虐之后,能对这些内容有一个更深入的理解,写出来的代码能更加具备可读性和简洁性。到时候希望重写这个项目。

那么也说一下fpga这个平台的特点吧

1.和硬件联系紧密,虽比我想象中松了一点,不是直接操作,但无时无刻不在体现着硬件的意志。基础的话一共就只有wire和reg两种数据类型,其中wire类型简直就是操纵硬件这个木偶的丝线,是连接逻辑部分和逻辑部分、逻辑部分和硬件部分的桥梁,reg类型是寄存器,可以存数据,而wire是不能存储数据的。

2.延时都是自己算出来的,没有专门的延时函数,甚至没有任何函数,连次方运算都没有。

3.资源有限,很容易就把资源用完了。

4.多线程,习惯了单线程突然变成多线程会很不习惯。

原创代码介绍:

时钟模块:

module clock
(
input  [2:0] BTN,  //button key
input clk,  //clk_in = 12mhz
input rst_n,
output reg [3:0] num1,
output reg [3:0] num2,
output reg [3:0] num3,
output reg [3:0] num4
);

reg [24:0] cnt1;
reg [24:0] cnt2;

always@(posedge clk or negedge rst_n)
	begin
		if(!rst_n) 
			begin
				cnt1<=0;
				cnt2<=0;
				num1<=0;
				num2<=0;
				num3<=0;
				num4<=0;
			end 
		else if(cnt1 >= 12000000-1)
			begin
				cnt1 <= 0;
				cnt2 <= cnt2 + 1;
				if(cnt2 >= 60-1)
					begin
						cnt2 <= 0;
						num1 = num1 + 1;
					end
			end
		else if(BTN[1])
			begin
				num1 <= num1 + 1;
			end
		else if (BTN[2])
			begin
				num3 = num3 + 1;
			end
		else if(num1 >= 10)
			begin
				num1 = 0;
				num2 = num2 + 1;
			end
		else if(num2 >= 6)
			begin
				num2 = 0;
				num3 = num3 + 1;
			end
		else if(num3 >= 10)
			begin
				num3 = 0;
				num4 = num4 + 1;
			end
		else if(num4 == 2 && num3 == 4)
			begin
				num3 = 0;
				num4 = 0;
			end
		else
			begin
				cnt1 <= cnt1 + 1;
			end
	end
	
		
endmodule 

		

用于产生0~1440之间的数字,对于一天的1440分钟,将这个数传递给oled模块解析成电子时钟的四个数字,同时还要传递给串口和蜂鸣器这两个模块,执行播放音乐等操作,通过两个按钮调整时间,一个按一下小时数加一,另一个则时分钟数加一。

蜂鸣器音乐播放模块:

// --------------------------------------------------------------------
// >>>>>>>>>>>>>>>>>>>>>>>>> COPYRIGHT NOTICE <<<<<<<<<<<<<<<<<<<<<<<<<
// --------------------------------------------------------------------
// Module: tone
// 
// Author: Step
// 
// Description: tone
// 
// Web: www.stepfapga.com
// 
// --------------------------------------------------------------------
// Code Revision History :
// --------------------------------------------------------------------
// Version: |Mod. Date:   |Changes Made:
// V1.0     |2016/04/20   |Initial ver
// --------------------------------------------------------------------
module tone
(
input					clk,
input					rst_n,
input             [119:0]	temp,
input             		   play1,
input             		   play2,
output	reg		[15:0]	cycle,
output   reg      stop
);

reg [3:0] clock[30];
reg [3:0] music[30];
reg [4:0] cnt1;
reg [25:0] cnt2;
reg [1:0] state;
integer i;

always@(posedge clk or negedge rst_n)
	begin
		clock[0]=1;clock[1]=2;clock[2]=3;clock[3]=4;clock[4]=5;
		clock[5]=6;clock[6]=7;clock[7]=8;clock[8]=9;clock[9]=10;
		clock[10]=11;clock[11]=12;clock[12]=13;clock[13]=14;clock[14]=15;
		clock[15]=15;clock[16]=14;clock[17]=13;clock[18]=12;clock[19]=11;
		clock[20]=10;clock[21]=9;clock[22]=8;clock[23]=7;clock[24]=6;
		clock[25]=5;clock[26]=4;clock[27]=3;clock[28]=2;clock[29]=1;
	end

always@(posedge clk or negedge rst_n)
	begin
		if(!rst_n)
			begin
				cnt1<=0;
				state[0]<=1;
			end
		else if(play1==1)
			begin
				state[0]<=1;
				for(i=0;i<30;i=i+1)
					music[i]<=clock[i];
			end
		else if(play2==1)
			begin
				state[1]<=1;
				stop <= 1;
				for(i=0;i<30;i=i+1)
					music[i] <= {temp[4*i+3],temp[4*i+2],temp[4*i+1],temp[4*i]};
			end
		else if(state[0]==0 && state[1]==0)
			begin
				for(i=0;i<30;i=i+1)
					music[i] <= 0;
			end
		else if(cnt1>=30) 
			begin
				state[0] <= 0;
				state[1] <= 0;
				cnt1 <= 0;
				stop <= 0;
			end
		else if(state[0]==1 || state[1]==1)
			begin
				cnt2 <= cnt2 + 1'b1;
				if(cnt2 >= 3000000)
					begin
						cnt2 <= 0;
						cnt1 <= cnt1 + 1;
					end
			end
	end

always@(cnt1)
	begin
		case(music[cnt1])
			4'd0: cycle = 16'd0;	//L1,
			4'd1: cycle = 16'd40858;	//L2,
			4'd2: cycle = 16'd36408;	//L3,
			4'd3: cycle = 16'd34364;	//L4,
			4'd4: cycle = 16'd30612;	//L5,
			4'd5: cycle = 16'd27273;	//L6,
			4'd6: cycle = 16'd24296;	//L7,
			4'd7: cycle = 16'd22931;	//M1,
			4'd8: cycle = 16'd20432;	//M2,
			4'd9: cycle = 16'd18201;	//M3,
			4'd10: cycle = 16'd17180;	//M4,
			4'd11: cycle = 16'd15306;	//M5,
			4'd12: cycle = 16'd13636;	//M6,
			4'd13: cycle = 16'd12148;	//M7,
			4'd14: cycle = 16'd11478;	//H1,
			4'd15: cycle = 16'd10215;	//H2,
			default:  cycle = 16'd0;		//cycle为0,PWM占空比为0,低电平
		endcase
	end
	
endmodule

clock为自带的音乐,在接收到整点时间信号时,播放clock中的音乐,具体实现是将clock数组的内容赋给music数组,播放完毕后清空music数组,如果接收到串口信号,就将temp中的内容赋给music,也进行一次播放。

串口模块:

// --------------------------------------------------------------------
// >>>>>>>>>>>>>>>>>>>>>>>>> COPYRIGHT NOTICE <<<<<<<<<<<<<<<<<<<<<<<<<
// --------------------------------------------------------------------
// Module: Display_Ctl
// 
// Author: Step
// 
// Description: Real time display with segment led_out
// 
// Web: www.stepfapga.com
// 
// --------------------------------------------------------------------
// Code Revision History :
// --------------------------------------------------------------------
// Version: |Mod. Date:   |Changes Made:
// V1.0     |2016/04/20   |Initial ver
// --------------------------------------------------------------------
module uart_seg 
(
input			 		 clk,		//系统时钟 12MHz
input			 		 rst_n,		//系统复位,低有效
input      [2:0]   send_pulse,
input      [15:0]	 send,
input					 fpga_rx,	//UART接收输入
output reg [119:0] receive,
output				 fpga_tx,	//UART发送输出
output reg         alarm
);

reg [25:0] temp1,temp2;
reg [25:0] send1;
reg [23:0] cnt1,cnt2;
reg flag;
reg [1:0] state;
reg tx_data_valid;

reg [1:0] num1;
reg [6:0] num2;
reg flag1;
wire rx_data_valid;
wire [7:0]	rx_data_out;

always @(send_pulse)
	begin
		temp1 <=  send[4]+ send[5]*2+ send[6]*4+
					 send[7]*8+ send[8]*16+ send[9]*32+
					 send[10]*64;
		temp2 <= ( send[3]*8+ send[2]*4
					+ send[1]*2+ send[0])*10/16;
	end

always @(posedge clk or negedge rst_n)
	begin
		if(!rst_n)
			tx_data_valid <= 1'b0;
		else if(send_pulse[0]==1)
			state <= 1;
		else if(state>=1 && flag==1)
			begin
				tx_data_valid <= 1'b1;
				if(state==1)
					begin
						send1 <= temp1;
						state <= 2;
						flag <= 0;
					end
				else
					begin
						send1 <= temp2;
						state <= 0;
						flag <= 0;
					end
			end
		else if(state==0)
			tx_data_valid <= 1'b0;
		else if(cnt1>=6000000)
			begin
				flag <= 1;
				cnt1 <= 0;
			end
		else
			begin
				cnt1 <= cnt1 + 1;
				tx_data_valid <= 1'b0;
			end
	end

always@(posedge clk or negedge rst_n)
	begin
		if(!rst_n)
			begin
				num1<=0;
				num2<=0;
			end
		else if(rx_data_valid)
		begin
			flag1 = 1;
			cnt2 = 0;
			receive[num2] = rx_data_out[num1];num1 = num1 + 1;num2 = num2 + 1;
			receive[num2] = rx_data_out[num1];num1 = num1 + 1;num2 = num2 + 1;
			receive[num2] = rx_data_out[num1];num1 = num1 + 1;num2 = num2 + 1;
			receive[num2] = rx_data_out[num1];num1 = num1 + 1;num2 = num2 + 1;
		end
		else
			begin
				if(flag1 == 1)
					cnt2 <= cnt2 + 1;
				if(alarm==0 && cnt2 >=1000000 && cnt2 <= 1000010)
					alarm = 1;
				if(alarm==1 && cnt2 >=1000010)
					begin
						alarm = 0;
						flag1 = 0;
						receive <= 120'b0;
						num1 <= 0;
						num2 <= 0;
					end
			end
	end
	
//Uart_Bus module
Uart_Bus u1
(	
.clk					(clk			),	//系统时钟 12MHz
.rst_n					(rst_n			),	//系统复位,低有效
//负责FPGA接收UART芯片的数据
.uart_rx				(fpga_rx		),	//UART接收输入
.rx_data_valid			(rx_data_valid	),	//接收数据有效脉冲
.rx_data_out			(rx_data_out	),	//接收到的数据
//负责FPGA发送数据给UART芯片
.tx_data_valid			(tx_data_valid	),
.tx_data_in				(send1		),
.uart_tx				(fpga_tx		)
);

endmodule

通过发送和接收信号,发送时发送两个数字,分别为温度的整数位和小数位,间隔半秒,接收时,因为每收到一次数据会有一个脉冲信号,如果一段时间接收不到这个脉冲,就说明接收完成,将每次收到的8位信号合成为120位的music信号,同时发送一个脉冲信号告诉蜂鸣器模块报警了。

软硬件
元器件
MAX10M02/08
Intel的低成本FPGA,内置配置FPGA用的Flash和用户Flash,3.3V供电,MAX10M08可以支持NIOS或其它软核
附件下载
mission.rar
项目源码及可直接烧写的文件
团队介绍
哈尔滨工业大学
团队成员
艾晟民
学生
评论
0 / 100
查看更多
目录
硬禾服务号
关注最新动态
0512-67862536
info@eetree.cn
江苏省苏州市苏州工业园区新平街388号腾飞创新园A2幢815室
苏州硬禾信息科技有限公司
Copyright © 2024 苏州硬禾信息科技有限公司 All Rights Reserved 苏ICP备19040198号