2021寒假在家练项目4——曹坤
使用小脚丫FPGA开发板设计一个能够定时、测温、报警、控制的项目,能通过串口显示当前温度,并配置警报音乐信息。
标签
嵌入式系统
FPGA
测试
数字逻辑
显示
k_k
更新2021-02-25
1089

内容介绍

项目描述及要实现的功能:

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

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

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

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

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

设计思路:

  1. oled显示时间和温度信息。
  2. 通过按键设置时间更新到时钟上,当时钟到14:00:00进行报警,发送当前时间和温度信息到上位机,并且oled停止刷新显示。
  3. 用上位机发送一份自制的文件到FPGA,FPGA收到文件后存在RAM中。
  4. 设置0.5秒读取一次RAM中数据到蜂鸣器中实现音乐的播放。

思维导图:

Fs3x89CGg-uxpPKUjPA5Hc7I9PV-

引脚信息:

FjN_tQ1TOFr76Ml4N3UXxXxgryDo

资源报告:

FnYBj5rh11TlFOe5viWuFP0gNuLY

RTL视图:

  • FuAiWfIYWceu8389OgltO3h66hB2

功能介绍:

sw1为oled显示模式切换开关,高电平为时间温度显示模式,低电平为时间设定模式。

sw4为复位开关,低电平复位。

key1为时间设定确认按键,按下即将设定时间更新为oled当前显示时间。

key3为数值加按键,按下将选中位选数值加1。

key4为位选切换按键,按下将切换数字位选。

代码具体实现:

  • 顶层模块设计

连接各个模块。

module top(
    input Clk,
    input Rst_n,
    input key_1_confirm,
    input key_2_minus,
    input key_3_add,
    input key_4_shift,
    input sw_1_oledmode,
    input sw_2_buzzer,
    input RXD,    
    
    inout one_wire,

    output TXD,
    
    output oled_csn,
    output oled_rst,
    output oled_clk,
    output oled_dat,
    output oled_dcn,
    
    output Buzzer_pwm,
    
    output led1_oled,
    output led7_tx,
    output led8_reset
);

    reg oled_stopbit;

    wire [3:0]sec_l;
    wire [3:0]sec_h;
    wire [3:0]min_l;
    wire [3:0]min_h;
    wire [3:0]hour_l;
    wire [3:0]hour_h;
    
    wire [3:0]timer_sec_l;
    wire [3:0]timer_sec_h;
    wire [3:0]timer_min_l;
    wire [3:0]timer_min_h;
    wire [3:0]timer_hour_l;
    wire [3:0]timer_hour_h;
    
    wire [3:0]temp_h;
    wire [3:0]temp_l;
    wire [3:0]temp_s;
       
    
    wire [7:0]buzzer_data;
    wire [7:0]rx_data,tx_data;
    wire [7:0]wraddress,rdaddress;
    wire Send_En;
    wire Rx_Done,Tx_Done;
    wire key1_state,key1_flag,key1_in;
    wire key3_state,key3_flag,key3_in;
    wire key4_state,key4_flag,key4_in;
    wire buzz_en;
    wire rden,wren;
    wire timeout,ringout;
    wire uart_tx_state;
    
    
    assign led1_oled = sw_1_oledmode;  
    assign led7_tx = ~uart_tx_state;
    assign led8_reset = Rst_n;
    
    
    OLED_SPI OLED_SPI(
        .clk(Clk),		//12MHz系统时钟
        .rst_n(Rst_n),		//系统复位,低有效
        .sw_1_oledmode(sw_1_oledmode),
        .oled_stopbit(oled_stopbit),
        
        .sec_l(sec_l),
        .min_l(min_l),
        .hour_l(hour_l),
        .sec_h(sec_h),
        .min_h(min_h),
        .hour_h(hour_h),
        
        .timer_sec_l(timer_sec_l),
        .timer_sec_h(timer_sec_h),
        .timer_min_l(timer_min_l),
        .timer_min_h(timer_min_h),
        .timer_hour_l(timer_hour_l),
        .timer_hour_h(timer_hour_h),
        
        .temp_h(temp_h),
        .temp_l(temp_l),
        .temp_s(temp_s),
        
        .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数据信号
    );
    
    temperature temperature(
        .Clk(Clk),
        .Rst_n(Rst_n),
        .one_wire(one_wire),
        .temp_h(temp_h),
        .temp_l(temp_l),
        .temp_s(temp_s)
    );
    
    clock clock(
        .Clk(Clk), 
        .Rst_n(Rst_n), 
        .key1_in(key1_in),
        
        .timer_sec_l(timer_sec_l),
        .timer_sec_h(timer_sec_h),
        .timer_min_l(timer_min_l),
        .timer_min_h(timer_min_h),
        .timer_hour_l(timer_hour_l),
        .timer_hour_h(timer_hour_h),
        
        .sec_l(sec_l),
        .min_l(min_l),
        .hour_l(hour_l),
        .sec_h(sec_h),
        .min_h(min_h),
        .hour_h(hour_h),
        
        .timeout(timeout)
    );
    
    Buzzer Buzzer(
        .Clk(Clk),
        .Rst_n(Rst_n),
        .data_in(buzzer_data),
        .buzz_en(buzz_en),
        .pwm_out(Buzzer_pwm)
    );
    
    RAM RAM(
        .aclr(1'b0),
        .clock(Clk),
        .data(rx_data),
        .rdaddress(rdaddress),
        .rden(rden),
        .wraddress(wraddress),
        .wren(wren),
        .q(buzzer_data)
    );
    
    uart_rxd uart_rxd(
        .Clk(Clk),        //模块时钟12M
        .Rst_n(Rst_n),      //模块复位
        .baud_set(3'd0),   //波特率设置
        .Rs232_Rx(RXD),   //RXD
        
        .data_byte(rx_data),  //并行数据输出
        .Rx_Done(Rx_Done)     //一次数据接收完成标志
    );
    
    uart_txd uart_txd(
        .Clk(Clk),
        .Rst_n(Rst_n),
        .baud_set(3'd0),
        .data_byte(tx_data),
        .Send_En(Send_En),
        
        .Rs232_Tx(TXD),//TXD
        .Tx_Done(Tx_Done),//标志数据传输完毕
        .uart_state(uart_tx_state)//标志数据正在发送
    );
    
    rx_buzz_ctrl rx_buzz_ctrl(
        .Clk(Clk),
        .Rst_n(Rst_n),
        .Rx_Done(Rx_Done),
        .sw_2_buzzer(sw_2_buzzer),//高使能
        
        .wren(wren),
        .rden(rden),
        .wraddress(wraddress),
        .rdaddress(rdaddress),
        .buzz_en(buzz_en),
        .ringout(ringout)
    );
    
    timeout_tx_ctrl timeout_tx_ctrl(
        .Clk(Clk),
        .Rst_n(Rst_n),
        .timeout(timeout),
        .Tx_Done(Tx_Done),

        .sec_l(sec_l),
        .sec_h(sec_h),
        .min_l(min_l),
        .min_h(min_h),
        .hour_l(hour_l),
        .hour_h(hour_h),

        .temp_h(temp_h),
        .temp_l(temp_l),
        .temp_s(temp_s),

        .Send_En(Send_En),
        .tx_data(tx_data)
    );
    
    timer timer(
        .Clk(Clk),
        .Rst_n(Rst_n),
//        .key2_in(key2_in),//minus
        .key3_in(key3_in),//add
        .key4_in(key4_in),

        .timer_sec_l(timer_sec_l),
        .timer_sec_h(timer_sec_h),
        .timer_min_l(timer_min_l),
        .timer_min_h(timer_min_h),
        .timer_hour_l(timer_hour_l),
        .timer_hour_h(timer_hour_h)
        
    );    
    
    key key1(
        .Clk(Clk),
        .Rst_n(Rst_n),
        .key_in(key_1_confirm),
        .key_flag(key1_flag),
        .key_state(key1_state)
    );
    
    key key3(
        .Clk(Clk),
        .Rst_n(Rst_n),
        .key_in(key_3_add),
        .key_flag(key3_flag),
        .key_state(key3_state)
    );
    
    key key4(
        .Clk(Clk),
        .Rst_n(Rst_n),
        .key_in(key_4_shift),
        .key_flag(key4_flag),
        .key_state(key4_state)
    );
    
    assign key1_in = key1_flag && !key1_state;
    assign key3_in = key3_flag && !key3_state;
    assign key4_in = key4_flag && !key4_state;
    

    always @(posedge Clk, negedge Rst_n)
        if (!Rst_n)
            oled_stopbit <= 1'b0;
        else if (timeout)
            oled_stopbit <= 1'b1;
        else if (ringout)
            oled_stopbit <= 1'b0;
        else  
            oled_stopbit <= oled_stopbit;
                
endmodule

  • 时钟模块

产生时分秒的24小时时钟,当按键key1按下时可更新设定的时间,当时间到达14:00:00时产生timeout信号。

module clock(
    input Clk,
    input Rst_n,
    input key1_in,
    
    input [3:0]timer_sec_l,
    input [3:0]timer_sec_h,
    input [3:0]timer_min_l,
    input [3:0]timer_min_h,
    input [3:0]timer_hour_l,
    input [3:0]timer_hour_h,
    
    output reg[3:0]sec_l,
    output reg[3:0]sec_h,
    output reg[3:0]min_l,
    output reg[3:0]min_h,
    output reg[3:0]hour_l,
    output reg[3:0]hour_h,
    
    output reg timeout
);
    
    reg Clk_1s;
    reg [23:0]cnt_1s;
    reg key1;
    
//    assign timeout = (hour_h == 4'd1 && hour_l == 4'd4 && min_h == 4'd0 && min_l == 4'd0 && sec_h == 4'd0 && sec_l == 4'd0);

    always @(posedge Clk or negedge Rst_n)//达到报警时间
        if (!Rst_n)
            timeout <= 1'b0;
        else if (hour_h == 4'd1 && hour_l == 4'd4 && min_h == 4'd0 && min_l == 4'd0 && sec_h == 4'd0 && sec_l == 4'd0)
            timeout <= 1'b1;
        else
            timeout <= 1'b0;

    always @(posedge Clk, negedge Rst_n)//1HZ时钟
        if (!Rst_n)begin
            Clk_1s <= 1'd0;
            cnt_1s <= 24'd0;
        end
        else if (cnt_1s == 24'd12_000_000 - 1) begin
            Clk_1s <= 1'd1;
            cnt_1s <= 24'd0;
        end
        else begin
            Clk_1s <= 1'd0;
            cnt_1s <= cnt_1s + 1'b1;
        end
        
    always @(posedge key1_in or posedge Clk_1s or negedge Rst_n)//更新设定时间
        if (!Rst_n)
            key1 <= 1'b0;
        else if (key1_in)
            key1 <= 1'b1;
        else
            key1 <= 1'b0;
        
    always @(posedge Clk_1s, negedge Rst_n)//秒
        if (!Rst_n)
            sec_l <= 4'd7;
        else if (key1)
            sec_l <= timer_sec_l;
        else if (sec_l == 4'd9)
            sec_l <= 4'd0;
        else
            sec_l <= sec_l + 1'b1;
            
    always @(posedge Clk_1s, negedge Rst_n)
        if (!Rst_n)
            sec_h <= 4'd2;
        else if (key1)
            sec_h <= timer_sec_h;
        else if (sec_h == 4'd5 && sec_l == 4'd9)
            sec_h <= 4'd0;
        else if (sec_l == 4'd9)
            sec_h <= sec_h + 1'b1;
        else
            sec_h <= sec_h;

    always @(posedge Clk_1s, negedge Rst_n)//分
        if (!Rst_n)
            min_l <= 4'd5;  
        else if (key1)
            min_l <= timer_min_l;      
        else if (sec_h == 4'd5 && sec_l == 4'd9)
            if (min_l == 4'd9)
                min_l <= 4'd0;
            else
                min_l <= min_l + 1'b1;
        else
            min_l <= min_l;
            
    always @(posedge Clk_1s, negedge Rst_n)
        if (!Rst_n)
            min_h <= 4'd3;    
        else if (key1)
            min_h <= timer_min_h;    
        else if (min_h == 4'd5 && min_l == 4'd9 && sec_h == 4'd5 && sec_l == 4'd9)
            min_h <= 4'd0;
        else if (min_l == 4'd9 && sec_h == 4'd5 && sec_l == 4'd9)
            min_h <= min_h + 1'b1;
        else
            min_h <= min_h;
            
    always @(posedge Clk_1s, negedge Rst_n)//时
        if (!Rst_n)
            hour_l <= 4'd4;
        else if (key1)
            hour_l <= timer_hour_l;
        else if (min_h == 4'd5 && min_l == 4'd9 && sec_h == 4'd5 && sec_l == 4'd9)
            if (hour_l == 4'd9 || (hour_h == 4'd2 && hour_l == 4'd3 && min_h == 4'd5 && min_l == 4'd9 && sec_h == 4'd5 && sec_l == 4'd9))
                hour_l <= 4'd0;
            else
                hour_l <= hour_l + 1'b1;
        else
            hour_l <= hour_l;
            
    always @(posedge Clk_1s, negedge Rst_n)
        if (!Rst_n)
            hour_h <= 4'd1; 
        else if (key1)
            hour_h <= timer_hour_h;       
        else if (hour_h == 4'd2 && hour_l == 4'd3 && min_h == 4'd5 && min_l == 4'd9 && sec_h == 4'd5 && sec_l == 4'd9)
            hour_h <= 4'd0;
        else if (hour_l == 4'd9 && min_h == 4'd5 && min_l == 4'd9 && sec_h == 4'd5 && sec_l == 4'd9)
            hour_h <= hour_h + 1'b1;
        else
            hour_h <= hour_h;
            

endmodule

  • 按键消抖模块

采用状态机对独立按键进行消抖。

module key(
    Clk,
    Rst_n,
    key_in,
    key_flag,
    key_state
    );

input Clk;
input Rst_n;
input key_in;

output reg key_flag;//按键是否处于触发沿
output reg key_state;//按键当前电平



reg key_in_s1,key_in_s2;
always@(posedge Clk, negedge Rst_n)//对外部输入信号进行处理
    if(!Rst_n)begin
        key_in_s1 <= 1'b0;
        key_in_s2 <= 1'b0;
    end
    else begin
        key_in_s1 <= key_in;
        key_in_s2 <= key_in_s1;
    end
    
reg key_tmp1,key_tmp2;
wire pedge,nedge;
always@(posedge Clk,negedge Rst_n)//D触发器存储两个相邻时钟上升沿时的外部输入信号
    if(!Rst_n)begin
        key_tmp1 <= 1'b0;
        key_tmp2 <= 1'b0;
    end
    else begin
        key_tmp1 <= key_in_s2;
        key_tmp2 <= key_tmp1;
    end
    
assign pedge = (!key_tmp1) & key_tmp2;//跳变沿
assign nedge = key_tmp1 & (!key_tmp2);


reg[19:0]cnt;
reg en_cnt;
reg cnt_full;
always@(posedge Clk,negedge Rst_n)//计数器
    if (!Rst_n)
        cnt <= 20'b0;
    else 
    if (en_cnt)
        cnt <= cnt + 1'b1;
    else
        cnt <= 20'b0;
        
always@(posedge Clk,negedge Rst_n)
    if (!Rst_n)
        cnt_full <= 1'b0;
    else if (cnt == 20'd999_999)
        cnt_full <= 1'b1;
    else
        cnt_full <= 1'b0;


localparam
    IDEL    =   4'b0001,
    FILTER0 =   4'b0010,
    DOWN    =   4'b0100,
    FILTER1 =   4'b1000;
    
reg [3:0]state;

always @(posedge Clk,negedge Rst_n)//按键状态机
    if (!Rst_n)begin
        state = IDEL;
        key_flag <= 1'b0;//防止发生复位时其他参数未复位
        key_state <= 1'b1;
        en_cnt = 1'b1;
    end
    else begin
        case(state)
            IDEL: begin
                key_flag <= 1'b0;
                if (nedge)begin
                    state <= FILTER0;
                    en_cnt <= 1'b1;
                end
                else
                    state <= IDEL;
            end
            FILTER0: begin
                if (cnt_full)begin
                    key_flag <= 1'b1;
                    key_state <= 1'b0;
                    en_cnt <=1'b0;
                    state <= DOWN;
                end
                else if (pedge)begin
                    state <= IDEL;
                    en_cnt <= 1'b0;
                end
                else
                    state <= FILTER0;
            end
            DOWN: begin
                key_flag = 1'b0;
                if (pedge)begin
                    state <= FILTER1;
                    en_cnt <= 1'b1;
                end
                else
                    state = DOWN;
            end
            FILTER1: begin
                if (cnt_full) begin
                    key_flag <= 1'b1;
                    key_state <= 1'b1;
                    state <= IDEL;
                    en_cnt <= 1'b0;
                end
                else if(nedge)begin
                    en_cnt <= 1'b0;
                    state = DOWN;
                end                    
                else
                    state <= FILTER1;
            end        
            default: begin
                state <= IDEL;
                en_cnt <= 1'b0;
                key_flag = 1'b0;
                key_state = 1'b1;
            end
        endcase
    end

endmodule

  • 串口接收模块

接收来自上位机通过串口发送的数据,并将串行数据转换为并行数据。

module uart_rxd(
    Clk,        //模块时钟12M
    Rst_n,      //模块复位
    baud_set,   //波特率设置
    Rs232_Rx,   //RS232数据输入
     
    data_byte,  //并行数据输出
    Rx_Done     //一次数据接收完成标志
);

    input Clk;
    input Rst_n;
    input [2:0]baud_set;
    input Rs232_Rx;
    
    output reg [7:0]data_byte;
    output reg Rx_Done;//数据接收完成
    
    wire nedege;//启示信号下降沿
    reg [2:0]START_BIT, STOP_BIT;//数据接收起始标志位
    reg uart_state;//数据接收状态
    
//串行同步输入处理,将异步输入信号转化为同步输入信号

    reg s0_Rs232_Rx,s1_Rs232_Rx; //同步寄存器
    always@(posedge Clk or negedge Rst_n)//同步寄存器,消除亚稳态
        if(!Rst_n)begin
            s0_Rs232_Rx <= 1'b0;
            s1_Rs232_Rx <= 1'b0; 
        end
        else begin
            s0_Rs232_Rx <= Rs232_Rx;
            s1_Rs232_Rx <= s0_Rs232_Rx;
        end
        
    reg tmp0_Rs232_Rx,tmp1_Rs232_Rx; //数据寄存器     
    always@(posedge Clk or negedge Rst_n)//数据寄存器
        if(!Rst_n)begin
            tmp0_Rs232_Rx <= 1'b0;
            tmp1_Rs232_Rx <= 1'b0; 
        end
        else begin
            tmp0_Rs232_Rx <= s1_Rs232_Rx;
            tmp1_Rs232_Rx <= tmp0_Rs232_Rx;
        end


    assign nedege = !tmp0_Rs232_Rx && tmp1_Rs232_Rx;//标志接收到起始下降沿信号
    
//采样时钟生成模块

//过采样方式对接收到数据进行采样,采样频率为波特率的16倍
//分频计数最大值 = (系统时钟频率 / (波特率 * 16))- 1
    reg [15:0]bps_DR;//分频计数最大值
    always@(posedge Clk or negedge Rst_n)
        if(!Rst_n)
            bps_DR <= 16'd77;
        else begin
            case(baud_set)
                0:bps_DR <= 16'd77;//9600
                1:bps_DR <= 16'd38;//19200
//                2:bps_DR <= 16'd18;//38400
//                3:bps_DR <= 16'd13;//57600
//                4:bps_DR <= 16'd5;//115200
                default:bps_DR <= 16'd77; 
            endcase
        end
    
    reg [15:0]div_cnt;//采样时钟分频计数器
    always@(posedge Clk or negedge Rst_n)
        if(!Rst_n)
            div_cnt <= 16'd0;
        else if(uart_state)begin
            if(div_cnt == bps_DR)
                div_cnt <= 16'd0;
            else
                div_cnt <= div_cnt + 1'b1;
        end
        else
            div_cnt <= 16'd0;
            
    reg bps_clk;//采样时钟频率
    always@(posedge Clk or negedge Rst_n)
        if(!Rst_n)
            bps_clk <= 1'b0;
        else if(div_cnt == 16'd1)
            bps_clk <= 1'b1;
        else
            bps_clk <= 1'b0; 
                        
    reg [7:0]bps_cnt;//采样时钟计数器
    always@(posedge Clk or negedge Rst_n)
        if(!Rst_n) 
            bps_cnt <= 8'd0;
            else if(bps_cnt == 8'd159 || (bps_cnt == 8'd12 && (START_BIT > 2)))//StART_BIT大于2说明采集到干扰信号
            bps_cnt <= 8'd0;
        else if(bps_clk)
            bps_cnt <= bps_cnt + 1'b1;
        else
            bps_cnt <= bps_cnt;
        
    always@(posedge Clk or negedge Rst_n)//标志接收完成
        if(!Rst_n)
            Rx_Done <= 1'b0;
        else if(bps_cnt == 8'd159)
            Rx_Done <= 1'b1;
        else
            Rx_Done <= 1'b0;
            
//采样数据接收模块
    
    reg [2:0]r_data_byte[7:0];
    always@(posedge Clk or negedge Rst_n)
    if(!Rst_n)begin
        START_BIT <= 3'd0;
        r_data_byte[0] <= 3'd0; 
        r_data_byte[1] <= 3'd0;
        r_data_byte[2] <= 3'd0; 
        r_data_byte[3] <= 3'd0;
        r_data_byte[4] <= 3'd0; 
        r_data_byte[5] <= 3'd0;
        r_data_byte[6] <= 3'd0; 
        r_data_byte[7] <= 3'd0;
        STOP_BIT = 3'd0;
    end
    else if(bps_clk)begin
        case(bps_cnt)
            0:  begin
                    START_BIT <= 3'd0;
                    r_data_byte[0] <= 3'd0;
                    r_data_byte[1] <= 3'd0;
                    r_data_byte[2] <= 3'd0;
                    r_data_byte[3] <= 3'd0;
                    r_data_byte[4] <= 3'd0;
                    r_data_byte[5] <= 3'd0;
                    r_data_byte[6] <= 3'd0;
                    r_data_byte[7] <= 3'd0;
                    STOP_BIT <= 3'd0; 
                end
            6,7,8,9,10,11:START_BIT <= START_BIT + s1_Rs232_Rx;
            22,23,24,25,26,27:r_data_byte[0] <= r_data_byte[0] + s1_Rs232_Rx;
            38,39,40,41,42,43:r_data_byte[1] <= r_data_byte[1] + s1_Rs232_Rx;
            54,55,56,57,58,59:r_data_byte[2] <= r_data_byte[2] + s1_Rs232_Rx;
            70,71,72,73,74,75:r_data_byte[3] <= r_data_byte[3] + s1_Rs232_Rx;
            86,87,88,89,90,91:r_data_byte[4] <= r_data_byte[4] + s1_Rs232_Rx;
            102,103,104,105,106,107:r_data_byte[5] <= r_data_byte[5] + s1_Rs232_Rx;
            118,119,120,121,122,123:r_data_byte[6] <= r_data_byte[6] + s1_Rs232_Rx;
            134,135,136,137,138,139:r_data_byte[7] <= r_data_byte[7] + s1_Rs232_Rx;
            150,151,152,153,154,155:STOP_BIT <= STOP_BIT + s1_Rs232_Rx;
            default: 
                begin
                    START_BIT <= START_BIT;
                    r_data_byte[0] <= r_data_byte[0];
                    r_data_byte[1] <= r_data_byte[1];
                    r_data_byte[2] <= r_data_byte[2];
                    r_data_byte[3] <= r_data_byte[3];
                    r_data_byte[4] <= r_data_byte[4];
                    r_data_byte[5] <= r_data_byte[5];
                    r_data_byte[6] <= r_data_byte[6];
                    r_data_byte[7] <= r_data_byte[7];
                    STOP_BIT <= STOP_BIT; 
                end
        endcase
    end
    
    
//数据状态判定模块

    always@(posedge Clk or negedge Rst_n)//累加结果最高位为1表示接收结果为1,为0表示接收结果为0
        if(!Rst_n)
            data_byte <= 8'd0;
        else if(bps_cnt == 8'd159)begin
            data_byte[0] <= r_data_byte[0][2];
            data_byte[1] <= r_data_byte[1][2];
            data_byte[2] <= r_data_byte[2][2];
            data_byte[3] <= r_data_byte[3][2];
            data_byte[4] <= r_data_byte[4][2];
            data_byte[5] <= r_data_byte[5][2];
            data_byte[6] <= r_data_byte[6][2];
            data_byte[7] <= r_data_byte[7][2];
        end
        else begin
            data_byte[0] <= data_byte[0];
            data_byte[1] <= data_byte[1];
            data_byte[2] <= data_byte[2];
            data_byte[3] <= data_byte[3];
            data_byte[4] <= data_byte[4];
            data_byte[5] <= data_byte[5];
            data_byte[6] <= data_byte[6];
            data_byte[7] <= data_byte[7];
        end
            
        
    
    always@(posedge Clk or negedge Rst_n)//数据接收状态
	if(!Rst_n)
		uart_state <= 1'b0;
	else if(nedege)
		uart_state <= 1'b1;
	else if(Rx_Done || (bps_cnt == 8'd12 && (START_BIT > 2)))
		uart_state <= 1'b0;
	else
		uart_state <= uart_state;
    


endmodule

  • 接收数据蜂鸣器响应控制模块

控制RAM写入来自串口接收模块的数据,并用0.5s时钟控制RAM输出数据给蜂鸣器。

module rx_buzz_ctrl(
    input Clk,
    input Rst_n,
    input Rx_Done,
    input sw_2_buzzer,//高使能

    output wren,
    output reg rden,
    output reg[7:0]wraddress,
    output reg[7:0]rdaddress,
    output buzz_en,
    output reg ringout
);

    reg [23:0]cnt;
    reg Clk_0_5s;

    assign wren = Rx_Done;
    assign buzz_en = rden;
    
    always @(posedge Clk, negedge Rst_n)//读使能
        if (!Rst_n)
            rden <= 1'b0;
        else if (!ringout) begin
            rden <= 1'b1;
        end
        else
            rden <= 1'b0;
    
    always @(posedge Clk, negedge Rst_n)//写RAM
        if (!Rst_n)
            wraddress <= 8'd0;
        else if (wren)
            wraddress <= wraddress + 1'b1;
        else
            wraddress <= wraddress;
    
            
    always @(posedge Clk, negedge Rst_n)
        if (!Rst_n)
            cnt <= 24'd0;
        else if (cnt == 6_000_000 - 1) begin
            cnt <= 24'd0;
            Clk_0_5s <= 1'b1;
        end
        else begin
            cnt <= cnt + 1'b1;
            Clk_0_5s <= 1'b0;
        end
            
    always @(posedge Clk_0_5s, negedge Rst_n)//读RAM
        if (!Rst_n)
            rdaddress <= 8'd0;
        else if (rden) 
            if (rdaddress == wraddress)
                rdaddress <= 8'd0;
            else
                rdaddress <= rdaddress + 1'b1;
        else
            rdaddress <= 8'd0;
        
    always @(posedge Clk_0_5s, negedge Rst_n)//读RAM
        if (!Rst_n)
            ringout <= 1'b0;
        else if (rden)  
            if (rdaddress == wraddress && rdaddress != 8'd0) 
                ringout <= 1'b1;
            else 
                ringout <= ringout;
        else
            ringout <= ringout;
        

endmodule
  • 串口发送模块

将并行数据转化为串行数据再发送给上位机。

module uart_txd(
    Clk,
    Rst_n,
    baud_set,
    data_byte,
    Send_En,
    
    Rs232_Tx,//TXD
    Tx_Done,//标志数据传输完毕
    uart_state//标志数据正在发送
);

    input Clk;
    input Rst_n;
    input [2:0]baud_set;
    input [7:0]data_byte;
    input Send_En;

    output reg Rs232_Tx;
    output reg Tx_Done;
    output reg uart_state;
    
    
//波特率时钟生成模块

//分频计数最大值 == (系统时钟频率 / 波特率) - 1
    reg [15:0]bps_DR;//波特率分频计数最大值
    always @(posedge Clk or negedge Rst_n)
        if (!Rst_n)
            bps_DR <= 16'd1249;
        else begin
            case(baud_set)
                0:bps_DR <= 16'd1249; //9600bps 
                1:bps_DR <= 16'd624; //19200bps
                2:bps_DR <= 16'd311; //38400bps
                3:bps_DR <= 16'd207; //57600bps
                4:bps_DR <= 16'd103; //115200bps
                default:bps_DR <= 16'd1249; 
            endcase
        end
    
    reg [15:0]div_cnt;//波特率分频计数器
    always @(posedge Clk or negedge Rst_n)
        if (!Rst_n)
            div_cnt <= 16'd0;
        else if (uart_state) begin
            if (div_cnt == bps_DR)
                div_cnt <= 16'd0;
            else
                div_cnt <= div_cnt + 1'b1;
        end            
        else
            div_cnt <= 16'd0;
            
    reg bps_clk;//波特率时钟
    always @(posedge Clk or negedge Rst_n)
        if (!Rst_n)
            bps_clk <= 1'b0;
        else if (div_cnt == 16'd1)
            bps_clk <= 1'b1;
        else 
            bps_clk <= 1'b0;
            
            
//数据输出模块     
    reg [3:0]bps_cnt;//波特率时钟计数器
    always @(posedge Clk or negedge Rst_n)
        if (!Rst_n)
            bps_cnt <= 4'd0;
        else if (bps_cnt == 4'd11)
            bps_cnt <= 4'd0;
        else if (bps_clk)
            bps_cnt <= bps_cnt + 1'b1;
        else 
            bps_cnt <= bps_cnt;
    
    always @(posedge Clk or negedge Rst_n)//标志数据传输完毕
        if (!Rst_n)
            Tx_Done <= 1'b0;
        else if (bps_cnt == 4'd11)
            Tx_Done <= 1'b1;
        else 
            Tx_Done <= 1'b0;
            
    always @(posedge Clk or negedge Rst_n)//标志数据正在发送
        if (!Rst_n)
            uart_state <= 1'b0;
        else if (Send_En)
            uart_state <= 1'b1;
        else if (bps_cnt == 4'd11)
            uart_state <= 1'b0;
        else 
            uart_state <= uart_state;
    
    reg [7:0]r_data_byte;//数据发送寄存器
    always @(posedge Clk or negedge Rst_n)
        if (!Rst_n)
            r_data_byte <= 8'd0;
        else if (Send_En)
            r_data_byte <= data_byte;
        else 
            r_data_byte <= r_data_byte;
    
    
//数据传输状态控制模块
    localparam START_BIT = 1'b0;
    localparam STOP_BIT = 1'b1;
    always@(posedge Clk or negedge Rst_n)
        if(!Rst_n)
            Rs232_Tx <= 1'b1;
        else begin
            case(bps_cnt)
                0:Rs232_Tx <= 1'b1;
                1:Rs232_Tx <= START_BIT;
                2:Rs232_Tx <= r_data_byte[0];
                3:Rs232_Tx <= r_data_byte[1];
                4:Rs232_Tx <= r_data_byte[2];
                5:Rs232_Tx <= r_data_byte[3];
                6:Rs232_Tx <= r_data_byte[4];
                7:Rs232_Tx <= r_data_byte[5];
                8:Rs232_Tx <= r_data_byte[6];
                9:Rs232_Tx <= r_data_byte[7];
                10:Rs232_Tx <= STOP_BIT;
                default:Rs232_Tx <= 1'b1;
            endcase
        end    
    

endmodule

  • 定时报警发送温度数据控制模块

接收到timeout信号后控制串口发送模块将温度转换模块数据发送给上位机。

module timeout_tx_ctrl(
    input Clk,
    input Rst_n,
    input timeout,
    input Tx_Done,
    
    input [3:0]sec_l,
    input [3:0]sec_h,
    input [3:0]min_l,
    input [3:0]min_h,
    input [3:0]hour_l,
    input [3:0]hour_h,
    
    input [3:0]temp_h,
    input [3:0]temp_l,
    input [3:0]temp_s,
    
    output reg Send_En,
    output reg[7:0]tx_data
);
    reg timeout_flag;
    reg send_out;
    reg [5:0]cnt;//42
    
    initial send_out = 1'b0;
    
    always @(posedge Clk, negedge Rst_n)
        if (!Rst_n)
            timeout_flag <= 1'b0;
        else if (timeout)
            timeout_flag <= 1'b1;
        else if (send_out)
            timeout_flag <= 1'b0;
        else
            timeout_flag <= timeout_flag;
            
    always @(posedge Clk, negedge Rst_n)
        if (!Rst_n)
            cnt <= 6'd0;
        else if (cnt == 6'd45)
            cnt <= 6'd0;
        else if (Tx_Done)
            cnt <= cnt + 1'b1;        
        else
            cnt <= cnt;
          
    always @(posedge Clk, negedge Rst_n)
        if (!Rst_n)
            Send_En <= 1'b0;
        else if (timeout_flag && !send_out)
            Send_En <= 1'b1;        
        else 
            Send_En <= 1'b0;
        
    always @(posedge Clk, negedge Rst_n)
        if (!Rst_n) begin
            tx_data <= 8'h00;
            send_out <= 1'b0;
        end
        else if (timeout_flag)
            case (cnt)
                0: tx_data <= 8'hB5;//当
                1: tx_data <= 8'hB1;
                2: tx_data <= 8'hC7;//前
                3: tx_data <= 8'hB0;
                4: tx_data <= 8'hCA;//时
                5: tx_data <= 8'hB1;
                6: tx_data <= 8'hBC;//间
                7: tx_data <= 8'hE4;
                8: tx_data <= 8'hCE;//为
                9: tx_data <= 8'hAA;    
                10: tx_data <= 8'hA3;//:
                11: tx_data <= 8'hBA;
                12: tx_data <= 8'h20;// 
                13: tx_data <= 8'h30 + hour_h;
                14: tx_data <= 8'h30 + hour_l;
                15: tx_data <= 8'hA3;//:
                16: tx_data <= 8'hBA;
                17: tx_data <= 8'h30 + min_h;
                18: tx_data <= 8'h30 + min_l;
                19: tx_data <= 8'hA3;//:
                20: tx_data <= 8'hBA;
                21: tx_data <= 8'h30 + sec_h;
                22: tx_data <= 8'h30 + sec_l;
                23: tx_data <= 8'h0D;//\r
                24: tx_data <= 8'h0A;//\n                
                
                26: tx_data <= 8'hB5;//当
                27: tx_data <= 8'hB1;
                28: tx_data <= 8'hC7;//前
                29: tx_data <= 8'hB0;
                30: tx_data <= 8'hCE;//温
                31: tx_data <= 8'hC2;
                32: tx_data <= 8'hB6;//度
                33: tx_data <= 8'hC8;
                34: tx_data <= 8'hCE;//为
                35: tx_data <= 8'hAA;    
                36: tx_data <= 8'hA3;//:
                37: tx_data <= 8'hBA;
                38: tx_data <= 8'h0D;//
                39: tx_data <= 8'h30 + temp_h;
                40: tx_data <= 8'h30 + temp_l;
                41: tx_data <= 8'h2E;//.
                42: tx_data <= 8'h30 + temp_s;
                43: tx_data <= 8'h0D;//\r
                44: tx_data <= 8'h0A;//\n
                45: begin tx_data <= 8'h00; send_out <= 1'b1;end
                default: tx_data <= 8'h00;
            endcase
        else
            tx_data <= 8'h00;
            
            

        

endmodule

遇到的困难及解决方法:

  • 串口发送和接收模块的编写。

通过网上浏览大量资料写出串口发送和接收模块。

  • 编程过程中遇到的时序问题。

通过网上寻找资料解决。

  • 写完代码下载到板子上发现有许多BUG。

通过Modelsim仿真和仔细查找代码的方法解决一些列BUG。

意见与建议:

  • 希望下一代产品的硬件资源方面能提升,逻辑单元较少,以至于代码写到一半逻辑单元就用光了,只好优化代码,体会到为什么嵌入式工程师编程需考虑硬件的资源分配。
  • 加入更多的各种接口。

心得体会:

  • 第一次接触FPGA,知道了硬件编程和软件编程有很大的不同。用Verilog编程不能完全用C编程的思维去思考,而是需要用硬件的思维去思考,头脑里先要对解决的问题有电路的抽象行为级建模,再将电路模型映射到Verilog代码上。还需要考虑一系列的时序问题,也学会了Moelsim仿真的使用。
  • 通过这个项目让我对所学的知识有了更深的认知,同时也在学习中收获了快乐,提升了对电子学习的兴趣。

附件下载

STEP_Project.sof
FPGA程序烧录sof文件
Two Tigers.txt
两只老虎音乐16进制编码文件
STEP_Project.rar
工程压缩文件

团队介绍

成都信息工程大学
团队成员
曹坤
一个很酷的人

评论

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