Pinball Machine EGGN 383 Final Project May 6, 2013 Mei Li and Caleb Whitehead Introduction The objective of our project was to design and implement a small-scale pinball machine. To make the project feasible given the allotted time we had, the operations of our pinball machine were limited. Two servo motors drive two separate paddles. User input via a pushbutton activates the paddles to strike the pinball, which then travels around the playing base. If the pinball enters the score region, it will be detected by infrared (IR) break-beam sensors and the machine will update the score on the liquid crystal display (LCD). If the pinball enters the escape region the machine will decrement the number of escapes remaining. Game is over when the pinball escapes three times; the goal is to enter the score region as many times as possible. Project specifications were met with two software steps. First, we initialized all necessary components such as the LCD and the PWM system, which controlled the servo motors. Second, we used a polling technique to read input from the pushbuttons and drive the paddles accordingly, as well as to read the input from the break beam sensors and adjust the scores on the LCD accordingly. Hardware Overview As shown in the hardware diagram in Figure 1, the main electrical components for this project are the servo motors, pushbuttons, IR break-beam sensors, and LCD. The mechanical aspects of this project consisted of the playing base, the paddles, and how they are mounted together with the electrical components. We used two servo motors, rated at 6V, to swing the paddles to 60 degrees. The motors were mounted on the bottom of the playing base, and as the pinball fell, the pushbuttons could activate the motors. Two IR break-beam sensors were installed around the center of the playing base. The gap between the emitter and the receiver served as the scoring region, and as the pinball travelled through the gap, the beam was broken and the LCD updated the scoring. A similar principle applied to the IR breakbeam sensor installed at the bottom of playing base, and the difference was that this sensor served as the escape region. The picture in Figure 2 shows our implementation of the conceptual design in Figure 1. We used wooden materials for the playing base and the paddles. Cardboard material was considered in our original design but it was too fragile and did not offer a smooth playing field for the pinball. However, we used cardboard material to make the edges and obstacles on the playing base. 1 Figure 1: Hardware Diagram Figure 2: Hardware Implementation 2 Circuit Description The circuit we used to construct the pinball machine consisted of two parts: the primary control circuit and the 555 timer circuit. The first, shown in Figure 3, included the wiring between the HCS12 microcontroller and all of the major components involved in mechanical controls and data transfer. This circuit can be further divided into three primary functions: The LCD was wired to PortAD, which was used for this project as nothing more than a simple input/output port. PortT monitors the pushbutton signal inputs and adjusts the servo motor outputs accordingly. Finally, the IR sensor outputs were captured using 3 different pins on PortM. The LCD used for this project was the same device used in Lab 7; the wiring method remained unchanged from what we used previously, but because PortAD pins 0 and 1 were hardwired to other components, all pins used were shifted up by two pins from those used on PortT. The IR sensor outputs were sent to PortM, pins 2 through 4, which were configured as inputs to read the signals. The wiring on PortT is somewhat convoluted for a couple of design reasons. The servo motors require a PWM signal to control them properly; changing the duty cycle adjusts the device position. PWM signals can be transferred from the PWM system to PortT using the MODRR register; however, they can only be transferred to pins 0 through 4. However, the servo motor outputs, J3 and J6, are hardwired to PortT pins 4 and 5. As a result, the PWM signals were sent to pins 3 and 4, and then the signal on pin 3 was hardwired to pin 5 on the header board. All of this wiring is shown in Figure 3. The pushbutton inputs used a simple pull-up resistor control scheme, and are sent to pins 0 and 2 on PortT, which are configured as inputs. Figure 3: Primary HCS12 control schematic 3 The second part of the circuit, shown in Figure 4, was the 555 timer circuit. Although the 555 timer was driven by the power supply from the SSMI board, this was an entirely separate circuit and used only to produce the 38 kHz signal necessary for operating the IR LED outputs to the break beam sensors. To achieve the desired square wave output, the 555 timer was configured in Astable mode, as depicted below. A variable resistor was used to fine tune the signal output for the IR devices used. The output from the timer was sent through a series resistor-IR LED circuit that transmitted a continuous beam to the IR sensors in Figure 3. Figure 4: 555CN IC Timer schematic Software Design As formerly mentioned, the software we used to control this device consisted of two primary tasks within the main function. The first was an initialization phase, and the second, within the infinite loop, was code that would continuously poll and respond to inputs on the pushbuttons and IR sensors. Pseudocode for the software is shown in Figure 5, whereas Appendix A3 details the actual main function in C used to implement the design. Initialize LCD (on PortAD) PWM system (on PortT) IR sensors (already set as inputs on PortM) Set score to zero Set number of escapes left to three While(true) Drive motor forward if button depressed Drive motor back if button released Waiting = 1 if score sensor detects ball Increase score, waiting = 0 if score sensor doesn't detect ball and waiting = 1 If escape sensor detects ball, wait until escape sensor doesn't detect ball, then decrease escapes left Update counters on the LCD Display "Game Over" if no escapes left While (Game Over) enter infinite loop to disable game Figure 5: Pinball Machine Pseudocode 4 In the initialization phase, all DDRx registers were set to appropriate values so that the pins were configured as input or output, and the score counters were initialized to 0, for the score, and 3, for the number of escapes allowed. All functions required to initialize the LCD are in Appendix A2; essentially, this required clearing the display, configuring the cursors, and preparing it to show characters as desired. In the polling phase, the motor functions were very simple. IF statements determined if the pushbuttons were currently depressed or not, and changed the duty cycle of the PWM signal to the servo motor accordingly. Polling the IR sensors required first evaluating whether or not any IR beams were broken and then using that information to update the LCD display. The pseudocode shows redundancies that were put in place to prevent unbounded scoring increases should the ball become lodged between sensors. When the escape counter reached zero the “Game Over” state was implemented by entering a second, nested loop that prevented the program from operating until the reset button was pushed. Verification Since this project consisted of different hardware components, we developed the testing procedure for one component at a time. We started the implementation of the project by verifying that we could adapt the LCD code and schematic from previous labs to be used on PortAD. After testing the LCD, we moved on to experiment with different types of motors. Initially, we were not sure which type of motor would be ideal to swing a paddle. The DC motor we had in the lab was not accessible as it was built into a wheel system architecture, and would also have created error with continuous forward and reverse motion. Furthermore, the stepper motor was designed for applications required accurate positioning but could not provide torque adequate for our project, so we opted to use the servo motor. Servo motors require a PWM signal to control their position. Having set-up the push-buttons to control the PWM duty cycle for their corresponding motors, we used an oscilloscope to measure the output signal being used. As Figure 6 shows, the steady position for each motor required a different duty cycle, as they are positioned in mirror locations from each other. Figure 6: PWM signal used to place each servo motor in initial, steady position 5 The next task was to implement the IR break-beam sensors. To make the pinball machine portable, we decided to use a 555 Timer instead of a function generator to modulate the frequency required for the IR emitter. In this process, we experimented with the potentiometer and the trimpot for the variable resistor. As Figure 7 shows, the modulated signal for the emitter was not exactly 38 kHz. It was important for us to maintain the 36.6 kHz signal in order for all the break-beam sensors to work properly. Through experimentation, we found out that trimpot gave us more stabilized signals. Figure 7: Modulated signal for IR emitters After verifying the 555 timer, we tested the IR sensors. As expected, when an object was in between the emitter and the receiver, the beam was broken. Figure 8 shows this instance while Figure 9 shows when there was nothing in between the emitter and the receiver. After testing the IR sensors we mounted them on the board and soldered their terminals to wires for connection to the SSMI board. 6 Figure 8: Oscilloscope trace when an object breaks the IR sensor beam Figure 9: Oscilloscope trace when there is no IR detection 7 Conclusion Given the scope of the project specifications from the beginning, which proposed a simplified version of a pinball game, this project was successful. After adding a second, external power supply we were able to drive both servo motor paddles with precision, and the IR sensor system was successful in detecting the presence of the ball and appropriately adjusting the score. Use of servo motors allowed the position of the paddles to be controlled with every button press, which eliminated positioning errors that may have been introduced by other motors. Also, the use of a 555 timer chip allowed us to make the game more compact and portable, while still providing adequate control over the output signal. While designing this project we faced three primary challenges that consumed a great deal of time. The first challenge dealt with capturing the signal from the pushbuttons; initially we planned to use input capture on PortT, but we discovered that the initial bouncing from the pushbutton produced occasional false triggers that caused this method to be unreliable. We resolved this problem by polling the state of the pushbuttons in the main function, rather than responding to the state transitions only. The second problem was caused by the HCS12’s power limitations. When we attached both servo motors simultaneously we observed erratic behavior when controlling them with the pushbuttons. This included false triggers on both motors, continued, random oscillatory behavior after the pushbuttons were no longer being operated, and failure to operate at all. This problem was resolved by powering the second motor with an external 5V DC power supply, but still using the PWM signal from the HCS12 for control. This system allowed us to retain both paddles in our design. Although we were able to use both motors, we were required to use slower motors provided in lab rather than higher speed servos, as the parts we ordered did not arrive on time. The final difficulty we faced was tied to the IR sensors. We found there to be some slight variation in the signal frequency that each IR sensor would detect, so there was a substantial amount of trial and error involved in configuring the 555 timer to output exactly the right signal so that high and low signals were consistent. We were pleased with the successful implementation of our design. We learned a great deal about the power dependency of the HCS12 microcontroller, and were able to expand our knowledge about using the various ports available. 8 Appendix A1: Microsecond Delay Function (delays number of microseconds input x10) void DelayuSec(unsigned int t) { if (t !=0){ t*=10; __asm { ; Main loop is 24 cycles, or 1 usec ldx t ; 3 additional cycles here loop: psha ; 2 E cycles pula ; 3 E cycles psha ; 2 E cycles pula ; 3 E cycles psha ; 2 E cycles pula ; 3 E cycles psha ; 2 E cycles pula ; 3 E cycles nop ; 1 E cycle dbne x,loop ; 3 E cycles } } } A2: LCD Functions (writeNibbleToLCD, writeByteToLCD, InitializeLCD, printLCD) void writeNibbleToLCD(char n, char rs, int t) { rs <<=5; PTAD = (rs|0x10|(0x0f & n))<<2; //PTAD <<= 2; DelayuSec(1); PTAD &= ~0x40; DelayuSec(t); } void writeByteToLCD(char b, char rs, int t) { char temp = b>>4; writeNibbleToLCD(temp, rs, 1); // for high nibble, shift left 4 // bits writeNibbleToLCD(b, rs, 1); // for low nibble, nibble function // will take care of masking b DelayuSec(t); } void InitializeLCD(void) { int i; for (i=0; i<100; i++) { // delay 100ms to allow LCD powerup DelayuSec(1000); } 9 // The first parameter in each call is the nibble to be sent, // the second parameter is the rs value (rs=0 indicates an // instruction), and the third parameter is the time to delay // after sending (in units of us). writeNibbleToLCD(0x03, 0, 5000); //delay at least 4 ms = 4000 us writeNibbleToLCD(0x03, 0, 5000); writeNibbleToLCD(0x03, 0, 5000); writeNibbleToLCD(0x02, 0, 5000); // The first parameter in each call is the byte to be sent (as // two nibbles), the second parameter is the rs value (rs=0 // indicates an instruction), and the 3rd parameter is the time // to delay after sending both nibbles (usec). // These commands are all fast (~40 us) except for "clear // display" (2 ms) writeByteToLCD(0x28,0,50); // 2 lines, 5x8 dots writeByteToLCD(0x0c,0,50); // display on, no cursor, no blinking writeByteToLCD(0x14,0,50); // shift cursor right writeByteToLCD(0x01,0,2000); // clear display and cursor home } void printLCD(char mystr[]) { int i; char strChar; writeByteToLCD(0x01,0,2000); // clear the display for(i = 0; i<80; i++) { strChar = mystr[i]; if(strChar == 0) break; writeByteToLCD(strChar, 1, 50); } } A3: Main Function (with include statements) #include <hidef.h> /* common defines and macros */ #include "derivative.h" /* derivative-specific definitions */ #include <stdio.h> void main(void) { /* initialize variables for LCD */ char toLCD[80]; // Stores the value to be displayed int file_number = 0; // Unused variable used to call sprintf int score = 0; // Score count int escapes = 3; // Number of escapes remaining char waiting, update; // Used in the IR polling function // initialize PAD pins for LCD DDRAD = 0xfc; 10 // Set up PWM System to drive the Servo motor(s) on PT3-PT4 PWMCLK = 0x18; // Select clock SA/SB as the clock source PWMPRCLK = 0x66; // Set clock prescalar to 64 on A and B PWMSCLA = 15; // Divider for clock SA PWMSCLB = 15; // Divider for clock SB PWMPOL = 0x18; // PWM3/PWM4 output high at the start of period PWMPER4 = 250; // Period = 250 ticks PWMPER3 = 250; PWMDTY4 = 14; // Duty = 12 ticks, which is about 1 ms PWMDTY3 = 22; PWME = 0x18; // Enable PWM3/PWM4 MODRR = 0x18; // Route PWM to PortT PT3/PT4 // Set up PT3/PT4 for output to the motors DDRT = 0x18; InitializeLCD(); EnableInterrupts; waiting = 0; // Initialize IR sensors with no ball detectio // Initialize score display to starting values file_number = sprintf(toLCD, "Score: %d \n\n Escapes Remaining: %d", score, escapes); printLCD(toLCD); for(;;) { /* * * * * POLL PUSHBUTTONS * * * * */ //"Outside" pushbutton controls J3, treats it as left paddle //"Inside" pushbutton controls J6, treats it as right paddle //PWM Duty cycles for forward and reverse positions are opposite //for left and right paddles if (!(PTT &0x01))//PT0 is low; Left Motor forward PWMDTY3 = 14; else PWMDTY3 = 22; if (!(PTT &0x04))//PT2 is low; Right Motor forward PWMDTY4 = 22; else PWMDTY4 = 14; /* * * * * POLL IR SENSORS * * * * * */ if (PTM & 0x18){ //check if ball is in a score region score++; //if present, increase score counter update=1; //”update” tells the LCD to refresh } 11 if (PTM & 0x04) { while (PTM & 0x04);//if ball enters escape region, wait for //user to remove it escapes--; update = 1; //”update” tells the LCD to refresh } if (escapes>0){ file_number = sprintf(toLCD, "Score: %d \n Escapes Remaining: %d", score, escapes); if (update){ //Takes time, so only refresh if scores change printLCD(toLCD); update =0; } } if (escapes ==0) { printLCD("Game Over"); for(;;); //infinite loop prevents further gameplay at “Game //Over” until Reset button pressed } _FEED_COP(); /* feeds the dog */ } /* loop forever */ /* please make sure that you never leave main */ } 12