02.26

김준혁·2026년 2월 26일

PCM(Pulse Code Modulation)

-아날로그 신호를 디지털 데이터로 변환시킴
표본화(sampling)=아날로그 신호를 일정한 시간 간격으로 쪼갬
양자화(Quantization)= 각 시점의 신호 크기를 반올림함
부호화(Encoding)=0과1의 이진 코드로 변환
표본화-양자화-부호화 순서대로 진행

PWM(Pulse Width Modulation)

-디지털 환경에서, 1이 켜져 있는 시간의 비율(폭)을 조절하여 아날로그적인 효과를 내는 제어 방식
신호가 켜져 있는 시간의 비율=Duty Cycle
%가 높을 수록 HIGH값이 늘어남

PWM 실습

`timescale1ns /1ps
module top(
    inputclk,
    inputreset,
    inputincrease_duty_btn,
    inputdecrease_duty_btn,
    input[1:0] motor_direction,
    outputPWM_OUT,
    outputPWM_OUT_LED,
    output[1:0] in1_in2,
    output[3:0] an,
    output[7:0] seg
wirew_clean_inc_btn;
wirew_clean_dec_btn;
wire[3:0] w_DUTY_CYCLE;
wirew_tick;
 tick_generator u_tick_generator(
    
.clk(clk),
.reset(reset),
.tick(w_tick)
);
 debouncer u_increase_duty_btn(
.clk(clk),
.reset(reset),
.noisy_btn(increase_duty_btn),  
.clean_btn(w_clean_inc_btn)
);
 debouncer u_decrease_duty_btn(
.clk(clk),
.reset(reset),
.noisy_btn(decrease_duty_btn),  
.clean_btn(w_clean_dec_btn)
);
 pwm_duty_control u_pwm_duty_control(
.clk(clk),
.reset(reset),
.duty_inc(w_clean_inc_btn),
.duty_dec(w_clean_dec_btn),
.DUTY_CYCLE(w_DUTY_CYCLE), //FND에 출력 0~9
.PWM_OUT(PWM_OUT),
.PWM_OUT_LED(PWM_OUT_LED)
 fnd_contorller u_fnd_contorller(
.clk(clk),
.reset(reset),
.tick(w_tick),
.in_data(w_DUTY_CYCLE),
.motor_dir(motor_direction),
        
.an(an),
.seg(seg)
);
assignin1_in2=motor_direction;
endmodule

btn_debouncer

`timescale1ns /1ps
module btn_debouncer(
    inputclk,
    inputreset,
    input[2:0] btn,   // 3개의 버튼 입력: btn[2:0] → 각각 btnL, btnC, btnR
    output[2:0] debounced_btn
);
    debouncer U_debouncer_btnL(
.clk(clk),
.reset(reset),
.noisy_btn(btn[0]),
.clean_btn(debounced_btn[0])
    debouncer U_debouncer_btnC(
.clk(clk),
.reset(reset),
.noisy_btn(btn[1]),
.clean_btn(debounced_btn[1])
    debouncer U_debouncer_btnR(
.clk(clk),
.reset(reset),
.noisy_btn(btn[2]),
.clean_btn(debounced_btn[2])
 //   assign led = debounced_btn;   // button을 누를때 마다 led가 동작 되도록 한다.
endmodule
// 아래에 있는 코드는 버튼 디바운서 이다. 
// 이는 버튼 3개(btn[2:0])의 입력에서 발생할 수 있는 
// 노이즈(채터링)을 제거해, 안정적인 버튼 입력(debounced_btn[2:0])으로 만들어준다.
// 파라미터는 보통 클럭 카운트 제한값으로 사용되어,
// 얼마나 오랫동안 입력이 안정적인지를 판단하기 위한 시간 지연 설정이다.
// 예: 100MHz 클럭이라면, 999,999는 약 10ms의 디바운싱 지연을 의미할 수 있다.
module debouncer#(parameterDEBOUNCE_LIMIT =20'd999_999) (
    inputclk,
    inputreset,
    inputnoisy_btn,  // raw noisy button input
    output regclean_btn
);
    reg[19:0] count;
    regbtn_state=0;
    always@(posedgeclk or posedgereset) begin
        if(reset) begin   // active-high reset
count <=0;
btn_state <=0;
clean_btn <=0;
        end else if(noisy_btn ==btn_state) begin  // 버튼 상태가 이전과 동일할 경우 (안정됨)
count <=0;
        end else begin
            if(count <DEBOUNCE_LIMIT)  // 버튼 상태가 바뀌었지만 아직 안정되지 않은 경우
count <=count +1;
            else begin  // 상태가 충분히 오랫동안 유지됨(10ms)
btn_state <=noisy_btn;
clean_btn <=noisy_btn;
count <=0;  // 리셋하면 다음 변경을 다시 감지할 수 있음
            end
        end
    end
endmodule
pwm_duty_control
`timescale1ns /1ps
// 100Mhz/10 -> 10Mhz의 주파수를 만듦.
// 100Mhz의 10% 조절 해상도를 가질 수 있는 최대 주파수
// 0~9까지 총 10번의 클럭을 세고
module pwm_duty_control(
    inputclk,
    inputreset,
    inputduty_inc,
    inputduty_dec,
    output[3:0] DUTY_CYCLE, //FND에 출력 0~9
    outputPWM_OUT,
    outputPWM_OUT_LED
reg[3:0] r_DUTY_CYCLE=4'd5;
reg[3:0] r_counter_PWM;
// edge 검출 register
regr_prev_duty_inc,r_prev_duty_dec;
wirew_duty_inc=(duty_inc &&!r_prev_duty_inc); //rising edge 검출
wirew_duty_dec=(duty_dec&&!r_prev_duty_dec);
//duty cycle 제어 btnU,btnD
always@(posedgeclk, posedgereset) begin
    if(reset) begin
r_DUTY_CYCLE<=4'd5; //50% duty cycle
    end else begin
r_prev_duty_inc <=duty_inc; //이전 상태 저장
r_prev_duty_dec<=duty_dec;  
        if(w_duty_inc &&r_DUTY_CYCLE<4'd9)
r_DUTY_CYCLE<=r_DUTY_CYCLE+1;
        if(w_duty_dec &&r_DUTY_CYCLE>4'd1)
r_DUTY_CYCLE<=r_DUTY_CYCLE-1;
    end
end
// 10MHz PWM 신호 생성(0~9)
always@(posedgeclk, posedgereset) begin
    if(reset) begin
r_counter_PWM <=0;
    end else begin
        if(r_counter_PWM >=4'd9)
r_counter_PWM<=0;
            elser_counter_PWM<=r_counter_PWM+1;
    end        
    end
    assignPWM_OUT=(reset)? 1'b0: (r_counter_PWM<r_DUTY_CYCLE) ? 1'b1: 1'b0;
    assignPWM_OUT_LED=PWM_OUT;
    assignDUTY_CYCLE=r_DUTY_CYCLE;
endmodule

pwm_duty_control

`timescale1ns /1ps
// 100Mhz/10 -> 10Mhz의 주파수를 만듦.
// 100Mhz의 10% 조절 해상도를 가질 수 있는 최대 주파수
// 0~9까지 총 10번의 클럭을 세고
module pwm_duty_control(
    inputclk,
    inputreset,
    inputduty_inc,
    inputduty_dec,
    output[3:0] DUTY_CYCLE, //FND에 출력 0~9
    outputPWM_OUT,
    outputPWM_OUT_LED
reg[3:0] r_DUTY_CYCLE=4'd5;
reg[3:0] r_counter_PWM;
// edge 검출 register
regr_prev_duty_inc,r_prev_duty_dec;
wirew_duty_inc=(duty_inc &&!r_prev_duty_inc); //rising edge 검출
wirew_duty_dec=(duty_dec&&!r_prev_duty_dec);
//duty cycle 제어 btnU,btnD
always@(posedgeclk, posedgereset) begin
    if(reset) begin
r_DUTY_CYCLE<=4'd5; //50% duty cycle
    end else begin
r_prev_duty_inc <=duty_inc; //이전 상태 저장
r_prev_duty_dec<=duty_dec;  
        if(w_duty_inc &&r_DUTY_CYCLE<4'd9)
r_DUTY_CYCLE<=r_DUTY_CYCLE+1;
        if(w_duty_dec &&r_DUTY_CYCLE>4'd1)
r_DUTY_CYCLE<=r_DUTY_CYCLE-1;
    end
end
// 10MHz PWM 신호 생성(0~9)
always@(posedgeclk, posedgereset) begin
    if(reset) begin
r_counter_PWM <=0;
    end else begin
        if(r_counter_PWM >=4'd9)
r_counter_PWM<=0;
            elser_counter_PWM<=r_counter_PWM+1;
    end        
    end
    assignPWM_OUT=(reset)? 1'b0: (r_counter_PWM<r_DUTY_CYCLE) ? 1'b1: 1'b0;
    assignPWM_OUT_LED=PWM_OUT;
    assignDUTY_CYCLE=r_DUTY_CYCLE;
endmodule

tick_generator

module tick_generator(
    
    inputclk,
    inputreset,
    output regtick
);
    parameterINPUT_FREQUENCY =100_000_000; // 100MHz
    parameterTICK_Hz =1000; // 1kHz
    parameterTICK_COUNT =INPUT_FREQUENCY /TICK_Hz; // 100,000
    reg[$clog2(TICK_COUNT)-1:0] r_tick_counter =0;
    always@(posedgeclk or posedgereset) begin
        // 초기화
        if(reset) begin
tick <=0;
r_tick_counter <=0;
        end
        // 카운팅 완료
        else if(r_tick_counter ==TICK_COUNT-1) begin
r_tick_counter <=0;
tick <=1'b1;          // 1클럭 폭 펄스
        end
        // r_counter 증가
        else begin
r_tick_counter <=r_tick_counter +1;
tick <=1'b0;
        end
    end
endmodule

btnU를 누르면 DUTY CYCLE이 커지고, btnD를 누르면 DUTY CYCLE이 작아진다.

profile
임베디드 개발자

0개의 댓글