寒假一起练 FPGA
寒假一起练 FPGA项目,很有幸年假期间参与了电子森林的这次活动,这个主题帖主要展示项目介绍和功能源码分享。
标签
FPGA
Argon
更新2021-02-26
994

# 题目要求

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

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

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

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

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

任务分析和分解

任务主要分为上位机和下位机(FPGA)两个部分,上位机拟采用Python+QT 形式编写,为了力求任务快速完成FPGA部分拟采用软核形式 为了能更多的学习FPGA编程这里采用Verilog 状态机形式进行代码编写 。为了不重复造轮子在编写逻辑时候尽量复用已有的代码。

FPGA部分功能模块划分

设计思路串口模块

串口模块借鉴了正点原子的串口发送和串口接收(具体实现原理可以参考正点原子启明星开发教程),只需要关注串口发送和接收时的标志位和数据寄存器即可。程序接口如下所示:

//串口接收模块
module uart_recv(
   input  sys_clk,                  //系统时钟
   input             sys_rst_n,                //系统复位,低电平有效
   
   input             uart_rxd,                 //UART接收端口
   output  reg       uart_done,                //接收一帧数据完成标志
   output  reg [7:0] uart_data                 //接收的数据
   );

//串口发送模块
module uart_send(
input      sys_clk,                  //系统时钟
input         sys_rst_n,                //系统复位,低电平有效

input         uart_en,                  //发送使能信号
input  [7:0]  uart_din,                 //待发送数据
output        uart_tx_busy,             //发送忙状态标志      
output  reg   uart_txd                  //UART发送端口
);
   
parameter  CLK_FREQ = 50000000;                //系统时钟频率
parameter  UART_BPS = 115200;                    //串口波特率    

蜂鸣器模块

题目要求需要输出音乐,通过简单的乐理只是可以知道音乐分为:音调、拍子两个部分,所谓的音调就是频率,拍子就是频率持续的时间,如下图所示。因此只需要设计一路逻辑输出频率可变的方波,一路输出时间控制的信号,二者进行调制 叠加之后就是我们所需的音频信号。(简单的乐理知识可以参考:https://www.bilibili.com/video/BV1Xi4y1b7De?from=search&seid=15935986860093005407

image-20210225130933358

 

image-20210225130917652

这里直接复用了电子森林提供的音调蜂鸣器的代码,并在外部设计了时间控制的代码逻辑,部分逻辑代码如下所示:

/*************************
Beep音调控制
*************************/
reg [17:0] time_cnt;
//当蜂鸣器使能时,计数器按照计数终值(分频系数)计数
always@(posedge clk_in or negedge rst_n_in) begin
if(!rst_n_in) begin
time_cnt <= 1'b0;
end else if(!tone_en) begin
time_cnt <= 1'b0;
end else if(time_cnt>=time_end) begin
time_cnt <= 1'b0;
end else begin
time_cnt <= time_cnt + 1'b1;
end
end

//根据计数器的周期,翻转蜂鸣器控制信号
always@(posedge clk_in or negedge rst_n_in) begin
if(!rst_n_in) begin
piano_out <= 1'b0;
end else if(time_cnt==time_end) begin
piano_out <= ~piano_out; //蜂鸣器控制输出翻转,两次翻转为1Hz
end else begin
piano_out <= piano_out;
end
end

/*************************
Beep拍子控制
*************************/
RUN:begin
case (cnt_run)
7'd0:begin
music_tone <= note[music_cnt][7:3];
cnt_run<=cnt_run+1'b1;
end
7'd1:begin
if(note[music_cnt][2:0] == 4'd1)
music_delay <= 16'd200;
else if(note[music_cnt][3:0] == 4'd2)
music_delay <= 16'd500;
else if(note[music_cnt][3:0] == 4'd3)    
music_delay <= 16'd1000;
else if(note[music_cnt][3:0] == 4'd4)    
music_delay <= 16'd2000;
else if(note[music_cnt][3:0] == 4'd5)    
music_delay <= 16'd4000;  
state <= DELAY; state_back<=RUN;
cnt_run<=cnt_run+1'b1;
end
7'd2:begin
if(music_cnt == 8'd95)          //播放完成
music_cnt <= 8'd0;
else begin
music_cnt <= music_cnt + 1'b1;
cnt_run <= 7'd0;
end
end
endcase
end
DELAY:begin
if(clk_cnt == 16'd12_000) begin
clk_cnt <= 16'b0;
if(cnt_delay >= music_delay) begin
cnt_delay <= 16'd0;
state <= state_back;
end
else
cnt_delay <= cnt_delay + 1'b1;
end
else begin
clk_cnt <= clk_cnt + 1'b1;
end
end

按键部分

按键部分分为两个功能模块,按键消抖和按键逻辑功能的处理。按键消抖原理检测到按键触发之后延时一段时间再次检测,如下图所示。将消抖后的状态值存放在寄存器中供按键逻辑处理函数查询。

image-20210224230125634

always @ (posedge sys_clk or negedge sys_rst_n)
   begin
       if (!sys_rst_n)
           begin
               key_rst <= 1'b1;
               key_rst_pre <= 1'b1;
           end
       else
           begin
               key_rst <= key;
               key_rst_pre <= key_rst;
           end
   end

assign key_edge = key_rst_pre & (~key_rst);
reg [17 : 0] cnt;
always @(posedge sys_clk or negedge sys_rst_n)
   begin
       if (!sys_rst_n)
           cnt <= 18'b0;
       else if (key_edge)
           cnt <= 18'h0;
       else
           cnt <= cnt + 18'd1;
   end

reg key_sec_pre; //延迟后检测电平,寄存器前一个值
reg key_sec; //延迟后的检测电平,寄存器当前值
always @(posedge sys_clk or negedge sys_rst_n)
   begin
       if (!sys_rst_n)
           begin
               key_sec <= 1'b1;
           end
       else if (cnt == 18'h3ffff)
           begin
               key_sec <= key;
           end
   end
always @(posedge sys_clk or negedge sys_rst_n)
   begin
       if (!sys_rst_n)
           begin
               key_sec_pre <= 1'b1;
           end
       else
           key_sec_pre <= key_sec;
   end
assign key_pulse = key_sec_pre & (~key_sec);

DS18B20 模块

DS18B20是一颗基于 1-wire 协议的单总线数组温度传感器,这里我们直接复用电子森林的DS18B20驱动对温度进行采集。

always@(posedge clk_1mhz or negedge rst_n_in) begin
   if(!rst_n_in) 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            //skip rom
                   4'd2: begin data_wr <= 8'h44;state <= WRITE; end            //convert
                   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     //500us reset
                   3'd2: begin one_wire_buffer <= 1'bz; end
                   3'd3: begin num_delay <= 20'd100;state <= DELAY;state_back <= INIT; end     //100us checkout
                   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     //400us
                   3'd6: begin state <= MAIN; end                                              //返回main函数
                   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;

BCD 逻辑模块

因为本次使用的这款FPGA没有除法的功能因此我们在OLED上进行数据显示的时候不能直接对数据进行除法和余数操作来获取数据的个位十位百位,这里需要使用移位加三的方式来完成数据位的取出,这里我们将其封装成BCD模块的形式在需要使用数据的时候直接进行调用。具体的移位加三逻辑可以参考:https://www.cnblogs.com/SummerSunnyDay/p/5013835.html

module BCD (
input [7 : 0] binary, //输入二进制,八位
output reg[3 : 0] hundres, //百位
output reg[3 : 0] tens, //十位
output reg[3 : 0] ones //个位
);

integer i;

always @(binary)
begin
hundres = 4'd0;
tens = 4'd0;
ones = 4'd0;

for (i = 7; i >= 0; i = i -1)
begin

if (hundres >= 5)
hundres = hundres + 3;
if (tens >= 5)
tens = tens + 3;
if (ones >= 5)
ones = ones + 3;

hundres = hundres << 1'b1;
hundres[0] = tens[3];
tens = tens << 1'b1;
tens[0] = ones[3];
ones = ones << 1'b1;
ones[0] = binary[i];
end
end

endmodule

OLED模块

OLED 模块分为OLED指令发送、逻辑操作和SPI协议数据传输三个部分,前两个部分移植了STM32上的OLED程序初始化和指令发送,这里需要注意0.91寸OLED和0.96寸OLED在初始化的时候有些许不同,详细区别可以参考这篇博客:https://blog.csdn.net/p1279030826/article/details/107234646。SPI部分代码如下:

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

时间计数模块

时间计数是通过将时钟脉冲进行计数,当脉冲达到秒计数次数的时候对秒进行加一,然后秒计数到达60之后对分计数进行加一,以此类推。详细的逻辑实现如下:

    always @(posedge clk_1s or negedge sys_rst_n) begin//计时模块
if (!sys_rst_n) begin
sec <= 8'd0;
min <= 8'd0;
hour <= 8'd0;
end else begin
if (key_mode) begin
sec <= sec_t;
min <= min_t;
hour <= hour_t;
end else begin
if (sec >= 59) begin
sec <= 1'b0;
min <= min + 1'b1;
end else
sec <= sec + 1'b1;
if (min >= 59) begin
min <= 1'b0;
hour <= 1'b1;
end
if (hour >= 23)
hour <= 1'b0;
end
end
end

定时设置模块

定时设置模块采用了一种影子寄存器的设计思路,定时寄存器作为走时寄存器的影子寄存器。只有设置拨码开关SW1为ON时候才可以对定时寄存器进行修改。

always @(posedge sys_clk or negedge sys_rst_n) begin
if (!sys_rst_n) begin
key_mode <= 3'd0;
time_set <= 1'b0;
hour_t <= 8'd0;
min_t <= 8'd0;
sec_t <= 8'd0;
min_warning <= 8'd1;
hour_warning <= 8'd0;
min_waning_t <= 8'd1;
hour_waning_t <= 8'd0;
TEM_high <= 8'd33;
end else begin
if (key1_pulse) begin
if(sw1 == 0)//判断SW1处于走时/报警设定
if(key_mode <= 2) begin
key_mode <= key_mode + 1'b1;
time_set <= 1'b1;
end else begin//走时
key_mode <= 3'd0;
time_set <= 1'b0;
end else begin
if (key_mode <= 1) begin//报警
key_mode <= key_mode + 1'b1;
time_set <= 1'b1;
end else begin
key_mode <= 3'd0;
time_set <= 1'b0;
end
end
end else
if (key2_pulse) begin//加
case (key_mode)
3'd1: begin
if(sw1 == 0) begin
hour_t <= hour_t + 1'b1;
if (hour_t >=23)
hour_t <= 0;
end else begin
hour_warning <= hour_warning + 1'b1;
if (hour_warning >=23)
hour_warning <= 0;
end
end
3'd2: begin
if(sw1 == 0) begin
min_t <= min_t + 1'b1;
if (min_t >= 59)
min_t <= 0;
end else begin
min_warning <= min_warning + 1'b1;
if (min_warning >= 59)
min_warning <= 0;
end
end
3'd3: begin
if(sw1 == 0) begin
sec_t <= sec_t + 1'b1;
if (sec_t >= 59)
sec_t <= 0;
end
end
endcase
end else
if (key3_pulse) begin//减
case (key_mode)
3'd1: begin
if(sw1 == 0) begin
hour_t <= hour_t - 1'b1;
if (hour_t <= 0)
hour_t <= 0;
end else begin
hour_warning <= hour_warning - 1'b1;
if (hour_warning <= 0)
hour_warning <= 0;
end
end
3'd2: begin
if(sw1 == 0) begin
min_t <= min_t - 1'b1;
if (min_t <= 0)
min_t <= 0;
end else begin
min_warning <= min_warning - 1'b1;
if (min_warning <= 0)
min_warning <= 0;
end
end
3'd3: begin
if(sw1 == 0) begin
sec_t <= sec_t - 1'b1;
if (sec_t <= 0)
sec_t <= 0;
end
end
endcase
end else
if (!key_mode) begin
sec_t <= sec;
min_t <= min;
hour_t <= hour;
min_waning_t <= min_warning;
hour_waning_t <= hour_warning;
end
end
end

上位机部分:

上位机部分通过PyQt进行编写,编译器使用的是VScode。环境搭建可以参考:

https://blog.csdn.net/u010261063/article/details/106434776

项目总结项目回顾:

项目中遇到了如下问题,以及解决方案:

  1. OLED占用资源较大,ROM控件不够用。解决方法:通过对数字0-9和部分字符进行取模处理减小对ROM的占用。

  2. 各个模块之间无法想C语言一样进行嵌套调用。解决方法:对各个模块的出入参数在Top模块中进行分发实现类似嵌套调用的逻辑。

  3. DS18B20测量温度偏高。解决思路:将DS18B20的IO引出通过STM32进行测量发现温度偏高是因为FPGA发热较大导致的。

  4. 编写逻辑后进行例化,例化之后IO管脚中的OUTPUT管脚配置消失。解决方法:经过群友的指点,明白了在逻辑中如果OUTPUT IO没有关联到真正的输入逻辑的话 在IO配置表中 此IO会变为去使能的状态。

感悟:

之前一直是做SOC开发没有涉足过FPGA的开发,通过年假期间的时间突击了一下FPGA的知识,开发了本次这个小项目。项目中依然存在很多的问题,例如所有状态判断都是每个时钟都进行判断对资源占用较大,后期有机会可以对其进行优化对不需要实时判断的逻辑做优化处理。

FPGA相对于单片机灵活性更高,因此实现的方式也更多。后面又机会会去涉猎下ZYNQ,通过linux和fpga核心的配合做出功能更加丰富的小玩意。

感谢我的好友@番茄红薯、@可乐可口 在项目的合作,同时感谢@。。。。(叶同学) @Andy大佬在我遇到问题时候提供的解决方案。

工程代码已经上传到Github,有兴趣的同学可以clone研究。(https://github.com/songzhishuo/Step_verilog

团队介绍
因为工作和学业繁忙,所以我们采用了分工的方式进行了本项目,我负责项目的规划统筹、QT上位机开发、驱动部分开发、田鹏飞同学负责应用逻辑分编写、徐加乐同学负责驱动部分的开发。
团队成员
田鹏飞
哈尔滨工程大学 水声学院 研一学生
徐加乐
黑龙江大学 信息工程学院 研一
评论
0 / 100
查看更多
目录
硬禾服务号
关注最新动态
0512-67862536
info@eetree.cn
江苏省苏州市苏州工业园区新平街388号腾飞创新园A2幢815室
苏州硬禾信息科技有限公司
Copyright © 2023 苏州硬禾信息科技有限公司 All Rights Reserved 苏ICP备19040198号