//ECE 4760, Final Project //Instrument Sonification using the PIC32 //Authors: Mengcheng Qi, Ryan Land //Using: //Protothreads by Adam Dunkels //Fix16, SPI, FM Synthesis, and TFT libraries from Bruce Land and Tahmid Mahbub //Include math.h for Sine wave table calculation #include #include "config.h" //Protothreads #include "pt_cornell_1_2_1.h" //////////////////////////////////// //Graphics #include "tft_master.h" #include "tft_gfx.h" #include //////////////////////////////////// /* ====== MCP4822 control word ============== bit 15 A/B: DACA or DACB Selection bit 1 = Write to DACB 0 = Write to DACA bit 14 ? Don?t Care bit 13 GA: Output Gain Selection bit 1 = 1x (VOUT = VREF * D/4096) 0 = 2x (VOUT = 2 * VREF * D/4096), where internal VREF = 2.048V. bit 12 SHDN: Output Shutdown Control bit 1 = Active mode operation. VOUT is available. ? 0 = Shutdown the selected DAC channel. Analog output is not available at the channel that was shut down. VOUT pin is connected to 500 k???typical)? bit 11-0 D11:D0: DAC Input Data bits. Bit x is ignored. */ // A-channel, 1x, active #define DAC_config_chan_A 0b0011000000000000 #define DAC_config_chan_B 0b1011000000000000 #define Fs 20000.0 #define two32 4294967296.0 // 2^32 // === thread structures ============================================ // thread control structs // note that UART input and output are threads static struct pt pt_cmd, pt_tick; // uart control threads static struct pt pt_input, pt_output, pt_DMA_output, pt_timer, pt_adc; ; static int play_flag; static int measure=128; static int master_counter = 50; static int pitch_multiplier = 0; // system 1 second interval tick int sys_time_seconds ; // === 16:16 fixed point macros ========================================== typedef signed int fix16 ; #define multfix16(a,b) ((fix16)(((( signed long long)(a))*(( signed long long)(b)))>>16)) //multiply two fixed 16:16 #define float2fix16(a) ((fix16)((a)*65536.0)) // 2^16 #define fix2float16(a) ((float)(a)/65536.0) #define fix2int16(a) ((int)((a)>>16)) #define int2fix16(a) ((fix16)((a)<<16)) #define divfix16(a,b) ((fix16)((((signed long long)(a)<<16)/(b)))) #define sqrtfix16(a) (float2fix16(sqrt(fix2float16(a)))) #define absfix16(a) abs(a) #define onefix16 0x00010000 // int2fix16(1) #define sustain_constant float2fix16(256.0/20000.0) ; // seconds per decay update //== Struct for Instrument =========================================== struct Instruments { volatile unsigned int phase_accum_fm, phase_incr_fm; volatile unsigned int phase_accum_main, phase_incr_main; volatile fix16 env_fm, wave_fm, dk_fm, attack_fm, dk_state_fm, attack_state_fm; volatile fix16 env_main, wave_main, dk_main, attack_main, dk_state_main, attack_state_main; volatile fix16 fm_depth, sustain_state, sustain_interval, amplitude; volatile int dk_interval, counter, counter_max, play, pattern, sustain_measures_max, sustain_measures_count, pitch_change_pattern; volatile double ratio; } Instrument_default = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; typedef struct Instruments instrument; //Create our three instrument containers instrument ukulele; instrument drum; instrument violin; //Number of instruments, so the ISR knows what to calculate #define num_instr 3 instrument instr[num_instr]; //Prepare variables for the ISR static int k; volatile fix16 dac_value; //Data that will actually be written to the MCP4822 volatile unsigned int DAC_data; volatile SpiChannel spiChn = SPI_CHANNEL2 ; // the SPI channel to use volatile int spiClkDiv = 2 ; // 20 MHz max speed for this DAC // DDS sine table #define sine_table_size 256 volatile fix16 sine_table[sine_table_size]; // profiling of ISR volatile int isr_time; // Stop flat (used in UART debug) volatile int stop = 0; //============================= //Used for UART transactions char buffer[60]; // ========== ADC =================================================== // define setup parameters for OpenADC10 // Turn module on | output in integer | trigger mode auto | disable auto-sample // ADC_CLK_AUTO -- Internal counter ends sampling and starts conversion (Auto convert) // ADC_AUTO_SAMPLING_OFF -- Sampling begins with AcquireADC10(); #define PARAM1 ADC_FORMAT_INTG16 | ADC_CLK_MANUAL | ADC_AUTO_SAMPLING_OFF // // define setup parameters for OpenADC10 // ADC ref external | disable offset test | disable scan mode | do 1 sample | use single buf | alternate mode off #define PARAM2 ADC_VREF_AVDD_AVSS | ADC_OFFSET_CAL_DISABLE | ADC_SCAN_OFF | ADC_SAMPLES_PER_INT_1 | ADC_ALT_BUF_OFF | ADC_ALT_INPUT_OFF // Define setup parameters for OpenADC10 // use peripheral bus clock | set sample time | set ADC clock divider // ADC_CONV_CLK_Tcy2 means divide CLK_PB by 2 (max speed) // ADC_SAMPLE_TIME_5 seems to work with a source resistance < 1kohm #define PARAM3 ADC_CONV_CLK_PB | ADC_SAMPLE_TIME_5 | ADC_CONV_CLK_Tcy2 //ADC_SAMPLE_TIME_15| ADC_CONV_CLK_Tcy2 // define setup parameters for OpenADC10 // set AN11 and as analog inputs #define PARAM4_AN0 ENABLE_AN0_ANA // pin 26 red #define PARAM4_AN11 ENABLE_AN11_ANA // pin 24 green #define PARAM4_AN5 ENABLE_AN5_ANA // pin 7 blue // define setup parameters for OpenADC10 // do not assign channels to scan #define PARAM5 SKIP_SCAN_ALL //Create arrays to store ADC values in static int adc_blue[3], adc_green[3], adc_red[3]; // right most is red // system 1 second interval tick int sys_time_seconds ; int finished_calculation; void __ISR(_TIMER_2_VECTOR, ipl2) Timer2Handler(void) { mT2ClearIntFlag(); //For each instrument, we calculate its next audio DAC value here //Using all of the FM synthesis equations. //Then we add those up and output to the DAC //So the user will hear all 3 instruments playing simultaneously. for ( k = 0; k < num_instr; k++){ // FM phase pitch_multiplier = instr[k].phase_accum_fm += instr[k].phase_incr_fm; // Main phase instr[k].phase_accum_main += instr[k].phase_incr_main + multfix16(sine_table[instr[k].phase_accum_fm>>24], instr[k].env_fm) ; // Init the exponential decays if (instr[k].play) { instr[k].play = 0; instr[k].dk_state_fm = instr[k].fm_depth; instr[k].dk_state_main = onefix16; instr[k].attack_state_fm = instr[k].fm_depth; instr[k].attack_state_main = onefix16; instr[k].phase_accum_fm = 0; instr[k].phase_accum_main = 0; instr[k].dk_interval = 0; instr[k].sustain_state = 0; instr[k].counter = 0; } else{ if ( instr[k].counter > instr[k].counter_max ){ //If the instrument has reached its time limit, start over instr[k].play = 1; instr[k].sustain_measures_count=0; } } // envelope calculations are 128 times slower than sample rate // computes 4 exponential decays and builds the product envelopes if ((instr[k].dk_interval++ & 0x7f) == 0){ instr[k].dk_state_fm = multfix16(instr[k].dk_state_fm, instr[k].dk_fm) ; instr[k].dk_state_main = multfix16(instr[k].dk_state_main, instr[k].dk_main) ; instr[k].attack_state_fm = multfix16(instr[k].attack_state_fm, instr[k].attack_fm); instr[k].attack_state_main = multfix16(instr[k].attack_state_main, instr[k].attack_main); instr[k].env_fm = multfix16(instr[k].fm_depth-instr[k].attack_state_fm, instr[k].dk_state_fm) ; instr[k].env_main = multfix16(onefix16-instr[k].attack_state_main, instr[k].dk_state_main) ; if(instr[k].sustain_state < instr[k].sustain_interval) { instr[k].dk_state_main = onefix16; instr[k].sustain_state = instr[k].sustain_state + sustain_constant ; } if (stop == 0 ){ instr[k].counter++; }else{ instr[k].counter = 0; } } } // === Channel A ============= // CS low to start transaction mPORTBClearBits(BIT_4); // start transaction // test for ready //while (TxBufFullSPI2()); // write to spi2 WriteSPI2(DAC_data); if (instr[0].play){ //This loop runs ONCE PER MEASURE //We eventually bitwise AND this value with the instrument pattern to silence it if the pattern calls for that. //The "measure" value can take on 1 of 8 values, and it tells us where we are, time wise, in the "pattern." measure=measure>>1; if (measure <1){ measure=128; } } for ( k = 0; k < num_instr; k++){ //We determine DAC values for each instrument instr[k].wave_main = multfix16(multfix16(sine_table[instr[k].phase_accum_main>>24], instr[k].env_main), instr[k].amplitude); //If the pattern of the instrument calls for the instrument to be silent during this measure, we set its value to 0. if (!(measure & instr[k].pattern)){ instr[k].wave_main=0; } } // truncate to 12 bits, read table, convert to int and add offset // We need to add the config to the DAC transmission, as well as the instrument values, plus an offset to avoid negative numbers. DAC_data = DAC_config_chan_A | (fix2int16(instr[0].wave_main) + fix2int16(instr[1].wave_main) + fix2int16(instr[2].wave_main) + 1024) ; // test for done while (SPI2STATbits.SPIBUSY); // wait for end of transaction // CS high mPORTBSetBits(BIT_4); // end transaction isr_time = max(ReadTimer2(), isr_time) ; // - isr_time; } // end ISR TIMER2 void setMuxChannel(int channel){ //This function selects a channel of the CD4051B to read photoresistors. //LSB A if (channel & 0x01){ mPORTASetBits(BIT_2); }else{ mPORTAClearBits(BIT_2); } //2nd LSB B if (channel & 0x02){ mPORTASetBits(BIT_3); }else{ mPORTAClearBits(BIT_3); } //MSB C if (channel & 0x04){ mPORTBSetBits(BIT_3); }else{ mPORTBClearBits(BIT_3); } } //Command thread (used for UART debug early-on.) static PT_THREAD (protothread_cmd(struct pt *pt)) { PT_BEGIN(pt); // The serial interface static char cmd[16]; static float value; // clear port used by thread 4 mPORTBClearBits(BIT_4); while(1) { // send the prompt via DMA to serial sprintf(PT_send_buffer,":) >"); // by spawning a print thread PT_SPAWN(pt, &pt_DMA_output, PT_DMA_PutSerialBuffer(&pt_DMA_output) ); //spawn a thread to handle terminal input // the input thread waits for input // -- BUT does NOT block other threads // string is returned in "PT_term_buffer" PT_SPAWN(pt, &pt_input, PT_GetSerialBuffer(&pt_input) ); // returns when the thead dies // in this case, when is pushed // now parse the string sscanf(PT_term_buffer, "%s %f", cmd, &value); //We used this to test instrument parameters. We discovered a violin, ukulele, and drum sound //Could be created by varying these parameters, then once we settled on instruments, we just //put the values permanently in main(). Our final demo did not use UART. switch(cmd[0]){ case 'f': // frequency 1 is FM; freq 2 is fundamental // freq*2^32/Fs instr[1].ratio = value; break; case 'p': // play instr[0].play = 1; instr[1].play = 1; instr[2].play = 1; stop = 0; break; case 'd': // fm depth instr[1].fm_depth = float2fix16(value); break; case 'u': // sustain instr[1].sustain_interval = float2fix16(value); instr[1].sustain_state = instr[1].sustain_interval; break; case 'k': // decay rate constant 1=FM 2=main if(cmd[1]=='f') instr[1].dk_fm=float2fix16(value); if(cmd[1]=='m') instr[1].dk_main=float2fix16(value); break; case 'a': // attack rate constant 1=FM 2=main if(cmd[1]=='f') instr[1].attack_fm=float2fix16(value); if(cmd[1]=='m') instr[1].attack_main=float2fix16(value); break; case 't': sprintf(PT_send_buffer, "%d\t%d\n\r", isr_time, sys_time_seconds); PT_SPAWN(pt, &pt_DMA_output, PT_DMA_PutSerialBuffer(&pt_DMA_output) ); break; case 's': for ( k = 0; k < num_instr; k++) instr[k].play = 0; play_flag = 0; stop = 1; break; } // never exit while } // END WHILE(1) PT_END(pt); } // thread 3 // === Timer Thread ================================================= // update a 1 second tick counter static PT_THREAD (protothread_timer(struct pt *pt)) { PT_BEGIN(pt); tft_setCursor(0, 0); tft_setTextColor(ILI9340_WHITE); tft_setTextSize(2); tft_writeString("Time: \n"); while(1) { // yield time 1 second PT_YIELD_TIME_msec(1000) ; sys_time_seconds++ ; // draw sys_time tft_fillRoundRect(0, 20, 100, 14, 0, ILI9340_BLACK);// x,y,w,h,radius,color tft_setCursor(0, 20); tft_setTextColor(ILI9340_YELLOW); tft_setTextSize(2); sprintf(buffer,"%d", sys_time_seconds); tft_writeString(buffer); // NEVER exit while } // END WHILE(1) PT_END(pt); } // timer thread // === ADC Thread ================================================= //This thread reads each ADC whenever the ISR isn't running. It also prints to the TFT for debug. static PT_THREAD (protothread_adc(struct pt *pt)) { PT_BEGIN(pt); while(1) { // yield time 1 second PT_YIELD_TIME_msec(10) ; // read 8 photoresister values from mux SetChanADC10( ADC_CH0_NEG_SAMPLEA_NVREF | ADC_CH0_POS_SAMPLEA_AN11 ); int i = 0; //Read each channel of the ADC, but we have 9 photoresistors, so at the end, we will measure directly. for (i = 0; i < 8; i++){ if (i == 0){ // 000 setMuxChannel(0); AcquireADC10(); int j = 200; while (j--); ConvertADC10(); // end sampling & start conversion // wait for complete while (!AD1CON1bits.DONE){}; // Wait for ADC conversion adc_blue[0] = ReadADC10(0); //adc_blue[0] = ReadADC10(0)+100; instr[0].phase_incr_main = adc_blue[0]*(float)two32/Fs; instr[0].phase_incr_fm = adc_blue[0]*instr[0].ratio*(float)two32/Fs; } else if (i == 1){ // 001 setMuxChannel(1); AcquireADC10(); int j = 200; while (j--); ConvertADC10(); // end sampling & start conversion // wait for complete while (!AD1CON1bits.DONE){}; // Wait for ADC conversion //Calibration: //Green is too powerful; tame it down adc_green[0] = ReadADC10(0)>>1; instr[0].counter_max = 5000/adc_green[0]; instr[1].counter_max = 5000/adc_green[0]; instr[2].counter_max = 5000/adc_green[0]; } else if (i == 2){ // 010 setMuxChannel(2); AcquireADC10(); int j = 200; while (j--); ConvertADC10(); // end sampling & start conversion // wait for complete while (!AD1CON1bits.DONE){}; // Wait for ADC conversion adc_red[0] = ReadADC10(0); if ( adc_red[0] < 10) instr[0].amplitude = 0; else instr[0].amplitude = divfix16(int2fix16(adc_red[0]), int2fix16(820))+divfix16(int2fix16(1), int2fix16(4)); } else if (i == 3){ // 011 setMuxChannel(3); AcquireADC10(); int j = 100; while (j--); ConvertADC10(); // end sampling & start conversion // wait for complete while (!AD1CON1bits.DONE){}; // Wait for ADC conversion adc_blue[1] = ReadADC10(0); instr[1].phase_incr_main = adc_blue[1]*(float)two32/Fs; instr[1].phase_incr_fm = adc_blue[1]*instr[1].ratio*(float)two32/Fs; } else if (i == 4){ // 100 setMuxChannel(4); AcquireADC10(); int j = 200; while (j--); ConvertADC10(); // end sampling & start conversion // wait for complete while (!AD1CON1bits.DONE){}; // Wait for ADC conversion //Calibration: //Green is too powerful; tame it down adc_green[1] = ReadADC10(0)>>1; } else if (i == 5){ // 101 setMuxChannel(5); AcquireADC10(); int j = 200; while (j--); ConvertADC10(); // end sampling & start conversion // wait for complete while (!AD1CON1bits.DONE){}; // Wait for ADC conversion adc_red[1] = ReadADC10(0); if ( adc_red[1] < 10) instr[1].amplitude = 0; else instr[1].amplitude = divfix16(int2fix16(adc_red[1]), int2fix16(820))+divfix16(int2fix16(1), int2fix16(4)); } else if (i == 6){ // 110 setMuxChannel(6); AcquireADC10(); int j = 200; while (j--); ConvertADC10(); // end sampling & start conversion // wait for complete while (!AD1CON1bits.DONE){}; // Wait for ADC conversion adc_blue[2] = ReadADC10(0); } else if (i == 7){ // 111 setMuxChannel(7); AcquireADC10(); int j = 200; while (j--); ConvertADC10(); // end sampling & start conversion // wait for complete while (!AD1CON1bits.DONE){}; // Wait for ADC conversion //Calibration: //Green is too powerful; tame it down adc_green[2] = ReadADC10(0)>>1; } } //Do stuff to instrument patterns based on ADC Thresholds if (adc_blue[0] < 100){ instr[0].pattern = 0b10101010; }else if (adc_blue[0] >= 100 && adc_blue[0] < 600){ instr[0].pattern = 0b11011000; }else if (adc_blue[0] >=600){ instr[0].pattern = 0b10010011; } if (adc_blue[1] < 450){ instr[1].pattern = 0b11101110; }else if (adc_blue[1] >= 650 && adc_blue[2] < 750){ instr[1].pattern = 0b10000000; }else if (adc_blue[1] >=750){ instr[1].pattern = 0b10011110; } if (adc_blue[2] < 150){ instr[2].pattern = 0b10101010; }else if (adc_blue[2] >= 150 && adc_blue[2] < 350){ instr[2].pattern = 0b01111101; }else if (adc_blue[2] >=350){ instr[2].pattern = 0b11111011; } // Here, we read the 9th photoresistor directly into AN0. SetChanADC10( ADC_CH0_NEG_SAMPLEA_NVREF | ADC_CH0_POS_SAMPLEA_AN0 ); AcquireADC10(); int j = 2000; while (j--); ConvertADC10(); // end sampling & start conversion // wait for complete while (!AD1CON1bits.DONE){}; // Wait for ADC conversion //Calibration: //Double ADC red 2 because it's weak adc_red[2] = ReadADC10(0)<<1; if ( adc_red[2] < 10) instr[2].amplitude = 0; else instr[2].amplitude = divfix16(int2fix16(adc_red[2]), int2fix16(820))+divfix16(int2fix16(1), int2fix16(4)); //TFT Debug purposes // print ADC value of red tft_fillRoundRect(0, 40, 240, 14, 0, ILI9340_BLACK);// x,y,w,h,radius,color tft_setCursor(0, 40); tft_setTextColor(ILI9340_BLUE); tft_setTextSize(2); sprintf(buffer,"B %d %d %d", adc_blue[0], adc_blue[1], adc_blue[2]); tft_writeString(buffer); // print ADC value of red tft_fillRoundRect(0, 60, 240, 14, 0, ILI9340_BLACK);// x,y,w,h,radius,color tft_setCursor(0, 60); tft_setTextColor(ILI9340_GREEN); tft_setTextSize(2); sprintf(buffer,"G %d %d %d", adc_green[0], adc_green[1], adc_green[1]); tft_writeString(buffer); // print ADC value of red tft_fillRoundRect(0, 80, 240, 14, 0, ILI9340_BLACK);// x,y,w,h,radius,color tft_setCursor(0, 80); tft_setTextColor(ILI9340_RED); tft_setTextSize(2); sprintf(buffer,"R %d %d %d", adc_red[0], adc_red[1], adc_red[2]); tft_writeString(buffer); //Draw Bar Graphs tft_fillRoundRect(0, 120, 240, 50, 0, ILI9340_BLACK);// x,y,w,h,radius,color tft_fillRoundRect(0, 120, adc_red[0]>>2, 2, 0, ILI9340_RED);// x,y,w,h,radius,color tft_fillRoundRect(0, 125, adc_green[0]>>2, 2, 0, ILI9340_GREEN);// x,y,w,h,radius,color tft_fillRoundRect(0, 130, adc_blue[0]>>2, 2, 0, ILI9340_BLUE);// x,y,w,h,radius,color tft_fillRoundRect(0, 135, adc_red[1]>>2, 2, 0, ILI9340_RED);// x,y,w,h,radius,color tft_fillRoundRect(0, 140, adc_green[1]>>2, 2, 0, ILI9340_GREEN);// x,y,w,h,radius,color tft_fillRoundRect(0, 145, adc_blue[1]>>2, 2, 0, ILI9340_BLUE);// x,y,w,h,radius,color tft_fillRoundRect(0, 150, adc_red[2]>>2, 2, 0, ILI9340_RED);// x,y,w,h,radius,color tft_fillRoundRect(0, 155, adc_green[2]>>2, 2, 0, ILI9340_GREEN);// x,y,w,h,radius,color tft_fillRoundRect(0, 160, adc_blue[2]>>2, 2, 0, ILI9340_BLUE);// x,y,w,h,radius,color // NEVER exit while } // END WHILE(1) PT_END(pt); } // timer thread // === Main ====================================================== void main(void) { //Initialize the instruments based on what we discovered during debug. instr[0] = ukulele; instr[0] = Instrument_default; instr[0].dk_fm = float2fix16(0.98); instr[0].dk_main = float2fix16(0.93); instr[0].fm_depth = float2fix16(1.75); instr[0].ratio = 0.25; instr[0].phase_incr_fm = instr[0].ratio*824.0*two32/Fs; instr[0].phase_incr_main = 824.0*two32/Fs; instr[0].counter_max = 50; instr[0].pattern = 0b11101100; instr[0].sustain_measures_max = 1; instr[1] = violin; instr[1] = Instrument_default; instr[1].dk_fm = float2fix16(0.99); instr[1].dk_main = float2fix16(0.9); instr[1].sustain_interval = float2fix16(0.2); instr[1].fm_depth = float2fix16(0.2); instr[1].ratio = 8; instr[1].phase_incr_fm = instr[1].ratio*824.0*two32/Fs; instr[1].phase_incr_main = 824.0*two32/Fs; instr[1].counter_max = 50; instr[1].pattern=0b10101010; instr[1].sustain_measures_max = 5; instr[2] = drum; instr[2] = Instrument_default; instr[2].dk_fm = float2fix16(0.99); instr[2].dk_main = float2fix16(0.9); instr[2].fm_depth = float2fix16(8); instr[2].ratio = 0.25; instr[2].phase_incr_fm = instr[2].ratio*824.0*two32/Fs; instr[2].phase_incr_main = 824.0*two32/Fs; instr[2].counter_max = 50; instr[2].pattern=0b10101010; instr[2].sustain_measures_max = 1; instr[2].pitch_change_pattern = 0b00000000000000000000000000000000; ANSELA = 0; ANSELB = 0; // === config the uart, DMA, vref, timer5 ISR ============= PT_setup(); // === setup ADC ================================================== // configure and enable the ADC CloseADC10(); // ensure the ADC is off before setting the configuration // use ground as neg ref for A | use AN11 for input A // configure to sample AN11 SetChanADC10( ADC_CH0_NEG_SAMPLEA_NVREF | ADC_CH0_POS_SAMPLEA_AN11 ); OpenADC10( PARAM1, PARAM2, PARAM3, PARAM4_AN0, PARAM5 ); // configure #8 OpenADC10( PARAM1, PARAM2, PARAM3, PARAM4_AN11, PARAM5 ); // configure #0-7 EnableADC10(); // Enable the ADC // === Timer Interrupt ================================================== // Set up timer2 on, interrupts, internal clock, prescalar 1, toggle rate // at 30 MHz PB clock 60 counts is two microsec // 400 is 100 ksamples/sec // 2000 is 20 ksamp/sec // 1000 is 40 ksample/sec // 2000 is 20 ks/sec OpenTimer2(T2_ON | T2_SOURCE_INT | T2_PS_1_1, 2000); // set up the timer interrupt with a priority of 2 ConfigIntTimer2(T2_INT_ON | T2_INT_PRIOR_2); mT2ClearIntFlag(); // and clear the interrupt flag // === setup DAC ================================================== // SCK2 is pin 26 // SDO2 (MOSI) is in PPS output group 2, could be connected to RB5 which is pin 14 PPSOutput(2, RPB5, SDO2); // control CS for DAC mPORTBSetPinsDigitalOut(BIT_4); mPORTBSetBits(BIT_4); // === SPI for UART PUTTY ================================================== // divide Fpb by 2, configure the I/O ports. Not using SS in this example // 16 bit transfer CKP=1 CKE=1 // possibles SPI_OPEN_CKP_HIGH; SPI_OPEN_SMP_END; SPI_OPEN_CKE_REV // For any given peripherial, you will need to match these SpiChnOpen(spiChn, SPI_OPEN_ON | SPI_OPEN_MODE16 | SPI_OPEN_MSTEN | SPI_OPEN_CKE_REV , spiClkDiv); // === setup system wide interrupts =============================== INTEnableSystemMultiVectoredInt(); // init the threads PT_INIT(&pt_cmd); PT_INIT(&pt_timer); PT_INIT(&pt_adc); // turn off the sustain until triggered for ( k = 0; k < num_instr; k++){ instr[k].sustain_state = float2fix16(100.0); } // build the sine lookup table // scaled to produce values between 0 and 1024 int i; for (i = 0; i < sine_table_size; i++){ sine_table[i] = float2fix16(1024*sin((float)i*6.283/(float)sine_table_size)); } // init the display tft_init_hw(); tft_begin(); tft_fillScreen(ILI9340_BLACK); //240x320 vertical display // schedule the threads while(1) { // round robin PT_SCHEDULE(protothread_cmd(&pt_cmd)); PT_SCHEDULE(protothread_timer(&pt_timer)); PT_SCHEDULE(protothread_adc(&pt_adc)); } } // main // === end ======================================================