/*Author: Ben Sheffer Date Modified: 04/15/2018 Description: This project is meant to test and develop the water effect which is likely to be used for The Dancing Dots project. At the moment, we have no audio input, so are going to just test the rendering speed required to render a frame while running the mesh of masses simulation (i.e. numerically solving the system of the equations of motion for a 40x100 mesh of masses each coupled to its nearest neighbor by a linear force)*/ // PIC32MZ0512EFE064 Configuration Bit Settings // 'C' source line config statements // DEVCFG3 // USERID = No Setting #pragma config FMIIEN = OFF // Ethernet RMII/MII Enable (RMII Enabled) #pragma config FETHIO = OFF // Ethernet I/O Pin Select (Alternate Ethernet I/O) #pragma config PGL1WAY = ON // Permission Group Lock One Way Configuration (Allow only one reconfiguration) #pragma config PMDL1WAY = ON // Peripheral Module Disable Configuration (Allow only one reconfiguration) #pragma config IOL1WAY = ON // Peripheral Pin Select Configuration (Allow only one reconfiguration) #pragma config FUSBIDIO = OFF // USB USBID Selection (Controlled by Port Function) // DEVCFG2 #pragma config FPLLIDIV = DIV_1 // System PLL Input Divider (1x Divider) #pragma config FPLLRNG = RANGE_34_68_MHZ// System PLL Input Range (34-68 MHz Input) #pragma config FPLLICLK = PLL_FRC // System PLL Input Clock Selection (FRC is input to the System PLL) #pragma config FPLLMULT = MUL_50 // System PLL Multiplier (PLL Multiply by 50) #pragma config FPLLODIV = DIV_2 // System PLL Output Clock Divider (2x Divider) #pragma config UPLLFSEL = FREQ_24MHZ // USB PLL Input Frequency Selection (USB PLL input is 24 MHz) // DEVCFG1 #pragma config FNOSC = SPLL // Oscillator Selection Bits (System PLL) #pragma config DMTINTV = WIN_127_128 // DMT Count Window Interval (Window/Interval value is 127/128 counter value) #pragma config FSOSCEN = OFF // Secondary Oscillator Enable (Disable SOSC) #pragma config IESO = ON // Internal/External Switch Over (Enabled) #pragma config POSCMOD = OFF // Primary Oscillator Configuration (Primary osc disabled) #pragma config OSCIOFNC = OFF // CLKO Output Signal Active on the OSCO Pin (Disabled) #pragma config FCKSM = CSECME // Clock Switching and Monitor Selection (Clock Switch Enabled, FSCM Enabled) #pragma config WDTPS = PS1048576 // Watchdog Timer Postscaler (1:1048576) #pragma config WDTSPGM = STOP // Watchdog Timer Stop During Flash Programming (WDT stops during Flash programming) #pragma config WINDIS = NORMAL // Watchdog Timer Window Mode (Watchdog Timer is in non-Window mode) #pragma config FWDTEN = OFF // Watchdog Timer Enable (WDT Disabled) #pragma config FWDTWINSZ = WINSZ_25 // Watchdog Timer Window Size (Window size is 25%) #pragma config DMTCNT = DMT31 // Deadman Timer Count Selection (2^31 (2147483648)) #pragma config FDMTEN = OFF // Deadman Timer Enable (Deadman Timer is disabled) // DEVCFG0 #pragma config DEBUG = OFF // Background Debugger Enable (Debugger is disabled) #pragma config JTAGEN = OFF // JTAG Enable (JTAG Disabled) #pragma config ICESEL = ICS_PGx1 // ICE/ICD Comm Channel Select (Communicate on PGEC1/PGED1) #pragma config TRCEN = OFF // Trace Enable (Trace features in the CPU are disabled) #pragma config BOOTISA = MIPS32 // Boot ISA Selection (Boot code and Exception code is MIPS32) #pragma config FECCCON = OFF_UNLOCKED // Dynamic Flash ECC Configuration (ECC and Dynamic ECC are disabled (ECCCON bits are writable)) #pragma config FSLEEP = OFF // Flash Sleep Mode (Flash is powered down when the device is in Sleep mode) #pragma config DBGPER = PG_ALL // Debug Mode CPU Access Permission (Allow CPU access to all permission regions) #pragma config SMCLR = MCLR_NORM // Soft Master Clear Enable bit (MCLR pin generates a normal system Reset) #pragma config SOSCGAIN = GAIN_2X // Secondary Oscillator Gain Control bits (2x gain setting) #pragma config SOSCBOOST = ON // Secondary Oscillator Boost Kick Start Enable bit (Boost the kick start of the oscillator) #pragma config POSCGAIN = GAIN_2X // Primary Oscillator Gain Control bits (2x gain setting) #pragma config POSCBOOST = ON // Primary Oscillator Boost Kick Start Enable bit (Boost the kick start of the oscillator) #pragma config EJTAGBEN = NORMAL // EJTAG Boot (Normal EJTAG functionality) // DEVCP0 #pragma config CP = OFF // Code Protect (Protection Disabled) // SEQ3 // DEVADC0 // DEVADC1 // DEVADC2 // DEVADC3 // DEVADC4 // DEVADC7 // #pragma config statements should precede project file includes. // Use project enums instead of #define for ON and OFF. #include #include #include #include #include "ws2812b.h" #include "gradient.h" #include "frames.h" //define some parameters for the mesh simulation #define m 0.005 //the mass of each bead on our string #define T 55.0 //the tension in the strings in our string of beads #define a 10.0 //the distance between masses on the string #define gamm .0185 //define the damping constant in our string of beads (without this the waves will reflect off the free end and continue forever) #define error 0.0 //this allows the termination to be imperfect (some reflections) which can give cool effects #define omega 5.0 //angular freq of driving force (later will be replaced with user input) #define AMin -a/100.0 #define AMax a/100.0 #define A AMax/2.0 //amplitude of driving force (later will be replaced with user input) #define brightnessMax 1000.0 //(out of 1000) #define threshold 2200 int img = 0; void _nmi_handler (void){} //void _simple_tlb_refill_exception_handler(void){} void _cache_err_exception_handler(void){} //void _general_exception_handler(void){} //void _simple_tlb_refill_exception_handler(void){} //void _general_exception_handler(void){} float positions[width][height] = {0}; float velocities[width][height] = {0}; long mapl(long x, long in_min, long in_max, long out_min, long out_max) { return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min; } float mapf(float x, float in_min, float in_max, float out_min, float out_max) { return ((x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min); } void delay(long t){ /*each t corresponds to 2 instructions, I think so to get ms*/ t = t*10000; while(t > 0){ t--; } } #define analogMax 4095 //12 bit res, 2^12 - 1 = 4095 void analogInit(){ //initialize all of the ADC features we need //1. configure ADC pin ANSELGbits.ANSG8 = 1; //enable analog feature of G8 (AN12) TRISGbits.TRISG8 = 1; //set this pin as an input //I believe this analog input is on ADC7, the shared ADC module. //2. transfer ADC calibration data to configuration registers ADC0CFG = DEVADC0; ADC1CFG = DEVADC1; ADC2CFG = DEVADC2; ADC3CFG = DEVADC3; ADC4CFG = DEVADC4; ADC7CFG = DEVADC7; //3. select multiplexer inputs ADCCON1 = 0; //disable most of the fancy features we don't want to fuck with ADCCON1bits.SELRES = 3; //resolution for ADC7 is 12 bits ADCCON1bits.STRGSRC = 1; //select scan trigger //4. select data format of output ADCIMCON1bits.SIGN12 = 0; //unsigned data format for AN12 ADCIMCON1bits.DIFF12 = 0; //single ended mode for AN12 //5. select conversion trigger source //There is no place in the ADCTRG registers for AN12, so I assume it is automatically set to scan trigger ADCCSS1 = 0; ADCCSS2 = 0; //clear all bits on the scan ADCCSS1bits.CSS12 = 1; //add AN12 to the scan (I think this should also cause it to trigger on a scan...?) //6. select reference voltages ADCCON3bits.VREFSEL = 1; //reference voltage source as AVDD and AVSS //7. select clock source and prescalers ADCCON3bits.ADCSEL = 3; //set clock source as FRC clock ADCCON3bits.CONCLKDIV = 1; //divide by 2 prescale ADCCON2bits.SAMC = 512; //sample time is 5*TAD7 ADCCON2bits.ADCDIV = 0b0011111; //TAD7 is half of input clock period ADCANCON = 0; ADCANCONbits.WKUPCLKCNT = 5; //wakeup exponent = 32 * TAD7 //8. disable unused interrupts ADCEIEN1 = 0; ADCEIEN2 = 0; //9. disable unused filters and comparators ADCCMPCON1 = 0; ADCCMPCON2 = 0; ADCCMPCON3 = 0; ADCCMPCON4 = 0; ADCCMPCON5 = 0; ADCCMPCON6 = 0; ADCFLTR1 = 0; ADCFLTR2 = 0; ADCFLTR3 = 0; ADCFLTR4 = 0; ADCFLTR5 = 0; ADCFLTR6 = 0; //9. turn on ADC ADCCON1bits.ON = 1; //turn on adc while(!ADCCON2bits.BGVRRDY); //wait until reference voltage is ready while(ADCCON2bits.REFFLT); //wait if there is a fault with the reference voltage ADCANCONbits.ANEN7 = 1; //enable clock to ADC7 while(!ADCANCONbits.WKRDY7); //wait until ADC7 is ready ADCCON3bits.DIGEN7 = 1; //enable ADC7 } void initStatePins(){ /*the demo mode will be set by a jumper on these pins*/ ANSELGbits.ANSG9 = 0; TRISGbits.TRISG9 = 1; CNPUGbits.CNPUG9 = 1; //enable internal week pull up on this pin ANSELGbits.ANSG7 = 0; TRISGbits.TRISG7 = 1; CNPUGbits.CNPUG7 = 1; } unsigned int analogRead(){ /*samples AN12 and returns the result (configuration controlled by analogInit*/ ADCCON3bits.GSWTRG = 1; //trigger a conversion via global software trigger while(ADCDSTAT1bits.ARDY12 == 0); //wait for conversion to complete return ADCDATA12; //return the analog reading } void bitMapDisp(const unsigned int frame[250]){ /*Adds bitmap image to color buffer (input parameter is frame) bit map formatting is as follows: -list of all pixels in image with no interruptions -moves from from top to bottom -moves left to right color formatting: -moves from bottom to top -moves from right to left (direction is exact opposite of bit map format...*/ int pos = 0, bitPos = 15, bitMapPos = 0, working = 0, x = 0, y=0, ind = 0; float globalBrightness = 0.2; //the brightness we'll set the pixels to (we'll start out making them all white) for(pos = 0; pos < ledLen; pos++){ working = frame[bitMapPos]; //select the correct variable from the bitmap x = 40-(pos % 40); y = 100-floor(pos/40); ind = getPixelIndex(x, y); if((working & (1 << bitPos)) != 0){ //if the bit for the corresponding positions is set, illuminate that pixel if(img == 0){ c[0][ind] = (unsigned char)(255.0*globalBrightness); c[1][ind] = (unsigned char)(255.0*globalBrightness); c[2][ind] = (unsigned char)(100.0*globalBrightness); } }else{ //otherwise, turn it off c[0][ind] = 0; c[1][ind] = 0; c[2][ind] = 0; } bitPos--; //increment to the next bit if(bitPos < 0){ //if we have already used all the bits in that int... bitPos = 15; //move to the next int in the frame bitMapPos++; } } } void init(){ /*initialize the chip and variables and what not*/ ws2812MultiInit(); //initialize the micro to transmit data on multiple lines (13 to be exact) analogInit(); //initialize the ADC module so we can drive the simulation with readings from a microphone (or other analog sensors) initStatePins(); srand(20); long color = 0; int ind = 0; //set all the values of the pixels to zero to start out with int i = 0, j = 0; //use these to iterate through every element in the simulation for(i = 0; i < width; i++){ for(j = 0; j < height; j++){ positions[i][j] = 0;//mapf((float)rand(), 0.0, (float)RAND_MAX, AMin, AMax); velocities[i][j] = 0; ind = (int)mapf((double)positions[i][j], AMin, AMax, 0.0, (float)(gradLen-1)); float brightness = pow((mapf(fabs(positions[i][j]),0, AMax, 0.0,brightnessMax)/1000.0),1); color = colorScaleOG[ind]; //get the color RBG value from the stored color gradient file ind = getPixelIndex(i, j); c[0][ind] = (unsigned char)(brightness* ((color & 0x00FF0000) >> 16)); c[1][ind] = (unsigned char)(brightness * ((color & 0x0000FF00) >> 8)); c[2][ind] = (unsigned char)(brightness* (color & 0x000000FF)); } } positions[19][49] = AMax; } void simStep(float tstep){ /*this function calculates the positions and velocities after one time step using forward euler's method*/ //iterate through every element in the simulation int i = 0, j = 0, ind = 0; long color = 0; float positionTerm = 0, force = 0; for(i = 0; i < width; i++){ for(j = 0; j < height; j++){ //calculate the force on this element based on its and its neighbors positions positionTerm = 0; //use this to keep track of the forces contributed by tensions (i.e. the position dependent component) if(i > 0) positionTerm += positions[i][j] - positions[i-1][j]; if(i < (width - 1)) positionTerm += positions[i][j] - positions[i+1][j]; if(j > 0) positionTerm += positions[i][j] - positions[i][j-1]; if(j < (height - 1)) positionTerm += positions[i][j] - positions[i][j+1]; if(positions[i][j] > AMax) positions[i][j] = AMax; if(positions[i][j] < AMin) positions[i][j] = AMin; force = -(T/a)*positionTerm - gamm*velocities[i][j]; //calculate the force as the sum of the z-component of the tensions minus the damping (gamma*v) //update the positions and velocities positions[i][j] = positions[i][j] + velocities[i][j]*tstep + 0.5*(force/m)*tstep*tstep; //get new position via second order taylor expansion about current point velocities[i][j] = velocities[i][j] + (force/m)*tstep; //get new velocity via first order taylor expansion about current point //now we need to assign colors to the pixels according to the position and motion of the LEDs. ind = (int)mapf((float)positions[i][j], AMin, AMax, 0.0, (float)(gradLen-1)); float brightness = pow((mapf(fabs(positions[i][j]),0, AMax, 0.0,1000.0)/1000.0), 1); /*the following is a picture of a reflection of the Sheraton and surrounding buildings * reflected in the charles as looking from the cambridge side at the harvard bridge*/ /* if(j > 85){ brightness = 0.0; if((i>5 && i < 10)){ brightness = 0.3; color = colorScaleSodiumYellow[1000]; }else if(i>14 && i<20){ brightness = 0.3; color = colorScaleXenonWhite[1000]; }else if(i>12 && i <=14){ brightness = 0.3; color = colorScaleLime[1000]; }else if(i > 27 && i < 35){ brightness = 0.3; color = colorScaleRed[1000]; } } if((j > 20) && (i>5 && i < 10)){ color = colorScaleSodiumYellow[ind]; //get the color RBG value from the stored color gradient file if(brightness < 0.02) brightness = 0.02; } else if((j > 20) && (i>12 && i<20)){ color = colorScaleXenonWhite[ind]; if(i>12 && i<=14) color = colorScaleLime[ind]; if(brightness < 0.02) brightness = 0.02; } else if((j > 35) && (i>27 && i <35)){ color = colorScaleRed[ind]; if(brightness < 0.02) brightness = 0.02; } else{ color = colorScaleNight[ind]; brightness = 0.9*brightness; }*/ color = colorScaleBlue[ind]; //color = colorScaleXenonWhite[ind]; ind = getPixelIndex(i, j); c[1][ind] = (unsigned char)(brightness* ((color & 0x00FF0000) >> 16)); c[2][ind] = (unsigned char)(brightness * ((color & 0x0000FF00) >> 8)); c[0][ind] = (unsigned char)(brightness* (color & 0x000000FF)); } } } int state = 0, test, maxFrameNo = 38; int main(void){ init(); int counter = 0; int frameNo = 0; while(1){ if(PORTGbits.RG9 == 1){ state = 0; }else{ state = 1; } if(PORTGbits.RG7 == 1){ img = 0; maxFrameNo = 38; }else{ img = 1; maxFrameNo = 199; } //int i = 0; //test = 0; // for(i = 0; i<1; i++){ //test+= analogRead(); //} // test = test/1; if(counter > 70){ //positions[0][99-(test/100)] = mapf((float)(analogMax-test), 0.0, (float)(analogMax-threshold), AMin/2, AMax/2);//mapf((float)rand(), 0.0, (float)RAND_MAX, AMin, AMax);; positions[rand()%40][rand()%100] = mapf((float)rand(), 0.0, (float)RAND_MAX, AMin, AMax);; if(state == 1 && img == 1)positions[rand()%40][rand()%100] = AMax; counter = 0; } simStep(.0015); if(state == 1){ if(img == 0) bitMapDisp((suz[frameNo])); else if(img == 1) bitMapDisp((ultimateChicken[frameNo])); } // test = analogRead(); // if(test > 600) c[2][50] = (unsigned char)(test/10); ws2812MultiSend(); ws2812MultiReset(); //delay(5000); if(state == 1){ frameNo++; if(frameNo >= maxFrameNo) frameNo = 0; } counter++; // int i = 0, j = 0, ind; // for(i = 0; i < 40; i++){ // for(j = 0; j < 28; j++){ // ind = getPixelIndex(i,j); // c[0][ind] = (((colorScaleOG[i]) & 0x000000FF))/4; // c[1][ind] = (((colorScaleOG[i]) & 0x00FF0000)>>16)/4; // c[2][ind] = (((colorScaleOG[i]) & 0x0000FF00)>>8)/4; // ws2812MultiSend(); // ws2812MultiReset(); // } // } // i = 0, j = 0; // for(i = 0; i < 40; i++){ // for(j = 0; j < 28; j++){ // ind = getPixelIndex(i,j); // c[0][ind] = 0; // c[1][ind] = 0; // c[2][ind] = 0; // ws2812MultiSend(); // ws2812MultiReset(); // } // } // for(i = 0; i < 1200; i++){ // c[0][i] = (((colorScaleOG[i]) & 0x000000FF))/4; // c[1][i] = (((colorScaleOG[i]) & 0x00FF0000)>>16)/4; // c[2][i] = (((colorScaleOG[i]) & 0x0000FF00)>>8)/4; // ws2812MultiSend(); // ws2812MultiReset(); // } // i = 0, j = 0, ind; // for(i = 0; i < 1200; i++){ // c[0][i] = 0; // c[1][i] = 0; // c[2][i] = 0; // ws2812MultiSend(); // ws2812MultiReset(); // } } }