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
#define F_CPU 8000000UL
#include <stdio.h>
#include <avr/io.h>
#include <avr/delay.h>
#include "lifecomm.c"
struct RULE {unsigned char config; unsigned char live; unsigned char born;};
struct OUTSIDE {unsigned char ns; unsigned char ew; unsigned char nd;};
void init();
void set_random();
void evolve();
struct RULE choose_rules();
unsigned char read_adc(unsigned char adc_input);
unsigned char fetch_trans_data(unsigned char index);
void rx_process(unsigned char dir, unsigned char index, unsigned char data);
void reset_border();
const struct RULE rule_set={0b00000000,0b00000110,0b00000100};
volatile struct OUTSIDE border;
int main()
{
unsigned long int a;
init();
set_random();
while(1)
{
reset_border();
for (a=0;a<40000;a++)
{
if (inprogress())
break;
}
transmit(3,&fetch_trans_data);
while (inprogress()) {}
evolve();
}
return 0;
}
void reset_border()
{
border.ns=0;
border.ew=0;
border.nd=0;
}
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;
}
}
void init()
{
PORTB=0xFF;
DDRB=0xFF;
PORTD=0xFF;
DDRD=0xFF;
DDRC&=0b10011111;
PORTC|=0b01100000;
DIDR0=0x00;
ADMUX=0x20;
ADCSRA=0x86;
init_rx(&rx_process);
}
void set_random()
{
unsigned char i,t1=0,t2=0;
for (i=0;i<8;i++)
{
t1|=(read_adc(7)&0b00000001)<<i;
_delay_ms(2);
t2|=(read_adc(7)&0b00000001)<<i;
_delay_ms(2);
}
PORTB=t1;
PORTD=t2;
}
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;
}
void evolve()
{
unsigned char i,j,cnt,newstate,map[6],temp[2]={0,0};
map[0]= (0b00100000&(border.nd<<4)) | //NE (bit1)
(0b00011110&(border.ns>>3)) |
(0b00000001&(border.nd)); //NW (bit0)
map[1]= (0b00100000&(border.ew<<1)) |
(0b00011110&(~PORTB<<1)) |
(0b00000001&(border.ew));
map[2]= (0b00100000&(border.ew)) |
(0b00011110&(~PORTB>>3)) |
(0b00000001&(border.ew>>1));
map[3]= (0b00100000&(border.ew>>1)) |
(0b00011110&(~PORTD<<1)) |
(0b00000001&(border.ew>>2));
map[4]= (0b00100000&(border.ew>>2)) |
(0b00011110&(~PORTD>>3)) |
(0b00000001&(border.ew>>3));
map[5]= (0b00100000&(border.nd<<2)) | //SE (bit3)
(0b00011110&(border.ns<<1)) |
(0b00000001&(border.nd>>2)); //SW (bit2)
for (i=0;i<4;i++)
{
for (j=0;j<4;j++)
{
cnt=((map[i]>>j)&1)+((map[i]>>j>>1)&1)+((map[i]>>j>>2)&1)+
((map[i+1]>>j)&1)+((map[i+1]>>j>>2)&1)+
((map[i+2]>>j)&1)+((map[i+2]>>j>>1)&1)+((map[i+2]>>j>>2)&1);
if ((map[i+1]>>j>>1)&1)
{
//cell currently alive
if (cnt==0)
newstate=1&rule_set.config;
else
newstate=1&(rule_set.live>>(cnt-1));
} else {
//cell currently dead
newstate=1&(rule_set.born>>(cnt-1));
}
temp[i>>1]|=newstate<<(j|((i&1)<<2));
}
}
PORTB=~temp[0];
PORTD=~temp[1];
}
//Grant Elliott
//August 2005
#define F_CPU 8000000UL
#include <stdio.h>
#include <avr/io.h>
#include <avr/delay.h>
#include "lifecomm.c"
struct RULE {unsigned char config; unsigned char live; unsigned char born;};
struct OUTSIDE {unsigned char ns; unsigned char ew; unsigned char nd;};
void init();
void set_random();
void evolve();
struct RULE choose_rules();
unsigned char read_adc(unsigned char adc_input);
unsigned char fetch_trans_data(unsigned char index);
void rx_process(unsigned char dir, unsigned char index, unsigned char data);
void reset_border();
const struct RULE rule_set={0b00000000,0b00000110,0b00000100};
volatile struct OUTSIDE border;
int main()
{
unsigned long int a;
init();
set_random();
while(1)
{
reset_border();
for (a=0;a<40000;a++)
{
if (inprogress())
break;
}
transmit(3,&fetch_trans_data);
while (inprogress()) {}
evolve();
}
return 0;
}
void reset_border()
{
border.ns=0;
border.ew=0;
border.nd=0;
}
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;
}
}
void init()
{
PORTB=0xFF;
DDRB=0xFF;
PORTD=0xFF;
DDRD=0xFF;
DDRC&=0b10011111;
PORTC|=0b01100000;
DIDR0=0x00;
ADMUX=0x20;
ADCSRA=0x86;
init_rx(&rx_process);
}
void set_random()
{
unsigned char i,t1=0,t2=0;
for (i=0;i<8;i++)
{
t1|=(read_adc(7)&0b00000001)<<i;
_delay_ms(2);
t2|=(read_adc(7)&0b00000001)<<i;
_delay_ms(2);
}
PORTB=t1;
PORTD=t2;
}
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;
}
void evolve()
{
unsigned char i,j,cnt,newstate,map[6],temp[2]={0,0};
map[0]= (0b00100000&(border.nd<<4)) | //NE (bit1)
(0b00011110&(border.ns>>3)) |
(0b00000001&(border.nd)); //NW (bit0)
map[1]= (0b00100000&(border.ew<<1)) |
(0b00011110&(~PORTB<<1)) |
(0b00000001&(border.ew));
map[2]= (0b00100000&(border.ew)) |
(0b00011110&(~PORTB>>3)) |
(0b00000001&(border.ew>>1));
map[3]= (0b00100000&(border.ew>>1)) |
(0b00011110&(~PORTD<<1)) |
(0b00000001&(border.ew>>2));
map[4]= (0b00100000&(border.ew>>2)) |
(0b00011110&(~PORTD>>3)) |
(0b00000001&(border.ew>>3));
map[5]= (0b00100000&(border.nd<<2)) | //SE (bit3)
(0b00011110&(border.ns<<1)) |
(0b00000001&(border.nd>>2)); //SW (bit2)
for (i=0;i<4;i++)
{
for (j=0;j<4;j++)
{
cnt=((map[i]>>j)&1)+((map[i]>>j>>1)&1)+((map[i]>>j>>2)&1)+
((map[i+1]>>j)&1)+((map[i+1]>>j>>2)&1)+
((map[i+2]>>j)&1)+((map[i+2]>>j>>1)&1)+((map[i+2]>>j>>2)&1);
if ((map[i+1]>>j>>1)&1)
{
//cell currently alive
if (cnt==0)
newstate=1&rule_set.config;
else
newstate=1&(rule_set.live>>(cnt-1));
} else {
//cell currently dead
newstate=1&(rule_set.born>>(cnt-1));
}
temp[i>>1]|=newstate<<(j|((i&1)<<2));
}
}
PORTB=~temp[0];
PORTD=~temp[1];
}
2. lifecomm.c
//Conway's Game of Rush
//Communication Library
//Grant Elliott
//August 2005
#include <stdio.h>
#include <avr/io.h>
#include <avr/delay.h>
#include <avr/signal.h>
#include <avr/interrupt.h>
#include "lifecomm.h"
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
}
volatile unsigned char inprogress()
{
return rx_status&0x0F;
}
void init_rx(void (*func)(unsigned char, unsigned char, unsigned char))
{
DDRC|=0b00000001; //C0 is broadcast
DDRC&=0b11100001; //C1-C4 are receive
PORTC|=0b00011111; //pullups enabled, broadcast line high
PCICR=0x02;
PCMSK1=0x1E;
TIMSK0=0x00;
TCCR0A=0x00;
TCCR0B=0x03;
TCNT0=0x00;
OCR0A=0x00;
OCR0B=0x00;
ASSR=0x00;
TIMSK2=0x00;
TCCR2A=0x00;
TCCR2B=0x04;
TCNT2=0x00;
OCR2A=0x00;
OCR2B=0x00;
sei();
rx_status=0xF0;
rx_safety=0x00;
rx_handler=func;
}
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;
}
}
//Communication Library
//Grant Elliott
//August 2005
#include <stdio.h>
#include <avr/io.h>
#include <avr/delay.h>
#include <avr/signal.h>
#include <avr/interrupt.h>
#include "lifecomm.h"
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
}
volatile unsigned char inprogress()
{
return rx_status&0x0F;
}
void init_rx(void (*func)(unsigned char, unsigned char, unsigned char))
{
DDRC|=0b00000001; //C0 is broadcast
DDRC&=0b11100001; //C1-C4 are receive
PORTC|=0b00011111; //pullups enabled, broadcast line high
PCICR=0x02;
PCMSK1=0x1E;
TIMSK0=0x00;
TCCR0A=0x00;
TCCR0B=0x03;
TCNT0=0x00;
OCR0A=0x00;
OCR0B=0x00;
ASSR=0x00;
TIMSK2=0x00;
TCCR2A=0x00;
TCCR2B=0x04;
TCNT2=0x00;
OCR2A=0x00;
OCR2B=0x00;
sei();
rx_status=0xF0;
rx_safety=0x00;
rx_handler=func;
}
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
#define RX_DELAY 200 //cycles (1/125kHz)
#define RX_TIMEOUT 250 //cycles (1/125kHz)
#define TX_DELAY 1 //ms
void transmit(unsigned char numbytes, unsigned char (*func)(unsigned char));
void init_rx(void (*func)(unsigned char, unsigned char, unsigned char));
volatile unsigned char in_progress();
//Communication Library
//Grant Elliott
//August 2005
#define RX_DELAY 200 //cycles (1/125kHz)
#define RX_TIMEOUT 250 //cycles (1/125kHz)
#define TX_DELAY 1 //ms
void transmit(unsigned char numbytes, unsigned char (*func)(unsigned char));
void init_rx(void (*func)(unsigned char, unsigned char, unsigned char));
volatile unsigned char in_progress();
이전글 - 라이프게임을 하드웨어로 구현하다.
[VIA:MAKE]