altair의 프로젝트 일기

라즈베리파이 피코에 128x64 Graphical LCD SPI로 연결하기 본문

IT/임베디드

라즈베리파이 피코에 128x64 Graphical LCD SPI로 연결하기

altair823 2022. 7. 27. 02:27

1. 배경

 저번 프로젝트에서는 파이 피코에 1602 LCD를 연결해 온습도계를 만들어 보았다. 부품을 사는 과정에서 더 크고 더 많은 것들을 화면에 출력할 수 있는 디스플레이들이 있다는 것을 알게되었다. 온습도계는 딱히 다양한 화면출력이 필요 없었지만 다음에 할 계산기 프로젝트를 위해 디스플레이를 사용해 원하는 내용을 출력해보고 싶었다. 그 중에서 12864 LCD를 구매했다. 

12864 데이터버스 그래픽LCD. 나는 화이트/블루 버전을 구매했다.

 인터넷을 찾아보니 아두이노 우노와 12864 LCD를 연결하기는 쉬웠다. 아래과 같이 이미 많은 레퍼런스와 튜토리얼들이 인터넷에 널려있었다. 

 

Interfacing 128x64 Graphical LCD Display- Blue Backlight with Arduino

Learn How to interface a 128x64 Graphical LCD Display with Arduino. using 128x64 LCD Display example code, circuit, pinout library

electropeak.com

 

 

Tutorial 16 - 128x64 ST7920 graphical/dot-matrix SPI LCD

In this tutorial I'll teach you how to run the U8g2 graphic test on a SPI 128x64 LCD display with ST7920 controller. More about U8g2: https: //github.com/olikraus/u8g2/

arduino-tutorials.net

 때문에 처음에는 계산기 프로젝트를 아두이노로 진행하려고 했다. u8g2라는 좋은 디스플레이 라이브러리가 있었고, 12864 LCD에서 쓰는 ST7920 컨트롤러는 SPI 연결을 지원했다. 몇 가지 실수와 뻘짓을 지나 아두이노에서 이 디스플레이를 사용해 원하는 글자를 출력할 수 있었다. 

 코드를 작성하다보니 난관에 부딛혔다. 먼저, 계산기는 기본적으로 숫자들을 저장하고 연산해야 한다. 그런데 아두이노 계산기 코드를 짜고 키패드로 숫자 입력을 받던 중 이상하게 너무 일찍 오버플로우가 일어나는 것이었다.

 찾아보니 아두이노는 기본적으로 int가 16비트 정수다. long이 32비트를, long long이 64비트 정수를 저장할 수 있다. 내부 계산에서는 64비트 정수를 사용할 수 있겠지만 u8g2 라이브러리는 이들의 표현을 지원하지 않거나 무언가 알 수 없는 오버플로우가 자꾸 발생했다. 그리고 무엇보다 double이 float과 같은 32비트 부동소수점 자료형이라는 것이 마음에 들지 않았다. 

아두이노 우노의 모습.

 두 번째로, 내가 주문한 아두이노는 아두이노 우노다. 프로토타입에는 유용하지만 크기가 생각보다 컸다. 거의 라즈베리파이 4 만한 크기니까 말이다. 그렇다고 아두이노 나노나 미니를 사자니 반도체 수급 부족 때문에 가격이 만만치 않게 오른 상태였다. 

 그래서 그냥 5000원짜리 라즈베리파이 피코를 쓰기로 했다. 보드를 바꾸기 위해 해결해야 할 문제는 다음과 같았다. 

2. 목표

  1. 피코에서 사용할 수 있는 u8g2와 같은 LCD 라이브러리를 찾고 검증하기
  2. 피코와 12864 LCD를 SPI로 연결하기

2-1. u8g2 사용하기

 오랜 시간이 흘렀지만 파이 피코를 위해 만들어진 안정적인 12864 LCD 라이브러리는 찾을 수 없었다. 그래서 그냥 아두이노 IDE를 사용하기로 했다. 아두이노 IDE는 라즈베리파이 피코를 지원한다. 무슨 뜻이냐면, 아두이노 IDE에서 보드를 라즈베리파이 피코로 바꾸면 아두이노 코드로 파이 피코를 돌릴 수 있다는 것이다. 

 많은 튜토리얼에서는 이를 위해 보드 매니저에서 파이 피코 패키지를 설치하라고 한다. 하지만 만약 pico-sdk를 사용하고자 한다면, 추가적으로 해야할 것이 있다. 

 

Raspberry Pi Pico SDK: Raspberry Pi Pico SDK

The Raspberry Pi Pico SDK (Software Development Kit), henceforth SDK, provides the headers, libraries and build system necessary to write programs for RP2040-based devices such as the Raspberry Pi Pico in C, C++ or assembly language. The SDK is designed to

raspberrypi.github.io

 Raspberry Pi Pico Arduino core를 설치해야 pico-sdk를 사용할 수 있다. 해당 패키지는 다음과 같은 방법으로 받을 수 있다. 

  1. 아두이노 IDE에서 설정->additional boards manager URLs 에 주소 https://github.com/earlephilhower/arduino-pico/releases/download/global/package_rp2040_index.json 를 넣고 확인을 누른다. 
  2.  보드 매니저를 열고 Earle F. Philhower의 Raspberry Pi Pico/RP2040 패키지를 설치한다. 

 이렇게 하면 파이 피코에서 u8g2 라이브러리도 사용할 수 있고, pico-sdk도 사용할 수 있다. 이제 라즈베리파이 피코와 12864 LCD를 SPI로 연결하는 법을 알아보자.

2-2. 피코와 LCD를 SPI로 연결하기

u8g2는 많은 예제들을 제공한다. 예제마다 디스플레이 객체 생성자들이 주석처리 되어 있다. 디스플레이, 그리고 디스플레이와 아두이노 우노 간의 연결 방법에 따라 올바른 생성자를 주석 해제하고 실행하면 된다.

주석처리 된 생성자들. 중간에 12864 LCD의 컨트롤러 ST7920의 생성자도 보인다.

 만약 아두이노 우노를 사용하고, 위의 튜토리얼처럼 10, 11, 13번 핀에 SPI로 화면을 연결했다면 다음 생성자만 주석 해제하면 될 것이다. 

U8G2_ST7920_128X64_1_SW_SPI u8g2(U8G2_R0, /* clock=*/ 13, /* data=*/ 11, /* CS=*/ 10, /* reset=*/ 8);

 하지만 만약 라즈베리파이 피코에 연결한다면 이 중에 사용할 수 있는 생성자가 없다. GPIO 핀들이 다르기 때문이다. 그렇다면 어떻게 해야할까? 라즈베리파이 피코 데이터 시트를 보자. 

파이 피코의 핀맵.

 피코 핀맵을 잘 보면 SPI0, SPI1과 같은 핀들이 보인다. 이 핀들로 SPI 연결을 하면 된다. 아래는 현재 내가 사용하는 와이어링이다.

 Fritzing의 12864 LCD 커넥터들의 이름이 내 LCD와 조금 다르지만 의미는 같은 것으로 알고 있다. 만약 위의 그림과 다른 이름의 커넥터들을 가진 LCD에 연결하려고 한다면 아래의 사진을 참고하도록 하자. 같은 위치의 핀은 같은 역할을 하므로 위 그림과 같은 곳에 연결하면 된다. 

 만약 USB로 피코에 전원을 공급하고 있다면 39번 핀(VSYS) 또는 40번 핀(VBUS)에서 5V 출력이 나올 것이다. 내 LCD는 5V 전압이 필요했기에 여기로 5V를 얻을 수 있었다. 배터리를 사용하는 경우 승압모듈로 전압을 올려 따로 LCD에 인가해줄 필요가 있을 것이다(참고로 파이 피코의 작동 전압은 1.8V부터 5.5V다. 5V 전압으로 둘을 모두 만족시킬 수 있다). 

 와이어링을 마쳤다면 이제 u8g2 라이브러리로 SPI를 사용할 차례다. u8g2 라이브러리 설치는 다음 메뉴얼에서 확인할 수 있다. 

 

GitHub - olikraus/u8g2: U8glib library for monochrome displays, version 2

U8glib library for monochrome displays, version 2 - GitHub - olikraus/u8g2: U8glib library for monochrome displays, version 2

github.com

 이제 우리가 연결한 핀에 맞는 생성자를 제공해야 한다. 잠깐 보고 넘어갔듯이 u8g2 예제에는 각 디스플레이에 맞는 생성자들이 주석처리 되어있다. 12864 LCD는 ST7920 컨트롤러를 사용하므로 해당 라인들을 살펴보자. 

//U8G2_ST7920_256X32_1_8080 u8g2(U8G2_R0, 8, 9, 10, 11, 4, 5, 6, 7, /*enable=*/ 18, /*cs=*/ U8X8_PIN_NONE, /*dc=*/ 17, /*reset=*/ U8X8_PIN_NONE);
//U8G2_ST7920_256X32_1_SW_SPI u8g2(U8G2_R0, /* clock=*/ 18 /* A4 */ , /* data=*/ 16 /* A2 */, /* CS=*/ 17 /* A3 */, /* reset=*/ U8X8_PIN_NONE);
//U8G2_ST7920_192X32_1_8080 u8g2(U8G2_R0, 8, 9, 10, 11, 4, 5, 6, 7, /*enable=*/ 18, /*cs=*/ U8X8_PIN_NONE, /*dc=*/ 17, /*reset=*/ U8X8_PIN_NONE);
//U8G2_ST7920_192X32_1_SW_SPI u8g2(U8G2_R0, /* clock=*/ 18 /* A4 */ , /* data=*/ 16 /* A2 */, /* CS=*/ 17 /* A3 */, /* reset=*/ U8X8_PIN_NONE);
//U8G2_ST7920_128X64_1_8080 u8g2(U8G2_R0, 8, 9, 10, 11, 4, 5, 6, 7, /*enable=*/ 18 /* A4 */, /*cs=*/ U8X8_PIN_NONE, /*dc/rs=*/ 17 /* A3 */, /*reset=*/ 15 /* A1 */);	// Remember to set R/W to 0 
//U8G2_ST7920_128X64_1_SW_SPI u8g2(U8G2_R0, /* clock=*/ 18 /* A4 */ , /* data=*/ 16 /* A2 */, /* CS=*/ 17 /* A3 */, /* reset=*/ U8X8_PIN_NONE);
//U8G2_ST7920_128X64_1_SW_SPI u8g2(U8G2_R0, /* clock=*/ 13, /* data=*/ 11, /* CS=*/ 10, /* reset=*/ 8);
//U8G2_ST7920_128X64_1_SW_SPI u8g2(U8G2_R0, /* clock=*/ 14, /* data=*/ 13, /* CS=*/ 15, /* reset=*/ 16); // Feather HUZZAH ESP8266, E=clock=14, RW=data=13, RS=CS
//U8G2_ST7920_128X64_1_HW_SPI u8g2(U8G2_R0, /* CS=*/ 10, /* reset=*/ 8);
//U8G2_ST7920_128X64_1_HW_SPI u8g2(U8G2_R0, /* CS=*/ 15, /* reset=*/ 16); // Feather HUZZAH ESP8266, E=clock=14, RW=data=13, RS=CS

 우리는 SPI로 디스플레이를 연결하고 있으니 해당 라인들만 추려보자. 

//U8G2_ST7920_256X32_1_SW_SPI u8g2(U8G2_R0, /* clock=*/ 18 /* A4 */ , /* data=*/ 16 /* A2 */, /* CS=*/ 17 /* A3 */, /* reset=*/ U8X8_PIN_NONE);
//U8G2_ST7920_192X32_1_SW_SPI u8g2(U8G2_R0, /* clock=*/ 18 /* A4 */ , /* data=*/ 16 /* A2 */, /* CS=*/ 17 /* A3 */, /* reset=*/ U8X8_PIN_NONE);
//U8G2_ST7920_128X64_1_SW_SPI u8g2(U8G2_R0, /* clock=*/ 18 /* A4 */ , /* data=*/ 16 /* A2 */, /* CS=*/ 17 /* A3 */, /* reset=*/ U8X8_PIN_NONE);
//U8G2_ST7920_128X64_1_SW_SPI u8g2(U8G2_R0, /* clock=*/ 13, /* data=*/ 11, /* CS=*/ 10, /* reset=*/ 8);
//U8G2_ST7920_128X64_1_SW_SPI u8g2(U8G2_R0, /* clock=*/ 14, /* data=*/ 13, /* CS=*/ 15, /* reset=*/ 16); // Feather HUZZAH ESP8266, E=clock=14, RW=data=13, RS=CS
//U8G2_ST7920_128X64_1_HW_SPI u8g2(U8G2_R0, /* CS=*/ 10, /* reset=*/ 8);
//U8G2_ST7920_128X64_1_HW_SPI u8g2(U8G2_R0, /* CS=*/ 15, /* reset=*/ 16); // Feather HUZZAH ESP8266, E=clock=14, RW=data=13, RS=CS

 피코 핀맵에서 전원들을 제외하고 연결한 핀들의 SPI 핀 이름들을 추려보면 다음과 같다. 

  • E -> SPI SCK (GPIO 18)
  • R/W -> SPI TX (GPIO 19)
  • RS -> SPI CSn (GPIO 17)
  • PSB -> GND

 이제 ST7920의 데이터 시트를 살펴보자. 

ST7920 데이터 시트의 핀 설명.

 데이터 시트에 따르면, E는 시리얼 클럭, R/W는 시리얼 데이터 입력, RS는 칩 선택으로 시리얼 모드에서 기능한다. 그리고 PSB를 0으로 놓으면 디스플레이를 시리얼 모드로 설정할 수 있다. 또한 파이 피코에서 각 SPI 핀들은, SCK가 시리얼 클럭, TX가 데이터 전송, CSn이 슬레이브 셀렉트로 기능한다. 따라서 각각을 연결하면 올바르게 SPI 통신을 시작할 수 있다. 

 다시 생성자로 돌아가자. 우리는 디스플레이로부터의 입력을 받지 않으니 총 세 개의 선을 사용한다. 클럭, 데이터, 슬레이브 셀렉트. 다음 주석 한 줄을 유심히 보자. 

//U8G2_ST7920_128X64_1_SW_SPI u8g2(U8G2_R0, /* clock=*/ 13, /* data=*/ 11, /* CS=*/ 10, /* reset=*/ 8);

 우리가 연결한 세 선이 모두 들어가 있지 않은가? 리셋핀은 사용하지 않으니 그대로 두어도 좋겠다. 나머지는 GPIO 핀 번호에 맞추어 수정하면 된다. 결과적으로 파이 피코를 위한 u8g2 생성자는 다음과 같다. 

U8G2_ST7920_128X64_1_SW_SPI u8g2(U8G2_R0, /* clock=*/ 18, /* data=*/ 19, /* CS=*/ 17, /* reset=*/ 8); // for pi pico

 이제 위와 같이 주석을 해제한 생성자를 넣고 그래픽 테스트 예제를 실행해보자. 

 예제의 모든 테스트가 정상적으로 실행되는 것을 볼 수 있다. 

3. 남은 일

 계산기 프로젝트를 진행하다가 겪은 어려움을 해결한 과정을 나누고자 작성했다. 개인적으로 정말 많은 시간이 들었다...... 게다가 이 이후에 키패드 핀의 납을 인두기로 녹여 뽑다가 커넥터 동판을 망가뜨렸다.

 잠시 기다림 후에 다른 키패드가 도착하는 대로 이어서 프로젝트를 완성해야겠다...

Comments