天天看點

Verilog HDL實作智能藥盒

Verilog HDL實作智能藥盒

文章目錄

  • ​​Verilog HDL實作智能藥盒​​
  • ​​一、簡介​​
  • ​​二、代碼​​
  • ​​1、主檔案(主子產品的代碼)​​
  • ​​2、主檔案中使用的按鍵消抖子產品​​
  • ​​3、主檔案中使用的時鐘分頻子產品​​
  • ​​三、後續操作​​
  • ​​四、實驗現象​​
  • ​​五、總結​​

一、簡介

Verilog HDL實作智能藥盒

我們知道,目前,老年人的數量是比較多的,進而導緻了照顧老年人成為了一個比較大的社會問題。由于老年人的記憶力相比之前可能是會有所下降的,而且與此同時,由于老年人的子女們也有上班工作等一些其他的生活的需求,是以說,老年人的身邊并不是總能有人在陪伴的,這就導緻了一些社會性的問題。例如,很多老年人随着年齡的增長,會伴随着健忘的問題。對于老年人來說,在生病的時候,甚至是日常生活中按時吃藥都是一個難題。如果需要在不同時間吃不同的藥,幾乎很難獨立完成。而設計實作一個具有計時提醒功能的智能藥盒就是為這些老年人所設計的。

我們前面已經說了,其中的問題之一,就是老年人如果生病需要吃藥的話,很有可能會由于自己的疏忽,忘記了吃藥這件事,而同時身邊也沒有人來即是的提醒,進而會使得老年人錯過正确的吃藥的時間,這就有可能對老年人的病情産生一些不利的影響,更嚴重的話,甚至有可能會帶來生命危險的。是以,我們需要制作一種可以自動的提醒老年人進行在設定的時間去吃藥的操作的一種智能藥盒。這種藥盒,我們在設計的時候,可以一次性設定最多三種藥的吃藥時間,對老年人進行提醒吃藥的操作,如果老年人吃過了藥,那麼提醒的警報就會消失,否則,會一直進行提醒。這樣一來就可以在正确的時間來提醒老年人吃藥,進而減小了錯過吃藥的時間的可能性了,更好的保證了老年人的安全與健康。

本實驗旨在設計一種模拟的智能藥盒,主要功能是進行模拟智能藥盒的設定提醒時間以及定時的提醒吃藥的功能。也就是說,本實驗模拟的藥盒可以實作定時的操作,設計的時候由于器件等原因,一次最多可以設定三種藥的吃藥的時間,然後在啟動裝置以後,裝置就會自動的計時,等達到了某一種藥的吃藥的時間的時候,系統就會自動提醒吃藥啦,吃過藥以後,提醒關閉。就這樣的一直循環下去,直到人為的關閉系統為止。當系統再一次啟動的時候,仍然可以重新設定定時以及後面的提醒工作可以正常的進行。

Verilog HDL實作智能藥盒

由于後面内容比較多,這裡先來一個現象:

​​https://www.bilibili.com/video/BV1MY411x7Xw?spm_id_from=333.999.0.0​​

Verilog HDL實作智能藥盒

下面開始正式的項目的制作了啦:

二、代碼

1、主檔案(主子產品的代碼)

代碼比價多、比較長,但是裡面有詳細的注釋。

module test_little_foot(clk,rst,btn1,btn2,btn3,btn4,btn5,btn6,
                        rled1, rled2, rled3,rled4,seg_led_1,seg_led_2,
                        row, col);// row, col 是點陣的設定。
                        
                        // 進行小腳丫上面的所有的代碼的一個整合啦。
                        
   output   reg   [7:0]   row;
   output   reg   [7:0]   col;
   // Dot Matrix.---點陣。
   
   localparam   s0 = 3'd0, s1 = 3'd1, s2 = 3'd2, s3 = 3'd3,
         s4 = 3'd4, s5 = 3'd5, s6 = 3'd6, s7 = 3'd7;  
         // 這個狀态是進行掃描的時候使用的啦。
         
   // Dot Matrix 顯示的時候所需要的控制狀态。
   
      
   reg   [12:0]   cnt;
   always @(posedge clk or negedge rst)
      if(!rst) cnt <= 1'b0;
      else if(cnt >= 13'd7499) cnt <= 1'b0;
      else cnt <= cnt + 1'b1;

   reg   clk_800hz;
   // 掃描的頻率是 800 Hz。
   always @(posedge clk or negedge rst)
      if(!rst) clk_800hz <= 1'b0;
      else if(cnt == 13'd7499) clk_800hz <= ~clk_800hz;
      // 時鐘分頻。
      else clk_800hz <= clk_800hz;
      
// 産生掃描的頻率哦。

//reg   [12:0]   cnt;
//always @(posedge clk or negedge rst_n)
//   if(!rst_n) cnt <= 1'b0;
//   else if(cnt >= 13'd7499) cnt <= 1'b0;
//   else cnt <= cnt + 1'b1;
//
//reg   clk_800hz;
//always @(posedge clk or negedge rst_n)
//   if(!rst_n) clk_800hz <= 1'b0;
//   else if(cnt == 13'd7499) clk_800hz <= ~clk_800hz;
//   else clk_800hz <= clk_800hz;
//   
   
   input clk;
   // 時鐘信号。
   input rst;
   // 重置信号,在2.0版本無使用。
   
   input btn1;  
   // 設定盒子開機。
   input btn2;
   // 設定三個藥箱中具體操作哪一個。
   
   input btn3;  // 按下一次,時間加一秒;
   input btn4;  // 按下一次,時間減一秒。
   // 設定時間。
   
   input btn5;
   // 設定完畢時間以後,需要有一個按鍵來開啟裝置的工作狀态。
   
   input btn6;
   // 關閉挺提醒的警報的按鍵。
   
   output rled1;       
   output rled2;  
   output rled3;
   output rled4;
   // 輸出四個led燈,前三個是對應于三個藥盒,第四個原本要接蜂鳴器,但是小腳丫沒有。
   
   reg led1;       
   reg led2;  
   reg led3; 
   reg led4 = 1;
   //   led4 = 1,表示在沒有正式進入工作轉态的時候,是處于熄滅的狀态。
   // 記錄在沒有正式進入工作狀态的時候的信号。
   
   reg led10;       
   reg led20;  
   reg led30; 
   reg led40;
   // 記錄在工作狀态下的時候的信号。
   
   reg times;
   // 設定一個數字來判斷是否需要進行 counting 置零的操作啦。
   
   reg times0;
   // 設定一個數字來判斷是否進行了置零的操作了。
   
   wire times_ = times;
   // 連線相接。
   
   wire times0_ = times0;
   // 連線相接。
   
   output [8:0] seg_led_1;
   output [8:0] seg_led_2;
   // 數位管的顯示。
   
   wire key_pulse1;
   wire key_pulse2;
   wire key_pulse3;
   wire key_pulse4;
   wire key_pulse5;
   wire key_pulse6;
   // 對應于 6 個按鍵的按鍵消抖的輸出線。
   
   reg state_of_start_or_nor = 0;
   // 判斷是否開機了。
   
   reg really_start = 0;
   // 判斷是否進行提醒吃藥的功能了。
   
   reg [3:0] location = 1'd0;
   // 記錄設定時間的時候的位置。
   
   wire clkout1;
   wire clkout2;
   wire clkout3;
   wire clkout4;
   // 有四個時鐘分頻,是以有四個輸出。
   
   reg [1:0] led_position = 2'b11;
   // 判斷是哪一個藥盒需要進行閃爍以及蜂鳴器的提醒。
   
   reg [3:0] seg_data_1= 1'd0;
   reg [3:0] seg_data_2= 1'd0;
   // 記錄沒有正式工作的時候的數位管對應的需要顯示的數字。
   
   reg [3:0] seg_data_10= 1'd0;
   reg [3:0] seg_data_20= 1'd0;
   // 記錄正式開始工作以後的數位管對應的所需要顯示的數字。
   
   reg [8:0] seg [10:0];      
   // 這個是數位管的顯示部分内容,我們會在初始化的之後直接給它指派。
   
   reg [5:0] first_medicine;
   reg [5:0] second_medicine;
   reg [5:0] third_medicine;
   // 存儲服藥的時間,因為有三個藥箱,是以需要三個資料進行寄存。
   
   reg [5:0] counting;
   // 計時器的時間的記錄,這個是一秒計一次數字。
   
   
   initial                                            
      begin
         seg[0] = 9'h3f;                                           
         // 對存儲器中第一個數指派9'b00_0011_1111,相當于共陰極接地,DP點變低不亮;
         // 7段顯示數字  0。
         seg[1] = 9'h06;                                           
         // 7段顯示數字  1。
         seg[2] = 9'h5b;                                           
         // 7段顯示數字  2。
         seg[3] = 9'h4f;                                           
         // 7段顯示數字  3。
         seg[4] = 9'h66;                                           
         // 7段顯示數字  4。
         seg[5] = 9'h6d;                                           
         // 7段顯示數字  5。
         seg[6] = 9'h7d;                                           
         // 7段顯示數字  6。
         seg[7] = 9'h07;                                           
         // 7段顯示數字  7。
         seg[8] = 9'h7f;                                           
         // 7段顯示數字  8。
         seg[9] = 9'h6f;                                           
         // 7段顯示數字  9。
         
         seg[10] = 9'b0_0111_0110;
         // 顯示H,表示說明是處于關機的狀态,為什麼是H呢,因為我的名字的首字母是H。
       end
   
   initial
      begin
         first_medicine <= 2'd0;
         second_medicine <= 2'd0;
         third_medicine <= 2'd0;
         // 讓初始的三個計數都是零。
         
         counting <= 2'd00;
         
         times <= 0;
         times0 <= 0;
      end
   reg [63:0] mem___;
   
   // 設定點陣什麼都不顯示的狀态。(初始狀态,以及已經吃過藥的狀态。)
   initial
   begin
      mem___={8'b0000_0000, 
               8'b0000_0000, 
               8'b0000_0000, 
               8'b0000_0000, 
               8'b0000_0000, 
               8'b0000_0000, 
               8'b0000_0000, 
               8'b0000_0000};
               // 所有的點都處于熄滅的狀态。
   end
   
   // Dot Matrix 有關的顯示
   reg   [63:0]   mem;
   reg   [63:0]   mem1;
   reg   [63:0]   mem2;
   reg   [63:0]   mem3;
   // 開機了,但是沒有正式開始工作時的閃爍現象。

   initial
    begin
    
    
    // 初始狀态。
    
    
      mem1={8'b0001_1000, 
               8'b0001_1000, 
               8'b0000_0000, 
               8'b0001_1000, 
               8'b0001_1000, 
               8'b0000_0000, 
               8'b0001_1000, 
               8'b0001_1000};
               // 第一個燈
               // 所有的該亮的顯示燈都涼亮了。
               
      mem2={8'b0001_1000, 
               8'b0001_1000, 
               8'b0000_0000, 
               8'b0001_1000, 
               8'b0001_1000, 
               8'b0000_0000, 
               8'b0001_1000, 
               8'b0001_1000};
               // 第二個燈
      mem3={8'b0001_1000, 
               8'b0001_1000, 
               8'b0000_0000, 
               8'b0001_1000, 
               8'b0001_1000, 
               8'b0000_0000, 
               8'b0001_1000, 
               8'b0001_1000};
               // 第三個燈
   end

   always@(posedge clk)
   begin
   // 通過時鐘信号來控制,高電平的時候燈滅,低電平的時候燈亮,進而實作閃爍。
      if(clkout1)
      // 頻率是 2 Hz
      
      begin
      
      
      // 熄滅狀态。
      
      
         mem1={8'b0000_0000, 
               8'b0000_0000, 
               8'b0000_0000, 
               8'b0001_1000, 
               8'b0001_1000, 
               8'b0000_0000, 
               8'b0001_1000, 
               8'b0001_1000};
               // 第一個燈
      mem2={8'b0001_1000, 
               8'b0001_1000, 
               8'b0000_0000, 
               8'b0000_0000, 
               8'b0000_0000, 
               8'b0000_0000, 
               8'b0001_1000, 
               8'b0001_1000};
               // 第二個燈
      mem3={8'b0001_1000, 
               8'b0001_1000, 
               8'b0000_0000, 
               8'b0001_1000, 
               8'b0001_1000, 
               8'b0000_0000, 
               8'b0000_0000, 
               8'b0000_0000}; 
               // 第三個燈
      end
      
      else
      begin
      
      
      // 恢複狀态。
      
      
         mem1={8'b0001_1000, 
               8'b0001_1000, 
               8'b0000_0000, 
               8'b0001_1000, 
               8'b0001_1000, 
               8'b0000_0000, 
               8'b0001_1000, 
               8'b0001_1000};
               // 第一個燈
      mem2={8'b0001_1000, 
               8'b0001_1000, 
               8'b0000_0000, 
               8'b0001_1000, 
               8'b0001_1000, 
               8'b0000_0000, 
               8'b0001_1000, 
               8'b0001_1000};
               // 第二個燈
      mem3={8'b0001_1000, 
               8'b0001_1000, 
               8'b0000_0000, 
               8'b0001_1000, 
               8'b0001_1000, 
               8'b0000_0000, 
               8'b0001_1000, 
               8'b0001_1000};
               // 第三個燈
      end
   end
   
   
   reg   [63:0]   mem0;
   reg   [63:0]   mem10;
   reg   [63:0]   mem20;
   reg   [63:0]   mem30;
   // 正式開始工作的時候的閃爍的現象。

   initial
    begin
    
    
    // 初始狀态。
    
    
      mem10={8'b0001_1000, 
               8'b0001_1000, 
               8'b0000_0000, 
               8'b0001_1000, 
               8'b0001_1000, 
               8'b0000_0000, 
               8'b0001_1000, 
               8'b0001_1000};
               // 第一個燈
      mem20={8'b0001_1000, 
               8'b0001_1000, 
               8'b0000_0000, 
               8'b0001_1000, 
               8'b0001_1000, 
               8'b0000_0000, 
               8'b0001_1000, 
               8'b0001_1000};
               // 第二個燈
      mem30={8'b0001_1000, 
               8'b0001_1000, 
               8'b0000_0000, 
               8'b0001_1000, 
               8'b0001_1000, 
               8'b0000_0000, 
               8'b0001_1000, 
               8'b0001_1000};
               // 第三個燈
   end

   always@(posedge clk)
   begin
   // 也是更上面的那個一樣的道理。
      if(clkout3)
      // 這個是 4 Hz 的頻率。
      
      begin
      
      
      // 熄滅狀态。
      
      
         mem10={8'b0000_0000, 
               8'b0000_0000, 
               8'b0000_0000, 
               8'b0001_1000, 
               8'b0001_1000, 
               8'b0000_0000, 
               8'b0001_1000, 
               8'b0001_1000};
               // 第一個燈
      mem20={8'b0001_1000, 
               8'b0001_1000, 
               8'b0000_0000, 
               8'b0000_0000, 
               8'b0000_0000, 
               8'b0000_0000, 
               8'b0001_1000, 
               8'b0001_1000};
               // 第二個燈
      mem30={8'b0001_1000, 
               8'b0001_1000, 
               8'b0000_0000, 
               8'b0001_1000, 
               8'b0001_1000, 
               8'b0000_0000, 
               8'b0000_0000, 
               8'b0000_0000}; 
               // 第三個燈
      end
      
      else
      begin
      
      
      // 恢複狀态。
      
      
         mem10={8'b0001_1000, 
               8'b0001_1000, 
               8'b0000_0000, 
               8'b0001_1000, 
               8'b0001_1000, 
               8'b0000_0000, 
               8'b0001_1000, 
               8'b0001_1000};
               // 第一個燈
      mem20={8'b0001_1000, 
               8'b0001_1000, 
               8'b0000_0000, 
               8'b0001_1000, 
               8'b0001_1000, 
               8'b0000_0000, 
               8'b0001_1000, 
               8'b0001_1000};
               // 第二個燈
      mem30={8'b0001_1000, 
               8'b0001_1000, 
               8'b0000_0000, 
               8'b0001_1000, 
               8'b0001_1000, 
               8'b0000_0000, 
               8'b0001_1000, 
               8'b0001_1000};
               // 第三個燈
      end
   end


   
   // 按鍵消抖,需要給使用到的 6 個按鍵都進行消抖,這裡rst我們暫時沒有用到,也許後續擴充的時候會使用。
   debounce_button  u1 (                               
                 .clk (clk),
                 .rst (rst),
                 .key (btn1),
                 .key_pulse (key_pulse1)
                 );
                 
   debounce_button  u2 (                               
                 .clk (clk),
                 .rst (rst),
                 .key (btn2),
                 .key_pulse (key_pulse2)
                 );
                 
   debounce_button  u3 (                               
                 .clk (clk),
                 .rst (rst),
                 .key (btn3),
                 .key_pulse (key_pulse3)
                 );
                 
   debounce_button  u4 (                               
                 .clk (clk),
                 .rst (rst),
                 .key (btn4),
                 .key_pulse (key_pulse4)
                 );
                 
   debounce_button  u5 (                               
                 .clk (clk),
                 .rst (rst),
                 .key (btn5),
                 .key_pulse (key_pulse5)
                 );
   
   debounce_button  u6 (                               
                 .clk (clk),
                 .rst (rst),
                 .key (btn6),
                 .key_pulse (key_pulse6)
                 );
                 
   // 時鐘分頻,我們需要一些不同的信号,是以需要不同的分頻子產品。
   time_split #(.N(6000000),
                  .WIDTH(23)) t1 (
               .clk(clk),
               .rst_n(rst),
               .clkout(clkout1));
               // 頻率為 2 Hz。
   
   time_split #(.N(12000000),
                  .WIDTH(24)) t2 (
               .clk(clk),
               .rst_n(rst),
               .clkout(clkout2));
               // 頻率為 1 Hz。
   
   time_split #(.N(3000000),
                  .WIDTH(22)) t3 (
               .clk(clk),
               .rst_n(rst),
               .clkout(clkout3));
               // 頻率為 4 Hz。
               
   time_split #(.N(1500000),
                  .WIDTH(21)) t4 (
               .clk(clk),
               .rst_n(rst),
               .clkout(clkout4));
               // 頻率為 8 Hz。
   
   // 顯示Dot Matrix.
   
reg   [63:0] memory;
reg   [2:0]   state;

// 确定采用哪一種方法來對Dot Matrix進行顯示。
always@(posedge clk)
begin

   if(state_of_start_or_nor && ~really_start)
   // 開機了,但是沒有完全開始工作,這個時候是由 location 來進行決定的。
   
   begin
   // location.
      case(location)
         4'b0000:
         // 第一個位置
            begin
               mem <= mem1;
            end
            
         4'b0001:
         // 第二個位置
            begin
               mem <= mem2;
            end
            
         4'b0010:
         // 第三個位置
            begin
               mem <= mem3;
            end
            
         default:
         // 一共隻有三個位置,是以這個也就是顯示第三個位置
            begin
               mem <= mem3;
            end
            
         endcase
         
      end
      
      if(state_of_start_or_nor && really_start)
      // 開機了,而且是完全開始了。
      begin
      // led_position.
      
         case(led_position)
            2'b00:
            // 第一個位置
            
            begin
            if(btn_true_or_not1)
            // 如果吃過藥了。
            begin
               mem0 <= mem___;
            end
            
            else
            begin
               mem0 <= mem10;
            end
            end
            
            2'b01:
            // 第二個位置
            
            begin
            if(btn_true_or_not2)
            // 如果吃過藥了。
            begin
               mem0 <= mem___;
            end
            
            else
            begin
               mem0 <= mem20;
            end
            end
            
            2'b10:
            // 第三個位置
            
            begin
            if(btn_true_or_not3)
            // 如果吃過藥了。
            begin
               mem0 <= mem___;
            end
            
            else
            begin
               mem0 <= mem30;
            end
            end
            
            default:
            // 已經吃過了所有的藥了。
            
            begin
               mem0 <= mem___;
            end
            
         endcase
      end
      
      memory = state_of_start_or_nor ? (really_start ? mem0 : mem) : mem___;
      // 設定最後的輸出的點陣。
      
end

always @(posedge clk_800hz or negedge rst)
// 掃描監測。
   if(!rst) begin state <= s0; col <= 8'hff; row = 8'hff; end
   // 如果按下了重置鍵以後的操作。
   
   else
   begin      
   // 采取掃描的形式進行狀态的監測。
   
      case(state)
      // 共有 8 個狀态,進行循環的掃描。
      
         s0:   begin col <= 8'b1111_1110; row = ~memory[56+:8]; state <= s1; end
         s1:   begin col <= 8'b1111_1101; row = ~memory[48+:8]; state <= s2; end
         s2:   begin col <= 8'b1111_1011; row = ~memory[40+:8]; state <= s3; end
         s3:   begin col <= 8'b1111_0111; row = ~memory[32+:8]; state <= s4; end
         s4:   begin col <= 8'b1110_1111; row = ~memory[24+:8]; state <= s5; end
         s5:   begin col <= 8'b1101_1111; row = ~memory[16+:8]; state <= s6; end
         s6:   begin col <= 8'b1011_1111; row = ~memory[ 8+:8]; state <= s7; end
         s7:   begin col <= 8'b0111_1111; row = ~memory[ 0+:8]; state <= s0; end
         default: begin state <= s0; col <= 8'hff; row = 8'hff; end
         
      endcase      
   end
   

            
   // 開機或者關機以及确定修改哪一個位置的藥箱的提醒時間。         
   always@(posedge clk)
   // 時鐘的上升沿到來。
      begin
      
         if(times_ && times0_)
         
         begin
            times <= 0;
         end
      
         if(key_pulse1)
         begin
            state_of_start_or_nor <= ~state_of_start_or_nor;
            
            times <= 1;
            // 如果發生了開關機,那麼就把times置為一(1)。
            
            // 如果按鍵 1 了,那麼就需要翻轉狀态。
         end
         
         else
         begin
            state_of_start_or_nor <= state_of_start_or_nor;
            // 如果沒有按鍵 1 ,狀态保持不變。
         end
         
         if(state_of_start_or_nor && ~really_start)
         // 開機了,但是,并沒有完全進入到工作狀态的時候執行。
         begin
            if(key_pulse2)
            // 如果按下了按鈕 2,那麼就需要把設定時間的目前藥盒後移一位。
            begin
               location <= (location + 1) % 3;
               // 總共有 3 個藥盒,是以 % 3。
            end
            
            else
            begin
               location <= location;
               // 沒有按鍵 2 的話,狀态保持不變。
            end
            
            case(location)
            // 判斷是哪一個藥箱在設定時間。
            
               1'd0:begin
                  led1 = clkout1;  // 閃爍。
                  // 目前的位置需要,以 2Hz 的頻率進行閃爍,我們讓其他位置一直亮着不變。
                  led2 = 0;
                  led3 = 0;
               end
               
               1'd1:begin
                  led1 = 0;
                  led2 = clkout1;
                  // 目前的位置需要,以 2Hz 的頻率進行閃爍,我們讓其他位置一直亮着不變。
                  led3 = 0;
               end
               
               default:begin
               // 這個其實就是隻能是最後一個位置了。
                  led1 = 0;
                  led2 = 0;
                  led3 = clkout1;
                  // 目前的位置需要,以 2Hz 的頻率進行閃爍,我們讓其他位置一直亮着不變。
               end
               
            endcase
         end
      end
      
//   always@(posedge clk or negedge rst)
//      begin
//         if(~rst)
//         begin
//            if(~state_of_start_or_nor && ~really_start)
//            begin
//                  
//               if(counting == 0)
//               begin
//               end
//               else
//               begin
//                  counting <= 0;
//               end
//               // 使得counting為0,這樣子的話,下一次開始就也是從0開始了啦,目的是為了保證使用的合理性與友善性。
//            end
//         end
//      end
      
   // 通過按鍵來決定是否已經正式開始工作了,即就是判斷是否開始提醒吃藥的功能了。
   always@(posedge clk)
      begin
         if(state_of_start_or_nor)
         begin
            if(key_pulse5 && ~really_start)
            // 按鍵 5 是控制正式開始工作的。
            begin
               really_start <= 1;
//               times <= 1;
//               // 如果發生了正式開始工作,那麼就把times置為一(1)。
               
//               if(counting == 0)
//               begin
//               end
//               else
//               begin
//                  counting <= 0;
//               end
//               // 使得counting為0,這樣子的話,下一次開始就也是從0開始了啦,目的是為了保證使用的合理性與友善性。
               //設定狀态為正式開始工作了,即就是 really_start 由 0 變為了 1。
               /**
               這個時候,由于已經正式開始工作了,
               于是,之前的切換藥箱的位置,以及設定提醒的時間的操作就被禁止了。
               */
               
            end
            
            if(key_pulse1 && really_start)
            begin
               really_start <= 0;
               
//               if(counting == 0)
//               begin
//               end
//               else
//               begin
//                  counting <= 0;
//               end
//               // 使得counting為0,這樣子的話,下一次開始就也是從0開始了啦,目的是為了保證使用的合理性與友善性。
               
               // 如果在正式工作的條件之下,按下了按鍵 1 ,
               // 那麼,就需要把正式工作的狀态由 0 變為 1,這時,可以保證下一次開機,不會直接工作
            end
         end
      end

   // 正式開始工作以後的計數操作。
   reg btn_true_or_not1 = 0;
   reg btn_true_or_not2 = 0;
   reg btn_true_or_not3 = 0;
   // 這三個變量是用來判斷是否已經吃過藥了的。
   
   always@(posedge clkout2)
   // 每個一秒鐘都會加一次數字,相當于一秒加一次一,一秒加一的計數器啦。
      begin
         if(state_of_start_or_nor && really_start)
         // 如果已經開機了而且是在已經處于正式開始工作了的狀态了。
         
         begin
         
            counting <= (counting + 1) % 60;
            // 計數。
            
            if(times_)
            // 如果發生了開機以及關機的相應的操作,就需要執行以下的代碼。
            
            begin
               counting <= 2'd00;
               // 如果發生了開機以及關機的相應的操作,就需要把counting置為零。
               seg_data_10 <= 0;
               seg_data_20 <= 0;
               
               times0 <= 1;
               
               // 為了防止一直處于置零的狀态,這裡必須把 times 置零,這裡是表示進行了置零的操作。
            end
            
            else
            begin
               if(times0_)
               begin
                  times0 <= 0;
                  // 置零,表示整個互動過程的結束。
                  
               end
            end
            
//            counting <= (counting + 1) % 60;
//            // 計數。
            
            // 計算表達的數字應該是多少。
            seg_data_10 <= counting / 10;
            if(counting % 10 == 0)
            begin 
               seg_data_20 <= 0;
            end
            else
            begin 
               seg_data_20 <= counting - 10 * seg_data_10;
               // 這裡與之前的其實是一樣的了啦。
            end
            
            // 記錄究竟是哪一個藥箱該拿取藥物了。
            if(counting >= 0 && counting < first_medicine)
               begin
                  led_position = 2'b11;
                  // 藥盒 1 、 2 、 3 都沒有達到取藥的條件啦,不取藥,是以是不需要提醒的狀态。
               end
               
            if(counting >= first_medicine && counting < second_medicine)
               begin
                  led_position = 2'b00;
                  // 藥盒 1 需要進行取藥。
               end
               
            if(counting >= second_medicine && counting < third_medicine)
               begin
                  led_position = 2'b01;
                  // 藥盒 2 需要取藥。
               end
               
            if(counting >= third_medicine && counting < 61)
               begin
                  led_position = 2'b10;
                  // 藥盒 3 需要取藥。
               end
         end
      end
      
   // 由于計時器和閃爍提醒的時間周期不一樣,是以說,我們需要把這兩個給它獨立起來操作,前一個是進行計數器的操作。
   // 這裡進行閃爍提醒的操作。
   always@(posedge clk)
      begin
         if(state_of_start_or_nor && really_start)
         // 如果說是,已經開機了,而且是正式的進入了工作的狀态了,就開始執行下面的執行的代碼了啦。
         begin
         
            case(led_position)
               // 如果說是第一個藥盒該開始提醒吃藥的 。
            
               2'b00:begin
               
               
                  if(btn_true_or_not3)
                  
                  begin
                     btn_true_or_not3 = 0;
                     // 置0 。
                  end
                  // 将前一個已經吃過藥的狀态設定為 0 ,即就是設定上一個 3 為未吃藥的狀态。
                  
                  
                  if(~btn_true_or_not1)
                  // 如果沒有吃藥。
                  
                  begin
                     led10 = clkout3;
                     led20 = 1;
                     led30 = 1;
                     // 該吃藥的對應的那個藥盒的等閃爍,其他的處于熄滅的狀态。
                     
                     led40 = clkout4;
                     // 蜂鳴器。
                     
                     if(key_pulse6)
                     // 按下了按鍵 6 ,表示吃藥了。
                     
                     begin
                        btn_true_or_not1 = 1;
                        led40 = 1;
                        // 如果已經吃過藥了,那麼,設定吃藥的狀态為 1 ,而且蜂鳴器不再響了。
                     end
                  end
                  
                  else
                  begin
                     led10 = 1;
                     led20 = 1;
                     led30 = 1;
                     led40 = 1;
                     // 雖然前面已經設定過了,但是在寫一遍不影響的額。
                  end
               end
               
               // 第二個藥盒該吃藥了。
               
               2'b01:begin
               
               
                  if(btn_true_or_not1)
                  
                  begin
                     btn_true_or_not1 = 0;
                     // 置0 。
                  end
                  // 将前一個已經吃過藥的狀态設定為 0 ,即就是設定上一個 1 為未吃藥的狀态。
                  
                  
                  if(~btn_true_or_not2)
                  // 如果沒有吃藥。
                  
                  begin 
                     led10 = 1;
                     led20 = clkout3;
                     led30 = 1;
                     // 該吃藥的對應的那個藥盒的等閃爍,其他的處于熄滅的狀态。
                     
                     led40 = clkout4;
                     // 蜂鳴器。
                     
                     if(key_pulse6)
                     // 按下了按鍵 6 ,表示吃藥了。
                     
                     begin
                        btn_true_or_not2 = 1;
                        led40 = 1;
                        // 如果已經吃過藥了,那麼,設定吃藥的狀态為 1 ,而且蜂鳴器不再響了。
                     end
                  end
                  
                  else
                  begin
                     led10 = 1;
                     led20 = 1;
                     led30 = 1;
                     led40 = 1;
                     // 雖然前面已經設定過了,但是在寫一遍不影響的額。
                  end
               end
               
               // 第三個藥盒該吃藥了。
               
               2'b10:begin
               
               
                  if(btn_true_or_not2)
                  
                  begin
                     btn_true_or_not2 = 0;
                     // 置0 。
                  end
                  // 将前一個已經吃過藥的狀态設定為 0 ,即就是設定上一個 2 為未吃藥的狀态。
                  
                  
                  if(~btn_true_or_not3)
                  // 如果沒有吃藥。
                  
                  begin
                     led10 = 1;
                     led20 = 1;
                     led30 = clkout3;
                     // 該吃藥的對應的那個藥盒的等閃爍,其他的處于熄滅的狀态。
                     
                     led40 = clkout4;
                     // 蜂鳴器。
                     
                     if(key_pulse6)
                     // 按下了按鍵 6 ,表示吃藥了。
                     
                     begin
                        btn_true_or_not3 = 1;
                        led40 = 1;
                        // 如果已經吃過藥了,那麼,設定吃藥的狀态為 1 ,而且蜂鳴器不再響了。
                     end
                  end
                  
                  else
                  begin
                     led10 = 1;
                     led20 = 1;
                     led30 = 1;
                     led40 = 1;
                     // 雖然前面已經設定過了,但是在寫一遍不影響的額。
                  end
               end
               
               default:begin
               // 其他的情況,也就是說,不需要吃藥的情況啦。
               
                  led10 = 1;
                  led20 = 1;
                  led30 = 1;
                  led40 = 1;
               end
            endcase
            end
      end
      
   // 在開機之後進行設定三個藥箱提醒吃藥的時間。   
   always@(posedge clk)
      begin
         if(state_of_start_or_nor && ~really_start)
         // 這裡也是開機了,但是沒有正式開始工作。
         begin
            
//            counting <= 0;
//            // 使得counting為0,這樣子的話,下一次開始就也是從0開始了啦,目的是為了保證使用的合理性與友善性。
         
            case(location)
            
               1'd0:begin
                  if(key_pulse3)
                  begin
                     first_medicine <= (first_medicine + 1) % 60;
                     // 如果按下了 3 按鍵,那麼,時間加一。
                  end
                  
                  else 
                  begin
                     if(key_pulse4)
                     begin
                        first_medicine <= (first_medicine -1 + 60) % 60;
                        // 防止減出負數來了。
                        // 如果按下了 4 按鍵,那麼,時間減一。
                     end
                     
                     else
                     begin
                        first_medicine <= first_medicine;
                        // 如果 3 、 4都沒有按的話,那麼,就保持不變。
                     end
                  end
                  
                  seg_data_1 <= first_medicine / 10;
                  // 10位數字。
                  if(first_medicine % 10 == 0)
                  begin 
                     seg_data_2 <= 0;
                     // 本來沒有必要這麼操作,但不知道為啥兩種情況綜合在一起就無法顯示0,是以才分開來了。
                  end
                  else
                  begin 
                     seg_data_2 <= first_medicine - 10 * seg_data_1;
                  end
                  // 個位數字。
               end
               
               1'd1:begin
                  if(key_pulse3)
                  begin
                     second_medicine <= (second_medicine + 1) % 60;
                     // 如果按下了 3 按鍵,那麼,時間加一。
                  end
                  
                  else 
                  begin
                     if(key_pulse4)
                     begin
                        second_medicine <= (second_medicine -1 + 60) % 60;
                        // 如果按下了 4 按鍵,那麼,時間減一。
                     end
                     else
                     begin
                        second_medicine <= second_medicine;
                        // 如果 3 、 4都沒有按的話,那麼,就保持不變。
                     end
                  end
                  
                  // 計算兩位數字分别是多少。
                  seg_data_1 <= second_medicine / 10;
                  if(second_medicine % 10 == 0)
                  begin 
                     seg_data_2 <= 0;
                  end
                  else
                  begin 
                     seg_data_2 <= second_medicine - 10 * seg_data_1;
                  end
               end
               
               default:begin
               // 這個其實就是位置 3 了。
                  if(key_pulse3)
                  begin
                     third_medicine <= (third_medicine + 1) % 60;
                     // 如果按下了 3 按鍵,那麼,時間加一。
                  end
                  else 
                  begin
                     if(key_pulse4)
                     begin
                        third_medicine <= (third_medicine -1 + 60) % 60;
                        // 如果按下了 4 按鍵,那麼,時間減一。
                     end
                     else
                     begin
                        third_medicine <= third_medicine;
                        // 如果 3 、 4都沒有按的話,那麼,就保持不變。
                     end
                  end
                  
                  // 計算兩位數字分别是多少。
                  seg_data_1 <= third_medicine / 10;
                  if(third_medicine % 10 == 0)
                  begin 
                     seg_data_2 <= 0;
                  end
                  else
                  begin 
                     seg_data_2 <= third_medicine - 10 * seg_data_1;
                  end
               end
               
            endcase
         end
         else
         begin 
         end
      end
   
   assign seg_led_1 = state_of_start_or_nor ? (really_start?seg[seg_data_10]:seg[seg_data_1]) : seg[10];                         
   assign seg_led_2 = state_of_start_or_nor ? (really_start?seg[seg_data_20]:seg[seg_data_2]) : seg[10];
   // 數位管的顯示。
   
   assign rled1=state_of_start_or_nor ? (really_start?led10:led1) :1;       
   assign rled2=state_of_start_or_nor ? (really_start?led20:led2) :1;  
   assign rled3=state_of_start_or_nor ? (really_start?led30:led3) :1;
   assign rled4=state_of_start_or_nor ? (really_start?led40:led4) :1;
   //      

2、主檔案中使用的按鍵消抖子產品

// ********************************************************************
// >>>>>>>>>>>>>>>>>>>>>>>>> COPYRIGHT NOTICE <<<<<<<<<<<<<<<<<<<<<<<<<
// ********************************************************************
// File name    : debounce.v
// Module name  : debounce
// Author       : STEP
// Description  : 
// Web          : www.stepfpga.com
// 
// --------------------------------------------------------------------
// Code Revision History : 
// --------------------------------------------------------------------
// Version: |Mod. Date:   |Changes Made:
// V1.0     |2017/03/02   |Initial ver
// --------------------------------------------------------------------
// Module Function:按鍵消抖
 
module debounce_button (clk,rst,key,key_pulse);
 
        parameter       N  =  1;                      //要消除的按鍵的數量
 
    input             clk;
        input             rst;
        input    [N-1:0]   key;                        //輸入的按鍵                    
    output  [N-1:0]   key_pulse;                  //按鍵動作産生的脈沖    
 
        reg     [N-1:0]   key_rst_pre;                //定義一個寄存器型變量存儲上一個觸發時的按鍵值
        reg     [N-1:0]   key_rst;                    //定義一個寄存器變量儲存儲目前時刻觸發的按鍵值
 
        wire    [N-1:0]   key_edge;                   //檢測到按鍵由高到低變化是産生一個高脈沖
 
        //利用非阻塞指派特點,将兩個時鐘觸發時按鍵狀态存儲在兩個寄存器變量中
        always @(posedge clk  or  negedge rst)
          begin
             if (!rst) begin
                 key_rst <= {N{1'b1}};                //初始化時給key_rst指派全為1,{}中表示N個1
                 key_rst_pre <= {N{1'b1}};
             end
             else begin
                 key_rst <= key;                     //第一個時鐘上升沿觸發之後key的值賦給key_rst,同時key_rst的值賦給key_rst_pre
                 key_rst_pre <= key_rst;             //非阻塞指派。相當于經過兩個時鐘觸發,key_rst存儲的是目前時刻key的值,key_rst_pre存儲的是前一個時鐘的key的值
             end    
           end
 
        assign  key_edge = key_rst_pre & (~key_rst);//脈沖邊沿檢測。當key檢測到下降沿時,key_edge産生一個時鐘周期的高電平
 
        reg    [17:0]      cnt;                       //産生延時所用的計數器,系統時鐘12MHz,要延時20ms左右時間,至少需要18位計數器     
 
        //産生20ms延時,當檢測到key_edge有效是計數器清零開始計數
        always @(posedge clk or negedge rst)
           begin
             if(!rst)
                cnt <= 18'h0;
             else if(key_edge)
                cnt <= 18'h0;
             else
                cnt <= cnt + 1'h1;
             end  
 
        reg     [N-1:0]   key_sec_pre;                //延時後檢測電平寄存器變量
        reg     [N-1:0]   key_sec;                    
 
 
        //延時後檢測key,如果按鍵狀态變低産生一個時鐘的高脈沖。如果按鍵狀态是高的話說明按鍵無效
        always @(posedge clk  or  negedge rst)
          begin
             if (!rst) 
                 key_sec <= {N{1'b1}};                
             else if (cnt==18'h3ffff)
                 key_sec <= key;  
          end
       always @(posedge clk  or  negedge rst)
          begin
             if (!rst)
                 key_sec_pre <= {N{1'b1}};
             else                   
                 key_sec_pre <= key_sec;             
         end      
       assign  key_pulse = key_sec_pre & (~key_sec);      

3、主檔案中使用的時鐘分頻子產品

// ********************************************************************
// >>>>>>>>>>>>>>>>>>>>>>>>> COPYRIGHT NOTICE <<<<<<<<<<<<<<<<<<<<<<<<<
// ********************************************************************
// File name    : divide.v
// Module name  : divide
// Author       : STEP
// Description  : clock divider
// Web          : www.stepfpga.com
// 
// --------------------------------------------------------------------
// Code Revision History : 
// --------------------------------------------------------------------
// Version: |Mod. Date:   |Changes Made:
// V1.0     |2017/03/02   |Initial ver


// --------------------------------------------------------------------











// Module Function:任意整數時鐘分頻
 
 
module time_split#(parameter WIDTH   = 24, parameter N   = 12000000)(clk,rst_n,clkout);
 
        input    clk,rst_n;                       //輸入信号,其中clk連接配接到FPGA的C1腳,頻率為12MHz
        output    clkout;                          //輸出信号,可以連接配接到LED觀察分頻的時鐘
 
        //parameter是verilog裡常數語句
//    parameter    WIDTH    = 24;             //計數器的位數,計數的最大值為 2**WIDTH-1
//    parameter    N    = 12000000;             //分頻系數,請確定 N < 2**WIDTH-1,否則計數會溢出
// 
    reg    [WIDTH-1:0]    cnt_p,cnt_n;     //cnt_p為上升沿觸發時的計數器,cnt_n為下降沿觸發時的計數器
    reg            clk_p,clk_n;     //clk_p為上升沿觸發時分頻時鐘,clk_n為下降沿觸發時分頻時鐘
 
    //上升沿觸發時計數器的控制
    always @ (posedge clk or negedge rst_n )         //posedge和negedge是verilog表示信号上升沿和下降沿
                                                         //當clk上升沿來臨或者rst_n變低的時候執行一次always裡的語句
        begin
            if(!rst_n)
                cnt_p<=0;
            else if (cnt_p==(N-1))
                cnt_p<=0;
            else cnt_p<=cnt_p+1;             //計數器一直計數,當計數到N-1的時候清零,這是一個模N的計數器
        end
 
         //上升沿觸發的分頻時鐘輸出,如果N為奇數得到的時鐘占空比不是50%;如果N為偶數得到的時鐘占空比為50%
         always @ (posedge clk or negedge rst_n)
        begin
            if(!rst_n)
                clk_p<=0;
            else if (cnt_p<(N>>1))          //N>>1表示右移一位,相當于除以2去掉餘數
                clk_p<=0;
            else 
                clk_p<=1;               //得到的分頻時鐘正周期比負周期多一個clk時鐘
        end
 
        //下降沿觸發時計數器的控制            
    always @ (negedge clk or negedge rst_n)
        begin
            if(!rst_n)
                cnt_n<=0;
            else if (cnt_n==(N-1))
                cnt_n<=0;
            else cnt_n<=cnt_n+1;
        end
 
        //下降沿觸發的分頻時鐘輸出,和clk_p相差半個時鐘
    always @ (negedge clk)
        begin
            if(!rst_n)
                clk_n<=0;
            else if (cnt_n<(N>>1))  
                clk_n<=0;
            else 
                clk_n<=1;                //得到的分頻時鐘正周期比負周期多一個clk時鐘
        end
 
        assign clkout = (N==1)?clk:(N[0])?(clk_p&clk_n):clk_p;      //條件判斷表達式
                                                                    //當N=1時,直接輸出clk
                                                                    //當N為偶數也就是N的最低位為0,N(0)=0,輸出clk_p
                                                                    //當N為奇數也就是N最低位為1,N(0)=1,輸出clk_p&clk_n。正周期多是以是相與
endmodule      

三、後續操作

編譯

Verilog HDL實作智能藥盒

設定引腳

(注意設定引腳以後需要重新進行編譯才可以實作修改項目的操作。)

Verilog HDL實作智能藥盒
Verilog HDL實作智能藥盒

燒錄

Verilog HDL實作智能藥盒

四、實驗現象

我們仍然采用視訊的形式展示實驗的現象:

​​https://www.bilibili.com/video/BV1MY411x7Xw?spm_id_from=333.999.0.0​​

Verilog HDL實作智能藥盒

五、總結

繼續閱讀