Harman 세미콘 아카데미/Verilog

2024.8.12 [Verilog]18 - 주방타이머 수정, 조이스틱 ADC

U_Pong 2024. 8. 12. 22:05

2024.8.12 수업날


cook_timer 원래 소스코드
//////////////////////////////////////////////// 피동 부저 주방 타이머 소스코드
module cook_timer(
      input clk, reset_p,
      input [3:0] btn,
      output [3:0] com,
      output [7:0] seg_7,
      output reg timeout_led,
      output buzz,
      output buzz_clk);                       
      
      wire [3:0] btn_pedge;
      wire load_enable;
      wire clk_usec, clk_msec, clk_sec;
      wire alarm_off, inc_min, inc_sec, btn_start;
      wire [3:0] set_sec1, set_sec10, set_min1, set_min10;  
      wire [3:0] cur_sec1, cur_sec10, cur_min1, cur_min10;  
      wire dec_clk;    
      wire [15:0] value, set_time, cur_time;
      
       reg start_stop;
       
       reg [16:0] clk_div;
       always @ (posedge clk) clk_div = clk_div + 1;
       
       assign buzz_clk = timeout_led ? clk_div[13] : 0;
       
       assign buzz =  timeout_led;
       
      
      button_cntr_reg_n_p start(
            .clk(clk), .reset_p(reset_p), 
            .btn(btn[0]), .btn_pedge(btn_pedge[0]));
      
      
      always @ (posedge clk or posedge reset_p) begin
            if(reset_p)begin
                  start_stop = 0;
                  timeout_led = 0;
            end
            else begin
                  if(btn_start)start_stop = ~start_stop;
                  else if(cur_time == 0  && start_stop )begin     //카운트다운을 시작한 후에 cur_time 이 0이면 led가 불이 켜진다.
                        start_stop = 0;
                        timeout_led = 1;              // led는 한시간 뒤에 켜짐
                  end
                  else  if (alarm_off) timeout_led = 0;
            end      
      end
      
      // wire load_enable;
      edge_detector_n ed(
            .clk(clk), .reset_p(reset_p),
            .cp(start_stop),
            .p_edge(load_enable));   
      
      
      button_cntr_reg_n_p btn_inc_sec(
            .clk(clk), .reset_p(reset_p), 
            .btn(btn[1]), .btn_pedge(btn_pedge[1]));   
      button_cntr_reg_n_p btn_inc_min(
            .clk(clk), .reset_p(reset_p), 
            .btn(btn[2]), .btn_pedge(btn_pedge[2]));     
      button_cntr_reg_n_p btn_alarm_stop(
            .clk(clk), .reset_p(reset_p), 
            .btn(btn[3]), .btn_pedge(btn_pedge[3]));
      
      
      // wire clk_usec, clk_msec, clk_sec;
      clock_div_100 usec_clk(  
            .clk(clk), .reset_p(reset_p), 
            .cp_div_100(clk_usec));
      
      clock_div_1000 msec_clk(     
            .clk(clk), .reset_p(reset_p),
            .clk_source(clk_usec),
            .cp_div_1000_nedge(clk_msec)); 
            
      clock_div_1000 sec_clk(     
            .clk(clk), .reset_p(reset_p),
            .clk_source(clk_msec),
            .cp_div_1000_nedge(clk_sec));
      
      
      // wire alarm_off, inc_min, inc_sec, btn_start;
      assign {alarm_off, inc_min, inc_sec, btn_start} = btn_pedge;      //btn_pedge의 0비트가 btn_start, 1비트가 inc_sec... 이런 방식으로
      
      // wire [3:0] set_sec1, set_sec10, set_min1, set_min10;      
      counter_bcd_60 counter_sec(
            .clk(clk), .reset_p(reset_p),
            .clk_time(inc_sec),
            .bcd1(set_sec1), .bcd10(set_sec10));
            
      counter_bcd_60 counter_min(
            .clk(clk), .reset_p(reset_p),
            .clk_time(inc_min),
            .bcd1(set_min1), .bcd10(set_min10));
      
      
      // wire [3:0] cur_sec1, cur_sec10, cur_min1, cur_min10;
      // wire dec_clk;
      loadable_down_counter_bcd_60 cur_sec(
            .clk(clk), .reset_p(reset_p),
            .clk_time(clk_sec),
            .load_enable(load_enable),
            .load_bcd1(set_sec1), .load_bcd10(set_sec10),
            .bcd1(cur_sec1), .bcd10(cur_sec10),
            .dec_clk(dec_clk));
      
      loadable_down_counter_bcd_60 cur_min(
            .clk(clk), .reset_p(reset_p),
            .clk_time(dec_clk),
            .load_enable(load_enable),
            .load_bcd1(set_min1), .load_bcd10(set_min10),
            .bcd1(cur_min1), .bcd10(cur_min10));
      
      
      // wire [15:0] value, set_time, cur_time;
      assign set_time = {set_min10, set_min1, set_sec10, set_sec1};
      assign cur_time = {cur_min10, cur_min1, cur_sec10, cur_sec1};
      assign value = start_stop ? cur_time : set_time;
      fnd_4digit_cntr fnd(clk, reset_p, value, seg_7, com);
      
endmodule

 

이대로 bitestream을 실행하면 btn_inc_sec, 

btn_inc_min 버튼을 한 번 누르고 start 버튼을 1초 이내에 누르면 다운카운트가 되지 않고 바로 timeout_led가 켜진다.

즉, 다운카운트가 되지 않아서 start 버튼을 누르자 마자 처음에 설정했던 시간으로 돌아온다는 것이다.

이는 btn_inc_sec을 두번 누르고 start 버튼을 1초 이내에 눌러도 마찬가지이다.

 

start 버튼을 1초 이내에 누르면 아래의 박스 부분이 성립하지 않기 때문에

timeout_led가 켜지고 다운카운트가 되지 않는 것이다.

 

이를 해결하기 위해서는 한 템포 빠르게 작동시키기 위해 아래와 같이 수정해야한다.

 

cook_timer 수정코드
////////////////////////////////////////////////////// 2024.8.12
//////////////////// 피동 부저 주방 타이머 소스코드, 버그 해결 버전
module cook_timer(
      input clk, reset_p,
      input [3:0] btn,
      output [3:0] com,
      output [7:0] seg_7,
      output reg timeout_led,
      output buzz,
      output buzz_clk);                       
      
      wire [3:0] btn_pedge;
      wire load_enable;
      wire clk_usec, clk_msec, clk_sec;
      wire alarm_off, inc_min, inc_sec, btn_start;
      wire [3:0] set_sec1, set_sec10, set_min1, set_min10;  
      wire [3:0] cur_sec1, cur_sec10, cur_min1, cur_min10;  
      wire dec_clk;    
      wire [15:0] value, set_time, cur_time;
      
       reg start_stop;
       
       reg [16:0] clk_div;
       always @ (posedge clk) clk_div = clk_div + 1;
       
       assign buzz_clk = timeout_led ? clk_div[13] : 0;
       
       assign buzz =  timeout_led;
       
      
      button_cntr_reg_n_p start(
            .clk(clk), .reset_p(reset_p), 
            .btn(btn[0]), .btn_pedge(btn_pedge[0]));
      
      
      always @ (posedge clk or posedge reset_p) begin
            if(reset_p)begin
                  start_stop = 0;
                  timeout_led = 0;
            end
            else begin
                  if(btn_start)start_stop = ~start_stop;
                  else if(cur_time == 0  && start_stop )begin     //카운트다운을 시작한 후에 cur_time 이 0이면 led가 불이 켜진다.
                        start_stop = 0;
                        timeout_led = 1;              // led는 한시간 뒤에 켜짐
                  end
                  else  if (alarm_off) timeout_led = 0;
            end      
      end
      
      // wire load_enable;
      edge_detector_n ed(
            .clk(clk), .reset_p(reset_p),
            .cp(start_stop),
            .p_edge(load_enable));   
      
      
      button_cntr_reg_n_p btn_inc_sec(
            .clk(clk), .reset_p(reset_p), 
            .btn(btn[1]), .btn_pedge(btn_pedge[1]));   
      button_cntr_reg_n_p btn_inc_min(
            .clk(clk), .reset_p(reset_p), 
            .btn(btn[2]), .btn_pedge(btn_pedge[2]));     
      button_cntr_reg_n_p btn_alarm_stop(
            .clk(clk), .reset_p(reset_p), 
            .btn(btn[3]), .btn_pedge(btn_pedge[3]));
      
      
      // wire clk_usec, clk_msec, clk_sec;
      clock_div_100 usec_clk(  
            .clk(clk), .reset_p(reset_p), 
            .cp_div_100(clk_usec));
      
      clock_div_1000 msec_clk(     
            .clk(clk), .reset_p(reset_p),
            .clk_source(clk_usec),
            .cp_div_1000_nedge(clk_msec)); 
            
      clock_div_1000 sec_clk(     
            .clk(clk), .reset_p(reset_p),
            .clk_source(clk_msec),
            .cp_div_1000_nedge(clk_sec));
      
      
      // wire alarm_off, inc_min, inc_sec, btn_start;
      assign {alarm_off, inc_min, inc_sec, btn_start} = btn_pedge;      //btn_pedge의 0비트가 btn_start, 1비트가 inc_sec... 이런 방식으로
      
      // wire [3:0] set_sec1, set_sec10, set_min1, set_min10;      
      counter_bcd_60 counter_sec(
            .clk(clk), .reset_p(reset_p),
            .clk_time(inc_sec),
            .bcd1(set_sec1), .bcd10(set_sec10));
            
      counter_bcd_60 counter_min(
            .clk(clk), .reset_p(reset_p),
            .clk_time(inc_min),
            .bcd1(set_min1), .bcd10(set_min10));
      
      
      // wire [3:0] cur_sec1, cur_sec10, cur_min1, cur_min10;
      // wire dec_clk;
      loadable_down_counter_bcd_60 cur_sec(
            .clk(clk), .reset_p(reset_p),
            .clk_time(clk_sec),
            // load_enable->btn_start 수정
            .load_enable(btn_start),
            .load_bcd1(set_sec1), .load_bcd10(set_sec10),
            .bcd1(cur_sec1), .bcd10(cur_sec10),
            .dec_clk(dec_clk));
      
      loadable_down_counter_bcd_60 cur_min(
            .clk(clk), .reset_p(reset_p),
            .clk_time(dec_clk),
            // load_enable->btn_start 수정
            .load_enable(btn_start),
            .load_bcd1(set_min1), .load_bcd10(set_min10),
            .bcd1(cur_min1), .bcd10(cur_min10));
      
      
      // wire [15:0] value, set_time, cur_time;
      assign set_time = {set_min10, set_min1, set_sec10, set_sec1};
      assign cur_time = {cur_min10, cur_min1, cur_sec10, cur_sec1};
      assign value = start_stop ? cur_time : set_time;
      fnd_4digit_cntr fnd(clk, reset_p, value, seg_7, com);
      
endmodule

 

 

< 조이스틱 ADC >

ADC의 내용을 다시 복습해볼겸 이번에는 조이스틱을 사용하여

channel 6, channel 15의 2개를 사용하여 ADC 제어를 해보자.

channel을 2개 사용하는 이유는 조이스틱이 x, y축의 2개를 사용하기 때문이다.

 

Vivado 진행방법

 

Generate를 클릭하면 아래와 같이 파일이 만들어진다. channel 6, channel 15가 추가된 것을 확인할 수 있다.

다음으로 module 이름을 아래와 같이 수정한다.

 

adc_sequence2_top 소스코드
//////////////////////////////////////// 조이스틱 adc
module adc_sequence2_top(
    input clk, reset_p,
    input vauxp6, vauxn6, vauxp15, vauxn15,
    output led_r, led_g,
    output [3:0] com,
    output [7:0] seg_7);
    
    wire [4:0] channel_out;
      wire eoc_out;
      wire [15:0] do_out;
      xadc_sequencer adc_seq2
          (
          .daddr_in({2'b0, channel_out}),            // Address bus for the dynamic reconfiguration port
          .dclk_in(clk),                                          // Clock input for the dynamic reconfiguration port
          .den_in(eoc_out),                                 // Enable Signal for the dynamic reconfiguration port
          .reset_in(reset_p),                               // Reset signal for the System Monitor control logic
          .vauxp6(vauxp6),                               // Auxiliary channel 6
          .vauxn6(vauxn6),
          .vauxp15(vauxp15),                               // Auxiliary channel 15
          .vauxn15(vauxn15),
          .channel_out(channel_out),            // Channel Selection Outputs
          .do_out(do_out),                            // Output data bus for dynamic reconfiguration port
          .eoc_out(eoc_out)                           // End of Conversion Signal
          );
          
      wire eoc_out_pedge;
      edge_detector_n ed(
            .clk(clk), .reset_p(reset_p), .cp(eoc_out), 
            .n_edge(eoc_out_pedge));
      
      reg [11:0] adc_value_x, adc_value_y;               // 2^12=4096, 비트수가 많을수록 정밀도가 높다, 대신 정밀도가 높으면 안정도가 낮다
      always @(posedge clk or posedge reset_p)begin
            if(reset_p)begin
                adc_value_x = 0;
                adc_value_y = 0;
            end
            else if(eoc_out_pedge)begin
                case(channel_out[3:0])
                    6: adc_value_x = do_out[15:4];
                    15: adc_value_y = do_out[15:4];
                endcase
                    
            end
      end
       
      wire [15:0] value, adc_value_bcd_x, adc_value_bcd_y;
      bin_to_dec bcd_x(.bin({6'b0, adc_value_x[11:6]}), .bcd(adc_value_bcd_x));
      bin_to_dec bcd_y(.bin({6'b0, adc_value_y[11:6]}), .bcd(adc_value_bcd_y));
      
      // 상위 2비트 따로, 하위 2비트 따로 읽어야함
      assign value = { adc_value_bcd_x[7:0], adc_value_bcd_y[7:0]};
      fnd_4digit_cntr FND_duty(.clk(clk), .value(value), .com(com), .seg_7(seg_7)); 
          
          
endmodule

 

아래는 .xdc 수정해야하는 부분이다.

JXADC에서 각 끝줄에 있는 P, N의 위치에 따라 vauxp, vauxn의 위치를 맞춰줘야한다.

 

channel 6는 XA1_P, XA1_N,

channel 15는 XA4_P, XA4_N과 연결되어 있기 때문에 .xdc 파일에서 위와 같이 수정해야한다.

 

 

basys3 보드와 연결할때는 

JXAC1에 조이스틱 x축을 연결하고,

JXAC4에 조이스틱 y축을 연결하고,

JXAC5(GND)와 JXAC7을 연결하고,

JXAC10과 JXAC11(GND)를 연결하고,

조이스틱의 GND와 basys3 보드의 GND를 연결하고,

조이스틱의 5V와 basys2 보드의 3V3에 10K 저항을 함께 연결한다.

 

 

조이스틱 ADC 결과

주방타이머 수정, 조이스틱 ADC 끝!