시간 기반 할인과 잔액 반환을 지원하는 스마트 자판기
📍회로 기능 설명
이 자판기 회로는 FSM(유한 상태 기계)을 기반으로 동전 투입, 상품 선택 및 배출, 잔액 반환 기능을 구현한 시스템입니다. 기본적으로 IDLE(대기) 상태에서 동전이 투입되면 잔액이 갱신되며, 사용자가 상품을 선택하면 DISPENSE(상품 배출) 상태로 전환되어 상품을 배출하고 잔액을 차감합니다. 이때, 1/8 분주기(Clock Divider)를 활용하여 일정 간격으로 할인 기능이 활성화되며, 할인 조건이 활성화된 경우 상품을 50% 할인된 가격으로 구매할 수 있습니다. 반환 버튼이 눌릴 경우 RETURN(잔액 반환) 상태로 전환되어 남은 잔액만큼 동전을 반환하며, 스텝 모터 제 어를 통해 동전 반환을 정밀하게 수행합니다. 반환이 완료되면 모터를 비활성화하고 다시 IDLE 상태로 전환됩니다. 이러한 구조는 상태 전이를 통해 자판기의 주요 기능을 안정적으로 수행하며, 사용자 편의성을 높이는 할인 및 잔액 반환 기능을 제공합니다.
📍상태 천이도
📄모듈 코드(Module Code)
module vending_machine (
input clk, // 클럭 신호
input rst, // 리셋 신호
input coin, // 동전 투입 신호
input refund, // 잔액 반환 신호
input [1:0] select, // 음료 선택 신호
output reg dispense, // 음료 배출 신호
output reg motor_enable, // 모터 활성화 신호
output reg [3:0] balance, // 잔액 표시
output reg [3:0] motor_step, // 모터 단계 제어 신호
output reg discount_active // 분주된 클럭(할인 활성화 상태 표시)
);
// 상태 정의 (FSM)
parameter IDLE = 3'b000;
parameter DISPENSE = 3'b100;
parameter RETURN = 3'b101;
reg [2:0] state_reg, next_state; // 현재 상태 및 다음 상태
reg [3:0] normal_price; // 음료의 정가
reg [3:0] refund_counter; // 반환할 동전 수 카운터
(* KEEP = "TRUE" *) reg [2:0] div_counter; // 1/8 분주기 카운터 + 최적화 방지 코드
<코드>
reg [3:0] discount_price; // 할인된 음료 가격
// 할인 조건 활성화를 위한 1/8 분주기 생성 (duty cycle 50%)
always @(posedge clk or posedge rst) begin
if (rst) begin
div_counter <= 3'b000;
discount_active <= 1'b0;
end
else begin
if(div_counter == 3'b011) begin
div_counter <= 3'b000;
discount_active <= ~ discount_active;
end
else div_counter <= div_counter +1;
end
end
// 음료 가격 설정 : 할인 조건이 활성화되면 50%할인이 적용된다.
always @(*) begin
case (select)
2'b01: begin
normal_price = 4;
discount_price = 2;
end
2'b10: begin
normal_price = 6;
discount_price = 3;
end
2'b11: begin
normal_price = 8;
discount_price = 4;
end
default: begin
normal_price = 0;
discount_price = 0;
end
endcase
end
// 상태 전이 로직
always @(posedge clk or posedge rst) begin
if (rst)
state_reg <= IDLE; // 리셋 시 초기 상태로 이동
else
state_reg <= next_state; // 다음 상태로 전환
end
// 다음 상태 결정 (FSM)
always @(*) begin
next_state = state_reg; // 기본값: 현재 상태 유지
case (state_reg)
IDLE: begin
if (coin)
next_state = IDLE; // 동전 투입 시 잔액 갱신
else if (select!=2'b00 && balance >=
(discount_active ? discount_price : normal_price))
next_state = DISPENSE; // 음료 선택 및 잔액 확인 후 DISPENSE로 전환
else if (refund)
next_state = RETURN; // 반환 버튼 누르면 RETURN 상태로 전환
end
DISPENSE: begin
next_state = IDLE; // DISPENSE 후 IDLE로 이동
end
RETURN: begin
if (balance == 0)
next_state = IDLE; // 반환 완료 후 IDLE로 이동
end
endcase
end
// 상태별 동작 정의
always @(posedge clk or posedge rst) begin
if (rst) begin
dispense <= 0;
motor_enable <= 0;
balance <= 0;
refund_counter <= 0;
motor_step <= 4'b0000;
end
else begin
case (state_reg)
IDLE: begin
dispense <= 0;
motor_enable <= 0;
if (coin)
balance <= balance + 1; // 동전 투입 시 잔액 갱신
end
DISPENSE: begin
dispense <= 1; // 음료 배출
if (discount_active)
balance <= balance - discount_price; // 할인 가격 차감
else
balance <= balance - normal_price; // 정가 차감
end
RETURN: begin
if (refund_counter == 0 && balance >0) begin
refund_counter <= balance; // 반환할 동전 초기화
motor_enable <= 1; // 모터 활성화
end
else if (refund_counter > 0) begin
case (refund_counter % 4) // 잔액에 따른 모터 실행
0: motor_step <= 4'b1001;
1: motor_step <= 4'b1100;
2: motor_step <= 4'b0110;
3: motor_step <= 4'b0011;
endcase
refund_counter <= refund_counter - 1;
balance <= balance -1;
end
else if(refund_counter==0) begin
motor_enable <= 0; // 반환 완료 시 모터 비활성화
balance <= 0;
next_state <= IDLE;
// 반환 완료 후 IDLE로 전환
end
end
endcase
end
end
endmodule
📄테스트 벤치 코드 (Test Bench Code)
module tb_vending_machine;
// Inputs
reg clk;
reg rst;
reg coin;
reg refund;
reg [1:0] select;
// Outputs
wire dispense;
wire motor_enable;
wire [3:0] balance;
wire [3:0] motor_step;
wire discount_active;
// Instantiate the Unit Under Test (UUT)
vending_machine uut (
.clk(clk),
.rst(rst),
.coin(coin),
.refund(refund),
.select(select),
.dispense(dispense),
.motor_enable(motor_enable),
.balance(balance),
.motor_step(motor_step),
.discount_active(discount_active)
);
// Generate clock signal
always #10 clk = ~clk; // 10ns clock period
initial begin
// Initialize Inputs
clk = 0;
rst = 1;
coin = 0;
refund = 0;
select = 2'b00;
// Wait for reset
#10 rst = 0;
// Step 1: Insert 3 coins
#10 coin = 1;
#20 coin = 0; // Insert first coin
#10 coin = 1;
#20 coin = 0; // Insert second coin
#10 coin = 1;
#20 coin = 0; // Insert third coin
#10 coin = 1;
#20 coin = 0; // Insert fourth coin
// Step 2: Select drink 1 (cost 2 coins in discount)
select = 2'b01; // Select drink 1
// Step 4: Refund remaining balance
#60 refund = 1;
#30 refund = 0;
// Finish simulation
#300 $finish;
end
endmodule
📄RTL Schematic
📄Technology Schematic
📄 파형 & 분석
*clk 신호는 10ns간격으로 변동한다.
*1/8 분주기(Duty Cycle 50%)가 4번의 상승 클록 신호 후 변동한다. → 할인 조건
<0ns~10ns: 초기화 상태>
rst = 1: 리셋 신호가 활성화되어 모든 상태와 레지스터가 초기화된다.
현재 상태(state_reg) : IDLE
이후, rst신호가 비활성화되어 FSM이 동작한다.
<50ns~150ns: 동전 투입>
동전 신호(coin=1)입력 시, 상승 클럭 타이밍에 따라 잔액(balance)이 증가한다.
마지막 4번째 동전 투입 후 잔액(balance) : 0011
<130ns>
select = 2'b01 : 첫 번째 음료가 선택된다.
현재 상태(state_reg) : DISPENSE
<150ns>
dispense = 1 : 할인된 첫 번째 음료의 가격(2)이 잔액(4)에서 차감된다.
잔액 : 2(0010)
<190ns>
refund=1 : 반환 신호가 활성화된다.
현재 상태 : RETURN으로 전환된다.
잔돈을 반환해주는 모터가 활성화되며 잔액이 감소한다.
<260ns>
잔액이 0이 됨에 따라 반환 모터가 비활성화된다
'Electronic Engeneering > Hardware Description Language' 카테고리의 다른 글
Mealy FSM 회로 (연속된 0또는 1 입력 검출기) (0) | 2024.12.13 |
---|