우선 동작에 관한 비디오 녹화입니다.
아이가 보드 게임을 좋아하기 때문에 2 개짜리 주사위를 만들게 되었습니다. 나중에 가지고 다니기 편하도록 이번에도 ATTiny 85를 이용해서 최종 결과물을 만들 예정입니다. 중간 과정에는 아두이노 나노와 빵판을 이용해서 필요한 지식을 미리 예습하는 과정을 거쳤습니다.
LED를 주사위를 표현할 때에는 대부분 [그림1]과 같이 7개의 LED를 사용합니다. [그림 1]에는 7개씩 두 개의 주사위가 있습니다. 가운데 하나가 더 들어가 있는 이유는 일반적인 주사위의 3과 5의 눈이 가운데를 사용하는 형식이기 때문입니다.
[그림 1] 최종 완성 단계
LED 14개를 사용해야 하는데, ATTiny의 GPIO 개수가 모자라기 때문에 shift register를 사용했습니다. 그 때문에 아이에게 shift register의 원리를 설명하기 위해서 아래의 예제를 먼저 공부하였습니다.
위의 예제를 좀 더 이해하기 쉽도록 아래와 같은 3 단계의 코드를 통해서 설명드리겠습니다.
[소스 1]
int pinData = 2; int pinClock = 3; int pinLatch = 4; void setup() { pinMode(pinData, OUTPUT); pinMode(pinClock, OUTPUT); pinMode(pinLatch, OUTPUT); } byte data = 0b10000000; void loop() { writeShift( data ); } void writeShift(byte data) { shiftOut( pinData, pinClock, MSBFIRST, data ); digitalWrite( pinLatch, HIGH ); digitalWrite( pinLatch, LOW ); }
이제 두 번 째 LED에 불이 들어오게 하려면 어떻게 해야 할 까요? 아이가 금새 답을 찾습니다. 아래와 같이 12: 라인을 변경해주면 됩니다.
이것을 좀 더 편리하게 하는 방법은 "data = data >> 1"과 같이 shift 연산을 이용하는 것입니다. >> 기호는 덧셈 뺄셈과 같은 연산자로 앞에 있는 숫자의 이전수 형식에서 뒤에 오는 숫자만큼 오른쪽으로 움직이라는 의미입니다.
[소스 2]
int pinData = 2; int pinClock = 3; int pinLatch = 4; void setup() { pinMode(pinData, OUTPUT); pinMode(pinClock, OUTPUT); pinMode(pinLatch, OUTPUT); } byte data = 0b10000000; void loop() { writeShift( data ); delay( 500 ); data = data >> 1; } void writeShift(byte data) { shiftOut( pinData, pinClock, MSBFIRST, data ); digitalWrite( pinLatch, HIGH ); digitalWrite( pinLatch, LOW ); }
[그림 2] LED가 차례로 켜지다가 마지막에는 모두 불이 꺼지게 됩니다.
오른쪽으로 계속 shift(이동) 하다보면 결국에는 모든 불이 꺼지게 됩니다. 오른쪽으로 이동하면서 왼쪽 첫 번 째 자리는 0으로 채워지기 때문입니다.
아래 아이가 소스는 LED가 계속 차례대로 불이 들어오도록 하라는 숙제를 해결한 내용입니다.
[소스 3]
int pinData = 2; int pinClock = 3; int pinLatch = 4; void setup() { pinMode(pinData, OUTPUT); pinMode(pinClock, OUTPUT); pinMode(pinLatch, OUTPUT); } byte data = 0b10000000; void loop() { writeShift( data ); delay( 500 ); data = data >> 1; if (data == 0) data = 0b10000000; } void writeShift(byte data) { shiftOut( pinData, pinClock, MSBFIRST, data ); digitalWrite( pinLatch, HIGH ); digitalWrite( pinLatch, LOW ); }
shift register는 이렇게 3개의 입력핀으로 8개의 출력을 흉내 낼 수 있는 기능이 있습니다. 또한 shift register를 서로 연결하면 여전히 3개의 입력핀으로 16, 24.. 처럼 계속 확장해 나갈 수도 있습니다. 이에 대한 설명은 다음 동영상을 참고하시기 바랍니다.
하지만, [그림 1]의 최종 단계에서는 shift register를 한 개만 사용하고 있습니다. 주사위 2개를 표현하기 위해서는 14개의 LED가 필요한데 여전히 출력핀이 부족한 상황입니다. 이것을 해결하기 위해서 이번 프로젝트에서는 multiplexing(멀티플렉싱)을 사용하기로 했습니다.
[그림 3] multiplexing (LED 아래서 본 것, 세로와 가로 선이 서로 합선 되면 안된다)
[그림 3]은 각 LED 윗쪽을 + 선으로 보고 이것을 가로로 한 줄 씩 모두 한 선에 연결한 상태입니다. 세로는 - 선끼리 연결한 상태입니다. 만약 [그림 3]과 같이 왼쪽 맨 위에 (1, 1) 있는 LED만 불이 들어오게 하고 싶다면, +1 선에 + 전원을 넣고, -1 선에는 - 전원을 넣으면 됩니다. 이때, -2 선에도 - 전원을 넣게 된다면, (1, 2) LED도 불이 들어오게 됩니다. 따라서, -2, -3, -4 에는 + 전원을 넣어야 합니다. 이렇게 8개의 핀을 4개씩 나눠서 4x4 2차원 평면을 만들고 원하는 곳에 불이 들어오도록 전원을 +(HIGH)와 -(LOW)를 입력하면 됩니다.
문제는 우리가 원하는 주사위에서는 하나의 LED가 아니라 여러 개의 LED에 불이 들어와야 합니다. 이것은 불을 켜고 싶은 LED를 하나씩 순서대로 불을 켜되, 상당히 빠른 속도로 번갈아서 불을 켜게 되면 사람 눈으로는 모든 불이 동시에 켜진 것처럼 보이게 됩니다. [소스 4]는 그것을 확인하기 위해서 작성 된 코드입니다.
[소스 4]
int pinData = 2; int pinClock = 3; int pinLatch = 4; void setup() { pinMode(pinData, OUTPUT); pinMode(pinClock, OUTPUT); pinMode(pinLatch, OUTPUT); } byte data = 0b10000000; int delay_count = 1000; void loop() { // Dice display test writeShift( 0b00011110 ); delay( delay_count ); writeShift( 0b00010111 ); delay( delay_count ); writeShift( 0b00101011 ); delay( delay_count ); delay_count = delay_count / 2; } void writeShift(byte data) { shiftOut( pinData, pinClock, MSBFIRST, data ); digitalWrite( pinLatch, HIGH ); digitalWrite( pinLatch, LOW ); }
[그림 4] 빠르게 반복되어 동시에 켜진 것처럼 보이는 LED들
그리고 [소스 5]는 최종 결과물입니다. 이진수 앞에 4칸은 LED + 선에 연결되었고, 뒤의 4칸은 - 선에 연결되었습니다. 그래서, 앞의 4칸 중 하나만 1(HIGH)이고 나머지는 0(LOW)인 반면, 뒤의 4칸은 하나만 0이고 나머지는 1이 됩니다. 이로써 원하는 위치의 하나의 LED만 켤 수 있게 됩니다. 한 줄에 여러 개씩 동시에 켜서 사용하는 방법도 있으니 연구해보시기 바랍니다.
[소스 5]
int pinData = 2; int pinClock = 3; int pinLatch = 4; int pinButton = 1; int leftNo = 0; int rightNo = 0; void setup() { pinMode(pinData, OUTPUT); pinMode(pinClock, OUTPUT); pinMode(pinLatch, OUTPUT); pinMode(pinButton, INPUT); } void loop() { if (digitalRead(pinButton) == LOW) { srand(millis()); leftNo = (rand() % 6) + 1; rightNo = (rand() % 6) + 1; } left(leftNo); right(rightNo); } void writeShift(byte data) { shiftOut( pinData, pinClock, MSBFIRST, data ); digitalWrite( pinLatch, HIGH ); digitalWrite( pinLatch, LOW ); } void left(int no) { switch (no) { case 1: writeShift( 0b00010111 ); break; case 2: writeShift( 0b00011110 ); writeShift( 0b00101011 ); break; case 3: writeShift( 0b00011110 ); writeShift( 0b00010111 ); writeShift( 0b00101011 ); break; case 4: writeShift( 0b00011110 ); writeShift( 0b00101110 ); writeShift( 0b00011011 ); writeShift( 0b00101011 ); break; case 5: writeShift( 0b00011110 ); writeShift( 0b00101110 ); writeShift( 0b00011011 ); writeShift( 0b00101011 ); writeShift( 0b00010111 ); break; case 6: writeShift( 0b00011110 ); writeShift( 0b00011101 ); writeShift( 0b00101110 ); writeShift( 0b00011011 ); writeShift( 0b00101101 ); writeShift( 0b00101011 ); break; default: writeShift(0); } } void right(int no) { switch (no) { case 1: writeShift( 0b01000111 ); break; case 2: writeShift( 0b01001110 ); writeShift( 0b10001011 ); break; case 3: writeShift( 0b01001110 ); writeShift( 0b01000111 ); writeShift( 0b10001011 ); break; case 4: writeShift( 0b01001110 ); writeShift( 0b10001110 ); writeShift( 0b01001011 ); writeShift( 0b10001011 ); break; case 5: writeShift( 0b01001110 ); writeShift( 0b10001110 ); writeShift( 0b01001011 ); writeShift( 0b10001011 ); writeShift( 0b01000111 ); break; case 6: writeShift( 0b01001110 ); writeShift( 0b01001101 ); writeShift( 0b10001110 ); writeShift( 0b01001011 ); writeShift( 0b10001101 ); writeShift( 0b10001011 ); break; default: writeShift(0); } }
아두이노 - 만보기 게임 (4) | 2015.05.05 |
---|---|
아두이노 - 금속 탐지기 (0) | 2015.04.25 |
아두이노 - 뮤직박스 (0) | 2015.04.19 |
SW-420 센서 (0) | 2015.04.12 |
아두이노 가스 안전 밸브 (0) | 2015.04.05 |