Verilog_I2C(LCD)

공이지·2024년 8월 23일

I2C

이전에 정리한 자료가 있습니다! 자세한 내용은 이전에 다뤘으니 간략하게 정리하겠습니다.

  • SCL = clock선(Master)
  • SDA = 데이터선(Slave)
  • SCL이 High일 때 데이터선의(SDA) 데이터를 읽어들여 Register에 저장시킵니다.(이때 SDA신호를 읽어들여야하므로 SDA신호는 변하지 않는다.)SCL이 LOW라면 SDA의 데이터가 변화하여 다음 SCL의 High일때 그 값을 읽고 저장합니다.

I2C 통신 과정


- 전체적인 통신과정입니다. SCL이 High일때 SDA에서 하강엣지가 발생하면 통신이 시작합니다. 이후 데이터를 받을 주소를 1bit씩 SCL이 High일 때 SDA에서 보내는 주소 값을 읽습니다. SCL은 10us씩 토글되면서 값을 읽습니다. 주소 값 + RW신호, 8bit를 모두 받으면 데이터를 받았다는 신호인 ACK신호, LOW를 보내줍니다. 이후 8bit의 데이터 값을 받습니다. 데이터 또한 잘받았다는 ACK신호를 보내줘야합니다. 잘 받았다면 LOW를 보내주고 이후 SCL이 High상태일 때 SDA의 상승엣지가 발생하면 통신을 중단합니다.

Verilog 실습 및 시뮬레이션결과.

  • SCL n_edge, p_edge 엣지디텍터로 받는 이유 => 하강엣지일 경우 sda의 데이터가 바뀌고 상승엣지라면 SCL이 high이므로 sda의 데이터를 읽는다!
  • COMM_GO_pedge 상승엣지로 받는 이유 => 통신을 시작하기위함을 알리려고!
  • SCL enable신호가 들어오지 않으면 high유지

시뮬레이션 결과

  • SCL은 state가 IDLE이라면 항상 high상태를 유지합니다 또한 SDAhigh를 유지합니다. (SCL_enable신호가 "0" 이라면 SCL은 항상 high상태를 유지합니다. )
    즉, 시뮬레이션에서 확인하면 SCL_clk_enable 신호가 "0" 이라면 SCL은 "1"을 유지하고 count_microsec5 의 값은 0의 값을 유지합니다. 이 SCL_clk_enable 신호가 1이라면. 즉, "COMM_START" state로 이동한다면 SDA는 "0"으로 값을 할당합니다 이유는 SCLhigh이고 SDA하강엣지일때 I2C 통신이 시작하기 때문입니다. 이 후 next_state를 "SEND_ADDR"로 이동합니다.
  • 데이터를 전송할 주소 값을 읽어들입니다. "SEND_ADDR" state에서 주소가 전송되며, SCL_nedge(SCL의 하강엣지)가 발생하면 SDA는 addr_rw의 최상위비트의 데이터를 읽어옵니다. 그리고 SCL_pedge(SCL의 상승엣지)가 발생할때 8비트 데이터의 변수로 선언한count_bit가 "0" 이 아니라면 최상위(0~7까지 8비트 이므로 7번 비트부터 시작!)비트 7비트 에서 -1 을 하면서 하위 LSB(0비트)까지 SCL_nedge이 발생할때마다 하위비트까지의 SDA의 데이터 값을 읽습니다. 그리고 count_bit가 0이 된다면 다시 데이터를 받아오기위해 count_bit값을 7로 설정합니다. 시뮬레이션 결과를 보면 코드에서는 addr_rw를 주소와 rd_wr 입력을 assign문으로 하나로 통합했습니다. 이유는 원래 주소를 받고 (R/W)여부를 알려주는 신호를 마지막에 보내야하는데 이를 합쳐 addr_rw로 만들어 8비트로 선언하였습니다. 그리고 다음 state인 "RD_ACK" state로 이동하여 ACK신호를 보내줘야합니다.
  • 이론으로는 주소를 잘 받았다는 의미로 LOW신호인 ACK신호를 보내야합니다. 만약 이 ACK신호를 받지 않았다면 SDA는 high상태가 되고 이를 NACK로 인식하여 주소를 다시 받거나 주소 전송을 중단한다고 합니다. Verilog실습에서는 ACK신호를 보내주는 대신 stop_flag라는 enable 값을 통해 next_state를 결정합니다. SCL_nedge 이라면 sda를 임피던스로 1주기를 출력하고 이때 SCL_pedge일때 stop_flag로 next_state를 결정합니다. 초기에는 stop_flag 는 "0" 이므로 stop_flag를 1로 설정하고 next_state"SEND_DATA"로 상태를 변경합니다. 만약stop_flag가 1이라면 stop_flag를 0으로 설정하고 다음상태를 "SCL_STOP"으로 변경합니다. stop_flag를 enable한 이유는 주소를 받거나 데이터를 받으면 잘 받았다는 의미로 ACK신호를 보내야하는데 이를 stop_flag를 활용하여 2개의 ACK신호를 보내는 state를 만들지 않고 한개의 ACK신호를 보내는 state를 만들어서 처리했습니다.즉 처음에 한번 주소를 받을 때stop_flag가 "0"이면 데이터를 받기위해 다음상태를 "SEND_DATA"로 상태를 변경데이터를 받은 뒤 stop_flag는 "1"이고 "SEND_DATA"에서 next_state는 다시 "RD_ACK"로 상태가 변경되면서 stop_flag가 1일때 stop_flag를 다시 0으로 하여 다음 주소와 데이터를 받을 준비를하도록 변경하고 다음상태를 "SCL_STOP"으로 변경합니다.
  • 주소와 데이터를 다 받으면 "RD_ACK"상태에서 "SCL_STOP" 상태로 이동합니다. 여기서는 이제 통신을 멈추기위한 준비를 하는 단계입니다. 이유는 통신을 중단하기위한 신호는 SCL이 high인 상태에서 SDA가 상승엣지일때 I2C통신이 중단되기 때문에 SCL이 하강엣지일때 SDA를 LOW(0)로 설정하고, SCL이 상승엣지일떄 다음 state로 통신을 중단하기위한 "COMM_STOP"로 상태를 변경합니다.
  • 통신을 중단하기 위해서 SDA의 상승엣지를 발생시켜야하는데, SCL_clk_enable이 활성화되어있으면 SCL은 항상 10us주기를 갖기 때문에 토글 될 것입니다. 통신이 중단되면 SCL과 SDA는 항상 High상태를 유지하기 때문에 SCL_clk_enable을 0으로 변경해야합니다. 하지만 SCL이 high상태일 때 바로 SDA의 상승엣지를 발생시키면 다음 실습에서 사용할 텍스트 디바이스가 신호를 못 받을 수 있기 때문에 3us를 기다린 다음에 SCL_clk_enable를 0으로 변경해서 SCL을 high상태로 만들고 SDA를 High로 변경해서 상승엣지를 만들어 통신을 중단하고 state를 "IDLE"로 변경합니다.

I2C통신 과정 오실로스코프 실습.

I2C 1602 LCD 모듈

지금까지 I2C통신이 어떻게 데이터 값을 전송하는지 그 과정을 알아봤습니다. 이제 I2C통신으로 읽은 데이터를 어떻게 LCD에 표현하는지 알아보겠습니다.

  • 8비트의 데이터값 한비트씩 shiftRegister로 그 값을 받아서 저장합니다. 그리고 첫번째 사진에서 보면 [RS, RW, E] 3가지로 다음 동작을 결정합니다.
  • RS는 MUX로 구성되어있고 이를 DataCommand 레지스터로 8비트의 데이터 값 통째로 전달하는데 어떤 레지스터로 옮길것인지 결정을 합니다. 이때 RS = 0이면 명령어 레지스터에 값이 전달되어 LCD패널의 명령어를 전달에 LCD패널의 동작(reset, 이동 등)을 수행하고 RS = 1이라면 LCD에 표시되는 문자, 즉 데이터 값을 아스키 코드로 표현하는 레지스터(Data)에 데이터 값이 이동합니다.
  • RW는 데이터 값을 읽을 것인지 쓸것인지 결정해줍니다 우리는 데이터 값을 주고 레지스터에 값을 저장해야하기에 RW를 0으로하여 Write모드를 사용합니다.
  • Enable은 데이터 값을 통째로 옮기는 동작을 통제합니다. Enable = 1이라면 다른 레지스터로 값을 넘겨주고, Enable = 0 이라면 다른 레지스터로 값을 옮기는 동작을 수행하지 않습니다.
  • 2번째 사진은 LCD장치의 레지스터를 나타냅니다. 원래 데이터 값 8비트를 모두 옮기지만 그렇게 되면 많은 포트를 사용해야하기 때문에 상위 4비트를 먼저 RS로 선택한 register에 값을 옮기고 나머지 하위 4비트도 값을 옮깁니다. 하지만 이 과정은 총 6번의 State를 갖습니다. 데이터 전송시작, 상위4비트 데이터 값 보낼준비, 상위 4비트 데이터 값 전송, 하위 4비트 데이터 값 보낼준비, 하위 4비트 데이터 값 전송, 데이터 전송 중단 순으로 8비트 데이터가 전송됩니다. 만약 0101_0011의 데이터 값을 전송할때 먼저 상위 4비트인 "0101"을 데이터 전송버퍼(코드에서는 send_buffer) 상위 4비트에 할당하고 나머지 4비트에는 BT(LCD를 ON시키는 비트) , Enable, RW,RS순으로 값을 할당하여 어디에 어떻게 저장하고 데이터를 보낼지 결정합니다. 우선 명령어 레지스터에 값을 할당하기 위해서 RS = 0, RW = 0, BT = 1을 할당합니다 그리고 이 데이터 전송버퍼(코드에서는 send_buffer) 에 전송할 비트를 저장해야하므로 먼저 Enable = 0으로 해서 저장을 한 다음에 Enable = 1로 하여 명령어 레지스터에 데이터 값의 상위 4비트를 저장합니다. 이후 하위 4비트 값인 "0011" 을 버퍼에 저장(Enable = 0)한뒤 명령어 레지스터에 값을 보내줍니다(Enable = 1)

I2C LCD 명령어 레지스터 초기화.

명령어

  • Clear 는 말 그대로 LCD화면을 Clear시켜줍니다.
  • Return home은 DDRAM의 주소를 초기위치로 세팅합니다. 또한 LCD 화면이 넘어갔다면 화면의 위치또한 초기위치로 옮겨줍니다. 다만 DDRAM에 작성된 데이터들은 변하지 않습니다.
  • Entry mode set은 I/D는 데이터를 화면에 오른쪽으로 밀면서 받을 것인지 왼쪽으로 밀면서 받을것인지 정합니다. 그리고 S는 디스플레이도 움직일것인지 결정합니다.
  • Display on/off는 말 그대로 화면을 on , off
  • cursor or Display Shift, S/C = 0 이면 화면을 쉬프트, S/C = 1이면 커서를 쉬프트, R/L = 1이면 오른쪽으로 쉬프트, R/L = 0이면 왼쪽으로 쉬프트
  • Fuction set은 데이터의 인터페이스, 즉 LCD에 표현되는 데이터(글자)의 길이와, 작성되는 줄, 폰트등을 설정할 수 있습니다.
  • setCGRAM은 CGRAM의 주소를 설정합니다.
  • setDDRAM은 DDRAM의 주소를 설정합니다 이를 활용해서 어디에 데이터를 작성할지 설정이 가능합니다. 예를들어서 1100_0000(h'C0)의 8비트 값을 보내주면 DDRAM의 위치가 아랫줄로 변경됩니다.
  • Read busy flag & address는 사용 중인 플래그 읽고(BF),내부 작업이 수행되고 있음을 나타내며 주소 카운터 내용을 읽습니다.

명령어 Verilog실습

  • LCD를 사용하기 전에 항상 초기화를 진행시켜줘야합니다. 이 초기화작업은 한번만 진행되기때문에 init_flag라는 enable 신호를 잡아서 초기화가 진행시킨뒤 init_flag = 1로 하여 초기화가 더 이상 진행되지 못하게 합니다. 초기에는 init_flag가 0이므로 "INIT"으로 상태를 변경하여 초기화를 한번 진행합니다. 이때 초기화 작업을 하기 위해서는 40ms 이상의 시간을 기다려줘야 한다고 데이터 시트에 작성되어있습니다. 실습에선 충분한 시간을 기다려주기 위해서 80ms의 시간동안 대기하고 초기화를 진행 했습니다.
  • 초기화 State에서 send_buffer에 할당하는 데이터는 위 사진의 5단계를 16진수로 표현한 내용입니다. 다음 8bit 값을 할당하여 LCD 초기화 작업을 진행합니다.
  • LCD는 2개의 줄을 표현할 수있는데, 이는 한줄에 40bit씩 메모리가 할당된 DDRAM이 존재한다. 사진에서 노란색 부분, 즉 최상위 비트가 1이면 아랫줄을 가르치고 0이라면 윗줄을 가르킨다.
    8h'C0를 할당하면 아랫줄로 DDRAM의 주소를 변경할 수 있고, 8h'80를 할당하면 가장 윗줄로 DDRAM의 주소를 변경할 수 있습니다.

Shift명령어 실습

  • 0번 버튼을 눌러서 LCD에 데이터를 표시해주는데, 이때 1번 버튼을 누르게 되면 가장 왼쪽에 있는 데이터가 LCD에서 사라진다 이는 왼쪽으로 shift되고 있음을 알 수있다

문자열 출력

  • 문자열을 출력하기위해서는 문자를 설정하고 전달할 reg가 필요합니다. 코드를 보면 reg[8*5-1:0]hello라고 선언한 reg"HELLO"로 초기화 시켜줬다. 이는 문자 하나씩 출력해주기위해서는 한 문자당 8bit씩 잡고 데이터를 보내줘야하기에 5글자인 "Hello"를 출력하기위해서는 8X5 = 40의 비트를 선언하고 -1 을 하게되면 [39:0]이라는 40bit의 reg가 선언됩니다. 이 후 3번 버튼이 눌리면 "HELLO"를 한 단어씩 받아서 LCD에 문자열을 표시합니다.
profile
화이팅..!

0개의 댓글