일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | |||
5 | 6 | 7 | 8 | 9 | 10 | 11 |
12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | 20 | 21 | 22 | 23 | 24 | 25 |
26 | 27 | 28 | 29 | 30 | 31 |
- 보석 관람
- 익산 보석 박물관
- 군산
- 128x64
- 목포명소
- HC-06
- 아두이노
- 당일치기여행
- 군산 구경
- 완도 구경
- 해양 전시회
- 완도 명소
- u8glib
- 이럴때아니면언제이런거보겠어
- 아두이노 블루투스
- 신안선
- 너무작잖아...
- 목포 박물관
- 대중음악박물관
- 군산명소
- 시퀀스 프로그램
- oled
- 목포
- 시퀀스 그리는 프로그램
- 해양문화재연구소
- HC-06 사용법
- 익산 명소
- 좀더큰규모일줄알았다규..
- 비트맵 띄우기
- 눈에보양
- Today
- Total
RS's Travel & Electronic
u8glib : 문자를 로그라이크 게임 처럼 움직이기 본문
지난번 버튼 함수 포스팅의 다섯 번째 함수와 U8 GLIB라는 라이브러리를 사용해서
128x64 OLED디스플레이에 띄운 문자를 마치 로그라이크 게임 플레이하듯이 움직여봅니다.
아래는 준비물입니다.
128x64 OLED I2C 디스플레이
한국에서 바로 살 수 있는곳 중에서는 메카 설루션이 좀 싼 거 같습니다.
개중에는 디스플레이 윗줄은 노란색, 아랫줄은 흰색 등 투칼라로 만들어진 디스플레이가 있습니다. 색 변경은 불가능한 모델이니까 미리 확인하고 사세요. 저는 I2C만 사용하지만 SPI를 선택하고 싶다면 그것도 상관없습니다.
택트 버튼 6개
https://www.devicemart.co.kr/goods/view?no=36627
어디에서나 파는 물건이고 개당 몇십 원 밖에 안 하니 아무데서나 사도 좋습니다만, 4핀 달린 버튼은 쳐다보지도 마세요. 실험하기 불편합니다. 브레드보드에 잘 안 끼워져요. 2핀 달린 놈이 최곱니다.
아두이노 mini or uno or MEGA or ETC
아두이노 종류로 쓸 수 있는거면 뭐든 좋습니다.
메카 설루션, 디바이스 마트, 아이씨 뱅큐 등 어디서든 팔고 있으니 비교해서 제일 싸게 살 수 있는 곳에서 사세요.
그리고, U8glib 라이브러리.
압축 풀어서 아두이노 설치 폴더/libraries에 넣어 줍니다.
저는 미리 만들어놓은 실험용 보드들을 사용하겠습니다.
별다른 건 아니고, 뭔가 실험해 보고 싶은 내용이 생겼을 때 즉각 해볼 수 있도록 핀을 확장하거나 통합하거나 해둔 겁니다. 이렇게 하면 그때그때마다 시간을 절약할 수 있죠. 기성품 중에 이렇게 만들어놓은 보드가 있기도 하지만 제가 직접 커스텀을 해야 저한테 맞는 파츠를 쓸 수 있습니다.
연결입니다.
문자를 움직이거나 변화를 줄 여섯개의 버튼은 각각 아두이노의
UP | 2 |
RIGHT | 3 |
LEFT | 4 |
DOWN | 5 |
아스키값 증가 | 6 |
아스키값 감소 | 7 |
번에 연결해 주세요.
OLED와 아두이노를 연결하는건 I2C냐 SPI냐, 그리고 아두이노가 무슨 종류냐에 따라 다르겠죠.
I2C 일 경우 다음과 같습니다.
보드 | SDA | SCL |
Uno, Ethernet | A4 | A5 |
Mega2560 | 20 | 21 |
Leonardo | 2 | 3 |
Due | 20 | 21 |
SPI일 경우는 이렇게 하시고요.
보드 | MOSI | MISO | SCK | SS - slave | SS - master |
Uno | 11 또는 ICSP-4 | 12 또는 ICSP-1 | 13 또는 ICSP-3 | 10 | - |
MEGA | 51 또는 ICSP-4 | 50 또는 ICSP-1 | 52 또는 ICSP-3 | 53 | - |
Leonardo | ICSP-4 | ICSP-1 | ICSP-3 | - | - |
Due | ICSP-4 | ICSP-1 | ICSP-3 | - | 4, 10, 52 |
근데, 사실 전 SPI 통신을 사용하는 모듈을 사용해 본 적이 없습니다.
핀을 4개나 연결해야 된다는 게 너무 불편해서요.
SPI가 통신속도가 빠르다고는 하지만 솔직히 잘 모르겠고요.
위 연결법은 여러 고수들의 블로그에도 똑같이 나와있는 거니까 저대로 연결하면 잘 될 겁니다.
코드입니다.
#include "U8glib.h"
U8GLIB_SSD1306_128X64 u8g(U8G_I2C_OPT_NONE); // I2C / TWI
#define CHATTERINGTIME 5
#define CONTINUESEC 500
#define CONTINUECYCLE 200
#define BUTTON 2
#define BT_U 2
#define BT_R 3
#define BT_L 4
#define BT_D 5
#define BT_1 6
#define BT_2 7
int x_ray = 10;
int y_ray = 10;
char mob[2] = "A";
/* 방향 위 */
void btPush_U()
{
static boolean btUStat = false;
static boolean labtUStat = true;
static unsigned long labtUTime = 0;
static boolean nowPushed = false;
static unsigned long pushStartTime = 0;
static unsigned long continueStartTime = 0;
boolean readingStat = digitalRead(BT_U);
if (nowPushed) {
if (readingStat) nowPushed = false;
if ( (nowPushed) && ( (millis() - pushStartTime) > CONTINUESEC ) ) {
if ( (millis() - continueStartTime) > CONTINUECYCLE ){
if (0 < (y_ray-5)) y_ray -= 5;
draw();
continueStartTime = millis();
}
}
}
if (readingStat != labtUStat) labtUTime = millis();
if ( (millis() - labtUTime) > CHATTERINGTIME ) {
if (readingStat != btUStat) {
btUStat = readingStat;
if (btUStat == false) {
nowPushed = true;
pushStartTime = millis();
if (0 < (y_ray-5)) y_ray -= 5;
draw();
}
}
}
labtUStat = readingStat;
}
/* 방향 우 */
void btPush_R()
{
static boolean btRStat = false;
static boolean labtRStat = true;
static unsigned long labtRTime = 0;
static boolean nowPushed = false;
static unsigned long pushStartTime = 0;
static unsigned long continueStartTime = 0;
boolean readingStat = digitalRead(BT_R);
if (nowPushed) {
if (readingStat) nowPushed = false;
if ( (nowPushed) && ( (millis() - pushStartTime) > CONTINUESEC ) ) {
if ( (millis() - continueStartTime) > CONTINUECYCLE ){
if (127 > (x_ray+5)) x_ray += 5;
draw();
continueStartTime = millis();
}
}
}
if (readingStat != labtRStat) labtRTime = millis();
if ( (millis() - labtRTime) > CHATTERINGTIME ) {
if (readingStat != btRStat) {
btRStat = readingStat;
if (btRStat == false) {
nowPushed = true;
pushStartTime = millis();
if (127 > (x_ray+5)) x_ray += 5;
draw();
}
}
}
labtRStat = readingStat;
}
/* 방향 좌 */
void btPush_L()
{
static boolean btLStat = false;
static boolean labtLStat = true;
static unsigned long labtLTime = 0;
static boolean nowPushed = false;
static unsigned long pushStartTime = 0;
static unsigned long continueStartTime = 0;
boolean readingStat = digitalRead(BT_L);
if (nowPushed) {
if (readingStat) nowPushed = false;
if ( (nowPushed) && ( (millis() - pushStartTime) > CONTINUESEC ) ) {
if ( (millis() - continueStartTime) > CONTINUECYCLE ){
if (0 < (x_ray-5)) x_ray -= 5;
draw();
continueStartTime = millis();
}
}
}
if (readingStat != labtLStat) labtLTime = millis();
if ( (millis() - labtLTime) > CHATTERINGTIME ) {
if (readingStat != btLStat) {
btLStat = readingStat;
if (btLStat == false) {
nowPushed = true;
pushStartTime = millis();
if (0 < (x_ray-5)) x_ray -= 5;
draw();
}
}
}
labtLStat = readingStat;
}
/* 방향 아래 */
void btPush_D()
{
static boolean btDStat = false;
static boolean labtDStat = true;
static unsigned long labtDTime = 0;
static boolean nowPushed = false;
static unsigned long pushStartTime = 0;
static unsigned long continueStartTime = 0;
boolean readingStat = digitalRead(BT_D);
if (nowPushed) {
if (readingStat) nowPushed = false;
if ( (nowPushed) && ( (millis() - pushStartTime) > CONTINUESEC ) ) {
if ( (millis() - continueStartTime) > CONTINUECYCLE ){
if (63 > (y_ray+5)) y_ray += 5;
draw();
continueStartTime = millis();
}
}
}
if (readingStat != labtDStat) labtDTime = millis();
if ( (millis() - labtDTime) > CHATTERINGTIME ) {
if (readingStat != btDStat) {
btDStat = readingStat;
if (btDStat == false) {
nowPushed = true;
pushStartTime = millis();
if (63 > (y_ray+5)) y_ray += 5;
draw();
}
}
}
labtDStat = readingStat;
}
/* 아스키 증가 */
void btPush_1()
{
static boolean bt1Stat = false;
static boolean labt1Stat = true;
static unsigned long labt1Time = 0;
static boolean nowPushed = false;
static unsigned long pushStartTime = 0;
static unsigned long continueStartTime = 0;
boolean readingStat = digitalRead(BT_1);
if (nowPushed) {
if (readingStat) nowPushed = false;
if ( (nowPushed) && ( (millis() - pushStartTime) > CONTINUESEC ) ) {
if ( (millis() - continueStartTime) > CONTINUECYCLE ){
mob[0]++;
draw();
continueStartTime = millis();
}
}
}
if (readingStat != labt1Stat) labt1Time = millis();
if ( (millis() - labt1Time) > CHATTERINGTIME ) {
if (readingStat != bt1Stat) {
bt1Stat = readingStat;
if (bt1Stat == false) {
nowPushed = true;
pushStartTime = millis();
mob[0]++;
draw();
}
}
}
labt1Stat = readingStat;
}
/* 아스키 감소 */
void btPush_2()
{
static boolean bt2Stat = false;
static boolean labt2Stat = true;
static unsigned long labt2Time = 0;
static boolean nowPushed = false;
static unsigned long pushStartTime = 0;
static unsigned long continueStartTime = 0;
boolean readingStat = digitalRead(BT_2);
if (nowPushed) {
if (readingStat) nowPushed = false;
if ( (nowPushed) && ( (millis() - pushStartTime) > CONTINUESEC ) ) {
if ( (millis() - continueStartTime) > CONTINUECYCLE ){
mob[0]--;
draw();
continueStartTime = millis();
}
}
}
if (readingStat != labt2Stat) labt2Time = millis();
if ( (millis() - labt2Time) > CHATTERINGTIME ) {
if (readingStat != bt2Stat) {
bt2Stat = readingStat;
if (bt2Stat == false) {
nowPushed = true;
pushStartTime = millis();
mob[0]--;
draw();
}
}
}
labt2Stat = readingStat;
}
void draw()
{
u8g.firstPage();
do {
u8g.setFont(u8g_font_6x10);
u8g.drawStr( x_ray, y_ray, mob);
} while( u8g.nextPage() );
}
void setup()
{
u8g.setColorIndex(255); // white
pinMode (BT_L, INPUT_PULLUP);
pinMode (BT_U, INPUT_PULLUP);
pinMode (BT_D, INPUT_PULLUP);
pinMode (BT_R, INPUT_PULLUP);
pinMode (BT_1, INPUT_PULLUP);
pinMode (BT_2, INPUT_PULLUP);
draw();
Serial.begin (9600);
}
void loop()
{
btPush_U();
btPush_R();
btPush_L();
btPush_D();
btPush_1();
btPush_2();
}
지금까지 시제품을 제작할 때도, 그냥 실험을 할때도 버튼 채터링을 무시하기 위한 시간 상수의 이름을 THISTIME이라고 선언해왔습니다. 별 이유가 있는 건 아니고, 그냥 어쩌다 지은 이름을 지금까지 계속 이어왔었죠. 근데 다른 사람이 보기에 이게 무슨 상수인지 모르겠다 싶어서 이번에 이름을 다시 지었습니다. CHATTERINGTIME 으로요.
버튼을 누르면 해당되는 동작을 하고 버튼을 누른 채로 0.5초 이상 지속하면 0.2초 간격으로 해당 동작을 반복합니다. 이 동작 간격을 주지 않으면 너무 빨리 동작이 연속으로 일어나서 사람 눈으로 파악하기가 어려워지니까요.
0.5초는 CONTINUESEC라는 상수로, 0.2초는 CONTINUECYCLE이란 상수로 선언되어 있습니다.
아스키 값 변화를 이용해서 출력된 문자를 변화시킬 수 있습니다. 계속 누르다 보면 영문자를 넘어서 기호가 출력되고, 어느 정도 값을 넘어가면 아무것도 출력 안되게 됩니다. OLED에서 어떤 문자들까지 출력할 수 있는지 확인하실 수 있을 겁니다.
스케치는 아두이노 프로 미니 5V/16 Mhz 중국산 짭퉁에 업로드했고, 용량은 위와 같이 사용되었습니다.
원래 U8 GLIB가 용량을 좀 많이 잡아먹습니다.
버튼 누름 지속에 관련된 변수들이 좀 많아서 동적 메모리를 좀 많이 사용했네요. 누름지속에 관련된 부분을 빼고 일반 푸시버튼 함수로 구현한다면 메모리를 훨씬 아낄 수 있겠습니다.
좀 짧은 분량의 로그라이크 게임 정도는 구현될 것 같습니다.
동작 영상입니다.
이후로도 제가 U8 GLIB로 128x64 OLED 디스플레이를 사용할 때의 코드들을 몇 가지 더 올려볼까 합니다.
'시퀀스와 아두이노' 카테고리의 다른 글
u8glib : OLED에 비트맵 이미지 출력하기 (0) | 2021.07.14 |
---|---|
u8glib : OLED에 사용자 메뉴 출력 (0) | 2021.07.14 |
I2C : 주소 확인하는 코드 (0) | 2021.07.11 |
버튼 : 상황에 맞는 응용 함수 (0) | 2021.07.11 |
전기 시퀀스 회로 기호 CAD 블록화 파일 (0) | 2021.07.03 |