PPOD-mini Project
Overview
The goal of this project was to create a miniature, simplified version of the PPOD (Programmable Part-feeding Oscillatory Device) that is currently in the Laboratory for Intelligent Mechanical Systems. This smaller model needed to be easily transportable so that it could be taken to various locations and shown to different groups. The PPOD is a device that is able to manipulate parts placed on it by creating a velocity field that is dependent on the phases and amplitudes of the speakers that are connected to the platform. More information on the original PPOD and related research can be found at the LIMS website.
Physical Construction
Base
The base for the PPOD-mini is constructed out of acrylic sheets that are approximately 0.22" thick. Each section was designed in AutoCad and then transferred to the Laser cutter to be created. The sections were designed with a jigsaw pattern on the edges so they would fit together tightly and could be placed exactly where desired. The dimension of the base are 10" x 10" x 3.5". The goal was to keep it as small as possible while still making it functional, this width was required to fit all of the speakers and the height was necessary for the power supplies to fit in the enclosed space.
Speaker Mounts
Six speaker mounts were designed to hold the speakers. They were designed to take up the least amount of space possible while still being stable enough to hold a vibrating speaker for an extended period of time. They were designed similarly to the base, with interlocking acrylic pieces made on the laser cutter and then joined together with a chemical bonding agent. A dimensioned drawing for these pieces is shown on the right.
Speakers
The speakers used currently are Jameco 8 ohm speakers, measuring approximately two inches in diameter. When a 5 volt signal is applied these speakers produce 3.125 watts of power. They are secured into the speakers mounts with epoxy. The flexure attachments are epoxied into the center of the speakers and the flexures are held in place by a set screw which is holding on to a threaded rod that runs into the flexure. These flexures are then attached to the platform with is eight inches across and also made of acrylic. All of the drawing files for these parts can be downloaded at File:PPOD drawing files.zip.
Circuit
Each speaker is driven by a combination of four op-amps and a pair of transistors. We used ------- because each chip has four separate op-amps and they are able to go rail to rail which simplified our power supply setup. We used two +5V power supplies in series in order to get +/- 5V for the circuit. We are also using a digital potentiometer ------------- so we can instantly change the amplitude of each speaker individually. The circuit diagram is shown to the right. To adjust the setting you press the red button, then using the knob you choose one of three preset patterns or Custom to choose your own phases and amplitudes. If "custom" is chosen you can choose one of 16 settings for the phase and amplitude for each speaker.
PIC Code
The following is the PIC code used to control the PPOD-mini with comments.
#include <18f4520.h> #DEVICE ADC=8 #fuses EC,NOLVP,NOWDT,NOPROTECT #use delay(clock=20000000) #include "flex_lcd.c" //Need to have this file in the same folder. //These define the different SPI modes in terms of constants the compiler knows about #define SPI_MODE_0 (SPI_L_TO_H | SPI_XMIT_L_TO_H) #define SPI_MODE_1 (SPI_L_TO_H) #define SPI_MODE_2 (SPI_H_TO_L) #define SPI_MODE_3 (SPI_H_TO_L | SPI_XMIT_L_TO_H) #define SS_PIN PIN_B1 //this can be any output pin on the master #DEFINE VAR_POT1 0 //Constants we defined to set digital potentiometers #DEFINE VAR_POT2 1 #DEFINE VAR_POT3 2 #DEFINE VAR_POT4 3 #DEFINE VAR_POT5 4 #DEFINE VAR_POT6 5 int32 frequency = 50; //Set speaker frequency in hertz. int32 delays; int8 s_p[6]={0,0,0,0,0,0}; //Array that will hold the phase of each speaker int8 s_a[6]={15,15,15,15,15,15}; //Array that holds amplitude of each speaker char * mode1 = "Custom"; char * mode2 = "Preset1"; //Names can be changed to fit whatever pattern you find char * mode3 = "Preset2"; char * mode4 = "Preset3"; char mode_name; int speaker; int phase; int amplitude; int mode; int i; void setup(void) //Digital potentiometer setup { output_high(SS_PIN); setup_spi(SPI_MASTER | SPI_MODE_1 | SPI_CLK_DIV_4); } void set_var_pot(int channel, int value) //Function to set resistance value on the digital potentiometer { output_low(SS_PIN); //pull the slave select line low to select the slave spi_write((int8)((channel<<1)|((value>>7)&1))); //This was necessary to make it work properly delay_us(1); //Necessary delay to allow for communication spi_write((int8)(value<<1)); output_high(SS_PIN); //deselect the slave. } #INT_EXT void INT0isr() { //Interrupt routine is called when Pin RB0 goes from low to high //which occurs when the red button is pressed and released setup_adc_ports(AN0_ANALOG); setup_adc( ADC_CLOCK_INTERNAL ); set_adc_channel(0); // there's only one ADC so select which input to connect to it; here pin AN0 delay_ms(1000); while(!input(PIN_B0)) { mode = read_adc(); mode = mode >> 6; switch(mode) //Chooses which pattern you want { case 0: mode_name = mode1; //LCD displays 12 digits in the top row, //the phases and amplitudes, and then the //name of the pattern in the 2nd row printf(lcd_putc,"\n\f%1X%1X%1X%1X%1X%1X%1X%1X%1X%1X%1X%1X\n%s",s_p[0],s_p[1],s_p[2],s_p[3],s_p[4],s_p[5],s_a[0],s_a[1],s_a[2],s _a[3],s_a[4],s_a[5],mode_name); delay_ms(50); break; case 1: mode_name = mode2; printf(lcd_putc,"\n\f%1X%1X%1X%1X%1X%1X%1X%1X%1X%1X%1X%1X\n%s",s_p[0],s_p[1],s_p[2],s_p[3],s_p[4],s_p[5],s_a[0],s_a[1],s_a[2],s_a[3],s_a[4],s_a[5],mode_name); delay_ms(50); break; case 2: mode_name = mode3; printf(lcd_putc,"\n\f%1X%1X%1X%1X%1X%1X%1X%1X%1X%1X%1X%1X\n%s",s_p[0],s_p[1],s_p[2],s_p[3],s_p[4],s_p[5],s_a[0],s_a[1],s_a[2],s_a[3],s_a[4],s_a[5],mode_name); delay_ms(50); break; case 3: mode_name = mode4; printf(lcd_putc,"\n\f%1X%1X%1X%1X%1X%1X%1X%1X%1X%1X%1X%1X\n%s",s_p[0],s_p[1],s_p[2],s_p[3],s_p[4],s_p[5],s_a[0],s_a[1],s_a[2],s_a[3],s_a[4],s_a[5],mode_name); delay_ms(50); break; default: printf(lcd_putc,"\n\fERROR"); delay_ms(50); break; } } delay_ms(1000); switch(mode) //Setting values for custom pattern { case 0: for(i=0; i<6; i++) { while(!input(PIN_B0)) { speaker = i; phase = read_adc(); phase = phase>>4; s_p[speaker]=phase; printf(lcd_putc,"\n\f%1X%1X%1X%1X%1X%1X%1X%1X%1X%1X%1X%1X\n%s",s_p[0],s_p[1],s_p[2],s_p[3],s_p[4],s_p[5],s_a[0],s_a[1],s_a[2],s_a[3],s_a[4],s_a[5],mode_name); delay_ms(50); } delay_ms(1000); } for(i=0; i<6; i++) { while(!input(PIN_B0)) { speaker = i; amplitude = read_adc(); amplitude = amplitude>>4; s_a[speaker]=amplitude; printf(lcd_putc,"\n\f%1X%1X%1X%1X%1X%1X%1X%1X%1X%1X%1X%1X\n%s",s_p[0],s_p[1],s_p[2],s_p[3],s_p[4],s_p[5],s_a[0],s_a[1],s_a[2],s_a[3],s_a[4],s_a[5],mode_name); delay_ms(50); } delay_ms(1000); } break; case 1: //Custom patterns: You should change these value to suit your needs s_p[0]=0; s_p[1]=0; s_p[2]=0; s_p[3]=0; s_p[4]=0; s_p[5]=0; s_a[0]=8; s_a[1]=8; s_a[2]=8; s_a[3]=8; s_a[4]=8; s_a[5]=8; break; case 2: s_p[0]=0; s_p[1]=0; s_p[2]=0; s_p[3]=0; s_p[4]=0; s_p[5]=0; s_a[0]=8; s_a[1]=8; s_a[2]=8; s_a[3]=8; s_a[4]=8; s_a[5]=8; break; case 3: s_p[0]=0; s_p[1]=0; s_p[2]=0; s_p[3]=0; s_p[4]=0; s_p[5]=0; s_a[0]=8; s_a[1]=8; s_a[2]=8; s_a[3]=8; s_a[4]=8; s_a[5]=8; break; default: printf(lcd_putc,"ERROR"); delay_ms(50); } set_var_pot(VAR_POT1,s_a[0]<<4); //Set resistors to determined values delay_ms(10); set_var_pot(VAR_POT2,s_a[1]<<4); delay_ms(10); set_var_pot(VAR_POT3,s_a[2]<<4); delay_ms(10); set_var_pot(VAR_POT4,s_a[3]<<4); delay_ms(10); set_var_pot(VAR_POT5,s_a[4]<<4); delay_ms(10); set_var_pot(VAR_POT6,s_a[5]<<4); delay_ms(10); } void main() { enable_interrupts(INT_EXT); //Enables external interrupts enable_interrupts(GLOBAL); ext_int_edge(0, L_TO_H); //Sets external interrupt to trigger when RB0 goes from low to high lcd_init(); delays = 62500/frequency; //This delay time corresponds to a 22.5 degree phase shift (360/16) //ground = input(PIN_E0); setup(); set_var_pot(VAR_POT1,s_a[0]<<4); //Set resistor values delay_ms(10); set_var_pot(VAR_POT2,s_a[1]<<4); delay_ms(10); set_var_pot(VAR_POT3,s_a[2]<<4); delay_ms(10); set_var_pot(VAR_POT4,s_a[3]<<4); delay_ms(10); set_var_pot(VAR_POT5,s_a[4]<<4); delay_ms(10); set_var_pot(VAR_POT6,s_a[5]<<4); delay_ms(10); mode_name = mode1; while(true) { if(s_p[0]==0)output_high(PIN_A1); //Sets speakers high or low depending on phase, always running except when the if(s_p[1]==0)output_high(PIN_A2); //interrupt is called. if(s_p[2]==0)output_high(PIN_A3); if(s_p[3]==0)output_high(PIN_A4); if(s_p[4]==0)output_high(PIN_A5); if(s_p[5]==0)output_high(PIN_E0); if(s_p[0]==8)output_low(PIN_A1); if(s_p[1]==8)output_low(PIN_A2); if(s_p[2]==8)output_low(PIN_A3); if(s_p[3]==8)output_low(PIN_A4); if(s_p[4]==8)output_low(PIN_A5); if(s_p[5]==8)output_low(PIN_E0); delay_us(delays); if(s_p[0]==1)output_high(PIN_A1); if(s_p[1]==1)output_high(PIN_A2); if(s_p[2]==1)output_high(PIN_A3); if(s_p[3]==1)output_high(PIN_A4); if(s_p[4]==1)output_high(PIN_A5); if(s_p[5]==1)output_high(PIN_E0); if(s_p[0]==9)output_low(PIN_A1); if(s_p[1]==9)output_low(PIN_A2); if(s_p[2]==9)output_low(PIN_A3); if(s_p[3]==9)output_low(PIN_A4); if(s_p[4]==9)output_low(PIN_A5); if(s_p[5]==9)output_low(PIN_E0); delay_us(delays); if(s_p[0]==2)output_high(PIN_A1); if(s_p[1]==2)output_high(PIN_A2); if(s_p[2]==2)output_high(PIN_A3); if(s_p[3]==2)output_high(PIN_A4); if(s_p[4]==2)output_high(PIN_A5); if(s_p[5]==2)output_high(PIN_E0); if(s_p[0]==10)output_low(PIN_A1); if(s_p[1]==10)output_low(PIN_A2); if(s_p[2]==10)output_low(PIN_A3); if(s_p[3]==10)output_low(PIN_A4); if(s_p[4]==10)output_low(PIN_A5); if(s_p[5]==10)output_low(PIN_E0); delay_us(delays); if(s_p[0]==3)output_high(PIN_A1); if(s_p[1]==3)output_high(PIN_A2); if(s_p[2]==3)output_high(PIN_A3); if(s_p[3]==3)output_high(PIN_A4); if(s_p[4]==3)output_high(PIN_A5); if(s_p[5]==3)output_high(PIN_E0); if(s_p[0]==11)output_low(PIN_A1); if(s_p[1]==11)output_low(PIN_A2); if(s_p[2]==11)output_low(PIN_A3); if(s_p[3]==11)output_low(PIN_A4); if(s_p[4]==11)output_low(PIN_A5); if(s_p[5]==11)output_low(PIN_E0); delay_us(delays); if(s_p[0]==4)output_high(PIN_A1); if(s_p[1]==4)output_high(PIN_A2); if(s_p[2]==4)output_high(PIN_A3); if(s_p[3]==4)output_high(PIN_A4); if(s_p[4]==4)output_high(PIN_A5); if(s_p[5]==4)output_high(PIN_E0); if(s_p[0]==12)output_low(PIN_A1); if(s_p[1]==12)output_low(PIN_A2); if(s_p[2]==12)output_low(PIN_A3); if(s_p[3]==12)output_low(PIN_A4); if(s_p[4]==12)output_low(PIN_A5); if(s_p[5]==12)output_low(PIN_E0); delay_us(delays); if(s_p[0]==5)output_high(PIN_A1); if(s_p[1]==5)output_high(PIN_A2); if(s_p[2]==5)output_high(PIN_A3); if(s_p[3]==5)output_high(PIN_A4); if(s_p[4]==5)output_high(PIN_A5); if(s_p[5]==5)output_high(PIN_E0); if(s_p[0]==13)output_low(PIN_A1); if(s_p[1]==13)output_low(PIN_A2); if(s_p[2]==13)output_low(PIN_A3); if(s_p[3]==13)output_low(PIN_A4); if(s_p[4]==13)output_low(PIN_A5); if(s_p[5]==13)output_low(PIN_E0); delay_us(delays); if(s_p[0]==6)output_high(PIN_A1); if(s_p[1]==6)output_high(PIN_A2); if(s_p[2]==6)output_high(PIN_A3); if(s_p[3]==6)output_high(PIN_A4); if(s_p[4]==6)output_high(PIN_A5); if(s_p[5]==6)output_high(PIN_E0); if(s_p[0]==14)output_low(PIN_A1); if(s_p[1]==14)output_low(PIN_A2); if(s_p[2]==14)output_low(PIN_A3); if(s_p[3]==14)output_low(PIN_A4); if(s_p[4]==14)output_low(PIN_A5); if(s_p[5]==14)output_low(PIN_E0); delay_us(delays); if(s_p[0]==7)output_high(PIN_A1); if(s_p[1]==7)output_high(PIN_A2); if(s_p[2]==7)output_high(PIN_A3); if(s_p[3]==7)output_high(PIN_A4); if(s_p[4]==7)output_high(PIN_A5); if(s_p[5]==7)output_high(PIN_E0); if(s_p[0]==15)output_low(PIN_A1); if(s_p[1]==15)output_low(PIN_A2); if(s_p[2]==15)output_low(PIN_A3); if(s_p[3]==15)output_low(PIN_A4); if(s_p[4]==15)output_low(PIN_A5); if(s_p[5]==15)output_low(PIN_E0); delay_us(delays); if(s_p[0]==8)output_high(PIN_A1); if(s_p[1]==8)output_high(PIN_A2); if(s_p[2]==8)output_high(PIN_A3); if(s_p[3]==8)output_high(PIN_A4); if(s_p[4]==8)output_high(PIN_A5); if(s_p[5]==8)output_high(PIN_E0); if(s_p[0]==0)output_low(PIN_A1); if(s_p[1]==0)output_low(PIN_A2); if(s_p[2]==0)output_low(PIN_A3); if(s_p[3]==0)output_low(PIN_A4); if(s_p[4]==0)output_low(PIN_A5); if(s_p[5]==0)output_low(PIN_E0); delay_us(delays); if(s_p[0]==9)output_high(PIN_A1); if(s_p[1]==9)output_high(PIN_A2); if(s_p[2]==9)output_high(PIN_A3); if(s_p[3]==9)output_high(PIN_A4); if(s_p[4]==9)output_high(PIN_A5); if(s_p[5]==9)output_high(PIN_E0); if(s_p[0]==1)output_low(PIN_A1); if(s_p[1]==1)output_low(PIN_A2); if(s_p[2]==1)output_low(PIN_A3); if(s_p[3]==1)output_low(PIN_A4); if(s_p[4]==1)output_low(PIN_A5); if(s_p[5]==1)output_low(PIN_E0); delay_us(delays); if(s_p[0]==10)output_high(PIN_A1); if(s_p[1]==10)output_high(PIN_A2); if(s_p[2]==10)output_high(PIN_A3); if(s_p[3]==10)output_high(PIN_A4); if(s_p[4]==10)output_high(PIN_A5); if(s_p[5]==10)output_high(PIN_E0); if(s_p[0]==2)output_low(PIN_A1); if(s_p[1]==2)output_low(PIN_A2); if(s_p[2]==2)output_low(PIN_A3); if(s_p[3]==2)output_low(PIN_A4); if(s_p[4]==2)output_low(PIN_A5); if(s_p[5]==2)output_low(PIN_E0); delay_us(delays); if(s_p[0]==11)output_high(PIN_A1); if(s_p[1]==11)output_high(PIN_A2); if(s_p[2]==11)output_high(PIN_A3); if(s_p[3]==11)output_high(PIN_A4); if(s_p[4]==11)output_high(PIN_A5); if(s_p[5]==11)output_high(PIN_E0); if(s_p[0]==3)output_low(PIN_A1); if(s_p[1]==3)output_low(PIN_A2); if(s_p[2]==3)output_low(PIN_A3); if(s_p[3]==3)output_low(PIN_A4); if(s_p[4]==3)output_low(PIN_A5); if(s_p[5]==3)output_low(PIN_E0); delay_us(delays); if(s_p[0]==12)output_high(PIN_A1); if(s_p[1]==12)output_high(PIN_A2); if(s_p[2]==12)output_high(PIN_A3); if(s_p[3]==12)output_high(PIN_A4); if(s_p[4]==12)output_high(PIN_A5); if(s_p[5]==12)output_high(PIN_E0); if(s_p[0]==4)output_low(PIN_A1); if(s_p[1]==4)output_low(PIN_A2); if(s_p[2]==4)output_low(PIN_A3); if(s_p[3]==4)output_low(PIN_A4); if(s_p[4]==4)output_low(PIN_A5); if(s_p[5]==4)output_low(PIN_E0); delay_us(delays); if(s_p[0]==13)output_high(PIN_A1); if(s_p[1]==13)output_high(PIN_A2); if(s_p[2]==13)output_high(PIN_A3); if(s_p[3]==13)output_high(PIN_A4); if(s_p[4]==13)output_high(PIN_A5); if(s_p[5]==13)output_high(PIN_E0); if(s_p[0]==5)output_low(PIN_A1); if(s_p[1]==5)output_low(PIN_A2); if(s_p[2]==5)output_low(PIN_A3); if(s_p[3]==5)output_low(PIN_A4); if(s_p[4]==5)output_low(PIN_A5); if(s_p[5]==5)output_low(PIN_E0); delay_us(delays); if(s_p[0]==14)output_high(PIN_A1); if(s_p[1]==14)output_high(PIN_A2); if(s_p[2]==14)output_high(PIN_A3); if(s_p[3]==14)output_high(PIN_A4); if(s_p[4]==14)output_high(PIN_A5); if(s_p[5]==14)output_high(PIN_E0); if(s_p[0]==6)output_low(PIN_A1); if(s_p[1]==6)output_low(PIN_A2); if(s_p[2]==6)output_low(PIN_A3); if(s_p[3]==6)output_low(PIN_A4); if(s_p[4]==6)output_low(PIN_A5); if(s_p[5]==6)output_low(PIN_E0); delay_us(delays); if(s_p[0]==15)output_high(PIN_A1); if(s_p[1]==15)output_high(PIN_A2); if(s_p[2]==15)output_high(PIN_A3); if(s_p[3]==15)output_high(PIN_A4); if(s_p[4]==15)output_high(PIN_A5); if(s_p[5]==15)output_high(PIN_E0); if(s_p[0]==7)output_low(PIN_A1); if(s_p[1]==7)output_low(PIN_A2); if(s_p[2]==7)output_low(PIN_A3); if(s_p[3]==7)output_low(PIN_A4); if(s_p[4]==7)output_low(PIN_A5); if(s_p[5]==7)output_low(PIN_E0); delay_us(delays); printf(lcd_putc,"\n\f%1X%1X%1X%1X%1X%1X%1X%1X%1X%1X%1X%1X\n%s",s_p[0],s_p[1],s_p[2],s_p[3],s_p[4],s_p[5],s_a[0],s_a[1],s_a[2],s_a[3],s_a[4],s_a[5],mode_name); } }
Next Steps
- More patterns should be found for the preset functions.
- Some speakers do not seem to be firing as strongly as others, this makes it difficult to find good patterns.
- Although +/- 5V are going into the final op-amp, the speakers are only getting +/- 2V maximum. We currently cannot determine the cause for this, however it will make the PPOD-mini much more powerful if it can be fixed.
- Interfacing the PIC with MATLAB using RS-232 will allow for more complicated patterns to be obtained.