Hello,
I'm working on a project where 56 speakers are suppose to play at the same time. The approach thus far is to generate the signals in a PC (MATLAB), send data to 7 Arduinos via SerialUSB and then to sync the sound by playing on external interrupts to the arduinos.
Because the RAM of the Arduinos are limited we need to send data and play at the same time, if we want to play audio in more than a couple of seconds. To do this we use RingBuffer: GitHub - Locoduino/RingBuffer: A RingBuffer library for Arduino
This makes having a buffer that is continiously filled and emtied much easier. And for the interrupts we use another arduino.
-------- PROBLEM --------
When we send a square wave to one arduino it plays with and frequency of ~ 200-300, altho the interrupt frequency is 8000hz (confirmed with ocilloscope).
Have we reached the limits of the Arduino? Is the problem that we have try using SerialUSB and external interrupts at the same time (if I'm correct Serial uses interrupts aswell)? Do we need pullup resistor to make the signal stronger? Can Arduino Due handle inputs of 8kHz to one of it's digital pins?
#include <RingBuf.h>
#define CONT_FLAG 0
#define STORE_FLAG 1
#define PLAY_FLAG 2
#define RECORD_FLAG 3
#define NUM_LM 8
#define BUFFER_SIZE 8192
#define CHUNK_SIZE 8
#define maskA (1<<2)
#define maskB (1<<1)
#define maskC (1<<3)
#define port PIOC
#define a 34
#define b 33
#define c 35
const int interruptPin = 31;
int analogPins[] = {A2, A3, A4, A5, A6, A7, A8, A9};
int modePins[] = {22, 23, 24, 25, 26, 27, 28, 29};
int now = 0;
int rate = 120; // 120 = 8kHz (fs)
// Volatile don't work ??
RingBuf<byte, BUFFER_SIZE> ringBuf0;
RingBuf<byte, BUFFER_SIZE> ringBuf1;
RingBuf<byte, BUFFER_SIZE> ringBuf2;
RingBuf<byte, BUFFER_SIZE> ringBuf3;
RingBuf<byte, BUFFER_SIZE> ringBuf4;
RingBuf<byte, BUFFER_SIZE> ringBuf5;
RingBuf<byte, BUFFER_SIZE> ringBuf6;
RingBuf<byte, BUFFER_SIZE> ringBuf7;
void setup() {
pinMode(DAC1, OUTPUT);
pinMode(interruptPin, INPUT_PULLUP);
pinMode(a, OUTPUT);
pinMode(b, OUTPUT);
pinMode(c, OUTPUT);
pinMode(DAC1, OUTPUT);
for (int i = 0; i < NUM_LM; i++) {
pinMode(modePins[i], OUTPUT);
pinMode(analogPins[i], INPUT_PULLUP);
}
// Setup the USB connection
SerialUSB.begin(115200);
delay(1000);
attachInterrupt(digitalPinToInterrupt(interruptPin), playInterrupt, CHANGE);
while (!SerialUSB) {}
}
void setSpeakerMode() {
for (int i = 0; i < NUM_LM; i++) {
// Set all L/M-units in speaker-mode
digitalWrite(modePins[i], LOW);
}
}
void mux(int val) {
switch (val) {
case 0:
port->PIO_CODR = maskA | maskB | maskC;
//Serial.println("000");
break;
case 1:
port->PIO_CODR = maskA | maskB | maskC;
port->PIO_SODR = maskC;
//Serial.println("001");
break;
case 2:
port->PIO_CODR = maskA | maskB | maskC;
port->PIO_SODR = maskB;
//Serial.println("010");
break;
case 3:
port->PIO_CODR = maskA | maskB | maskC;
port->PIO_SODR = maskC | maskB;
//Serial.println("011");
break;
case 4:
port->PIO_CODR = maskA | maskB | maskC;
port->PIO_SODR = maskA;
//Serial.println("100");
break;
case 5:
port->PIO_CODR = maskA | maskB | maskC;
port->PIO_SODR = maskC | maskA;
//Serial.println("101");
break;
case 6:
port->PIO_CODR = maskA | maskB | maskC;
port->PIO_SODR = maskA | maskB;
//Serial.println("110");
break;
case 7:
port->PIO_SODR = maskA | maskB | maskC;
//Serial.println("111");
break;
default:
//Serial.println("Numbers only in range 0-7.");
break;
}
}
void contStore() {
SerialUSB.flush();
setSpeakerMode();
boolean sending = true;
byte data[CHUNK_SIZE] = {};
while(sending)
{
// Data aviable?
if (SerialUSB.available() > 0)
{
// More data to send?
if (SerialUSB.read() == 1)
{
// The queues are full?
if (!ringBuf0.isFull())
{
// Buffer is not full, add one byte to them all.
SerialUSB.write(byte(1));
SerialUSB.readBytes(data, CHUNK_SIZE); // To be pushed to circular buffer
ringBuf0.push(data[0]);
ringBuf1.push(data[1]);
ringBuf2.push(data[2]);
ringBuf3.push(data[3]);
ringBuf4.push(data[4]);
ringBuf5.push(data[5]);
ringBuf6.push(data[6]);
ringBuf7.push(data[7]);
// Confirm that all has been recived and buffered.
// SerialUSB.write(byte(1));
}
else
{
// Buffers were full, inform sender of this.
SerialUSB.write(byte(0));
delay(1);
}
}
else
{
// The flag was 0 => no more data to receive
sending = false;
}
}
else
{
delay(1);
}
}
// Session has ended
SerialUSB.write(byte(3));
}
void playInterrupt() {
byte data = 0;
if (ringBuf0.pop(data))
{
mux(0);
analogWrite(DAC1, data);
}
if (ringBuf1.pop(data))
{
mux(1);
analogWrite(DAC1, data);
}
if (ringBuf2.pop(data))
{
mux(2);
analogWrite(DAC1, data);
}
if (ringBuf3.pop(data))
{
mux(3);
analogWrite(DAC1, data);
}
if (ringBuf4.pop(data))
{
mux(4);
analogWrite(DAC1, data);
}
if (ringBuf5.pop(data))
{
mux(5);
analogWrite(DAC1, data);
}
if (ringBuf6.pop(data))
{
mux(6);
analogWrite(DAC1, data);
}
if (ringBuf7.pop(data))
{
mux(7);
analogWrite(DAC1, data);
}
}
void loop() {
if (SerialUSB.available() > 0)
{
byte flags = SerialUSB.read();
byte command = getCommand(flags);
int numOfBytes = getNumOfBytes(flags);
if (command == CONT_FLAG)
{
contStore();
}
else if (command == STORE_FLAG)
{
store(numOfBytes);
}
else if (command == PLAY_FLAG)
{
play(numOfBytes);
}
else if (command == RECORD_FLAG)
{
record(numOfBytes);
}
}
}
To Make this shorter I have removed a couple of functions not relevant to the issue. The functions of main concern is playInterrupt() and contStore(). We know that contStore() stores correctly, we can send long arrays and get them back via USB.
I have tried using the lockedPOP and lockedPush, and it didn't make any difference.
The speakers seem to play corectly, just much slower than 8000 samples per second.
Thanks for your time. ![]()