Digital Sound Synthesizer for lab 4

Dependencies:   LSM9DS1_Library_cal PinDetect mbed-rtos mbed

Revision:
0:0d977b83a68d
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/main.cpp	Tue Mar 15 21:27:30 2016 +0000
@@ -0,0 +1,207 @@
+#include "mbed.h"
+#include "rtos.h"
+#include "mpr121.h"
+#include "PinDetect.h"
+#include "LSM9DS1.h"
+
+// Sound waveform synthesizer
+// The program precomputes 128 data points on one wave cycle 
+// for triangular, sawtooth, and sine waves. These are stored 
+// and outout to the PWM to vary the PWM duty cycle. A very high 
+// PWM frequency is used and the frequency of the output soundwave 
+// is changed by changing the sampling rate. The program uses the 
+// IMU to shift frequencies, the keypad to choose center frequencies, 
+// the RGB LED to visually display pitch, and a pushbutton to switch between wave modes.
+
+#define PI 3.141592653589793238462
+
+InterruptIn irq(p11);
+I2C i2c1(p9,p10);
+Mpr121 touchpad(&i2c1, Mpr121::ADD_VSS); 
+PwmOut PWM(p26);
+Serial pc(USBTX,USBRX);
+LSM9DS1 IMU(p28, p27, 0xD6, 0x3C);
+Mutex stdio_mutex; 
+
+PwmOut RGBLED_r(p21);
+PwmOut RGBLED_g(p22);
+PwmOut RGBLED_b(p23);
+
+PinDetect pb1(p17);
+DigitalOut led1(LED1);
+DigitalOut led2(LED2);
+DigitalOut led3(LED3);
+DigitalOut led4(LED4);
+
+//Global variables used by interrupt routines and threads
+volatile int i=0;
+volatile int j=0;
+float sineInit[128];
+float sawInit[128];
+float triInit[128];
+volatile float baseFreq = 440;
+float maxDeltaFreq = 200;
+float maxDeltaXAxis = 15;
+volatile float freq = 900;
+volatile float deltaFreq = 0;
+volatile float deltaXAxis = 0;
+volatile float deltaXAxisOld = 0;          
+float frequencyScales[12] = {261.626, 293.665, 329.628, 349.228, 391.995, 440.0,
+                             493.883, 523.251, 587.33, 659.255, 783.991, 880};
+
+enum Waveforms {sine=0,sawTooth,triangle};
+Waveforms wave = sine;
+
+//Samples IMU every .5 seconds and adjusts frequencies accordingly
+void sample_IMU(void const *args)
+{
+    while(1) {
+        if(IMU.accelAvailable()) {
+            stdio_mutex.lock();
+            deltaXAxisOld = deltaXAxis;
+            deltaXAxis = (0.0024*IMU.readAccel(X_AXIS)); //Measures difference of IMU from base value of 0
+            stdio_mutex.unlock();
+        }
+        Thread::wait(500);
+    }
+}
+
+//Plays the current waveform at the current frequency
+void playSineWave() {
+    while(j<150) {  //Controls how many cycles. Plays about 3 full cycles
+        switch(wave) {
+            case sine:
+                PWM = (sineInit[i]);
+            break;
+            case sawTooth:
+                PWM = sawInit[i];
+            break;
+            case triangle:
+                PWM = triInit[i];
+            break;
+            default:
+                led2 = 0;
+                led3 = 0;
+                led4 = 0;
+            break;
+        }
+        //(1.0 + sin((float(i)*freq*6.28318530717959/(128.0))))/2.0);
+        // increment pointer and wrap around back to 0 at 128
+        if(i>60) {
+            j++;
+        }
+        i = (i+1) & 0x07F;
+        wait_us(1000000.0/((freq+deltaFreq)*128));
+    }
+    
+    //At the end of 3 cycles, reset counters
+    i=0;
+    j=0;
+    
+    int value=touchpad.read(0x00);
+    value +=touchpad.read(0x01)<<8;
+    if (value == 0) {
+        //Reset values if keypad is not being touched
+        PWM = 0;
+        deltaFreq = 0;
+        deltaXAxis = 0;
+        RGBLED_r = 0;
+        RGBLED_g = 0;
+        RGBLED_b = 0;
+    } else {
+        //If the keypad has a nonzero value, update the frequency and play more cycles
+        stdio_mutex.lock();
+        float diff = (abs(deltaXAxis - deltaXAxisOld) > 2) ? deltaXAxis : deltaXAxisOld; //Makes the delta frequency resistant to noise in the IMU
+        stdio_mutex.unlock();
+        
+        //Calculates delta frequency from center frequency based off of IMU accelerometer
+        if(abs(diff) > maxDeltaXAxis)
+            deltaFreq = maxDeltaFreq;
+        else {
+            deltaFreq = diff/maxDeltaXAxis*maxDeltaFreq;
+        }
+        
+        RGBLED_r = abs(freq-deltaFreq-220)/880;
+        RGBLED_g = abs(freq-220)/880;
+        RGBLED_b = abs(freq+deltaFreq-220)/880;;
+        playSineWave();
+    }
+}
+
+//Called when the touchpad is tapped
+void fallInterrupt() {
+    int key_code=0;
+    int n=0;
+    int value=touchpad.read(0x00);
+    value +=touchpad.read(0x01)<<8;
+    for (n=0; n<12; n++) {
+        if (((value>>n)&0x01)==1) {
+            key_code=n+1;
+        }
+    }
+    //sets frequency based on the tapped value
+    freq = frequencyScales[key_code-1];
+    if (value != 0) {
+        playSineWave();
+    }
+}
+
+//Switches the current waveform and leds
+void pb1_released_callback(void){
+    if (wave == triangle) {
+        led2 = 1;
+        led3 = 0;
+        led4 = 0;
+        wave = sine;
+    } else if (wave == sine) {
+        led2 = 0;
+        led3 = 1;
+        led4 = 0;
+        wave = sawTooth;
+    } else {
+        led2 = 0;
+        led3 = 0;
+        led4 = 1;
+        wave = triangle;
+    }
+}
+
+int main()
+{
+    pc.printf("Program starting.");
+    PWM.period(1.0/200000.0);
+    led2 = 1;
+    led3 = 0;
+    led4 = 0;
+    
+    // Precompute 128 sample points on one wave cycle for three wave forms
+    // used for continuous wave output later
+    for(int k=0; k<128; k++) {
+        sineInit[k] = 1.0*((1.0 + sin((float(k)/128.0*2*PI)))/2.0);
+        sawInit[k] = (1.0 + ((-2.0*1.0/PI)*atan(cos(float(k)*PI/128.0)/sin(float(k)*PI/128.0))))/2.0;
+        triInit[k] = (1.0 + ((2.0*1.0/PI)*asin(sin(2.0*PI*float(k)/128.0))))/2.0;
+        // scale the waves from 0.0 to 1.0 - as needed for AnalogOut arg 
+    }
+    
+    //Configure the IMU
+    IMU.begin();
+    if (!IMU.begin()) {
+        pc.printf("Failed to communicate with LSM9DS1.\n");
+    }
+    IMU.calibrate(1);
+    
+    //Configure the Push button
+    pb1.mode(PullUp);
+    wait(0.01);
+    pb1.attach_deasserted(&pb1_released_callback);
+    pb1.setSampleFrequency();
+    
+    //Set up the I2C Touch keypad interrupt
+    irq.fall(&fallInterrupt);
+    irq.mode(PullUp);
+    
+    //And the IMU Sample thread
+    Thread t1(sample_IMU);
+    
+    while(1) {}
+}
\ No newline at end of file