0
\$\begingroup\$

I'm seeking advice to help my circuit/code/mcu work more reliably and accurately. What I've got right now is almost there, but I'm just missing the know-how to get me consistent readings. I've tried to paint a good picture of what I'm doing and why so bear with me to explain.

The goal of my design is really straightforward, it's just to capture the volume of a drum. I'm doing this through a Electret mic, into a preamp, into the ADC onboard of the PIC18F2620. My goal in capturing the volume of drums is to display the loudest reading over a given sampling period, so the user knows how loud they're playing. The sampling period is set to about 500ms, which gives the user time to read it properly.

Where it gets a little complicated and where I'm struggling is trying to capture the loudest point of the transients of a drum hit. The problem with these transients is that they're come and gone so fast, that sometimes when I give it a good whack, it displays a value no-where near how loud it should have been. Below is what the transient of a drum looks like, showing how brief that loudest point is.

Here's an image of the sort of transients that come from a drum.

My instinct is that then I need more samples per second, but then I wonder if having less but longer samples would solve this problem (I just don't know).

Ok, hopefully I've been able to paint a picture of the context, here's how I've been doing it.

It all begins with an electret condenser capsule (rated up to 130dB SPL) and the preamp design from this answer, except I'm using the LMC6484IMX. I've included this detail in case you can see this preamp isn't fit for what I'm trying to do.

The output of that then feeds into the ADC input of my PIC18F2620. I decided to run the whole ADC workflow with interrupts to avoid polling and wasting time.

It begins with running the sampling as a high priority interrupt, by grabbing and keeping the largest sample:

void __interrupt(high_priority) ISRs(void)
{
    if(PIR1bits.ADIF == 1)
    {
        ADC = (ADRESH << 8) | (ADRESL);     // Get ADC value from registers
        if(ADC > ADCpeak)                   // Check if volume result is greater than existing temp
        {
            ADCpeak = ADC;                  // If it is then make that new temp peak
        }
        else{}                              // If it isn't then do nothing
        ADCON0bits.GODONE = 1;              // Start ADC again
        PIR1bits.ADIF = 0;                  // Clear interrupt flag
    }
}

Then, once timer 0 runs out, the rest is managed as a low priority interrupt. It's designed to run at the rate of user display refresh (~500ms). I plan to process the results through formulas, so am happy for it to be interrupted by more sampling/other high priority interrupts while the maths churns in the background:

void __interrupt(low_priority) adcISR(void)          
{
    if(INTCONbits.TMR0IF == 1)
    {
        float adcr = (float)((ADCpeak*100)/1024);    // Convert ADC reading to percentage
        volume = adcr;                               // New volume int ready for displaying
        ADCpeak = 0;                                 // Reset ADC peak
        TMR0 = 61618;                                // Reload timer
        INTCONbits.TMR0IF = 0;                       // Clear flag
    }
}

Right now, I have been setting the ADC acquisition time to 12Tad and the conversion clock to Fosc/8, and it's been working ok. Clock speed is 8Mhz too. As previously mentioned though, it still sometimes misses loud hits, and hits roughly the same volume are sometimes displayed as vastly different numbers.

I know there's things I can do to make it capture samples more accurately and faster, but I don't know how. I guess ideally, I'm just looking for someone to tell me my kind of preamp isn't quick enough, or I should change my aquisition time etc...

Would greatly appreciate all input/suggestions.

\$\endgroup\$
  • 1
    \$\begingroup\$ As you have discovered, the peak positive ADC reading is not well correlated with the measurement that you're really interested in, which is the energy of the drum hit. At the very least, you should be taking a peak-to-peak reading, and better still, you should measure the RMS power of all the samples over a short interval -- e.g., a few ms at a time. \$\endgroup\$ – Dave Tweed Jun 21 at 13:08
  • 1
    \$\begingroup\$ Maybe a VU meter circuit and don’t try to sample the audio at all. \$\endgroup\$ – Spehro Pefhany Jun 21 at 14:39
2
\$\begingroup\$

Perhaps a peak detector would help.

Peak detector

The output of a peak detector rises when the input rises. But when the input falls again, the output stays where it is. When you have sampled, you can close the MOSFET switch to bring down the output back down again. However, there's a risk that a peak suddenly happens between your ADC sample, and the closing of the MOSFET switch.

Instead, you could add a resistor across the capacitor to allow the output to slowly come back down to base line.

Peak detection

This means you won't have to sample the input so frequently, because now that sharp peak has been extended.

| improve this answer | |
\$\endgroup\$
  • \$\begingroup\$ Great idea, thank you! This is what I need. If I find a resistor value that just slows the output level down enough to read, could I remove the mosfet? \$\endgroup\$ – ezra_vdj Jun 21 at 20:41
  • 1
    \$\begingroup\$ Yes, definitely. It makes a lot more sense that way. \$\endgroup\$ – Rocketmagnet Jun 21 at 21:51
  • \$\begingroup\$ doing some research, I read that peak detectors and envelope followers are very similar, and the preamp design that I’m using (see link above) has an envelope follower section in there. Could I in theory swap the 10uf capacitor out for a bigger one, and turn it into a peak detector? \$\endgroup\$ – ezra_vdj Jun 21 at 22:55
  • \$\begingroup\$ @ezra_vdj - Yes, absolutely. I don't even think you'd need to change the capacitor. \$\endgroup\$ – Rocketmagnet Jun 22 at 17:32
2
\$\begingroup\$

You need faster sampling. I would say in the order of 10,000 samples per second if you want a well defined peek.

How do you do that? Get rid of floating points. Integers only. Good luck.

| improve this answer | |
\$\endgroup\$
  • \$\begingroup\$ It isn't running in a loop. The sampling is on a timer. \$\endgroup\$ – JRE Jun 21 at 18:16
  • \$\begingroup\$ The adc isn’t even running on a timer, it just starts again straight after it’s finished with the help of an interrupt \$\endgroup\$ – ezra_vdj Jun 22 at 23:15

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.