/* copyright dan goldwater - dgold@zblob.com 20040410 version 8j: completed exponential dna replication nano8 test code - toggles power magnets - comm coils alternate tx and rx, leds light when another unit is detected Target : atmega8 CKSEL fuses set for 8.000Mhz */ #pragma data:eeprom // eeprom initialization // pre-load the eeprom if desired: // 1st byte: algorithm // 2nd byte: unittype // 3rd byte, etc: not used char table[] = {2, 1, 0, 0}; #pragma data:data // source code #include #include #define setbit(x,y) (x |= (1 << (y))) #define clearbit(x,y) (x &= ~(1 << (y))) #define checkbit(x,y) (x & (1 << (y))) #define checkbit2(x,y) ((x >> (y)) & 1) #define mod4(a) ((a) & 0x03) #define NUMLEDS 5 #define def // set '0' for green plastic base, '1' for yellow plastic base #define UNITTYPE_GREEN 0 #define UNITTYPE_YELLOW 1 #define UNITTYPE (*(volatile unsigned char *)0x29) // unsigned char UNITTYPE = 0; // addresses for data stored in eeprom #define EEPROMADDR_ALGORITHM 1 #define EEPROMADDR_UNITTYPE 2 // header bit positions in the 2-byte rxdata structure // note that raw transmit and receive positions are different #define HEADER_BIT 15 #define GENDER_BIT 14 #define UNITTYPE_BIT 13 #define PROGRAMCHANGE_BIT 12 #define RXUNITTYPE(a) checkbit2(ad->rxdata[a], UNITTYPE_BIT) // set 1 to make a programming unit which instructs other units to switch // their algorithm. set at most one of these. #define PROGRAMMER1 0 #define PROGRAMMER2 0 // set to 1 to create a crystal algorithm 'seed' unit #define CRYSTALSEED 0 // we only know if a unit has disconnected when we do not receive any // data from it for a long time. this is how many seconds. // also: how long to wait to see 2 sequential rx values #define DISCONNECT_TIME 48 // seconds to keep magnet latch on #define MAGNET_ON_TIME 8 // how long user has to assemble DNA after power-up #define DNA_ASSEMBLY_TIME 48 // string length for fixed-length string algorithm #define MAXSTRLEN 5 // minimum number of clocks between pulse trains // maximum length for a pulse is 80 #define PULSE_TRAIN_TIME_SEP 130 struct _ALGODATA { unsigned short rxdata[4]; // most recently received data on each side unsigned char txdata[4]; // data to send for each comm coil unsigned short magneton[4]; // time when magnets are turned on unsigned short state[4]; // additional state data }; typedef struct _ALGODATA ALGODATA; // function pointer for algorithms typedef void (*algoptr)(ALGODATA *); // hack for switching programs - 'timed breakup' and 'virus' features //#define HACK1 //#define DEBUG_ADC //#define DEBUG_NORX //#define DEBUG_NOTX //#define DEBUG_RX //#define DEBUG_TX //#define DEBUG_FLIPPER /* raw received ADC samples from the 4 comm coils. for a valid pulse train we use up to about 45 sense array elements */ unsigned char sense[64][4]; unsigned char sense_index[4]; unsigned short last_sense[4]; // random data from the ADC #define adcrand (*(volatile unsigned char *)0x2C) // pack data for transmit. a and b are unsigned chars //#define HEADER(a) ((unsigned char)(a >> 12)) #define DATA(a) (a & 0x0FFF) // use unused registers for fast global variable. 16-bits total. // counts at 4kHz, so we overflow after 16 seconds #define timer0 (*(volatile unsigned short *)0x48) // count seconds #define epoch (*(volatile unsigned short *)0x4A) //unsigned short epoch = 0; #ifdef notdef #define TX_PULSES2 25 #define TX_PULSES 1 #define TX_PULSE_LEN 25 #define TX_PULSE_LEN2 34 /* send comm pulses: energize comm coil then de-energize. the strange timing here is tweaked to produce as large a positive duty cycle as possible (65-70%) */ void comm2(unsigned char ids) { unsigned short i, j, k; ids &= 0x0F; for(k=0; k> 2); PORTD = ((PORTD & 0xF0) | (~rotids & 0x0F)); for(i=0; i<4; i++) { led(i, 0, checkbit(ids, i)); } } // interrupts must be disabled when reading/writing eeprom void eeprom_write(unsigned int addr, unsigned char data) { while(checkbit(EECR, EEWE)) {} // wait for completion of previous write EEAR = addr; EEDR = data; setbit(EECR, EEMWE); setbit(EECR, EEWE); } unsigned char eeprom_read(unsigned int addr) { while(checkbit(EECR, EEWE)) {} // wait for completion of previous write EEAR = addr; setbit(EECR, EERE); return EEDR; } #pragma interrupt_handler timer0_ovf_isr:10 void timer0_ovf_isr(void) { //TCNT0 = 0x00; //reload counter value timer0++; } void rx_reset(void) { unsigned char i, j; for(j=0; j<4; j++) { last_sense[j] = 0; sense_index[j] = 0; for(i=0; i<64; i++) { sense[i][j] = 255; } } } /* sample the four comm coils in rotation as fast as possible. if a signal is detected, store the time since the last signal. with pulse length of about 2.8ms we get 7 ADC samples per received pulse. at a 65% pulse duty cycle, we will miss about 1 in 1500 pulses. transmitted pulse trains are 20-40 pulses long, so overall we should get at least 97% of pulse trains received successfully. */ #pragma interrupt_handler adc_isr:15 void adc_isr(void) { unsigned short x, value; unsigned char i, n; //conversion complete, read value (int) using... value = ADCL; //Read 8 low bits first (important) value |= (int)ADCH << 8; //read 2 high bits and shift into top byte n = (ADMUX + 3) & 0x03; // the coil whose adc result we just got ADMUX = (ADMUX + 1) & 0xF3; // cycle between the four comm coils // with good magnet alignment we get a 50mV signal on the receiving coil // at 10-12mm distance with a symmetrical transmitter output. // with non-symmetrical transmitter output the positive side signal level // is reduced while duty cycle is increased. // with current transmit code we see 50mV at 5-6mm. adcrand += (unsigned char)value; //if(n != 2) return; // DEBUG #ifdef DEBUG_ADC magnet(3, 1); #endif if(value > 25) { // 20 is a 50mV signal #ifdef DEBUG_ADC magnet(0, 1); #endif x = timer0 - last_sense[n]; // XXX - timer0 wraps around if(x > 16) { // new pulse found, record time since last pulse #ifdef DEBUG_ADC magnet(1, 1); #endif last_sense[n] = timer0; if(x > PULSE_TRAIN_TIME_SEP) { // new pulse train found, clear out old one #ifdef DEBUG_ADC magnet(2, 1); #endif sense_index[n] = 0; sense[0][n] = 255; } else { // new pulse in existing pulse train sense[sense_index[n]][n] = x; // move end-of-data pointer, prevent going past end of array sense_index[n] = (sense_index[n] + 1) & 0x3F; sense[sense_index[n]][n] = 255; // end-of-data marker } } } #ifdef DEBUG_ADC magnet(2, 0); magnet(1, 0); magnet(0, 0); magnet(3, 0); #endif } //call this routine to initialise all peripherals void init_devices(void) { //stop errant interrupts until set up CLI(); //disable all interrupts // port init PORTB = 0x3C; DDRB = 0xC3; PORTC = 0x40; //m103 output only DDRC = 0x30; PORTD = 0x0F; DDRD = 0xFF; // timer0 init // prescale: 8, rate: about 4khz TCCR0 = 0x00; //stop TCNT0 = 0x00; //set count timer0 = 0; TCCR0 = 0x02; //start timer // adc init ADCSR = 0x00; //disable adc rx_reset(); ADMUX = 0xC0; //select adc input 0 ACSR = 0x80; // free-running ADC // would like to do 52us conversion, but adc_isr() is not fast enough ADCSR = 0xEE; // /0xED: 52us conversion, 0xEE: 104us conversion // general init MCUCR = 0x00; GICR = 0x00; TIMSK = 0x01; //timer interrupt sources SEI(); //re-enable interrupts //all peripherals are now initialised } // blink a single led with specified blink rate and number of blinks // maximum value for speed is 65 void blinkled(unsigned char ledid, unsigned char color, unsigned char blinks, unsigned char speed) { unsigned char i; unsigned short j, k = 0; led(ledid, color, 0); if(blinks == 0) { // pause for(j=0; j<(speed<<10); j++) {k++;} return; } for(i=0; irxdata[a] & 0x000F) // get rid of header //#define TXSTATE(a) (ad->txdata[a] & 0x000F) // get rid of header #define SIDECCW(a) (mod4(a+1)) #define SIDEOPP(a) (mod4(a+2)) #define SIDECW(a) (mod4(a+3)) // we reference the 4 sides of a unit based on which side is 'down' (SIDE0). // the 'down' side is determined when a loose unit is connected. #define SIDE0 (ad->state[1]) #define SIDE1 (SIDECCW(SIDE0)) #define SIDE2 (SIDEOPP(SIDE0)) #define SIDE3 (SIDECW(SIDE0)) void set_txdata(unsigned char a, unsigned char b, unsigned char c, unsigned char d, ALGODATA *ad) { ad->txdata[SIDE0] = a; ad->txdata[SIDE1] = b; ad->txdata[SIDE2] = c; ad->txdata[SIDE3] = d; } void disconnect_sides(unsigned char sides, ALGODATA *ad) { unsigned char i, side; for(i=0; i<4; i++) { side = mod4((ad->state[1]) + i); if(ad->rxdata[side] && (checkbit(sides, i))) { ad->magneton[side] = epoch; } } /* if(ad->rxdata[SIDE0] && (sides & 0x1)) ad->magneton[SIDE0] = epoch; if(ad->rxdata[SIDE1] && (sides & 0x2)) ad->magneton[SIDE1] = epoch; if(ad->rxdata[SIDE2] && (sides & 0x4)) ad->magneton[SIDE2] = epoch; if(ad->rxdata[SIDE3] && (sides & 0x8)) ad->magneton[SIDE3] = epoch; */ } void algo_dna_exp(ALGODATA *ad); void dna_change_state(unsigned char newstate, ALGODATA *ad) { ad->state[0] = newstate; ad->state[3] = epoch; // time of state change algo_dna_exp(ad); } /* signals sent between dna/rna units */ // 1: attach to any binding dna side (signal 4, 5, 6) // 2: dna/rna attach (side 1) // 3: dna/rna attach (side 3) // 4: accept rna attaching (from dna start unit) // 5: accept rna attaching (from dna middle unit) // 6: accept rna attaching (from dna end unit) // 7: signal adjacent dna to accept connections void dna_state1(ALGODATA *ad) { unsigned char side0, side1, side2, side3; side0 = SIDE0; side1 = SIDE1; side2 = SIDE2; side3 = SIDE3; if(ad->state[0] == 100) { // dna starting unit with rna binding site active // state change logic if(RXSTATE(side0) == 1 && RXUNITTYPE(side0) == UNITTYPE) { dna_change_state(101, ad); return; } if(RXSTATE(side1) != 3) { // undesired dna breakup dna_change_state(130, ad); return; } // disconnect anything except rxstate 3 on face 1 disconnect_sides(1|4|8, ad); // set face states set_txdata(4, 2, 0, 0, ad); } else if(ad->state[0] == 101) { // dna starting unit with rna attached if(RXSTATE(side0) != 1 || RXUNITTYPE(side0) != UNITTYPE) { dna_change_state(100, ad); return; } if(RXSTATE(side1) != 3) { // undesired dna breakup dna_change_state(130, ad); return; } disconnect_sides(4|8, ad); set_txdata(4, 7, 0, 0, ad); } else if(ad->state[0] == 110) { // dna middle unit if(RXSTATE(side3) == 7) { dna_change_state(111, ad); return; } if(RXSTATE(side1) != 3 || RXSTATE(side3) != 2) { // undesired dna breakup dna_change_state(130, ad); return; } disconnect_sides(1|4, ad); set_txdata(0, 2, 0, 3, ad); } else if(ad->state[0] == 111) { // dna middle unit with rna binding site active if(RXSTATE(side0) == 1 && RXUNITTYPE(side0) == UNITTYPE) { dna_change_state(112, ad); return; } if(RXSTATE(side1) != 3 || (RXSTATE(side3) != 2 && RXSTATE(side3) != 7)) { // undesired dna breakup dna_change_state(130, ad); return; } disconnect_sides(1|4, ad); set_txdata(5, 2, 0, 3, ad); } else if(ad->state[0] == 112) { // dna middle unit with rna attached if(RXSTATE(side0) != 1 || RXUNITTYPE(side0) != UNITTYPE) { dna_change_state(110, ad); return; } if(RXSTATE(side1) != 3 || (RXSTATE(side3) != 2 && RXSTATE(side3) != 7)) { // undesired dna breakup dna_change_state(130, ad); return; } disconnect_sides(4, ad); set_txdata(5, 7, 0, 3, ad); } else if(ad->state[0] == 1) { // setup-phase: manual dna assembly // we don't try to detect invalid dna setup. // all the original dna units must be lined up manually with // circuit-board side 0 on either the bottom or the right side. unsigned char i; blinkled(4, 1, 6, 20); i = 0; ad->state[0] = 130; ad->state[1] = 0; // warning: this invalidates side0..3 if(RXSTATE(0) == 3) { // circuit side 0 on the right i++; ad->state[0] = 100; ad->state[1] = 3; } else if(RXSTATE(1) == 3) { // circuit side 0 on the bottom i++; ad->state[0] = 100; // ad->state[1] = 0; } if(RXSTATE(2) == 2) { i++; ad->state[0] = 120; ad->state[1] = 3; } else if(RXSTATE(3) == 2) { i++; ad->state[0] = 120; // ad->state[1] = 0; } if(i > 1) { ad->state[0] = 110; } // i==2 is a middle dna unit, i==1 is an end unit, i==0 is a loose rna ad->state[3] = epoch; algo_dna_exp(ad); } else { // remaining states are illegal led(4, 1, 1); } } void dna_state2(ALGODATA *ad) { unsigned char side0, side1, side2, side3; side0 = SIDE0; side1 = SIDE1; side2 = SIDE2; side3 = SIDE3; if(ad->state[0] == 120) { // dna end unit if(RXSTATE(side3) == 7) { dna_change_state(121, ad); return; } if(RXSTATE(side3) != 2) { // undesired dna breakup dna_change_state(130, ad); return; } disconnect_sides(1|2|4, ad); set_txdata(0, 0, 0, 3, ad); } else if(ad->state[0] == 121) { // dna end unit with rna binding activated or rna attached if(RXSTATE(side0) == 1 && RXUNITTYPE(side0) == UNITTYPE) { dna_change_state(122, ad); return; } if(RXSTATE(side3) != 2 && RXSTATE(side3) != 7) { // undesired dna breakup dna_change_state(130, ad); return; } disconnect_sides(1|2|4, ad); set_txdata(6, 0, 0, 3, ad); } else if(ad->state[0] == 122) { // dna end unit with rna binding activated or rna attached if(RXSTATE(side0) != 1 || RXUNITTYPE(side0) != UNITTYPE) { dna_change_state(120, ad); return; } if(RXSTATE(side3) != 2 && RXSTATE(side3) != 7) { // undesired dna breakup dna_change_state(130, ad); return; } disconnect_sides(2|4, ad); set_txdata(6, 0, 0, 3, ad); } else if(ad->state[0] == 130) { // free rna unit unsigned char i; for(i=0; i<4; i++) { if(RXUNITTYPE(i) == UNITTYPE) { if(RXSTATE(i) == 4) ad->state[0] = 140; // rna start if(RXSTATE(i) == 5) ad->state[0] = 131; // rna middle if(RXSTATE(i) == 6) ad->state[0] = 150; // rna end if(ad->state[0] != 130) { ad->state[1] = SIDEOPP(i); // set side0 ad->state[3] = epoch; algo_dna_exp(ad); return; } } } disconnect_sides(1|2|4|8, ad); for(i=0; i<4; i++) { ad->txdata[i] = 1; // attach to any binding dna } } else if(ad->state[0] == 131) { // rna middle unit connected to dna middle unit if(RXSTATE(side1) == 3 && (RXSTATE(side3) == 2 || RXSTATE(side3) == 7)) { dna_change_state(110, ad); return; } if((RXSTATE(side2) != 5) || (ad->rxdata[side3] && (RXSTATE(side3) != 2 && RXSTATE(side3) != 7))) { // undesired breakoff from dna, or bad rna connection dna_change_state(130, ad); return; } disconnect_sides(1, ad); if(ad->rxdata[side1] && RXSTATE(side1) != 3) ad->magneton[side1] = epoch; set_txdata(0, 2, 1, 3, ad); } else if(ad->state[0] == 140) { // rna start unit connected to dna start unit if(RXSTATE(side1) == 3) { dna_change_state(100, ad); return; } if(RXSTATE(side2) != 4) { // undesired breakoff from dna dna_change_state(130, ad); return; } disconnect_sides(1|8, ad); if(ad->rxdata[side1] && RXSTATE(side1) != 3) ad->magneton[side1] = epoch; set_txdata(0, 2, 1, 0, ad); } else if(ad->state[0] == 150) { // rna end unit connected to dna end unit if((RXSTATE(side3) == 2 || RXSTATE(side3) == 7) && ((ad->state[3] + MAGNET_ON_TIME + 6 < epoch))) { // avoid race condition where the 131 unit on side3 thinks we are still // a 130 unit but we have already gone from 130 to 150 to 120 and detached. dna_change_state(120, ad); return; } if((RXSTATE(side2) != 6) || (ad->rxdata[side3] && (RXSTATE(side3) != 2 && RXSTATE(side3) != 7))) { // undesired breakoff from dna, or bad rna connection dna_change_state(130, ad); return; } disconnect_sides(1|2, ad); set_txdata(0, 0, 1, 3, ad); } else { // remaining states are illegal led(4, 1, 1); } } void algo_dna_exp(ALGODATA *ad) { unsigned char i; //blinkled(4, 1, 7, 12); //return; // #ifdef notdef if(epoch < (DNA_ASSEMBLY_TIME+DISCONNECT_TIME)) { // line assembly time ad->state[0] = 1; ad->state[1] = 0; set_txdata(2, 2, 3, 3, ad); return; } // check for invalid received data for(i=0; i<4; i++) { if(RXSTATE(i) > 7) { blinkled(4, 1, i+1, 20); } } if(ad->state[0] < 120) { dna_state1(ad); } else { dna_state2(ad); } // #endif } // crystal growth. start with yellow seed unit, grow green crystal around it. void algo_crystal_checker(ALGODATA *ad) { unsigned char i, x1, x2, x3, flag; unsigned char bind1 = 10; unsigned char bind2 = 10; #if CRYSTALSEED // seed units ad->txdata[0] = 8; if(ad->rxdata[0] && RXUNITTYPE(0) == UNITTYPE) { ad->magneton[0] = epoch; // correct color binding site } for(i=1; i<4; i++) { ad->txdata[i] = 1; // part of grown crystal // disconnect units which are not part of crystal if(ad->rxdata[i] && !DATA(ad->rxdata[i])) ad->magneton[i] = epoch; } #else // non-seed units // find out if we are part of a crystal, and if we were previously or not for(i=0; i<4; i++) { if(ad->txdata[i] == 3) { // previous binding site bind1 = i; } if(DATA(ad->rxdata[i]) == 8) { // connected to a binding site if(RXUNITTYPE(i) != UNITTYPE) { // correct color binding site bind2 = i; } } } if(bind1 == 10 && bind2 == 10) { // no binding site connection, disconnect any connected units for(i=0; i<4; i++) { ad->txdata[i] = 0; if(ad->rxdata[i]) ad->magneton[i] = epoch; } return; } // connected to a binding site // if(bind1 != 10 && bind2 == 10) led(4, 0, 0); // error detect if(bind1 == 10) { bind1 = bind2; ad->txdata[bind1] = 3; // tell corresponding binding site it has been bound } // our binding site is the first side clockwise which is not // connected to existing crystal, or is the previously found binding site x1 = SIDECCW(bind1); x2 = SIDEOPP(bind1); x3 = SIDECW(bind1); if((DATA(ad->rxdata[x1]) == 3) || (DATA(ad->rxdata[x1]) == 0)) { ad->txdata[x1] = 8; ad->txdata[x2] = 1; ad->txdata[x3] = 1; if(ad->rxdata[x2] && !DATA(ad->rxdata[x2])) ad->magneton[x2] = epoch; // disconnect non-crystal units } else { ad->txdata[x1] = 1; ad->txdata[x2] = 8; ad->txdata[x3] = 1; //if((DATA(ad->rxdata[x2]) != 3) && (DATA(ad->rxdata[x2]) != 0)) { // led(4, 1, 1); // error //} } if(ad->rxdata[x3] && !DATA(ad->rxdata[x3])) ad->magneton[x3] = epoch; // disconnect non-crystal units for(i=0; i<4; i++) { // wrong color if(ad->rxdata[i] && RXUNITTYPE(i) == UNITTYPE) { ad->magneton[i] = epoch; } } #endif } // crystal growth. start with yellow seed unit, grow green crystal around it. void algo_crystal(ALGODATA *ad) { unsigned char i, x1, x2, x3, flag; unsigned char bind1 = 10; unsigned char bind2 = 10; #if CRYSTALSEED // seed units ad->txdata[0] = 8; for(i=1; i<4; i++) { ad->txdata[i] = 1; // part of grown crystal // disconnect units which are not part of crystal if(ad->rxdata[i] && !DATA(ad->rxdata[i])) ad->magneton[i] = epoch; } #else // non-seed units // find out if we are part of a crystal, and if we were previously or not for(i=0; i<4; i++) { if(ad->txdata[i] == 3) { // previous binding site bind1 = i; } if(DATA(ad->rxdata[i]) == 8) { // connected to a binding site bind2 = i; } } if(bind1 == 10 && bind2 == 10) { // no binding site connection, disconnect any connected units for(i=0; i<4; i++) { ad->txdata[i] = 0; if(ad->rxdata[i]) ad->magneton[i] = epoch; } return; } // connected to a binding site // if(bind1 != 10 && bind2 == 10) led(4, 0, 0); // error detect if(bind1 == 10) { bind1 = bind2; ad->txdata[bind1] = 3; // tell corresponding binding site it has been bound } // our binding site is the first side clockwise which is not // connected to existing crystal, or is the previously found binding site x1 = SIDECCW(bind1); x2 = SIDEOPP(bind1); x3 = SIDECW(bind1); if((DATA(ad->rxdata[x1]) == 3) || (DATA(ad->rxdata[x1]) == 0)) { ad->txdata[x1] = 8; ad->txdata[x2] = 1; ad->txdata[x3] = 1; if(ad->rxdata[x2] && !DATA(ad->rxdata[x2])) ad->magneton[x2] = epoch; // disconnect non-crystal units } else { ad->txdata[x1] = 1; ad->txdata[x2] = 8; ad->txdata[x3] = 1; //if((DATA(ad->rxdata[x2]) != 3) && (DATA(ad->rxdata[x2]) != 0)) { // led(4, 1, 1); // error //} } if(ad->rxdata[x3] && !DATA(ad->rxdata[x3])) ad->magneton[x3] = epoch; // disconnect non-crystal units #endif } #ifdef notdef // virus transmission void algo_virus(ALGODATA *ad) { unsigned char i; for(i=0; i<4; i++) { ad->txdata[i] = 0xFF; ad->magneton[i] = 0; } } // permanent virus unit void algo_virus_core(ALGODATA *ad) { unsigned char i; unsigned char connected = 0; for(i=0; i<4; i++) { ad->txdata[i] = 0xFF; if(ad->rxdata[i]) { connected++; } } if(connected) { if(ad->state[2] == 0) { ad->state[2] = epoch; } if(ad->state[2] + 20 < epoch) { for(i=0; i<4; i++) { ad->txdata[i] = 0; ad->magneton[i] = epoch; } } } else { ad->state[2] = 0; } } #endif // strings of robots, no length limit. green and yellow strings. void algo_line(ALGODATA *ad) { unsigned char connected = 0; unsigned char i, j, k; for(i=0; i<4; i++) { if(ad->rxdata[i]) { if(RXUNITTYPE(i) != UNITTYPE) { // wrong type, disconnect ad->magneton[i] = epoch; ad->rxdata[i] = 0; } else if(!DATA(ad->rxdata[i]) || !ad->txdata[i]) { // one of the units want disconnect ad->magneton[i] = epoch; ad->rxdata[i] = 0; } else { // mark connected, may decide later to disconnect it setbit(connected, i); } } } // activate all sides for(i=0; i<4; i++) { ad->txdata[i] = 1; } if(connected) { // deactivate sides not in-line with connection for(i=0; i<4; i++) { if(checkbit(connected, i)) { j = SIDECCW(i); k = SIDECW(i); ad->txdata[j] = 0; ad->txdata[k] = 0; if(ad->rxdata[j]) ad->magneton[j] = epoch; if(ad->rxdata[k]) ad->magneton[k] = epoch; break; } } } } #if PROGRAMMER1 // set algorithm codes for programming other units // only used by programming unit void algo_set_algo(ALGODATA *ad) { unsigned char i; for(i=0; i<4; i++) { ad->txdata[i] = i; } } #endif #if PROGRAMMER2 // set options codes for programming other units // only used by programming unit void algo_set_opts(ALGODATA *ad) { unsigned char i; for(i=0; i<4; i++) { ad->txdata[i] = i+4; } } #endif /* check for data packet on a single comm coil. return 1 if we got data, 0 if not. if we got data, fill in rxdata */ unsigned char receive_data(unsigned char side, unsigned short *rxdata) { unsigned short rxdatabuf = 0; unsigned char parity = 0; unsigned char j = 0; unsigned short k = 0; if((sense_index[side] >= 14) && ((timer0 - last_sense[side]) > PULSE_TRAIN_TIME_SEP)) { //led(side, 1, 1); // set led // complete received pulse train found. minimum header + data + parity + term // pulses sent is 4 + 12 + 1 + 1 = 18. #ifdef DEBUG_RX //led(i, 1, 0); magnet(3, 1); #endif while(j < 60 && sense[j][side] < 255) { if(sense[j][side] < 48 && sense[j+1][side] < 48) { // got a 1 #ifdef DEBUG_RX magnet(0, 1); #endif rxdatabuf <<= 1; rxdatabuf |= 1; j += 2; parity++; #ifdef DEBUG_RX magnet(0, 0); #endif } else if(sense[j][side] >= 48 && sense[j][side] < 80) { // got a 0 #ifdef DEBUG_RX magnet(1, 1); #endif rxdatabuf <<= 1; j++; #ifdef DEBUG_RX magnet(1, 0); #endif } else { // invalid timing found, abort processing #ifdef DEBUG_RX magnet(2, 1); //led(i, 1, 1); #endif //sense_index[side] = 0; //sense[0][i] = 255; #ifdef DEBUG_RX magnet(2, 0); #endif break; } } #ifdef DEBUG_RX magnet(3, 0); #endif // clear processed pulse train sense_index[side] = 0; sense[0][side] = 255; // bits: 10gu p0dd dd0p // check data validity k = (side & 1) ? 0 : 0x0200; // gender bit of sending side if(((rxdatabuf & 0x0C42) == 0x0800) && ((rxdatabuf & 0x0200) == k)) { // valid header and intermixed null bits, got exactly 12 bits total j = (unsigned char)rxdatabuf & 0x1; // parity bit parity -= j; // remove parity bit from the parity count if((parity & 0x1) == j) { // parity is valid #ifdef DEBUG_RX //led(side, 1, 1); #endif // grab the 11 header+data bits, get rid of parity bit *rxdata = 0x8000 | ((rxdatabuf & 0x0380) << 5) | ((rxdatabuf & 0x003C) >> 2); // GOT THE DATA! // 4 header bits at positions 15-12, 4 data bits at positions 3-0 //blinkled(side, 1, DATA(*rxdata)+1, 10); // for debug unit 1 led(side, 1, 1); // set led return 1; } } } return 0; } void algorithm_init(unsigned char side, ALGODATA *ad, unsigned short *lastrx, unsigned short *rxdataraw, unsigned short *rxdatarawage, algoptr *algo) { unsigned char i; led(4, 0, 0); ADCSR = 0x00; // disable interrupts to access eeprom TCCR0 = 0x00; if(side == 10) { // startup - read from eeprom i = eeprom_read(EEPROMADDR_ALGORITHM); if(i > 3) i = 0; } else { i = DATA(ad->rxdata[side]); if(i <= 3) { // i == 0 - 3 if(i != eeprom_read(EEPROMADDR_ALGORITHM)) { eeprom_write(EEPROMADDR_ALGORITHM, i); } } else if(i <= 5) { // i == 4, 5 if(eeprom_read(EEPROMADDR_UNITTYPE) != (i - 4)) { eeprom_write(EEPROMADDR_UNITTYPE, i - 4); } } } if(i == 0) *algo = &algo_line; else if(i == 1) *algo = &algo_crystal; else if(i == 2) *algo = &algo_dna_exp; else if(i == 3) *algo = &algo_crystal_checker; #if PROGRAMMER1 i = 10; *algo = &algo_set_algo; #endif #if PROGRAMMER2 i = 11; *algo = &algo_set_opts; #endif ADCSR = 0xEE; // enable interrupts TCCR0 = 0x02; if(eeprom_read(EEPROMADDR_UNITTYPE)) {UNITTYPE = UNITTYPE_YELLOW;} else {UNITTYPE = UNITTYPE_GREEN;} blinkled(4, 0, 0, 8); if(UNITTYPE) {blinkled(4, 1, i+1, 8);} else {blinkled(4, 0, i+1, 8);} // initialize construction algorithm state for(i=0; i<4; i++) { //last_sense_last[i] = 0; ad->txdata[i] = 0; ad->rxdata[i] = 0; // high bit is always set for valid received data lastrx[i] = 0; ad->magneton[i] = 0; ad->state[i] = 0; rxdataraw[i] = 0; rxdatarawage[i] = 0; } blinkme(); led(4, 0, 1); epoch = DISCONNECT_TIME + 10; } C_task void main(void) { algoptr algorithm; unsigned short lasttimer0 = 0; unsigned short commtimer, txstart; unsigned char i, j, r, txtimer, txflag; unsigned short k; //unsigned short last_sense_last[4]; ALGODATA ad; unsigned short txdataraw[4]; // after padding and header unsigned char parity[4]; // tx parity bit calculation unsigned short lastrx[4]; // last epoch we got a message on each side (age of rxdata[i]) unsigned short rxdataraw[4]; unsigned short rxdatarawage[4]; unsigned char epochstate = 0; unsigned char commtimerstate = 0; unsigned char rbit; unsigned short rseed = 1; #ifdef DEBUG_FLIPPER unsigned char magnettimer; #endif UNITTYPE = 0; epoch = 0; init_devices(); ad.state[3] = 0; algorithm_init(10, &ad, lastrx, rxdataraw, rxdatarawage, &algorithm); //algorithm = &algo_virus_core; for(;;) { // loop forever if(timer0 == lasttimer0) { // random number generator. avoiding transmit collisions depends // on having good random numbers. // see: http://www.avrfreaks.net/Freaks/FAQ/freakfaq.php?action=1&id=16 // alternative: http://list.dprg.org/archive/1998-January/003038.html rbit = (unsigned char)((rseed>>15)&1) ^ (((unsigned char)rseed>>1)&1) ^ ((unsigned char)rseed&1); rseed = (rseed<<1) | rbit; continue; } lasttimer0 = timer0; // increment epoch every second if(timer0 & 0x0800) { // 2k to 4k if(!epochstate) { epochstate = 1; epoch++; } } else { // 0 to 2k epochstate = 0; } // we transmit once per commtimer cycle and receive whenever we are not transmitting. commtimer = timer0 & 0x1FFF; // 2 seconds if(commtimer & 0x1000) { // 4k to 8k commtimerstate = 0; } else { // 0 to 4k if(!commtimerstate) { // usually we get here with commtimer == 0 but the rx function // may run over a cycle so sometimes commtimer will be nonzero /* transmitting starts randomly in the timer cycle so that two units txing to each other will eventually not overlap their transmissions. we divide cycle into 8 (250ms each) and start at one of those 8 times. collision probability is about 0.25 (2 of the 8 choices will collide with the other transmitter). we never want a failed connection by chance, so must wait quite a lot of cycles before we can assume the two units are no longer attached. over 24 cycles (48 seconds) chance of 0 successful transmits is 1 in 2^48. chance of 0 or 1 successful transmits is 1 in 2^24. we require 2 successful transmits. our real probability is worse because of error transmissions, but we don't know how common they are. with 4 sides per unit, run time of 4 hours chance of failure is 1 in 14k per unit. with 50 units we are at 1 in 280. this assumes all 50 units are fully connected for 4 straight hours. */ //comm2(0xf); // for debug commtimerstate = 1; txstart = (0x2000 >> 3) * ((unsigned char)rseed & 0x07); txflag = 0; // add some real randomness to the random numbers rseed = (rseed<<4) | (adcrand & 0xf); //comm2(0x0); } } #ifdef DEBUG_FLIPPER magnettimer = (timer0 >> 10) & 3; // use 10-14 to slow down magnet toggling #endif #ifdef HACK1 // hacks for switching programs: if((algorithm == &algo_line) && (epoch > 1200)) { // after 20 minutes, change from line to crystal. // for 1 minute at the change, keep magnets up magnets(0x05); // only the 'real' magnets if(epoch > 1260) { ad.rxdata[0] = 1; // set program for crystals algorithm_init(0, rxdata, txdata, magneton, state, lastrx, rxdataraw, rxdatarawage, &algorithm); } continue; } if((algorithm == &algo_virus) && (epoch > (ad.state[3]+60))) { magnets(0x05); // only the 'real' magnets if(epoch > (ad.state[3] + 120)) { ad.rxdata[0] = 0; // set program for lines algorithm_init(0, rxdata, txdata, magneton, state, lastrx, rxdataraw, rxdatarawage, &algorithm); } continue; } #endif #ifdef def /******************** TRANSMIT *******************/ // 4 data bits, 4 header bits, 3 padding bits and 1 parity bit at 4 cycles each plus // termination pulse at 1 cycle = 49 txtimer cycles #define TXCYCLES 48 #define TXTIME (TXCYCLES * 16) #ifdef DEBUG_NOTX if(0) { #else if((commtimer >= txstart) && (commtimer <= txstart + TXTIME + 24)) { #endif // when not transmitting we receive. we leave a small period between transmit // and receive where we do nothing in order to start and stop ADC and reduce noise. if(!txflag) { // commtimer == txstart ADCSR = 0x00; // disable ADC // prep for running the algorithm for(i=0; i<4; i++) { if(ad.magneton[i] || ((lastrx[i] + DISCONNECT_TIME) < epoch)) { // ignore data received on a nonlatching side // also detect units that have unintentionally been disconnected and clear their data ad.rxdata[i] = 0; } if(ad.magneton[i] || ((rxdatarawage[i] + DISCONNECT_TIME) < epoch)) { rxdataraw[i] = 0; } } // algorithm must finish before commtimer == txstart + 12 algorithm(&ad); // set/reset magnets when no tx/rx is going on to prevent noise j = 0; // magnet settings bitfield for(i=0; i<4; i++) { led(i, 1, 0); // turn off red leds indicating valid received data // turn off magnets when unit has been disconnected from appropriate side // only turn on magnets if a unit is connected to the side // if magnet is set > epoch then we allow it to be on even if no connected unit if((ad.magneton[i] > epoch) || (((ad.magneton[i] + MAGNET_ON_TIME) > epoch) && ((lastrx[i] + DISCONNECT_TIME) > epoch))) { setbit(j, i); } else { ad.magneton[i] = 0; } } magnets(j); // prep the 2 byte txdataraw for sending by padding as follows: // bit 10 (MSB): always a 1 // bit 9: always 0 // bit 8: transmit side gender // bit 7: unit type // bit 6: program change command // bit 5: always 0 // bit 4,3,2,1: data bits // bit 0: always 0 for(i=0; i<4; i++) { txdataraw[i] = ((ad.txdata[i] & 0x000F) << 1); // clear header section, leave 4 data bits txdataraw[i] |= (0x0400 | ((i&1)<<8) | (UNITTYPE<<7) | ((PROGRAMMER1 | PROGRAMMER2) << 6)); parity[i] = 0; // reset parity counter } txflag = 1; } /* pulse length: 2.8ms short pulse separator: 6ms long pulse separator: 14ms about 16.5ms total per bit about 210ms total for complete 12 bit transmit with header, parity, termination */ if((commtimer >= txstart + 12) && (commtimer <= txstart + 12 + TXTIME)) { // transmit header, data, parity and termination if((commtimer & 0xF) == 12) { // synchronize 250Hz tx time slices with 4khz clock txtimer = (commtimer - txstart - 12) >> 4; /* data encoding: 4 txtimer time slices per bit. in slice 1 we send a pulse. in slice 3 we send a pulse if bit=1 and no pulse if bit=0. no pulses in timeslices 2 and 4. there are 4 comm coils, we have 2 bytes of unique data per coil to send. we send 11 bits of header+data (MSB first), then a parity bit, then a termination pulse. */ if((txtimer & 0x0003) == 2) { // pulse for a 1, not for a 0 j = 0; for(i=0; i<4; i++) { // figure out which of the 4 coils is sending a 1 or 0 if(txtimer <= (TXCYCLES-6)) { // txtimer 2 to 42: send the 16 data bits k = checkbit(txdataraw[i], (TXCYCLES-5 - txtimer) >> 2); } else { // parity bit at txtimer == TXCYCLES-2 k = parity[i] & 0x1; } if(k) { // coil i is sending a 1 j |= 1<= epoch)) { // got the same message twice in a row, assume it is valid ad.rxdata[i] = k; if(!(PROGRAMMER1 || PROGRAMMER2) && checkbit(ad.rxdata[i], PROGRAMCHANGE_BIT)) { algorithm_init(i, &ad, lastrx, rxdataraw, rxdatarawage, &algorithm); } #ifdef HACK1 if((DATA(ad.rxdata[i]) == 0xf) && (algorithm != &algo_line)) { // custom hack for virus propagation - should be its own bit algorithm = &algo_virus; if(ad.state[3] == 0) { ad.state[3] = epoch; } } #endif lastrx[i] = epoch; } // remember most recently received data rxdataraw[i] = k; rxdatarawage[i] = epoch; } } } // end of receiving #endif /******************* OTHER **********************/ #ifdef notdef // toggle green status led //led(4, 0, magnettimer == 0); //led(4, 1, magnettimer == 2); #endif #ifdef DEBUG_FLIPPER // toggle the 2 latches if(magnettimer == 1) { led(0, 0, 1); magnet(0, 1); } else if(magnettimer == 3) { led(2, 0, 1); magnet(2, 1); } else { led(0, 0, 0); led(2, 0, 0); magnet(0, 0); magnet(2, 0); } #endif } }