Go Down

Topic: how to drive a speaker from a r2r ladder (Read 9239 times) previous topic - next topic

HCPUNK

hello everyone! i'd like to know, is the signal that comes out from a R2R ladder (like this one http://didier.longueville.free.fr/arduinoos/wp-content/uploads/DDS00.jpg ) ready to drive an 8ohm speaker or do i need to put a power amplifier (made out from an LM386, for example) between the ladder and the speaker? also, how can i send the data from a huge array of 8bit samples to the eight arduino pins all at once? could someone help me to complete this piece of code? thanks :D

char Sample[]={/*data stored in a 8bit precision*/};
for(int i=0;i<SAMPLES_NUMBER;i++)
{
  digitalWrite(pin0, /*what should go here*/);
  digitalWrite(pin1, /*and here*/);
  ...
  digitalWrite(pin7, /*and here?*/);
  delayMicroseconds(32)  //for about 31.250kHz sample rate
}

AWOL

Code: [Select]
digitalWrite(pin0, /*what should go here*/);
  digitalWrite(pin1, /*and here*/);
  ...
  digitalWrite(pin7, /*and here?*/);

If you use digitalWrite, your output sample rate will be quite low - all the mapping of the pins slows the function down.
Have a look around the forum for direct port manipulation

CrossRoads

The answer to the first part is that you will to amplify the output to drive a typical 4 ohm, 8 ohm, 16 ohm speaker.

For the 2nd part, is seems you are trying to perform a parallel write to the output pins?
Have you considered just doing a shiftout to a shift register instead? Something like this?
location = start_location;
while ( location <= end_location){
shiftout(datapin,clockpin, MSBFIRST, Sample[location]);
digitalWrite (output_register_clock, LOW);
digitalWrite (output_register_clock, HIGH);  // rising edge loads the data to the output pins.
location  = location +1;
}

Then put your ladder on the output of the shift register. This would give 8-bit output instead of 6 also.
I believe the arduino is fast enough that you could do several in parallel, buffer each with an op amp, and make a summing amplifier to mix them together and send that to a power amplifier for the speaker.
Designing & building electrical circuits for over 25 years.  Screw Shield for Mega/Due/Uno,  Bobuino with ATMega1284P, & other '328P & '1284P creations & offerings at  my website.

HCPUNK

thanks for the help!!

by using direct port manipulation, it should be something like this:

Code: [Select]
char Sample[]={/*data stored in a 8bit precision*/};
DDRB = B11111111;
PORTB = 00000000;
for(int i=0; i<SAMPLES_NUMBER;i++)
{
  PORTB = Sample[i];
  delayMicroseconds(32);
}

right?


on the other hand, i could use a shift register to send all the data through a single pin and then obtain an 8-pins parallel output on the other side of the register, right? in this way, the clock sets also the sample rate of the digital audio data coming out from the register, but how can i set it to a determined frequency value? however, this time the code should be something like this (arranged from a playground tutorial):

Code: [Select]
int latchPin = 8; //storage clock
int clockPin = 12; //register clok
int dataPin = 11; //serial data
char Sample[]={/*data stored in a 8bit precision*/};

void setup()
{
  pinMode(latchPin, OUTPUT);
  pinMode(clockPin, OUTPUT);
  pinMode(dataPin, OUTPUT);
}

void loop()
{
  for (int i= 0; i<SAMPLES_NUMBER; i++)
  {
    digitalWrite(latchPin, LOW);
    shiftOut(dataPin, clockPin, MSBFIRST, Sample[i]); 
    digitalWrite(latchPin, HIGH);
    delayMicroseconds(32);  //not sure about that
  }
}


are those pieces of code exact? which one is more powerful in terms of sample rate respect and speed?

CrossRoads

To have the data come out at a predetermined rate, you could have your loop only execute the a shiftout every 32uS.
This code checks for 100uS intervals to elapse & then does stuff - set up yours similar but using 32uS (1/32150)
Code: [Select]

void loop(){
  // *******************************************************************
  // start timing interval

  currentMicros = micros();  // sample the time
  if (currentMicros - previousMicros >= hundred_uS_interval) // more than our interval?
  {
    // save the last time we okayed time updates
    previousMicros = currentMicros; // save the current time for next comparison
    toggle = 1-toggle;  // results in 0 1 0 1 ...                                                                                                                                     

    // do stuff on 0.1mS intervals, 1mS intervals, ets.

   
  }  // end of time interval
} // end of void loop

Designing & building electrical circuits for over 25 years.  Screw Shield for Mega/Due/Uno,  Bobuino with ATMega1284P, & other '328P & '1284P creations & offerings at  my website.

HCPUNK

thanks for your patience and kindness again, but i'm afraid i haven't understood that piece of code properly. in the way you showed me, where do i have to put my code? another question is: in this way, is still possible to do other stuff like controlling some analog pins, sending some data from the serial pin, or even do some on-the-fly operation to the data in my array? if yes, where should i put my other code?

CrossRoads

Sorry about that, re-reading I can see I wasn't too clear.
Your midi stuff goes where these lines are.

    toggle = 1-toggle;  // results in 0 1 0 1 ...                                                                                                                                     

    // do stuff on 0.1mS intervals, 1mS intervals, ets.

}  // end of time interval

Then your other stuff occurs.

So, loop starts, if 32uS have gone by you write out the next part of the waveform.

After that, or if 32uS have not gone by, your other stuff occurs.

Then, you go back to the top & start again:
} // end of void loop


Designing & building electrical circuits for over 25 years.  Screw Shield for Mega/Due/Uno,  Bobuino with ATMega1284P, & other '328P & '1284P creations & offerings at  my website.

HCPUNK

i'm still not sure about that: what that code does is to check again and again if "N"uS has passed and then run the instructions i want, which should be:
Code: [Select]
digitalWrite(latchPin, LOW);
shiftOut(dataPin, clockPin, MSBFIRST, Sample[i]); 
digitalWrite(latchPin, HIGH);
i++;

right? but what's the purpose of the toggle variable? furthermore, where do i have to put my for-loop? please check this:
Code: [Select]
while(i<SAMPLES_NUMBER)
{
  currentMicros = micros();
  if (currentMicros - previousMicros >= 32)
  {
    previousMicros = currentMicros;
    toggle = 1-toggle;
    digitalWrite(latchPin, LOW);
    shiftOut(dataPin, clockPin, MSBFIRST, Sample[i]);
    digitalWrite(latchPin, HIGH);
    i++;
  }
};

is it what i was supposed to write? i'm wondering if direct port manipulation would simplify the whole thing..

CrossRoads

Take out the toggle, I was just using that as an example. In my code I write that out every pass thru, then read it back thru a different pin to see if a switch is closed.
In your case, I would add some code to see if the shifting out was supposed to start, and a check to see when it ends to stop shifting.
Which your while (i<samples_number) does.
If you're only playing out the 1 sound, then sitting in the while loop would be fine.

I'd be tempted instead to set some flags so you could sense when multiple sounds were to start and to let multiple sounds play at once.

So something along these lines:

void loop(){
if (play1flag not set){
read switch1
if (switch1 pressed) {set play1flag}
}
if (play2flag not set){
read switch2
if (switch2 pressed) {set play1flag}
}

currentMicros = micros();
if (currentMicros - previousMicros >= 32)
  {
    previousMicros = currentMicros;
if play1flag set{
shiftout
i=i+1
if i = end {
i=0,
clear play1flag}
}

if play2flagset{
shiftout[j]
j=j+1
if j = end {
j=0,
clear play2flag}
}
} // end time interval check
} // end void loop
Designing & building electrical circuits for over 25 years.  Screw Shield for Mega/Due/Uno,  Bobuino with ATMega1284P, & other '328P & '1284P creations & offerings at  my website.

HCPUNK

playing 2 or more different sound samples each one triggered by its own button? sound really exciting!! Thanks again for your important help.. this should be my last request: i rearranged (a little piece of) my code with your useful advices, so could you please give it a glance and tell me if it's everything ok and what i can do to improve it? here it is:
Code: [Select]
#define dataP 11      //data pin for the shift reg
#define clockP 1      //clock pin for the shift reg
#define latchP 8      //latch clock pin for the shift reg
#define LAST_SAMPLE 4096    //max size of every sample
#define PIEZOTHRESHOLD 150  //the fewest value that can be read
#define TOTAL_PINS 6        //number of analog devices that trim the samples

//those are the audio samples, codified in 8bit
//the last array contains all the previous data
uint8_t Sample1[LAST_SAMPLE];
uint8_t Sample2[LAST_SAMPLE];
uint8_t Sample3[LAST_SAMPLE];
uint8_t Sample4[LAST_SAMPLE];
uint8_t Sample5[LAST_SAMPLE];
uint8_t Sample6[LAST_SAMPLE];
uint8_t* Samples[TOTAL_PINS]={Sample1,Sample2,Sample3,Sample4,Sample5,Sample6};

int playflags[TOTAL_PINS]={0,0,0,0,0,0};  //flags: 0=unset 1=set
int piezovals[TOTAL_PINS];                //values read from each pin
int i[TOTAL_PINS]={0,0,0,0,0,0};          //the counters, every sample has its own
int pins;
int samp;
int currentMicros;
int previousMicros;

void setup()
{
  //set up the shift reg pins
  pinMode(latchP, OUTPUT);
  pinMode(clockP, OUTPUT);
  pinMode(dataP, OUTPUT);
}

void loop()
{
  for(pins=0;pins<TOTAL_PINS;pins++){   //Search for any not-yet-activated pin,
    if(playflags[pins]==0){             //then check if some of them have been pressed
      piezovals[pins]=readPiezo(pins);  //using a function that return its "velocity" or 0.
      if(piezovals[pins]!=0)            //If the value is different from 0 (in other words, if
        playflags[pins]=1;              //the botton has been pressed) set his flag.
    }
  }

  currentMicros=micros();                  //Read the current time, checking if 32us have passed
  if(currentMicros-previousMicros>=32){
    previousMicros=currentMicros;

    for(pins=0;pins<TOTAL_PINS;pins++){    //Search among all the pins
      if(playflags[pins]==1){              //the ones whose flag has been set.
        samp=i[pins];                      //Temporarily save the current counter
        digitalWrite(latchP, LOW);         //Toggle the latch clock
        shiftOut(dataP, clockP, MSBFIRST, Samples[pins][samp]);  //Send the data to the shift reg
        digitalWrite(latchP, HIGH);        //Toggle the latch clock again
        i[pins]++;                         //Increase the counter
        if(i[pins]==LAST_SAMPLE){          //If the the last sample has been reached,
          i[pins]=0;                       //reset the current counter and
          playflags[pins]=0;               //toggle his respective flag.
        }
      }
    }
  }
}

//This function reads a given analog pin, and returns his average
//velocity, checking how many times his value stays above threshold/2.
//In this way, is possible to play sounds at different velocities, only
//multiply or divide the last shiftOut parameter by a certain value
//(related to the velocity we've read). I'm wondering if all this might
//add to much delay between the pression and the play.

int readPiezo(int piezoPin)
{
  int val, t;
  val=analogRead(piezoPin);
  if(val>=PIEZOTHRESHOLD){
    t=0;
    while(analogRead(piezoPin)>=PIEZOTHRESHOLD/2)
      t++;
    return(t*2);
  }
  return(0);
}


thanks in advance!!

CrossRoads

That is very clever the way you have it do 6 sounds like that!  I guess there is no reason that couldn't be expanded if the needed # of instructions could occur in time (32uS/62.5nS = 512 instructions per pass thru the loop).

So, every 32uS you will go thru all 6 pins and if playing shift out a byte of data, all going thru the same shift register?
I was thinking each would have its own shift register/ladder then  be mixed together with a summing op amp and some lowpass fitering to take out higher frequency switching noise. (use common shift clock, data line, and have 6 latch lines)

This way may work very well. I am wondering what would happen soundwise when you have 1 sound playing vs 6 sounds for instance, and if it would matter if the sound was say 1,2 then nothing for 3,4,5, and then 6, or nothing for 1,2,3, then 4,5 and not 6. You can see how it would appear to have data going  out at different data rates, from every 32uS to something that might look  like every 5.3uS or something in between that is not necessarily evenly spaced.

I'd say for now if you have the 4K sound samples, go give it a try.  Where is the 24K of data being stored? In the 32K of flash memory?
Designing & building electrical circuits for over 25 years.  Screw Shield for Mega/Due/Uno,  Bobuino with ATMega1284P, & other '328P & '1284P creations & offerings at  my website.

HCPUNK

maybe i forgot to tell you i'm not English, however i'm afraid i haven't understood a word.. sometimes it's hard to understand what I've just written too..

CrossRoads

I will try to simplify.
The way you wrote  your array checking, updating, everything is very clever, nice & compact, that part is fine.

On playback, you may have 1 sound being played, and you may 6 sounds being played.  All these sounds are going thru  shift register to 1 R2R ladder.

Will that impact the sound?  I don't know.  You may notice the sound being different when 1 is being played compared to two being played.
I don't have a good way to draw a picture.
Imagine this: Sound 1 is playing a sine wive, going from 0V up to 5V, nice and smooth.
Now sound 2 starts - so at 32uS sound1 goes out, then  some hundreds of nanoseconds later sound 2 goes out, and now the level stays at sound2 until the next 32uS when sound 1 is updated.  Will that impact the way sound1 is heard?
Imagine another example: Sound 1 is playing a sine wive, going from 0V up to 5V, nice and smooth.
Now sound 5 starts - so at 32uS sound1 goes out, then  some more hundreds of nanoseconds later sound 5 goes out, (delayed by the checking of flags for sounds 2,3,4) and now the level stays at sound5 until the next 32uS when sound 1 is updated.    Will that impact the way sound1 is heard?

Maybe it will all mix together nice, maybe not. If you find the sound is not as expected, then having each sound go thru separate R2R ladders may be needed instead.


Designing & building electrical circuits for over 25 years.  Screw Shield for Mega/Due/Uno,  Bobuino with ATMega1284P, & other '328P & '1284P creations & offerings at  my website.

HCPUNK

well, i never thought i could receive compliments here, so i'm kind of happy :)

however, the second part of your explanation made me a bit cofused.. you've "raised" an important issue.. the first thing i thought was to resolve the problem by software, in this way (or something similiar):
Code: [Select]
  currentMicros=micros();                  //Read the current time, checking if 32us have passed
  if(currentMicros-previousMicros>=32){
    previousMicros=currentMicros;
    sample=0;

    for(pins=0;pins<TOTAL_PINS;pins++){    //Search among all the pins
      if(playflags[pins]==1)               //the ones whose flag has been set and
        sample+=Samples[pins][i];          //sum together their respective samples.
    }
    digitalWrite(latchP, LOW);                       //Toggle the latch clock
    shiftOut(dataP, clockP, MSBFIRST, (sample>>8));  //Send the first half of data to the shift reg
    shiftOut(dataP, clockP, MSBFIRST, sample);       //Send the second half of data to the shift reg
    digitalWrite(latchP, HIGH);                      //Toggle the latch clock again
    i++;                                             //Increase the counter
    if(i==LAST_SAMPLE){                          //If the the last sample has been reached,
      i=0;                                       //reset the counter and
      for(pins=0;pins<TOTAL_PINS;pins++)         //all the play flags.
        playflags[pins]=0;
    }
  }

in this way, i'm able to send the data once, at twice of the depth, using only 2 registers, but i'll need a bigger R2R ladder. i'm still wondering if arduino's computing capabilities are enough to do all those instructions in just 32us (that are about 512 clock cycles).

Anyway, if i would use different regs and ladders for every sample, how can i mix the sounds together?

CrossRoads

I was discussing this with college professer, he suggested the same. Add all together, shift out to an 11 or 12 bit R2R ladder.

Otherwise, seperate R2Rs, mix with a non-inverting op amp summing amplifier - can be readily found with google search - am running out, can send when I get back in a couple hours.
Designing & building electrical circuits for over 25 years.  Screw Shield for Mega/Due/Uno,  Bobuino with ATMega1284P, & other '328P & '1284P creations & offerings at  my website.

Go Up