// ***************************************** // HowToBuildAPinballMachine.Wordpress.Com // Space-Eight.com // // Instructions: // This code uses the Arduino to send commands to a Multimorphic Solenoid driver, // allowing you to test your playfield mechanisms and check for faults. // // Wire up your driver using the schematics found on the sites listed above. // With the USB cable attached to your Ardunio, open the Serial Monitor. Set the // menu to send newline feeds, then type the number of the solenoid to test, and hit "SEND". // // The selected solenoid should fire for a duration set by the "delay(5)" and THUMPER_FIRE_DURATION loops. // // Post questions or comments to http://howtobuildapinballmachine.wordpress.com // // ***************************************** #include #include #include #define PDB_COMMAND_WRITE 1 #define BANK_ONE 1 #define BANK_ZERO 0 #define THUMPER_FIRE_DURATION 10 unsigned char solenoidFireFlag[16]; // start no digit selected unsigned char selectSolenoid = 0; // unsigned char dataSolenoid = 0; // unsigned char dataSolenoidChime = 0; // int j; byte board = 0; void setup() { // initialize serial monitor: Serial.begin(9600); // this serial for solenoid driver SPI.begin(); SPI.setBitOrder(MSBFIRST); SPI.setClockDivider(SPI_CLOCK_DIV2); // Need 8 MHz. So use DIV2 on a 16 MHz board. } // end setup void loop() { // if there's any serial available, read it: while (Serial.available() > 0) { // look for the next valid integer in the incoming serial stream: selectSolenoid = Serial.parseInt(); // look for the newline if (Serial.read() == '\n') { fireSolenoidNow(selectSolenoid, THUMPER_FIRE_DURATION); } // end read /n } // end while serial available delay(5) ; // this sets the ramp-down speed, and thus the "real" fire duration //--------------------------------------- // SELECT BANK ONE //--------------------------------------- dataSolenoid = 0; for (j = 0; j < 8; j++) { selectSolenoid = j; if (solenoidFireFlag[j] > 0) { dataSolenoid |= 1 << selectSolenoid; // set solenoid if ( solenoidFireFlag[j] < 253 ) { solenoidFireFlag[j] -= 1; // ramp down to zero } } } sendPDBCommand(board, PDB_COMMAND_WRITE, BANK_ONE, dataSolenoid); // 1 == MAIN BANK //--------------------------------------- //--------------------------------------- // SELECT BANK TWO //--------------------------------------- dataSolenoidChime = 0; for (j = 0; j < 8; j++) { selectSolenoid = j; if (solenoidFireFlag[j + 8] > 0) { dataSolenoidChime |= 1 << selectSolenoid; // set solenoid if ( solenoidFireFlag[j + 8] < 253 ) { solenoidFireFlag[j + 8] -= 1; // ramp down to zero } } } sendPDBCommand(board, PDB_COMMAND_WRITE, BANK_ZERO, dataSolenoidChime); // 0 == AUX BANK //--------------------------------------- }// end loop void sendPDBCommand(byte addr, byte command, byte bankAddr, byte data) { byte cmdWord[5]; cmdWord[0] = addr; cmdWord[1] = command; cmdWord[2] = bankAddr; cmdWord[3] = (data >> 4) & 0xF; cmdWord[4] = data & 0xF; // Turn off interrupts so the transfer doesn't get interrupted. noInterrupts(); // Hardcode transfers to minimize IDLEs between transfers. // Using a for-loop adds 5 extra IDLEs between transfers. SPI.transfer(cmdWord[0]); SPI.transfer(cmdWord[1]); SPI.transfer(cmdWord[2]); SPI.transfer(cmdWord[3]); SPI.transfer(cmdWord[4]); // Re-enable interrupts interrupts(); return; } void fireSolenoidNow(byte selectSolenoid_in, byte duration_in ) { dataSolenoid |= 1 << selectSolenoid_in; // set solenoid sendPDBCommand(board, PDB_COMMAND_WRITE, BANK_ONE, dataSolenoid); solenoidFireFlag[selectSolenoid_in] = duration_in; //50; // thumper one return; }