2021寒假在家练项目4——刘建伟
使用little feet开发板实现2021寒假在家一起练项目四。
标签
嵌入式系统
FPGA
数字逻辑
大伟和他的小伙伴们
更新2021-03-03
1171

内容介绍

  • 项目需求

1.可定时时钟的功能:采用小脚丫FPGA核心模块的4个按键设置当前的时间,OLED显示数字钟的当前时间,精确到分钟。

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

3.OLED功能:同时显示时间和温度。

4.定时时钟整点报警功能:时间到达整点时,将温度信息通过UART传递到电脑上,电脑上能够显示当前板子上的温度信息。

5.电脑下发音频文件功能:电脑收到报警的温度信号以后,将一段音频文件(“两只老虎”)通过UART发送给小脚丫FPGA,蜂鸣器播放收到的这段音频文件。

6.音频播放期间信息暂停更新功能:播放音乐时,OLED屏幕上显示的时间信息和温度信息都停住不再更新,音频文件播放完毕,OLED开始更新时间信息和当前的温度信息

  • 开发环境

开发环境使用Lattice Diamond软件

1.下载地址

http://www.latticesemi.com/Products/DesignSoftwareAndIP/FPGAandLDS/LatticeDiamond

Fhp1PAD9E58GcXyXq9KlXZvUi0sH

2.License安装

Lattice Diamond软件安装后,需要从官网申请免费的License,才能正常运行,license申请渠道有很多种。

2.1免费license申请

登录Lattice官网,注册Lattice账号,然后进入Diamond License下载页面 。然后填写自己的mac地址以及选取license所支持的功能和IP,最后点击Generate License,然后就可以在自己邮箱接收到申请的License了。

2.2 License安装

安装Diamond License,就是把申请的License.dat文件放到指定的文件夹。

  • 硬件介绍

1、LED:Little feet板卡上有两种灯,2颗RGB LED灯和8颗普通LED灯。上电后除了期中一颗RGB LED 灯蓝色灯闪烁提示系统正在运行,其余灯引脚输出高电平,熄灭LED。

FlGlgS3TTQ9W5Udo--8QBexnrOd1FtWv3RIU1iJeSBxfP2KJbXsEoIhR

 

 

2、按键:按键无硬件滤波,默认接高电平。

FrpabQnvfkzJbHGp1glAwRgVa3VJ

3、温度传感器:采用DS18B20,引脚接1K上拉电阻。

FkCWrkf20WIzK8xhJIK2ailT3NvD

4、蜂鸣器:采用无源蜂鸣器,S8050三极管驱动。

Fld_1nU-yO_1rTBTfeaIOOD3eiZj

5、OLED:采用0.91寸OLED,像素为128*32,白色。

FrCzfGDQ3KT4lT8VcPOiOI3O6Mnr

Fh6xM7lh3YyBfsKMC0LQ5CtqB85D

 

6、TTL电平转USB芯片:采用CH340C,最高2MHZ波特率。

FvSolWcOCRusIxLa4MiyLMOWhXSw

 

  • 系统设计

根据项目要求,采用LCMXO2-4000HC-4MG132,完成任务需要用到OLED,DS18B20,蜂鸣器,UART串口以及按键功能,首先完成并测试各模块代码,再通过顶层模块连接各个外设协同工作,实现项目要求,具体框架如下:

FhJr4nOF6elyWWNKYDQ57UpoJXN4

编译后生成的Netlist如图所示:

FhqF88vbC3HQKJcdQ_tnm34ud9WM

 

未使用ip核,占用资源如下:

Foam9QlmS7KWwvNirKoW2ieOxJzs

上位机截图:

FuVGUbYP4PXC2O_OrMXb6Khx-qRk

我采用一个文件中放一个module形式,参考了硬禾课堂、电子森林和杨彧同学的代码,文件目录如下:

FpWZDNACHpuLo8iTQsc2NTb8r2La

1、顶层文件

顶层文件为lf_project,列出了所有输入输出引脚,将各个模块连接到一起。

module lf_project
(
	input				clk,		// 12MHz系统时钟
	input				rst_n,		// //系统复位,低有效
	inout				one_wire,	// ds18b20z输入信号
	input 				hour,       //调整小时按键
	input			    min_a,		//调整分钟加按键
	input				min_d,      //调整分钟减按键
	input				uart_in,    //串口rx
	output				oled_csn,	//OLCD液晶屏使能
	output				oled_rst,	//OLCD液晶屏复位
	output			    oled_dcn,	//OLCD数据指令控制
	output				oled_clk,	//OLCD时钟信号
	output			    oled_dat,	//OLCD数据信号
	output  	reg		R1,         //板子上所有灯  初始状态熄灭
	output   	reg		G1,
	output   			B1,
	output   	reg		R2,
	output   	reg		G2,
	output   	reg		B2,
	output reg[7:0]     L,
	output				beeper,    //蜂鸣器
	output 				uart_out   //串口输出引脚
);

always@( negedge rst_n) begin
	if(!rst_n) begin
		 R1<=1'b1;	  //板载不使用灯关闭
		 G1<=1'b1; 
		 R2<=1'b1; 
		 G2<=1'b1; 
		 B2<=1'b1;	
		 L[7:0]<=8'hff;	
	end
end

//DS18B20获取温度*************************************************************
wire	[15:0]	data_out;
DS18B20Z DS18B20Z_lf
(
.clk					(clk			),	// 12MHz系统时钟
.rst_n					(rst_n			),	// 系统复位,低有效
.one_wire				(one_wire		),	// ds18b20输入引脚
.data_out				(data_out		)	// ds18b20数据输出
);
// 判断温度符号
wire [3:0] temperature_flag = data_out[15:11]? 4'h0:4'h1;
// 获取温度
wire [10:0] temperature_code = temperature_flag? data_out[10:0]:(~data_out[10:0])+1'b1; 
// 转换温度到BCD码****************************************************************
wire [20:0] bin_code = temperature_code * 16'd625;
wire [24:0] bcd_code; //十位[23:20],个位[19:16],小数位[14:12]
bin_to_bcd bin_to_bcd_lf
(
.rst_n					(rst_n			),	// 系统复位,低有效
.bin_code				(bin_code		),	// bin输入
.bcd_code				(bcd_code		)	// bcd输出
);
//转化小时到BCD码******************************************************************
wire [7:0] hour_out,min_out;//小时分钟输出信号
wire [20:0] bin_hour =hour_out*10000;
wire [24:0] bcd_hour;
bin_to_bcd bin_to_bcd_hour
(
.rst_n					(rst_n			),	// 系统复位,低有效
.bin_code				(bin_hour		),	// bin输入
.bcd_code				(bcd_hour		)	// bcd输出
);
//转化分钟到BCD码******************************************************************
wire [20:0] bin_min =min_out*10000;
wire [24:0] bcd_min;
bin_to_bcd bin_to_bcd_min
(
.rst_n					(rst_n			),	// 系统复位,低有效
.bin_code				(bin_min		),	// bin输入
.bcd_code				(bcd_min		)	// bcd输出
);
//oled********************************************************************************
wire display_flag;
OLED12832   OLED12832_lf
(
.clk			(clk						),//12MHz系统时钟
.rst_n			(rst_n						),//系统复位,低有效
.display_flag   ( display_flag 				),//显示开关
.hour_g	   	 	(bcd_hour[19:16]			),//小时个位
.hour_s			(bcd_hour[23:20]			),//小时十位
.min_g			(bcd_min[19:16]				),//分钟个位
.min_s			(bcd_min[23:20]				),//分钟十位
.tem_g			((bcd_code[19:16])			),//温度个位
.tem_s      	((bcd_code[23:20])			),//温度十位
.tem_sign   	(temperature_flag			),//温度符号
.tem_d			((bcd_code[15:12])			),//温度小数位				
.oled_csn		(oled_csn					),//OLCD液晶屏使能
.oled_rst		(oled_rst					),//OLCD液晶屏复位
.oled_dcn		(oled_dcn					),//OLCD数据指令控制
.oled_clk		(oled_clk					),//OLCD时钟信号
.oled_dat		(oled_dat					)//OLCD数据信号
);
//蜂鸣器********************************************************************************
wire  [311:0]data;
wire pwm_en;
buzzer  buzzer_lf
(
.tri_uart 	(pwm_en			),//蜂鸣器发声标志
.clk       	(clk			),//12MHz系统时钟
.clk_buzzer (B1				),//B1LED闪烁频率
.data_uart 	(data			),//串口接收数据
.stop      	(display_flag	),//播放标志位
.out       	(beeper			),//接蜂鸣器引脚
.rst_n      (rst_n			) //系统复位,低有效

);
//产生9600hz频率,提供给串口发送使用********************************************************
wire clk_o;
clk_uart  clk_uart_lf
(
.rst_n   (rst_n			),//系统复位,低有效
.clk_in  (clk          	),//12MHz系统时钟
.clk_out (clk_o        	)//9600HZ时钟输出
);		
//串口发送**********************************************************************************
wire uart_en;        //串口发送的控制信号
uart_tx  uart_tx_lf 
(
.rst_n      (rst_n				),
.tem_g  	((bcd_code[19:16])	),//温度个位
.tem_s  	((bcd_code[23:20])	),//温度十位
.tem_d  	((bcd_code[15:12])	),//温度小数位
.clk_tx 	(clk_o          	),//发送时钟
.en     	(uart_en			),//串口发送标志
.uart_out  	(uart_out			)//串口发送引脚
);	
//串口接收*********************************************************************************
uart_rx  uart_rx_lf
(
.rst_n  	(rst_n				),//系统复位,低有效
.uart_in  	(uart_in			),//串口接收引脚
.clk  		(clk				),//12MHz系统时钟
.receive   	(data				),//串口接收数据输出
.pwm_en    	(pwm_en				) //蜂鸣器发声标志
);
//产生1HZ信号模块******************************************************************************
wire clk_1ms;
clk_1ms clk_1ms_lf
(
.clk 		(clk				),//12MHz系统时钟
.rst_n  	(rst_n				),//复位信号
.clk_1ms	(clk_1ms			) //1HZ信号输出
);
//时钟模块******************************************************************************
clock clock_lf
(
.hour       (hour				),//调整小时按键  
.min_a      (min_a				),//调整分钟加按键
.min_d      (min_d				),//调整分钟减按键
.clk_1ms    (clk_1ms   			),//1HZ信号输入
.clk_500ms  (B1     			),//B1LED闪烁频率
.hour_out   (hour_out			),//小时输出
.min_out    (min_out			),//分钟输出
.rst_n      (rst_n			    ),//复位信号
.uart_en    (uart_en			)//串口发送标志
);
//信号灯闪烁模块************************************************************************
signal_led  signal_led_lf
(
.clk     (clk					),//12MHz系统时钟
.rst_n   (rst_n					),//复位信号
.led     (B1					)//B1LED闪烁频率
);
endmodule

2、OLED模块

OLED参考了电子森林的代码,采用状态机。

module OLED12832
(
	input				clk,		//12MHz系统时钟
	input				rst_n,		//系统复位,低有效
	input[3:0]    		tem_g,tem_s,tem_sign,tem_d,
	input[3:0]          hour_g,hour_s,min_g,min_s,
						//小时个位,小时十位,分钟个位,分钟十位,
						//温度个位,温度十位,温度符号,温度小数
		
    input				display_flag, //显示标志	
	output	reg			oled_csn,	//OLCD液晶屏使能
	output	reg			oled_rst,	//OLCD液晶屏复位
	output	reg			oled_dcn,	//OLCD数据指令控制
	output	reg			oled_clk,	//OLCD时钟信号
	output	reg			oled_dat	//OLCD数据信号
);
	
	localparam INIT_DEPTH = 16'd25; //LCD初始化的命令的数量
	localparam IDLE = 6'h1, MAIN = 6'h2, INIT = 6'h4, SCAN = 6'h8, WRITE = 6'h10, DELAY = 6'h20;
	localparam HIGH	= 1'b1, LOW = 1'b0;
	localparam DATA	= 1'b1, CMD = 1'b0;
	
	reg [7:0] cmd [24:0];
	reg [39:0] mem [123:0];
	reg	[7:0]	y_p, x_ph, x_pl;
	reg	[(8*21-1):0] char;
	reg	[7:0]	num, char_reg;				
	reg	[4:0]	cnt_main, cnt_init, cnt_scan, cnt_write;
	reg	[15:0]	num_delay, cnt_delay, cnt;
	reg	[5:0] 	state, state_back;
	
	wire[7:0] sign;
	assign sign = tem_sign?" ":"-";
	
	
	reg					clk_1hz;
	reg		[23:0]		cnt_1hz;
	always@(posedge clk or negedge rst_n) begin  //产生1HZ  用来更新时钟冒号的闪烁
		if(!rst_n) begin
			cnt_1hz <= 24'h0;
			clk_1hz <= 1'b1;
		end else if(cnt_1hz >= 24'd 12000000) begin
			cnt_1hz <= 24'h0;
			clk_1hz <= ~clk_1hz;
		end else begin
			cnt_1hz <= cnt_1hz + 1'b1;
		end
	end
	
	always@(posedge clk or negedge rst_n) begin
		if(!rst_n) begin
			cnt_main <= 1'b0; cnt_init <= 1'b0; cnt_scan <= 1'b0; cnt_write <= 1'b0;
			y_p <= 1'b0; x_ph <= 1'b0; x_pl <= 1'b0;
			num <= 1'b0; char <= 1'b0; char_reg <= 1'b0;
			num_delay <= 16'd5; cnt_delay <= 1'b0; cnt <= 1'b0;
			oled_csn <= HIGH; oled_rst <= HIGH; oled_dcn <= CMD; oled_clk <= HIGH; oled_dat <= LOW;
			state    <= IDLE; state_back  <= IDLE;
		end else begin
			case(state)
				IDLE:begin
						cnt_main <= 1'b0; cnt_init <= 1'b0; cnt_scan <= 1'b0; cnt_write <= 1'b0;
						y_p <= 1'b0; x_ph <= 1'b0; x_pl <= 1'b0;
						num <= 1'b0; char <= 1'b0; char_reg <= 1'b0;
						num_delay <= 16'd5; cnt_delay <= 1'b0; cnt <= 1'b0;
						oled_csn <= HIGH; oled_rst <= HIGH; oled_dcn <= CMD; oled_clk <= HIGH; oled_dat <= LOW;
						state <= MAIN; state_back <= MAIN;
					end
				MAIN:begin
						if(cnt_main >= 5'd6) cnt_main <= 5'd5;
						else cnt_main <= cnt_main + 1'b1;
						
			
							case(cnt_main)	//MAIN状态
								5'd0:	begin state <= INIT; end
								5'd1:	begin y_p <= 8'hb0; x_ph <= 8'h10; x_pl <= 8'h00; num <= 5'd16; char <= "TIME:           ";						 state <= SCAN; end
								5'd2:	begin y_p <= 8'hb3; x_ph <= 8'h10; x_pl <= 8'h00; num <= 5'd16; char <= "TEMP:           ";						 state <= SCAN; end	
								5'd3:	begin y_p <= 8'hb1; x_ph <= 8'h10; x_pl <= 8'h00; num <= 5'd16; char <= "                ";						 state <= SCAN; end
								5'd4:	begin y_p <= 8'hb2; x_ph <= 8'h10; x_pl <= 8'h00; num <= 5'd16; char <= "                ";						 state <= SCAN; end	
								5'd5:	begin if(display_flag)begin if(clk_1hz)begin y_p <= 8'hb0; x_ph <= 8'h13; x_pl <= 8'h00; num <= 5'd 5; char <= {4'h0,hour_s,4'h0,hour_g,":",4'h0,min_s,4'h0,min_g}; state <= SCAN;end 
													else begin y_p <= 8'hb0; x_ph <= 8'h13; x_pl <= 8'h00; num <= 5'd 5; char <= {4'h0,hour_s,4'h0,hour_g," ",4'h0,min_s,4'h0,min_g}; state <= SCAN;end
												end   end
								5'd6:	begin if(display_flag)begin y_p <= 8'hb3; x_ph <= 8'h12; x_pl <= 8'h08; num <= 5'd 7; char <= {sign,4'h0,tem_s,4'h0,tem_g,".",4'h0,tem_d,8'h7b,"C"}; 	 state <= SCAN; end end
								default: state <= IDLE;
							endcase
						
					end
				INIT:begin	//初始化状态
						case(cnt_init)
							5'd0:	begin oled_rst <= LOW; cnt_init <= cnt_init + 1'b1; end	//复位有效
							5'd1:	begin num_delay <= 16'd25000; state <= DELAY; state_back <= INIT; cnt_init <= cnt_init + 1'b1; end	//延时大于3us
							5'd2:	begin oled_rst <= HIGH; cnt_init <= cnt_init + 1'b1; end	//复位恢复
							5'd3:	begin num_delay <= 16'd25000; state <= DELAY; state_back <= INIT; cnt_init <= cnt_init + 1'b1; end	//延时大于220us
							5'd4:	begin 
										if(cnt>=INIT_DEPTH) begin	//当25条指令及数据发出后,配置完成
											cnt <= 1'b0;
											cnt_init <= cnt_init + 1'b1;
										end else begin	
											cnt <= cnt + 1'b1; num_delay <= 16'd5;
											oled_dcn <= CMD; char_reg <= cmd[cnt]; state <= WRITE; state_back <= INIT;
										end
									end
							5'd5:	begin cnt_init <= 1'b0; state <= MAIN; end	//初始化完成,返回MAIN状态
							default: state <= IDLE;
						endcase
					end
				SCAN:begin	//刷屏状态,从RAM中读取数据刷屏
						if(cnt_scan == 5'd11) begin
							if(num) cnt_scan <= 5'd3;
							else cnt_scan <= cnt_scan + 1'b1;
						end else if(cnt_scan == 5'd12) cnt_scan <= 1'b0;
						else cnt_scan <= cnt_scan + 1'b1;
						case(cnt_scan)
							5'd 0:	begin oled_dcn <= CMD; char_reg <= y_p; state <= WRITE; state_back <= SCAN; end		//定位列页地址
							5'd 1:	begin oled_dcn <= CMD; char_reg <= x_pl; state <= WRITE; state_back <= SCAN; end	//定位行地址低位
							5'd 2:	begin oled_dcn <= CMD; char_reg <= x_ph; state <= WRITE; state_back <= SCAN; end	//定位行地址高位
							
							5'd 3:	begin num <= num - 1'b1;end
							5'd 4:	begin oled_dcn <= DATA; char_reg <= 8'h00; state <= WRITE; state_back <= SCAN; end	//将5*8点阵编程8*8
							5'd 5:	begin oled_dcn <= DATA; char_reg <= 8'h00; state <= WRITE; state_back <= SCAN; end	//将5*8点阵编程8*8
							5'd 6:	begin oled_dcn <= DATA; char_reg <= 8'h00; state <= WRITE; state_back <= SCAN; end	//将5*8点阵编程8*8
							5'd 7:	begin oled_dcn <= DATA; char_reg <= mem[char[(num*8)+:8]][39:32]; state <= WRITE; state_back <= SCAN; end
							5'd 8:	begin oled_dcn <= DATA; char_reg <= mem[char[(num*8)+:8]][31:24]; state <= WRITE; state_back <= SCAN; end
							5'd 9:	begin oled_dcn <= DATA; char_reg <= mem[char[(num*8)+:8]][23:16]; state <= WRITE; state_back <= SCAN; end
							5'd10:	begin oled_dcn <= DATA; char_reg <= mem[char[(num*8)+:8]][15: 8]; state <= WRITE; state_back <= SCAN; end
							5'd11:	begin oled_dcn <= DATA; char_reg <= mem[char[(num*8)+:8]][ 7: 0]; state <= WRITE; state_back <= SCAN; end
							5'd12:	begin state <= MAIN; end
							default: state <= IDLE;
						endcase
					end
				WRITE:begin	//WRITE状态,将数据按照SPI时序发送给屏幕
						if(cnt_write >= 5'd17) cnt_write <= 1'b0;
						else cnt_write <= cnt_write + 1'b1;
						case(cnt_write)
							5'd 0:	begin oled_csn <= LOW; end	//9位数据最高位为命令数据控制位
							5'd 1:	begin oled_clk <= LOW; oled_dat <= char_reg[7]; end	//先发高位数据
							5'd 2:	begin oled_clk <= HIGH; end
							5'd 3:	begin oled_clk <= LOW; oled_dat <= char_reg[6]; end
							5'd 4:	begin oled_clk <= HIGH; end
							5'd 5:	begin oled_clk <= LOW; oled_dat <= char_reg[5]; end
							5'd 6:	begin oled_clk <= HIGH; end
							5'd 7:	begin oled_clk <= LOW; oled_dat <= char_reg[4]; end
							5'd 8:	begin oled_clk <= HIGH; end
							5'd 9:	begin oled_clk <= LOW; oled_dat <= char_reg[3]; end
							5'd10:	begin oled_clk <= HIGH; end
							5'd11:	begin oled_clk <= LOW; oled_dat <= char_reg[2]; end
							5'd12:	begin oled_clk <= HIGH; end
							5'd13:	begin oled_clk <= LOW; oled_dat <= char_reg[1]; end
							5'd14:	begin oled_clk <= HIGH; end
							5'd15:	begin oled_clk <= LOW; oled_dat <= char_reg[0]; end	//后发低位数据
							5'd16:	begin oled_clk <= HIGH; end
							5'd17:	begin oled_csn <= HIGH; state <= DELAY; end	//
							default: state <= IDLE;
						endcase
					end
				DELAY:begin	//延时状态
						if(cnt_delay >= num_delay) begin
							cnt_delay <= 16'd0; state <= state_back; 
						end else cnt_delay <= cnt_delay + 1'b1;
					end
				default:state <= IDLE;
			endcase
		end
	end
	
	//OLED配置指令数据
	always@(posedge rst_n)
		begin
			cmd[ 0] = {8'hae}; 
			cmd[ 1] = {8'h00}; 
			cmd[ 2] = {8'h10}; 
			cmd[ 3] = {8'h00}; 
			cmd[ 4] = {8'hb0}; 
			cmd[ 5] = {8'h81}; 
			cmd[ 6] = {8'hff}; 
			cmd[ 7] = {8'ha1}; 
			cmd[ 8] = {8'ha6}; 
			cmd[ 9] = {8'ha8}; 
			cmd[10] = {8'h1f}; 
			cmd[11] = {8'hc8};
			cmd[12] = {8'hd3};
			cmd[13] = {8'h00};
			cmd[14] = {8'hd5};
			cmd[15] = {8'h80};
			cmd[16] = {8'hd9};
			cmd[17] = {8'h1f};
			cmd[18] = {8'hda};
			cmd[19] = {8'h00};
			cmd[20] = {8'hdb};
			cmd[21] = {8'h40};
			cmd[22] = {8'h8d};
			cmd[23] = {8'h14};
			cmd[24] = {8'haf};
		end 
		
	//5*8点阵字库数据
	always@(posedge rst_n)
		begin
			mem[  0] = {8'h3E, 8'h51, 8'h49, 8'h45, 8'h3E};   // 48  0
			mem[  1] = {8'h00, 8'h42, 8'h7F, 8'h40, 8'h00};   // 49  1
			mem[  2] = {8'h42, 8'h61, 8'h51, 8'h49, 8'h46};   // 50  2
			mem[  3] = {8'h21, 8'h41, 8'h45, 8'h4B, 8'h31};   // 51  3
			mem[  4] = {8'h18, 8'h14, 8'h12, 8'h7F, 8'h10};   // 52  4
			mem[  5] = {8'h27, 8'h45, 8'h45, 8'h45, 8'h39};   // 53  5
			mem[  6] = {8'h3C, 8'h4A, 8'h49, 8'h49, 8'h30};   // 54  6
			mem[  7] = {8'h01, 8'h71, 8'h09, 8'h05, 8'h03};   // 55  7
			mem[  8] = {8'h36, 8'h49, 8'h49, 8'h49, 8'h36};   // 56  8
			mem[  9] = {8'h06, 8'h49, 8'h49, 8'h29, 8'h1E};   // 57  9
			mem[ 10] = {8'h7C, 8'h12, 8'h11, 8'h12, 8'h7C};   // 65  A
			mem[ 11] = {8'h7F, 8'h49, 8'h49, 8'h49, 8'h36};   // 66  B
			mem[ 12] = {8'h3E, 8'h41, 8'h41, 8'h41, 8'h22};   // 67  C
			mem[ 13] = {8'h7F, 8'h41, 8'h41, 8'h22, 8'h1C};   // 68  D
			mem[ 14] = {8'h7F, 8'h49, 8'h49, 8'h49, 8'h41};   // 69  E
			mem[ 15] = {8'h7F, 8'h09, 8'h09, 8'h09, 8'h01};   // 70  F

			mem[ 32] = {8'h00, 8'h00, 8'h00, 8'h00, 8'h00};   // 32  sp 
			mem[ 33] = {8'h00, 8'h00, 8'h2f, 8'h00, 8'h00};   // 33  !  
			mem[ 34] = {8'h00, 8'h07, 8'h00, 8'h07, 8'h00};   // 34  
			mem[ 35] = {8'h14, 8'h7f, 8'h14, 8'h7f, 8'h14};   // 35  #
			mem[ 36] = {8'h24, 8'h2a, 8'h7f, 8'h2a, 8'h12};   // 36  $
			mem[ 37] = {8'h62, 8'h64, 8'h08, 8'h13, 8'h23};   // 37  %
			mem[ 38] = {8'h36, 8'h49, 8'h55, 8'h22, 8'h50};   // 38  &
			mem[ 39] = {8'h00, 8'h05, 8'h03, 8'h00, 8'h00};   // 39  '
			mem[ 40] = {8'h00, 8'h1c, 8'h22, 8'h41, 8'h00};   // 40  (
			mem[ 41] = {8'h00, 8'h41, 8'h22, 8'h1c, 8'h00};   // 41  )
			mem[ 42] = {8'h14, 8'h08, 8'h3E, 8'h08, 8'h14};   // 42  *
			mem[ 43] = {8'h08, 8'h08, 8'h3E, 8'h08, 8'h08};   // 43  +
			mem[ 44] = {8'h00, 8'h00, 8'hA0, 8'h60, 8'h00};   // 44  ,
			mem[ 45] = {8'h08, 8'h08, 8'h08, 8'h08, 8'h08};   // 45  -
			mem[ 46] = {8'h00, 8'h60, 8'h60, 8'h00, 8'h00};   // 46  .
			mem[ 47] = {8'h20, 8'h10, 8'h08, 8'h04, 8'h02};   // 47  /
			mem[ 48] = {8'h3E, 8'h51, 8'h49, 8'h45, 8'h3E};   // 48  0
			mem[ 49] = {8'h00, 8'h42, 8'h7F, 8'h40, 8'h00};   // 49  1
			mem[ 50] = {8'h42, 8'h61, 8'h51, 8'h49, 8'h46};   // 50  2
			mem[ 51] = {8'h21, 8'h41, 8'h45, 8'h4B, 8'h31};   // 51  3
			mem[ 52] = {8'h18, 8'h14, 8'h12, 8'h7F, 8'h10};   // 52  4
			mem[ 53] = {8'h27, 8'h45, 8'h45, 8'h45, 8'h39};   // 53  5
			mem[ 54] = {8'h3C, 8'h4A, 8'h49, 8'h49, 8'h30};   // 54  6
			mem[ 55] = {8'h01, 8'h71, 8'h09, 8'h05, 8'h03};   // 55  7
			mem[ 56] = {8'h36, 8'h49, 8'h49, 8'h49, 8'h36};   // 56  8
			mem[ 57] = {8'h06, 8'h49, 8'h49, 8'h29, 8'h1E};   // 57  9
			mem[ 58] = {8'h00, 8'h36, 8'h36, 8'h00, 8'h00};   // 58  :
			mem[ 59] = {8'h00, 8'h56, 8'h36, 8'h00, 8'h00};   // 59  ;
			mem[ 60] = {8'h08, 8'h14, 8'h22, 8'h41, 8'h00};   // 60  <
			mem[ 61] = {8'h14, 8'h14, 8'h14, 8'h14, 8'h14};   // 61  =
			mem[ 62] = {8'h00, 8'h41, 8'h22, 8'h14, 8'h08};   // 62  >
			mem[ 63] = {8'h02, 8'h01, 8'h51, 8'h09, 8'h06};   // 63  ?
			mem[ 64] = {8'h32, 8'h49, 8'h59, 8'h51, 8'h3E};   // 64  @
			mem[ 65] = {8'h7C, 8'h12, 8'h11, 8'h12, 8'h7C};   // 65  A
			mem[ 66] = {8'h7F, 8'h49, 8'h49, 8'h49, 8'h36};   // 66  B
			mem[ 67] = {8'h3E, 8'h41, 8'h41, 8'h41, 8'h22};   // 67  C
			mem[ 68] = {8'h7F, 8'h41, 8'h41, 8'h22, 8'h1C};   // 68  D
			mem[ 69] = {8'h7F, 8'h49, 8'h49, 8'h49, 8'h41};   // 69  E
			mem[ 70] = {8'h7F, 8'h09, 8'h09, 8'h09, 8'h01};   // 70  F
			mem[ 71] = {8'h3E, 8'h41, 8'h49, 8'h49, 8'h7A};   // 71  G
			mem[ 72] = {8'h7F, 8'h08, 8'h08, 8'h08, 8'h7F};   // 72  H
			mem[ 73] = {8'h00, 8'h41, 8'h7F, 8'h41, 8'h00};   // 73  I
			mem[ 74] = {8'h20, 8'h40, 8'h41, 8'h3F, 8'h01};   // 74  J
			mem[ 75] = {8'h7F, 8'h08, 8'h14, 8'h22, 8'h41};   // 75  K
			mem[ 76] = {8'h7F, 8'h40, 8'h40, 8'h40, 8'h40};   // 76  L
			mem[ 77] = {8'h7F, 8'h02, 8'h0C, 8'h02, 8'h7F};   // 77  M
			mem[ 78] = {8'h7F, 8'h04, 8'h08, 8'h10, 8'h7F};   // 78  N
			mem[ 79] = {8'h3E, 8'h41, 8'h41, 8'h41, 8'h3E};   // 79  O
			mem[ 80] = {8'h7F, 8'h09, 8'h09, 8'h09, 8'h06};   // 80  P
			mem[ 81] = {8'h3E, 8'h41, 8'h51, 8'h21, 8'h5E};   // 81  Q
			mem[ 82] = {8'h7F, 8'h09, 8'h19, 8'h29, 8'h46};   // 82  R
			mem[ 83] = {8'h46, 8'h49, 8'h49, 8'h49, 8'h31};   // 83  S
			mem[ 84] = {8'h01, 8'h01, 8'h7F, 8'h01, 8'h01};   // 84  T
			mem[ 85] = {8'h3F, 8'h40, 8'h40, 8'h40, 8'h3F};   // 85  U
			mem[ 86] = {8'h1F, 8'h20, 8'h40, 8'h20, 8'h1F};   // 86  V
			mem[ 87] = {8'h3F, 8'h40, 8'h38, 8'h40, 8'h3F};   // 87  W
			mem[ 88] = {8'h63, 8'h14, 8'h08, 8'h14, 8'h63};   // 88  X
			mem[ 89] = {8'h07, 8'h08, 8'h70, 8'h08, 8'h07};   // 89  Y
			mem[ 90] = {8'h61, 8'h51, 8'h49, 8'h45, 8'h43};   // 90  Z
			mem[ 91] = {8'h00, 8'h7F, 8'h41, 8'h41, 8'h00};   // 91  [
			mem[ 92] = {8'h55, 8'h2A, 8'h55, 8'h2A, 8'h55};   // 92  
			mem[ 93] = {8'h00, 8'h41, 8'h41, 8'h7F, 8'h00};   // 93  ]
			mem[ 94] = {8'h04, 8'h02, 8'h01, 8'h02, 8'h04};   // 94  ^
			mem[ 95] = {8'h40, 8'h40, 8'h40, 8'h40, 8'h40};   // 95  _
			mem[ 96] = {8'h00, 8'h01, 8'h02, 8'h04, 8'h00};   // 96  '
			mem[ 97] = {8'h20, 8'h54, 8'h54, 8'h54, 8'h78};   // 97  a
			mem[ 98] = {8'h7F, 8'h48, 8'h44, 8'h44, 8'h38};   // 98  b
			mem[ 99] = {8'h38, 8'h44, 8'h44, 8'h44, 8'h20};   // 99  c
			mem[100] = {8'h38, 8'h44, 8'h44, 8'h48, 8'h7F};   // 100 d
			mem[101] = {8'h38, 8'h54, 8'h54, 8'h54, 8'h18};   // 101 e
			mem[102] = {8'h08, 8'h7E, 8'h09, 8'h01, 8'h02};   // 102 f
			mem[103] = {8'h18, 8'hA4, 8'hA4, 8'hA4, 8'h7C};   // 103 g
			mem[104] = {8'h7F, 8'h08, 8'h04, 8'h04, 8'h78};   // 104 h
			mem[105] = {8'h00, 8'h44, 8'h7D, 8'h40, 8'h00};   // 105 i
			mem[106] = {8'h40, 8'h80, 8'h84, 8'h7D, 8'h00};   // 106 j
			mem[107] = {8'h7F, 8'h10, 8'h28, 8'h44, 8'h00};   // 107 k
			mem[108] = {8'h00, 8'h41, 8'h7F, 8'h40, 8'h00};   // 108 l
			mem[109] = {8'h7C, 8'h04, 8'h18, 8'h04, 8'h78};   // 109 m
			mem[110] = {8'h7C, 8'h08, 8'h04, 8'h04, 8'h78};   // 110 n
			mem[111] = {8'h38, 8'h44, 8'h44, 8'h44, 8'h38};   // 111 o
			mem[112] = {8'hFC, 8'h24, 8'h24, 8'h24, 8'h18};   // 112 p
			mem[113] = {8'h18, 8'h24, 8'h24, 8'h18, 8'hFC};   // 113 q
			mem[114] = {8'h7C, 8'h08, 8'h04, 8'h04, 8'h08};   // 114 r
			mem[115] = {8'h48, 8'h54, 8'h54, 8'h54, 8'h20};   // 115 s
			mem[116] = {8'h04, 8'h3F, 8'h44, 8'h40, 8'h20};   // 116 t
			mem[117] = {8'h3C, 8'h40, 8'h40, 8'h20, 8'h7C};   // 117 u
			mem[118] = {8'h1C, 8'h20, 8'h40, 8'h20, 8'h1C};   // 118 v
			mem[119] = {8'h3C, 8'h40, 8'h30, 8'h40, 8'h3C};   // 119 w
			mem[120] = {8'h44, 8'h28, 8'h10, 8'h28, 8'h44};   // 120 x
			mem[121] = {8'h1C, 8'hA0, 8'hA0, 8'hA0, 8'h7C};   // 121 y
			mem[122] = {8'h44, 8'h64, 8'h54, 8'h4C, 8'h44};   // 122 z
			mem[123] = {8'h00, 8'h00, 8'h00, 8'h03, 8'h03};   // 123 摄氏度小圈
		end
	
endmodule

3、温度传感器

DS18B20代码使用了电子森林的。

module DS18B20Z
(
	input				clk,			// system clock
	input				rst_n,			// system reset, active low
	inout				one_wire,		// ds18b20z one-wire-bus
	output	reg	[15:0]	data_out		// ds18b20z data_out
);
	
	localparam	IDLE	=	3'd0;
	localparam	MAIN	=	3'd1;
	localparam	INIT	=	3'd2;
	localparam	WRITE	=	3'd3;
	localparam	READ	=	3'd4;
	localparam	DELAY	=	3'd5;
	
	//generate clk_1mhz clock
	reg					clk_1mhz;
	reg		[2:0]		cnt_1mhz;
	always@(posedge clk or negedge rst_n) begin
		if(!rst_n) begin
			cnt_1mhz <= 3'd0;
			clk_1mhz <= 1'b0;
		end else if(cnt_1mhz >= 3'd6) begin
			cnt_1mhz <= 3'd0;
			clk_1mhz <= ~clk_1mhz;
		end else begin
			cnt_1mhz <= cnt_1mhz + 1'b1;
		end
	end
	
	reg					one_wire_buffer;
	reg		[3:0]		cnt_main;
	reg		[7:0]		data_wr;
	reg		[7:0]		data_wr_buffer;
	reg		[2:0]		cnt_init;
	reg		[19:0]		cnt_delay;
	reg		[19:0]		num_delay;
	reg		[5:0]		cnt_write;
	reg		[5:0]		cnt_read;
	reg		[15:0]		temperature;
	reg		[7:0]		temperature_buffer;
	reg		[2:0] 		state = IDLE;
	reg		[2:0] 		state_back = IDLE;
	always@(posedge clk_1mhz or negedge rst_n) begin
		if(!rst_n) begin
			state <= IDLE;
			state_back <= IDLE;
			cnt_main <= 4'd0;
			cnt_init <= 3'd0;
			cnt_write <= 6'd0;
			cnt_read <= 6'd0;
			cnt_delay <= 20'd0;
			one_wire_buffer <= 1'bz;
			temperature <= 16'h0;
		end else begin
			case(state)
				IDLE:begin
						state <= MAIN;
						state_back <= MAIN;
						cnt_main <= 4'd0;
						cnt_init <= 3'd0;
						cnt_write <= 6'd0;
						cnt_read <= 6'd0;
						cnt_delay <= 20'd0;
						one_wire_buffer <= 1'bz;
					end
				MAIN:begin
						if(cnt_main >= 4'd11) cnt_main <= 1'b0;
						else cnt_main <= cnt_main + 1'b1;
						case(cnt_main)
							4'd0: begin state <= INIT; end
							4'd1: begin data_wr <= 8'hcc;state <= WRITE; end
							4'd2: begin data_wr <= 8'h44;state <= WRITE; end
							4'd3: begin num_delay <= 20'd750000;state <= DELAY;state_back <= MAIN; end
							
							4'd4: begin state <= INIT; end
							4'd5: begin data_wr <= 8'hcc;state <= WRITE; end
							4'd6: begin data_wr <= 8'hbe;state <= WRITE; end
							
							4'd7: begin state <= READ; end
							4'd8: begin temperature[7:0] <= temperature_buffer; end
							
							4'd9: begin state <= READ; end
							4'd10: begin temperature[15:8] <= temperature_buffer; end
							
							4'd11: begin state <= IDLE;data_out <= temperature; end
							default: state <= IDLE;
						endcase
					end
				INIT:begin
						if(cnt_init >= 3'd6) cnt_init <= 1'b0;
						else cnt_init <= cnt_init + 1'b1;
						case(cnt_init)
							3'd0: begin one_wire_buffer <= 1'b0; end
							3'd1: begin num_delay <= 20'd500;state <= DELAY;state_back <= INIT; end
							3'd2: begin one_wire_buffer <= 1'bz; end
							3'd3: begin num_delay <= 20'd100;state <= DELAY;state_back <= INIT; end
							3'd4: begin if(one_wire) state <= IDLE; else state <= INIT; end
							3'd5: begin num_delay <= 20'd400;state <= DELAY;state_back <= INIT; end
							3'd6: begin state <= MAIN; end
							default: state <= IDLE;
						endcase
					end
				WRITE:begin
						if(cnt_write >= 6'd50) cnt_write <= 1'b0;
						else cnt_write <= cnt_write + 1'b1;
						case(cnt_write)
							//lock data_wr
							6'd0: begin data_wr_buffer <= data_wr; end
							//write bit 0
							6'd1: begin one_wire_buffer <= 1'b0; end
							6'd2: begin num_delay <= 20'd2;state <= DELAY;state_back <= WRITE; end
							6'd3: begin one_wire_buffer <= data_wr_buffer[0]; end
							6'd4: begin num_delay <= 20'd80;state <= DELAY;state_back <= WRITE; end
							6'd5: begin one_wire_buffer <= 1'bz; end
							6'd6: begin num_delay <= 20'd2;state <= DELAY;state_back <= WRITE; end
							//write bit 1
							6'd7: begin one_wire_buffer <= 1'b0; end
							6'd8: begin num_delay <= 20'd2;state <= DELAY;state_back <= WRITE; end
							6'd9: begin one_wire_buffer <= data_wr_buffer[1]; end
							6'd10: begin num_delay <= 20'd80;state <= DELAY;state_back <= WRITE; end
							6'd11: begin one_wire_buffer <= 1'bz; end
							6'd12: begin num_delay <= 20'd2;state <= DELAY;state_back <= WRITE; end
							//write bit 2
							6'd13: begin one_wire_buffer <= 1'b0; end
							6'd14: begin num_delay <= 20'd2;state <= DELAY;state_back <= WRITE; end
							6'd15: begin one_wire_buffer <= data_wr_buffer[2]; end
							6'd16: begin num_delay <= 20'd80;state <= DELAY;state_back <= WRITE; end
							6'd17: begin one_wire_buffer <= 1'bz; end
							6'd18: begin num_delay <= 20'd2;state <= DELAY;state_back <= WRITE; end
							//write bit 3
							6'd19: begin one_wire_buffer <= 1'b0; end
							6'd20: begin num_delay <= 20'd2;state <= DELAY;state_back <= WRITE; end
							6'd21: begin one_wire_buffer <= data_wr_buffer[3]; end
							6'd22: begin num_delay <= 20'd80;state <= DELAY;state_back <= WRITE; end
							6'd23: begin one_wire_buffer <= 1'bz; end
							6'd24: begin num_delay <= 20'd2;state <= DELAY;state_back <= WRITE; end
							//write bit 4
							6'd25: begin one_wire_buffer <= 1'b0; end
							6'd26: begin num_delay <= 20'd2;state <= DELAY;state_back <= WRITE; end
							6'd27: begin one_wire_buffer <= data_wr_buffer[4]; end
							6'd28: begin num_delay <= 20'd80;state <= DELAY;state_back <= WRITE; end
							6'd29: begin one_wire_buffer <= 1'bz; end
							6'd30: begin num_delay <= 20'd2;state <= DELAY;state_back <= WRITE; end
							//write bit 5
							6'd31: begin one_wire_buffer <= 1'b0; end
							6'd32: begin num_delay <= 20'd2;state <= DELAY;state_back <= WRITE; end
							6'd33: begin one_wire_buffer <= data_wr_buffer[5]; end
							6'd34: begin num_delay <= 20'd80;state <= DELAY;state_back <= WRITE; end
							6'd35: begin one_wire_buffer <= 1'bz; end
							6'd36: begin num_delay <= 20'd2;state <= DELAY;state_back <= WRITE; end
							//write bit 6
							6'd37: begin one_wire_buffer <= 1'b0; end
							6'd38: begin num_delay <= 20'd2;state <= DELAY;state_back <= WRITE; end
							6'd39: begin one_wire_buffer <= data_wr_buffer[6]; end
							6'd40: begin num_delay <= 20'd80;state <= DELAY;state_back <= WRITE; end
							6'd41: begin one_wire_buffer <= 1'bz; end
							6'd42: begin num_delay <= 20'd2;state <= DELAY;state_back <= WRITE; end
							//write bit 7
							6'd43: begin one_wire_buffer <= 1'b0; end
							6'd44: begin num_delay <= 20'd2;state <= DELAY;state_back <= WRITE; end
							6'd45: begin one_wire_buffer <= data_wr_buffer[7]; end
							6'd46: begin num_delay <= 20'd80;state <= DELAY;state_back <= WRITE; end
							6'd47: begin one_wire_buffer <= 1'bz; end
							6'd48: begin num_delay <= 20'd2;state <= DELAY;state_back <= WRITE; end
							//back to main
							6'd49: begin num_delay <= 20'd80;state <= DELAY;state_back <= WRITE; end
							6'd50: begin state <= MAIN; end
							default: state <= IDLE;
						endcase
					end
				READ:begin
						if(cnt_read >= 6'd48) cnt_read <= 1'b0;
						else cnt_read <= cnt_read + 1'b1;
						case(cnt_read)
							//read bit 0
							6'd0: begin one_wire_buffer <= 1'b0; end
							6'd1: begin num_delay <= 20'd2;state <= DELAY;state_back <= READ; end
							6'd2: begin one_wire_buffer <= 1'bz; end
							6'd3: begin num_delay <= 20'd10;state <= DELAY;state_back <= READ; end
							6'd4: begin temperature_buffer[0] <= one_wire; end
							6'd5: begin num_delay <= 20'd55;state <= DELAY;state_back <= READ; end
							//read bit 1
							6'd6: begin one_wire_buffer <= 1'b0; end
							6'd7: begin num_delay <= 20'd2;state <= DELAY;state_back <= READ; end
							6'd8: begin one_wire_buffer <= 1'bz; end
							6'd9: begin num_delay <= 20'd10;state <= DELAY;state_back <= READ; end
							6'd10: begin temperature_buffer[1] <= one_wire; end
							6'd11: begin num_delay <= 20'd55;state <= DELAY;state_back <= READ; end
							//read bit 2
							6'd12: begin one_wire_buffer <= 1'b0; end
							6'd13: begin num_delay <= 20'd2;state <= DELAY;state_back <= READ; end
							6'd14: begin one_wire_buffer <= 1'bz; end
							6'd15: begin num_delay <= 20'd10;state <= DELAY;state_back <= READ; end
							6'd16: begin temperature_buffer[2] <= one_wire; end
							6'd17: begin num_delay <= 20'd55;state <= DELAY;state_back <= READ; end
							//read bit 3
							6'd18: begin one_wire_buffer <= 1'b0; end
							6'd19: begin num_delay <= 20'd2;state <= DELAY;state_back <= READ; end
							6'd20: begin one_wire_buffer <= 1'bz; end
							6'd21: begin num_delay <= 20'd10;state <= DELAY;state_back <= READ; end
							6'd22: begin temperature_buffer[3] <= one_wire; end
							6'd23: begin num_delay <= 20'd55;state <= DELAY;state_back <= READ; end
							//read bit 4
							6'd24: begin one_wire_buffer <= 1'b0; end
							6'd25: begin num_delay <= 20'd2;state <= DELAY;state_back <= READ; end
							6'd26: begin one_wire_buffer <= 1'bz; end
							6'd27: begin num_delay <= 20'd10;state <= DELAY;state_back <= READ; end
							6'd28: begin temperature_buffer[4] <= one_wire; end
							6'd29: begin num_delay <= 20'd55;state <= DELAY;state_back <= READ; end
							//read bit 5
							6'd30: begin one_wire_buffer <= 1'b0; end
							6'd31: begin num_delay <= 20'd2;state <= DELAY;state_back <= READ; end
							6'd32: begin one_wire_buffer <= 1'bz; end
							6'd33: begin num_delay <= 20'd10;state <= DELAY;state_back <= READ; end
							6'd34: begin temperature_buffer[5] <= one_wire; end
							6'd35: begin num_delay <= 20'd55;state <= DELAY;state_back <= READ; end
							//read bit 6
							6'd36: begin one_wire_buffer <= 1'b0; end
							6'd37: begin num_delay <= 20'd2;state <= DELAY;state_back <= READ; end
							6'd38: begin one_wire_buffer <= 1'bz; end
							6'd39: begin num_delay <= 20'd10;state <= DELAY;state_back <= READ; end
							6'd40: begin temperature_buffer[6] <= one_wire; end
							6'd41: begin num_delay <= 20'd55;state <= DELAY;state_back <= READ; end
							//read bit 7
							6'd42: begin one_wire_buffer <= 1'b0; end
							6'd43: begin num_delay <= 20'd2;state <= DELAY;state_back <= READ; end
							6'd44: begin one_wire_buffer <= 1'bz; end
							6'd45: begin num_delay <= 20'd10;state <= DELAY;state_back <= READ; end
							6'd46: begin temperature_buffer[7] <= one_wire; end
							6'd47: begin num_delay <= 20'd55;state <= DELAY;state_back <= READ; end
							//back to main
							6'd48: begin state <= MAIN; end
							default: state <= IDLE;
						endcase
					end
				DELAY:begin
						if(cnt_delay >= num_delay) begin
							cnt_delay <= 1'b0;
							state <= state_back; 
						end else cnt_delay <= cnt_delay + 1'b1;
					end
			endcase
		end
	end
	
	assign	one_wire = one_wire_buffer;
	
endmodule

4、BIN转BCD代码也是参考电子森林,在顶层文件中调用了三次。

module bin_to_bcd #
(
parameter B_SIZE = 21
)
(
input						rst_n,			// system reset, active low
input		[B_SIZE-1:0]	bin_code,		// binary code
output	reg	[B_SIZE+3:0]	bcd_code		// bcd code
);

reg		[2*B_SIZE+3:0]		shift_reg;   
always@(bin_code or rst_n)begin      
	shift_reg= {25'h0,bin_code};          
	if(!rst_n) bcd_code <= 0;      
	else begin         
		repeat(B_SIZE)//repeat B_SIZE times
			begin                                
				if (shift_reg[24:21] >= 5) shift_reg[24:21] = shift_reg[24:21] + 2'b11;
				if (shift_reg[28:25] >= 5) shift_reg[28:25] = shift_reg[28:25] + 2'b11;
				if (shift_reg[32:29] >= 5) shift_reg[32:29] = shift_reg[32:29] + 2'b11;
				if (shift_reg[36:33] >= 5) shift_reg[36:33] = shift_reg[36:33] + 2'b11;
				if (shift_reg[40:37] >= 5) shift_reg[40:37] = shift_reg[40:37] + 2'b11;
				if (shift_reg[44:41] >= 5) shift_reg[44:41] = shift_reg[44:41] + 2'b11;
				shift_reg = shift_reg << 1; 
			end         
		bcd_code<=shift_reg[45:21];   
	end  
end

endmodule

5、led闪烁,该模块对12MHZ时钟分频3000000,产生4HZ时钟供LED闪烁和调整小时分钟使用。

module signal_led
(
	input        clk,
	input 		 rst_n,
	output       led

);
	//generate clk_4hz clock
	reg					clk_4hz;
	reg		[23:0]		cnt_4hz;
	always@(posedge clk or negedge rst_n) begin
		if(!rst_n) begin
			cnt_4hz <= 24'h0;
			clk_4hz <= 1'b1;
		end else if(cnt_4hz >= 24'd 1500000) begin
			cnt_4hz <= 24'h0;
			clk_4hz <= ~clk_4hz;
		end else begin
			cnt_4hz <= cnt_4hz + 1'b1;
		end
	end
	assign led=clk_4hz;
	
	
endmodule

6、产生9600HZ时钟,该时钟供给串口发送时使用。

module clk_uart
(
input      clk_in,
input      rst_n,
output reg clk_out
);

reg [9:0] times_lf;

always@(posedge clk_in or negedge rst_n)
    begin
		if(!rst_n) begin
			times_lf<=10'h0;
			clk_out<=1'b1;	
		end else if(times_lf < 10'd625)
			    times_lf <= times_lf+1'b1;
		    else
			    begin			
				  times_lf <= 10'h0;
				  clk_out <= ~clk_out;		  
			    end
	end
endmodule

7、定时时钟模块

该模块参考了杨彧同学,主要的目标是进行计时和对时间的设置。时间可以通过板载的四个按键进行设置。按键抖动时间较长(约为10ms),故没有采用边沿触发的方式,而是选择了定时检测四个按键状态的方式减少按键抖动的影响。该模块有两个输入时钟,低频时钟4hz用于检测按键状态,并根据按键状态改变寄存器中保存的时间值;高频时钟(周期为1ms)用于计时,并查看设置的时间是否有更新。同时,当时间到达整点时,输出一个脉冲控制uart发送温度信息到电脑。

module clock
(

input hour,
input min_a,
input min_d,
input clk_1ms,
input clk_500ms,
input rst_n,
output reg [7:0] hour_out,min_out,
output reg uart_en

);
reg[7:0] hour_mem,min_mem,hour_set,min_set;
reg[15:0] second;

always @(posedge clk_500ms or negedge rst_n) begin
		  if(!rst_n) begin		
				hour_set = 0;
				min_set  = 0;
		  end else  begin
			  if(hour == 0&&min_a == 0&&min_d == 0)
				 begin
				  hour_set = 0;
				  min_set = 0;
				  end
			else if(hour == 0)
				 begin
				  hour_set = hour_out;
				  min_set = min_out;
				  hour_set = hour_set+1'b1;
				  end
			 else if(min_a == 0)
				 begin
				  hour_set = hour_out;
				  min_set = min_out;
				  min_set = min_set+1'b1;
				  end
			 else if(min_d == 0)
				 begin
				  hour_set = hour_out;
				  min_set = min_out;
				  min_set = min_set-1'b1;
				  end
			 if(hour_set == 8'd24)
				 hour_set = 1'b0;
			 if(hour_set > 8'd24)
				 hour_set = 8'd23;
			 if(min_set == 8'd60)
				 min_set = 1'b0;
			 if(min_set > 8'd60)
				 min_set = 8'd59;
		 end
	 end
	 
always @(posedge clk_1ms or negedge rst_n) begin
		  if(!rst_n) begin
			  min_out=0;
			  hour_out=0;
			  uart_en  = 0;
			  hour_mem = 0;
			  min_mem  = 0;
			  second   = 0;
		  end  else begin 
		if(uart_en == 1'b1)
			uart_en = 1'b0;
		if(hour_set != hour_mem || min_set != min_mem)
			begin
				hour_mem = hour_set;
				min_mem = min_set;
				hour_out = hour_set;
				min_out = min_set;
			end
		else
			begin
			  if(second < 16'd60000)
				  second = second+1'b1;
			  else
				  begin
					min_out = min_out+1'b1;
					second = 1'b0;
					if(min_out == 8'd60)
						begin
						 hour_out = hour_out+1'b1;
						 min_out = 1'b0;
						 uart_en = 1'b1;
						 if(hour_out == 8'd24)
							 begin
							  hour_out = 1'b0;
							  end
						 end
					end
			  end  
		 end
	 
	 end
endmodule

8、1KHZ模块,产生1KHZ,供给时钟模块使用。

module clk_1ms
(
	input        clk,
	input 		 rst_n,
	output       clk_1ms

);
	//generate clk_1000hz clock
	reg					clk_1000hz;
	reg		[15:0]		cnt_1000hz;
	always@(posedge clk or negedge rst_n) begin
		if(!rst_n) begin
			cnt_1000hz <= 16'h0;
			clk_1000hz <= 1'b1;
		end else if(cnt_1000hz >= 16'd 6000) begin
			cnt_1000hz <= 16'h0;
			clk_1000hz <= ~clk_1000hz;
		end else begin
			cnt_1000hz <= cnt_1000hz + 1'b1;
		end
	end
	assign clk_1ms=clk_1000hz;
	
	
endmodule

9、串口接收部分


串口通信的通信协议为:1位起始位“0”,8位数据位,一位停止位“1”。串口通信速率为9600波特。接收时采用12MHz时钟,通过对连续1250个周期时接收到的值进行判断这一比特对应的数据,减少因噪声而产生的误码概率。在接收到足够的数据后,触发蜂鸣器播放音乐。

module uart_rx
(
input clk,
input uart_in,

input rst_n,
output reg  [311:0] receive,//39B
output reg pwm_en
);
localparam IDLE = 2'b00;
localparam CHEC = 2'b01;
localparam RECE = 2'b11;
localparam TIMES_L = 1250;

reg[1:0] state;
reg[10:0] times,times_zero;
reg[5:0] num;
reg[5:0] B;



always @(posedge clk or negedge rst_n) begin
	if(!rst_n)
	 begin
		 times = 0;
		 times_zero = 0;
		 state = 0;
		 num = 0;
		 B = 0;
		 receive = 0;
	 end else begin
		 if(pwm_en == 1)
			 pwm_en = 0;
		 case(state)
			 IDLE:
			  begin
			  if(uart_in == 0)
					state = CHEC;
			  end
			  CHEC:
			  begin
			  if(uart_in == 0)
				times_zero = times_zero+1'b1;
				times = times+1'b1;
			  if(times >= TIMES_L)
				  begin
					if(times_zero >= (TIMES_L>>1))
						begin
						 state = RECE;
						 times = 0;
						 times_zero = 0;
						 end
				   else
						begin
						 state = IDLE ;
						 times = 0;
						 times_zero = 0;
						 end
					end
			end
			  RECE:
			  begin
			  times = times+1;
			  if(uart_in == 0)
				  begin
					times_zero = times_zero+1'b1;
					end
			  if(times >= TIMES_L)
				  begin
					if(times_zero >= (TIMES_L>>1))
						begin
						 receive[B*8+num] = 1'b0;
						 end
					else
						begin
						 receive[B*8+num] = 1'b1;
						 end
					num = num+1'b1;
					times = 0;
					times_zero = 0;
				   end
			  if(num > 7)
				   begin
					state = IDLE;
					num = 0;
					times = 0;
					times_zero = 0;
					B = B+1'b1;
					if(B == 39)
						begin
						 B = 0;
						 pwm_en = 1;
						 end
					end
			end
		    endcase
		 end
	 end
endmodule

10、串口发送部分

发送时需发送“XX.X℃”几个字符,将这几个字符及起始位、终止位存入寄存器中,依次发出。发送时钟为9600Hz。

module uart_tx
(
	input[3:0]  tem_g,tem_s,tem_d,
	input rst_n,
	input clk_tx,
	input en,
	output reg uart_out
);
localparam IDLE = 2'b0;
localparam SEND = 2'b1;
reg flag_1,flag_2,state;
reg[60:0] uart_data;
reg[7:0] tab[9:0];
reg[5:0] i;
 
always @(posedge en  or negedge rst_n) begin
	 if(!rst_n)
	 begin
		 flag_1 = 0;
		 uart_data = 1;
	 end else
		 begin
			 uart_data = {1'd1,8'he6,1'd0,1'd1,8'ha1,1'd0,1'd1,tab[tem_d],1'd0,1'd1,8'h2e,1'd0,1'd1,tab[tem_g],1'd0,1'd1,tab[tem_s],1'd0,1'd1};
			 flag_1 = ~flag_1;
		end
	end
	 
	 
always @(posedge clk_tx or negedge rst_n) begin
	if(!rst_n)
		begin
			 tab[0] = 8'h30;
			 tab[1] = 8'h31;
			 tab[2] = 8'h32;
			 tab[3] = 8'h33;
			 tab[4] = 8'h34;
			 tab[5] = 8'h35;
			 tab[6] = 8'h36;
			 tab[7] = 8'h37;
			 tab[8] = 8'h38;
			 tab[9] = 8'h39;
			 flag_2 = 0;
			 i = 0;
			 state = IDLE;
		 end else begin
			 case(state)
				 IDLE:
				 if(flag_2 != flag_1)
					  begin
						flag_2 = flag_1;
						state = SEND;
						end
				  SEND:
				  if(i < 61)
					  begin
						uart_out = uart_data[i];
						i = i+1'b1;
						end
				  else
					  begin
						i = 0;
						state = IDLE;
						end
			endcase
		end
	 end 
endmodule

11、蜂鸣器部分

当串口接收到39个字节时,蜂鸣器播放收到的音乐,播发期间控制OLED不再刷新。

module buzzer
(
input clk,
input tri_uart,

input clk_buzzer,
input rst_n,

input [311:0] data_uart,

output reg stop,out

);

localparam size = 40;
localparam IDLE = 2'b00;
localparam LOAD = 2'b01;
localparam MAIN = 2'b11;
reg[7:0] tone;
reg[5:0] num;
reg[15:0] time_end;
reg[17:0] time_cnt;
reg[size*8-1:0] music;
reg[1:0] state;
reg[23:0] num_delay;

reg judge;

always @(posedge clk_buzzer  or negedge rst_n)
    begin
	if(!rst_n) begin
	 stop = 1'b1;
	 music = 312'd0;
	 state = 2'b00;
	 num = 6'd40;
	end else begin
	
	 case(state)
	     IDLE:
		  begin
		  music = 312'd0;
			if(judge==1'b1)
			  begin
		       state = LOAD;
			  end
			   else
			  state=IDLE;
			  
		  end
		  LOAD:
		  begin
		  music = data_uart;
		  num = 6'd40;
		  state = MAIN;
		  stop = 1'b0;
		  end
		  MAIN:
		  begin
		  
	     if(num == 0)
		      begin
	            num = 6'd0;
				state = IDLE;
				stop = 1'b1; 
				end
		  else
		      begin
				  num  = num - 1'b1;
				  tone = music[((num*8)-1)-:8];
	          
				end
		  end
		  default:state = IDLE;
    endcase
	end
	 end
always @(posedge clk  or negedge rst_n)
    begin
		if(!rst_n) begin

	    time_cnt = 0;
	    end else begin
	
	 case(tone)
	      5'd1:	 	time_end = 16'd45872;	//L1,
		  5'd2: 	time_end = 16'd40858;	//L2,
		  5'd3: 	time_end = 16'd36408;	//L3,
		  5'd4:	 	time_end = 16'd34364;	//L4,
		  5'd5:	 	time_end = 16'd30612;	//L5,
		  5'd6:		time_end = 16'd27273;	//L6,
		  5'd7: 	time_end = 16'd24296;	//L7,
		  5'd8:     time_end = 16'd22931;	//M1,
		  5'd9:     time_end = 16'd20432;	//M2,
		  5'd10:    time_end = 16'd18201;	//M3,
		  5'd11:    time_end = 16'd17180;	//M4,
		  5'd12:    time_end = 16'd15306;	//M5,
		  5'd13:    time_end = 16'd13636;	//M6,
		  5'd14:    time_end = 16'd12148;	//M7,
		  5'd15:	time_end =	16'd5740;	//H1,0f
		  5'd16:	time_end =	16'd5107;	//H2,11
		  5'd17:	time_end =	16'd4549;	//H3,12
		  5'd18:	time_end =	16'd4294;	//H4,13
		  5'd19:	time_end =	16'd3825;	//H5,14
		  5'd20:	time_end =	16'd3408;	//H6,15
		  5'd21:	time_end =	16'd3036;	//H7,16
		  default:  time_end = 16'd0;
	 endcase
	 if(time_end == 0||num == 0)
	     out <= 1'b0;
	 else if(time_cnt >= time_end)
	     begin
		  out <= ~out;
		  time_cnt <= 1'b0;
		  end
	 else
	     time_cnt <= time_cnt + 1'b1;
		 end 
	 end
always @(posedge clk or negedge rst_n) begin
	if(!rst_n) begin
	 num_delay=24'd0;
	
	end else begin
		if(num_delay != 0)
			 begin
			  num_delay = num_delay - 1'b1;
			  end
	    else
			 begin
			  judge = tri_uart;
			  if(judge != 0)
				 begin
				  num_delay = 4000000;
				  end
			  else
				  num_delay = 24'd0;
			  end
		end
	end
endmodule
  • 项目总结

本次实验历经三周,前一周查看各类资料,研究他人代码,完成各模块代码,后两周开始正式着手项目,经过漫长的调试过程,终于完成了题目要求。

在完成项目的过程中遇到的问题以及解决方法:

  1. 自己添加复位信号后,整个工程乱跑。后研究发现,我没有正确写出异步复位逻辑,缺少else。
  2. 串口接收到音频数据后,最后一个字节无法播放。检查发现忽略了并行执行的代码,造成错误。

完成项目后有以下感悟:

  1. VHDL和verilog各有优劣,其中verilog语法与c有些许相似之处,个人学习使用更加方便。但是语言仅仅只是一个工具,尤其在硬件设计里,代码写得漂不漂亮,并不重要,最关键的是设计思想。
  2. 单片机和FPGA编程完全是两种思路,既然叫做硬件描述语言,不能按照写单片机的方式,而是要“心中有电路”,各语句是并行的!
  3. 要充分了解各种通信协议,FPGA编写通信协议要比C语言理解更加深刻。
  4. 相比于单片机的引脚,FPGA引脚功能更加自由,设置更加灵活。。
  5. FPGA分析代码要比C语言顺序执行的难很多,所以要理清思路,时刻记住并行思想。
  • 后期计划

 

1.OLED上显示汉字

2.增加显示年月日功能

3.通过上位机设置时间

4.优化播放声音的生硬感

 

 

 

 

软件 & 硬件

电路图

附件下载

小脚丫上位机.zip
上位机
lf_project_impl1.jed
可以下载验证
lf_project - 1.0.zip
工程文件

团队介绍

中北大学,仪器与电子学院,微电子专业学生
团队成员
刘建伟
电子爱好者

评论

0 / 100
查看更多
目录
硬禾服务号
关注最新动态
0512-67862536
info@eetree.cn
江苏省苏州市苏州工业园区新平街388号腾飞创新园A2幢815室
苏州硬禾信息科技有限公司
Copyright © 2023 苏州硬禾信息科技有限公司 All Rights Reserved 苏ICP备19040198号