2005년에 MIT학생들로 이루어진 Dropout Design Team(DDT)은 뭔가 특별한 것을 시도한다. 그들은 보기에 멋지고, 만들기 쉽고, 누구나 쉽게 사용가능한 키트를 만들게 된다.
이것이 Conway 의 라이프게임을 LED로 구현한 키트다. 이 키트는 모듈로 구성된다. 모듈별 구성은 여러개가 합쳐지면서 보다 큰 형태의 LED 판넬을 만들수 있다.
이 키트는 간단한 땜질로 조립이 가능하고 쉽게 휴대할 수 있을 뿐만 아니라, 가격도 저렴하다. 각 모듈은 16개의 LED로
구성되며 4x4 의 격자모양을 가진다. 각 모듈은 마이크로프로세서 Atmega48 을 사용하고, 모듈별로 총 16개, 4x4의
LED 를 컨트롤하게 된다.
가격은 $19.99 이지만 한국에서 구입할 곳은 없다.
Atmega48 의 소스코드는 다음과 같다.
1. life.c
//Conway's Game of Rush //Grant Elliott //August 2005
unsigned char fetch_trans_data(unsigned char index) { //This scheme only requires 20 bits, but we transmit bytewise, so it uses 24 //Rather than simply transmit the entire map, we use an efficient scheme and //leave four bits available for other uses. //Also want to transmit corners first to ensure time for diagonal swapping switch(index) { case 0: //transmit N and S sides: bit0123=N, bit4567=S; return (~PORTB&0x0F)|(~PORTD&0xF0); break; case 1: //transmit edges: bit01=E, bit 23=W return ((~PORTB&0b10000000)>>7)|((~PORTD&0b00001000)>>2)| ((~PORTB&0b00010000)>>2)|((~PORTD&0b00000001)<<3); break; case 2: //bounce diagonals: bit0=NW, bit1=NE, bit2=SW, bit3=SE, bit4=EN, bit5=ES, bit6=WN, bit7=WS return ((border.ns&0b00010000)>>4)|((border.ns&0b10000000)>>6)| ((border.ns&0b00000001)<<2)|(border.ns&0b00001000)| (border.ew&0b00010000)|((border.ew&0b10000000)>>2)| ((border.ew&0b00000001)<<6)|((border.ew&0b00001000)<<4); break; default: return 0; break; } }
void rx_process(unsigned char dir, unsigned char index, unsigned char data) { switch (index) { case 0: //receive N and S borders switch (dir) { case 0: //E border.ew|=(data&0x01)<<4; border.ew|=(data&0x10)<<3; break; case 1: //N border.ns|=(data&0xF0); break; case 2: //S border.ns|=(data&0x0F); break; case 3: //W border.ew|=(data&0x08)>>3; border.ew|=(data&0x80)>>4; break; } break; case 1: switch (dir) { case 0: //E border.ew|=(data&0x0C)<<3; break; case 3: //W border.ew|=(data&0x03)<<1; break; } break; case 2: switch (dir) { case 0: //E border.nd|=(data&0x01)<<1; //NW tx -> NE rx border.nd|=(data&0x04)<<1; //SW tx -> SE rx break; case 1: //N border.nd|=(data&0x80)>>7; //Ws tx -> NW rx border.nd|=(data&0x20)>>4; //ES tx -> NE rx break; case 2: //S border.nd|=(data&0x40)>>4; //WN tx -> SW rx border.nd|=(data&0x10)>>1; //EN tx -> SE rx break; case 3: //W border.nd|=(data&0x02)>>1; //NE tx -> NW rx border.nd|=(data&0x08)>>1; //SE tx -> SW rx break; } break; } }
unsigned char read_adc(unsigned char adc_input) { ADMUX=adc_input|0x20; // Start the AD conversion ADCSRA|=0x40; // Wait for the AD conversion to complete while ((ADCSRA & 0x10)==0); ADCSRA|=0x10; return ADCH; }
volatile unsigned char rx_status; //Top nib - Last pin states measured //Bot nib - Transmissions in progress //Bit ordering - WSNE (matches PORTC ordering) volatile unsigned char rx_safety; volatile unsigned char rx_data[4]={0,0,0,0}; volatile signed char rx_bit[4]={0,0,0,0};//the location of the next bit to load volatile void (*rx_handler)(unsigned char, unsigned char, unsigned char);
void transmit(unsigned char numbytes, unsigned char (*txfunc)(unsigned char)) { unsigned char mybyte=0,data,i; PORTC&=0b11111110; //initiate a transfer by pulling low _delay_ms(TX_DELAY); //send two garbage bits to sync... this shouldn't be necessary... it's a hack PORTC|=0b0000001; _delay_ms(TX_DELAY); PORTC&=0b11111110; _delay_ms(TX_DELAY); _delay_ms(TX_DELAY); PORTC|=0b0000001; _delay_ms(TX_DELAY); for (mybyte=0;mybyte<numbytes;mybyte++) { data=txfunc(mybyte); for (i=0;i<8;i++) { PORTC=(PORTC&0b11111110)|((~data)&1); //set up for the transfer _delay_ms(TX_DELAY); PORTC^=0b00000001; //the bit transmission _delay_ms(TX_DELAY); data=data>>1; } } PORTC|=1; //pull high again to get ready for next transmission }
SIGNAL(SIG_PIN_CHANGE1) { unsigned char temp; signed char i; temp=((0x0F&(PCMSK1>>1))|(0xF0&(PCMSK1<<3))); temp&=((rx_status<<4)&((PINC<<3)^rx_status)&0xF0)|((~((PINC>>1)|rx_status))&0x0F); for (i=3;i>=0;i--) { if ((temp>>i)&1) { //Start a reception rx_status|=0x01<<i; rx_bit[i]=-2; //Disable PCINT for this side PCMSK1&=~(0b00000010<<i); //Enable and set the appropriate timer rx_safety|=0x01<<i; switch(i) { case 0: OCR0A=RX_DELAY+TCNT0;//E TIMSK0|=0b00000010; break; case 1: OCR0B=RX_DELAY+TCNT0;//N TIMSK0|=0b00000100; break; case 2: OCR2A=RX_DELAY+TCNT2;//S TIMSK2|=0b00000010; break; case 3: OCR2B=RX_DELAY+TCNT2;//W TIMSK2|=0b00000100; break; } } else if ((temp>>4>>i)&1) { //Receive a bit if (rx_bit[i]>=0) //ignore magic sync bits {
rx_data[i]=(rx_data[i]&~(0x01<<(rx_bit[i]&0x07)))|(((PINC>>1>>i)&1)<<(rx_bit[i]&0x07)); if ((rx_bit[i]&0x07)==0x07) { rx_handler(i,(rx_bit[i]&0x7F)>>3,rx_data[i]); } } rx_bit[i]++; //Disable PCINT for this side PCMSK1&=~(0b00000010<<i); //set the appropriate timer rx_safety|=0x01<<i; switch(i) { case 0: OCR0A=RX_DELAY+TCNT0;//E break; case 1: OCR0B=RX_DELAY+TCNT0;//N break; case 2: OCR2A=RX_DELAY+TCNT2;//S break; case 3: OCR2B=RX_DELAY+TCNT2;//W break; } } } }
SIGNAL(SIG_OUTPUT_COMPARE0A)//E { if (rx_safety&0b00000001) { //record curent state rx_status=(rx_status&0b11101111)|((PINC<<3)&0b00010000); //enable the PCINT PCMSK1|=0b00000010; //setup the safety timout rx_safety&=0b11111110; OCR0A=RX_TIMEOUT+TCNT0; } else { //disable the timer TIMSK0&=0b11111101; //clear the flag rx_status&=0b11111110; //process the data we got //rx_handler(0,(rx_bit[0]&0x7F)>>3,rx_data[0]); //enable the PCINT PCMSK1|=0b00000010; } }
SIGNAL(SIG_OUTPUT_COMPARE0B)//N { if (rx_safety&0b00000010) { //record curent state rx_status=(rx_status&0b11011111)|((PINC<<3)&0b00100000); //enable the PCINT PCMSK1|=0b00000100; //setup the safety timout rx_safety&=0b11111101; OCR0B=RX_TIMEOUT+TCNT0; } else { //disable the timer TIMSK0&=0b11111011; //clear the flag rx_status&=0b11111101; //process the data we got //rx_handler(1,(rx_bit[1]&0x7F)>>3,rx_data[1]); //enable the PCINT PCMSK1|=0b00000100; } }
SIGNAL(SIG_OUTPUT_COMPARE2A)//S { if (rx_safety&0b00000100) { //record curent state rx_status=(rx_status&0b10111111)|((PINC<<3)&0b01000000); //enable the PCINT PCMSK1|=0b00001000; //setup the safety timout rx_safety&=0b11111011; OCR2A=RX_TIMEOUT+TCNT2; } else { //disable the timer TIMSK2&=0b11111101; //clear the flag rx_status&=0b11111011; //process the data we got //rx_handler(2,(rx_bit[2]&0x7F)>>3,rx_data[2]); //enable the PCINT PCMSK1|=0b00001000; } }
SIGNAL(SIG_OUTPUT_COMPARE2B)//W { if (rx_safety&0b00001000) { //record curent state rx_status=(rx_status&0b01111111)|((PINC<<3)&0b10000000); //enable the PCINT PCMSK1|=0b00010000; //setup the safety timout rx_safety&=0b11110111; OCR2B=RX_TIMEOUT+TCNT2; } else { //disable the timer TIMSK2&=0b11111011; //clear the flag rx_status&=0b11110111; //process the data we got //rx_handler(3,(rx_bit[3]&0x7F)>>3,rx_data[3]); //enable the PCINT PCMSK1|=0b00010000; } }
3. lifecomm.h
//Conway's Game of Rush //Communication Library //Grant Elliott //August 2005