8/23 I2C 커맨드 레지스터

정유석·2024년 8월 23일

교육 - 베릴로그

목록 보기
26/28

커맨드 레지스터 사용

('*' 또는 '-'는 don't care로써 0또는1 아무거나 써도 상관없음)

히타치 데이터 시트 24p~25p를 참조하여 원하는 동작을 제어

  • SET DDRAM ADDRESS : ADD를 조절하여 주소지를 정하여 커서 위치 정할 수 있음, 위 아래 두줄이 있고 한줄에 40칸이 가능하고 화면에는 16칸이 표현, 00_0000 윗줄 첫간, 10_0000 아랫줄 첫칸
module i2c_txtlcd_top(
    input clk, reset_p,
    input [3:0] btn,
    output scl, sda,
    output [15:0]led);
    
    parameter IDLE = 6'b00_0001;
    parameter INIT = 6'b00_0010;
    parameter SEND_DATA = 6'b00_0100;
    parameter SEND_COMMAND = 6'b01_0000;
    
     wire clk_usec;
    clock_div_100 usec_clk (.clk(clk), .reset_p(reset_p), .clk_div_100_nedge(clk_usec));
    
    reg [21:0] count_usec; //3초 대기를 위함
    reg count_usec_e;
    always @(negedge clk or posedge reset_p)begin
        if(reset_p)count_usec = 0;
        else if(clk_usec && count_usec_e)count_usec = count_usec + 1; //S_IDLE에서 count_usec_e를 1주면 카운트 시작
        else if(!count_usec_e)count_usec = 0; //count_usec_e가 0이되면 리셋
    end
    
    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]));
    
    reg [7:0] send_buffer;
    reg rs, send;
    wire busy;
    i2c_lcd_send_byte txtlcd(.clk(clk), .reset_p(reset_p),
        .addr(7'h27),
        .send_buffer(send_buffer), //보낼 데이터
        .rs(rs), .send(send), // send가 1됬을때 1바이트 보냄
        .scl(scl), .sda(sda),
        .busy(busy), //보내는 동안 시간이 걸리기 때문에 busy가 0일때만 send 가능    
        .led(led));
    
    reg [5:0] state, next_state; //6비트짜리 상태들
    always @(negedge clk or posedge reset_p)begin
        if(reset_p)state = IDLE;
        else state = next_state;
    end
    
    reg init_flag;
    reg [3:0] cnt_data;
    always @(posedge clk or posedge reset_p)begin
        if(reset_p)begin
            next_state = IDLE;
            init_flag = 0;
            cnt_data = 0;
            rs = 0;
        end
        else begin
            case(state)
                IDLE:begin
                    if(init_flag)begin
                        if(btn_pedge[0])next_state = SEND_DATA;
                        if(btn_pedge[1])next_state = SEND_COMMAND;
                    end
                    else begin
                        if(count_usec <= 22'd80_000)begin
                            count_usec_e = 1;
                        end
                        else begin
                            next_state = INIT;
                            count_usec_e = 0;
                        end
                    end
                end
                INIT:begin
                    if(busy)begin
                        send = 0;
                        if(cnt_data >= 6)begin
                            next_state = IDLE;
                            init_flag = 1;
                            cnt_data = 0;
                        end                        
                    end
                    else if(!send)begin //send가 1이되면 i2c_lcd_send_byte모듈에서 엣지디텍터에 의해 한 클락 뒤에 send_pedge가 발생하고 이 send_pedge신호를 보고 i2c_lcd_send_byte모듈의 상태머신에서 busy가 1이된다, 
                        case(cnt_data) //그러면 busy가 1이되기 전에 이미 이쪽 상태머신에 들어와 다시 case문에 들어와서 1번케이스의 send_buffer를 바꾸기 때문에 send가 0일때만 들어올 수 있도록 조건을 건다
                            0:send_buffer = 8'h33;
                            1:send_buffer = 8'h32;
                            2:send_buffer = 8'h28;
                            3:send_buffer = 8'h0f;
                            4:send_buffer = 8'h01;
                            5:send_buffer = 8'h06;
                        endcase 
                        rs = 0;                                              
                        send = 1;
                        cnt_data = cnt_data + 1;
                    end
                    
                end
                SEND_DATA:begin
                    if(busy)begin
                        next_state = IDLE;
                        send = 0;
                        if(cnt_data >= 9)cnt_data = 0;
                        else cnt_data = cnt_data + 1;    
                    end
                    else begin
                        send_buffer = "0"+cnt_data; //"A":A의 대문자 아스키값 ""사용//"A"+cnt_data:아스키코드 값 점점 증가//"0"+cnt_data:점점 증가하는 숫자 출력(0에 해당하는 아스키값에 cnt_data 덤함
                        rs = 1; //데이터 레지스터에게 보냄
                        send = 1;
                    end
                    
                end
                SEND_COMMAND:begin
                    if(busy)begin
                        next_state = IDLE;
                        send = 0;
                        if(cnt_data >= 9)cnt_data = 0;
                        else cnt_data = cnt_data + 1;    
                    end
                    else begin
//                        send_buffer = 8'h18; //버튼1을 누르면 화면 왼쪽으로 밀림
                        send_buffer = 8'hc0; //칸 주소지를 정하여 화면 아랫줄 첫칸으로 커서 보냄
                        rs = 0; //커맨드 레지스터에게 보냄
                        send = 1;
                    end
                    
                end
            endcase
        end
    end
    
endmodule

LCD에 I2C통신으로 문자열 전송

module i2c_txtlcd_top(
    input clk, reset_p,
    input [3:0] btn,
    output scl, sda,
    output [15:0]led);
    
    parameter IDLE = 6'b00_0001;
    parameter INIT = 6'b00_0010;
    parameter SEND_DATA = 6'b00_0100;
    parameter SEND_COMMAND = 6'b00_1000;
    parameter SEND_STRING = 6'b01_0000;
    
     wire clk_usec;
    clock_div_100 usec_clk (.clk(clk), .reset_p(reset_p), .clk_div_100_nedge(clk_usec));
    
    reg [21:0] count_usec; //3초 대기를 위함
    reg count_usec_e;
    always @(negedge clk or posedge reset_p)begin
        if(reset_p)count_usec = 0;
        else if(clk_usec && count_usec_e)count_usec = count_usec + 1; //S_IDLE에서 count_usec_e를 1주면 카운트 시작
        else if(!count_usec_e)count_usec = 0; //count_usec_e가 0이되면 리셋
    end
    
    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]));
    
    reg [7:0] send_buffer;
    reg rs, send;
    wire busy;
    i2c_lcd_send_byte txtlcd(.clk(clk), .reset_p(reset_p),
        .addr(7'h27),
        .send_buffer(send_buffer), //보낼 데이터
        .rs(rs), .send(send), // send가 1됬을때 1바이트 보냄
        .scl(scl), .sda(sda),
        .busy(busy), //보내는 동안 시간이 걸리기 때문에 busy가 0일때만 send 가능    
        .led(led));
    
    reg [5:0] state, next_state; //6비트짜리 상태들
    always @(negedge clk or posedge reset_p)begin
        if(reset_p)state = IDLE;
        else state = next_state;
    end
    
    reg init_flag;
    reg [3:0] cnt_data;
    reg [8*5-1:0] hello; //40비트짜리 레지스터//5글자 짜리 문자열 변수, 한 글자당 8비트//[]안에 변수안됨 상수만 가능
    reg [3:0] cnt_string;
    
    always @(posedge clk or posedge reset_p)begin
        if(reset_p)begin
            next_state = IDLE;
            init_flag = 0;
            cnt_data = 0;
            rs = 0;
            hello = "HELLO"; //8비트씩 5글자가 순서대로 들어감
            cnt_string = 0;
        end
        else begin
            case(state)
                IDLE:begin
                    if(init_flag)begin
                        if(btn_pedge[0])next_state = SEND_DATA;
                        if(btn_pedge[1])next_state = SEND_COMMAND;
                        if(btn_pedge[2])next_state = SEND_STRING;
                    end
                    else begin
                        if(count_usec <= 22'd80_000)begin
                            count_usec_e = 1;
                        end
                        else begin
                            next_state = INIT;
                            count_usec_e = 0;
                        end
                    end
                end
                INIT:begin
                    if(busy)begin
                        send = 0;
                        if(cnt_data >= 6)begin
                            next_state = IDLE;
                            init_flag = 1;
                            cnt_data = 0;
                        end                        
                    end
                    else if(!send)begin //send가 1이되면 i2c_lcd_send_byte모듈에서 엣지디텍터에 의해 한 클락 뒤에 send_pedge가 발생하고 이 send_pedge신호를 보고 i2c_lcd_send_byte모듈의 상태머신에서 busy가 1이된다, 
                        case(cnt_data) //그러면 busy가 1이되기 전에 이미 이쪽 상태머신에 들어와 다시 case문에 들어와서 1번케이스의 send_buffer를 바꾸기 때문에 send가 0일때만 들어올 수 있도록 조건을 건다
                            0:send_buffer = 8'h33;
                            1:send_buffer = 8'h32;
                            2:send_buffer = 8'h28;
                            3:send_buffer = 8'h0f;
                            4:send_buffer = 8'h01;
                            5:send_buffer = 8'h06;
                        endcase 
                        rs = 0; //커맨드 레지스터에게 보냄                                              
                        send = 1;
                        cnt_data = cnt_data + 1;
                    end
                    
                end
                SEND_DATA:begin
                    if(busy)begin
                        next_state = IDLE;
                        send = 0;
                        if(cnt_data >= 9)cnt_data = 0;
                        else cnt_data = cnt_data + 1;    
                    end
                    else begin
                        send_buffer = "0"+cnt_data; //"A":A의 대문자 아스키값 ""사용//"A"+cnt_data:아스키코드 값 점점 증가//"0"+cnt_data:점점 증가하는 숫자 출력(0에 해당하는 아스키값에 cnt_data 덤함
                        rs = 1; //데이터 레지스터에게 보냄
                        send = 1;
                    end
                    
                end
                SEND_COMMAND:begin
                    if(busy)begin
                        next_state = IDLE;
                        send = 0;
                        if(cnt_data >= 9)cnt_data = 0;
                        else cnt_data = cnt_data + 1;    
                    end
                    else begin
                        send_buffer = 8'h18; //버튼1을 누르면 화면 왼쪽으로 밀림
//                        send_buffer = 8'hc0; //칸 주소지를 정하여 화면 아랫줄 첫칸으로 커서 보냄
                        rs = 0; //커맨드 레지스터에게 보냄
                        send = 1;
                    end
                    
                end
                SEND_STRING:begin
                    if(busy)begin
                        send = 0;
                        if(cnt_string >= 5)begin
                            next_state = IDLE;
                            cnt_string = 0;
                        end                        
                    end
                    else if(!send)begin //send가 1이되면 i2c_lcd_send_byte모듈에서 엣지디텍터에 의해 한 클락 뒤에 send_pedge가 발생하고 이 send_pedge신호를 보고 i2c_lcd_send_byte모듈의 상태머신에서 busy가 1이된다, 
                        case(cnt_string) //그러면 busy가 1이되기 전에 이미 이쪽 상태머신에 들어와 다시 case문에 들어와서 1번케이스의 send_buffer를 바꾸기 때문에 send가 0일때만 들어올 수 있도록 조건을 건다
                            0:send_buffer = hello[39:32];
                            1:send_buffer = hello[31:24];
                            2:send_buffer = hello[23:16];
                            3:send_buffer = hello[15:8];
                            4:send_buffer = hello[7:0];
                        endcase 
                        rs = 1; //커맨드 레지스터에게 보냄                                              
                        send = 1;
                        cnt_string = cnt_string + 1;
                    end
                end
            endcase
        end
    end
    
endmodule
profile
개인 기록공간

0개의 댓글