寒假在家一起练(四)--韩旭
小脚丫综合训练平台让我有了进一步接触FPGA的机会,为将来参加各种嵌入式系统比赛打下了良好的基础。本项目成功完成了活动要求的五项内容,实现了时钟,温度传感器,OLED屏幕显示,串口通信等功能,期待将来有进一步使用该开发板实现更多功能的机会。
标签
FPGA
Hanx
更新2021-03-03
817

FtNXG9p94qDA0hpWd3N0zMEiukTg

一、项目需求

  1. 实现一个可定时时钟的功能,用小脚丫FPGA核心模块的4个按键设置当前的时间,OLED显示数字钟的当前时间,精确到分钟即可,到整点的时候比如8:00,蜂鸣器报警,播放音频信号,最长可持续30秒;
  2. 实现温度计的功能,小脚丫通过板上的温度传感器实时测量环境温度,并同时间一起显示在OLED的屏幕上;
  3. 定时时钟整点报警的同时,将温度信息通过UART传递到电脑上,电脑上能够显示当前板子上的温度信息(任何显示形式都可以),要与OLED显示的温度值一致;
  4. PC收到报警的温度信号以后,将一段音频文件(自己制作,持续10秒钟左右)通过UART发送给小脚丫FPGA,蜂鸣器播放收到的这段音频文件,OLED屏幕上显示的时间信息和温度信息都停住不再更新;
  5. 音频文件播放完毕,OLED开始更新时间信息和当前的温度信息。

二、实现思路

   本项目将实验的内容分为模块化处理,不同的模块对应不同的要求与功能,其中包含时间生成模块,温度传感器模块,蜂鸣器发声模块,OLED配置模块以及串口通信模块,并最终通过一个顶层模块的编写完成各项功能的协同。

  1. 顶层模块
module system_top(
	input				    clk_in,		
	input				    rst_n_in,		

    input           [2:0]   key,        
    output reg              en,

    inout				    one_wire,   

    input                   rs232_rx,
    output                  rs232_tx,

    output                  piano_out,

	output   			    oled_csn,	
	output  			    oled_rst,	
	output  			    oled_dcn,	
	output  			    oled_clk,	
	output  			    oled_dat,	

    output          [6:0]   seg_led_1,
    output          [6:0]   seg_led_2
);

wire [11:0]         tamp_data;  
wire [23:0]         time_data;  
wire                flag;
wire                play_done;

always @ ( posedge clk_in or negedge rst_n_in )
    if ( ~rst_n_in )
        en <= 1'b1;
    else if ( flag )
        en <= 1'b0;
    else if ( play_done )
        en <= 1'b1;

//时间生成
time_generator time_generator_inst(
	.clk_in	        (clk_in	        ),	
	.rst_n_in          (rst_n_in          ),
    .control        (key            ),
    .en             (en             ),
    .time_out       (time_data      ),
    .flag           (flag           )
);

//温度传感器
DS18B20Z DS18B20Z_inst(
	.clk_in	        (clk_in	        ),	
	.rst_n_in          (rst_n_in          ),
    .en             (en             ),
	.one_wire       (one_wire       ),
	.tamp_out       (tamp_data      )	
);

//OLED
OLED12832 OLED12832_inst(
	.clk_in            (clk_in            ),
	.rst_n_in          (rst_n_in          ),
    .time_data      (time_data[23:8]      ),
    .tamp_data      (tamp_data      ),
	.oled_csn       (oled_csn       ),
	.oled_rst       (oled_rst       ),
	.oled_dcn       (oled_dcn       ),
	.oled_clk       (oled_clk       ),
	.oled_dat       (oled_dat       )
);

wire                rx_done;
wire [9:0]   data_length;
wire [959:0] data_buffer;
//串口
uart_top #
(
.BPS_PARA			(1250   		)
)
uart_top_inst(
    .clk_in            (clk_in            ),
    .rst_n_in          (rst_n_in          ),
    .flag           (flag           ),
    .tamp_data      (tamp_data      ),
    .rs232_rx       (rs232_rx       ),
    .rs232_tx       (rs232_tx       ),
    .data_length    (data_length    ),
    .data_buffer    (data_buffer    ),
    .rx_done        (rx_done        )
);

//蜂鸣器发声
beeper_control beeper_control_inst(
    .clk_in            (clk_in            ),
    .rst_n_in          (rst_n_in          ),
    .en             (~en            ),
    .rx_done        (rx_done        ),
    .data_length    (data_length    ),
    .data_buffer    (data_buffer    ),
    .piano_out      (piano_out      ),
    .play_done      (play_done      )
);

//数码管
segment segment_inst(
    .seg_data_1     (time_data[7:4] ),
    .seg_data_2     (time_data[3:0] ),
    .seg_led_1      (seg_led_1      ),
    .seg_led_2      (seg_led_2      )
);

endmodule

   2.时钟模块

   时钟模块的主要功能是产生时分秒,并且在整点时改变flag标志位的状态,向蜂鸣器传递信号,从而达到整点报时的效果。

module time_generator(
	input                   clk_in	    , //12M
	input                   rst_n_in   ,
    input           [2:0]   control , 
    input                   en      ,
    output          [23:0]  time_out,
    output  reg             flag
);


wire				clk_in_5hz;
reg		[23:0]		cnt_5hz;
always@(posedge clk_in or negedge rst_n_in)
    if(!rst_n_in)
        cnt_5hz <= 24'd0;
    else if(cnt_5hz == 24'd2399999)
        cnt_5hz <= 24'd0;
    else if( en )
        cnt_5hz <= cnt_5hz + 1'b1;

assign clk_in_5hz = (cnt_5hz == 24'd2000000)?1'b1:1'b0;

reg [2:0]           cnt;
always @ ( posedge clk_in or negedge rst_n_in )
    if (~rst_n_in)
        cnt <= 3'd0;
    else if ( cnt == 3'd4 && cnt_5hz == 24'd2399999)
        cnt <= 3'd0;
    else if (cnt_5hz == 24'd2399999)
        cnt <= cnt + 1'b1;

reg [7:0]           time_shi;
reg [7:0]           time_fen;
reg [7:0]           time_miao;

//时
always @ ( posedge clk_in_5hz or negedge rst_n_in )
    if (~rst_n_in)
        time_shi <= 8'd0;
    else if( en && ((time_fen[7:4]==4'd5 && time_fen[3:0]==4'd9 && time_miao[7:4]==4'd5 && time_miao[3:0]==4'd9 && cnt == 3'd4) || ~control[2]) )begin
        if( time_shi[7:4]==4'd2 && time_shi[3:0]==4'd3 )
            time_shi <= 8'd0;
        else if ( time_shi[3:0]==4'd9 ) begin
            time_shi[7:4] <= time_shi[7:4] + 1'b1;
            time_shi[3:0] <= 4'd0;
        end
        else
            time_shi[3:0] <= time_shi[3:0] + 1'b1;
    end

//分
always @ ( posedge clk_in_5hz or negedge rst_n_in )
    if (~rst_n_in)
        time_fen <= 8'd0;
    else if( en && ((time_miao[7:4]==4'd5 && time_miao[3:0]==4'd9  && cnt == 3'd4) || ~control[1]) )begin
        if( time_fen[7:4]==4'd5 && time_fen[3:0]==4'd9 )
            time_fen <= 8'd0;
        else if ( time_fen[3:0]==4'd9 ) begin
            time_fen[7:4] <= time_fen[7:4] + 1'b1;
            time_fen[3:0] <= 4'd0;
        end
        else
            time_fen[3:0] <= time_fen[3:0] + 1'b1;
    end

//秒
always @ ( posedge clk_in_5hz or negedge rst_n_in )
    if (~rst_n_in)
        time_miao <= 8'd0;
    else if ( en && (cnt == 3'd4 || ~control[0])) begin
        if( time_miao[7:4]==4'd5 && time_miao[3:0]==4'd9)
            time_miao <= 8'd0;
        else if ( time_miao[3:0]==4'd9 ) begin
            time_miao[7:4] <= time_miao[7:4] + 1'b1;
            time_miao[3:0] <= 4'd0;
        end
        else
            time_miao[3:0] <= time_miao[3:0] + 1'b1;
    end

assign time_out = {time_shi,time_fen,time_miao};

always @ ( posedge clk_in or negedge rst_n_in )
    if ( ~rst_n_in )
        flag <= 1'b0;
    else if ( flag == 1'b1 )
        flag <= 1'b0;
    else if (time_fen==8'd0&&time_miao==8'd0&&cnt==3'd0&&cnt_5hz == 24'd2399999)
        flag <= 1'b1;

endmodule

3.蜂鸣器发声模块

   蜂鸣器接收到串口传来的音频信号后,完成控制蜂鸣器发出相应的音色功能,借鉴了一定的FPGA社区中的开源代码实现了相关的功能

module beeper_control(
	input                   clk_in	        , //12M
	input                   rst_n_in       ,
    input                   en          ,
    input                   rx_done     , 
    input           [9:0]   data_length ,
    input           [959:0] data_buffer ,
    output                  piano_out   ,
    output                  play_done
);

reg		[23:0]		cnt;
always@(posedge clk_in or negedge rst_n_in)
    if(~rst_n_in)
        cnt <= 24'd0;
    else if(cnt == 24'd3999999)
        cnt <= 24'd0;
    else
        cnt <= cnt + 1'b1;
        

reg [9:0]           data_length_reg;
always@(posedge clk_in or negedge rst_n_in)
    if(~rst_n_in)
        data_length_reg <= 10'd0;
    else if ( rx_done )
        data_length_reg <= data_length;
    else if ( data_length_reg && cnt == 24'd3999999)
        data_length_reg <= data_length_reg - 1'b1;

reg [959:0]           data_buffer_reg;
always@(posedge clk_in or negedge rst_n_in)
    if(~rst_n_in)
        data_buffer_reg <= 960'd0;
    else if ( rx_done )
        data_buffer_reg <= data_buffer;
    else if ( data_length_reg && cnt == 24'd3999999) begin
        data_buffer_reg <= data_buffer_reg>>8;
    end
        

wire [4:0]          tone;

assign tone = data_buffer_reg[4:0];

wire                play_en;
assign play_en = (data_length_reg == 10'd0)?1'b0:1'b1;

Beeper Beeper_inst(
    .clk_in            (clk_in            ),		
    .rst_n_in          (rst_n_in          ),	    
    .tone_en        (play_en        ),	    
    .tone           (tone           ),		
    .piano_out      (piano_out      )	    
);

assign play_done = ( data_length_reg == 10'd1 && en)?1'b1:1'b0;

endmodule

4.OLED模块

   OLED显示模块参考了电子森林使用的应用案例及参考代码完成了相关的内容,故不再赘述。

5.温度传感器DS18B20Z模块

   温度传感器模块使用FPGA社区的相关资源以及在同学们的相互帮助下完成了配置,但是此处仅展示对于温度信号的处理部分。

always @ (posedge clk_in_1mhz or negedge rst_n_in)
    if ( ~rst_n_in )
        tamp_out <= 12'd0;
    else if (en) begin
        case(data_out[2:0])
            3'b000:         tamp_out[3:0] <= (data_out[3])?4'd5:4'd0;
            3'b001,3'b010:  tamp_out[3:0] <= (data_out[3])?4'd6:4'd1;
            3'b011:         tamp_out[3:0] <= (data_out[3])?4'd7:4'd2;
            3'b100,3'b101:  tamp_out[3:0] <= (data_out[3])?4'd8:4'd3;
            3'b110,3'b111:  tamp_out[3:0] <= (data_out[3])?4'd9:4'd4;
        endcase
        case(data_out[7:4])
            4'b0000:  begin tamp_out[11:8] <= (data_out[8])?4'd1:4'd0; tamp_out[7:4] <= (data_out[8])?4'd6:4'd0; end
            4'b0001:  begin tamp_out[11:8] <= (data_out[8])?4'd1:4'd0; tamp_out[7:4] <= (data_out[8])?4'd7:4'd1; end
            4'b0010:  begin tamp_out[11:8] <= (data_out[8])?4'd1:4'd0; tamp_out[7:4] <= (data_out[8])?4'd8:4'd2; end
            4'b0011:  begin tamp_out[11:8] <= (data_out[8])?4'd1:4'd0; tamp_out[7:4] <= (data_out[8])?4'd9:4'd3; end
            4'b0100:  begin tamp_out[11:8] <= (data_out[8])?4'd2:4'd0; tamp_out[7:4] <= (data_out[8])?4'd0:4'd4; end

            4'b0101:  begin tamp_out[11:8] <= (data_out[8])?4'd2:4'd0; tamp_out[7:4] <= (data_out[8])?4'd1:4'd5; end
            4'b0110:  begin tamp_out[11:8] <= (data_out[8])?4'd2:4'd0; tamp_out[7:4] <= (data_out[8])?4'd2:4'd6; end
            4'b0111:  begin tamp_out[11:8] <= (data_out[8])?4'd2:4'd0; tamp_out[7:4] <= (data_out[8])?4'd3:4'd7; end
            4'b1000:  begin tamp_out[11:8] <= (data_out[8])?4'd2:4'd0; tamp_out[7:4] <= (data_out[8])?4'd4:4'd8; end
            4'b1001:  begin tamp_out[11:8] <= (data_out[8])?4'd2:4'd0; tamp_out[7:4] <= (data_out[8])?4'd5:4'd9; end
            
            4'b1010:  begin tamp_out[11:8] <= (data_out[8])?4'd2:4'd1; tamp_out[7:4] <= (data_out[8])?4'd6:4'd0; end
            4'b1011:  begin tamp_out[11:8] <= (data_out[8])?4'd2:4'd1; tamp_out[7:4] <= (data_out[8])?4'd7:4'd1; end
            4'b1100:  begin tamp_out[11:8] <= (data_out[8])?4'd2:4'd1; tamp_out[7:4] <= (data_out[8])?4'd8:4'd2; end
            4'b1101:  begin tamp_out[11:8] <= (data_out[8])?4'd2:4'd1; tamp_out[7:4] <= (data_out[8])?4'd9:4'd3; end

            4'b1110:  begin tamp_out[11:8] <= (data_out[8])?4'd3:4'd1; tamp_out[7:4] <= (data_out[8])?4'd0:4'd4; end
            4'b1111:  begin tamp_out[11:8] <= (data_out[8])?4'd3:4'd1; tamp_out[7:4] <= (data_out[8])?4'd1:4'd5; end
        endcase
    end
 
endmodule

6.串口通信模块

   借鉴了STEP FPGA开源社区的相关内容,在完成对于UART信号的处理后,产生脉冲信号,将其送入蜂鸣器模块并产生声音。

//接受音符并产生脉冲
wire                    ready;

always @ ( posedge clk_in or negedge rst_n_in )
    if ( ~rst_n_in )
        data_length <= 10'd0;
    else if ( rx_done )
        data_length <= 10'd0;
    else if ( ready && rx_data==8'h1f) 
        data_length <= data_length + 1'b1;
    else if ( ready )
        data_length <= data_length + 1'b1;

always @ ( posedge clk_in or negedge rst_n_in )
    if ( ~rst_n_in )
        data_buffer <= 960'd0;
    else if ( rx_done )
        data_buffer <= 960'd0;
    else if ( ready && rx_data==8'h1f)
        data_buffer <= data_buffer;
    else if ( ready ) begin
        data_buffer[7:0] <= rx_data;
        data_buffer[959:8] <= data_buffer[951:0];
    end

always @ (posedge clk_in or negedge rst_n_in)
	if(!rst_n_in)
        rx_done <= 1'b0;
    else if (rx_done)
        rx_done <= 1'b0;
    else if ( ready && rx_data==8'h1f)
        rx_done <= 1'b1;

三、遇到的主要的问题

  1. 初次接触此系列的FPGA,有一些生疏,在电子森林课程的帮助下步上正轨。
  2. 相关硬件的配置有一定的苦难,在查看学习开源社区的内容后成功解决。
  3. 上位机最终编写python代码实现,使用了其相当方便的库serial。

四、资源使用情况

Fu_dGms7ZumgJE6BvluXPHKtml1VFqa4KnXufWIkAIqGNbrrc089KLO2

五、项目总结

   未来不知道是否还有机会使用该开发板完成进一步的开发,电子森林为我们提供了丰富的开源社区资源,这对于开发以及学习使用都有相当巨大的帮助,实践是最好的老师,在开源实例的帮助下我们能快速的硬件的配置及相关功能的实现,收获颇丰。在上位机的编写上,许多同学也是大显神通,在同学们的友好交流与互帮互助下完成了项目的所有内容,感谢电子森林提供的这次与众多热爱FPGA开发的同学同聚一堂的机会。

 

附件下载
swj.py
上位机文件,需要用python打开上位机文件(具体按照视频演示操作即可)
system_impl1.jed
可用于验证的工程文件
团队介绍
北京理工大学信息与电子学院,共同合作的FPGA团队
团队成员
韩旭
北京理工大学信息与电子学院在读大三学生,喜好进行嵌入式开发等相关工作。
柯洋
北京理工大学信息与电子学院在读大三学生
张千龙
北京理工大学信息与电子学院在读大三学生
评论
0 / 100
查看更多
目录
硬禾服务号
关注最新动态
0512-67862536
info@eetree.cn
江苏省苏州市苏州工业园区新平街388号腾飞创新园A2幢815室
苏州硬禾信息科技有限公司
Copyright © 2023 苏州硬禾信息科技有限公司 All Rights Reserved 苏ICP备19040198号