i2c 통신은 데이터를 주고 받는 통신 규약이다.
dht11인 경우에는 datasheet에서 통신의 규약을 정의한 케이스이고, 이것은 dht11 모듈만 갖는 종속적인 통신 규약이다.
반면에 i2c 통신은 표준적인 통신 규약이다. i2c 통신이 적용된 모듈은 i2c 통신 규약을 따르다.
이번에는 i2c 통신 규약을 따르는 Text LCD 모듈에 대해서 공부하면서 I2C에 대해서 공부해보자.
i2c 통신 정리 블로그 (교수님 공유)
https://blog.naver.com/specialist0/220645221966
1 대 다 통신이 가능한 통신 규약
i2c 통신 Clock 기준으로 동작하며, Clock 기준으로 데이터를 읽게 된다.
Clock Signal이 High Level일 때마다 딱 한 번 데이터가 읽는다.
=> 신호가 High Level일 때마다 딱 한 번만 데이터를 읽는다.
=> 신호가 High Level일 때마다 딱 한 번만 데이터를 읽기 때문에
=> 신호가 Low Lvel일 때 데이터를 변환하여야 한다.

dht11인 경우에는 Low Level 신호를 기준으로 갖고, PWM의 펄스폭을 기준으로 데이터의 값을 정한다.
번외) UART 통신은 시간으로 기준으로 데이터를 받는다.
= 1초마다 데이터를 한 번만 읽는다.
= 4초면 4BIT 데이터를 읽게 되는 것이다.
하나의 Master에 아래의 그림과 같이 Slave 모듈을 연결할 수 있다.
I2C 버스는 SDA(Serial Data)과 SCL(Serial Clock) 신호선으로 구성되어 있다.
=> 역활: 양방향 데이터 전송을 담당
=> 의미: Master와 Slave간에 데이터를 양방향으로 송수신한다.
=> 데이터 비트, 주소 비트, ACK/NACK 비트 등을 전송합니다.
=> 역활 : 통신의 동기화를 위한 Clock 신호를 제공
=> 의미 : 통신의 동기화를 위한 클럭 신호를 제공함으로서 데이터 송수신 타이밍을 제공
=> Mater 쪽에서 Clock 신호를 제공하는 동시에 제어를 하게 된다.
=> SDA 선의 데이터 샘플링 및 변경 타이밍을 결정합니다.
=> SDA 신호선은 양방향으로 가능하지만, 일반적으로 SCL 신호선은 Master에서 Slave으로 Clock Signal을 전달한다.

SCL 신호선과 SDA 신호선은 아래와 같은 Pulse wave를 갖고 송수신하게 된다.

SCL 신호선에서 High-Level일때, SDA 신호선의 데이터 값을 읽기 때문에 SCL 신호선이 High Level 일때, SDA 신호선의 신호 값을 0 or 1로 유지해야 한다.
만약, 0 or 1이 아닌 값이 변하는 상태라면 Start or End Signal을 의미한다.
SCL 신호선이 High level일 때, SDA 신호선이 Negative edge이라면 Master 측에서 Slave에게 Start Signal을 전송하는 것을 의미한다.
반대로 SCL 신호선이 High level일 때, SDA 신호선이 Positive edge이라면 Master 측에서 Slave에게 End Signal을 전송하는 것을 의미한다.

ACK : Slayer측에서 Master쪽으로 보내는 Responce signal
=> Slayer측에서 Master에게 "어, 신호 잘 받았어."를 알려주는 신호
SDA 신호선은 아래 단계에 걸쳐 데이터 전송된다.
1단계) Start Signal 전송
2단계) Slave의 Address (7bit) 을 전송하여 어떤 Slave에게 Read/Write을 할지를 해당 Slave에게 전송한다.
3단계) 해당 Slave에 Read or Write할지를 전송 (Read : 1, Write : 0)
4단계) 해당 Slave측에서 Master에게 ACK or NACK 신호를 전송
=> ACK은 Slave측에서 신호를 잘 받았다고 반응하는 신호
=> NACK은 Slave측에서 신호를 못 받았다고 반응하는 신호
5단계) Master에서 Slave측으로 데이터를 전송한다.
6단계) 해당 Slave측에서 Master에게 ACK or NACK 신호를 전송
7단계) sda신호선의 상승엣지 발생시 종료
https://blog.naver.com/specialist0/220645221966
module I2C_master (
input clk, reset_p,
input [6:0] addr, // Slave의 주소는 7bit이다.
input rd_wr, // 데이터를 read할것인지 write할 것인지
input [7:0] data, // Slave쪽으로 보낼 데이터의 크기는 8bit
input comm_go, // 1을 주면 통신 시작
output reg scl, sda,
output reg [15:0] led );
// 7개의 state로 정의
// 블로그 게시글 참고
parameter IDLE = 7'b000_0001;
parameter COMM_START = 7'b000_0010;
parameter SEND_ADDR = 7'b000_0100;
parameter RD_ACK = 7'b000_1000;
parameter SEND_DATA = 7'b001_0000;
parameter SCL_STOP = 7'b010_0000;
parameter COMM_STOP = 7'b100_0000;
// Slave 주소 + Master 쪽에서 Read할것인지 Write할것인지를 담고 있는 8bit wire
wire [7:0] addr_rw;
assign addr_rw = {addr, rd_wr};
// I2C 통신에서 사용하는 Clock signal은 "일반적으로" 100kHz를 사용 == 10us period 인 Clock Pulse 생성
// Master에서 Clock Speed을 정해서 사용할 수 있음.
// I2C 통신의 Clock
wire clk_usec;
clock_div_100 usec_clk (.clk(clk), .reset_p(reset_p), .clk_div_100_nedge(clk_usec));
//
reg [2:0] counter_usec5; // 10us 주기인 Clock Pulse를 만들기 위한 Register
reg scl_e; // scl 신호선의 enable
// 10usec 주기인 Clock Pulse를 만들기
always @(posedge clk or posedge reset_p) begin
if(reset_p) begin
counter_usec5 = 0;
scl = 1; // IDLE 상태에서는 High-level이다.
end
else if(scl_e) begin
if(clk_usec) begin
if(counter_usec5 >= 4) begin
counter_usec5 = 0;
scl = ~scl; // SCL 신호선의 값을 Toggle시킨다.
end
else counter_usec5 = counter_usec5 + 1;
end
end
else if(!scl_e) begin
scl = 1; // SCL 신호선이 Disable이면 1로 초기화
counter_usec5 = 0;
end
end
// Get edge of 10usec인 Clock Pulse
wire scl_nedge, scl_pedge;
edge_detector_n scl_edge (.clk(clk), .reset_p(reset_p), .cp(scl), .p_edge(scl_pedge), .n_edge(scl_nedge));
// Get positive edge of Comm-go
wire comm_go_pedge;
edge_detector_n _edge (.clk(clk), .reset_p(reset_p), .cp(comm_go), .p_edge(comm_go_pedge));
reg [6:0] state, next_state;
always @(negedge clk or posedge reset_p)begin
if(reset_p)state = IDLE;
else state = next_state;
end
reg [2:0] cnt_bit;
reg stop_flag;
always @(posedge clk or posedge reset_p)begin
if(reset_p)begin
next_state = IDLE;
scl_e = 0;
sda = 1;
cnt_bit = 7;
stop_flag = 0;
end
else begin
case(state)
IDLE:begin
scl_e = 0;
sda = 1;
if(comm_go_pedge)next_state = COMM_START;
end
COMM_START:begin
sda = 0;
scl_e = 1;
next_state = SEND_ADDR;
end
SEND_ADDR:begin
if(scl_nedge)sda = addr_rw[cnt_bit]; //하강엣지일때 데이터를 sda에 주면
if(scl_pedge)begin //상승엣지일때 슬레이브의 칩이 알아서 읽음
if(cnt_bit == 0)begin
cnt_bit = 7;
next_state = RD_ACK;
end
else cnt_bit = cnt_bit - 1;
end
end
RD_ACK:begin
if(scl_nedge)sda = 'bz;
else if(scl_pedge)begin
if(stop_flag)begin
stop_flag = 0;
next_state = SCL_STOP;
end
else begin
stop_flag = 1;
next_state = SEND_DATA;
end
end
end
SEND_DATA:begin
if(scl_nedge)sda = data[cnt_bit]; //하강엣지일때 데이터를 sda에 주면
if(scl_pedge)begin //상승엣지일때 슬레이브의 칩이 알아서 읽음
if(cnt_bit == 0)begin
cnt_bit = 7;
next_state = RD_ACK;
end
else cnt_bit = cnt_bit - 1;
end
end
SCL_STOP:begin
if(scl_nedge)sda = 0;
else if(scl_pedge)next_state = COMM_STOP;
end
COMM_STOP:begin
if(counter_usec5 >= 3)begin
scl_e = 0;
sda = 1;
next_state = IDLE;
end
end
endcase
end
end
endmodule
시작신호

주소 보냄 010_0111

임피던스 후 데이터 보냄 0100_0000
