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.
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.