우선 동작에 관한 비디오 녹화입니다.




아이가 보드 게임을 좋아하기 때문에 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 );
}
  • 12: 라인에서 보면 이진수로 data라는 변수에 10000000을 입력하였습니다.
  • 15: 라인에서 이 변수의 값을 shift register로 전송하고 있습니다.  반복되고 있지만, 한 번만 전송하여도 됩니다.  이렇게 되면 shift register의 첫 번 째 출력 핀에 + 전류가 발생합니다.  On 상태가 되는 것 입니다.  (첫 번 째가 아니라 여덟 번 째이지만 편의상 그렇게 표현했으며, 순서는 여러분들이 변경 할 수도 있습니다)  결국 shift register의 첫 번 째 핀에 연결된 LED만 불이 들어오게 됩니다.


이제 두 번 째 LED에 불이 들어오게 하려면 어떻게 해야 할 까요?  아이가 금새 답을 찾습니다.  아래와 같이 12: 라인을 변경해주면 됩니다.

  • byte data = 0b01000000;


이것을 좀 더 편리하게 하는 방법은 "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 );
}
  • 16: 라인에서 0.5초 쉬었다가 18: 라인에서 data의 값을 1 칸씩 오른쪽으로 이동하도록 하고 있습니다.  (이진수 기준입니다)  매번 loop가 반복 될 때마다 data 값이 변하면서 연결 된 LED의 불이 차례로 이동하면서 [그림 2]와 같이 켜지게 됩니다.


[그림 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 );
}
  • 18:, 19:, 20: 라인에서 세 개의 하나씩 LED를 차례대로 켜고 잠시 기다렸다가 다음 LED를 켜는 것을 반복합니다.  이렇게 되면 불이 하나씩 차례로 들어오는 과정을 관찰 할 수가 있습니다.
  • 22: 라인에서 loop() 함수가 반복 될 때마다 기다리는 시간을 반씩 줄여 나갑니다.  LED 불이 바뀌는 것이 점점 빨라집니다.  그러다가 결국에는 [그림 4]처럼 모두 켜진 것처럼 보이게 됩니다.


[그림 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);
   } 
}







'프로그래밍 > Arduino & IoT' 카테고리의 다른 글

아두이노 - 만보기 게임  (0) 2015.05.05
아두이노 - 금속 탐지기  (0) 2015.04.25
아두이노 - 주사위 만들기  (0) 2015.04.19
아두이노 - 뮤직박스  (0) 2015.04.19
SW-420 센서  (0) 2015.04.12
아두이노 가스 안전 밸브  (0) 2015.04.05

Posted by 류종택