parent
e894318451
commit
b649550cb3
@ -0,0 +1,114 @@ |
|||||||
|
//#include <MIDI.h>
|
||||||
|
#include "BAGuitar.h" |
||||||
|
|
||||||
|
using namespace BAGuitar; |
||||||
|
AudioInputI2S i2sIn; |
||||||
|
AudioOutputI2S i2sOut; |
||||||
|
BAAudioControlWM8731 codec; |
||||||
|
|
||||||
|
#define USE_EXT |
||||||
|
|
||||||
|
#ifdef USE_EXT |
||||||
|
ExternalSramManager externalSram(1); // Manage both SRAMs
|
||||||
|
ExtMemSlot delaySlot; // For the external memory
|
||||||
|
AudioEffectAnalogDelay myDelay(&delaySlot); |
||||||
|
#else |
||||||
|
AudioEffectAnalogDelay myDelay(200.0f); |
||||||
|
#endif |
||||||
|
|
||||||
|
AudioMixer4 mixer; // Used to mix the original dry with the wet (effects) path.
|
||||||
|
|
||||||
|
AudioConnection patch0(i2sIn,0, myDelay,0); |
||||||
|
AudioConnection mixerDry(i2sIn,0, mixer,0); |
||||||
|
AudioConnection mixerWet(myDelay,0, mixer,1); |
||||||
|
AudioConnection leftOut(mixer,0, i2sOut, 0); |
||||||
|
AudioConnection rightOut(mixer,0, i2sOut, 1); |
||||||
|
|
||||||
|
unsigned loopCount = 0; |
||||||
|
|
||||||
|
void setup() { |
||||||
|
delay(100); |
||||||
|
Serial.begin(57600); |
||||||
|
codec.disable(); |
||||||
|
delay(100); |
||||||
|
AudioMemory(128); |
||||||
|
delay(5); |
||||||
|
|
||||||
|
// Setup MIDI
|
||||||
|
//usbMIDI.setHandleControlChange(OnControlChange);
|
||||||
|
Serial.println("Enabling codec...\n"); |
||||||
|
codec.enable(); |
||||||
|
delay(100); |
||||||
|
|
||||||
|
|
||||||
|
#ifdef USE_EXT |
||||||
|
Serial.println("Using EXTERNAL memory"); |
||||||
|
//externalSram.requestMemory(&delaySlot, 1400.0f);
|
||||||
|
//externalSram.requestMemory(&delaySlot, 1400.0f, MemSelect::MEM0, true);
|
||||||
|
externalSram.requestMemory(&delaySlot, 1400.0f, MemSelect::MEM1, true); |
||||||
|
#else |
||||||
|
Serial.println("Using INTERNAL memory"); |
||||||
|
#endif |
||||||
|
|
||||||
|
myDelay.delay(200.0f); |
||||||
|
//myDelay.delay( 128.0f/44100.0f*1000.0f);
|
||||||
|
//myDelay.delay(0, 0.0f);
|
||||||
|
//myDelay.delay((size_t)8192);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
myDelay.mapMidiBypass(16); |
||||||
|
myDelay.mapMidiDelay(20); |
||||||
|
myDelay.mapMidiFeedback(21); |
||||||
|
myDelay.mapMidiMix(22); |
||||||
|
|
||||||
|
myDelay.enable(); |
||||||
|
myDelay.bypass(false); |
||||||
|
myDelay.mix(1.0f); |
||||||
|
myDelay.feedback(0.0f); |
||||||
|
|
||||||
|
mixer.gain(0, 0.0f); // unity gain on the dry
|
||||||
|
mixer.gain(1, 1.0f); // unity gain on the wet
|
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
void OnControlChange(byte channel, byte control, byte value) { |
||||||
|
myDelay.processMidi(channel, control, value); |
||||||
|
Serial.print("Control Change, ch="); |
||||||
|
Serial.print(channel, DEC); |
||||||
|
Serial.print(", control="); |
||||||
|
Serial.print(control, DEC); |
||||||
|
Serial.print(", value="); |
||||||
|
Serial.print(value, DEC); |
||||||
|
Serial.println(); |
||||||
|
|
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
void loop() { |
||||||
|
// usbMIDI.read() needs to be called rapidly from loop(). When
|
||||||
|
// each MIDI messages arrives, it return true. The message must
|
||||||
|
// be fully processed before usbMIDI.read() is called again.
|
||||||
|
//Serial.println(".");
|
||||||
|
|
||||||
|
if (loopCount % 262144 == 0) { |
||||||
|
Serial.print("Processor Usage: "); Serial.print(AudioProcessorUsage()); |
||||||
|
Serial.print(" "); Serial.print(AudioProcessorUsageMax()); |
||||||
|
Serial.print(" Delay: "); Serial.print(myDelay.processorUsage()); |
||||||
|
Serial.print(" "); Serial.println(myDelay.processorUsageMax()); |
||||||
|
} |
||||||
|
loopCount++; |
||||||
|
|
||||||
|
if (usbMIDI.read()) { |
||||||
|
byte type, channel, data1, data2, cable; |
||||||
|
type = usbMIDI.getType(); // which MIDI message, 128-255
|
||||||
|
channel = usbMIDI.getChannel(); // which MIDI channel, 1-16
|
||||||
|
data1 = usbMIDI.getData1(); // first data byte of message, 0-127
|
||||||
|
data2 = usbMIDI.getData2(); // second data byte of message, 0-127
|
||||||
|
//cable = usbMIDI.getCable(); // which virtual cable with MIDIx8, 0-7
|
||||||
|
if (type == 3) { |
||||||
|
OnControlChange(channel-1, data1, data2); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,19 @@ |
|||||||
|
// To give your project a unique name, this code must be
|
||||||
|
// placed into a .c file (its own tab). It can not be in
|
||||||
|
// a .cpp file or your main sketch (the .ino file).
|
||||||
|
|
||||||
|
#include "usb_names.h" |
||||||
|
|
||||||
|
// Edit these lines to create your own name. The length must
|
||||||
|
// match the number of characters in your custom name.
|
||||||
|
|
||||||
|
#define MIDI_NAME {'B','l','a','c','k','a','d','d','r',' ','A','u','d','i','o',' ','T','G','A',' ','P','r','o'} |
||||||
|
#define MIDI_NAME_LEN 23 |
||||||
|
|
||||||
|
// Do not change this part. This exact format is required by USB.
|
||||||
|
|
||||||
|
struct usb_string_descriptor_struct usb_string_product_name = { |
||||||
|
2 + MIDI_NAME_LEN * 2, |
||||||
|
3, |
||||||
|
MIDI_NAME |
||||||
|
}; |
@ -0,0 +1,316 @@ |
|||||||
|
/*************************************************************************
|
||||||
|
* This demo uses the BAGuitar library to provide enhanced control of |
||||||
|
* the TGA Pro board. |
||||||
|
*
|
||||||
|
* The latest copy of the BA Guitar library can be obtained from |
||||||
|
* https://github.com/Blackaddr/BAGuitar
|
||||||
|
*
|
||||||
|
* This demo will perform a DMA memory test on MEM0 attached |
||||||
|
* to SPI. |
||||||
|
*
|
||||||
|
* NOTE: SPI MEM0 can be used by a Teensy 3.2/3.5/3.6. |
||||||
|
* pins. |
||||||
|
*
|
||||||
|
*/ |
||||||
|
#include <Wire.h> |
||||||
|
#include "BAGuitar.h" |
||||||
|
|
||||||
|
using namespace BAGuitar; |
||||||
|
|
||||||
|
//#define SANITY
|
||||||
|
#define DMA_SIZE 256 |
||||||
|
|
||||||
|
#define SPI_WRITE_CMD 0x2 |
||||||
|
#define SPI_READ_CMD 0x3 |
||||||
|
#define SPI_ADDR_2_MASK 0xFF0000 |
||||||
|
#define SPI_ADDR_2_SHIFT 16 |
||||||
|
#define SPI_ADDR_1_MASK 0x00FF00 |
||||||
|
#define SPI_ADDR_1_SHIFT 8 |
||||||
|
#define SPI_ADDR_0_MASK 0x0000FF |
||||||
|
SPISettings memSettings(20000000, MSBFIRST, SPI_MODE0); |
||||||
|
const int cs0pin = 15; |
||||||
|
|
||||||
|
BAGpio gpio; // access to User LED
|
||||||
|
BASpiMemoryDMA spiMem0(SpiDeviceId::SPI_DEVICE0); |
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
bool compareBuffers(uint8_t *a, uint8_t *b, size_t numBytes) |
||||||
|
{ |
||||||
|
bool pass=true; |
||||||
|
int errorCount = 0; |
||||||
|
for (size_t i=0; i<numBytes; i++) { |
||||||
|
if (a[i] != b[i]) { |
||||||
|
if (errorCount < 10) { |
||||||
|
Serial.print(i); Serial.print(":: a:"); Serial.print(a[i]); Serial.print(" does not match b:"); Serial.println(b[i]); |
||||||
|
pass=false; |
||||||
|
errorCount++; |
||||||
|
} else { return false; } |
||||||
|
} |
||||||
|
} |
||||||
|
return pass; |
||||||
|
} |
||||||
|
|
||||||
|
bool compareBuffers16(uint16_t *a, uint16_t *b, size_t numWords) |
||||||
|
{ |
||||||
|
return compareBuffers(reinterpret_cast<uint8_t *>(a), reinterpret_cast<uint8_t*>(b), sizeof(uint16_t)*numWords); |
||||||
|
} |
||||||
|
|
||||||
|
constexpr size_t TEST_END = SPI_MAX_ADDR; |
||||||
|
|
||||||
|
void setup() { |
||||||
|
|
||||||
|
Serial.begin(57600); |
||||||
|
while (!Serial) {} |
||||||
|
delay(5); |
||||||
|
|
||||||
|
Serial.println("Enabling SPI"); |
||||||
|
spiMem0.begin(); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
bool spi8BitTest(void) { |
||||||
|
|
||||||
|
size_t spiAddress = 0; |
||||||
|
int spiPhase = 0; |
||||||
|
constexpr uint8_t MASK80 = 0xaa; |
||||||
|
constexpr uint8_t MASK81 = 0x55; |
||||||
|
|
||||||
|
uint8_t src8[DMA_SIZE]; |
||||||
|
uint8_t dest8[DMA_SIZE]; |
||||||
|
|
||||||
|
// Write to the memory using 8-bit transfers
|
||||||
|
Serial.println("\nStarting 8-bit test Write/Read"); |
||||||
|
while (spiPhase < 4) { |
||||||
|
spiAddress = 0; |
||||||
|
while (spiAddress < TEST_END) { |
||||||
|
|
||||||
|
// fill the write data buffer
|
||||||
|
switch (spiPhase) { |
||||||
|
case 0 : |
||||||
|
for (int i=0; i<DMA_SIZE; i++) { |
||||||
|
src8[i] = ( (spiAddress+i) & 0xff) ^ MASK80; |
||||||
|
} |
||||||
|
break; |
||||||
|
case 1 : |
||||||
|
for (int i=0; i<DMA_SIZE; i++) { |
||||||
|
src8[i] = ((spiAddress+i) & 0xff) ^ MASK81; |
||||||
|
} |
||||||
|
break; |
||||||
|
case 2 : |
||||||
|
for (int i=0; i<DMA_SIZE; i++) { |
||||||
|
src8[i] = ((spiAddress+i) & 0xff) ^ (!MASK80); |
||||||
|
} |
||||||
|
break; |
||||||
|
case 3 : |
||||||
|
for (int i=0; i<DMA_SIZE; i++) { |
||||||
|
src8[i] = ((spiAddress+i) & 0xff) ^ (!MASK81); |
||||||
|
} |
||||||
|
break; |
||||||
|
}
|
||||||
|
|
||||||
|
// Send the DMA transfer
|
||||||
|
spiMem0.write(spiAddress, src8, DMA_SIZE); |
||||||
|
// wait until write is done
|
||||||
|
while (spiMem0.isWriteBusy()) {} |
||||||
|
spiAddress += DMA_SIZE;
|
||||||
|
} |
||||||
|
|
||||||
|
// Read back the data using 8-bit transfers
|
||||||
|
spiAddress = 0; |
||||||
|
while (spiAddress < TEST_END) { |
||||||
|
// generate the golden data
|
||||||
|
switch (spiPhase) { |
||||||
|
case 0 : |
||||||
|
for (int i=0; i<DMA_SIZE; i++) { |
||||||
|
src8[i] = ( (spiAddress+i) & 0xff) ^ MASK80; |
||||||
|
} |
||||||
|
break; |
||||||
|
case 1 : |
||||||
|
for (int i=0; i<DMA_SIZE; i++) { |
||||||
|
src8[i] = ((spiAddress+i) & 0xff) ^ MASK81; |
||||||
|
} |
||||||
|
break; |
||||||
|
case 2 : |
||||||
|
for (int i=0; i<DMA_SIZE; i++) { |
||||||
|
src8[i] = ((spiAddress+i) & 0xff) ^ (!MASK80); |
||||||
|
} |
||||||
|
break; |
||||||
|
case 3 : |
||||||
|
for (int i=0; i<DMA_SIZE; i++) { |
||||||
|
src8[i] = ((spiAddress+i) & 0xff) ^ (!MASK81); |
||||||
|
} |
||||||
|
break; |
||||||
|
} |
||||||
|
|
||||||
|
// Read back the DMA data
|
||||||
|
spiMem0.read(spiAddress, dest8, DMA_SIZE); |
||||||
|
// wait until read is done
|
||||||
|
while (spiMem0.isReadBusy()) {} |
||||||
|
if (!compareBuffers(src8, dest8, DMA_SIZE)) {
|
||||||
|
Serial.println(String("ERROR @") + spiAddress); |
||||||
|
return false; } // stop the test
|
||||||
|
spiAddress += DMA_SIZE;
|
||||||
|
} |
||||||
|
Serial.println(String("Phase ") + spiPhase + String(": 8-bit Write/Read test passed!")); |
||||||
|
spiPhase++; |
||||||
|
} |
||||||
|
|
||||||
|
#ifdef SANITY |
||||||
|
for (int i=0; i<DMA_SIZE; i++) { |
||||||
|
Serial.print(src8[i], HEX); Serial.print("="); Serial.println(dest8[i],HEX); |
||||||
|
} |
||||||
|
#endif |
||||||
|
|
||||||
|
Serial.println("\nStarting 8-bit test Zero/Read"); |
||||||
|
// Clear the memory
|
||||||
|
spiAddress = 0; |
||||||
|
memset(src8, 0, DMA_SIZE); |
||||||
|
while (spiAddress < SPI_MAX_ADDR) { |
||||||
|
// Send the DMA transfer
|
||||||
|
spiMem0.zero(spiAddress, DMA_SIZE); |
||||||
|
// wait until write is done
|
||||||
|
while (spiMem0.isWriteBusy()) {} |
||||||
|
spiAddress += DMA_SIZE;
|
||||||
|
} |
||||||
|
|
||||||
|
// Check the memory is clear
|
||||||
|
spiAddress = 0; |
||||||
|
while (spiAddress < SPI_MAX_ADDR) { |
||||||
|
// Read back the DMA data
|
||||||
|
spiMem0.read(spiAddress, dest8, DMA_SIZE); |
||||||
|
// wait until read is done
|
||||||
|
while (spiMem0.isReadBusy()) {} |
||||||
|
if (!compareBuffers(src8, dest8, DMA_SIZE)) { return false; } // stop the test
|
||||||
|
spiAddress += DMA_SIZE;
|
||||||
|
} |
||||||
|
Serial.println(String(": 8-bit Zero/Read test passed!")); |
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
bool spi16BitTest(void) { |
||||||
|
size_t spiAddress = 0; |
||||||
|
int spiPhase = 0; |
||||||
|
constexpr uint16_t MASK160 = 0xaaaa; |
||||||
|
constexpr uint16_t MASK161 = 0x5555; |
||||||
|
constexpr int NUM_DATA = DMA_SIZE / sizeof(uint16_t); |
||||||
|
|
||||||
|
uint16_t src16[NUM_DATA]; |
||||||
|
uint16_t dest16[NUM_DATA]; |
||||||
|
|
||||||
|
// Write to the memory using 16-bit transfers
|
||||||
|
Serial.println("\nStarting 16-bit test"); |
||||||
|
while (spiPhase < 4) { |
||||||
|
spiAddress = 0; |
||||||
|
while (spiAddress < SPI_MAX_ADDR) { |
||||||
|
|
||||||
|
// fill the write data buffer
|
||||||
|
switch (spiPhase) { |
||||||
|
case 0 : |
||||||
|
for (int i=0; i<NUM_DATA; i++) { |
||||||
|
src16[i] = ( (spiAddress+i) & 0xffff) ^ MASK160; |
||||||
|
} |
||||||
|
break; |
||||||
|
case 1 : |
||||||
|
for (int i=0; i<NUM_DATA; i++) { |
||||||
|
src16[i] = ((spiAddress+i) & 0xffff) ^ MASK161; |
||||||
|
} |
||||||
|
break; |
||||||
|
case 2 : |
||||||
|
for (int i=0; i<NUM_DATA; i++) { |
||||||
|
src16[i] = ((spiAddress+i) & 0xffff) ^ (!MASK160); |
||||||
|
} |
||||||
|
break; |
||||||
|
case 3 : |
||||||
|
for (int i=0; i<NUM_DATA; i++) { |
||||||
|
src16[i] = ((spiAddress+i) & 0xffff) ^ (!MASK161); |
||||||
|
} |
||||||
|
break; |
||||||
|
} |
||||||
|
|
||||||
|
// Send the DMA transfer
|
||||||
|
spiMem0.write16(spiAddress, src16, NUM_DATA); |
||||||
|
// wait until write is done
|
||||||
|
while (spiMem0.isWriteBusy()) {} |
||||||
|
spiAddress += DMA_SIZE;
|
||||||
|
} |
||||||
|
|
||||||
|
// Read back the data using 8-bit transfers
|
||||||
|
spiAddress = 0; |
||||||
|
while (spiAddress < SPI_MAX_ADDR) { |
||||||
|
// generate the golden data
|
||||||
|
switch (spiPhase) { |
||||||
|
case 0 : |
||||||
|
for (int i=0; i<NUM_DATA; i++) { |
||||||
|
src16[i] = ( (spiAddress+i) & 0xffff) ^ MASK160; |
||||||
|
} |
||||||
|
break; |
||||||
|
case 1 : |
||||||
|
for (int i=0; i<NUM_DATA; i++) { |
||||||
|
src16[i] = ((spiAddress+i) & 0xffff) ^ MASK161; |
||||||
|
} |
||||||
|
break; |
||||||
|
case 2 : |
||||||
|
for (int i=0; i<NUM_DATA; i++) { |
||||||
|
src16[i] = ((spiAddress+i) & 0xffff) ^ (!MASK160); |
||||||
|
} |
||||||
|
break; |
||||||
|
case 3 : |
||||||
|
for (int i=0; i<NUM_DATA; i++) { |
||||||
|
src16[i] = ((spiAddress+i) & 0xffff) ^ (!MASK161); |
||||||
|
} |
||||||
|
break; |
||||||
|
} |
||||||
|
|
||||||
|
// Read back the DMA data
|
||||||
|
spiMem0.read16(spiAddress, dest16, NUM_DATA); |
||||||
|
// wait until read is done
|
||||||
|
while (spiMem0.isReadBusy()) {} |
||||||
|
if (!compareBuffers16(src16, dest16, NUM_DATA)) {
|
||||||
|
Serial.print("ERROR @"); Serial.println(spiAddress, HEX); |
||||||
|
return false; } // stop the test
|
||||||
|
spiAddress += DMA_SIZE;
|
||||||
|
} |
||||||
|
Serial.println(String("Phase ") + spiPhase + String(": 16-bit test passed!")); |
||||||
|
spiPhase++; |
||||||
|
} |
||||||
|
#ifdef SANITY |
||||||
|
for (int i=0; i<NUM_DATA; i++) { |
||||||
|
Serial.print(src16[i], HEX); Serial.print("="); Serial.println(dest16[i],HEX); |
||||||
|
} |
||||||
|
#endif |
||||||
|
|
||||||
|
Serial.println("\nStarting 16-bit test Zero/Read"); |
||||||
|
// Clear the memory
|
||||||
|
spiAddress = 0; |
||||||
|
memset(src16, 0, DMA_SIZE); |
||||||
|
while (spiAddress < SPI_MAX_ADDR) { |
||||||
|
// Send the DMA transfer
|
||||||
|
spiMem0.zero16(spiAddress, NUM_DATA); |
||||||
|
// wait until write is done
|
||||||
|
while (spiMem0.isWriteBusy()) {} |
||||||
|
spiAddress += DMA_SIZE;
|
||||||
|
} |
||||||
|
|
||||||
|
// Check the memory is clear
|
||||||
|
spiAddress = 0; |
||||||
|
while (spiAddress < SPI_MAX_ADDR) { |
||||||
|
// Read back the DMA data
|
||||||
|
spiMem0.read16(spiAddress, dest16, NUM_DATA); |
||||||
|
// wait until read is done
|
||||||
|
while (spiMem0.isReadBusy()) {} |
||||||
|
if (!compareBuffers16(src16, dest16, NUM_DATA)) { return false; } // stop the test
|
||||||
|
spiAddress += DMA_SIZE;
|
||||||
|
} |
||||||
|
Serial.println(String(": 16-bit Zero/Read test passed!")); |
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
void loop() { |
||||||
|
|
||||||
|
spi8BitTest(); |
||||||
|
spi16BitTest(); |
||||||
|
while(true) {} |
||||||
|
|
||||||
|
} |
||||||
|
|
@ -0,0 +1,316 @@ |
|||||||
|
/*************************************************************************
|
||||||
|
* This demo uses the BAGuitar library to provide enhanced control of |
||||||
|
* the TGA Pro board. |
||||||
|
*
|
||||||
|
* The latest copy of the BA Guitar library can be obtained from |
||||||
|
* https://github.com/Blackaddr/BAGuitar
|
||||||
|
*
|
||||||
|
* This demo will perform a DMA memory test on MEM1 attached |
||||||
|
* to SPI1. |
||||||
|
*
|
||||||
|
* NOTE: SPI MEM1 can be used by a Teensy 3.5/3.6. |
||||||
|
* pins. |
||||||
|
*
|
||||||
|
*/ |
||||||
|
#include <Wire.h> |
||||||
|
#include "BAGuitar.h" |
||||||
|
|
||||||
|
using namespace BAGuitar; |
||||||
|
|
||||||
|
//#define SANITY
|
||||||
|
#define DMA_SIZE 256 |
||||||
|
|
||||||
|
#define SPI_WRITE_CMD 0x2 |
||||||
|
#define SPI_READ_CMD 0x3 |
||||||
|
#define SPI_ADDR_2_MASK 0xFF0000 |
||||||
|
#define SPI_ADDR_2_SHIFT 16 |
||||||
|
#define SPI_ADDR_1_MASK 0x00FF00 |
||||||
|
#define SPI_ADDR_1_SHIFT 8 |
||||||
|
#define SPI_ADDR_0_MASK 0x0000FF |
||||||
|
SPISettings memSettings(20000000, MSBFIRST, SPI_MODE0); |
||||||
|
const int cs0pin = 15; |
||||||
|
|
||||||
|
BAGpio gpio; // access to User LED
|
||||||
|
BASpiMemoryDMA spiMem1(SpiDeviceId::SPI_DEVICE1); |
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
bool compareBuffers(uint8_t *a, uint8_t *b, size_t numBytes) |
||||||
|
{ |
||||||
|
bool pass=true; |
||||||
|
int errorCount = 0; |
||||||
|
for (size_t i=0; i<numBytes; i++) { |
||||||
|
if (a[i] != b[i]) { |
||||||
|
if (errorCount < 10) { |
||||||
|
Serial.print(i); Serial.print(":: a:"); Serial.print(a[i]); Serial.print(" does not match b:"); Serial.println(b[i]); |
||||||
|
pass=false; |
||||||
|
errorCount++; |
||||||
|
} else { return false; } |
||||||
|
} |
||||||
|
} |
||||||
|
return pass; |
||||||
|
} |
||||||
|
|
||||||
|
bool compareBuffers16(uint16_t *a, uint16_t *b, size_t numWords) |
||||||
|
{ |
||||||
|
return compareBuffers(reinterpret_cast<uint8_t *>(a), reinterpret_cast<uint8_t*>(b), sizeof(uint16_t)*numWords); |
||||||
|
} |
||||||
|
|
||||||
|
constexpr size_t TEST_END = SPI_MAX_ADDR; |
||||||
|
|
||||||
|
void setup() { |
||||||
|
|
||||||
|
Serial.begin(57600); |
||||||
|
while (!Serial) {} |
||||||
|
delay(5); |
||||||
|
|
||||||
|
Serial.println("Enabling SPI"); |
||||||
|
spiMem1.begin(); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
bool spi8BitTest(void) { |
||||||
|
|
||||||
|
size_t spiAddress = 0; |
||||||
|
int spiPhase = 0; |
||||||
|
constexpr uint8_t MASK80 = 0xaa; |
||||||
|
constexpr uint8_t MASK81 = 0x55; |
||||||
|
|
||||||
|
uint8_t src8[DMA_SIZE]; |
||||||
|
uint8_t dest8[DMA_SIZE]; |
||||||
|
|
||||||
|
// Write to the memory using 8-bit transfers
|
||||||
|
Serial.println("\nStarting 8-bit test Write/Read"); |
||||||
|
while (spiPhase < 4) { |
||||||
|
spiAddress = 0; |
||||||
|
while (spiAddress < TEST_END) { |
||||||
|
|
||||||
|
// fill the write data buffer
|
||||||
|
switch (spiPhase) { |
||||||
|
case 0 : |
||||||
|
for (int i=0; i<DMA_SIZE; i++) { |
||||||
|
src8[i] = ( (spiAddress+i) & 0xff) ^ MASK80; |
||||||
|
} |
||||||
|
break; |
||||||
|
case 1 : |
||||||
|
for (int i=0; i<DMA_SIZE; i++) { |
||||||
|
src8[i] = ((spiAddress+i) & 0xff) ^ MASK81; |
||||||
|
} |
||||||
|
break; |
||||||
|
case 2 : |
||||||
|
for (int i=0; i<DMA_SIZE; i++) { |
||||||
|
src8[i] = ((spiAddress+i) & 0xff) ^ (!MASK80); |
||||||
|
} |
||||||
|
break; |
||||||
|
case 3 : |
||||||
|
for (int i=0; i<DMA_SIZE; i++) { |
||||||
|
src8[i] = ((spiAddress+i) & 0xff) ^ (!MASK81); |
||||||
|
} |
||||||
|
break; |
||||||
|
}
|
||||||
|
|
||||||
|
// Send the DMA transfer
|
||||||
|
spiMem1.write(spiAddress, src8, DMA_SIZE); |
||||||
|
// wait until write is done
|
||||||
|
while (spiMem1.isWriteBusy()) {} |
||||||
|
spiAddress += DMA_SIZE;
|
||||||
|
} |
||||||
|
|
||||||
|
// Read back the data using 8-bit transfers
|
||||||
|
spiAddress = 0; |
||||||
|
while (spiAddress < TEST_END) { |
||||||
|
// generate the golden data
|
||||||
|
switch (spiPhase) { |
||||||
|
case 0 : |
||||||
|
for (int i=0; i<DMA_SIZE; i++) { |
||||||
|
src8[i] = ( (spiAddress+i) & 0xff) ^ MASK80; |
||||||
|
} |
||||||
|
break; |
||||||
|
case 1 : |
||||||
|
for (int i=0; i<DMA_SIZE; i++) { |
||||||
|
src8[i] = ((spiAddress+i) & 0xff) ^ MASK81; |
||||||
|
} |
||||||
|
break; |
||||||
|
case 2 : |
||||||
|
for (int i=0; i<DMA_SIZE; i++) { |
||||||
|
src8[i] = ((spiAddress+i) & 0xff) ^ (!MASK80); |
||||||
|
} |
||||||
|
break; |
||||||
|
case 3 : |
||||||
|
for (int i=0; i<DMA_SIZE; i++) { |
||||||
|
src8[i] = ((spiAddress+i) & 0xff) ^ (!MASK81); |
||||||
|
} |
||||||
|
break; |
||||||
|
} |
||||||
|
|
||||||
|
// Read back the DMA data
|
||||||
|
spiMem1.read(spiAddress, dest8, DMA_SIZE); |
||||||
|
// wait until read is done
|
||||||
|
while (spiMem1.isReadBusy()) {} |
||||||
|
if (!compareBuffers(src8, dest8, DMA_SIZE)) {
|
||||||
|
Serial.println(String("ERROR @") + spiAddress); |
||||||
|
return false; } // stop the test
|
||||||
|
spiAddress += DMA_SIZE;
|
||||||
|
} |
||||||
|
Serial.println(String("Phase ") + spiPhase + String(": 8-bit Write/Read test passed!")); |
||||||
|
spiPhase++; |
||||||
|
} |
||||||
|
|
||||||
|
#ifdef SANITY |
||||||
|
for (int i=0; i<DMA_SIZE; i++) { |
||||||
|
Serial.print(src8[i], HEX); Serial.print("="); Serial.println(dest8[i],HEX); |
||||||
|
} |
||||||
|
#endif |
||||||
|
|
||||||
|
Serial.println("\nStarting 8-bit test Zero/Read"); |
||||||
|
// Clear the memory
|
||||||
|
spiAddress = 0; |
||||||
|
memset(src8, 0, DMA_SIZE); |
||||||
|
while (spiAddress < SPI_MAX_ADDR) { |
||||||
|
// Send the DMA transfer
|
||||||
|
spiMem1.zero(spiAddress, DMA_SIZE); |
||||||
|
// wait until write is done
|
||||||
|
while (spiMem1.isWriteBusy()) {} |
||||||
|
spiAddress += DMA_SIZE;
|
||||||
|
} |
||||||
|
|
||||||
|
// Check the memory is clear
|
||||||
|
spiAddress = 0; |
||||||
|
while (spiAddress < SPI_MAX_ADDR) { |
||||||
|
// Read back the DMA data
|
||||||
|
spiMem1.read(spiAddress, dest8, DMA_SIZE); |
||||||
|
// wait until read is done
|
||||||
|
while (spiMem1.isReadBusy()) {} |
||||||
|
if (!compareBuffers(src8, dest8, DMA_SIZE)) { return false; } // stop the test
|
||||||
|
spiAddress += DMA_SIZE;
|
||||||
|
} |
||||||
|
Serial.println(String(": 8-bit Zero/Read test passed!")); |
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
bool spi16BitTest(void) { |
||||||
|
size_t spiAddress = 0; |
||||||
|
int spiPhase = 0; |
||||||
|
constexpr uint16_t MASK160 = 0xaaaa; |
||||||
|
constexpr uint16_t MASK161 = 0x5555; |
||||||
|
constexpr int NUM_DATA = DMA_SIZE / sizeof(uint16_t); |
||||||
|
|
||||||
|
uint16_t src16[NUM_DATA]; |
||||||
|
uint16_t dest16[NUM_DATA]; |
||||||
|
|
||||||
|
// Write to the memory using 16-bit transfers
|
||||||
|
Serial.println("\nStarting 16-bit test"); |
||||||
|
while (spiPhase < 4) { |
||||||
|
spiAddress = 0; |
||||||
|
while (spiAddress < SPI_MAX_ADDR) { |
||||||
|
|
||||||
|
// fill the write data buffer
|
||||||
|
switch (spiPhase) { |
||||||
|
case 0 : |
||||||
|
for (int i=0; i<NUM_DATA; i++) { |
||||||
|
src16[i] = ( (spiAddress+i) & 0xffff) ^ MASK160; |
||||||
|
} |
||||||
|
break; |
||||||
|
case 1 : |
||||||
|
for (int i=0; i<NUM_DATA; i++) { |
||||||
|
src16[i] = ((spiAddress+i) & 0xffff) ^ MASK161; |
||||||
|
} |
||||||
|
break; |
||||||
|
case 2 : |
||||||
|
for (int i=0; i<NUM_DATA; i++) { |
||||||
|
src16[i] = ((spiAddress+i) & 0xffff) ^ (!MASK160); |
||||||
|
} |
||||||
|
break; |
||||||
|
case 3 : |
||||||
|
for (int i=0; i<NUM_DATA; i++) { |
||||||
|
src16[i] = ((spiAddress+i) & 0xffff) ^ (!MASK161); |
||||||
|
} |
||||||
|
break; |
||||||
|
} |
||||||
|
|
||||||
|
// Send the DMA transfer
|
||||||
|
spiMem1.write16(spiAddress, src16, NUM_DATA); |
||||||
|
// wait until write is done
|
||||||
|
while (spiMem1.isWriteBusy()) {} |
||||||
|
spiAddress += DMA_SIZE;
|
||||||
|
} |
||||||
|
|
||||||
|
// Read back the data using 8-bit transfers
|
||||||
|
spiAddress = 0; |
||||||
|
while (spiAddress < SPI_MAX_ADDR) { |
||||||
|
// generate the golden data
|
||||||
|
switch (spiPhase) { |
||||||
|
case 0 : |
||||||
|
for (int i=0; i<NUM_DATA; i++) { |
||||||
|
src16[i] = ( (spiAddress+i) & 0xffff) ^ MASK160; |
||||||
|
} |
||||||
|
break; |
||||||
|
case 1 : |
||||||
|
for (int i=0; i<NUM_DATA; i++) { |
||||||
|
src16[i] = ((spiAddress+i) & 0xffff) ^ MASK161; |
||||||
|
} |
||||||
|
break; |
||||||
|
case 2 : |
||||||
|
for (int i=0; i<NUM_DATA; i++) { |
||||||
|
src16[i] = ((spiAddress+i) & 0xffff) ^ (!MASK160); |
||||||
|
} |
||||||
|
break; |
||||||
|
case 3 : |
||||||
|
for (int i=0; i<NUM_DATA; i++) { |
||||||
|
src16[i] = ((spiAddress+i) & 0xffff) ^ (!MASK161); |
||||||
|
} |
||||||
|
break; |
||||||
|
} |
||||||
|
|
||||||
|
// Read back the DMA data
|
||||||
|
spiMem1.read16(spiAddress, dest16, NUM_DATA); |
||||||
|
// wait until read is done
|
||||||
|
while (spiMem1.isReadBusy()) {} |
||||||
|
if (!compareBuffers16(src16, dest16, NUM_DATA)) {
|
||||||
|
Serial.print("ERROR @"); Serial.println(spiAddress, HEX); |
||||||
|
return false; } // stop the test
|
||||||
|
spiAddress += DMA_SIZE;
|
||||||
|
} |
||||||
|
Serial.println(String("Phase ") + spiPhase + String(": 16-bit test passed!")); |
||||||
|
spiPhase++; |
||||||
|
} |
||||||
|
#ifdef SANITY |
||||||
|
for (int i=0; i<NUM_DATA; i++) { |
||||||
|
Serial.print(src16[i], HEX); Serial.print("="); Serial.println(dest16[i],HEX); |
||||||
|
} |
||||||
|
#endif |
||||||
|
|
||||||
|
Serial.println("\nStarting 16-bit test Zero/Read"); |
||||||
|
// Clear the memory
|
||||||
|
spiAddress = 0; |
||||||
|
memset(src16, 0, DMA_SIZE); |
||||||
|
while (spiAddress < SPI_MAX_ADDR) { |
||||||
|
// Send the DMA transfer
|
||||||
|
spiMem1.zero16(spiAddress, NUM_DATA); |
||||||
|
// wait until write is done
|
||||||
|
while (spiMem1.isWriteBusy()) {} |
||||||
|
spiAddress += DMA_SIZE;
|
||||||
|
} |
||||||
|
|
||||||
|
// Check the memory is clear
|
||||||
|
spiAddress = 0; |
||||||
|
while (spiAddress < SPI_MAX_ADDR) { |
||||||
|
// Read back the DMA data
|
||||||
|
spiMem1.read16(spiAddress, dest16, NUM_DATA); |
||||||
|
// wait until read is done
|
||||||
|
while (spiMem1.isReadBusy()) {} |
||||||
|
if (!compareBuffers16(src16, dest16, NUM_DATA)) { return false; } // stop the test
|
||||||
|
spiAddress += DMA_SIZE;
|
||||||
|
} |
||||||
|
Serial.println(String(": 16-bit Zero/Read test passed!")); |
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
void loop() { |
||||||
|
|
||||||
|
spi8BitTest(); |
||||||
|
spi16BitTest(); |
||||||
|
while(true) {} |
||||||
|
|
||||||
|
} |
||||||
|
|
@ -1,356 +0,0 @@ |
|||||||
/*
|
|
||||||
* LibBasicFunctions.cpp |
|
||||||
* |
|
||||||
* Created on: Dec 23, 2017 |
|
||||||
* Author: slascos |
|
||||||
*/ |
|
||||||
|
|
||||||
#include "Audio.h" |
|
||||||
#include "LibBasicFunctions.h" |
|
||||||
|
|
||||||
namespace BAGuitar { |
|
||||||
|
|
||||||
size_t calcAudioSamples(float milliseconds) |
|
||||||
{ |
|
||||||
return (size_t)((milliseconds*(AUDIO_SAMPLE_RATE_EXACT/1000.0f))+0.5f); |
|
||||||
} |
|
||||||
|
|
||||||
QueuePosition calcQueuePosition(size_t numSamples) |
|
||||||
{ |
|
||||||
QueuePosition queuePosition; |
|
||||||
queuePosition.index = (int)(numSamples / AUDIO_BLOCK_SAMPLES); |
|
||||||
queuePosition.offset = numSamples % AUDIO_BLOCK_SAMPLES; |
|
||||||
return queuePosition; |
|
||||||
} |
|
||||||
QueuePosition calcQueuePosition(float milliseconds) { |
|
||||||
size_t numSamples = (int)((milliseconds*(AUDIO_SAMPLE_RATE_EXACT/1000.0f))+0.5f); |
|
||||||
return calcQueuePosition(numSamples); |
|
||||||
} |
|
||||||
|
|
||||||
size_t calcOffset(QueuePosition position) |
|
||||||
{ |
|
||||||
return (position.index*AUDIO_BLOCK_SAMPLES) + position.offset; |
|
||||||
} |
|
||||||
|
|
||||||
void alphaBlend(audio_block_t *out, audio_block_t *dry, audio_block_t* wet, float mix) |
|
||||||
{ |
|
||||||
//Non-optimized version for illustrative purposes
|
|
||||||
// for (int i=0; i< AUDIO_BLOCK_SAMPLES; i++) {
|
|
||||||
// out->data[i] = (dry->data[i] * (1 - mix)) + (wet->data[i] * mix);
|
|
||||||
// }
|
|
||||||
// return;
|
|
||||||
|
|
||||||
// ARM DSP optimized
|
|
||||||
int16_t wetBuffer[AUDIO_BLOCK_SAMPLES]; |
|
||||||
int16_t dryBuffer[AUDIO_BLOCK_SAMPLES]; |
|
||||||
int16_t scaleFractWet = (int16_t)(mix * 32767.0f); |
|
||||||
int16_t scaleFractDry = 32767-scaleFractWet; |
|
||||||
|
|
||||||
arm_scale_q15(dry->data, scaleFractDry, 0, dryBuffer, AUDIO_BLOCK_SAMPLES); |
|
||||||
arm_scale_q15(wet->data, scaleFractWet, 0, wetBuffer, AUDIO_BLOCK_SAMPLES); |
|
||||||
arm_add_q15(wetBuffer, dryBuffer, out->data, AUDIO_BLOCK_SAMPLES); |
|
||||||
} |
|
||||||
|
|
||||||
void clearAudioBlock(audio_block_t *block) |
|
||||||
{ |
|
||||||
memset(block->data, 0, sizeof(int16_t)*AUDIO_BLOCK_SAMPLES); |
|
||||||
} |
|
||||||
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////
|
|
||||||
// AudioDelay
|
|
||||||
////////////////////////////////////////////////////
|
|
||||||
AudioDelay::AudioDelay(size_t maxSamples) |
|
||||||
: m_slot(nullptr) |
|
||||||
{ |
|
||||||
m_type = (MemType::MEM_INTERNAL); |
|
||||||
|
|
||||||
// INTERNAL memory consisting of audio_block_t data structures.
|
|
||||||
QueuePosition pos = calcQueuePosition(maxSamples); |
|
||||||
m_ringBuffer = new RingBuffer<audio_block_t *>(pos.index+2); // If the delay is in queue x, we need to overflow into x+1, thus x+2 total buffers.
|
|
||||||
} |
|
||||||
|
|
||||||
AudioDelay::AudioDelay(float maxDelayTimeMs) |
|
||||||
: AudioDelay(calcAudioSamples(maxDelayTimeMs)) |
|
||||||
{ |
|
||||||
|
|
||||||
} |
|
||||||
|
|
||||||
AudioDelay::AudioDelay(ExtMemSlot *slot) |
|
||||||
{ |
|
||||||
m_type = (MemType::MEM_EXTERNAL); |
|
||||||
m_slot = slot; |
|
||||||
} |
|
||||||
|
|
||||||
AudioDelay::~AudioDelay() |
|
||||||
{ |
|
||||||
if (m_ringBuffer) delete m_ringBuffer; |
|
||||||
} |
|
||||||
|
|
||||||
audio_block_t* AudioDelay::addBlock(audio_block_t *block) |
|
||||||
{ |
|
||||||
audio_block_t *blockToRelease = nullptr; |
|
||||||
|
|
||||||
if (m_type == (MemType::MEM_INTERNAL)) { |
|
||||||
// INTERNAL memory
|
|
||||||
|
|
||||||
// purposefully don't check if block is valid, the ringBuffer can support nullptrs
|
|
||||||
if ( m_ringBuffer->size() >= m_ringBuffer->max_size() ) { |
|
||||||
// pop before adding
|
|
||||||
blockToRelease = m_ringBuffer->front(); |
|
||||||
m_ringBuffer->pop_front(); |
|
||||||
} |
|
||||||
|
|
||||||
// add the new buffer
|
|
||||||
m_ringBuffer->push_back(block); |
|
||||||
return blockToRelease; |
|
||||||
|
|
||||||
} else { |
|
||||||
// EXTERNAL memory
|
|
||||||
if (!m_slot) { Serial.println("addBlock(): m_slot is not valid"); } |
|
||||||
|
|
||||||
if (block) { |
|
||||||
|
|
||||||
// Audio is stored in reverse in block so we need to write it backwards to external memory
|
|
||||||
// to maintain temporal coherency.
|
|
||||||
// int16_t *srcPtr = block->data + AUDIO_BLOCK_SAMPLES - 1;
|
|
||||||
// for (int i=0; i<AUDIO_BLOCK_SAMPLES; i++) {
|
|
||||||
// m_slot->writeAdvance16(*srcPtr);
|
|
||||||
// srcPtr--;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// this causes pops
|
|
||||||
m_slot->writeAdvance16(block->data, AUDIO_BLOCK_SAMPLES); |
|
||||||
|
|
||||||
// Code below worked
|
|
||||||
// int16_t *srcPtr = block->data;
|
|
||||||
// for (int i=0; i<AUDIO_BLOCK_SAMPLES; i++) {
|
|
||||||
// m_slot->writeAdvance16(*srcPtr);
|
|
||||||
// srcPtr++;
|
|
||||||
// }
|
|
||||||
|
|
||||||
} |
|
||||||
blockToRelease = block; |
|
||||||
} |
|
||||||
return blockToRelease; |
|
||||||
} |
|
||||||
|
|
||||||
audio_block_t* AudioDelay::getBlock(size_t index) |
|
||||||
{ |
|
||||||
audio_block_t *ret = nullptr; |
|
||||||
if (m_type == (MemType::MEM_INTERNAL)) { |
|
||||||
ret = m_ringBuffer->at(m_ringBuffer->get_index_from_back(index)); |
|
||||||
} |
|
||||||
return ret; |
|
||||||
} |
|
||||||
|
|
||||||
bool AudioDelay::getSamples(audio_block_t *dest, size_t offsetSamples, size_t numSamples) |
|
||||||
{ |
|
||||||
if (!dest) { |
|
||||||
Serial.println("getSamples(): dest is invalid"); |
|
||||||
return false; |
|
||||||
} |
|
||||||
|
|
||||||
if (m_type == (MemType::MEM_INTERNAL)) { |
|
||||||
QueuePosition position = calcQueuePosition(offsetSamples); |
|
||||||
size_t index = position.index; |
|
||||||
|
|
||||||
audio_block_t *currentQueue0 = m_ringBuffer->at(m_ringBuffer->get_index_from_back(index)); |
|
||||||
// The latest buffer is at the back. We need index+1 counting from the back.
|
|
||||||
audio_block_t *currentQueue1 = m_ringBuffer->at(m_ringBuffer->get_index_from_back(index+1)); |
|
||||||
|
|
||||||
// check if either queue is invalid, if so just zero the destination buffer
|
|
||||||
if ( (!currentQueue0) || (!currentQueue0) ) { |
|
||||||
// a valid entry is not in all queue positions while it is filling, use zeros
|
|
||||||
memset(static_cast<void*>(dest->data), 0, numSamples * sizeof(int16_t)); |
|
||||||
return true; |
|
||||||
} |
|
||||||
|
|
||||||
if (position.offset == 0) { |
|
||||||
// single transfer
|
|
||||||
memcpy(static_cast<void*>(dest->data), static_cast<void*>(currentQueue0->data), numSamples * sizeof(int16_t)); |
|
||||||
return true; |
|
||||||
} |
|
||||||
|
|
||||||
// Otherwise we need to break the transfer into two memcpy because it will go across two source queues.
|
|
||||||
// Audio is stored in reverse order. That means the first sample (in time) goes in the last location in the audio block.
|
|
||||||
int16_t *destStart = dest->data; |
|
||||||
int16_t *srcStart; |
|
||||||
|
|
||||||
// Break the transfer into two. Copy the 'older' data first then the 'newer' data with respect to current time.
|
|
||||||
//currentQueue = m_ringBuffer->at(m_ringBuffer->get_index_from_back(index+1)); // The latest buffer is at the back. We need index+1 counting from the back.
|
|
||||||
srcStart = (currentQueue1->data + AUDIO_BLOCK_SAMPLES - position.offset); |
|
||||||
size_t numData = position.offset; |
|
||||||
memcpy(static_cast<void*>(destStart), static_cast<void*>(srcStart), numData * sizeof(int16_t)); |
|
||||||
|
|
||||||
//currentQueue = m_ringBuffer->at(m_ringBuffer->get_index_from_back(index)); // now grab the queue where the 'first' data sample was
|
|
||||||
destStart += numData; // we already wrote numData so advance by this much.
|
|
||||||
srcStart = (currentQueue0->data); |
|
||||||
numData = AUDIO_BLOCK_SAMPLES - numData; |
|
||||||
memcpy(static_cast<void*>(destStart), static_cast<void*>(srcStart), numData * sizeof(int16_t)); |
|
||||||
|
|
||||||
return true; |
|
||||||
|
|
||||||
} else { |
|
||||||
// EXTERNAL Memory
|
|
||||||
if (numSamples*sizeof(int16_t) <= m_slot->size() ) { |
|
||||||
int currentPositionBytes = (int)m_slot->getWritePosition() - (int)(AUDIO_BLOCK_SAMPLES*sizeof(int16_t)); |
|
||||||
size_t offsetBytes = offsetSamples * sizeof(int16_t); |
|
||||||
|
|
||||||
if ((int)offsetBytes <= currentPositionBytes) { |
|
||||||
m_slot->setReadPosition(currentPositionBytes - offsetBytes); |
|
||||||
} else { |
|
||||||
// It's going to wrap around to the end of the slot
|
|
||||||
int readPosition = (int)m_slot->size() + currentPositionBytes - offsetBytes; |
|
||||||
m_slot->setReadPosition((size_t)readPosition); |
|
||||||
} |
|
||||||
|
|
||||||
//m_slot->printStatus();
|
|
||||||
|
|
||||||
// write the data to the destination block in reverse
|
|
||||||
// int16_t *destPtr = dest->data + AUDIO_BLOCK_SAMPLES-1;
|
|
||||||
// for (int i=0; i<AUDIO_BLOCK_SAMPLES; i++) {
|
|
||||||
// *destPtr = m_slot->readAdvance16();
|
|
||||||
// destPtr--;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// This causes pops
|
|
||||||
m_slot->readAdvance16(dest->data, AUDIO_BLOCK_SAMPLES); |
|
||||||
|
|
||||||
// Code below worked
|
|
||||||
// int16_t *destPtr = dest->data;
|
|
||||||
// for (int i=0; i<AUDIO_BLOCK_SAMPLES; i++) {
|
|
||||||
// *destPtr = m_slot->readAdvance16();
|
|
||||||
// destPtr++;
|
|
||||||
// }
|
|
||||||
|
|
||||||
return true; |
|
||||||
} else { |
|
||||||
// numSampmles is > than total slot size
|
|
||||||
Serial.println("getSamples(): ERROR numSamples > total slot size"); |
|
||||||
return false; |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
} |
|
||||||
|
|
||||||
////////////////////////////////////////////////////
|
|
||||||
// IirBiQuadFilter
|
|
||||||
////////////////////////////////////////////////////
|
|
||||||
IirBiQuadFilter::IirBiQuadFilter(unsigned numStages, const int32_t *coeffs, int coeffShift) |
|
||||||
: NUM_STAGES(numStages) |
|
||||||
{ |
|
||||||
m_coeffs = new int32_t[5*numStages]; |
|
||||||
memcpy(m_coeffs, coeffs, 5*numStages * sizeof(int32_t)); |
|
||||||
|
|
||||||
m_state = new int32_t[4*numStages]; |
|
||||||
arm_biquad_cascade_df1_init_q31(&m_iirCfg, numStages, m_coeffs, m_state, coeffShift); |
|
||||||
} |
|
||||||
|
|
||||||
IirBiQuadFilter::~IirBiQuadFilter() |
|
||||||
{ |
|
||||||
if (m_coeffs) delete [] m_coeffs; |
|
||||||
if (m_state) delete [] m_state; |
|
||||||
} |
|
||||||
|
|
||||||
|
|
||||||
bool IirBiQuadFilter::process(int16_t *output, int16_t *input, size_t numSamples) |
|
||||||
{ |
|
||||||
if (!output) return false; |
|
||||||
if (!input) { |
|
||||||
// send zeros
|
|
||||||
memset(output, 0, numSamples * sizeof(int16_t)); |
|
||||||
} else { |
|
||||||
|
|
||||||
// create convertion buffers on teh stack
|
|
||||||
int32_t input32[numSamples]; |
|
||||||
int32_t output32[numSamples]; |
|
||||||
for (size_t i=0; i<numSamples; i++) { |
|
||||||
input32[i] = (int32_t)(input[i]); |
|
||||||
} |
|
||||||
|
|
||||||
arm_biquad_cascade_df1_fast_q31(&m_iirCfg, input32, output32, numSamples); |
|
||||||
|
|
||||||
for (size_t i=0; i<numSamples; i++) { |
|
||||||
output[i] = (int16_t)(output32[i] & 0xffff); |
|
||||||
} |
|
||||||
} |
|
||||||
return true; |
|
||||||
} |
|
||||||
|
|
||||||
// HIGH QUALITY
|
|
||||||
IirBiQuadFilterHQ::IirBiQuadFilterHQ(unsigned numStages, const int32_t *coeffs, int coeffShift) |
|
||||||
: NUM_STAGES(numStages) |
|
||||||
{ |
|
||||||
m_coeffs = new int32_t[5*numStages]; |
|
||||||
memcpy(m_coeffs, coeffs, 5*numStages * sizeof(int32_t)); |
|
||||||
|
|
||||||
m_state = new int64_t[4*numStages];; |
|
||||||
arm_biquad_cas_df1_32x64_init_q31(&m_iirCfg, numStages, m_coeffs, m_state, coeffShift); |
|
||||||
} |
|
||||||
|
|
||||||
IirBiQuadFilterHQ::~IirBiQuadFilterHQ() |
|
||||||
{ |
|
||||||
if (m_coeffs) delete [] m_coeffs; |
|
||||||
if (m_state) delete [] m_state; |
|
||||||
} |
|
||||||
|
|
||||||
|
|
||||||
bool IirBiQuadFilterHQ::process(int16_t *output, int16_t *input, size_t numSamples) |
|
||||||
{ |
|
||||||
if (!output) return false; |
|
||||||
if (!input) { |
|
||||||
// send zeros
|
|
||||||
memset(output, 0, numSamples * sizeof(int16_t)); |
|
||||||
} else { |
|
||||||
|
|
||||||
// create convertion buffers on teh stack
|
|
||||||
int32_t input32[numSamples]; |
|
||||||
int32_t output32[numSamples]; |
|
||||||
for (size_t i=0; i<numSamples; i++) { |
|
||||||
input32[i] = (int32_t)(input[i]); |
|
||||||
} |
|
||||||
|
|
||||||
arm_biquad_cas_df1_32x64_q31(&m_iirCfg, input32, output32, numSamples); |
|
||||||
|
|
||||||
for (size_t i=0; i<numSamples; i++) { |
|
||||||
output[i] = (int16_t)(output32[i] & 0xffff); |
|
||||||
} |
|
||||||
} |
|
||||||
return true; |
|
||||||
} |
|
||||||
|
|
||||||
// FLOAT
|
|
||||||
IirBiQuadFilterFloat::IirBiQuadFilterFloat(unsigned numStages, const float *coeffs) |
|
||||||
: NUM_STAGES(numStages) |
|
||||||
{ |
|
||||||
m_coeffs = new float[5*numStages]; |
|
||||||
memcpy(m_coeffs, coeffs, 5*numStages * sizeof(float)); |
|
||||||
|
|
||||||
m_state = new float[4*numStages];; |
|
||||||
arm_biquad_cascade_df2T_init_f32(&m_iirCfg, numStages, m_coeffs, m_state); |
|
||||||
} |
|
||||||
|
|
||||||
IirBiQuadFilterFloat::~IirBiQuadFilterFloat() |
|
||||||
{ |
|
||||||
if (m_coeffs) delete [] m_coeffs; |
|
||||||
if (m_state) delete [] m_state; |
|
||||||
} |
|
||||||
|
|
||||||
|
|
||||||
bool IirBiQuadFilterFloat::process(float *output, float *input, size_t numSamples) |
|
||||||
{ |
|
||||||
if (!output) return false; |
|
||||||
if (!input) { |
|
||||||
// send zeros
|
|
||||||
memset(output, 0, numSamples * sizeof(float)); |
|
||||||
} else { |
|
||||||
|
|
||||||
arm_biquad_cascade_df2T_f32(&m_iirCfg, input, output, numSamples); |
|
||||||
|
|
||||||
} |
|
||||||
return true; |
|
||||||
} |
|
||||||
|
|
||||||
} |
|
||||||
|
|
@ -0,0 +1,171 @@ |
|||||||
|
/*
|
||||||
|
* AudioDelay.cpp |
||||||
|
* |
||||||
|
* Created on: January 1, 2018 |
||||||
|
* Author: slascos |
||||||
|
* |
||||||
|
* This program is free software: you can redistribute it and/or modify |
||||||
|
* it under the terms of the GNU General Public License as published by |
||||||
|
* the Free Software Foundation, either version 3 of the License, or |
||||||
|
* (at your option) any later version.* |
||||||
|
* |
||||||
|
* This program is distributed in the hope that it will be useful, |
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||||
|
* GNU General Public License for more details. |
||||||
|
* |
||||||
|
* You should have received a copy of the GNU General Public License |
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/ |
||||||
|
|
||||||
|
#include "Audio.h" |
||||||
|
#include "LibBasicFunctions.h" |
||||||
|
|
||||||
|
namespace BAGuitar { |
||||||
|
|
||||||
|
////////////////////////////////////////////////////
|
||||||
|
// AudioDelay
|
||||||
|
////////////////////////////////////////////////////
|
||||||
|
AudioDelay::AudioDelay(size_t maxSamples) |
||||||
|
: m_slot(nullptr) |
||||||
|
{ |
||||||
|
m_type = (MemType::MEM_INTERNAL); |
||||||
|
|
||||||
|
// INTERNAL memory consisting of audio_block_t data structures.
|
||||||
|
QueuePosition pos = calcQueuePosition(maxSamples); |
||||||
|
m_ringBuffer = new RingBuffer<audio_block_t *>(pos.index+2); // If the delay is in queue x, we need to overflow into x+1, thus x+2 total buffers.
|
||||||
|
} |
||||||
|
|
||||||
|
AudioDelay::AudioDelay(float maxDelayTimeMs) |
||||||
|
: AudioDelay(calcAudioSamples(maxDelayTimeMs)) |
||||||
|
{ |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
AudioDelay::AudioDelay(ExtMemSlot *slot) |
||||||
|
{ |
||||||
|
m_type = (MemType::MEM_EXTERNAL); |
||||||
|
m_slot = slot; |
||||||
|
} |
||||||
|
|
||||||
|
AudioDelay::~AudioDelay() |
||||||
|
{ |
||||||
|
if (m_ringBuffer) delete m_ringBuffer; |
||||||
|
} |
||||||
|
|
||||||
|
audio_block_t* AudioDelay::addBlock(audio_block_t *block) |
||||||
|
{ |
||||||
|
audio_block_t *blockToRelease = nullptr; |
||||||
|
|
||||||
|
if (m_type == (MemType::MEM_INTERNAL)) { |
||||||
|
// INTERNAL memory
|
||||||
|
|
||||||
|
// purposefully don't check if block is valid, the ringBuffer can support nullptrs
|
||||||
|
if ( m_ringBuffer->size() >= m_ringBuffer->max_size() ) { |
||||||
|
// pop before adding
|
||||||
|
blockToRelease = m_ringBuffer->front(); |
||||||
|
m_ringBuffer->pop_front(); |
||||||
|
} |
||||||
|
|
||||||
|
// add the new buffer
|
||||||
|
m_ringBuffer->push_back(block); |
||||||
|
return blockToRelease; |
||||||
|
|
||||||
|
} else { |
||||||
|
// EXTERNAL memory
|
||||||
|
if (!m_slot) { Serial.println("addBlock(): m_slot is not valid"); } |
||||||
|
|
||||||
|
if (block) { |
||||||
|
// this causes pops
|
||||||
|
m_slot->writeAdvance16(block->data, AUDIO_BLOCK_SAMPLES); |
||||||
|
} |
||||||
|
blockToRelease = block; |
||||||
|
} |
||||||
|
return blockToRelease; |
||||||
|
} |
||||||
|
|
||||||
|
audio_block_t* AudioDelay::getBlock(size_t index) |
||||||
|
{ |
||||||
|
audio_block_t *ret = nullptr; |
||||||
|
if (m_type == (MemType::MEM_INTERNAL)) { |
||||||
|
ret = m_ringBuffer->at(m_ringBuffer->get_index_from_back(index)); |
||||||
|
} |
||||||
|
return ret; |
||||||
|
} |
||||||
|
|
||||||
|
bool AudioDelay::getSamples(audio_block_t *dest, size_t offsetSamples, size_t numSamples) |
||||||
|
{ |
||||||
|
if (!dest) { |
||||||
|
Serial.println("getSamples(): dest is invalid"); |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
if (m_type == (MemType::MEM_INTERNAL)) { |
||||||
|
QueuePosition position = calcQueuePosition(offsetSamples); |
||||||
|
size_t index = position.index; |
||||||
|
|
||||||
|
audio_block_t *currentQueue0 = m_ringBuffer->at(m_ringBuffer->get_index_from_back(index)); |
||||||
|
// The latest buffer is at the back. We need index+1 counting from the back.
|
||||||
|
audio_block_t *currentQueue1 = m_ringBuffer->at(m_ringBuffer->get_index_from_back(index+1)); |
||||||
|
|
||||||
|
// check if either queue is invalid, if so just zero the destination buffer
|
||||||
|
if ( (!currentQueue0) || (!currentQueue0) ) { |
||||||
|
// a valid entry is not in all queue positions while it is filling, use zeros
|
||||||
|
memset(static_cast<void*>(dest->data), 0, numSamples * sizeof(int16_t)); |
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
if (position.offset == 0) { |
||||||
|
// single transfer
|
||||||
|
memcpy(static_cast<void*>(dest->data), static_cast<void*>(currentQueue0->data), numSamples * sizeof(int16_t)); |
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
// Otherwise we need to break the transfer into two memcpy because it will go across two source queues.
|
||||||
|
// Audio is stored in reverse order. That means the first sample (in time) goes in the last location in the audio block.
|
||||||
|
int16_t *destStart = dest->data; |
||||||
|
int16_t *srcStart; |
||||||
|
|
||||||
|
// Break the transfer into two. Copy the 'older' data first then the 'newer' data with respect to current time.
|
||||||
|
//currentQueue = m_ringBuffer->at(m_ringBuffer->get_index_from_back(index+1)); // The latest buffer is at the back. We need index+1 counting from the back.
|
||||||
|
srcStart = (currentQueue1->data + AUDIO_BLOCK_SAMPLES - position.offset); |
||||||
|
size_t numData = position.offset; |
||||||
|
memcpy(static_cast<void*>(destStart), static_cast<void*>(srcStart), numData * sizeof(int16_t)); |
||||||
|
|
||||||
|
//currentQueue = m_ringBuffer->at(m_ringBuffer->get_index_from_back(index)); // now grab the queue where the 'first' data sample was
|
||||||
|
destStart += numData; // we already wrote numData so advance by this much.
|
||||||
|
srcStart = (currentQueue0->data); |
||||||
|
numData = AUDIO_BLOCK_SAMPLES - numData; |
||||||
|
memcpy(static_cast<void*>(destStart), static_cast<void*>(srcStart), numData * sizeof(int16_t)); |
||||||
|
|
||||||
|
return true; |
||||||
|
|
||||||
|
} else { |
||||||
|
// EXTERNAL Memory
|
||||||
|
if (numSamples*sizeof(int16_t) <= m_slot->size() ) { |
||||||
|
int currentPositionBytes = (int)m_slot->getWritePosition() - (int)(AUDIO_BLOCK_SAMPLES*sizeof(int16_t)); |
||||||
|
size_t offsetBytes = offsetSamples * sizeof(int16_t); |
||||||
|
|
||||||
|
if ((int)offsetBytes <= currentPositionBytes) { |
||||||
|
m_slot->setReadPosition(currentPositionBytes - offsetBytes); |
||||||
|
} else { |
||||||
|
// It's going to wrap around to the end of the slot
|
||||||
|
int readPosition = (int)m_slot->size() + currentPositionBytes - offsetBytes; |
||||||
|
m_slot->setReadPosition((size_t)readPosition); |
||||||
|
} |
||||||
|
|
||||||
|
// This causes pops
|
||||||
|
m_slot->readAdvance16(dest->data, AUDIO_BLOCK_SAMPLES); |
||||||
|
|
||||||
|
return true; |
||||||
|
} else { |
||||||
|
// numSampmles is > than total slot size
|
||||||
|
Serial.println("getSamples(): ERROR numSamples > total slot size"); |
||||||
|
return false; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
|
@ -0,0 +1,73 @@ |
|||||||
|
/*
|
||||||
|
* AudioHelpers.cpp |
||||||
|
* |
||||||
|
* Created on: January 1, 2018 |
||||||
|
* Author: slascos |
||||||
|
* |
||||||
|
* This program is free software: you can redistribute it and/or modify |
||||||
|
* it under the terms of the GNU General Public License as published by |
||||||
|
* the Free Software Foundation, either version 3 of the License, or |
||||||
|
* (at your option) any later version.* |
||||||
|
* |
||||||
|
* This program is distributed in the hope that it will be useful, |
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||||
|
* GNU General Public License for more details. |
||||||
|
* |
||||||
|
* You should have received a copy of the GNU General Public License |
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/ |
||||||
|
|
||||||
|
#include "Audio.h" |
||||||
|
#include "LibBasicFunctions.h" |
||||||
|
|
||||||
|
namespace BAGuitar { |
||||||
|
|
||||||
|
size_t calcAudioSamples(float milliseconds) |
||||||
|
{ |
||||||
|
return (size_t)((milliseconds*(AUDIO_SAMPLE_RATE_EXACT/1000.0f))+0.5f); |
||||||
|
} |
||||||
|
|
||||||
|
QueuePosition calcQueuePosition(size_t numSamples) |
||||||
|
{ |
||||||
|
QueuePosition queuePosition; |
||||||
|
queuePosition.index = (int)(numSamples / AUDIO_BLOCK_SAMPLES); |
||||||
|
queuePosition.offset = numSamples % AUDIO_BLOCK_SAMPLES; |
||||||
|
return queuePosition; |
||||||
|
} |
||||||
|
QueuePosition calcQueuePosition(float milliseconds) { |
||||||
|
size_t numSamples = (int)((milliseconds*(AUDIO_SAMPLE_RATE_EXACT/1000.0f))+0.5f); |
||||||
|
return calcQueuePosition(numSamples); |
||||||
|
} |
||||||
|
|
||||||
|
size_t calcOffset(QueuePosition position) |
||||||
|
{ |
||||||
|
return (position.index*AUDIO_BLOCK_SAMPLES) + position.offset; |
||||||
|
} |
||||||
|
|
||||||
|
void alphaBlend(audio_block_t *out, audio_block_t *dry, audio_block_t* wet, float mix) |
||||||
|
{ |
||||||
|
//Non-optimized version for illustrative purposes
|
||||||
|
// for (int i=0; i< AUDIO_BLOCK_SAMPLES; i++) {
|
||||||
|
// out->data[i] = (dry->data[i] * (1 - mix)) + (wet->data[i] * mix);
|
||||||
|
// }
|
||||||
|
// return;
|
||||||
|
|
||||||
|
// ARM DSP optimized
|
||||||
|
int16_t wetBuffer[AUDIO_BLOCK_SAMPLES]; |
||||||
|
int16_t dryBuffer[AUDIO_BLOCK_SAMPLES]; |
||||||
|
int16_t scaleFractWet = (int16_t)(mix * 32767.0f); |
||||||
|
int16_t scaleFractDry = 32767-scaleFractWet; |
||||||
|
|
||||||
|
arm_scale_q15(dry->data, scaleFractDry, 0, dryBuffer, AUDIO_BLOCK_SAMPLES); |
||||||
|
arm_scale_q15(wet->data, scaleFractWet, 0, wetBuffer, AUDIO_BLOCK_SAMPLES); |
||||||
|
arm_add_q15(wetBuffer, dryBuffer, out->data, AUDIO_BLOCK_SAMPLES); |
||||||
|
} |
||||||
|
|
||||||
|
void clearAudioBlock(audio_block_t *block) |
||||||
|
{ |
||||||
|
memset(block->data, 0, sizeof(int16_t)*AUDIO_BLOCK_SAMPLES); |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
|
@ -0,0 +1,113 @@ |
|||||||
|
/*
|
||||||
|
* LibMemoryManagement.cpp |
||||||
|
* |
||||||
|
* Created on: Jan 19, 2018 |
||||||
|
* Author: slascos |
||||||
|
* |
||||||
|
* This program is free software: you can redistribute it and/or modify |
||||||
|
* it under the terms of the GNU General Public License as published by |
||||||
|
* the Free Software Foundation, either version 3 of the License, or |
||||||
|
* (at your option) any later version.* |
||||||
|
* |
||||||
|
* This program is distributed in the hope that it will be useful, |
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||||
|
* GNU General Public License for more details. |
||||||
|
* |
||||||
|
* You should have received a copy of the GNU General Public License |
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/ |
||||||
|
#include <cstring> |
||||||
|
#include <new> |
||||||
|
|
||||||
|
#include "Audio.h" |
||||||
|
#include "LibMemoryManagement.h" |
||||||
|
|
||||||
|
namespace BAGuitar { |
||||||
|
|
||||||
|
/////////////////////////////////////////////////////////////////////////////
|
||||||
|
// EXTERNAL SRAM MANAGER
|
||||||
|
/////////////////////////////////////////////////////////////////////////////
|
||||||
|
bool ExternalSramManager::m_configured = false; |
||||||
|
MemConfig ExternalSramManager::m_memConfig[BAGuitar::NUM_MEM_SLOTS]; |
||||||
|
|
||||||
|
ExternalSramManager::ExternalSramManager(unsigned numMemories) |
||||||
|
{ |
||||||
|
// Initialize the static memory configuration structs
|
||||||
|
if (!m_configured) { |
||||||
|
for (unsigned i=0; i < NUM_MEM_SLOTS; i++) { |
||||||
|
m_memConfig[i].size = MEM_MAX_ADDR[i]; |
||||||
|
m_memConfig[i].totalAvailable = MEM_MAX_ADDR[i]; |
||||||
|
m_memConfig[i].nextAvailable = 0; |
||||||
|
|
||||||
|
m_memConfig[i].m_spi = nullptr; |
||||||
|
} |
||||||
|
m_configured = true; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
ExternalSramManager::~ExternalSramManager() |
||||||
|
{ |
||||||
|
for (unsigned i=0; i < NUM_MEM_SLOTS; i++) { |
||||||
|
if (m_memConfig[i].m_spi) { delete m_memConfig[i].m_spi; } |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
size_t ExternalSramManager::availableMemory(BAGuitar::MemSelect mem) |
||||||
|
{ |
||||||
|
return m_memConfig[mem].totalAvailable; |
||||||
|
} |
||||||
|
|
||||||
|
bool ExternalSramManager::requestMemory(ExtMemSlot *slot, float delayMilliseconds, BAGuitar::MemSelect mem, bool useDma) |
||||||
|
{ |
||||||
|
// convert the time to numer of samples
|
||||||
|
size_t delayLengthInt = (size_t)((delayMilliseconds*(AUDIO_SAMPLE_RATE_EXACT/1000.0f))+0.5f); |
||||||
|
return requestMemory(slot, delayLengthInt * sizeof(int16_t), mem, useDma); |
||||||
|
} |
||||||
|
|
||||||
|
bool ExternalSramManager::requestMemory(ExtMemSlot *slot, size_t sizeBytes, BAGuitar::MemSelect mem, bool useDma) |
||||||
|
{ |
||||||
|
|
||||||
|
if (m_memConfig[mem].totalAvailable >= sizeBytes) { |
||||||
|
Serial.println(String("Configuring a slot for mem ") + mem); |
||||||
|
// there is enough available memory for this request
|
||||||
|
slot->m_start = m_memConfig[mem].nextAvailable; |
||||||
|
slot->m_end = slot->m_start + sizeBytes -1; |
||||||
|
slot->m_currentWrPosition = slot->m_start; // init to start of slot
|
||||||
|
slot->m_currentRdPosition = slot->m_start; // init to start of slot
|
||||||
|
slot->m_size = sizeBytes; |
||||||
|
|
||||||
|
if (!m_memConfig[mem].m_spi) { |
||||||
|
if (useDma) { |
||||||
|
m_memConfig[mem].m_spi = new BAGuitar::BASpiMemoryDMA(static_cast<BAGuitar::SpiDeviceId>(mem)); |
||||||
|
slot->m_useDma = true; |
||||||
|
} else { |
||||||
|
m_memConfig[mem].m_spi = new BAGuitar::BASpiMemory(static_cast<BAGuitar::SpiDeviceId>(mem)); |
||||||
|
slot->m_useDma = false; |
||||||
|
} |
||||||
|
if (!m_memConfig[mem].m_spi) { |
||||||
|
} else { |
||||||
|
Serial.println("Calling spi begin()"); |
||||||
|
m_memConfig[mem].m_spi->begin(); |
||||||
|
} |
||||||
|
} |
||||||
|
slot->m_spi = m_memConfig[mem].m_spi; |
||||||
|
|
||||||
|
// Update the mem config
|
||||||
|
m_memConfig[mem].nextAvailable = slot->m_end+1; |
||||||
|
m_memConfig[mem].totalAvailable -= sizeBytes; |
||||||
|
slot->m_valid = true; |
||||||
|
if (!slot->isEnabled()) { slot->enable(); } |
||||||
|
Serial.println("Clear the memory\n"); Serial.flush(); |
||||||
|
slot->clear(); |
||||||
|
Serial.println("Done Request memory\n"); Serial.flush(); |
||||||
|
return true; |
||||||
|
} else { |
||||||
|
// there is not enough memory available for the request
|
||||||
|
|
||||||
|
return false; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
|
@ -0,0 +1,145 @@ |
|||||||
|
/*
|
||||||
|
* IirBiquadFilter.cpp |
||||||
|
* |
||||||
|
* Created on: January 1, 2018 |
||||||
|
* Author: slascos |
||||||
|
* |
||||||
|
* This program is free software: you can redistribute it and/or modify |
||||||
|
* it under the terms of the GNU General Public License as published by |
||||||
|
* the Free Software Foundation, either version 3 of the License, or |
||||||
|
* (at your option) any later version.* |
||||||
|
* |
||||||
|
* This program is distributed in the hope that it will be useful, |
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||||
|
* GNU General Public License for more details. |
||||||
|
* |
||||||
|
* You should have received a copy of the GNU General Public License |
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/ |
||||||
|
|
||||||
|
#include "Audio.h" |
||||||
|
#include "LibBasicFunctions.h" |
||||||
|
|
||||||
|
namespace BAGuitar { |
||||||
|
|
||||||
|
////////////////////////////////////////////////////
|
||||||
|
// IirBiQuadFilter
|
||||||
|
////////////////////////////////////////////////////
|
||||||
|
IirBiQuadFilter::IirBiQuadFilter(unsigned numStages, const int32_t *coeffs, int coeffShift) |
||||||
|
: NUM_STAGES(numStages) |
||||||
|
{ |
||||||
|
m_coeffs = new int32_t[5*numStages]; |
||||||
|
memcpy(m_coeffs, coeffs, 5*numStages * sizeof(int32_t)); |
||||||
|
|
||||||
|
m_state = new int32_t[4*numStages]; |
||||||
|
arm_biquad_cascade_df1_init_q31(&m_iirCfg, numStages, m_coeffs, m_state, coeffShift); |
||||||
|
} |
||||||
|
|
||||||
|
IirBiQuadFilter::~IirBiQuadFilter() |
||||||
|
{ |
||||||
|
if (m_coeffs) delete [] m_coeffs; |
||||||
|
if (m_state) delete [] m_state; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
bool IirBiQuadFilter::process(int16_t *output, int16_t *input, size_t numSamples) |
||||||
|
{ |
||||||
|
if (!output) return false; |
||||||
|
if (!input) { |
||||||
|
// send zeros
|
||||||
|
memset(output, 0, numSamples * sizeof(int16_t)); |
||||||
|
} else { |
||||||
|
|
||||||
|
// create convertion buffers on teh stack
|
||||||
|
int32_t input32[numSamples]; |
||||||
|
int32_t output32[numSamples]; |
||||||
|
for (size_t i=0; i<numSamples; i++) { |
||||||
|
input32[i] = (int32_t)(input[i]); |
||||||
|
} |
||||||
|
|
||||||
|
arm_biquad_cascade_df1_fast_q31(&m_iirCfg, input32, output32, numSamples); |
||||||
|
|
||||||
|
for (size_t i=0; i<numSamples; i++) { |
||||||
|
output[i] = (int16_t)(output32[i] & 0xffff); |
||||||
|
} |
||||||
|
} |
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
// HIGH QUALITY
|
||||||
|
IirBiQuadFilterHQ::IirBiQuadFilterHQ(unsigned numStages, const int32_t *coeffs, int coeffShift) |
||||||
|
: NUM_STAGES(numStages) |
||||||
|
{ |
||||||
|
m_coeffs = new int32_t[5*numStages]; |
||||||
|
memcpy(m_coeffs, coeffs, 5*numStages * sizeof(int32_t)); |
||||||
|
|
||||||
|
m_state = new int64_t[4*numStages];; |
||||||
|
arm_biquad_cas_df1_32x64_init_q31(&m_iirCfg, numStages, m_coeffs, m_state, coeffShift); |
||||||
|
} |
||||||
|
|
||||||
|
IirBiQuadFilterHQ::~IirBiQuadFilterHQ() |
||||||
|
{ |
||||||
|
if (m_coeffs) delete [] m_coeffs; |
||||||
|
if (m_state) delete [] m_state; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
bool IirBiQuadFilterHQ::process(int16_t *output, int16_t *input, size_t numSamples) |
||||||
|
{ |
||||||
|
if (!output) return false; |
||||||
|
if (!input) { |
||||||
|
// send zeros
|
||||||
|
memset(output, 0, numSamples * sizeof(int16_t)); |
||||||
|
} else { |
||||||
|
|
||||||
|
// create convertion buffers on teh stack
|
||||||
|
int32_t input32[numSamples]; |
||||||
|
int32_t output32[numSamples]; |
||||||
|
for (size_t i=0; i<numSamples; i++) { |
||||||
|
input32[i] = (int32_t)(input[i]); |
||||||
|
} |
||||||
|
|
||||||
|
arm_biquad_cas_df1_32x64_q31(&m_iirCfg, input32, output32, numSamples); |
||||||
|
|
||||||
|
for (size_t i=0; i<numSamples; i++) { |
||||||
|
output[i] = (int16_t)(output32[i] & 0xffff); |
||||||
|
} |
||||||
|
} |
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
// FLOAT
|
||||||
|
IirBiQuadFilterFloat::IirBiQuadFilterFloat(unsigned numStages, const float *coeffs) |
||||||
|
: NUM_STAGES(numStages) |
||||||
|
{ |
||||||
|
m_coeffs = new float[5*numStages]; |
||||||
|
memcpy(m_coeffs, coeffs, 5*numStages * sizeof(float)); |
||||||
|
|
||||||
|
m_state = new float[4*numStages];; |
||||||
|
arm_biquad_cascade_df2T_init_f32(&m_iirCfg, numStages, m_coeffs, m_state); |
||||||
|
} |
||||||
|
|
||||||
|
IirBiQuadFilterFloat::~IirBiQuadFilterFloat() |
||||||
|
{ |
||||||
|
if (m_coeffs) delete [] m_coeffs; |
||||||
|
if (m_state) delete [] m_state; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
bool IirBiQuadFilterFloat::process(float *output, float *input, size_t numSamples) |
||||||
|
{ |
||||||
|
if (!output) return false; |
||||||
|
if (!input) { |
||||||
|
// send zeros
|
||||||
|
memset(output, 0, numSamples * sizeof(float)); |
||||||
|
} else { |
||||||
|
|
||||||
|
arm_biquad_cascade_df2T_f32(&m_iirCfg, input, output, numSamples); |
||||||
|
|
||||||
|
} |
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
|
@ -0,0 +1,467 @@ |
|||||||
|
/*
|
||||||
|
* BASpiMemory.cpp |
||||||
|
* |
||||||
|
* Created on: May 22, 2017 |
||||||
|
* Author: slascos |
||||||
|
* |
||||||
|
* This program is free software: you can redistribute it and/or modify |
||||||
|
* it under the terms of the GNU General Public License as published by |
||||||
|
* the Free Software Foundation, either version 3 of the License, or |
||||||
|
* (at your option) any later version.* |
||||||
|
* |
||||||
|
* This program is distributed in the hope that it will be useful, |
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||||
|
* GNU General Public License for more details. |
||||||
|
* |
||||||
|
* You should have received a copy of the GNU General Public License |
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/ |
||||||
|
|
||||||
|
#include "Arduino.h" |
||||||
|
#include "BASpiMemory.h" |
||||||
|
|
||||||
|
namespace BAGuitar { |
||||||
|
|
||||||
|
// MEM0 Settings
|
||||||
|
constexpr int SPI_CS_MEM0 = 15; |
||||||
|
constexpr int SPI_MOSI_MEM0 = 7; |
||||||
|
constexpr int SPI_MISO_MEM0 = 8; |
||||||
|
constexpr int SPI_SCK_MEM0 = 14; |
||||||
|
|
||||||
|
// MEM1 Settings
|
||||||
|
constexpr int SPI_CS_MEM1 = 31; |
||||||
|
constexpr int SPI_MOSI_MEM1 = 21; |
||||||
|
constexpr int SPI_MISO_MEM1 = 5; |
||||||
|
constexpr int SPI_SCK_MEM1 = 20; |
||||||
|
|
||||||
|
// SPI Constants
|
||||||
|
constexpr int SPI_WRITE_MODE_REG = 0x1; |
||||||
|
constexpr int SPI_WRITE_CMD = 0x2; |
||||||
|
constexpr int SPI_READ_CMD = 0x3; |
||||||
|
constexpr int SPI_ADDR_2_MASK = 0xFF0000; |
||||||
|
constexpr int SPI_ADDR_2_SHIFT = 16; |
||||||
|
constexpr int SPI_ADDR_1_MASK = 0x00FF00; |
||||||
|
constexpr int SPI_ADDR_1_SHIFT = 8; |
||||||
|
constexpr int SPI_ADDR_0_MASK = 0x0000FF; |
||||||
|
|
||||||
|
constexpr int CMD_ADDRESS_SIZE = 4; |
||||||
|
constexpr int MAX_DMA_XFER_SIZE = 0x4000; |
||||||
|
|
||||||
|
BASpiMemory::BASpiMemory(SpiDeviceId memDeviceId) |
||||||
|
{ |
||||||
|
m_memDeviceId = memDeviceId; |
||||||
|
m_settings = {20000000, MSBFIRST, SPI_MODE0}; |
||||||
|
} |
||||||
|
|
||||||
|
BASpiMemory::BASpiMemory(SpiDeviceId memDeviceId, uint32_t speedHz) |
||||||
|
{ |
||||||
|
m_memDeviceId = memDeviceId; |
||||||
|
m_settings = {speedHz, MSBFIRST, SPI_MODE0}; |
||||||
|
} |
||||||
|
|
||||||
|
// Intitialize the correct Arduino SPI interface
|
||||||
|
void BASpiMemory::begin() |
||||||
|
{ |
||||||
|
switch (m_memDeviceId) { |
||||||
|
case SpiDeviceId::SPI_DEVICE0 : |
||||||
|
m_csPin = SPI_CS_MEM0; |
||||||
|
m_spi = &SPI; |
||||||
|
m_spi->setMOSI(SPI_MOSI_MEM0); |
||||||
|
m_spi->setMISO(SPI_MISO_MEM0); |
||||||
|
m_spi->setSCK(SPI_SCK_MEM0); |
||||||
|
m_spi->begin(); |
||||||
|
break; |
||||||
|
|
||||||
|
#if defined(__MK64FX512__) || defined(__MK66FX1M0__) |
||||||
|
case SpiDeviceId::SPI_DEVICE1 : |
||||||
|
m_csPin = SPI_CS_MEM1; |
||||||
|
m_spi = &SPI1; |
||||||
|
m_spi->setMOSI(SPI_MOSI_MEM1); |
||||||
|
m_spi->setMISO(SPI_MISO_MEM1); |
||||||
|
m_spi->setSCK(SPI_SCK_MEM1); |
||||||
|
m_spi->begin(); |
||||||
|
break; |
||||||
|
#endif |
||||||
|
|
||||||
|
default : |
||||||
|
// unreachable since memDeviceId is an enumerated class
|
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
pinMode(m_csPin, OUTPUT); |
||||||
|
digitalWrite(m_csPin, HIGH); |
||||||
|
m_started = true; |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
BASpiMemory::~BASpiMemory() { |
||||||
|
} |
||||||
|
|
||||||
|
// Single address write
|
||||||
|
void BASpiMemory::write(size_t address, uint8_t data) |
||||||
|
{ |
||||||
|
m_spi->beginTransaction(m_settings); |
||||||
|
digitalWrite(m_csPin, LOW); |
||||||
|
m_spi->transfer(SPI_WRITE_CMD); |
||||||
|
m_spi->transfer((address & SPI_ADDR_2_MASK) >> SPI_ADDR_2_SHIFT); |
||||||
|
m_spi->transfer((address & SPI_ADDR_1_MASK) >> SPI_ADDR_1_SHIFT); |
||||||
|
m_spi->transfer((address & SPI_ADDR_0_MASK)); |
||||||
|
m_spi->transfer(data); |
||||||
|
m_spi->endTransaction(); |
||||||
|
digitalWrite(m_csPin, HIGH); |
||||||
|
} |
||||||
|
|
||||||
|
// Single address write
|
||||||
|
void BASpiMemory::write(size_t address, uint8_t *src, size_t numBytes) |
||||||
|
{ |
||||||
|
uint8_t *dataPtr = src; |
||||||
|
|
||||||
|
m_spi->beginTransaction(m_settings); |
||||||
|
digitalWrite(m_csPin, LOW); |
||||||
|
m_spi->transfer(SPI_WRITE_CMD); |
||||||
|
m_spi->transfer((address & SPI_ADDR_2_MASK) >> SPI_ADDR_2_SHIFT); |
||||||
|
m_spi->transfer((address & SPI_ADDR_1_MASK) >> SPI_ADDR_1_SHIFT); |
||||||
|
m_spi->transfer((address & SPI_ADDR_0_MASK)); |
||||||
|
|
||||||
|
for (size_t i=0; i < numBytes; i++) { |
||||||
|
m_spi->transfer(*dataPtr++); |
||||||
|
} |
||||||
|
m_spi->endTransaction(); |
||||||
|
digitalWrite(m_csPin, HIGH); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
void BASpiMemory::zero(size_t address, size_t numBytes) |
||||||
|
{ |
||||||
|
m_spi->beginTransaction(m_settings); |
||||||
|
digitalWrite(m_csPin, LOW); |
||||||
|
m_spi->transfer(SPI_WRITE_CMD); |
||||||
|
m_spi->transfer((address & SPI_ADDR_2_MASK) >> SPI_ADDR_2_SHIFT); |
||||||
|
m_spi->transfer((address & SPI_ADDR_1_MASK) >> SPI_ADDR_1_SHIFT); |
||||||
|
m_spi->transfer((address & SPI_ADDR_0_MASK)); |
||||||
|
|
||||||
|
for (size_t i=0; i < numBytes; i++) { |
||||||
|
m_spi->transfer(0); |
||||||
|
} |
||||||
|
m_spi->endTransaction(); |
||||||
|
digitalWrite(m_csPin, HIGH); |
||||||
|
} |
||||||
|
|
||||||
|
void BASpiMemory::write16(size_t address, uint16_t data) |
||||||
|
{ |
||||||
|
m_spi->beginTransaction(m_settings); |
||||||
|
digitalWrite(m_csPin, LOW); |
||||||
|
m_spi->transfer16((SPI_WRITE_CMD << 8) | (address >> 16) ); |
||||||
|
m_spi->transfer16(address & 0xFFFF); |
||||||
|
m_spi->transfer16(data); |
||||||
|
m_spi->endTransaction(); |
||||||
|
digitalWrite(m_csPin, HIGH); |
||||||
|
} |
||||||
|
|
||||||
|
void BASpiMemory::write16(size_t address, uint16_t *src, size_t numWords) |
||||||
|
{ |
||||||
|
uint16_t *dataPtr = src; |
||||||
|
|
||||||
|
m_spi->beginTransaction(m_settings); |
||||||
|
digitalWrite(m_csPin, LOW); |
||||||
|
m_spi->transfer16((SPI_WRITE_CMD << 8) | (address >> 16) ); |
||||||
|
m_spi->transfer16(address & 0xFFFF); |
||||||
|
|
||||||
|
for (size_t i=0; i<numWords; i++) { |
||||||
|
m_spi->transfer16(*dataPtr++); |
||||||
|
} |
||||||
|
|
||||||
|
m_spi->endTransaction(); |
||||||
|
digitalWrite(m_csPin, HIGH); |
||||||
|
} |
||||||
|
|
||||||
|
void BASpiMemory::zero16(size_t address, size_t numWords) |
||||||
|
{ |
||||||
|
m_spi->beginTransaction(m_settings); |
||||||
|
digitalWrite(m_csPin, LOW); |
||||||
|
m_spi->transfer16((SPI_WRITE_CMD << 8) | (address >> 16) ); |
||||||
|
m_spi->transfer16(address & 0xFFFF); |
||||||
|
|
||||||
|
for (size_t i=0; i<numWords; i++) { |
||||||
|
m_spi->transfer16(0); |
||||||
|
} |
||||||
|
|
||||||
|
m_spi->endTransaction(); |
||||||
|
digitalWrite(m_csPin, HIGH); |
||||||
|
Serial.println("DONE!"); |
||||||
|
} |
||||||
|
|
||||||
|
// single address read
|
||||||
|
uint8_t BASpiMemory::read(size_t address) |
||||||
|
{ |
||||||
|
int data; |
||||||
|
|
||||||
|
m_spi->beginTransaction(m_settings); |
||||||
|
digitalWrite(m_csPin, LOW); |
||||||
|
m_spi->transfer(SPI_READ_CMD); |
||||||
|
m_spi->transfer((address & SPI_ADDR_2_MASK) >> SPI_ADDR_2_SHIFT); |
||||||
|
m_spi->transfer((address & SPI_ADDR_1_MASK) >> SPI_ADDR_1_SHIFT); |
||||||
|
m_spi->transfer((address & SPI_ADDR_0_MASK)); |
||||||
|
data = m_spi->transfer(0); |
||||||
|
m_spi->endTransaction(); |
||||||
|
digitalWrite(m_csPin, HIGH); |
||||||
|
return data; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
void BASpiMemory::read(size_t address, uint8_t *dest, size_t numBytes) |
||||||
|
{ |
||||||
|
uint8_t *dataPtr = dest; |
||||||
|
|
||||||
|
m_spi->beginTransaction(m_settings); |
||||||
|
digitalWrite(m_csPin, LOW); |
||||||
|
m_spi->transfer(SPI_READ_CMD); |
||||||
|
m_spi->transfer((address & SPI_ADDR_2_MASK) >> SPI_ADDR_2_SHIFT); |
||||||
|
m_spi->transfer((address & SPI_ADDR_1_MASK) >> SPI_ADDR_1_SHIFT); |
||||||
|
m_spi->transfer((address & SPI_ADDR_0_MASK)); |
||||||
|
|
||||||
|
for (size_t i=0; i<numBytes; i++) { |
||||||
|
*dataPtr++ = m_spi->transfer(0); |
||||||
|
} |
||||||
|
|
||||||
|
m_spi->endTransaction(); |
||||||
|
digitalWrite(m_csPin, HIGH); |
||||||
|
} |
||||||
|
|
||||||
|
uint16_t BASpiMemory::read16(size_t address) |
||||||
|
{ |
||||||
|
|
||||||
|
uint16_t data; |
||||||
|
m_spi->beginTransaction(m_settings); |
||||||
|
digitalWrite(m_csPin, LOW); |
||||||
|
m_spi->transfer16((SPI_READ_CMD << 8) | (address >> 16) ); |
||||||
|
m_spi->transfer16(address & 0xFFFF); |
||||||
|
data = m_spi->transfer16(0); |
||||||
|
m_spi->endTransaction(); |
||||||
|
|
||||||
|
digitalWrite(m_csPin, HIGH); |
||||||
|
return data; |
||||||
|
} |
||||||
|
|
||||||
|
void BASpiMemory::read16(size_t address, uint16_t *dest, size_t numWords) |
||||||
|
{ |
||||||
|
|
||||||
|
uint16_t *dataPtr = dest; |
||||||
|
m_spi->beginTransaction(m_settings); |
||||||
|
digitalWrite(m_csPin, LOW); |
||||||
|
m_spi->transfer16((SPI_READ_CMD << 8) | (address >> 16) ); |
||||||
|
m_spi->transfer16(address & 0xFFFF); |
||||||
|
|
||||||
|
for (size_t i=0; i<numWords; i++) { |
||||||
|
*dataPtr++ = m_spi->transfer16(0); |
||||||
|
} |
||||||
|
|
||||||
|
m_spi->endTransaction(); |
||||||
|
digitalWrite(m_csPin, HIGH); |
||||||
|
} |
||||||
|
|
||||||
|
/////////////////////////////////////////////////////////////////////////////
|
||||||
|
// BASpiMemoryDMA
|
||||||
|
/////////////////////////////////////////////////////////////////////////////
|
||||||
|
BASpiMemoryDMA::BASpiMemoryDMA(SpiDeviceId memDeviceId) |
||||||
|
: BASpiMemory(memDeviceId) |
||||||
|
{ |
||||||
|
int cs; |
||||||
|
switch (memDeviceId) { |
||||||
|
case SpiDeviceId::SPI_DEVICE0 : |
||||||
|
cs = SPI_CS_MEM0; |
||||||
|
m_cs = new ActiveLowChipSelect(cs, m_settings); |
||||||
|
break; |
||||||
|
case SpiDeviceId::SPI_DEVICE1 : |
||||||
|
cs = SPI_CS_MEM1; |
||||||
|
m_cs = new ActiveLowChipSelect1(cs, m_settings); |
||||||
|
break; |
||||||
|
default : |
||||||
|
cs = SPI_CS_MEM0; |
||||||
|
} |
||||||
|
|
||||||
|
// add 4 bytes to buffer for SPI CMD and 3 bytes of address
|
||||||
|
m_txCommandBuffer = new uint8_t[CMD_ADDRESS_SIZE]; |
||||||
|
m_rxCommandBuffer = new uint8_t[CMD_ADDRESS_SIZE]; |
||||||
|
m_txTransfer = new DmaSpi::Transfer[2]; |
||||||
|
m_rxTransfer = new DmaSpi::Transfer[2]; |
||||||
|
} |
||||||
|
|
||||||
|
BASpiMemoryDMA::BASpiMemoryDMA(SpiDeviceId memDeviceId, uint32_t speedHz) |
||||||
|
: BASpiMemory(memDeviceId, speedHz) |
||||||
|
{ |
||||||
|
int cs; |
||||||
|
switch (memDeviceId) { |
||||||
|
case SpiDeviceId::SPI_DEVICE0 : |
||||||
|
cs = SPI_CS_MEM0; |
||||||
|
m_cs = new ActiveLowChipSelect(cs, m_settings); |
||||||
|
break; |
||||||
|
case SpiDeviceId::SPI_DEVICE1 : |
||||||
|
cs = SPI_CS_MEM1; |
||||||
|
m_cs = new ActiveLowChipSelect1(cs, m_settings); |
||||||
|
break; |
||||||
|
default : |
||||||
|
cs = SPI_CS_MEM0; |
||||||
|
} |
||||||
|
|
||||||
|
m_txCommandBuffer = new uint8_t[CMD_ADDRESS_SIZE]; |
||||||
|
m_rxCommandBuffer = new uint8_t[CMD_ADDRESS_SIZE]; |
||||||
|
m_txTransfer = new DmaSpi::Transfer[2]; |
||||||
|
m_rxTransfer = new DmaSpi::Transfer[2]; |
||||||
|
} |
||||||
|
|
||||||
|
BASpiMemoryDMA::~BASpiMemoryDMA() |
||||||
|
{ |
||||||
|
delete m_cs; |
||||||
|
if (m_txTransfer) delete [] m_txTransfer; |
||||||
|
if (m_rxTransfer) delete [] m_rxTransfer; |
||||||
|
if (m_txCommandBuffer) delete [] m_txCommandBuffer; |
||||||
|
if (m_rxCommandBuffer) delete [] m_txCommandBuffer; |
||||||
|
} |
||||||
|
|
||||||
|
void BASpiMemoryDMA::m_setSpiCmdAddr(int command, size_t address, uint8_t *dest) |
||||||
|
{ |
||||||
|
dest[0] = command; |
||||||
|
dest[1] = ((address & SPI_ADDR_2_MASK) >> SPI_ADDR_2_SHIFT); |
||||||
|
dest[2] = ((address & SPI_ADDR_1_MASK) >> SPI_ADDR_1_SHIFT); |
||||||
|
dest[3] = ((address & SPI_ADDR_0_MASK)); |
||||||
|
} |
||||||
|
|
||||||
|
void BASpiMemoryDMA::begin(void) |
||||||
|
{ |
||||||
|
switch (m_memDeviceId) { |
||||||
|
case SpiDeviceId::SPI_DEVICE0 : |
||||||
|
m_csPin = SPI_CS_MEM0; |
||||||
|
m_spi = &SPI; |
||||||
|
m_spi->setMOSI(SPI_MOSI_MEM0); |
||||||
|
m_spi->setMISO(SPI_MISO_MEM0); |
||||||
|
m_spi->setSCK(SPI_SCK_MEM0); |
||||||
|
m_spi->begin(); |
||||||
|
//m_spiDma = &DMASPI0;
|
||||||
|
m_spiDma = new DmaSpiGeneric(); |
||||||
|
break; |
||||||
|
|
||||||
|
#if defined(__MK64FX512__) || defined(__MK66FX1M0__) |
||||||
|
case SpiDeviceId::SPI_DEVICE1 : |
||||||
|
m_csPin = SPI_CS_MEM1; |
||||||
|
m_spi = &SPI1; |
||||||
|
m_spi->setMOSI(SPI_MOSI_MEM1); |
||||||
|
m_spi->setMISO(SPI_MISO_MEM1); |
||||||
|
m_spi->setSCK(SPI_SCK_MEM1); |
||||||
|
m_spi->begin(); |
||||||
|
m_spiDma = new DmaSpiGeneric(1); |
||||||
|
//m_spiDma = &DMASPI1;
|
||||||
|
break; |
||||||
|
#endif |
||||||
|
|
||||||
|
default : |
||||||
|
// unreachable since memDeviceId is an enumerated class
|
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
m_spiDma->begin(); |
||||||
|
m_spiDma->start(); |
||||||
|
|
||||||
|
m_started = true; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// SPI must build up a payload that starts the teh CMD/Address first. It will cycle
|
||||||
|
// through the payloads in a circular buffer and use the transfer objects to check if they
|
||||||
|
// are done before continuing.
|
||||||
|
void BASpiMemoryDMA::write(size_t address, uint8_t *src, size_t numBytes) |
||||||
|
{ |
||||||
|
size_t bytesRemaining = numBytes; |
||||||
|
uint8_t *srcPtr = src; |
||||||
|
size_t nextAddress = address; |
||||||
|
while (bytesRemaining > 0) { |
||||||
|
m_txXferCount = min(bytesRemaining, MAX_DMA_XFER_SIZE); |
||||||
|
while ( m_txTransfer[1].busy()) {} // wait until not busy
|
||||||
|
m_setSpiCmdAddr(SPI_WRITE_CMD, nextAddress, m_txCommandBuffer); |
||||||
|
m_txTransfer[1] = DmaSpi::Transfer(m_txCommandBuffer, CMD_ADDRESS_SIZE, nullptr, 0, m_cs, TransferType::NO_END_CS); |
||||||
|
m_spiDma->registerTransfer(m_txTransfer[1]); |
||||||
|
|
||||||
|
while ( m_txTransfer[0].busy()) {} // wait until not busy
|
||||||
|
m_txTransfer[0] = DmaSpi::Transfer(srcPtr, m_txXferCount, nullptr, 0, m_cs, TransferType::NO_START_CS); |
||||||
|
m_spiDma->registerTransfer(m_txTransfer[0]); |
||||||
|
bytesRemaining -= m_txXferCount; |
||||||
|
srcPtr += m_txXferCount; |
||||||
|
nextAddress += m_txXferCount; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
void BASpiMemoryDMA::zero(size_t address, size_t numBytes) |
||||||
|
{ |
||||||
|
size_t bytesRemaining = numBytes; |
||||||
|
size_t nextAddress = address; |
||||||
|
while (bytesRemaining > 0) { |
||||||
|
m_txXferCount = min(bytesRemaining, MAX_DMA_XFER_SIZE); |
||||||
|
while ( m_txTransfer[1].busy()) {} // wait until not busy
|
||||||
|
m_setSpiCmdAddr(SPI_WRITE_CMD, nextAddress, m_txCommandBuffer); |
||||||
|
m_txTransfer[1] = DmaSpi::Transfer(m_txCommandBuffer, CMD_ADDRESS_SIZE, nullptr, 0, m_cs, TransferType::NO_END_CS); |
||||||
|
m_spiDma->registerTransfer(m_txTransfer[1]); |
||||||
|
|
||||||
|
while ( m_txTransfer[0].busy()) {} // wait until not busy
|
||||||
|
m_txTransfer[0] = DmaSpi::Transfer(nullptr, m_txXferCount, nullptr, 0, m_cs, TransferType::NO_START_CS); |
||||||
|
m_spiDma->registerTransfer(m_txTransfer[0]); |
||||||
|
bytesRemaining -= m_txXferCount; |
||||||
|
nextAddress += m_txXferCount; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
void BASpiMemoryDMA::write16(size_t address, uint16_t *src, size_t numWords) |
||||||
|
{ |
||||||
|
write(address, reinterpret_cast<uint8_t*>(src), sizeof(uint16_t)*numWords); |
||||||
|
} |
||||||
|
|
||||||
|
void BASpiMemoryDMA::zero16(size_t address, size_t numWords) |
||||||
|
{ |
||||||
|
zero(address, sizeof(uint16_t)*numWords); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
void BASpiMemoryDMA::read(size_t address, uint8_t *dest, size_t numBytes) |
||||||
|
{ |
||||||
|
size_t bytesRemaining = numBytes; |
||||||
|
uint8_t *destPtr = dest; |
||||||
|
size_t nextAddress = address; |
||||||
|
while (bytesRemaining > 0) { |
||||||
|
m_setSpiCmdAddr(SPI_READ_CMD, nextAddress, m_rxCommandBuffer); |
||||||
|
|
||||||
|
while ( m_rxTransfer[1].busy()) {} |
||||||
|
m_rxTransfer[1] = DmaSpi::Transfer(m_rxCommandBuffer, CMD_ADDRESS_SIZE, nullptr, 0, m_cs, TransferType::NO_END_CS); |
||||||
|
m_spiDma->registerTransfer(m_rxTransfer[1]); |
||||||
|
|
||||||
|
m_rxXferCount = min(bytesRemaining, MAX_DMA_XFER_SIZE); |
||||||
|
while ( m_rxTransfer[0].busy()) {} |
||||||
|
m_rxTransfer[0] = DmaSpi::Transfer(nullptr, m_rxXferCount, destPtr, 0, m_cs, TransferType::NO_START_CS); |
||||||
|
m_spiDma->registerTransfer(m_rxTransfer[0]); |
||||||
|
|
||||||
|
bytesRemaining -= m_rxXferCount; |
||||||
|
destPtr += m_rxXferCount; |
||||||
|
nextAddress += m_rxXferCount; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
void BASpiMemoryDMA::read16(size_t address, uint16_t *dest, size_t numWords) |
||||||
|
{ |
||||||
|
read(address, reinterpret_cast<uint8_t*>(dest), sizeof(uint16_t)*numWords); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
bool BASpiMemoryDMA::isWriteBusy(void) const |
||||||
|
{ |
||||||
|
return (m_txTransfer[0].busy() or m_txTransfer[1].busy()); |
||||||
|
} |
||||||
|
|
||||||
|
bool BASpiMemoryDMA::isReadBusy(void) const |
||||||
|
{ |
||||||
|
return (m_rxTransfer[0].busy() or m_rxTransfer[1].busy()); |
||||||
|
} |
||||||
|
|
||||||
|
} /* namespace BAGuitar */ |
Loading…
Reference in new issue