changeset 268:8d80eda512cd prerelease

Added new overlay for using PRU0 or PRU1, a script to halt board on button press, and several example projects
author andrewm
date Tue, 17 May 2016 14:46:26 +0100
parents 247a182adb6d
children cf98c06c72fd
files core/PRU.cpp projects/7segment/render.cpp projects/cape_test/render.cpp projects/level_meter/render.cpp projects/mpr121/I2C_MPR121.cpp projects/mpr121/I2C_MPR121.h projects/mpr121/render.cpp projects/stepper/render.cpp projects/tank_wars/game.cpp projects/tank_wars/game.h projects/tank_wars/main.cpp projects/tank_wars/render.cpp resources/BB-BONE-PRU-BELA-00A0.dtbo resources/BB-BONE-PRU-BELA-00A0.dts resources/shutdown_switch.sh
diffstat 15 files changed, 1002 insertions(+), 27 deletions(-) [+]
line wrap: on
line diff
--- a/core/PRU.cpp	Tue May 17 14:38:03 2016 +0100
+++ b/core/PRU.cpp	Tue May 17 14:46:26 2016 +0100
@@ -37,6 +37,9 @@
 
 using namespace std;
 
+// PRU memory: PRU0 and PRU1 RAM are 8kB (0x2000) long each
+//             PRU-SHARED RAM is 12kB (0x3000) long
+
 #define PRU_MEM_MCASP_OFFSET 0x2000  // Offset within PRU-SHARED RAM
 #define PRU_MEM_MCASP_LENGTH 0x1000  // Length of McASP memory, in bytes
 #define PRU_MEM_DAC_OFFSET 0x0     // Offset within PRU0 RAM
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/projects/7segment/render.cpp	Tue May 17 14:46:26 2016 +0100
@@ -0,0 +1,127 @@
+/*
+ * render.cpp
+ *
+ *  Created on: Oct 24, 2014
+ *      Author: parallels
+ */
+
+
+#include <BeagleRT.h>
+#include <Utilities.h>
+
+#define NUM_PINS 12
+
+// Breadboard wiring layout:
+// 11 10 12 9  8  7
+// [ LED DISP ]
+// 1  2  3  6  4  5
+
+// Organised by display segments:
+// e d . X c g b X X X f a
+const int kPins[NUM_PINS] = {P8_07, P8_08, P8_09, P8_10, P8_11, P8_12,
+							 P8_15, P8_16, P8_27, P8_28, P8_29, P8_30};
+
+// Indices into the above array: pins 12, 9, 8, 6
+const int kDigits[4] = {9, 8, 7, 3};
+
+int gCurrentlyDisplayingDigit = 0;
+int gDigitDisplayTime = 0;
+const int kDigitMaxDisplayTime = 44;
+
+int gState = 0;
+int gStateCounter = 0;
+const int kMaxState = 25;
+
+// . g f e d c b a
+//const unsigned char kBELA[4] = {0x7C, 0x79, 0x38, 0x77};
+const unsigned char kBELA[4] = {0x7C, 0x7B, 0x38, 0x5F};
+const unsigned char kPerimeter[6] = {0x01, 0x02, 0x04, 0x08, 0x10, 0x20};
+
+int gCharacterToDisplay[4] = {0, 0, 0, 0};
+	
+// setup() is called once before the audio rendering starts.
+// Use it to perform any initialisation and allocation which is dependent
+// on the period size or sample rate.
+//
+// userData holds an opaque pointer to a data structure that was passed
+// in from the call to initAudio().
+//
+// Return true on success; returning false halts the program.
+
+bool setup(BeagleRTContext *context, void *userData)
+{	
+	// This project makes the assumption that the audio and digital
+	// sample rates are the same. But check it to be sure!
+	if(context->audioFrames != context->digitalFrames) {
+		rt_printf("Error: this project needs the audio and digital sample rates to be the same.\n");
+		return false;
+	}
+	
+	for(int i = 0; i < NUM_PINS; i++) {
+		pinModeFrame(context, 0, kPins[i], OUTPUT);
+	}
+
+	return true;
+}
+
+// render() is called regularly at the highest priority by the audio engine.
+// Input and output are given from the audio hardware and the other
+// ADCs and DACs (if available). If only audio is available, numMatrixFrames
+// will be 0.
+
+void render(BeagleRTContext *context, void *userData)
+{
+	for(unsigned int n = 0; n < context->audioFrames; n++) {
+		// Check for rotation between digits
+		if(--gDigitDisplayTime <= 0) {
+			gCurrentlyDisplayingDigit = (gCurrentlyDisplayingDigit + 1) % 4;
+			gDigitDisplayTime = kDigitMaxDisplayTime;
+		}
+	
+		// Write the currently displaying digit low and the rest high
+		for(int i = 0; i < 4; i++)
+				digitalWriteFrameOnce(context, n, kPins[kDigits[i]], HIGH);
+		digitalWriteFrameOnce(context, n, kPins[kDigits[gCurrentlyDisplayingDigit]], LOW);
+		
+		// Write the digit to the other outputs
+		digitalWriteFrameOnce(context, n, kPins[11],
+			gCharacterToDisplay[gCurrentlyDisplayingDigit] & 0x01);	// a
+		digitalWriteFrameOnce(context, n, kPins[6], 
+			gCharacterToDisplay[gCurrentlyDisplayingDigit] & 0x02);	// b
+		digitalWriteFrameOnce(context, n, kPins[4], 
+			gCharacterToDisplay[gCurrentlyDisplayingDigit] & 0x04);	// c
+		digitalWriteFrameOnce(context, n, kPins[1],
+			gCharacterToDisplay[gCurrentlyDisplayingDigit] & 0x08);	// d
+		digitalWriteFrameOnce(context, n, kPins[0],
+			gCharacterToDisplay[gCurrentlyDisplayingDigit] & 0x10);	// e
+		digitalWriteFrameOnce(context, n, kPins[10],
+			gCharacterToDisplay[gCurrentlyDisplayingDigit] & 0x20);	// f
+		digitalWriteFrameOnce(context, n, kPins[5],
+			gCharacterToDisplay[gCurrentlyDisplayingDigit] & 0x40);	// g
+		digitalWriteFrameOnce(context, n, kPins[2],
+			gCharacterToDisplay[gCurrentlyDisplayingDigit] & 0x80);	// .
+			
+		// Check for changing state
+		if(--gStateCounter <= 0) {
+			gState = (gState + 1) % kMaxState;
+			if(gState != (kMaxState - 1)) {
+				for(int i = 0; i < 4; i++)
+					gCharacterToDisplay[i] = 1 << (gState % 6);
+				gStateCounter = 2000;
+			}
+			else {
+				for(int i = 0; i < 4; i++)
+					gCharacterToDisplay[i] = kBELA[i];
+				gStateCounter = 50000;
+			}
+		}
+	}
+}
+
+// cleanup() is called once at the end, after the audio has stopped.
+// Release any resources that were allocated in setup().
+
+void cleanup(BeagleRTContext *context, void *userData)
+{
+	
+}
--- a/projects/cape_test/render.cpp	Tue May 17 14:38:03 2016 +0100
+++ b/projects/cape_test/render.cpp	Tue May 17 14:46:26 2016 +0100
@@ -14,10 +14,29 @@
 
 const int gDACPinOrder[] = {6, 4, 2, 0, 1, 3, 5, 7};
 
+enum {
+	kStateTestingAudioLeft = 0,
+	kStateTestingAudioRight,
+	kStateTestingAudioDone
+};
+
 uint64_t gLastErrorFrame = 0;
 uint32_t gEnvelopeSampleCount = 0;
-float gEnvelopeValue = 0.5;
+float gEnvelopeValueL = 0.5, gEnvelopeValueR = 0.5;
 float gEnvelopeDecayRate = 0.9995;
+int gEnvelopeLastChannel = 0;
+
+float gPositivePeakLevels[2] = {0, 0};
+float gNegativePeakLevels[2] = {0, 0};
+float gPeakLevelDecayRate = 0.999;
+const float gPeakLevelLowThreshold = 0.02;
+const float gPeakLevelHighThreshold = 0.2;
+const float gDCOffsetThreshold = 0.1;
+int gAudioTestState = kStateTestingAudioLeft;
+int gAudioTestStateSampleCount = 0;
+int gAudioTestSuccessCounter = 0;
+const int gAudioTestSuccessCounterThreshold = 64;
+const int gAudioTestStateSampleThreshold = 16384;
 
 // setup() is called once before the audio rendering starts.
 // Use it to perform any initialisation and allocation which is dependent
@@ -47,27 +66,146 @@
 
 	// Play a sine wave on the audio output
 	for(unsigned int n = 0; n < context->audioFrames; n++) {
-		context->audioOut[2*n] = context->audioOut[2*n + 1] = gEnvelopeValue * sinf(phase);
+		
+		// Peak detection on the audio inputs, with offset to catch
+		// DC errors
+		for(int ch = 0; ch < 2; ch++) {
+			if(context->audioIn[2*n + ch] > gPositivePeakLevels[ch])
+				gPositivePeakLevels[ch] = context->audioIn[2*n + ch];
+			gPositivePeakLevels[ch] += 0.1;
+			gPositivePeakLevels[ch] *= gPeakLevelDecayRate;
+			gPositivePeakLevels[ch] -= 0.1;
+			if(context->audioIn[2*n + ch] < gNegativePeakLevels[ch])
+				gNegativePeakLevels[ch] = context->audioIn[2*n + ch];
+			gNegativePeakLevels[ch] -= 0.1;			
+			gNegativePeakLevels[ch] *= gPeakLevelDecayRate;
+			gNegativePeakLevels[ch] += 0.1;
+		}
+		
+		if(gAudioTestState == kStateTestingAudioLeft) {
+			context->audioOut[2*n] = 0.2 * sinf(phase);
+			context->audioOut[2*n + 1] = 0;		
+			
+			frequency = 3000.0;
+			phase += 2.0 * M_PI * frequency / 44100.0;
+			if(phase >= 2.0 * M_PI)
+				phase -= 2.0 * M_PI;
+			
+			gAudioTestStateSampleCount++;
+			if(gAudioTestStateSampleCount >= gAudioTestStateSampleThreshold) {
+				// Check if we have the expected input: signal on the left but not
+				// on the right. Also check that there is not too much DC offset on the
+				// inactive signal
+				if((gPositivePeakLevels[0] - gNegativePeakLevels[0]) >= gPeakLevelHighThreshold 
+					&& (gPositivePeakLevels[1] -  gNegativePeakLevels[1]) <= gPeakLevelLowThreshold &&
+					fabsf(gPositivePeakLevels[1]) < gDCOffsetThreshold &&
+					fabsf(gNegativePeakLevels[1]) < gDCOffsetThreshold) {
+					// Successful test: increment counter
+					gAudioTestSuccessCounter++;
+					if(gAudioTestSuccessCounter >= gAudioTestSuccessCounterThreshold) {
+						gAudioTestState = kStateTestingAudioRight;
+						gAudioTestStateSampleCount = 0;
+						gAudioTestSuccessCounter = 0;
+					}
 
-		// If one second has gone by with no error, play one sound, else
-		// play another
-		if(context->audioSampleCount + n - gLastErrorFrame > 44100) {
-			gEnvelopeValue *= gEnvelopeDecayRate;
-			gEnvelopeSampleCount++;
-			if(gEnvelopeSampleCount > 22050) {
-				gEnvelopeValue = 0.5;
-				gEnvelopeSampleCount = 0;
+				}
+				else {
+					if(!((context->audioSampleCount + n) % 22050)) {
+						// Debugging print messages
+						if((gPositivePeakLevels[0] - gNegativePeakLevels[0]) < gPeakLevelHighThreshold)
+							rt_printf("Left Audio In FAIL: insufficient signal: %f\n", 
+										gPositivePeakLevels[0] - gNegativePeakLevels[0]);
+						else if(gPositivePeakLevels[1] -  gNegativePeakLevels[1] > gPeakLevelLowThreshold)
+							rt_printf("Right Audio In FAIL: signal present when it should not be: %f\n",
+										gPositivePeakLevels[1] -  gNegativePeakLevels[1]);
+						else if(fabsf(gPositivePeakLevels[1]) >= gDCOffsetThreshold ||
+								fabsf(gNegativePeakLevels[1]) >= gDCOffsetThreshold)
+							rt_printf("Right Audio In FAIL: DC offset: (%f, %f)\n",
+										gPositivePeakLevels[1], gNegativePeakLevels[1]);						
+					}
+					gAudioTestSuccessCounter--;
+					if(gAudioTestSuccessCounter <= 0)
+						gAudioTestSuccessCounter = 0;
+				}
 			}
-			frequency = 880.0;
+		}
+		else if(gAudioTestState == kStateTestingAudioRight) {
+			context->audioOut[2*n] = 0;
+			context->audioOut[2*n + 1] = 0.2 * sinf(phase);
+			
+			frequency = 3000.0;
+			phase += 2.0 * M_PI * frequency / 44100.0;
+			if(phase >= 2.0 * M_PI)
+				phase -= 2.0 * M_PI;
+			
+			gAudioTestStateSampleCount++;
+			if(gAudioTestStateSampleCount >= gAudioTestStateSampleThreshold) {
+				// Check if we have the expected input: signal on the left but not
+				// on the right
+				if((gPositivePeakLevels[1] - gNegativePeakLevels[1]) >= gPeakLevelHighThreshold 
+					&& (gPositivePeakLevels[0] -  gNegativePeakLevels[0]) <= gPeakLevelLowThreshold &&
+					fabsf(gPositivePeakLevels[0]) < gDCOffsetThreshold &&
+					fabsf(gNegativePeakLevels[0]) < gDCOffsetThreshold) {
+					// Successful test: increment counter
+					gAudioTestSuccessCounter++;
+					if(gAudioTestSuccessCounter >= gAudioTestSuccessCounterThreshold) {
+						gAudioTestSuccessCounter = 0;							
+						gAudioTestStateSampleCount = 0;
+						gAudioTestState = kStateTestingAudioDone;
+					}
+				}
+				else {
+					if(!((context->audioSampleCount + n) % 22050)) {
+						// Debugging print messages
+						if((gPositivePeakLevels[1] - gNegativePeakLevels[1]) < gPeakLevelHighThreshold)
+							rt_printf("Right Audio In FAIL: insufficient signal: %f\n", 
+										gPositivePeakLevels[1] - gNegativePeakLevels[1]);
+						else if(gPositivePeakLevels[0] -  gNegativePeakLevels[0] > gPeakLevelLowThreshold)
+							rt_printf("Left Audio In FAIL: signal present when it should not be: %f\n",
+										gPositivePeakLevels[0] -  gNegativePeakLevels[0]);
+						else if(fabsf(gPositivePeakLevels[0]) >= gDCOffsetThreshold ||
+								fabsf(gNegativePeakLevels[0]) >= gDCOffsetThreshold)
+							rt_printf("Left Audio In FAIL: DC offset: (%f, %f)\n",
+										gPositivePeakLevels[0], gNegativePeakLevels[0]);						
+					}
+					gAudioTestSuccessCounter--;
+					if(gAudioTestSuccessCounter <= 0)
+						gAudioTestSuccessCounter = 0;
+				}
+			}			
 		}
 		else {
-			gEnvelopeValue = 0.5;
-			frequency = 220.0;
+			// Audio input testing finished. Play tones depending on status of
+			// analog testing
+			context->audioOut[2*n] = gEnvelopeValueL * sinf(phase);
+			context->audioOut[2*n + 1] = gEnvelopeValueR * sinf(phase);
+
+			// If one second has gone by with no error, play one sound, else
+			// play another
+			if(context->audioSampleCount + n - gLastErrorFrame > 44100) {
+				gEnvelopeValueL *= gEnvelopeDecayRate;
+				gEnvelopeValueR *= gEnvelopeDecayRate;
+				gEnvelopeSampleCount++;
+				if(gEnvelopeSampleCount > 22050) {
+					if(gEnvelopeLastChannel == 0)
+						gEnvelopeValueR = 0.5;
+					else
+						gEnvelopeValueL = 0.5;
+					gEnvelopeLastChannel = !gEnvelopeLastChannel;
+					gEnvelopeSampleCount = 0;
+				}
+				frequency = 880.0;
+			}
+			else {
+				gEnvelopeValueL = gEnvelopeValueR = 0.5;
+				gEnvelopeLastChannel = 0;
+				frequency = 220.0;
+			}
+
+			phase += 2.0 * M_PI * frequency / 44100.0;
+			if(phase >= 2.0 * M_PI)
+				phase -= 2.0 * M_PI;
 		}
-
-		phase += 2.0 * M_PI * frequency / 44100.0;
-		if(phase >= 2.0 * M_PI)
-			phase -= 2.0 * M_PI;
 	}
 
 	for(unsigned int n = 0; n < context->analogFrames; n++) {
--- a/projects/level_meter/render.cpp	Tue May 17 14:38:03 2016 +0100
+++ b/projects/level_meter/render.cpp	Tue May 17 14:46:26 2016 +0100
@@ -21,6 +21,7 @@
 
 // Thresholds for LEDs: set in setup()
 float gThresholds[NUMBER_OF_SEGMENTS + 1];
+int gSamplesToLight[NUMBER_OF_SEGMENTS];
 
 // High-pass filter on the input
 float gLastX[2] = {0};
@@ -57,8 +58,10 @@
 		gThresholds[i] = powf(10.0f, (-1.0 * (NUMBER_OF_SEGMENTS - i)) * .05);
 	}
 	
-	for(int i = 0; i < NUMBER_OF_SEGMENTS; i++)
+	for(int i = 0; i < NUMBER_OF_SEGMENTS; i++) {
+		gSamplesToLight[i] = 0;
 		pinModeFrame(context, 0, i, OUTPUT);
+	}
 
 	return true;
 }
@@ -111,9 +114,15 @@
 			// for the peak level also remains lit.
 			int state = LOW;
 				
-			if(gAudioLocalLevel > gThresholds[led])
+			if(gAudioLocalLevel > gThresholds[led]) {
 				state = HIGH;
-			else if(gAudioPeakLevel > gThresholds[led] && gAudioPeakLevel <= gThresholds[led + 1])
+				gSamplesToLight[led] = 1000;
+			}
+			/*else if(gAudioPeakLevel > gThresholds[led] && gAudioPeakLevel <= gThresholds[led + 1]) {
+				state = HIGH;
+				gSamplesToLight[led] = 1000;
+			}*/
+			else if(--gSamplesToLight[led] > 0)
 				state = HIGH;
 			
 			// Write LED
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/projects/mpr121/I2C_MPR121.cpp	Tue May 17 14:46:26 2016 +0100
@@ -0,0 +1,176 @@
+/*
+ * I2C_MPR121.cpp
+ *
+ *  Created on: Oct 14, 2013
+ *      Author: Victor Zappi
+ */
+
+
+#include "I2C_MPR121.h"
+
+I2C_MPR121::I2C_MPR121() {
+
+}
+
+boolean I2C_MPR121::begin(uint8_t bus, uint8_t i2caddr) {
+  _i2c_address = i2caddr;
+	
+  if(initI2C_RW(bus, i2caddr, 0) > 0)
+	  return false;
+
+  // soft reset
+  writeRegister(MPR121_SOFTRESET, 0x63);
+  usleep(1000);
+  //delay(1);
+  for (uint8_t i=0; i<0x7F; i++) {
+  //  Serial.print("$"); Serial.print(i, HEX); 
+  //  Serial.print(": 0x"); Serial.println(readRegister8(i));
+  }
+  
+
+  writeRegister(MPR121_ECR, 0x0);
+
+  uint8_t c = readRegister8(MPR121_CONFIG2);
+  
+  if (c != 0x24) {
+	  rt_printf("MPR121 read 0x%x instead of 0x24\n", c);
+	  return false;
+  }
+
+  setThresholds(12, 6);
+  writeRegister(MPR121_MHDR, 0x01);
+  writeRegister(MPR121_NHDR, 0x01);
+  writeRegister(MPR121_NCLR, 0x0E);
+  writeRegister(MPR121_FDLR, 0x00);
+
+  writeRegister(MPR121_MHDF, 0x01);
+  writeRegister(MPR121_NHDF, 0x05);
+  writeRegister(MPR121_NCLF, 0x01);
+  writeRegister(MPR121_FDLF, 0x00);
+
+  writeRegister(MPR121_NHDT, 0x00);
+  writeRegister(MPR121_NCLT, 0x00);
+  writeRegister(MPR121_FDLT, 0x00);
+
+  writeRegister(MPR121_DEBOUNCE, 0);
+  writeRegister(MPR121_CONFIG1, 0x10); // default, 16uA charge current
+  writeRegister(MPR121_CONFIG2, 0x20); // 0.5uS encoding, 1ms period
+
+//  writeRegister(MPR121_AUTOCONFIG0, 0x8F);
+
+//  writeRegister(MPR121_UPLIMIT, 150);
+//  writeRegister(MPR121_TARGETLIMIT, 100); // should be ~400 (100 shifted)
+//  writeRegister(MPR121_LOWLIMIT, 50);
+  // enable all electrodes
+  writeRegister(MPR121_ECR, 0x8F);  // start with first 5 bits of baseline tracking
+
+  return true;
+}
+
+void I2C_MPR121::setThresholds(uint8_t touch, uint8_t release) {
+  for (uint8_t i=0; i<12; i++) {
+    writeRegister(MPR121_TOUCHTH_0 + 2*i, touch);
+    writeRegister(MPR121_RELEASETH_0 + 2*i, release);
+  }
+}
+
+uint16_t  I2C_MPR121::filteredData(uint8_t t) {
+  if (t > 12) return 0;
+  return readRegister16(MPR121_FILTDATA_0L + t*2);
+}
+
+uint16_t  I2C_MPR121::baselineData(uint8_t t) {
+  if (t > 12) return 0;
+  uint16_t bl = readRegister8(MPR121_BASELINE_0 + t);
+  return (bl << 2);
+}
+
+uint16_t  I2C_MPR121::touched(void) {
+  uint16_t t = readRegister16(MPR121_TOUCHSTATUS_L);
+  return t & 0x0FFF;
+}
+
+/*********************************************************************/
+
+
+uint8_t I2C_MPR121::readRegister8(uint8_t reg) {
+    unsigned char inbuf, outbuf;
+    struct i2c_rdwr_ioctl_data packets;
+    struct i2c_msg messages[2];
+
+    /*
+     * In order to read a register, we first do a "dummy write" by writing
+     * 0 bytes to the register we want to read from.  This is similar to
+     * the packet in set_i2c_register, except it's 1 byte rather than 2.
+     */
+    outbuf = reg;
+    messages[0].addr  = 0x5A;
+    messages[0].flags = 0;
+    messages[0].len   = sizeof(outbuf);
+    messages[0].buf   = &outbuf;
+
+    /* The data will get returned in this structure */
+    messages[1].addr  = 0x5A;
+    messages[1].flags = I2C_M_RD/* | I2C_M_NOSTART*/;
+    messages[1].len   = sizeof(inbuf);
+    messages[1].buf   = &inbuf;
+
+    /* Send the request to the kernel and get the result back */
+    packets.msgs      = messages;
+    packets.nmsgs     = 2;
+    if(ioctl(i2C_file, I2C_RDWR, &packets) < 0) {
+        rt_printf("Unable to send data");
+        return 0;
+    }
+
+    return inbuf;
+}
+
+uint16_t I2C_MPR121::readRegister16(uint8_t reg) {
+    unsigned char inbuf[2], outbuf;
+    struct i2c_rdwr_ioctl_data packets;
+    struct i2c_msg messages[2];
+
+    /*
+     * In order to read a register, we first do a "dummy write" by writing
+     * 0 bytes to the register we want to read from.  This is similar to
+     * the packet in set_i2c_register, except it's 1 byte rather than 2.
+     */
+    outbuf = reg;
+    messages[0].addr  = _i2c_address;
+    messages[0].flags = 0;
+    messages[0].len   = sizeof(outbuf);
+    messages[0].buf   = &outbuf;
+
+    /* The data will get returned in this structure */
+    messages[1].addr  = _i2c_address;
+    messages[1].flags = I2C_M_RD/* | I2C_M_NOSTART*/;
+    messages[1].len   = sizeof(inbuf);
+    messages[1].buf   = inbuf;
+
+    /* Send the request to the kernel and get the result back */
+    packets.msgs      = messages;
+    packets.nmsgs     = 2;
+    if(ioctl(i2C_file, I2C_RDWR, &packets) < 0) {
+        rt_printf("Unable to send data");
+        return 0;
+    }
+
+    return (uint16_t)inbuf[0] | (((uint16_t)inbuf[1]) << 8);
+}
+
+/**************************************************************************/
+/*!
+    @brief  Writes 8-bits to the specified destination register
+*/
+/**************************************************************************/
+void I2C_MPR121::writeRegister(uint8_t reg, uint8_t value) {
+	uint8_t buf[2] = { reg, value };
+
+	if(write(i2C_file, buf, 2) != 2)
+	{
+		cout << "Failed to write register " << (int)reg << " on MPR121\n";
+		return;
+	}
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/projects/mpr121/I2C_MPR121.h	Tue May 17 14:46:26 2016 +0100
@@ -0,0 +1,82 @@
+/*
+ * MPR121 Bela demo
+ * 
+ * Andrew McPherson
+ * Based on Adafruit library by Limor Fried/Ladyada
+ */
+
+#ifndef I2CTK_H_
+#define I2CTK_H_
+
+#include <I2c.h>
+#include "Utilities.h"
+
+typedef bool boolean;
+
+#define MPR121_I2CADDR_DEFAULT 0x5A
+
+#define MPR121_TOUCHSTATUS_L 0x00
+#define MPR121_TOUCHSTATUS_H 0x01
+#define MPR121_FILTDATA_0L  0x04
+#define MPR121_FILTDATA_0H  0x05
+#define MPR121_BASELINE_0   0x1E
+#define MPR121_MHDR         0x2B
+#define MPR121_NHDR         0x2C
+#define MPR121_NCLR         0x2D
+#define MPR121_FDLR         0x2E
+#define MPR121_MHDF         0x2F
+#define MPR121_NHDF         0x30
+#define MPR121_NCLF         0x31
+#define MPR121_FDLF         0x32
+#define MPR121_NHDT         0x33
+#define MPR121_NCLT         0x34
+#define MPR121_FDLT         0x35
+
+#define MPR121_TOUCHTH_0    0x41
+#define MPR121_RELEASETH_0    0x42
+#define MPR121_DEBOUNCE 0x5B
+#define MPR121_CONFIG1 0x5C
+#define MPR121_CONFIG2 0x5D
+#define MPR121_CHARGECURR_0 0x5F
+#define MPR121_CHARGETIME_1 0x6C
+#define MPR121_ECR 0x5E
+#define MPR121_AUTOCONFIG0 0x7B
+#define MPR121_AUTOCONFIG1 0x7C
+#define MPR121_UPLIMIT   0x7D
+#define MPR121_LOWLIMIT  0x7E
+#define MPR121_TARGETLIMIT  0x7F
+
+#define MPR121_GPIODIR  0x76
+#define MPR121_GPIOEN  0x77
+#define MPR121_GPIOSET  0x78
+#define MPR121_GPIOCLR  0x79
+#define MPR121_GPIOTOGGLE  0x7A
+
+#define MPR121_SOFTRESET 0x80
+
+class I2C_MPR121 : public I2c
+{
+public:
+	// Hardware I2C
+	I2C_MPR121();
+
+	boolean begin(uint8_t bus = 1, uint8_t i2caddr = MPR121_I2CADDR_DEFAULT);
+
+	uint16_t filteredData(uint8_t t);
+	uint16_t  baselineData(uint8_t t);
+
+	uint8_t readRegister8(uint8_t reg);
+	uint16_t readRegister16(uint8_t reg);
+	void writeRegister(uint8_t reg, uint8_t value);
+	uint16_t touched(void);
+ 
+ 	void setThresholds(uint8_t touch, uint8_t release);
+	
+	int readI2C() { return 0; } // Unused
+	
+private:
+	int _i2c_address;
+};
+
+
+#endif /* I2CTK_H_ */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/projects/mpr121/render.cpp	Tue May 17 14:46:26 2016 +0100
@@ -0,0 +1,129 @@
+#include <BeagleRT.h>
+#include <Utilities.h>
+#include <cmath>
+#include <rtdk.h>
+#include "I2C_MPR121.h"
+
+// How many pins there are
+#define NUM_TOUCH_PINS 12
+
+// Define this to print data to terminal
+#undef DEBUG_MPR121
+
+// Change this to change how often the MPR121 is read (in Hz)
+int readInterval = 50;
+
+// Change this threshold to set the minimum amount of touch
+int threshold = 40;
+
+// This array holds the continuous sensor values
+int sensorValue[NUM_TOUCH_PINS];
+
+// ---- test code stuff -- can be deleted for your example ----
+
+// 12 notes of a C major scale...
+float gFrequencies[NUM_TOUCH_PINS] = {261.63, 293.66, 329.63, 349.23, 392.00, 440.00, 493.88, 523.25, 587.33, 659.25, 698.25, 783.99};
+
+// This is internal stuff for the demo
+float gNormFrequencies[NUM_TOUCH_PINS];
+float gPhases[NUM_TOUCH_PINS] = {0};
+
+// ---- internal stuff -- do not change -----
+
+I2C_MPR121 mpr121;			// Object to handle MPR121 sensing
+AuxiliaryTask i2cTask;		// Auxiliary task to read I2C
+
+int readCount = 0;			// How long until we read again...
+int readIntervalSamples = 0; // How many samples between reads
+
+void readMPR121();
+
+// setup() is called once before the audio rendering starts.
+// Use it to perform any initialisation and allocation which is dependent
+// on the period size or sample rate.
+//
+// userData holds an opaque pointer to a data structure that was passed
+// in from the call to initAudio().
+//
+// Return true on success; returning false halts the program.
+
+bool setup(BeagleRTContext *context, void *userData)
+{
+	if(!mpr121.begin(1, 0x5A)) {
+		rt_printf("Error initialising MPR121\n");
+		return false;
+	}
+	
+	i2cTask = BeagleRT_createAuxiliaryTask(readMPR121, 50, "beaglert-mpr121");
+	readIntervalSamples = context->audioSampleRate / readInterval;
+	
+	for(int i = 0; i < NUM_TOUCH_PINS; i++) {
+		gNormFrequencies[i] = 2.0 * M_PI * gFrequencies[i] / context->audioSampleRate;
+	}
+	
+	return true;
+}
+
+// render() is called regularly at the highest priority by the audio engine.
+// Input and output are given from the audio hardware and the other
+// ADCs and DACs (if available). If only audio is available, numAnalogFrames
+// will be 0.
+
+void render(BeagleRTContext *context, void *userData)
+{
+	for(int n = 0; n < context->audioFrames; n++) {
+		// Keep this code: it schedules the touch sensor readings
+		if(++readCount >= readIntervalSamples) {
+			readCount = 0;
+			BeagleRT_scheduleAuxiliaryTask(i2cTask);
+		}
+		
+		float sample = 0.0;
+		
+		// This code can be replaced with your favourite audio code
+		for(int i = 0; i < NUM_TOUCH_PINS; i++) {
+			float amplitude = sensorValue[i] / 400.0;
+			
+			// Prevent clipping
+			if(amplitude > 0.5)
+				amplitude = 0.5;
+			
+			sample += amplitude * sinf(gPhases[i]);
+			gPhases[i] += gNormFrequencies[i];
+			if(gPhases[i] > 2.0 * M_PI)
+				gPhases[i] -= 2.0 * M_PI;
+		}
+		
+		for(int ch = 0; ch < context->audioChannels; ch++)
+			context->audioOut[context->audioChannels * n + ch] = sample;
+	}
+}
+
+// cleanup() is called once at the end, after the audio has stopped.
+// Release any resources that were allocated in setup().
+
+void cleanup(BeagleRTContext *context, void *userData)
+{
+	// Nothing to do here
+}
+
+
+// Auxiliary task to read the I2C board
+void readMPR121()
+{
+	for(int i = 0; i < NUM_TOUCH_PINS; i++) {
+		sensorValue[i] = -(mpr121.filteredData(i) - mpr121.baselineData(i));
+		sensorValue[i] -= threshold;
+		if(sensorValue[i] < 0)
+			sensorValue[i] = 0;
+#ifdef DEBUG_MPR121
+		rt_printf("%d ", sensorValue[i]);
+#endif
+	}
+#ifdef DEBUG_MPR121
+	rt_printf("\n");
+#endif
+	
+	// You can use this to read binary on/off touch state more easily
+	//rt_printf("Touched: %x\n", mpr121.touched());
+}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/projects/stepper/render.cpp	Tue May 17 14:46:26 2016 +0100
@@ -0,0 +1,151 @@
+/*
+ * render.cpp
+ *
+ *  Created on: Oct 24, 2014
+ *      Author: parallels
+ */
+
+
+#include <BeagleRT.h>
+#include <Utilities.h>
+
+const int kStepLengthSlow = 1000;
+const int kStepLengthFast = 500;
+
+int gStepLengthSamples = kStepLengthSlow;
+
+const int gPinA1 = P8_27;
+const int gPinA2 = P8_28;
+const int gPinB1 = P8_29;
+const int gPinB2 = P8_30;
+const int gPinServo = P9_16;
+
+int gStepCounter = 0;
+int gPhase = 0;
+
+int gServoCounter = 0;
+
+
+enum {
+	kStateMoveRight1 = 0,
+	kStateMoveLeft1,
+	kStateMoveRight2,
+	kStateMoveLeft2,
+	kStateMoveRight3,
+	kStateMoveLeft3,
+	kStateSpin,
+	kStateMax
+};
+
+int gState = 0;
+int gStateCounter = 0;
+
+// setup() is called once before the audio rendering starts.
+// Use it to perform any initialisation and allocation which is dependent
+// on the period size or sample rate.
+//
+// userData holds an opaque pointer to a data structure that was passed
+// in from the call to initAudio().
+//
+// Return true on success; returning false halts the program.
+
+bool setup(BeagleRTContext *context, void *userData)
+{	
+	// This project makes the assumption that the audio and digital
+	// sample rates are the same. But check it to be sure!
+	if(context->audioFrames != context->digitalFrames) {
+		rt_printf("Error: this project needs the audio and digital sample rates to be the same.\n");
+		return false;
+	}
+	
+	pinModeFrame(context, 0, gPinA1, OUTPUT);
+	pinModeFrame(context, 0, gPinA2, OUTPUT);
+	pinModeFrame(context, 0, gPinB1, OUTPUT);
+	pinModeFrame(context, 0, gPinB2, OUTPUT);
+	pinModeFrame(context, 0, gPinServo, OUTPUT);
+		
+	return true;
+}
+
+// render() is called regularly at the highest priority by the audio engine.
+// Input and output are given from the audio hardware and the other
+// ADCs and DACs (if available). If only audio is available, numMatrixFrames
+// will be 0.
+
+void render(BeagleRTContext *context, void *userData)
+{
+	for(unsigned int n = 0; n < context->audioFrames; n++) {
+		if(gPhase == 0 || gPhase == 1) {
+			digitalWriteFrameOnce(context, n, gPinB1, HIGH);
+			digitalWriteFrameOnce(context, n, gPinB2, LOW);
+		}
+		else {
+			digitalWriteFrameOnce(context, n, gPinB1, LOW);
+			digitalWriteFrameOnce(context, n, gPinB2, HIGH);			
+		}
+		
+		if(gPhase == 1 || gPhase == 2) {
+			digitalWriteFrameOnce(context, n, gPinA1, HIGH);
+			digitalWriteFrameOnce(context, n, gPinA2, LOW);
+		}
+		else {
+			digitalWriteFrameOnce(context, n, gPinA1, LOW);
+			digitalWriteFrameOnce(context, n, gPinA2, HIGH);			
+		}
+		
+		if(--gServoCounter > 0) 
+			digitalWriteFrameOnce(context, n, gPinServo, HIGH);
+		else
+			digitalWriteFrameOnce(context, n, gPinServo, LOW);
+		
+		if(++gStepCounter >= gStepLengthSamples) {
+			gStateCounter++;
+			
+			switch(gState) {
+				case kStateMoveRight1:
+				case kStateMoveRight2:
+				case kStateMoveRight3:
+					gPhase = (gPhase + 1) & 3;
+					break;
+				case kStateMoveLeft1:
+				case kStateMoveLeft2:
+				case kStateMoveLeft3:				
+					gPhase = (gPhase + 3) & 3;	
+					break;
+				case kStateSpin:
+					gPhase = (gPhase + 1) & 3;
+					break;		
+			}
+			
+			if(gState == kStateSpin) {
+				if(gStateCounter >= 48) {
+					gStateCounter = 0;
+					gState = 0;
+					gStepLengthSamples = kStepLengthSlow;
+				}				
+			}
+			else {
+				if(gStateCounter >= 16) {
+					gStateCounter = 0;
+					gState++;
+					if(gState & 1)
+						gServoCounter = 120;
+					else
+						gServoCounter = 80;
+					if(gState == kStateSpin)
+						gStepLengthSamples = kStepLengthFast;
+				}
+			}
+			
+			gStepCounter = 0;
+		}
+	}
+}
+
+// cleanup() is called once at the end, after the audio has stopped.
+// Release any resources that were allocated in setup().
+
+void cleanup(BeagleRTContext *context, void *userData)
+{
+	
+}
--- a/projects/tank_wars/game.cpp	Tue May 17 14:38:03 2016 +0100
+++ b/projects/tank_wars/game.cpp	Tue May 17 14:46:26 2016 +0100
@@ -35,6 +35,7 @@
 
 // Infor needed for sound rendering
 bool collisionJustOccurred = false;
+bool tankHitJustOccurred = false;
 
 // Useful utility function for generating random floating-point values
 float randomFloat(float low, float hi)
@@ -46,8 +47,8 @@
 // Restart the game, without reallocating memory
 void restartGame()
 {
-	float player1Height = randomFloat(screenHeight/2, screenHeight-5);
-	float player2Height = randomFloat(screenHeight/2, screenHeight-5);
+	float player1Height = screenHeight * 3/4; // randomFloat(screenHeight/2, screenHeight-5);
+	float player2Height = screenHeight - 5; // randomFloat(screenHeight/2, screenHeight-5);
 	for(int i = 0; i < screenWidth * 0.2; i++) {
 		groundLevel[i] = player1Height;
 	}
@@ -104,7 +105,8 @@
 		<= tankRadius * tankRadius)
 	{
 		projectileInMotion = false;
-		collisionJustOccurred = true;
+		collisionJustOccurred = false;
+		tankHitJustOccurred = true;
 		playerHasWon = 2;
 	}
 	else if((tank2X - projectilePositionX)*(tank2X - projectilePositionX) +
@@ -112,7 +114,8 @@
 		<= tankRadius * tankRadius)
 	{
 		projectileInMotion = false;
-		collisionJustOccurred = true;
+		collisionJustOccurred = false;
+		tankHitJustOccurred = true;
 		playerHasWon = 1;
 	}
 	else if(projectilePositionX < 0 || projectilePositionX >= screenWidth) {
@@ -201,6 +204,16 @@
 	return false;
 }
 
+bool gameStatusTankHitOccurred()
+{
+	if(tankHitJustOccurred) {
+		tankHitJustOccurred = false;
+		return true;
+	}
+	return false;
+}
+
+
 float gameStatusProjectileHeight()
 {
 	return projectilePositionY / (float)screenHeight;
--- a/projects/tank_wars/game.h	Tue May 17 14:38:03 2016 +0100
+++ b/projects/tank_wars/game.h	Tue May 17 14:46:26 2016 +0100
@@ -27,6 +27,7 @@
 bool gameStatusProjectileInMotion();
 int gameStatusWinner();
 bool gameStatusCollisionOccurred();
+bool gameStatusTankHitOccurred();
 float gameStatusProjectileHeight();
 
 // Render screen; returns length of buffer used
--- a/projects/tank_wars/main.cpp	Tue May 17 14:38:03 2016 +0100
+++ b/projects/tank_wars/main.cpp	Tue May 17 14:46:26 2016 +0100
@@ -19,6 +19,9 @@
 int gMusicBufferLength = 0;
 float *gSoundBoomBuffer = 0;
 int gSoundBoomBufferLength = 0;
+float *gSoundHitBuffer = 0;
+int gSoundHitBufferLength = 0;
+
 
 using namespace std;
 
@@ -80,7 +83,8 @@
 	BeagleRTInitSettings settings;	// Standard audio settings
 	string musicFileName = "music.wav";
 	string soundBoomFileName = "boom.wav";
-
+	string soundHitFileName = "hit.wav";
+	
 	struct option customOptions[] =
 	{
 		{"help", 0, NULL, 'h'},
@@ -121,7 +125,10 @@
 	if(loadSoundFile(soundBoomFileName, &gSoundBoomBuffer, &gSoundBoomBufferLength) != 0) {
 		cout << "Warning: unable to load sound file " << soundBoomFileName << endl;
 	}
-
+	if(loadSoundFile(soundHitFileName, &gSoundHitBuffer, &gSoundHitBufferLength) != 0) {
+		cout << "Warning: unable to load sound file " << soundHitFileName << endl;
+	}
+	
 	// Initialise the PRU audio device
 	if(BeagleRT_initAudio(&settings, 0) != 0) {
 		cout << "Error: unable to initialise audio" << endl;
@@ -154,7 +161,9 @@
 		free(gMusicBuffer);
 	if(gSoundBoomBuffer != 0)
 		free(gSoundBoomBuffer);
-
+	if(gSoundHitBuffer != 0)
+		free(gSoundHitBuffer);
+	
 	// All done!
 	return 0;
 }
--- a/projects/tank_wars/render.cpp	Tue May 17 14:38:03 2016 +0100
+++ b/projects/tank_wars/render.cpp	Tue May 17 14:46:26 2016 +0100
@@ -68,10 +68,13 @@
 extern int gMusicBufferLength;
 extern float *gSoundBoomBuffer;
 extern int gSoundBoomBufferLength;
+extern float *gSoundHitBuffer;
+extern int gSoundHitBufferLength;
 
 // Current state for sound and music
 int gMusicBufferPointer = 0;	  // 0 means start of buffer...
 int gSoundBoomBufferPointer = -1; // -1 means don't play...
+int gSoundHitBufferPointer = -1;
 float gSoundProjectileOscillatorPhase = 0;
 float gSoundProjectileOscillatorGain = 0.2;
 float gOscillatorPhaseScaler = 0;
@@ -178,6 +181,12 @@
 					gSoundBoomBufferPointer = -1;
 			}
 
+			if(gSoundHitBuffer != 0 && gSoundHitBufferPointer >= 0) {
+				audioSample += gSoundHitBuffer[gSoundHitBufferPointer++];
+				if(gSoundHitBufferPointer >= gSoundHitBufferLength)
+					gSoundHitBufferPointer = -1;
+			}
+
 			// Oscillator plays to indicate projectile height
 			if(gameStatusProjectileInMotion()) {
 				audioSample += gSoundProjectileOscillatorGain * sinf(gSoundProjectileOscillatorPhase);
@@ -237,6 +246,10 @@
 			if(gameStatusCollisionOccurred()) {
 				gSoundBoomBufferPointer = 0;
 			}
+			
+			if(gameStatusTankHitOccurred()) {
+				gSoundHitBufferPointer = 0;
+			}
 		}
 
 		if(gScreenBufferReadPointer >= gScreenBufferReadLength - 1
Binary file resources/BB-BONE-PRU-BELA-00A0.dtbo has changed
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/resources/BB-BONE-PRU-BELA-00A0.dts	Tue May 17 14:46:26 2016 +0100
@@ -0,0 +1,59 @@
+/*
+* Copyright (C) 2013 Matt Ranostay <mranostay@gmail.com>
+*
+* This program is free software; you can redistribute it and/or modify
+* it under the terms of the GNU General Public License version 2 as
+* published by the Free Software Foundation.
+*/
+/dts-v1/;
+/plugin/;
+
+/ {
+	compatible = "ti,beaglebone", "ti,beaglebone-black", "ti,beaglebone-green";
+
+	/* identification */
+	part-number = "BB-BONE-PRU-BELA";
+	version = "00A0";
+
+	/* state the resources this cape uses */
+	exclusive-use =
+		/* the pin header uses */
+		"P8.41",	/* pru1: pr1_pru1_pru_r30_4 */
+		"P8.42",	/* pru1: pr1_pru1_pru_r30_5 */
+		"P8.43",	/* pru1: pr1_pru1_pru_r30_2 */
+		"P8.44",	/* pru1: pr1_pru1_pru_r30_3 */
+		"P8.45",	/* pru1: pr1_pru1_pru_r30_0 */
+		"P8.46",	/* pru1: pr1_pru1_pru_r30_1 */
+		"P9.27",
+		/* the hardware IP uses */
+		"pru0",
+		"pru1";
+
+	fragment@0 {
+		target = <&am33xx_pinmux>;
+		__overlay__ {
+
+			pru_bela_pins: pinmux_pru_bela_pins {
+				pinctrl-single,pins = <
+					0x1a4 0x37	/* P9 27 GPIO3_19: mcasp0_fsr.gpio3[19] | MODE7 | INPUT | pullup */
+					0x0b0 0x25	/* lcd_data4.pr1_pru1_pru_r30_4, MODE5 | OUTPUT | PRU */
+					0x0b4 0x25	/* lcd_data5.pr1_pru1_pru_r30_5, MODE5 | OUTPUT | PRU */
+					0x0ac 0x25	/* lcd_data3.pr1_pru1_pru_r30_3, MODE5 | OUTPUT | PRU */
+					0x0a0 0x25	/* lcd_data0.pr1_pru1_pru_r30_0, MODE5 | OUTPUT | PRU */
+					0x0a4 0x25	/* lcd_data1.pr1_pru1_pru_r30_1, MODE5 | OUTPUT | PRU */
+					0x0a8 0x25	/* lcd_data2.pr1_pru1_pru_r30_2, MODE5 | OUTPUT | PRU */
+				>;
+			};
+		};
+	};
+
+	fragment@2 {
+		target = <&pruss>;
+		__overlay__ {
+			status = "okay";
+
+			pinctrl-names = "default";
+			pinctrl-0 = <&pru_bela_pins>;
+		};
+	};
+};
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/resources/shutdown_switch.sh	Tue May 17 14:46:26 2016 +0100
@@ -0,0 +1,65 @@
+#!/bin/bash
+#
+# shutdown_switch.sh: script for gracefully halting the BeagleBone Black
+# when an onboard button is pressed. 
+#
+# (c) 2016 Andrew McPherson, C4DM, QMUL
+# Developed for Bela: http://bela.io
+
+
+# Prepare P9.27 as input (will be configured with pullup)
+# via BB-BONE-PRU-BELA overlay
+
+BUTTON_PIN=115
+
+echo $BUTTON_PIN > /sys/class/gpio/export
+echo in > /sys/class/gpio/gpio"$BUTTON_PIN"/direction
+
+if [ ! -r /sys/class/gpio/gpio"$BUTTON_PIN"/value ]
+then
+	echo "$(basename $0): Unable to read GPIO pin $BUTTON_PIN for shutdown button."
+	exit
+fi
+
+# First, wait for pin to go high. If it starts at 0, that's more
+# likely that the GPIO is not set correctly, so do not treat this
+# as a button press
+
+while [ $(cat /sys/class/gpio/gpio"$BUTTON_PIN"/value) -ne 1 ]; do
+	# Keep checking pin is readable in case it gets unexported
+	if [ ! -r /sys/class/gpio/gpio"$BUTTON_PIN"/value ]
+	then
+		echo "$(basename $0): Unable to read GPIO pin $BUTTON_PIN for shutdown button."
+		exit
+	fi
+	sleep 0.5
+done
+
+# Now for button press. Make sure the button is held at least
+# 1 second before shutting down
+
+PRESS_COUNT=0
+
+while true; do
+  # Keep checking pin is readable in case it gets unexported
+  if [ ! -r /sys/class/gpio/gpio"$BUTTON_PIN"/value ]
+  then
+    echo "$(basename $0): Unable to read GPIO pin $BUTTON_PIN for shutdown button."
+	exit
+  fi
+	
+  # Button pressed? (pressed = low)
+  if [ $(cat /sys/class/gpio/gpio"$BUTTON_PIN"/value) -eq 0 ]
+  then
+    PRESS_COUNT=$((PRESS_COUNT+1))
+	if [ "$PRESS_COUNT" -ge 4 ]
+	then
+    	echo "$(basename $0): Shutdown button pressed. Halting system..."
+		halt
+		exit 1
+	fi
+  else
+    PRESS_COUNT=0
+  fi
+  sleep 0.5
+done
\ No newline at end of file