MCU처럼 내부 I2C IP를 쓰지 않고, Verilog로 I2C 신호 생성(SCL, SDA)을 직접 구현
I2C 통신의 각 단계를 FSM으로 하드웨어화
사용 코드 _ 상위 모듈
module i2c_txtlcd_top(
input clk, reset_p, // 시스템 클록(예: 100MHz), 리셋(Active-high)
input [3:0] btn, // 4개의 버튼 입력
output scl, sda, // I2C 통신선(SCL:클록, SDA:데이터) - LCD와 연결
output [15:0] debug_led // 디버그용 LED 출력 (내부 상태 등 표시)
);
// ----------------------------------------
// 1. 딜레이 타이머: LCD 전원 투입 직후 안정화 대기(약 80ms)
// LCD의 전원이 켜진 후 바로 명령을 보내면 오동작할 수 있으므로,
// 최초 리셋 후 약 80ms 동안 대기하는 카운터 회로
// ----------------------------------------
integer cnt_clk; // 카운트 변수(정수형)
reg count_clk_e; // 카운트 활성화 신호
always @(negedge clk or posedge reset_p) begin
if (reset_p)
cnt_clk = 0; // 리셋 시 카운터 0으로 초기화
else if (count_clk_e)
cnt_clk = cnt_clk + 1; // count_clk_e=1이면 카운트 증가
else
cnt_clk = 0; // count_clk_e=0이면 카운트 초기화(대기완료 후 재사용)
end
// ----------------------------------------
// 2. 버튼 상승 에지 검출 회로 (debounce 포함)
// 버튼이 눌릴 때마다 1클록 동안만 1로 출력 (btn_pedge)
// 매번 버튼을 누를 때마다 중복동작/챠터링 없이 이벤트를 생성
// ----------------------------------------
wire [3:0] btn_pedge; // 각 버튼에 대한 상승에지 출력
button_cntr btn0(.clk(clk), .reset_p(reset_p), .btn(btn[0]), .btn_pedge(btn_pedge[0]));
button_cntr btn1(.clk(clk), .reset_p(reset_p), .btn(btn[1]), .btn_pedge(btn_pedge[1]));
button_cntr btn2(.clk(clk), .reset_p(reset_p), .btn(btn[2]), .btn_pedge(btn_pedge[2]));
button_cntr btn3(.clk(clk), .reset_p(reset_p), .btn(btn[3]), .btn_pedge(btn_pedge[3]));
// ----------------------------------------
// 3. LCD 전송 관련 신호 정의
// send_buffer : 전송할 데이터(명령/문자) 저장
// send : 데이터 전송 요청 신호(1클록 펄스)
// rs : 1이면 데이터, 0이면 명령어 전송 (LCD RS 신호 역할)
// busy : LCD 전송중일 때 1 (완료까지 명령 추가 금지)
// ----------------------------------------
reg [7:0] send_buffer; // I2C LCD에 전송할 1바이트 데이터
reg send, rs; // 전송 요청/데이터-명령 전환
wire busy; // LCD 송신기 busy 상태
i2c_lcd_send_byte i2c_shift_SIPO(
.clk(clk), .reset_p(reset_p),
.addr(7'h3f), // I2C LCD 주소(0x3F, 보드에 따라 0x27일 수도 있음)
.send_buffer(send_buffer), .send(send), .rs(rs),
.scl(scl), .sda(sda), .busy(busy),
.debug_led(debug_led)
);
// ----------------------------------------
// 4. FSM 상태 정의 (6비트 One-Hot 인코딩)
// 각 상태는 1비트씩 할당하여 가독성과 안정성을 높임
// ----------------------------------------
parameter IDLE = 6'b00_0001; // 대기/초기화 전 상태
parameter INIT = 6'b00_0010; // LCD 초기화 명령 송신 중
parameter SEND_BYTE = 6'b00_0100; // "0~9" 문자 출력
parameter SHIFT_RIGHT_DISPLAY = 6'b00_1000; // 화면 오른쪽 이동 명령
parameter SHIFT_LEFT_DISPLAY = 6'b01_0000; // 화면 왼쪽 이동 명령
parameter SEND_STRING = 6'b10_0000; // HELLO 문자열 출력 상태
reg [5:0] state, next_state; // 현재/다음 FSM 상태
// ----------------------------------------
// 5. FSM 현재 상태 레지스터 (비동기 리셋/네거티브 엣지 클록)
// - Vivado 등에서는 항상 posedge clk 권장 (일부 IP/블록에 맞춘다면 예외)
// ----------------------------------------
always @(negedge clk or posedge reset_p) begin
if (reset_p)
state <= IDLE;
else
state <= next_state;
end
// ----------------------------------------
// 6. FSM 동작/제어 변수 선언
// - cnt_data: INIT, SEND_BYTE에서 송신 바이트 위치 인덱스
// - init_flag: LCD 초기화 완료 플래그
// - hello: "HELLO" 문자열 저장(ASCII, 5글자)
// - cnt_string: HELLO 출력 시 문자의 위치 인덱스
// ----------------------------------------
reg [2:0] cnt_data; // LCD 초기화 or 문자 출력시 데이터 인덱스
reg init_flag; // 초기화 완료 플래그
reg [8*5-1:0] hello; // "HELLO" 문자열 저장(40비트=5바이트)
reg [2:0] cnt_string; // HELLO 출력용 인덱스
// ----------------------------------------
// 7. FSM 메인 로직 (모든 상태 변화, 데이터 처리, LCD 전송 요청 제어)
// 동기처리: posedge clk 기준으로 모든 제어 신호/상태 갱신
// ----------------------------------------
always @(posedge clk or posedge reset_p) begin
if (reset_p) begin
// 리셋 시 모든 변수/상태를 초기화
next_state <= IDLE;
init_flag <= 0;
count_clk_e <= 0;
cnt_data <= 0;
send <= 0;
send_buffer <= 0;
rs <= 0;
hello <= "HELLO";
cnt_string <= 5; // 5글자 모두 출력해야 하므로 5로 시작
end else begin
case (state)
// -------------------------
// IDLE: LCD 초기화 완료 전에는 딜레이 카운트,
// 초기화 완료 후에는 각 버튼 이벤트 대기
// -------------------------
IDLE: begin
if (init_flag) begin
// 초기화 완료 시, 버튼 입력별로 상태 전이
if (btn_pedge[0]) next_state <= SEND_BYTE;
if (btn_pedge[1]) next_state <= SHIFT_RIGHT_DISPLAY;
if (btn_pedge[2]) next_state <= SHIFT_LEFT_DISPLAY;
if (btn_pedge[3]) next_state <= SEND_STRING;
end else begin
// 초기화 전: 8_000_000 클록 동안 카운트 (약 80ms, 100MHz 기준)
if (cnt_clk <= 32'd8_000_000)
count_clk_e <= 1; // 카운터 동작 시작
else begin
next_state <= INIT; // 시간 지나면 초기화 상태 진입
count_clk_e <= 0; // 카운트 정지
end
end
end
// -------------------------
// INIT: LCD 초기화 시퀀스 명령을 차례로 송신 (6단계)
// -------------------------
INIT: begin
if (busy) begin
send <= 0; // busy 상태에서는 send 신호 내려줌
// 모든 명령 송신 완료하면 플래그 set, 상태 전이
if (cnt_data >= 6) begin
cnt_data <= 0;
next_state <= IDLE;
init_flag <= 1; // 초기화 완료 플래그 ON
end
end else if (!send) begin
// busy==0 & send==0이면 명령어 준비 및 전송
case (cnt_data)
0: send_buffer <= 8'h33; // Function set (재설정, 8비트)
1: send_buffer <= 8'h32; // Function set (4비트 모드)
2: send_buffer <= 8'h28; // 2라인, 5x8도트, 4비트
3: send_buffer <= 8'h0C; // Display ON, Cursor OFF
4: send_buffer <= 8'h01; // Display Clear
5: send_buffer <= 8'h06; // Entry Mode Set (오른쪽 이동)
endcase
send <= 1; // 실제 전송 요청 신호 (1클록)
cnt_data <= cnt_data + 1; // 다음 명령 준비
end
end
// -------------------------
// SEND_BYTE: 버튼0 누를 때 "0~9" 문자(ASCII) 출력 (한 번에 1개씩)
// -------------------------
SEND_BYTE: begin
if (busy) begin
send <= 0; // busy==1일 때 send 신호 내림
next_state <= IDLE; // 완료되면 IDLE로 복귀
if (cnt_data >= 9) // 9까지 출력하면 카운터 리셋
cnt_data <= 0;
else
cnt_data <= cnt_data + 1; // 다음 숫자 준비
end else begin
rs <= 1; // 데이터 모드 (문자 출력)
send_buffer <= "0" + cnt_data; // 아스키 코드 '0'+n
send <= 1; // 전송 요청
end
end
// -------------------------
// SHIFT_RIGHT_DISPLAY: 버튼1 → 화면 우측 이동 명령 (한 칸)
// -------------------------
SHIFT_RIGHT_DISPLAY: begin
if (busy) begin
next_state <= IDLE;
send <= 0;
end else begin
rs <= 0; // 명령어 모드
send_buffer <= 8'h1C; // Shift Right 명령
send <= 1;
end
end
// -------------------------
// SHIFT_LEFT_DISPLAY: 버튼2 → 화면 좌측 이동 명령 (한 칸)
// -------------------------
SHIFT_LEFT_DISPLAY: begin
if (busy) begin
next_state <= IDLE;
send <= 0;
end else begin
rs <= 0; // 명령어 모드
send_buffer <= 8'h18; // Shift Left 명령
send <= 1;
end
end
// -------------------------
// SEND_STRING: 버튼3 → "HELLO" 문자열(5글자) 출력
// cnt_string을 5~1까지 하나씩 감소하며 문자 전송
// busy==0일 때만 실제 문자 전송, busy==1이면 send 내리고 대기
// -------------------------
SEND_STRING: begin
if (busy) begin
send <= 0; // busy==1이면 send 내림
if (cnt_string == 0) begin
cnt_string <= 5; // 모든 문자 송신 끝나면 인덱스 리셋
next_state <= IDLE;
end else begin
next_state <= SEND_STRING; // 아직 남아있으면 반복
end
end else if (send == 0) begin
// HELLO 각 문자 하나씩 전송 (MSB→LSB 순서)
case (cnt_string)
5: send_buffer <= hello[39:32]; // 'H'
4: send_buffer <= hello[31:24]; // 'E'
3: send_buffer <= hello[23:16]; // 'L'
2: send_buffer <= hello[15:8]; // 'L'
1: send_buffer <= hello[7:0]; // 'O'
endcase
rs <= 1; // 데이터 모드
send <= 1; // 전송 요청
cnt_string <= cnt_string - 1; // 다음 문자 준비
next_state <= SEND_STRING;
end
end
endcase
end
end
endmodule
사용 코드 _ 2c_lcd_send_byte
module i2c_lcd_send_byte(
input clk, reset_p, // 시스템 클록, 비동기 리셋(Active-high)
input [6:0] addr, // I2C LCD 슬레이브 주소 (7비트)
input [7:0] send_buffer, // 송신할 LCD 데이터(명령/문자)
input send, rs, // 송신 트리거(1펄스), RS(Data/Command)
output scl, sda, // I2C 신호(LCD 연결)
output reg busy, // 동작중 busy 표시
output [15:0] debug_led // 디버깅용 LED 출력(상태 등)
);
// ------------------------------------------------------------
// FSM 상태 정의 (One-hot encoding: 각 상태마다 비트 1개)
// - IDLE: 대기
// - SEND_HIGH_NIBBLE_DISABLE: 상위 4비트, EN=0(Disable)
// - SEND_HIGH_NIBBLE_ENABLE: 상위 4비트, EN=1(Enable)
// - SEND_LOW_NIBBLE_DISABLE: 하위 4비트, EN=0(Disable)
// - SEND_LOW_NIBBLE_ENABLE: 하위 4비트, EN=1(Enable)
// - SEND_DISABLE: 마무리 EN=0(Disable)
// HD44780 4bit 모드는 EN 신호(Enable)로 데이터 latch,
// EN=1 → 데이터 latch, EN=0 → 명령 전송 완료!
// ------------------------------------------------------------
parameter IDLE = 6'b00_0001;
parameter SEND_HIGH_NIBBLE_DISABLE = 6'b00_0010;
parameter SEND_HIGH_NIBBLE_ENABLE = 6'b00_0100;
parameter SEND_LOW_NIBBLE_DISABLE = 6'b00_1000;
parameter SEND_LOW_NIBBLE_ENABLE = 6'b01_0000;
parameter SEND_DISABLE = 6'b10_0000;
reg [7:0] data; // I2C 버퍼(실제 전송될 8비트)
reg comm_start; // I2C 마스터 송신 트리거
// ------------------------------------------------------------
// 송신 신호(send) 상승 에지 검출 (한 번만 트리거)
// ------------------------------------------------------------
wire send_pedge;
edge_detector_p ed_start(.clk(clk), .reset_p(reset_p), .cp(send), .p_edge(send_pedge));
// ------------------------------------------------------------
// 100클록(1us) 주기의 내부 유닛 클록 생성 (전송 타이밍 제어용)
// LCD에 데이터 전송시마다 최소 1us 이상 EN 신호를 유지해야 함
// ------------------------------------------------------------
wire clk_usec;
clock_div_100 usec_clock(.clk(clk), .reset_p(reset_p), .nedge_div_100(clk_usec));
// ------------------------------------------------------------
// 딜레이 카운터 (count_usec)
// - 각 단계마다 일정 시간동안 데이터/EN 유지(약 200us)
// - count_usec_e=1일 때 동작, 0일 때 즉시 리셋
// ------------------------------------------------------------
reg [21:0] count_usec; // 최대 4백만 us(4초)까지 카운트 가능
reg count_usec_e; // 카운트 enable 신호
always @(negedge clk, posedge reset_p) begin
if (reset_p) begin
count_usec = 0;
end else begin
if (clk_usec && count_usec_e)
count_usec = count_usec + 1; // 1us마다 증가
else if (!count_usec_e)
count_usec = 0; // 동작 끝나면 즉시 0으로 초기화
end
end
// ------------------------------------------------------------
// FSM 상태 레지스터(비동기 리셋, negedge clk)
// - 보통 posedge clk 권장, 특이하게 negedge 사용
// ------------------------------------------------------------
reg [5:0] state, next_state;
always @(negedge clk or posedge reset_p) begin
if (reset_p) begin
state = IDLE;
end else begin
state = next_state;
end
end
// ------------------------------------------------------------
// FSM 메인 로직
// 각 상태마다 LCD용 4비트 데이터, EN 신호, 타이밍, busy/comm_start 제어
// ------------------------------------------------------------
always @(posedge clk or posedge reset_p) begin
if (reset_p) begin
next_state = IDLE;
busy = 0;
comm_start = 0;
count_usec_e = 0;
data = 0;
end else begin
case (state)
// ------------------------------------------
// 1. IDLE: 대기상태, 송신 트리거(send_pedge) 대기
// ------------------------------------------
IDLE: begin
if (send_pedge) begin
next_state = SEND_HIGH_NIBBLE_DISABLE;
busy = 1; // busy 시작
end
end
// ------------------------------------------
// 2. 상위 nibble(4비트) 전송, EN=0 (Disable)
// - 하드웨어 LCD(HD44780)는 4비트 데이터+RS+RW+EN+BL 조합 사용
// - {D7~D4, BL=1, EN, RW=0, RS}
// - EN=0에서 데이터를 준비한 후 EN=1로 latch 시킨다
// ------------------------------------------
SEND_HIGH_NIBBLE_DISABLE: begin
if (count_usec <= 22'd200) begin // 200us 대기
data = {send_buffer[7:4], 3'b100, rs}; // EN=0
comm_start = 1; // I2C 마스터 송신 요청
count_usec_e = 1;
end else begin
next_state = SEND_HIGH_NIBBLE_ENABLE;
count_usec_e = 0;
comm_start = 0;
end
end
// ------------------------------------------
// 3. 상위 nibble(4비트) 전송, EN=1 (Enable)
// - EN=1로 데이터 latch
// ------------------------------------------
SEND_HIGH_NIBBLE_ENABLE: begin
if (count_usec <= 22'd200) begin
data = {send_buffer[7:4], 3'b110, rs}; // EN=1
comm_start = 1;
count_usec_e = 1;
end else begin
next_state = SEND_LOW_NIBBLE_DISABLE;
count_usec_e = 0;
comm_start = 0;
end
end
// ------------------------------------------
// 4. 하위 nibble(4비트) 전송, EN=0 (Disable)
// ------------------------------------------
SEND_LOW_NIBBLE_DISABLE: begin
if (count_usec <= 22'd200) begin
data = {send_buffer[3:0], 3'b100, rs}; // EN=0
comm_start = 1;
count_usec_e = 1;
end else begin
next_state = SEND_LOW_NIBBLE_ENABLE;
count_usec_e = 0;
comm_start = 0;
end
end
// ------------------------------------------
// 5. 하위 nibble(4비트) 전송, EN=1 (Enable)
// ------------------------------------------
SEND_LOW_NIBBLE_ENABLE: begin
if (count_usec <= 22'd200) begin
data = {send_buffer[3:0], 3'b110, rs}; // EN=1
comm_start = 1;
count_usec_e = 1;
end else begin
next_state = SEND_DISABLE;
count_usec_e = 0;
comm_start = 0;
end
end
// ------------------------------------------
// 6. 마지막으로 EN=0 한번 더(Disable)
// - 프로토콜 규격에 따라 EN=1→EN=0 토글 필요
// ------------------------------------------
SEND_DISABLE: begin
if (count_usec <= 22'd200) begin
data = {send_buffer[3:0], 3'b100, rs}; // EN=0
comm_start = 1;
count_usec_e = 1;
end else begin
next_state = IDLE;
count_usec_e = 0;
comm_start = 0;
busy = 0; // busy 해제
end
end
endcase
end
end
// ------------------------------------------------------------
// 하위 모듈: I2C 마스터(별도 구현 필요)
// - comm_start=1일 때 data값을 addr 주소로 전송
// - rd_wr=0 (LCD는 항상 Write 동작만)
// ------------------------------------------------------------
I2C_master master (
.clk(clk), .reset_p(reset_p),
.addr(addr),
.data(data), .rd_wr(0), .comm_start(comm_start),
.scl(scl), .sda(sda), .debug_led(debug_led)
);
endmodule
사용 코드 _ I2C_master
module I2C_master(
input clk, reset_p, // 시스템 클록, 비동기 리셋(Active-high)
input [6:0] addr, // I2C 슬레이브 7비트 주소
input [7:0] data, // I2C 송신 데이터(8비트)
input rd_wr, // 0: Write, 1: Read
input comm_start, // 송신 시작 트리거(1클록 펄스)
output reg scl, // I2C 클록(Serial Clock Line)
output reg sda, // I2C 데이터(Serial Data Line, open-drain)
output reg [6:0] debug_led // 디버깅(상태 표시 등)
);
// --------------------------------------------------------------
// 1. FSM 상태 정의 (One-hot 인코딩)
// --------------------------------------------------------------
parameter IDLE = 7'b000_0001; // 대기
parameter COMM_START = 7'b000_0010; // Start 조건 생성
parameter SEND_ADDR = 7'b000_0100; // 슬레이브 주소(7비트)+R/W 전송
parameter RD_ACK = 7'b000_1000; // ACK 비트 읽기 및 핸들링
parameter SEND_DATA = 7'b001_0000; // 데이터 8비트 전송
parameter SCL_STOP = 7'b010_0000; // Stop 조건 생성 준비
parameter COMM_STOP = 7'b100_0000; // Stop 조건(전송 완료)
// --------------------------------------------------------------
// 2. 1us 주기 내부 클록 생성 (I2C SCL 타이밍, 전송 속도 제어)
// I2C 표준모드 100kHz 이하 보장 (여유있게 클록 나눔)
// --------------------------------------------------------------
wire clk_usec;
clock_div_100 usec_clock(
.clk(clk),
.reset_p(reset_p),
.nedge_div_100(clk_usec)
);
// --------------------------------------------------------------
// 3. 신호(에지) 검출: comm_start, scl (펄스, 상승/하강에지)
// --------------------------------------------------------------
wire comm_start_pedge, scl_nedge, scl_pedge;
edge_detector_p ed_start(
.clk(clk),
.reset_p(reset_p),
.cp(comm_start),
.p_edge(comm_start_pedge)
);
edge_detector_p ed_scl(
.clk(clk),
.reset_p(reset_p),
.cp(scl),
.n_edge(scl_nedge),
.p_edge(scl_pedge)
);
// --------------------------------------------------------------
// 4. SCL(클록) 생성 FSM
// - scl_e=1: 내부 카운터(5us 단위)로 scl 토글(약 100kHz)
// - scl_e=0: scl을 1로 유지(Stop/Idle), 동작 멈춤
// --------------------------------------------------------------
reg [2:0] count_usec5; // 5us 단위 카운터 (5클록: SCL period 조절)
reg scl_e; // SCL 토글 동작 enable
always @(posedge clk, posedge reset_p) begin
if (reset_p) begin
count_usec5 = 0;
scl = 0;
end else if (scl_e) begin
if (clk_usec) begin
if (count_usec5 >= 4) begin // 5us마다 SCL 토글
count_usec5 = 0;
scl = ~scl;
end else count_usec5 = count_usec5 + 1;
end
end else if (!scl_e) begin
count_usec5 = 0;
scl = 1; // SCL idle state(Stop 등에서 high 유지)
end
end
// --------------------------------------------------------------
// 5. FSM(상태전이) 레지스터 (negedge clk, 비동기 리셋)
// --------------------------------------------------------------
reg [6:0] state, next_state;
always @(negedge clk, posedge reset_p) begin
if (reset_p) state = IDLE;
else state = next_state;
end
// --------------------------------------------------------------
// 6. 송신 데이터 구성 및 비트 전송 인덱스
// --------------------------------------------------------------
wire [7:0] addr_rd_wr; // [7:1]: 주소, [0]: R/W
assign addr_rd_wr = {addr, rd_wr};
reg [2:0] cnt_bit; // 7~0 (비트별 전송 인덱스)
reg stop_flag; // 마지막(데이터 후) Stop Condition 판별
// --------------------------------------------------------------
// 7. I2C 전송 FSM: 각 상태별 동작
// --------------------------------------------------------------
always @(posedge clk or posedge reset_p) begin
if (reset_p) begin
next_state = IDLE;
scl_e = 0;
sda = 1;
cnt_bit = 7; // 주소/데이터 7~0 순서 전송
stop_flag = 0;
end else begin
case (state)
// ------------------------------------------------------
// IDLE: 대기, comm_start 펄스 들어오면 Start 조건 준비
// ------------------------------------------------------
IDLE: begin
if (comm_start_pedge) next_state = COMM_START;
scl_e = 0; // SCL 동작 정지
sda = 1; // SDA high (idle)
end
// ------------------------------------------------------
// COMM_START: Start 조건 생성 (SCL=1 상태에서 SDA=1→0)
// I2C Start: SDA가 SCL high에서 1→0으로 변하면 생성
// ------------------------------------------------------
COMM_START: begin
sda = 0; // SDA low(시작)
scl_e = 1; // SCL 토글 enable (SCL: 1→0 변환시 동작)
next_state = SEND_ADDR;
end
// ------------------------------------------------------
// SEND_ADDR: 슬레이브 주소+R/W 비트 송신 (MSB부터)
// SCL falling edge에서 데이터 셋, rising edge에서 샘플
// ------------------------------------------------------
SEND_ADDR: begin
if (scl_nedge) sda = addr_rd_wr[cnt_bit]; // 데이터 출력
if (scl_pedge) begin
if (cnt_bit == 0) begin
cnt_bit = 7; // 데이터 전송 준비
next_state = RD_ACK; // ACK 비트 수신
end else cnt_bit = cnt_bit - 1;
end
end
// ------------------------------------------------------
// RD_ACK: 슬레이브의 ACK 비트 수신
// 송신 시 SDA를 high-Z(‘z’)로 두고, rising edge에서 ACK 감지
// ------------------------------------------------------
RD_ACK: begin
if (scl_nedge) sda = 'bz; // 버스 개방(ACK 받아야 함)
else if (scl_pedge) begin
if (stop_flag) begin // 마지막 ACK(데이터 후)면 Stop으로
stop_flag = 0;
next_state = SCL_STOP;
end else begin // 아니면 데이터 송신
stop_flag = 1;
next_state = SEND_DATA;
end
end
end
// ------------------------------------------------------
// SEND_DATA: 8비트 데이터(MSB부터) 전송
// ------------------------------------------------------
SEND_DATA: begin
if (scl_nedge) sda = data[cnt_bit];
if (scl_pedge) begin
if (cnt_bit == 0) begin
cnt_bit = 7; // 비트 인덱스 리셋
next_state = RD_ACK; // 데이터 후 ACK
end else cnt_bit = cnt_bit - 1;
end
end
// ------------------------------------------------------
// SCL_STOP: Stop 조건 준비
// SDA=0, SCL=1에서 SDA=0→1로 변할 때 Stop 조건
// ------------------------------------------------------
SCL_STOP: begin
if (scl_nedge) sda = 0; // SDA low, SCL=0
if (scl_pedge) next_state = COMM_STOP;
end
// ------------------------------------------------------
// COMM_STOP: Stop 조건 유지(3us) 후 FSM 리셋(Idle)
// ------------------------------------------------------
COMM_STOP: begin
if (count_usec5 >= 3) begin // 약 3us(짧은 여유시간)
scl_e = 0; // SCL 동작 정지
sda = 1; // SDA high(버스 idle)
next_state = IDLE;
end
end
endcase
end
end
endmodule