Mercurial > hg > beaglert
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
--- /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