working Multiverse demo

pull/19/head
Blackaddr 1 year ago
parent 129e3a3e3b
commit 701a7cc1a5
  1. 19
      examples/Delay/AnalogDelayDemo/AnalogDelayDemo.ino
  2. 37
      examples/Delay/AnalogDelayDemoExpansion/AnalogDelayDemoExpansion.ino
  3. 22
      examples/Delay/SoundOnSoundDemo/SoundOnSoundDemo.ino
  4. 39
      examples/Delay/SoundOnSoundExpansionDemo/SoundOnSoundExpansionDemo.ino
  5. 9
      examples/Tests/Multiverse_BasicDemo/DebugPrintf.h
  6. 276
      examples/Tests/Multiverse_BasicDemo/Multiverse_BasicDemo.ino
  7. 222
      examples/Tests/Multiverse_BasicDemo/PhysicalControls.cpp
  8. 11
      examples/Tests/Multiverse_BasicDemo/PhysicalControls.h
  9. 16
      src/BAPhysicalControls.h
  10. 110
      src/DmaSpi.h
  11. 11
      src/common/ExternalSramManager.cpp
  12. 8
      src/peripherals/BAPhysicalControls.cpp
  13. 10
      src/peripherals/BASpiMemory.cpp
  14. 24
      src/peripherals/DmaSpi.cpp

@ -1,17 +1,17 @@
/*************************************************************************
* This demo uses the BALibrary 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/BALibrary
*
*
* This example demonstrates teh BAAudioEffectsAnalogDelay effect. It can
* be controlled using USB MIDI. You can get a free USB MIDI Controller
* appliation at
* appliation at
* http://www.blackaddr.com/downloads/BAMidiTester/
* or the source code at
* https://github.com/Blackaddr/BAMidiTester
*
*
* Even if you don't control the guitar effect with USB MIDI, you must set
* the Arduino IDE USB-Type under Tools to "Serial + MIDI"
*/
@ -83,7 +83,7 @@ void OnControlChange(byte channel, byte control, byte value) {
Serial.print(", value=");
Serial.print(value, DEC);
Serial.println();
#endif
#endif
}
void setup() {
@ -96,7 +96,7 @@ void setup() {
//SPI_MEM0_4M(); // Older REVA and REVB boards came with 4M or 1M
//SPI_MEM0_1M();
#endif
delay(100);
Serial.begin(57600); // Start the serial port
@ -117,6 +117,7 @@ void setup() {
Serial.println("Using EXTERNAL memory");
// We have to request memory be allocated to our slot.
externalSram.requestMemory(&delaySlot, 500.0f, MemSelect::MEM0, true);
delaySlot.clear();
#else
Serial.println("Using INTERNAL memory");
#endif
@ -126,7 +127,7 @@ void setup() {
MIDI.setHandleControlChange(OnControlChange);
usbMIDI.setHandleControlChange(OnControlChange);
// Configure which MIDI CC's will control the effect parameters
analogDelay.mapMidiControl(AudioEffectAnalogDelay::BYPASS,16);
analogDelay.mapMidiControl(AudioEffectAnalogDelay::DELAY,20);
@ -136,7 +137,7 @@ void setup() {
// Besure to enable the delay. When disabled, audio is is completely blocked
// to minimize resources to nearly zero.
analogDelay.enable();
analogDelay.enable();
// Set some default values.
// These can be changed by sending MIDI CC messages over the USB using
@ -162,7 +163,7 @@ void setup() {
void loop() {
// usbMIDI.read() needs to be called rapidly from loop().
if (timer > 1000) {
timer = 0;
Serial.print("Processor Usage, Total: "); Serial.print(AudioProcessorUsage());

@ -1,24 +1,24 @@
/*************************************************************************
* This demo uses the BALibrary 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/BALibrary
*
*
* This example demonstrates teh BAAudioEffectsAnalogDelay effect. It can
* be controlled using the Blackaddr Audio "Expansion Control Board".
*
*
* POT1 (left) controls amount of delay
* POT2 (right) controls amount of feedback
* POT3 (center) controls the wet/dry mix
* SW1 will enable/bypass the audio effect. LED1 will be on when effect is enabled.
* SW2 will cycle through the 3 pre-programmed analog filters. LED2 will be on when SW2 is pressed.
*
*
* !!! SET POTS TO REASONABLE VALUES BEFORE STARTING TO AVOID SCREECHING FEEDBACK!!!!
* - set POT1 (delay) fully counter-clockwise then increase it slowly.
* - set POT2 (feedback) fully counter-clockwise, then increase it slowly
* - set POT3 (wet/dry mix) to half-way at the detent.
*
*
* Using the Serial Montitor, send 'u' and 'd' characters to increase or decrease
* the headphone volume between values of 0 and 9.
*/
@ -79,7 +79,7 @@ AudioConnection rightOut(analogDelay,0, i2sOut, 1);
// - LED2 (right) will illuminate when pressing SW2.
//////////////////////////////////////////
// To get the calibration values for your particular board, first run the
// BAExpansionCalibrate.ino example and
// BAExpansionCalibrate.ino example and
constexpr int potCalibMin = 1;
constexpr int potCalibMax = 1018;
constexpr bool potSwapDirection = true;
@ -100,27 +100,27 @@ void setup() {
TGA_PRO_MKII_REV1(); // Declare the version of the TGA Pro you are using.
//TGA_PRO_REVB(x);
//TGA_PRO_REVA(x);
#ifdef USE_EXT
SPI_MEM0_64M(); // Optional 64Mbit SPI RAM
//SPI_MEM0_4M(); // Older REVB and REVA boards offered 1M or 4M
//SPI_MEM0_1M();
//SPI_MEM0_1M();
#endif
delay(100); // wait a bit for serial to be available
Serial.begin(57600); // Start the serial port
delay(100);
// Configure the hardware
// Setup the controls. The return value is the handle to use when checking for control changes, etc.
// pushbuttons
bypassHandle = controls.addSwitch(BA_EXPAND_SW1_PIN); // will be used for bypass control
filterHandle = controls.addSwitch(BA_EXPAND_SW2_PIN); // will be used for stepping through filters
// pots
delayHandle = controls.addPot(BA_EXPAND_POT1_PIN, potCalibMin, potCalibMax, potSwapDirection); // control the amount of delay
feedbackHandle = controls.addPot(BA_EXPAND_POT2_PIN, potCalibMin, potCalibMax, potSwapDirection);
mixHandle = controls.addPot(BA_EXPAND_POT3_PIN, potCalibMin, potCalibMax, potSwapDirection);
feedbackHandle = controls.addPot(BA_EXPAND_POT2_PIN, potCalibMin, potCalibMax, potSwapDirection);
mixHandle = controls.addPot(BA_EXPAND_POT3_PIN, potCalibMin, potCalibMax, potSwapDirection);
// leds
led1Handle = controls.addOutput(BA_EXPAND_LED1_PIN);
led2Handle = controls.addOutput(BA_EXPAND_LED2_PIN); // will illuminate when pressing SW2
@ -140,18 +140,19 @@ void setup() {
Serial.println("Using EXTERNAL memory");
// We have to request memory be allocated to our slot.
externalSram.requestMemory(&delaySlot, 500.0f, MemSelect::MEM0, true);
delaySlot.clear();
#else
Serial.println("Using INTERNAL memory");
#endif
// Besure to enable the delay. When disabled, audio is is completely blocked by the effect
// to minimize resource usage to nearly to nearly zero.
analogDelay.enable();
analogDelay.enable();
// Set some default values.
// These can be changed using the controls on the Blackaddr Audio Expansion Board
analogDelay.bypass(false);
controls.setOutput(led1Handle, !analogDelay.isBypass()); // Set the LED when NOT bypassed
controls.setOutput(led1Handle, !analogDelay.isBypass()); // Set the LED when NOT bypassed
analogDelay.mix(0.5f);
analogDelay.feedback(0.0f);
@ -178,7 +179,7 @@ void loop() {
bool bypass = analogDelay.isBypass(); // get the current state
bypass = !bypass; // change it
analogDelay.bypass(bypass); // set the new state
controls.setOutput(led1Handle, !bypass); // Set the LED when NOT bypassed
controls.setOutput(led1Handle, !bypass); // Set the LED when NOT bypassed
Serial.println(String("BYPASS is ") + bypass);
}
@ -217,11 +218,11 @@ void loop() {
if (Serial.available() > 0) {
while (Serial.available()) {
char key = Serial.read();
if (key == 'u') {
if (key == 'u') {
headphoneVolume = (headphoneVolume + 1) % MAX_HEADPHONE_VOL;
Serial.println(String("Increasing HEADPHONE volume to ") + headphoneVolume);
}
else if (key == 'd') {
else if (key == 'd') {
headphoneVolume = (headphoneVolume - 1) % MAX_HEADPHONE_VOL;
Serial.println(String("Decreasing HEADPHONE volume to ") + headphoneVolume);
}
@ -231,7 +232,7 @@ void loop() {
}
delay(20); // Without some minimal delay here it will be difficult for the pots/switch changes to be detected.
if (timer > 1000) {
timer = 0;
Serial.print("Processor Usage, Total: "); Serial.print(AudioProcessorUsage());

@ -1,21 +1,21 @@
/*************************************************************************
* This demo uses the BALibrary 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/BALibrary
*
*
* THIS DEMO REQUIRES THE EXTERNAL SRAM MEM0
*
*
* This demo combines MIDI control with the BAAudioEffectSoundOnSound. You can use
* the BAMidiTester to control the effect but it's best to use external MIDI footswitch
* or the Blackaddr Audio Expansion Control Board.
*
*
* User must set the Arduino IDE USB-Type to "Serial + MIDI" in the Tools menu.
*
*
* Afters startup, the effect will spend about 5 seconds clearing the audio delay buffer to prevent
* any startup pops or clicks from propagating.
*
*
*/
#include <Audio.h>
#include <MIDI.h>
@ -91,7 +91,7 @@ void OnControlChange(byte channel, byte control, byte value) {
Serial.print(", value=");
Serial.print(value, DEC);
Serial.println();
#endif
#endif
}
void setup() {
@ -122,7 +122,7 @@ void setup() {
// We have to request memory be allocated to our slot.
externalSram.requestMemory(&delaySlot, BAHardwareConfig.getSpiMemSizeBytes(MemSelect::MEM0), MemSelect::MEM0, true);
//externalSram.requestMemory(&delaySlot, 50.0f, MemSelect::MEM0, true);
delaySlot.clear();
// Setup MIDI
MIDI.begin(MIDI_CHANNEL_OMNI);
@ -131,7 +131,7 @@ void setup() {
// Configure the LED to indicate the gate status
sos.setGateLedGpio(USR_LED_ID);
// Configure which MIDI CC's will control the effect parameters
//sos.mapMidiControl(AudioEffectSOS::BYPASS,16);
sos.mapMidiControl(AudioEffectSOS::GATE_TRIGGER,16);
@ -143,7 +143,7 @@ void setup() {
// Besure to enable the delay. When disabled, audio is is completely blocked
// to minimize resources to nearly zero.
sos.enable();
sos.enable();
// Set some default values.
// These can be changed by sending MIDI CC messages over the USB using
@ -170,7 +170,7 @@ void setup() {
delay(1000);
sos.clear();
}
void loop() {

@ -1,29 +1,29 @@
/*************************************************************************
* This demo uses the BALibrary 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/BALibrary
*
*
* THIS DEMO REQUIRES BOTH THE EXTERNAL SRAM AND EXPANSION BOARD ADD-ONS
*
*
* This demo combines the Blackaddr Audio Expansion board with the BAAudioEffectSOS,
* which provides sound-on-sound. The pushbuttons control the opening of the effect
* which provides sound-on-sound. The pushbuttons control the opening of the effect
* gate, as well as clearing the sound being held.
*
*
* The pots control the feedback, as well as the gate opening and close times.
*
*
* Afters startup, the effect will spend about 5 seconds clearing the audio delay buffer to prevent
* any startup pops or clicks from propagating.
*
*
* POT1 - Gate open time. Middle position (detent) is about 2100 ms.
* POT2 - gate close time. Middle position (detent) is about 2100 ms.
* POT3 - Effect volume. Controls the volume of the SOS effect separate from the normal volume
* SW1 - Strum and hold a chord then push this button. Continue holding the button until the LED1 light goes out.
* SW2 - Push this button to clear out the sound circulating in the delay.
*
*
*/
#include <Audio.h>
#include <Audio.h>
#include "BALibrary.h"
#include "BAEffects.h"
@ -86,7 +86,7 @@ AudioConnection outputRight(mixer, 0, i2sOut, 1);
// - LED2 (right) will illuminate when pressing SW2.
//////////////////////////////////////////
// To get the calibration values for your particular board, first run the
// BAExpansionCalibrate.ino example and
// BAExpansionCalibrate.ino example and
constexpr int potCalibMin = 1;
constexpr int potCalibMax = 1018;
constexpr bool potSwapDirection = true;
@ -125,8 +125,8 @@ void setup() {
clearHandle = controls.addSwitch(BA_EXPAND_SW2_PIN); // will be used for stepping through filters
// pots
openHandle = controls.addPot(BA_EXPAND_POT1_PIN, potCalibMin, potCalibMax, potSwapDirection); // control the amount of delay
closeHandle = controls.addPot(BA_EXPAND_POT2_PIN, potCalibMin, potCalibMax, potSwapDirection);
volumeHandle = controls.addPot(BA_EXPAND_POT3_PIN, potCalibMin, potCalibMax, potSwapDirection);
closeHandle = controls.addPot(BA_EXPAND_POT2_PIN, potCalibMin, potCalibMax, potSwapDirection);
volumeHandle = controls.addPot(BA_EXPAND_POT3_PIN, potCalibMin, potCalibMax, potSwapDirection);
// leds
led1Handle = controls.addOutput(BA_EXPAND_LED1_PIN);
led2Handle = controls.addOutput(BA_EXPAND_LED2_PIN); // will illuminate when pressing SW2
@ -142,6 +142,7 @@ void setup() {
// We have to request memory be allocated to our slot.
externalSram.requestMemory(&delaySlot, BAHardwareConfig.getSpiMemSizeBytes(MemSelect::MEM0), MemSelect::MEM0, true);
delaySlot.clear();
// Configure the LED to indicate the gate status, this is controlled directly by SOS effect, not by
// by BAPhysicalControls
@ -149,7 +150,7 @@ void setup() {
// Besure to enable the SOS. When disabled, audio is is completely blocked
// to minimize resources to nearly zero.
sos.enable();
sos.enable();
// Set some default values.
// These can be changed by sending MIDI CC messages over the USB using
@ -176,7 +177,7 @@ void setup() {
delay(1000);
sos.clear();
}
void loop() {
@ -186,7 +187,7 @@ void loop() {
// Check if SW1 has been toggled (pushed) and trigger the gate
// LED1 will be directly control by the SOS effect, not by BAPhysicalControls
if (controls.isSwitchToggled(gateHandle)) {
sos.trigger();
sos.trigger();
Serial.println("GATE OPEN is triggered");
}
@ -223,11 +224,11 @@ void loop() {
if (Serial.available() > 0) {
while (Serial.available()) {
char key = Serial.read();
if (key == 'u') {
if (key == 'u') {
headphoneVolume = (headphoneVolume + 1) % MAX_HEADPHONE_VOL;
Serial.println(String("Increasing HEADPHONE volume to ") + headphoneVolume);
}
else if (key == 'd') {
else if (key == 'd') {
headphoneVolume = (headphoneVolume - 1) % MAX_HEADPHONE_VOL;
Serial.println(String("Decreasing HEADPHONE volume to ") + headphoneVolume);
}
@ -235,9 +236,9 @@ void loop() {
}
}
}
delay(20); // Without some minimal delay here it will be difficult for the pots/switch changes to be detected.
if (timer > 1000) {
timer = 0;
Serial.print("Processor Usage, Total: "); Serial.print(AudioProcessorUsage());

@ -0,0 +1,9 @@
#ifndef DEBUG_PRINTF_H_
#define DEBUG_PRINTF_H_
// Make a safe call to Serial where it checks if the object is valid first. This allows your
// program to work even when no USB serial is connected. Printing to the Serial object when
// no valid connection is present will crash the CPU.
#define DEBUG_PRINT(x) {if (Serial) {x;}}
#endif

@ -0,0 +1,276 @@
/***********************************************************************************
* MULTIVERSE DEMO
*
* This demo program shows how to use BALibrary to access the hardware
* features of the Aviate Audio Multiverse effects processor.
*
* The following are demonstrated in this programming using BALibrary:
* - WM8731 stereo audio codec in master mode (NOTE: not slave mode like TGA Pro
* - Interact with all physical controls
* - Control the 128x64 pixel OLED display (connected to SPI0)
* - Use the 8MB external SRAM (simple memory test)
*
* USAGE INSTRUCTIONS
* - Use the 'Gain' knob to control the input gain on the codec. See checkPot().
* - Use the 'Level' knob to control output volume with an AudioMixer4 object.
* - Stomp switches S1 and S2 will write status to display, and turn on LED
* - Encoder push-button switches will write status to display when pressed/released
* - Encoder rotary control will adjust a positive/negative count and update display
*/
#include <Audio.h>
#include <SPI.h>
#include "BALibrary.h"
#include "DebugPrintf.h"
#include "PhysicalControls.h"
using namespace BALibrary;
// OLED display stuff
#include "Adafruit_SH1106.h"
#include "Adafruit_GFX.h"
#include "Fonts/FreeSansBold9pt7b.h"
constexpr unsigned SCREEN_WIDTH = 128; // OLED display width, in pixels
constexpr unsigned SCREEN_HEIGHT = 64; // OLED display height, in pixels
Adafruit_SH1106 display(37, 35, 10);
// External SPI RAM
ExternalSramManager sramManager;
ExtMemSlot memSlot;
BASpiMemory spiMem1(SpiDeviceId::SPI_DEVICE1);
unsigned spiAddress = 0;
unsigned spiAddressMax;
unsigned sramStage = 0; // stage 0 is zero, 1 is write, 2 is read
volatile float sramCompletion = 0.0f;
volatile unsigned errorCount = 0;
AudioInputI2Sslave i2sIn;
AudioOutputI2Sslave i2sOut;
AudioMixer4 volumeOut;
// i2sIn --> volumeOut(Mixer) --> i2sOut
AudioConnection patchIn0(i2sIn, 0, volumeOut, 0);
AudioConnection patchIn1(i2sIn, 1, volumeOut, 1);
AudioConnection patchOut0(volumeOut,0, i2sOut, 0);
AudioConnection patchOut1(volumeOut,0, i2sOut, 1);
BAAudioControlWM8731master codec;
elapsedMillis timer;
// Create a control object using the number of switches, pots, encoders and outputs on the
// Multiverse pedal
BAPhysicalControls controls(6, 4, 4, 2); // (SW, POT, ENC, LED)
unsigned loopCounter = 0;
void drawProgressBar(float completion); // declaration
void drawBlackaddrAudio() {
display.setCursor(0, 24); // (x,y)
display.printf(" Blackaddr");
display.setCursor(0, 40); // (x,y)
display.printf(" Audio");
}
void setup() {
codec.disable(); // this will reset the codec
// wait up for the serial to appear for up to 1 second
Serial.begin(57600);
unsigned serialLoopCount = 10;
while (!Serial && (serialLoopCount > 0)) {
delay(100);
serialLoopCount--;
}
MULTIVERSE(); // constants defined in BALibrary become valid only after this call
SPI_MEM1_64M(); // Declare the correct memory size
// Init the display
display.begin(SH1106_SWITCHCAPVCC, SH1106_I2C_ADDRESS, true);
display.clearDisplay();
display.display();
display.setTextColor(WHITE); // Draw white text
display.setFont(&FreeSansBold9pt7b);
drawBlackaddrAudio();
display.display();
configPhysicalControls(&controls, &codec);
// Request a memory slot from the external RAM
size_t numBytes = BAHardwareConfig.getSpiMemSizeBytes(MemSelect::MEM1);
spiAddressMax = BAHardwareConfig.getSpiMemMaxAddr(1)/4; // test the first 25% of memory
bool success = sramManager.requestMemory(&memSlot, numBytes, MemSelect::MEM1, /* no DMA */ false);
if (!success && Serial) { printf("Request for memory slot failed\n\r"); }
// Allocated audio buffers and enable codec
AudioMemory(64);
codec.enable();
delay(100);
// Mixer at full volume
volumeOut.gain(0,1.0f);
volumeOut.gain(1,1.0f);
// flush the pot filters. The analog measurement of the analog pots is averaged (filtered)
// over time, so at startup you will see a bunch of false changes detected as the filter
// settles. We can force this with a few dozen repeated calls.
for (unsigned i=0; i < 50; i++) {
float potValue;
for (unsigned j=0; j < BA_EXPAND_NUM_POT; j++) {
controls.checkPotValue(j, potValue);
}
delay(10);
}
}
void loop() {
// Check all the physical controls for updates
checkPot(0);
checkPot(1);
checkPot(2);
checkPot(3);
checkSwitch(0);
checkSwitch(1);
checkSwitch(2);
checkSwitch(3);
checkSwitch(4);
checkSwitch(5);
checkEncoder(0);
checkEncoder(1);
checkEncoder(2);
checkEncoder(3);
// If the SRAM test is not complete, run the next block
if (sramCompletion < 1.0f) {
nextSpiMemTestBlock();
}
// Adjusting one of the knobs/switches will result in its value being display for
// 2 seconds in the check*() functions.
if (timer > 2000) {
loopCounter++;
display.clearDisplay();
drawBlackaddrAudio();
drawSramProgress(sramCompletion);
display.display();
}
}
// This function will draw on the display which stage the memory test is in, and
// the percentage complete for that stage.
void drawSramProgress(float completion)
{
if (errorCount > 0) { // If errors, print the error count
display.setCursor(0, SCREEN_HEIGHT-1);
display.printf("Errors: %d", errorCount);
return;
}
// Draw the SRAM test progress at the bottom of the screen
display.setCursor(0, SCREEN_HEIGHT-1);
switch(sramStage) {
case 0 : display.printf("0 mem:"); break;
case 1 : display.printf("0 chk:"); break;
case 2 : display.printf("wr mem:"); break;
case 3 : display.printf("rd mem:"); break;
case 4 : // same as default
default: display.printf("Done"); break;
}
display.setCursor(SCREEN_WIDTH*0.63f, SCREEN_HEIGHT-1); // position to lower right corner
display.printf("%0.f%%", 100.0f * completion);
}
// Create a predictable data pattern based on address.
constexpr int mask0 = 0x5555;
constexpr int mask1 = 0xaaaa;
int calcNextData(int spiAddress, int loopPhase, int maskPhase)
{
int data;
int phase = ((loopPhase << 1) + maskPhase) & 0x3;
switch(phase)
{
case 0 :
data = spiAddress ^ mask0;
break;
case 1:
data = spiAddress ^ mask1;
break;
case 2:
data = ~spiAddress ^ mask0;
break;
case 3:
data = ~spiAddress ^ mask1;
}
return (data & 0xffff);
}
// Process the next block of data in the memory test
void nextSpiMemTestBlock()
{
constexpr unsigned BLOCK_SIZE_BYTES = 256; // transfer 256 bytes (arbitrary) per transaction
constexpr unsigned NUM_BLOCK_WORDS = BLOCK_SIZE_BYTES;
static uint8_t buffer[BLOCK_SIZE_BYTES];
static int16_t buffer16a[NUM_BLOCK_WORDS];
static int16_t buffer16b[NUM_BLOCK_WORDS];
static int maskPhase = 0;
if (sramStage == 0) { // Zero write
// zero the memory
while (spiMem1.isWriteBusy()) {} // wait for DMA write to complete
memSlot.zero(spiAddress, BLOCK_SIZE_BYTES);
spiAddress += BLOCK_SIZE_BYTES;
} else if (sramStage == 1) { // Zero check
memSlot.read(spiAddress, buffer, BLOCK_SIZE_BYTES);
while (spiMem1.isReadBusy()) {} // wait for DMA read results
for (unsigned i=0; i < BLOCK_SIZE_BYTES; i++) {
if (buffer[i] != 0) { errorCount++; }
}
spiAddress += BLOCK_SIZE_BYTES;
}
else if (sramStage == 2) { // write test
// Calculate the data for a block
for (unsigned i=0; i<NUM_BLOCK_WORDS; i++) {
buffer16a[i] = calcNextData(spiAddress+i, 0, 0);
maskPhase = (maskPhase+1) % 2;
}
memSlot.write16(spiAddress, buffer16a, NUM_BLOCK_WORDS);
while (memSlot.isWriteBusy()) {} // wait for DMA write to complete
spiAddress += BLOCK_SIZE_BYTES;
}
else if (sramStage == 3) { // read test
// Calculate the data for a block
for (unsigned i=0; i<NUM_BLOCK_WORDS; i++) {
buffer16a[i] = calcNextData(spiAddress+i, 0, 0);
maskPhase = (maskPhase+1) % 2;
}
memSlot.read16(spiAddress, buffer16b, NUM_BLOCK_WORDS);
while (memSlot.isReadBusy()) {} // wait for DMA read results
for (unsigned i=0; i < NUM_BLOCK_WORDS; i++) {
if (buffer16a[i] != buffer16b[i]) { errorCount++; }
}
spiAddress += BLOCK_SIZE_BYTES;
}
else if (sramStage == 4) {
sramCompletion = 1.0f;
return;
}
if (spiAddress > spiAddressMax && sramStage < 4) {
spiAddress = 0; sramStage++; sramCompletion = 0.0f;
return;
}
sramCompletion = (float)spiAddress / (float)spiAddressMax ;
}

@ -0,0 +1,222 @@
#include <cmath>
#include "Adafruit_SH1106.h"
#include "BALibrary.h"
#include "DebugPrintf.h"
using namespace BALibrary;
// Declare the externally shared variables from the main .ino
extern Adafruit_SH1106 display;
extern BAAudioControlWM8731master codec;
extern AudioMixer4 volumeOut;
extern elapsedMillis timer;
constexpr int displayRow = 36; // Row to start OLED display updates on
constexpr int potCalibMin = 8;
constexpr int potCalibMax = 1016;
constexpr bool potSwapDirection = true;
constexpr bool encSwapDirection = true;
int pot1Handle= -1, pot2Handle = -1, pot3Handle = -1, pot4Handle = -1;
int sw1Handle = -1, sw2Handle = -1, sw3Handle = -1, sw4Handle = -1, sw5Handle = -1, sw6Handle = -1;
int enc1Handle = -1, enc2Handle = -1, enc3Handle = -1, enc4Handle = -1;
int led1Handle = -1, led2Handle = -1;
BAAudioControlWM8731master *codecPtr = nullptr;
BAPhysicalControls *controlPtr = nullptr;
// Configure and setup the physical controls
void configPhysicalControls(BAPhysicalControls* controls, BAAudioControlWM8731master* codec)
{
// Setup the controls. The return value is the handle to use when checking for control changes, etc.
controlPtr = controls;
codecPtr = codec;
if (!controlPtr) { DEBUG_PRINT(Serial.printf("ERROR: controlPtr is invalid\n\r")); return; }
if (!codecPtr) { DEBUG_PRINT(Serial.printf("ERROR: codecPtr is invalid\n\r")); return; }
// pushbuttons
sw1Handle = controlPtr->addSwitch(BA_EXPAND_SW1_PIN);
sw2Handle = controlPtr->addSwitch(BA_EXPAND_SW2_PIN);
sw3Handle = controlPtr->addSwitch(BA_EXPAND_SW3_PIN);
sw4Handle = controlPtr->addSwitch(BA_EXPAND_SW4_PIN);
sw5Handle = controlPtr->addSwitch(BA_EXPAND_SW5_PIN);
sw6Handle = controlPtr->addSwitch(BA_EXPAND_SW6_PIN);
// pots
pot1Handle = controlPtr->addPot(BA_EXPAND_POT1_PIN, potCalibMin, potCalibMax, potSwapDirection);
pot2Handle = controlPtr->addPot(BA_EXPAND_POT2_PIN, potCalibMin, potCalibMax, potSwapDirection);
pot3Handle = controlPtr->addPot(BA_EXPAND_POT3_PIN, potCalibMin, potCalibMax, potSwapDirection);
pot4Handle = controlPtr->addPot(BA_EXPAND_POT4_PIN, potCalibMin, potCalibMax, potSwapDirection);
// encoders
enc1Handle = controlPtr->addRotary(BA_EXPAND_ENC1_A_PIN, BA_EXPAND_ENC1_B_PIN, encSwapDirection);
enc2Handle = controlPtr->addRotary(BA_EXPAND_ENC2_A_PIN, BA_EXPAND_ENC2_B_PIN, encSwapDirection);
enc3Handle = controlPtr->addRotary(BA_EXPAND_ENC3_A_PIN, BA_EXPAND_ENC3_B_PIN, encSwapDirection);
enc4Handle = controlPtr->addRotary(BA_EXPAND_ENC4_A_PIN, BA_EXPAND_ENC4_B_PIN, encSwapDirection);
// leds
led1Handle = controlPtr->addOutput(BA_EXPAND_LED1_PIN);
led2Handle = controlPtr->addOutput(BA_EXPAND_LED2_PIN); // will illuminate when pressing SW2
}
void checkPot(unsigned id)
{
float potValue;
unsigned handle;
switch(id) {
case 0 :
handle = pot1Handle;
break;
case 1 :
handle = pot2Handle;
break;
case 2 :
handle = pot3Handle;
break;
case 3 :
handle = pot4Handle;
break;
default :
handle = pot1Handle;
}
if ((handle < 0) || (handle >= controlPtr->getNumPots())) {
DEBUG_PRINT(Serial.printf("ILLEGAL POT HANDLE: %d for id %d\n\r", handle, id));
return;
}
if (controlPtr->checkPotValue(handle, potValue)) {
// Pot has changed
DEBUG_PRINT(Serial.println(String("POT") + id + String(" value: ") + potValue));
timer = 0;
display.clearDisplay();
display.setCursor(0,displayRow);
switch(id) {
case 0 :
{
display.printf("Gain: %0.f\n", potValue * 100.0f);
int gain = static_cast<int>(std::roundf(31.0f * potValue));
codecPtr->setLeftInputGain(gain);
codecPtr->setRightInputGain(gain);
yield(); // give time for i2C transfers to complete
break;
}
case 1 :
{
display.printf("Level: %0.f\n", potValue * 100.0f);
volumeOut.gain(0, potValue);
volumeOut.gain(1, potValue);
break;
}
case 2 : display.printf("Exp T: %0.f\n", potValue * 100.0f); break;
case 3 : display.printf("Exp R: %0.f\n", potValue * 100.0f); break;
}
display.display();
}
}
int checkSwitch(unsigned id, bool getValueOnly=false)
{
unsigned swHandle = -1;
unsigned ledHandle = -1;
switch(id) {
case 0 :
swHandle = sw1Handle;
ledHandle = led1Handle;
break;
case 1 :
swHandle = sw2Handle;
ledHandle = led2Handle;
break;
case 2 :
swHandle = sw3Handle;
break;
case 3 :
swHandle = sw4Handle;
break;
case 4 :
swHandle = sw5Handle;
break;
case 5 :
swHandle = sw6Handle;
break;
default :
swHandle = sw1Handle;
ledHandle = led1Handle;
}
if ((swHandle < 0) || (swHandle >= controlPtr->getNumSwitches())) {
DEBUG_PRINT(Serial.printf("ILLEGAL SWITCH HANDLE: %d for id %d\n\r", swHandle, id); Serial.flush());
return -1;
}
bool switchValue;
bool changed = controlPtr->hasSwitchChanged(swHandle, switchValue);
if (getValueOnly) { return controlPtr->getSwitchValue(swHandle); }
if (changed) {
DEBUG_PRINT(Serial.println(String("Button ") + id + String(" pressed")));
timer = 0;
display.clearDisplay();
display.setCursor(0, displayRow);
switch(id) {
case 0 : display.printf("S1: %d\n", switchValue); break;
case 1 : display.printf("S2: %d\n", switchValue); break;
case 2 : display.printf("EncSw A: %d\n", switchValue); break;
case 3 : display.printf("EncSw B: %d\n", switchValue); break;
case 4 : display.printf("EncSw C: %d\n", switchValue); break;
case 5 : display.printf("EncSw D: %d\n", switchValue); break;
}
display.display();
}
if (swHandle < 2) { // these SWs map to LEDs
bool pressed = controlPtr->isSwitchHeld(swHandle);
controlPtr->setOutput(ledHandle, pressed);
}
return controlPtr->getSwitchValue(swHandle);
}
void checkEncoder(unsigned id)
{
unsigned encHandle;
static int enc1 = 0, enc2 = 0, enc3 = 0, enc4 = 0;
switch(id) {
case 0 :
encHandle = enc1Handle;
break;
case 1 :
encHandle = enc2Handle;
break;
case 2 :
encHandle = enc3Handle;
break;
case 3 :
encHandle = enc4Handle;
break;
default :
encHandle = enc1Handle;
}
if ((encHandle < 0) || (encHandle >= controlPtr->getNumRotary())) {
DEBUG_PRINT(Serial.printf("ILLEGAL ENCODER HANDLE: %d for id %d\n\r", encHandle, id); Serial.flush());
return;
}
int adj= controlPtr->getRotaryAdjustUnit(encHandle);
if (adj != 0) {
DEBUG_PRINT(Serial.printf("Enc %d: %d\n\r", id, adj); Serial.flush());
display.clearDisplay();
display.setCursor(0, displayRow);
switch(id) {
case 0 : enc1 += adj; display.printf("Enc A: %d", enc1); break;
case 1 : enc2 += adj; display.printf("Enc B: %d", enc2); break;
case 2 : enc3 += adj; display.printf("Enc C: %d", enc3); break;
case 3 : enc4 += adj; display.printf("Enc D: %d", enc4); break;
}
display.display();
timer = 0;
}
}

@ -0,0 +1,11 @@
#ifndef PHYSICAL_CONTROLS_H_
#define PHYSICAL_CONTROLS_H_
#include "BALibrary.h"
void configPhysicalControls(BALibrary::BAPhysicalControls* controls, BALibrary::BAAudioControlWM8731master* codec);
void checkPot(unsigned id);
int checkSwitch(unsigned id, bool getValueOnly=false);
void checkEncoder(unsigned id);
#endif

@ -238,18 +238,30 @@ public:
/// @returns the index in the encoder vector the new encoder was placed at.
unsigned addRotary(uint8_t pin1, uint8_t pin2, bool swapDirection = false, int divider = 1);
/// Get the number of registered rotary encoders
/// @returns the number of encoders registered
unsigned getNumRotary();
/// add a switch to the controls
/// @param pin the pin number connected to the switch
/// @param intervalMilliseconds, optional, specifies the filtering time to debounce a switch
/// @returns the index in the switch vector the new switch was placed at.
unsigned addSwitch(uint8_t pin, unsigned long intervalMilliseconds = 10);
/// Get the number of registered switches
/// @returns the number of switches registered
unsigned getNumSwitches();
/// add a pot to the controls
/// @param pin the pin number connected to the wiper of the pot
/// @param minCalibration the value corresponding to lowest pot setting
/// @param maxCalibration the value corresponding to the highest pot setting
unsigned addPot(uint8_t pin, unsigned minCalibration, unsigned maxCalibration);
/// Get the number of registered pots
/// @returns the number of pots registered
unsigned getNumPots();
/// add a pot to the controls
/// @param pin the pin number connected to the wiper of the pot
/// @param minCalibration the value corresponding to lowest pot setting
@ -263,6 +275,10 @@ public:
/// @returns a handle (unsigned) to the added output. Use this to access the output.
unsigned addOutput(uint8_t pin);
/// Get the number of registered outputs
/// @returns the number of outputs registered
unsigned getNumOutputs();
/// Set the output specified by the provided handle
/// @param handle the handle that was provided previously by calling addOutput()
/// @param val the value to set the output. 0 is low, not zero is high.

@ -114,7 +114,7 @@ class ActiveLowChipSelect : public AbstractChipSelect
};
#if defined(__MK66FX1M0__)
#if defined(__MK66FX1M0__) || (defined(__IMXRT1062__) && defined(ARDUINO_TEENSY_MICROMOD))
class ActiveLowChipSelect1 : public AbstractChipSelect
{
public:
@ -154,7 +154,7 @@ class ActiveLowChipSelect1 : public AbstractChipSelect
const SPISettings settings_;
};
#endif
#endif // defined(__MK66FX1M0__) || (defined(__IMXRT1062__) && defined(ARDUINO_TEENSY_MICROMOD))
@ -764,37 +764,8 @@ public:
IMXRT_LPSPI4_S.DER = LPSPI_DER_TDDE | LPSPI_DER_RDDE; //enable DMA on both TX and RX
IMXRT_LPSPI4_S.SR = 0x3f00; // clear out all of the other status...
// if (m_pCurrentTransfer->m_pSource) {
// arm_dcache_flush((void *)m_pCurrentTransfer->m_pSource, m_pCurrentTransfer->m_transferCount);
// }
}
// static void pre_cs_impl()
// {
//
// //LPSPI4_PARAM = LPSPI4_PARAM;
// //LPSPI4_PARAM = 0x0404;
// //DMASPI_PRINT(("!!!!!!!!!!!!!!!!!!!!!PARAM reg is %08X\n", LPSPI4_PARAM));
// txChannel_()->TCD->ATTR_SRC = 0; //Make sure set for 8 bit mode...
// txChannel_()->TCD->SLAST = 0; // Finish with it pointing to next location
// rxChannel_()->TCD->ATTR_DST = 0; //Make sure set for 8 bit mode...
// rxChannel_()->TCD->DLASTSGA = 0;
//
// //DMASPI_PRINT(("STATUS SR reg is %08X\n", LPSPI4_SR));
// if (LPSPI4_SR & 0x1800) {
// DMASPI_PRINT(("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!ERROR SR reg is %08X\n", LPSPI4_SR));
// }
// LPSPI4_SR = 0x3f00; // clear various error and status flags
// DMASPI_PRINT(("********************************************CHECK SR reg is %08X\n", LPSPI4_SR));
//
// LPSPI4_TCR = (LPSPI4_TCR & ~(LPSPI_TCR_FRAMESZ(31))) | LPSPI_TCR_FRAMESZ(7); // Set the FRAMESZ to 7 for 8-bit frame size
// LPSPI4_FCR = 0; // set watermarks to zero, this ensures ready flag is set whenever fifo is not empty
//
// LPSPI4_CR = LPSPI_CR_MEN | LPSPI_CR_RRF | LPSPI_CR_RTF; //enable module and reset both FIFOs
// LPSPI4_DER = LPSPI_DER_TDDE | LPSPI_DER_RDDE; // enable DMA on both TX and RX
// }
static void post_cs_impl()
{
rxChannel_()->enable();
@ -810,23 +781,76 @@ public:
IMXRT_LPSPI4_S.CR = LPSPI_CR_MEN | LPSPI_CR_RRF | LPSPI_CR_RTF; // actually clear both...
IMXRT_LPSPI4_S.SR = 0x3f00; // clear out all of the other status...
// if (m_pCurrentTransfer->m_pDest) {
// arm_dcache_delete((void *)m_pCurrentTransfer->m_pDest, m_pCurrentTransfer->m_transferCount);
// }
}
// static void post_finishCurrentTransfer_impl()
// {
// //LPSPI4_FCR = LPSPI_FCR_TXWATER(15); // restore FSR status
// LPSPI4_DER = 0; // DMA no longer doing TX or RX
// LPSPI4_CR = LPSPI_CR_MEN | LPSPI_CR_RRF | LPSPI_CR_RTF; //enable module and reset both FIFOs
// LPSPI4_SR = 0x3f00; // clear out all the other statuses
// }
private:
};
extern DmaSpi0 DMASPI0;
#if (defined(__IMXRT1062__) && defined(ARDUINO_TEENSY_MICROMOD))
// On T4.X, SPI1 is LPSPI3
class DmaSpi1 : public AbstractDmaSpi<DmaSpi1, SPIClass, SPI1>
{
public:
static void begin_setup_txChannel_impl()
{
txChannel_()->disable();
txChannel_()->destination((volatile uint8_t&)IMXRT_LPSPI3_S.TDR);
txChannel_()->disableOnCompletion();
txChannel_()->triggerAtHardwareEvent(DMAMUX_SOURCE_LPSPI3_TX);
}
static void begin_setup_rxChannel_impl()
{
rxChannel_()->disable();
rxChannel_()->source((volatile uint8_t&)IMXRT_LPSPI3_S.RDR); // POPR is the receive fifo register for the SPI
rxChannel_()->disableOnCompletion();
rxChannel_()->triggerAtHardwareEvent(DMAMUX_SOURCE_LPSPI3_RX); // The DMA RX id for MT66 is 14
rxChannel_()->attachInterrupt(rxIsr_);
rxChannel_()->interruptAtCompletion();
}
static void pre_cs_impl()
{
if (LPSPI3_SR & 0x1800) {
DMASPI_PRINT(("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!ERROR SR reg is %08X\n\r", LPSPI3_SR));
}
DMASPI_PRINT(("********************************************CHECK SR reg is %08X\n\r", LPSPI3_SR));
IMXRT_LPSPI3_S.TCR = (IMXRT_LPSPI3_S.TCR & ~(LPSPI_TCR_FRAMESZ(31))) | LPSPI_TCR_FRAMESZ(7);
IMXRT_LPSPI3_S.FCR = 0;
IMXRT_LPSPI3_S.CR = LPSPI_CR_MEN; // I had to add the enable otherwise it wont' work
// Lets try to output the first byte to make sure that we are in 8 bit mode...
IMXRT_LPSPI3_S.DER = LPSPI_DER_TDDE | LPSPI_DER_RDDE; //enable DMA on both TX and RX
IMXRT_LPSPI3_S.SR = 0x3f00; // clear out all of the other status...
}
static void post_cs_impl()
{
rxChannel_()->enable();
txChannel_()->enable();
DMASPI_PRINT(("Done post_cs_impl()\n\r"));
}
static void post_finishCurrentTransfer_impl()
{
IMXRT_LPSPI3_S.FCR = LPSPI_FCR_TXWATER(15); // _spi_fcr_save; // restore the FSR status...
IMXRT_LPSPI3_S.DER = 0; // DMA no longer doing TX (or RX)
IMXRT_LPSPI3_S.CR = LPSPI_CR_MEN | LPSPI_CR_RRF | LPSPI_CR_RTF; // actually clear both...
IMXRT_LPSPI3_S.SR = 0x3f00; // clear out all of the other status...
}
private:
};
extern DmaSpi1 DMASPI1;
extern DmaSpi0 DMASPI0;
#endif // (defined(__IMXRT1062__) && defined(ARDUINO_TEENSY_MICROMOD))
#elif defined(KINETISK)
@ -1050,7 +1074,7 @@ public:
// {
// DMASPI_PRINT(("%s: %x %x %x %x \n", sz, p[0], p[1], p[2], p[3]));
// }
static void post_cs_impl()
{
DMASPI_PRINT(("post_cs S C1 C2: %x %x %x\n", SPI1_S, SPI1_C1, SPI1_C2));

@ -89,7 +89,7 @@ bool ExternalSramManager::requestMemory(ExtMemSlot *slot, size_t sizeBytes, BALi
if (!m_memConfig[mem].m_spi) {
if (Serial) { Serial.printf("Failed to create SPI for id %d\n\r", (int)mem);}
} else {
Serial.println("Calling spi begin()");
if (Serial) { Serial.println("Calling spi begin()"); }
m_memConfig[mem].m_spi->begin();
}
}
@ -100,15 +100,14 @@ bool ExternalSramManager::requestMemory(ExtMemSlot *slot, size_t sizeBytes, BALi
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();
// Note: we no longer auto-clear the slot (on purpose)
if (Serial) { Serial.println("Done Request memory\n"); Serial.flush(); }
return true;
} else {
// there is not enough memory available for the request
Serial.println(String("ExternalSramManager::requestMemory(): Insufficient memory in slot, request/available: ")
if (Serial) { Serial.println(String("ExternalSramManager::requestMemory(): Insufficient memory in slot, request/available: ")
+ sizeBytes + String(" : ")
+ m_memConfig[mem].totalAvailable);
+ m_memConfig[mem].totalAvailable); }
return false;
}
}

@ -56,6 +56,8 @@ unsigned BAPhysicalControls::addRotary(uint8_t pin1, uint8_t pin2, bool swapDire
return m_encoders.size()-1;
}
unsigned BAPhysicalControls::getNumRotary() { return m_encoders.size(); }
unsigned BAPhysicalControls::addSwitch(uint8_t pin, unsigned long intervalMilliseconds) {
m_switches.emplace_back();
m_switches.back().attach(pin);
@ -64,6 +66,8 @@ unsigned BAPhysicalControls::addSwitch(uint8_t pin, unsigned long intervalMillis
return m_switches.size()-1;
}
unsigned BAPhysicalControls::getNumSwitches() { return m_switches.size(); }
unsigned BAPhysicalControls::addPot(uint8_t pin, unsigned minCalibration, unsigned maxCalibration) {
m_pots.emplace_back(pin, minCalibration, maxCalibration);
pinMode(pin, INPUT);
@ -76,12 +80,16 @@ unsigned BAPhysicalControls::addPot(uint8_t pin, unsigned minCalibration, unsign
return m_pots.size()-1;
}
unsigned BAPhysicalControls::getNumPots() { return m_pots.size(); }
unsigned BAPhysicalControls::addOutput(uint8_t pin) {
m_outputs.emplace_back(pin);
pinMode(pin, OUTPUT);
return m_outputs.size()-1;
}
unsigned BAPhysicalControls::getNumOutputs() { return m_outputs.size(); }
void BAPhysicalControls::setOutput(unsigned handle, int val) {
if (handle >= m_outputs.size()) { return; }
m_outputs[handle].set(val);

@ -82,7 +82,7 @@ void BASpiMemory::begin()
m_dieBoundary = BAHardwareConfig.getSpiMemoryDefinition(MemSelect::MEM0).DIE_BOUNDARY;
break;
#if defined(__MK64FX512__) || defined(__MK66FX1M0__)
#if defined(ARDUINO_TEENSY_MICROMOD) || defined(__MK64FX512__) || defined(__MK66FX1M0__)
case SpiDeviceId::SPI_DEVICE1 :
m_csPin = SPI1_CS_PIN;
m_spi = &SPI1;
@ -400,7 +400,7 @@ void BASpiMemoryDMA::begin(void)
cs = SPI0_CS_PIN;
m_cs = new ActiveLowChipSelect(cs, m_settings);
break;
#if defined(__MK66FX1M0__)
#if defined(ARDUINO_TEENSY_MICROMOD) || defined(__MK66FX1M0__)
case SpiDeviceId::SPI_DEVICE1 :
cs = SPI1_CS_PIN;
m_cs = new ActiveLowChipSelect1(cs, m_settings);
@ -415,8 +415,8 @@ void BASpiMemoryDMA::begin(void)
m_rxCommandBuffer = new uint8_t[CMD_ADDRESS_SIZE];
m_txTransfer = new DmaSpi::Transfer[2];
m_rxTransfer = new DmaSpi::Transfer[2];
switch (m_memDeviceId) {
case SpiDeviceId::SPI_DEVICE0 :
m_csPin = SPI0_CS_PIN;
@ -429,7 +429,7 @@ void BASpiMemoryDMA::begin(void)
m_dieBoundary = BAHardwareConfig.getSpiMemoryDefinition(MemSelect::MEM0).DIE_BOUNDARY;
break;
#if defined(__MK66FX1M0__) // DMA on SPI1 is only supported on T3.6
#if defined(ARDUINO_TEENSY_MICROMOD) || defined(__MK66FX1M0__) // DMA on SPI1 is only supported on T3.6 or Micromod
case SpiDeviceId::SPI_DEVICE1 :
m_csPin = SPI1_CS_PIN;
m_spi = &SPI1;

@ -1,15 +1,19 @@
#include "DmaSpi.h"
#if defined(__IMXRT1062__) // T4.0
DmaSpi0 DMASPI0;
#if defined(__IMXRT1062__) // T4.X
DmaSpi0 DMASPI0;
#if defined(ARDUINO_TEENSY_MICROMOD) || defined (ARDUINO_TEENSY41)
DmaSpi1 DMASPI1;
#endif
#elif defined(KINETISK)
DmaSpi0 DMASPI0;
#if defined(__MK66FX1M0__)
DmaSpi1 DMASPI1;
//DmaSpi2 DMASPI2;
#endif
DmaSpi0 DMASPI0;
#if defined(__MK66FX1M0__)
DmaSpi1 DMASPI1;
//DmaSpi2 DMASPI2;
#endif
#elif defined (KINETISL)
DmaSpi0 DMASPI0;
DmaSpi1 DMASPI1;
#else
DmaSpi0 DMASPI0;
DmaSpi1 DMASPI1;
#else
#endif // defined

Loading…
Cancel
Save