adafruit / Adafruit_ZeroDMA

DMA helper/wrapped for ATSAMD21 such as Arduino Zero & Feather M0
MIT License
58 stars 22 forks source link

Multiple definition of DMAC_Handler when using SPI and I2S #12

Closed fontanon closed 4 years ago

fontanon commented 4 years ago

I wonder why Adafruit_ZeroDMA is trying to redefine DMAC_Handler when it was previously defined by the I2S library. The Adafruit-samd v1.5.2 includes both I2S and SPI libraries. It is actually the SPI one which includes Adafruit_ZeroDMA.

I'm wondering whether any fix could be applied so both I2S and SPI could be included in a sketch for Arduinocore-samd v1.5.2, or whether Adafruit_ZeroDMA could check and reuse DMAC_Handler if it was previously defined.

Below the steps to reproduce.

#include <Arduino.h>
#include <I2S.h>
#include <SPI.h>
#include <stdio.h>
#include <math.h>

void setup() {
}

void loop() {

}
/tmp/arduino_build_458350/libraries/Adafruit_ZeroDMA/Adafruit_ZeroDMA.cpp.o: In function `DMAC_Handler':
Adafruit_ZeroDMA.cpp:(.text.DMAC_Handler+0x0): multiple definition of `DMAC_Handler'
/tmp/arduino_build_458350/libraries/I2S/utility/DMA.cpp.o:DMA.cpp:(.text.DMAC_Handler+0x0): first defined here
collect2: error: ld returned 1 exit status
fontanon commented 4 years ago

Related? https://github.com/arduino/ArduinoCore-samd/issues/419

fontanon commented 4 years ago

Actually, including in the sketch Adafruit_ZeroDMA instead of SPI produces the same error:

#include <I2S.h>
#include <Adafruit_ZeroDMA.h>

void setup() {
}

void loop() {

}
/tmp/arduino_build_458350/libraries/Adafruit_ZeroDMA/Adafruit_ZeroDMA.cpp.o: In function `DMAC_Handler':
Adafruit_ZeroDMA.cpp:(.text.DMAC_Handler+0x0): multiple definition of `DMAC_Handler'
/tmp/arduino_build_458350/libraries/I2S/utility/DMA.cpp.o:DMA.cpp:(.text.DMAC_Handler+0x0): first defined here
collect2: error: ld returned 1 exit status
ladyada commented 4 years ago

we don't really use/support the official arduino i2s library, try the Audio library fork we have

dennischrist commented 4 years ago

@fontanon

did you find a workaround?

ladyada commented 4 years ago

https://github.com/adafruit/Audio

dennischrist commented 4 years ago

@ladyada thanks! do you maybe have a small pointer how I would use the mic in with a MEMS microphone? Or which sketch should I use?

ladyada commented 4 years ago

sorry no idea!

dennischrist commented 4 years ago

@ladyada ok thanks.

https://learn.adafruit.com/adafruit-i2s-mems-microphone-breakout/arduino-wiring-and-test

This tutorial is busted then (the part with the ArduinoSound library).

ladyada commented 4 years ago

yep - i2s is not supported right now, you can remove the zerodma library from within the adafruit arduino core for now look in Arduino15 folder

fontanon commented 4 years ago

@fontanon

did you find a workaround?

Yes @dennischrist I actually make it work by importing and using Adafruit_ZeroI2S instead of the I2S library itself!

https://github.com/adafruit/Adafruit_ZeroI2S

I'll be actually submitting soon to github a full firmware example using Adafruit_ZeroI2S to communicate with the microphone and arduino-lmic (which depends on SPI) to report via LoRaWAN the measurements.

fontanon commented 4 years ago

Hi @dennischrist @ladyada find below a sample arduino sketch file illustrating how we make it work. I hope it helps.

// Sample firmware to illustrate how to interface with I2S devices and 
// using arduino-lmic library with Adafruit Feather M0 Boards.

#include <Arduino.h>
#include <stdio.h>
#include <math.h>

// Most relevant libraries here: Adafruit-samd v1.5.2 (board) + Adafruit_ZeroI2S + arduino-lmic
#include <Adafruit_ZeroI2S.h>
#include <lmic.h>
#include <hal/hal.h>

// Mic sampling parameters
// Unsing InvenSense INMP441 via I2S interface
// Further hardware info: https://www.invensense.com/wp-content/uploads/2015/02/INMP441.pdf
#define SAMPLERATE_HZ 44100
int32_t audiosamples = 128;
const int sample_delay = 1000;

// Pin mapping for RFM95w LoRaWAN interface (via LMIC)
// Further hardware info: https://www.hoperf.com/modules/lora/RFM95.html
const lmic_pinmap lmic_pins = {
  .nss = 8,
  .rxtx = LMIC_UNUSED_PIN,
  .rst = LMIC_UNUSED_PIN,
  .dio = {3, 6, LMIC_UNUSED_PIN},
};

// The I2S interface with the Mic using Adafruit Library.
Adafruit_ZeroI2S i2s; 

void setup() {

  // Configure serial port.
  Serial.begin(115200);
  Serial.println("Zero I2S Audio Tone Generator");

  // Initialize the I2S transmitter.
  if (!i2s.begin(I2S_32_BIT, SAMPLERATE_HZ)) {
    Serial.println("Failed to initialize I2S transmitter!");
    while (1);
  }

  // Enable receive PIN 11 for Digital Output.
  i2s.enableRx(); 
  pinMode(11, OUTPUT);
}

void loop() {

  // This is a stereo Mic. But we'll only read from LEFT channel
  digitalWrite(11, LOW);  

  float sound;
  int32_t left,right;
  int i;
  int sample=0;

  // Read a bunch of samples!!!
  int32_t samples[audiosamples];

  for (int i = 0; i < audiosamples; i++) {
    i2s.read(&left, &right);
    delay(1);  // Workaround delay to prevent oversizing the buffer
    sample = left;
    // convert to 18 bit signed
    sample >>= 14;
    samples[i] = abs(sample);
  }

  // Calculate mean (avg) over samples
  float meanval = 0;
  for (int i = 0; i < audiosamples; i++) {
    meanval += samples[i];
  }
  meanval /= audiosamples;

  // subtract it from all samples to get a 'normalized' output
  for (int i = 0; i < audiosamples; i++) {
    samples[i] -= meanval;
  }

  // find the 'peak to peak' max
  float maxsample, minsample;
  minsample = 100000;
  maxsample = -100000;

  for (int i = 0; i < audiosamples; i++) {
    minsample = min(minsample, samples[i]);
    maxsample = max(maxsample, samples[i]);
  }

  sound = 10 * log(maxsample - minsample);
  Serial.println(sound);
  delay(1000); // Let's just process a measurement each second.
}
Gr3yCodes commented 3 years ago

@fontanon this is brilliant, it works great on the featherM0 WiFi. Thank you for posting your example!

rishi-a commented 2 years ago

@fontanon I was trying your code to interface the Adafruit I2S microphone with Adafruit Feather M0 datalogger. My objective is to write audio data in the SD card.

I observed that, the datarate of Arduino's I2S library is faster compared to the Adafruit_I2S library. Although I made this observation by looking at the output of Serial.plotter as shown below.

image Output of Arduino serial monitor with Arduino's I2S library.

image Output of Arduino serial monitor with Adafruit_I2S library.

I'm using the following code which is a adaptation of your code:

`

include

include

include

//enable/disable the part of code that is involved with SD card actions bool SDENABLE = 0;

//audio intake settings.

define SAMPLERATE_HZ 8000

int32_t audiosamples = 128; //128 because 512B/4B = 32B. 512B is what we can write in one block of the SD card. 4B is the size of the variable

// The I2S interface with the Mic using Adafruit Library. Adafruit_ZeroI2S i2s;

//for sd card const int chipSelect = 4; const int writeIndicator = 8;

void setup() { // Open serial communications and wait for port to open: // Configure serial port. Serial.begin(115200); Serial.println("Zero I2S Audio Tone Generator");

// Initialize the I2S transmitter. if (!i2s.begin(I2S_32_BIT, SAMPLERATE_HZ)) { Serial.println("Failed to initialize I2S transmitter!"); while (1); }

// Enable receive PIN 11 for Digital Output. i2s.enableRx(); pinMode(11, OUTPUT);

//LED to indicate correct writing pinMode(writeIndicator,OUTPUT); if(SDENABLE){ //check SD card status if(!SD.begin(chipSelect)){ Serial.println("Card failed, or not present");
//don't do anything more while(1); } Serial.println("Card Initialised"); } }

void loop() { // This is a stereo Mic. But we'll only read from LEFT channel //digitalWrite(11, LOW);
float sound; int32_t left,right; int i; int sample=0;

// Read a bunch of samples!!! int32_t samples[audiosamples]; //char charSamples[audiosamples];

for (int i = 0; i < audiosamples; i++) { i2s.read(&left, &right); //delay(1); // Workaround delay to prevent oversizing the buffer sample = left; //Serial.println(sample); // convert to 18 bit signed //sample >>= 14; //samples[i] = abs(sample); Serial.println(sample); samples[i] = sample;

} if(SDENABLE){ //store data in SD card; File dataFile = SD.open("datalog.dat",FILE_WRITE);

//if file is available, write to it
if(dataFile){
  dataFile.write((const uint8_t *)&samples, sizeof(samples));
  dataFile.close();
  digitalWrite(writeIndicator, HIGH);  
}

} }`

Why is the Serial.monitor showing a slowed down version for the Adafruit_I2S microphone?