2024.8.13 수업날
< 조이스틱 3색 led 제어 >
이번에는 조이스틱을 사용하여 3색 led의 빨강, 초록색을 pwm으로 제어해본다.
2개의 색만 사용하는 이유는 조이스틱에서 x,y 결과값만 사용하기 때문이다.
adc_sequence2_led_top 소스코드
//////////////////////////////////////////////////// 2024.8.13
/////////////////////////// 조이스틱으로 3색 led 제어
module adc_sequence2_led_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
pwm_128step pwm_r(
.clk(clk), .reset_p(reset_p),
.duty(adc_value_x[11:5]), // 7비트 정도만 쓰기 대문에 정밀도는 떨어지지만, 안정도는 높다
.pwm(led_r));
pwm_128step pwm_g(
.clk(clk), .reset_p(reset_p),
.duty(adc_value_y[11:5]), // 7비트 정도만 쓰기 대문에 정밀도는 떨어지지만, 안정도는 높다
.pwm(led_g));
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 수정해야하는 곳
< I2C_LCD >
I2C 통신으로 LCD를 제어해본다. 16x2 LCD를 사용하며, 각 행에 16문자를 쓸 수 있다.
기존의 LCD는 핀이 너무 많아 사용에 불편함이 있었기 때문에 아래 사진과 같은 I2C모듈이 뒤에 납땜된 형태로 되어있다.
아래의 사진은 I2C 통신을 하기 위해서 state를 정한 순서이다.
SCL이 HIGH인 상태에서는 SDA가 HIGH에서 LOW가 되면 Start 상태이고,
SCL이 HIGH인 상태에서 SDA가 LOW에서 HIGH가 되면 Stop 상태가 된다.
첫번째 사진은 7비트를 전송하는 주소 패킷과 달리 데이터 패킷은 8비트를 보내는 것을 나타낸다.
두번째 사진은 첫번째 사진을 참고하여 state를 구분한 것이다. 이를 코드로 구현하면 된다.
I2C 통신 프로토콜 : 네이버 블로그 (naver.com)
I2C_master 소스코드
////////////////////////////////////////////////// 2024.8.13
/////////////////////////////// 마스터 디바이스가 데이터를 송신하고 수신하는 과정
module I2C_master(
input clk, reset_p, // 입력 클럭과 리셋 신호 (positive reset)
input [6:0] addr, // 7비트 주소 입력
input [7:0] data, // 8비트 데이터 입력
input rd_wr, comm_go, // 읽기/쓰기 제어 신호, 통신 시작 신호 (읽기=1, 쓰기=0)
output reg sda, scl, // I2C 데이터선(SDA)과 클럭선(SCL) 출력
output reg [6:0] led); // LED 상태 표시 출력
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; // 통신 종료 상태
wire [7:0] addr_rw;
assign addr_rw = {addr, rd_wr}; // 주소와 읽기/쓰기 비트를 결합하여 addr_rw로 설정
wire clk_usec;
clock_div_100 usec_clk(
.clk(clk), .reset_p(reset_p),
.clk_div_100(clk_usec)); // 클럭을 100분의 1로 나누는 모듈 인스턴스화
reg [2:0] count_usec5;
reg scl_e;
always @(posedge clk or posedge reset_p) begin
if(reset_p) begin
count_usec5 = 0; // 리셋 시 5us 카운터 초기화
scl = 1; // SCL을 기본적으로 HIGH로 설정
end
else if(scl_e) begin // SCL이 활성화된 경우
if(clk_usec) begin // 1us 클럭이 들어오면
if(count_usec5 >= 4) begin
count_usec5 = 0; // 5us 이후 SCL 신호 반전
scl = ~scl;
end
else count_usec5 = count_usec5 + 1; // 5us 카운터 증가
end
end
else if(!scl_e) begin // SCL이 비활성화된 경우
count_usec5 = 0; // 카운터 초기화
scl = 1; // SCL을 HIGH로 설정
end
end
wire comm_go_pedge;
edge_detector_n ed_go(
.clk(clk), .reset_p(reset_p),
.cp(comm_go), .p_edge(comm_go_pedge)); // 통신 시작 신호의 상승 에지 검출
wire scl_pedge, scl_nedge;
edge_detector_n ed_scl(
.clk(clk), .reset_p(reset_p),
.cp(scl), .p_edge(scl_pedge), .n_edge(scl_nedge)); // SCL 신호의 상승 및 하강 에지 검출
reg [6:0] state, next_state;
always @(negedge clk or posedge reset_p) begin
if(reset_p) state = IDLE; // 리셋 시 상태를 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; // 리셋 시 다음 상태를 IDLE로 설정
scl_e = 0; // SCL을 비활성화
sda = 1; // SDA를 HIGH로 설정
cnt_bit = 7; // 비트 카운터 초기화
stop_flag = 0; // 종료 플래그 초기화
led = 0; // LED를 모두 끔
end
else begin
case(state)
IDLE: begin
led[0] = 1; // IDLE 상태 LED ON
scl_e = 0; // SCL 비활성화
sda = 1; // SDA HIGH
if(comm_go_pedge) next_state = COMM_START; // 통신 시작 신호가 들어오면 COMM_START 상태로 전환
end
COMM_START: begin
led[1] = 1; // COMM_START 상태 LED ON
sda = 0; // 통신 시작을 위해 SDA LOW로 설정
scl_e = 1; // SCL 활성화
next_state = SEND_ADDR; // 다음 상태는 SEND_ADDR
end
SEND_ADDR: begin
led[2] = 1; // SEND_ADDR 상태 LED ON
if(scl_nedge) sda = addr_rw[cnt_bit]; // SCL 하강 에지에서 주소 비트를 전송
if(scl_pedge) begin
if(cnt_bit == 0) begin
cnt_bit = 7; // 모든 주소 비트 전송 후 비트 카운터 초기화
next_state = RD_ACK; // 다음 상태는 RD_ACK
end
else cnt_bit = cnt_bit - 1; // 다음 비트를 전송하기 위해 비트 카운터 감소
end
end
RD_ACK: begin
led[3] = 1; // RD_ACK 상태 LED ON
if(scl_nedge) sda = 'bz; // 슬레이브로부터 확인 응답을 받기 위해 SDA를 고임피던스 상태로 설정
else if(scl_pedge) begin
if(stop_flag) begin
stop_flag = 0; // 종료 플래그 리셋
next_state = SCL_STOP; // 다음 상태는 SCL_STOP
end
else begin
stop_flag = 1; // 종료 플래그 설정
next_state = SEND_DATA; // 다음 상태는 SEND_DATA
end
end
end
SEND_DATA: begin
led[4] = 1; // SEND_DATA 상태 LED ON
if(scl_nedge) sda = data[cnt_bit]; // SCL 하강 에지에서 데이터 비트를 전송
if(scl_pedge) begin
if(cnt_bit == 0) begin
cnt_bit = 7; // 모든 데이터 비트 전송 후 비트 카운터 초기화
next_state = RD_ACK; // 다음 상태는 RD_ACK
end
else cnt_bit = cnt_bit - 1; // 다음 비트를 전송하기 위해 비트 카운터 감소
end
end
SCL_STOP: begin
led[5] = 1; // SCL_STOP 상태 LED ON
if(scl_nedge) sda = 0; // 통신 종료를 위해 SDA를 LOW로 설정
else if(scl_pedge) next_state = COMM_STOP; // 다음 상태는 COMM_STOP
end
COMM_STOP: begin
led[6] = 1; // COMM_STOP 상태 LED ON
if(count_usec5 >= 3) begin
scl_e = 0; // SCL 비활성화
sda = 1; // SDA를 HIGH로 설정
next_state = IDLE; // 다음 상태는 IDLE
end
end
endcase
end
end
endmodule
I2C_master_top 소스코드
//////////////////////////////// 버튼 2개 입력 받아서 lcd on, lcd off를 할 수 있는모듈
module I2C_master_top(
input clk, reset_p,
input [1:0] btn,
output sda, scl,
output [6:0] led);
reg [7:0] data;
reg comm_go;
I2C_master master ( .clk(clk), .reset_p(reset_p),
.addr(7'h27),
.data(data),
.rd_wr(0), .comm_go(comm_go),
.sda(sda), .scl(scl),
.led(led));
wire [1:0]btn_pedge;
button_cntr_reg_n_p btn_0(
.clk(clk), .reset_p(reset_p),
.btn(btn[0]),
.btn_pedge(btn_pedge[0]));
button_cntr_reg_n_p btn_1(
.clk(clk), .reset_p(reset_p),
.btn(btn[1]),
.btn_pedge(btn_pedge[1]));
always @(posedge clk or posedge reset_p) begin
if(reset_p) begin
data = 0;
comm_go = 0;
end
else begin
if(btn_pedge[0])begin
data = 8'b0000_0000;
comm_go = 1;
end
else if(btn_pedge[1]) begin
data = 8'b1111_1111;
//data = 8'b0000_1000;
comm_go = 1;
end
else comm_go = 0;
end
end
endmodule
아래는 .xdc에서 수정해야하는 부분이다.
아래의 사진은 I2C_master_top 소스코드를 오실로스코프로 파형을 확인한 것인데,
SEND_DATA에서 data = 8'b1111_1111; 를 나타낸 것이다.
조이스틱 3색 led 제어, I2C_LCD 끝!
'Harman 세미콘 아카데미 > Verilog' 카테고리의 다른 글
2024.8.14 [Verilog] - 문잠김 구현이 가능한 도어락 만들기 프로젝트 (0) | 2024.08.14 |
---|---|
2024.8.14 [Verilog]20 - I2C_LCD 텍스트 제어 (0) | 2024.08.14 |
2024.8.12 [Verilog]18 - 주방타이머 수정, 조이스틱 ADC (0) | 2024.08.12 |
2024.7.11 [Verilog] 다기능 선풍기 만들기 팀 프로젝트 (0) | 2024.07.11 |
2024.7.10 [Verilog]17 - PWM으로 servo 모터 제어하기, ADC (0) | 2024.07.10 |