본문 바로가기
1.B. Low Level Engineering/Micro Controller Unit

[MCU] P1. I2C 디스플레이 연결하기 (1602A LCD + Adafruit OLED)

by Never Settle Down 2023. 2. 15.
반응형


 

 

 

MCU 카테고리의 글을 읽는 독자 친구 한 명과 전화를 했다.

- 마리우스가 누구에요?

- 어 있어 묻지 마셈.

- 정수까지만 하고 왜 안올려요

- 바빠 임뫄

- 이론 쓰기 귀찮아서 그렇죠

- ㅇㅇ

- 어차피 우리 (복수 인원이다) 모두 공대 출신인데 (기계, 산업, 신재생에너지 등)

  이론 제끼고 실무부터 해주면 안되요?

- 니네 컴퓨터학 개론 배웠냐 모르고 하면 헷갈릴텐데

- 저 파이썬 A+이에요. 1학년 전필임.

- ...!

- 전에 알려줄때 #define 꿀팁같은거 위주로 좀 부탁합니다.

- 오키

 

 

 

 

그래.

일반적인 콤퓨터 이론 말고

MCU 특유의 것들만 전달해주자.

 

그래야 나도 몇 줄 쓰다가

논문까지 뒤져보는 번거로움 없이

내가 아는 짜리몽땅한 지식을

인터넷에 쏟아 부을 수 있지 않을까.

 

 

 

 

 

브금은 몽골 - 알타이 - 러시아쪽으로 추정되는 언어

(동양인 외모 + 키릴 문자라 찍어봄)

트로트싱잉이 가미된 가요입니다.

 

저 트로트싱잉이 주는 이국적이고 신기한 분위기를

오늘의 주제, 디스플레이 장치를 만지작 거리며 느껴보시라고.

 

 

 

 

 

자, 이번 시간에는 싸구려 LCD 디스플레이를 아두이노에 엮어서 써보겠읍니다.

재밌어 보이지?

콤퓨타와 아두이노를 USB로 연결해

시리얼로 데이터를 읽는 방법도 있지만

(특히 기능 개발 중)

 

LCD가 있으면 전원 공급만 해도

읽을 수 있기에

LCD가 결과물에 필요없는 경우에도 붙여서 쓰기 참 좋은

그런 디바이스일지도 모르겠읍니다.

 

 

집 LED등을 개량하다가

태워먹은 관계로

오늘의 조명은 디월트 12V 작업등이 대신합니다.

 

좀 밝더라도 참으십시오.

 

 

 

아두이노를 콤퓨타에 꽂고,

빈 프로젝트를 생성한 뒤 바로 업로드 해주자.

 

괜히 전에 호작질하던 코드때문에 오동작 할 수도 있으니깐.

 

2023.01.31 - [Engineering Log/Micro Controller Unit] - Arduino - 1. IDE & 드라이버 설치, 예제

 

Arduino - 1. IDE & 드라이버 설치, 예제

Marius님, 정글에 들어오신 것을 환영합니다. 아두이노와 내 시간을 갖다버릴 마음의 준비를 가지고 있다면 이제 시작할 차례이다. IDE란? Integrated Developement Environtment. 적분된 개발 환경. 아, 통합

thewanderer.tistory.com

기억 안 나면 다녀오시고,

 

 

USB 전원을 제거한 뒤

결선을 시작해보자.

 

쇼트나 오결선으로 파손을 막기 위함이다.

사진 가운데 보이는 SCL, SDA를 각각,

VCC에는 5V를, GND는 GND를 연결해준다.

 

 

이번의 나처럼 Nano를 써서 SCL SDA핀이 없거나

우노 중에 표기가 없어 헷갈리는 경우

 

아래와 같이 결선해도 된다.

 

전원은 5V를 주면 되고

SDA - A4

SCL - A5

로 연결하면 된다.

 

 

 

 

I2C는 "주소"라는 이름이 있다.

I2C 관련 포스팅:

 

 

 

주인님께서(마스터) 이름을 부르고 할 일을 말해주면

노예 LCD놈(슬레이브)은 묵묵히 일을 해야하잖아.

 

여러 노예(슬레이브)가 있을 때

이름이 같으면 두 놈이 똑같은 일을 하것쥬.

그래서 주소를 다르게 해줘야 될 수 있다.

 

한 놈이라도 그 놈의 이름 (주소)가 어떻게 되는지

알리에서 3달러주고 노예를 사온 신(God, 프로그래머)는

주인(마스터)에게 이름이 뭔지 알아오라고 해야겠지.

 

 

 

 

 

이걸 I2C 스캔이라고 하는데,

주소를 모르면 이걸 먼저 해보자.

I2CScanner.ino
0.00MB

adatafruit 사에서 만든 코드인데

결과변수를 error라고 칭글리시를 해놔서

result로 변수명만 바꿔놨다.

 

그리고, 똑같은 주소를 5초마다 무한반복하던 기존 코드가 맘에 안 들어서

한 번만 돌고 안 하게끔 바꿨다.

 

다른 I2C 장치를 추가로 연결하고

(핫 플러그를 해도 된다. 아두이노를 안 끄고 연결해도 된다는 의미이다.)

본체의 리셋 버튼을 누르면 재검색을 할 수 있을거여.

 

시가레트는 피지 말자. 8년째 on and off 릴레이션십을 이어가고 있다.언제 끊나 ㅜㅜ

랜선을 까서 만든 점퍼선이 잘못되었는지

한참을 인식이 안돼서 20분을 허비했다.

 

하.

전선 바꾸니까 된다 슈밤.

 

연구실에서 점퍼선 잘못 깠다고

박사샛기한테 뒤통수 맞은게 생각난다.

학부연구생 할 때 너무 노예처럼 다녔던걸 생각하면

아직도 가끔 열이 올라온다.

 

 

 

만약에 I2C 디스플레이를 여러개 쓰고싶으면 어떻게 하면 되냐.

연결은 모두 같은 핀에 해주면 된다.

SDA는 SDA끼리,

SCL은 SCL끼리.

(전원 2핀은 말할 필요도 없구)

 

주소는 이렇게 (사진을 자세히 보면)

A0 ~ A2에 납땜을 하면 된다.

 

나는 A2에

저항 다리를 잘라 붙였다.

 

 

 

전에는 점퍼가 있는 모델을 썼는데,

알리에서 제일 싼걸 사니 금속 패드만 덜렁 나와있다.

이렇게 주소가 다른 2개 디스플레이가 감지되었다.

 

 

사이버대 과제로 낸 것 중 하나다.

당시 썼던 디스플레이.

 

 

 

헬로우 월드를 뿌려보자.

i2c display hello world.ino
0.00MB

소스코드 보기:

더보기
#include <LiquidCrystal_I2C.h>
#include <Wire.h>

#define LEFT_LCD 0x23  //lcd addr
#define RIGH_LCD 0x27
#define firstLine  0    //lcd top row
#define secondLine 1   //lcd bottom row

void writeOnDisplay(LiquidCrystal_I2C* lcdPointer, String sentence, int line);
LiquidCrystal_I2C lcd_left(LEFT_LCD,16,2);
LiquidCrystal_I2C lcd_right(RIGH_LCD,16,2);


void setup() {

  Serial.begin(115200);
  Serial.println("SPI SLAVE Initalised.");

  Wire.begin();
  lcd_left.init();
  lcd_right.init();
  lcd_left.backlight();
  lcd_right.backlight();
  writeOnDisplay(&lcd_left, "Hello", firstLine);
  writeOnDisplay(&lcd_right, "World", firstLine);


}

void loop() {
  // put your main code here, to run repeatedly:
  delay(500);
}

//Written By Never Settle Down
void writeOnDisplay(LiquidCrystal_I2C* lcdPointer, String sentence, int line)
{
  lcdPointer->setCursor(0, line);
  (*lcdPointer).print(sentence);
}

 

내가 전에 과제에 썼던 코드를 대충 긁어왔다.

한 줄을 아예 덮어 씌워버리는 방법을 쓴다.

그 행위를 하는 함수는 void writeOnDisplay(...)

 

(포인터는 알면 응용해서 쓰면 되고,

'포인터? 레이저포인터 말하는건가?' 하는 경우 그냥 공식처럼 가져다 쓰면 된다.)

 

...?

왜이러니??

 

점퍼를 직결해도 그러네.

 

다른 포스팅같으면 내 실수를 수정하고

일 없었다는듯이 글을 쓰겠지만,

이 글을 쓰는 목적은 코드 몇 줄 올려놓고 끝내는 것이 아니다.

 

하드웨어 + 소프트웨어적 문제를 해결해내는 방법을 알려주는 것.

그 것이 이 글의 목적이다.

 

어떻게 문제를 해결해내는지,

적어도 나는 어떻게 했는지를 그대로 적을 예정이다.

 

제대로 작동하는 코드도 올려둘게여.

 

 

일단 내가 가지고 있는 아두이노 중

손에 잡히는걸 모두 꺼내보았다.

 

우노의 경우 제조사 2종류.

 

이렇게 우노용 잉여 쉴드에

납땜을 좀 해서 I2C를 공유할 수 있게 만들었다.

즌문영어로 테스팅 지그라고 한다.

 

 

아두이노를 각각 A, B, C로 이름붙여주었다.

각자 다른 코드가 올라가있었으므로

새로 업로드해줬다.

 

 

뭐시여?

잘되네.

왓 다 빡..?

 

 

어제썼던 녀석이 C.

어제 올려둔 펌웨어 그대로 냅뒀다.

그래서 바로 꽂고 테스트.

 

어제랑 똑같이 안 된다.

 

 

 

I2C 관련 회로가 나갔나.

근데 어찌 저리 첫 글자는 잘 나오는 것이느뇨.

 

 

미간을 찌푸리며 아무 생각 없이 업로드를 눌렀다.

...

된다.

...

똑같은 코드.

...

랜덤 히껍 (딸꾹질) 떄문에 내 시간이... 허비...

어제 한 40분을 만지작거리며 시간을 태워버렸는데

오늘 대충 시작 하자마자, 어떻게 해결했는지 영문도 국문도 모른채 정상화가 되어버렸다.

 

즌믄영어로 "럭키 헌치"

 

 

안되는 펌웨어 올렸을 떄와

이번에 ABCD에 펌웨어 올렸을 때의 차이:

- PC를 껐다 켰다. (하루가 지남)

- USB 연결을 바꿨다. [PC직결]에서 [PC - 모니터 내장 USB허브 - 싸구려USB허브] 형태로.

- 다른 저녁을 먹었다. 어제는 연어, 오늘은 순대볶음.

- 라이브러리 1.1.1에서 1.1.2로 업데이트했다.

 

 

내 추측은

LiquidCrystal_I2C

라이브러리 버전 차이인 것 같다.

 

 

라이브러리 문제인가?

라이브러리를 업데이트 해주었다.

이런.  스샷을 안 찍었네.

 

저렇게 가서 Type을 Installed로 바꾸면

설치된 라이브러리를 볼 수 있다.

 

 

라이브러리가 뭐냐고? (궁금하면 아래의 더보기 클릭)

더보기

------------------------------------------------------------------------------------------------------------

[ 남이 짜 놓은 코드 ] 를 [ 다른 코드에서 쓸 수 있게 만든 형태 ] 라고 생각하면 된다.

 

아두이노에서 적분을 하고 싶다고 가정해보자.

CISC 타입 프로세서, i.e. 인텔이라 AMD같은 "일반"적인 PC 프로세서는 적분기가 내장되어있지만,

아두이노같은 저렴한 프로세서에는 적분을 할 수 있는 회로가 없다.

 

물론 코딩에 뜻이 있어서

직접 적분기를 맹글어 써도 되지만

언제 그거 다 짜고 앉아있냐.

코딩신이 만들어둔 만능 적분 소프트웨어를 쓰면 좋지 않을까?

바퀴를 재발명하지 마라. 휠 얼라 맡겨라.

integrator.c와 integrator.h로 구성된 (종종 또 다른 사람의 코드도 들어간) "라이브러리"를

다운받아 내 코드에서불러주면 되는 것이다.

------------------------------------------------------------------------------------------------------------

 

 

진짜 라이브러리 문제였는지 검증을 해봐야했다.

https://forum.arduino.cc/t/i2c-lcd-only-prints-first-character-of-the-string/352264/9

 

I2C LCD only prints first character of the string

I am sorry but I am a beginner and not yet fully up to speed with all the libs for this (there are many!!) and which might work, etc I recommend that you install the hd44780 library available through the library manager. This sketch should get you going #i

forum.arduino.cc

구글링 최상단 결과

 

포럼.아두이노.ㅊㅊ

믿을만한 정보원이다.

 

 

 

버전 1.1.1에 문제가 있는게 맞나보다.

그들의 방어 안에서, 저기에는 너무 많은, 잘못된 정보가 밖에 있다. 한 용매 밖에서 매우 단단하게 찾거나 형태를 띄는 이 토픽 위에.

버전 1.1.2를 쓰든지, 다른 라이브르리를 쓰든지.

 

나는 1.1.2를 고집하기로 했다.

검증된걸 써야지. 적어도 나에게는 검증된걸로.

 

 

 

 

여튼.

그럼 하나 더 해볼까나.

가변저항 (나는 5Kohm을 썼다) 양단을 VCC와 GND로 연결해준다.

가운데 조절되는 핀은 아두이노의 A0에 꼽았다.

 

위의 코드, I2C display hello world.c에서

 

void loop()

부분에

- 포텐시오미터 (가변저항) 읽는 코드

- 해당 값을 그냥/연산하여 LCD에 뿌리는 코드

를 추가했다.

 

아래 writeOnDisplay()는

기존의 String만 받던 함수형태 only에서

int값을 받을 수 있는 버전도 추가해 주었다.

(함수명이 동일해도 상관없음)

 

9, 10번 라인에 보이듯이

함수 먼저 선언도 해줬다.

(중요한건 아니다. 안해도 잘 되긴 함)

 

 

소스코드 보기:

더보기
#include <LiquidCrystal_I2C.h>
#include <Wire.h>

#define LEFT_LCD 0x23  //lcd addr
#define RIGH_LCD 0x27
#define firstLine  0    //lcd top row
#define secondLine 1   //lcd bottom row

void writeOnDisplay(LiquidCrystal_I2C* lcdPointer, String sentence, int line);
void writeOnDisplay(LiquidCrystal_I2C* lcdPointer, int numericDatum, int line);
LiquidCrystal_I2C lcd_left(LEFT_LCD,16,2);
LiquidCrystal_I2C lcd_right(RIGH_LCD,16,2);

const unsigned short potentiometreInputPin = A0;


void setup() {
  Serial.begin(115200);
  Serial.println("SPI SLAVE Initalised.");

  Wire.begin();
  lcd_left.init();
  lcd_right.init();
  lcd_left.backlight();
  lcd_right.backlight();
  writeOnDisplay(&lcd_left, "Potentiometre", firstLine);
  writeOnDisplay(&lcd_right, "ADC Value", firstLine);
}

void loop() {
  int potentioVal = analogRead(potentiometreInputPin);
  writeOnDisplay(&lcd_left, (int)((double)potentioVal/10.24), secondLine);
  writeOnDisplay(&lcd_right, potentioVal, secondLine);
  delay(500);
}

//Written By Never Settle Down
void writeOnDisplay(LiquidCrystal_I2C* lcdPointer, String sentence, int line)
{
  lcdPointer->setCursor(0, line);
  (*lcdPointer).print(sentence);
}
void writeOnDisplay(LiquidCrystal_I2C* lcdPointer, int numericDatum, int line)
{
  lcdPointer->setCursor(0, line);
  (*lcdPointer).print(numericDatum);
}

 

 

 

오른쪽의 포텐시오미터가 돌아가면서

두 화면의 값이 바뀐다.

 

왼 쪽은 포텐시오미터의 퍼센테이지를 (0 ~ 100)

오른쪽은 Analog to Digital Converter 값을 (0 ~ 1024)

 

ADC란? (궁금하면 you know the drill)

더보기

------------------------------------------------------------------------------------------------------------

ADC는 말 그대로 "아날로그 값을 디지털로 바꾸는" 주변장치이다.

 

아두이노의 경우

0V ~ 5V를 1024개로 나누어 입력값을 받을 수 있다.

여기서 0부터 5까지 무한개의 실수를

천개의 유한한 정수로 바꾸는 행위를 "양자화", digitisation이라고 한다.

 

내가 만약 아날로그 핀에 2.5V 전압을 걸어주면

아두이노에서 값이 512로 읽힌다.

 

계산 식은

5 * (아두이노 값) / 1024 = 실제 전압

이다.

 

물론, 이 "양자화"를 하면 오차가 발생하게 된다.

읽어들인 값의 오차 범위는 +/- 0.00244140625V로 매우 작은 값이다.

 

이 양자화값 (아두이노의 경우 1024, 10비트)를

1비트로 하게되면 디지털이 된다.

0V = 0b0

5V = 0b1

(여기서 prefix "0b"는 비트 임을 나타낸다.

바이트, 2^4 단위로 묶으면 0x)

------------------------------------------------------------------------------------------------------------

 

 

여기까지 해보고 만지작거리다

문제를 발견한사람 손?

ADC 값이

0 ~ 1024라 그랬는데

디스플레이에 왜 6220이란 값이 있는거지?

 

 

이제 디스플레이의 "커서" 개념을 써볼 차례이다.

물론, 띄어쓰기 16개, "                "를 입혀도 되긴 한다.

아래 코드처럼.

매번 값을 띄울때마다

2번째 줄을 비우고 (엄밀히 말하자면 띄어쓰기를 표현하고)

다시 쓰는 형태.

 

 

이 얼마나 비효율적인가. (디버깅용으론 이게 편하겠디.)

유즈케이스 따라 (LCD에 할애하는 시간을 줄이거나, 전력 소모량을 낮추거나...)

최적화 할 필요가 있을 수도 있다.

 

그리고 이 코드를 쓰면

화면이 (2열) 껌뻑껌뻑거린다.

지웠다 썼다 지웠다 썼다...

 

이 코드는 쭉 실행하고 마지막에 0.5초를 (500ms) 쉰다.

이걸 1ms로 줄여버리면 이게 더 심해진다.

 

따라하지 마셈. 구조체 배열을 써볼라고 뻘짓한거라 안 된다.

하... LCD 2개라

구조체 배열로 조져보려 했으나

C 만진지 너무 오래된 터.

뻘짓만 하고 있다 ㅋㅋㅋㅋ ㅜ

 

 

i2c display hello world.ino
0.00MB

소스코드 보기 (주석부분이 깨져서, 보이기로는 코드가 주석으로, 주석이 코드로 (vice versa) 보인다)

더보기
#include <LiquidCrystal_I2C.h>
#include <Wire.h>

#define LEFT_LCD 0x23  //lcd addr
#define RIGH_LCD 0x27
#define firstLine  0   //lcd top row
#define secondLine 1   //lcd bottom row
#define leftLCD  0     //to Enhance Readablility
#define rightLCD 1

void writeOnDisplay(LiquidCrystal_I2C* lcdPointer, String sentence, int line);
void writeOnDisplay(LiquidCrystal_I2C* lcdPointer, int numericDatum, int line);
void writeOnDisplayAt(LiquidCrystal_I2C* lcdPointer, String numericDatum, int line, int location);
int numberOfDigit(int datum);


LiquidCrystal_I2C lcd_left(LEFT_LCD,16,2);
LiquidCrystal_I2C lcd_right(RIGH_LCD,16,2);
const unsigned short potentiometreInputPin = A0;
int lastNumOfDigit[2] = {0, 0};
int lastNumericData[2] = {0, 0};


void setup() {
  Serial.begin(115200);
  Serial.println("SPI SLAVE Initalised.");

  //Wire.begin(); //this one is included in lcd.init().
  lcd_left.init();
  lcd_right.init();
  lcd_left.backlight();
  lcd_right.backlight();
  writeOnDisplay(&lcd_left,  "Potentiometre %", firstLine);
  writeOnDisplay(&lcd_right, "ADC (0-1024)", firstLine);
}


void loop() {
  int numbersToShowOn[2];
  int digitsOfThisTimeOn[2];
  String tempStr = "";


  //reading ADC & calculatiing
  numbersToShowOn[rightLCD] = analogRead(potentiometreInputPin);
  //numbersToShowOn[leftLCD]  = (int)((double)(numbersToShowOn[rightLCD] - 512)/5.12);
  numbersToShowOn[leftLCD]  = (int)((double)(numbersToShowOn[rightLCD])/10.14);

  //if the data's the same, skipping LCD part
  if(lastNumericData[leftLCD] == numbersToShowOn[leftLCD] && lastNumericData[rightLCD] == numbersToShowOn[rightLCD]) goto skipLCD;
  else { lastNumericData[leftLCD] = numbersToShowOn[leftLCD]; lastNumericData[rightLCD] = numbersToShowOn[rightLCD]; }

  //checking if there's some previous numbers to wipe out 
  for(int thisSide = 0; thisSide < 2; thisSide++)
  {
    digitsOfThisTimeOn[thisSide] = numberOfDigit(numbersToShowOn[thisSide]);

    if(digitsOfThisTimeOn[thisSide] < lastNumOfDigit[thisSide])
    {
      for(int i = -1; i < lastNumOfDigit[thisSide] - digitsOfThisTimeOn[thisSide]; i++) tempStr += " ";

      switch(thisSide)
      {
        case leftLCD:
        writeOnDisplayAt(&lcd_left,  tempStr, secondLine, digitsOfThisTimeOn[thisSide]);
        break;

        case rightLCD:
        writeOnDisplayAt(&lcd_right, tempStr, secondLine, digitsOfThisTimeOn[thisSide]);
        break;
      }
      tempStr = "";
    }
    
    lastNumOfDigit[thisSide] = digitsOfThisTimeOn[thisSide];
  }
  
  writeOnDisplay(&lcd_left,  numbersToShowOn[leftLCD], secondLine);
  writeOnDisplayAt(&lcd_left,  "%", secondLine, digitsOfThisTimeOn[leftLCD]);
  writeOnDisplay(&lcd_right, numbersToShowOn[rightLCD], secondLine);

skipLCD:
  delay(10);
}



//User Defined Functions
//Written By Never Settle Down
int numberOfDigit(int datum)
{
  int absoluteOfDatum = datum<0 ? datum * -1 : datum;
  if(absoluteOfDatum < 10) return datum<0 ? 2 : 1;
  else                     return 1 + numberOfDigit(datum/10);
}
void writeOnDisplayAt(LiquidCrystal_I2C* lcdPointer, String numericDatum, int line, int location)
{
  lcdPointer->setCursor(location, line);
  (*lcdPointer).print(numericDatum);
}

void writeOnDisplay(LiquidCrystal_I2C* lcdPointer, String sentence, int line)
{
  writeOnDisplayAt(lcdPointer, sentence, line, 0);
  //lcdPointer->setCursor(0, line);
  //(*lcdPointer).print(sentence);
}
void writeOnDisplay(LiquidCrystal_I2C* lcdPointer, int numericDatum, int line)
{
  lcdPointer->setCursor(0, line);
  (*lcdPointer).print(numericDatum);
}

 

 

#define.

해시태그 디파인. ㅇㅈㄹ.

 

샵 디파인 뒤에 이름쓰고, 값 (정수, 실수, 스트링...)을 넣고

코드 중간에 디파인 이름을 박으면

컴파일러가 컴파일할 때 이 이름들을 모두 그 뒤의 값으로 바꾼 뒤 진행한다.

 

- 여러번 쓰는 고정값인데 나중에 바뀔 수도 있는 경우 (lcd의 i2c 주소처럼)

- 가독성을 높이고 싶을 경우 (e.g. 코드 상의 leftLCD, rightLCD처럼. 실제 값인 0이랑 1로 하면 겁나 헷갈릴걸)

에 쓰면 된다.

 

 

 

 

하단 사용자 정의 함수부분에서

writeOnDisplayAt

함수가 보이나?

저게 커서 위치를 조정하는 기능을 품은 코드여.

 

 

 

 

goto문을 썼다. Goto란? YKD (you know the drill)

더보기

------------------------------------------------------------------------------------------------------------

길다란 코드 전체를 if문으로 묶기 시작하면

코드는 점점 오른쪽으로 밀리고

나중에 진짜 뭐가 뭔지 헷갈린다.

 

if (condition) goto LABEL;

...//컨디션이 false일때만 실행

LABEL:

...//컨디션 상관없이 공동 실행.

 

이렇게 쓰면 된다.

그렇다고 코드 위아래를 미친듯이 점핑하기 시작하면

다시 짜야된다고 보면 되고,

if문 대체용 식으로 간단히 쓸 때 좋다.

 

특히 하나의 조건으로 break문 두 번 써야될 때.

while(condition A)

{

   while(condition B)

   {

      if(condition X) goto getDaHellOuttaHere;

   }

}

getDaHellOuttaHere:

나머지코드()

...

 

이프 조건문에 break를 달면

안쪽 while는 벗어나지만

바깥 while은 못 벗어난다.

 

이럴 떄 유용한 코드다.

------------------------------------------------------------------------------------------------------------

 

 

 

 

 

 

absoluteOfDatum = ... ? ... : ... ;

코드 보이지.

삼항연산자야. 궁금하면 유노더드릴

더보기

------------------------------------------------------------------------------------------------------------

연산결과를넣을변수 = 조건식 ? 참일경우값 : 거짓일경우값

 

String 오늘_저녁_메뉴;

 

오늘_저녁_메뉴 = (나는고기가땡긴다() == true) ? "폭찹" : "연어구이";

괄호는 쳐도 되고 안 쳐도 된다.

------------------------------------------------------------------------------------------------------------

 

 

상단의 전역변수 (군대를 다녀온 변수가 아니고 아무 함수에서나 불러다 쓸 수 있는 "공용"변수)

 

int lastNumOfDigit[2] = {0, 0};

int lastNumericData[2] = {0, 0};

 

를 가지고 이전 숫자가 뭐였는지 저장하는 부분.

 

플로우차트를 만들어주면 구구절절 안떠들어도 되려나?

i2c hello.drawio
0.00MB

구글의 diagrams.net (구 draw.io)로 열면 된다.

 

 

좋은 소스를 만드려면

기능별로 사용자 정의 함수를 만드는 것이나,

어제 끄적 끄적 만들다보니 메인함수 (loop)에 다 때려박아버렸다.

 

떼어내면 로직을 많이 수정해야해서

그냥. 간다.

 

 

 

 

 

 

 

 

이 다음은 Adafruit의 OLED 디스플레이를 써볼 예정이다.

전 직장 두 곳에서 썼던 녀석들이다.

 

하나는 나노 + OLED

하나는 마이크로 + OLED

 

저 두 아두이노의 유즈케이스가 궁금하다면, 유 노우 다 드릴

더보기

------------------------------------------------------------------------------------------------------------

- 나노의 경우 컴퓨터 PC 안에, 프론트패널 IO에 연결해놓고 썼다.

출근시간 10분 전에 전원을 인가해서 미리 켜놓고 (거기는 PC on = 전산상 출근이었음)

근무시간 종료 후 2분 지나면 최대절전모드로 들어가도록

세팅.

 

 

- 마이크로의 경우

ㅋㅋㅋㅋㅋㅋㅋㅋㅋ

내 직전 직장 재택용으로 썼다.

회사에서 주어지는 일이 정말 단순노무밖에 없었다.

 

엑셀 찔끔. 워드 찔끔. PPT 찔끔.

전자과나와서 영어 번역이나 하고 있었다.

나는 통번역 2개 전선 두개를 A+/A0를 받았으므로

 

엑셀은 진짜 양식 한 3일 걸려서 만들어놓고

업무 시기 되면 값 붙여넣기 - 복사하기 - 셀값 붙여넣기

끝. 5초면 됨.

5학사를 바라보고 있는 입장에서

기가 차는 업무 아니냐 진짜. 망해라. 상폐를 간절히 소망합니다.

 

 

그래서 재택근무시에는

리석감지시스템을 무력화하여

나의 자유시간을 늘리고

동무들에게는 렬심히 로동하는 모습을 보여

김종은 동지의 영광을... ㅋㅋㅋㅋ

 

 

마이크로의 USB 통신 기능을 이용해

마우스 클릭, 드래그, 랜덤 움직임을 사용했다.

리모컨을 이용해 기능을 껏다 켤 수 있고,

급하면 (e.g. 팀장님이 메세지를 보냈을 경우) 본체의 버튼 (지금은 삭제되었다)을 눌러

끌 수도 있게 하였다.

 

몇가지 비밀번호는 (공용 ID같은거)

아두이노 내부의 Flash 메모리에 저장하는 방식.

시리얼 터미널을 켜서 비번을 업데이트 해주면

플래시의 변수값을 덮어써버려

하드코딩된 비번을 수정하고 매번 아두이노 FW를 다시 올리지 않아도 되었다.

 

물론 내 리모컨이 없으면 비번도 리석감지시스템도 무력화되고

(소유기반인증)

리모컨이 있어도 어떤 시퀀스를 눌러야 작동하는지도 다른 사람은 모를터이고

(지식기반인증)

저장한 암호 자체가 유출되도 별 영향이 없는 낮은 중요도 정보였기 때문에

(내 회사 계정 password는 사생활 password와 완전히 달랐음 + 사내망PC에 접속하지 않으면 쓸 수 없는 공용ID)

안전하다고 판단, 암호화따위는 하지 않았다.

(아두이노 칩셋 안에 들어있는 데이터를 뺴내는게 더 어려울듯.)

------------------------------------------------------------------------------------------------------------

 

 

 

에이다 프루트 LCD 모두 I2C 코넥숀을 사용한다.

 

...I2C 스캐너로 찍어보니

두 놈 주소가 같게 나온다.

 

뚱뚱한놈은 주소를 바꿀 수 있으나

길쭉이는 주소를 바꿀 수 없다.

 

길쭉이만 뽑아 쓰기로 결정했다.

 

춘장익스프레스에서 길쭉이 2개 뚱떙이 2개를

추가로 주문했다. 무배 10,820원

 

#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>

요 두 라이브러리를 쓸 예정이다.

추가해주자.

 

이렇게 예제를 올려보자

 

에이다프룻 로고로 바로 넘어가고

로고가 빨리 떨어지도록 예제를 수정한 파일:

ssd1306_128x32_i2c_Swooping.ino
0.01MB

 

저 위에서 PC 안에 넣었던 녀석

코드

(혹시 궁금하면)

Work_PC_Mod.ino
0.01MB

씸플한 예제코드 먼저 보자.

Adafruit OLED simple test.ino
0.00MB

쏘오쓰 코드 보기:

더보기
//#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>

#define SCREEN_WIDTH 128 // OLED display width, in pixels
#define SCREEN_HEIGHT 32 // OLED display height, in pixels
// Declaration for an SSD1306 display connected to I2C (SDA, SCL pins)
#define OLED_RESET     -1 // Reset pin # (or -1 if sharing Arduino reset pin)
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);



void setup() {
  display.begin(SSD1306_SWITCHCAPVCC, 0x3C);
  display.clearDisplay();
  
  display.setTextColor(SSD1306_WHITE);
  display.setTextSize(2);
  display.setCursor(0,0);
  display.print("HelloWorld\nPrivietMir");
  if(SCREEN_HEIGHT < 33) goto displayThis;
  display.setTextSize(1);
  display.setCursor(0,32);
  display.print("HelloWorld\nPrivietMir");
displayThis:
  display.display(); 
}

void loop() {
  // put your main code here, to run repeatedly:

}

똰.

 

128 x 64짜리

뚱떙이 디플을 써볼까?

코드 수정을 안 하고

디스플레이만 갈아끼운 뒤

아두이노를 리셋시켰다.

 

오우.

디스플레이 드라이버 (디플 밑에 있는 칩셋)에서

알아서 배율을 조정해주나 ㅋㅋㅋㅋㅋ

펌웨어에서

해시태그 디파인 값을 수정해주고 다시 올리면

이렇게 4 줄이 뜬다.

 

 

 

 

 

이제 글자 옆 빈 공간에

숫자를 띄워보자.

void loop() {
  static byte number = 0;
  display.setCursor(0,32+16); display.setTextColor(SSD1306_BLACK); display.print(number);
  display.setCursor(0,32+16); display.setTextColor(SSD1306_WHITE); display.print(++number);
  display.display();
  //delay(10);
}

루프만 이렇게 바꿔주면 된다.

커서 위치는 알아서들 바꾸시고...

 

 

 

 

아놔 사진 안 찍었네.

 

 

 

 

 

여기서

static을 쓰는 이유: YKD

더보기

------------------------------------------------------------------------------------------------------------

스태틱을 지우고 해보면 알 것이다.

1으로 숫자가 고정된다.

 

이는 loop를 돌 때마다 변수 number를 0으로 초기화 시키기 때문.

 static을 붙여주면 loop 처음 진입할 때만 초기화를 시키고

그 뒤로는 그 값을 유지한다.

------------------------------------------------------------------------------------------------------------

 

 

 

++number를 쓰는 이유: YKD

더보기

------------------------------------------------------------------------------------------------------------

number++과 차이점은

언제 더하느냐이다.

++을 앞에 쓰면 "그 라인을 실행하기 전에" number += 1을

++을 뒤에 쓰면 "그 라인을 실행하고 나서" number += 1을

수행한다.

------------------------------------------------------------------------------------------------------------

 

 

 

근데,

단순하게 숫자만 띄우는게 아니고

 

setTextColour Black? White? 이건 뭐여.

싶을 것 같다.

 

 

나도 그냥 숫자 띄워봤지...

 

워낙 빠르게 숫자를 띄우니까

그냥 하얀 네모로 뜨더라곸ㅋㅋㅋㅋ

이전 숫자를 안 지우고

같은자리에 다음 숫자를 써버려서

흰색만 남아버림..

 

 

 

setTextColour함수에서

Black, White를

번갈아 가면서 쓴 이유를 아래 적어 뒀다.

 

 

일단, 그 메커니즘을 이해하기 위해

아래 영상을 보자. 내가 보여주고자 하는 부분이 바로 틀어질 것이다.

https://youtu.be/YE0U018Copw?t=911 

아 쒸부엉.

정확하게 그 시간으로 안가네.

(링크 타고 가면 정확히 가는데 여기엔 그게 안된다)

 

15:10으로 가면

그 모습을 볼 수 있다.

 

글자를 쳤던 부분에

점착 테이프를 올리고

정확하게 다시 쳐냄으로서

글자를 떼어내는 것.

 

이 것이 이번 예제에서 써낸 메커니즘이다.

 

 

직전에 뿌렸던 글자 모얌대로 검은색 픽셀을 띄워서 글자를 지우고,

이번에 뿌릴 글자 모양대로 흰색 픽셀을 띄워서 글자를 띄우는 방법론.

 

 

띄어쓰기를 해봤는데

블럭 전체를 지우는 i2c LCD와 달리

Adafruit OLED는 아무 것도 하지 않았다.

 

 

물론 OLED 전체를 Clear하고

전부 다시 뿌릴 수도 있다.

 

 

상단의 고정 문자열도 기억했다 전송해야하고

변하지 않는 큰자릿수 값도 다시 뿌려야한다.

 

엄청난 컴퓨팅파워 낭비일 뿐더러

통신속도가 느린 만큼 OLED 모듈과 통신하는데 시간을 많이 잡아먹는다.

 

리얼타임 OS, rtOS에 가깝게 코딩하고싶다면

코딩 편하자고 컴퓨터가 뻘짓하기보단

코딩 빡세게 해서 컴퓨터가 편하게 해주는게 좋을 것이다.

 

즉,

잔대가리 잘 굴려야 MCU 코딩을 잘 할 수 있다.

 

 

 

 

이정도 하면 된거같네.

 

그래픽스는 일단 나는 패스.

나중에 요청 들어오면 글 올려드려야지.

반응형

댓글