Below is the file 'firm/master/main.c' from this revision. You can also download the file.
// ### BOILERPLATE ### // 8^2 Automaton Firmware // Copyright (C) 2005 Peter Todd <pete@petertodd.org> // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation; either version 2 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License along // with this program; if not, write to the Free Software Foundation, Inc., // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // ### BOILERPLATE ### #include <pic18fregs.h> #include <stdint.h> #include <signal2.h> #include <stdio.h> #include <stdlib.h> #include <../common.h> #pragma stack 0x200 255 #define FOSC (20000000) code char at __CONFIG1H config1h = 0xFF & _OSC_HS_1H & _OSCS_OFF_1H; code char at __CONFIG2L config2l = 0xFF & _PUT_ON_2L & _BODEN_ON_2L & _BODENV_4_2V_2L; code char at __CONFIG2H config2h = 0xFF & _WDTPS_1_128_2H; // watch-dog timer on, max post-scaler code char at __CONFIG3H config3H = 0xFF; code char at __CONFIG4L config4l = 0xFF & _LVP_OFF_4L & _BACKBUG_OFF_4L; code char at __CONFIG5H config5h = 0xFF; code char at __CONFIG5L config5l = 0xFF; code char at __CONFIG6H config6h = 0xFF; code char at __CONFIG6L config6l = 0xFF; code char at __CONFIG7H config7h = 0xFF; code char at __CONFIG7L config7l = 0xFF; // Dip switch stuff. // This is the port to read to get the dip switch value from. #define DIP_SW_PORT (PORTC) // We define masks and modes. Apply the mask to the port with & and then test for equality to the mode. // xx1 Reflash the slaves. Tested seperately as this can be done in conjunction with all of the other modes. #define DIP_SW_REFLASH_MASK (0x01) #define DIP_SW_REFLASH_MODE (0x01) // 00x Standard simulation mode. Does the simulation as intended. #define DIP_SW_NORMAL_MASK (0x06) #define DIP_SW_NORMAL_MODE (0x00) // 01x Test mode gradient. Sets each slave's value to it's ID * 4 #define DIP_SW_TEST_GRAD_MASK (0x06) #define DIP_SW_TEST_GRAD_MODE (0x02) // 10x Test mode... undefined as of yet #define DIP_SW_TEST_UNDEF_MASK (0x06) #define DIP_SW_TEST_UNDEF_MODE (0x04) // 11x Test mode gang. All slaves are controlled in unison by the DIP switches. #define DIP_SW_TEST_GANG_MASK (0x06) #define DIP_SW_TEST_GANG_MODE (0x06) // Variables // This is the big one. The two arrays of the status of each cell. There is a // old and new and we switch between them on each cycle. The +2 is for padding // so we don't have to deal with any special cases. uint8_t cell_array[2][grid_xsize + 2][grid_ysize + 2]; #define cell_user_move 255 #define cell_alive 255 #define cell_dead 0 // Vars so we can efficiently switch between old and new. #define cell_old (cell_old_var) #define cell_new (cell_new_var) #define cell_switch() cell_tmp_var = cell_new_var; \ cell_new_var = cell_old_var; \ cell_old_var = cell_tmp_var; uint8_t cell_old_var,cell_new_var,cell_tmp_var; // This is used to communicate between TMR0 and the main_loop code. Main loop // sets it, TMR0 clears it. volatile uint8_t tmr0_wait_sync; #define tmr0_wait_sync_set (0xFF) #define tmr0_wait_sync_clear (0x00) // The current seed for the LFSR routine. We don't bother initalizing this one, // so long as it's not zero, unlikely, we're fine. uint8_t rand_seed; // Returns a random number after permutating the LFSR seed. uint8_t lfsr_rand(){ __asm BCF STATUS,0 RRCF _rand_seed,W BTFSC STATUS,0 XORLW 0xB4 MOVWF _rand_seed _endasm; return rand_seed; } #include <serial.c> #include <i2c.c> #include <slave_com.c> #include <bootloader.c> // Interupts, AKA signals. We have two types that we use, the TMR0 int, and the USART ints. DEF_INTHIGH(high_int) DEF_HANDLER(SIG_TMR0,_tmr0_handler) END_DEF SIGHANDLER(_tmr0_handler) { // clear the TRM0 synchronization variable for the main loop tmr0_wait_sync = tmr0_wait_sync_clear; // get some hardware randomness. A note about this... Right now the ADC // is connected to a voltage divider consisting of two back to back // 4.7k resistors. This connection is jumpered, so the ADC can be left // free floating as well. If the ADC is connected to ground or 5V the // result is very clean, with maybe the occational least significant // byte flip. But through the divider or floating it's incredibly // noisy, even more so floating. It's hard to say what patterns are in // that noise, but at least some of it must be random. A rather odd // result though, not sure why exactly that happens. // first set ADC into right justified mode, we want the lower bits as they are most susceptable to random effects ADCON1bits.ADFM = 1; // select AN4 ADCON0bits.CHS2 = 1; ADCON0bits.CHS1 = 0; ADCON0bits.CHS0 = 0; // start conversion ADCON0bits.GO = 1; while (ADCON0bits.GO); // done, xor result with rand_seed rand_seed ^= ADRESL; rand(); // mix it up // reenable ourselves INTCONbits.T0IF = 0; } uint8_t motor_inhibit[8]; void main(){ static uint8_t x,y,z,i,f; static uint8_t dip_sw_status; static uint16_t n; static char junk[32]; // Initialization // Variables // Clear the cell array for (i = 0; i < 2; i++){ for (x = 0; x < grid_xsize + 2; x++){ for (y = 0; y < grid_ysize + 2; y++){ cell_array[i][x][y] = 0; } } } // Setup old and new cell_old_var = 0; cell_new_var = 1; // Ports TRISA = 0; TRISB = 0; PORTB = 0; TRISC = 0; // setup dip switches TRISCbits.TRISC0 = 1; TRISCbits.TRISC1 = 1; TRISCbits.TRISC2 = 1; // setup i2c TRISCbits.TRISC3 = 1; TRISCbits.TRISC4 = 1; SSPCON1 = 0x28; SSPSTAT = 80; SSPADD = 0xFF; #if 0 // setup TMR0 for interrupts // 20,000,000/4=5,000,000/256/256=76ints/sec T0CONbits.T0PS0 = 0; T0CONbits.T0PS1 = 0; T0CONbits.T0PS2 = 0; T0CONbits.PSA = 0; T0CONbits.T0SE = 0; T0CONbits.T0CS = 0; // internal clock source T0CONbits.T08BIT = 1; // 8-bit mode INTCONbits.T0IF = 0; INTCONbits.T0IE = 1; INTCONbits.GIE = 1; T0CONbits.TMR0ON = 1; // GO! #endif // setup serial open_serial(); // Tell the world we're alive rs232_sendbuf("Cell Web Controller $Id: main.c,v 1.20 2005/12/14 00:00:03 pete Exp $\n",255); // The power supply on this whole sheebang is a bit touchy, so pause // awhile to make sure it's stable. rs232_sendbuf("Waiting for power to become stable",255); for (i = 1; i < 3; i++){ sprintf(junk,"...%d",i); rs232_sendbuf(junk,sizeof(junk)); for (x = 0; x < 255; x++){ for (y = 0; y < 255; y++){ for (z = 0; z < 10; z++); ClrWdt(); } } // At end of first wait, turn on FET to provide power to the cells. PORTB = 0xFF; } rs232_sendbuf("...done\n",255); // After this we go to various operating modes based on what the dip // switches are set too. // Save current status for comparison later. dip_sw_status = DIP_SW_PORT; // Every mode has the option to reflash the slaves first. if ((dip_sw_status & DIP_SW_REFLASH_MASK) == DIP_SW_REFLASH_MODE){ rs232_sendbuf("Reflashing slaves:\m",255); for (x = 0;x < grid_xsize; x++){ for (y = 0; y < grid_ysize; y++){ f = slave_reflash(x,y,10); sprintf(junk,"%d,%d returned %d\n",(unsigned int)x,(unsigned int)y,(unsigned int)f); rs232_sendbuf(junk,sizeof(junk)); ClrWdt(); } } rs232_sendbuf("Reflashing done.\n",255); } else{ rs232_sendbuf("NOT reflashing slaves.\n",255); } // All modes require the slaves to be running, so start them up. rs232_sendbuf("Sending run command to all slaves:\n",255); for (x = 0;x < grid_xsize; x++){ for (y = 0; y < grid_ysize; y++){ // try to get the attention of the slave, with timeout sprintf(junk,"%d,%d try",x,y); rs232_sendbuf(junk,sizeof(junk)); for (i = 1; i < 11; i++){ ClrWdt(); sprintf(junk,"...%d",i); rs232_sendbuf(junk,sizeof(junk)); if (slave_bootload_ping(x,y) == bootload_status_clear){ // success! rs232_sendbuf("...success!\n",255); slave_bootload_run(x,y); goto slave_run_loop_done; } } rs232_sendbuf("...failed\n",255); slave_run_loop_done: slave_bootload_run(x,y); } } // Test for the different operating modes. // Ganged test mode if ((dip_sw_status & DIP_SW_TEST_GANG_MASK) == DIP_SW_TEST_GANG_MODE){ while(1){ ClrWdt(); rs232_sendbuf("Test mode gang, write loop starting\n",255); for (x = 0;x < grid_xsize; x++){ for (y = 0; y < grid_ysize; y++){ i = ((PORTC & 0x0F) << 4) | (PORTC & 0x0F); slave_cell_write(x,y,i); sprintf(junk,"value %d,%d = %d\n",(unsigned int)x,(unsigned int)y,(unsigned int)i); rs232_sendbuf(junk,31); } } } } // Gradient test mode if ((dip_sw_status & DIP_SW_TEST_GRAD_MASK) == DIP_SW_TEST_GRAD_MODE){ while(1){ ClrWdt(); rs232_sendbuf("Test mode gradient, write loop starting\n",255); for (x = 0;x < grid_xsize; x++){ for (y = 0; y < grid_ysize; y++){ // The + 3 is so that 7,7 gets a value of 255 rather than 252, do the math. i = (((x * grid_xsize) + y) * 4) + 3; slave_cell_write(x,y,i); sprintf(junk,"value %d,%d = %d\n",(unsigned int)x,(unsigned int)y,(unsigned int)i); rs232_sendbuf(junk,31); } } } } // Normal mode, currently implementing a simple conways game of life system, rules 23 if ((dip_sw_status & DIP_SW_NORMAL_MASK) == DIP_SW_NORMAL_MODE){ // The following is this devices uuid... cell_array[cell_new][1][1] = 255; cell_array[cell_new][1][5] = 255; cell_array[cell_new][1][7] = 255; cell_array[cell_new][1][8] = 255; cell_array[cell_new][2][2] = 255; cell_array[cell_new][2][5] = 255; cell_array[cell_new][2][7] = 255; cell_array[cell_new][3][1] = 255; cell_array[cell_new][3][3] = 255; cell_array[cell_new][3][4] = 255; cell_array[cell_new][3][5] = 255; cell_array[cell_new][3][6] = 255; cell_array[cell_new][3][8] = 255; cell_array[cell_new][4][1] = 255; cell_array[cell_new][4][3] = 255; cell_array[cell_new][5][3] = 255; cell_array[cell_new][5][5] = 255; cell_array[cell_new][5][6] = 255; cell_array[cell_new][5][7] = 255; cell_array[cell_new][6][7] = 255; cell_array[cell_new][6][8] = 255; cell_array[cell_new][7][2] = 255; cell_array[cell_new][7][5] = 255; cell_array[cell_new][7][6] = 255; cell_array[cell_new][7][7] = 255; cell_array[cell_new][8][2] = 255; cell_array[cell_new][8][3] = 255; cell_array[cell_new][8][4] = 255; cell_array[cell_new][8][5] = 255; cell_array[cell_new][8][7] = 255; cell_array[cell_new][8][8] = 255; while (1){ ClrWdt(); cell_switch(); // calculate the new state for each cell for (x = 1; x < (grid_xsize + 1); x++){ for (y = 1; y < (grid_ysize + 1); y++){ // Add up the values of the adjacent cells. n = (uint16_t)cell_array[cell_old][x - 1][y - 1] + (uint16_t)cell_array[cell_old][x ][y - 1] + (uint16_t)cell_array[cell_old][x + 1][y - 1] + (uint16_t)cell_array[cell_old][x - 1][y ] + // middle cell not used, yet (uint16_t)cell_array[cell_old][x + 1][y ] + (uint16_t)cell_array[cell_old][x - 1][y + 1] + (uint16_t)cell_array[cell_old][x ][y + 1] + (uint16_t)cell_array[cell_old][x + 1][y + 1]; // Calculate the new state for the cell based on the rules 23/3 // Cell going to be born? Also handles the case of a existing cell surviving. if (n == (255 * 3)){ cell_array[cell_new][x][y] = 255; } // Cell going to survive? (or stay dead) else if (n == (255 * 2)){ cell_array[cell_new][x][y] = cell_array[cell_old][x][y]; } // Kill the cell. else{ cell_array[cell_new][x][y] = 0; } } } // calculated next state of simulation, update slaves for (x = 1; x < (grid_xsize + 1); x++){ for (y = 1; y < (grid_ysize + 1); y++){ // tell slave what it's new state should be if (cell_array[cell_new][x][y] == cell_dead) f = 0; if (cell_array[cell_new][x][y] >= cell_alive) f = 255; slave_cell_write(x - 1,y - 1,f); } } for (i = 0; i < 7; i++) motor_inhibit[i] = 5; // now loop checking slaves for new user input for (i = 0; i < 25;i++){ for (y = 1; y < (grid_ysize + 1); y++){ if (motor_inhibit[y]) motor_inhibit[y]--; for (x = 1; x < (grid_xsize + 1); x++){ // Read the user input from the slave. f = slave_cell_read(x - 1,y - 1); // If the inhibit timer is active, ignore any user input. if (!motor_inhibit[y]){ if (f == cell_user_move){ // Got new movement. cell_array[cell_new][x][y] = cell_alive; // Tell the cell to start moving slave_cell_write(x - 1,y - 1,cell_array[cell_new][x][y]); motor_inhibit[y] = 5; } } } } } } } }