아이가 런닝맨을 좋아해서 런닝맨에 가끔 나오는 만보기 게임기를 만들었습니다.  포스트를 작성하는 동안에도 주머니에 넣고 온 집을 뛰어다니고 있습니다 ㅋㅋ  아래는 손으로 흔들기 데모입니다.  부저가 울리면 시작되고, 부저가 다시 울리면 흔들어도 숫자가 더 이상 늘어나지 않습니다.  (어린이 날은 집에서 아두이노를 ㅡ.ㅡ)






작동 원리


수은 스위치가 기울어지거나 흔들릴 때마다 숫자를 1씩 증가 시키도록 하였습니다.  정확도가 떨어지기 때문에 만보기로 사용하기에는 무리가 있습니다.  정해진 시간(30초) 동안 누가 더 많은 숫자를 기록하느냐 게임을 하는 것을 목표로 만들어 졌습니다.  단순하지만 상당히 좋아합니다.  손으로 흔들지 않고 주머니에 넣고 뛰어 다니면서 게임을 하였습니다.  (운동량이 상당하네요)




사용 된 부품


프로토타입은 [그림 1] 빵판에서 작업하였습니다.


[그림 1] 빵판에 작업 중인 만보기




[그림 2] 완성 된 작품의 뒷면




[그림 3] 완성 된 작품의 앞면




아래는 사용 된 소스 코드입니다.  기본적인 라이브러리는 미리 준비해 주고 아이가 코딩을 하는 동안 옆에서 계속 코치해주었습니다.  이번에는 제 코딩 방식을 대부분 따라하도록 하였기 때문에 코딩 시간이 평소보다 상당히 길어 졌습니다.


[소스]

unsigned char LED_BCD[16] =
  { 0xc0, 0xf9, 0xa4, 0xb0, 0x99, 0x92, 0x82, 0xf8, 0x80, 0x90, 0x88, 0x83, 0xc6, 0xa1, 0x86, 0x8e }; 

int data = 1;  
int rck  = 3;
int sck  = 4;

int pin_switch = 2;

int pin_buzzer = 0;

volatile int count = 0;

unsigned long old_tick;
unsigned long tick;

void setup() {
//  Serial.begin(9600);
  
  pinMode( data, OUTPUT );
  pinMode( rck,  OUTPUT );
  pinMode( sck,  OUTPUT );

  pinMode(pin_switch, INPUT);
  digitalWrite(pin_switch, HIGH);
  
  pinMode(pin_buzzer, OUTPUT);
  
  tone(pin_buzzer, 262, 500);
  delay(550);

  tone(pin_buzzer, 262, 500);
  delay(550);
  
  tone(pin_buzzer, 524, 1000);
  
  attachInterrupt(0, on_rising, RISING); 
  
  old_tick = millis();
}

int interval = 1000 * 30;
int is_finished = 0;

void loop() {
//  Serial.println(count);
//  delay(100);

  display_integer(count);

  tick = millis();
  
  if ((tick - old_tick) > interval) {
    if (is_finished == 0) {
      is_finished = 1;
      
      detachInterrupt(0);
      tone(pin_buzzer, 262, 2000);
    }
  }
}

void on_rising() {
  count = count + 1;
}

void display_integer(int value)
{
  int d = value / 1000;
  display_number( 3, d, 0 );
  value = value - d * 1000;
  
  d = value / 100;
  display_number( 2, d, 0 );
  value = value - d * 100;
  
  d = value / 10;
  display_number( 1, d, 0 );
  value = value - d * 10;

  display_number( 0, value, 0 );
}

void display_number(byte no, byte value, byte set_dp_on)
{
  unsigned int HC_DISP = 0;
  unsigned int HC_LEDCODE;
  unsigned int HC_LEDCODE_temp = 0;
 
  if (value > 15) value = 0;
  
  HC_LEDCODE = LED_BCD[value]; 
  
  for (unsigned char i=0; i<8; ++i) {
     HC_LEDCODE_temp <<= 1;
     if (HC_LEDCODE&0x01) HC_LEDCODE_temp |= 0x01;
     HC_LEDCODE >>= 1;
  }

  if (set_dp_on)  HC_LEDCODE_temp &= 0xfe;
  HC_DISP = HC_LEDCODE_temp;
 
  switch(no) {
     case 0: HC_DISP |= 0x8000; break;
     case 1: HC_DISP |= 0x4000; break;
     case 2: HC_DISP |= 0x2000; break;
     case 3: HC_DISP |= 0x1000; break;
  }
 
  write_74HC595( HC_DISP );
}

void write_74HC595(unsigned int data_a) 
{
  digitalWrite( rck, LOW ); 
  digitalWrite( sck, LOW );

  for (byte look=0; look<16; ++look) {
    if (data_a&0x0001) digitalWrite( data, HIGH );
    else digitalWrite( data, LOW );
    
    digitalWrite( sck, HIGH );
    digitalWrite( sck, LOW );
    
    data_a >>= 1;
  }
  
  digitalWrite( rck, HIGH );
}


1-2: 라인과 67: 라인 아래는 제가 미리 준비해두었습니다.  4 bits LED 모듈을 이용하기 위한 함수들입니다.


20-22: LED 모듈의 핀 3개에 연결 할 핀들을 OUPUT으로 지정합니다.


24-25: 수은 스위치에 사용 할 핀은 INPUT으로 하고, 디바운싱을 위해 풀업 저항을 사용하기 위해 해당 핀을 HIGH로 지정합니다.


29- 35: 소리로 게임 시작을 알립니다.


37: 수은 스위치가 off 상태가 되면 on_rising 함수가 인터럽트에 의해서 실행됩니다.  0번 인터럽트를 사용하고 있기 때문에 핀은 2번 핀을 사용하게 됩니다.  수은 스위치의 핀 번호가 2번인 이유는 이것 때문입니다.


49: 현재의 count (수은 스위치가 off 된 횟수)를 LED 모듈에 표시합니다.


53-60: 30초 이후에는 더 이상 게임이 진행되지 않도록 합니다.  인터럽트를 멈추고, 부저 소리로 게임이 끝난 것을 알립니다.




배선


[그림 4] 배선





Posted by 류종택