This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

ADC on an audio signal

Good afternoon Ladies and Gents,

I am trying to perform an ADC on an audio signal to around 20kHz and I'm taking 512 samples at a time. I then want to perform an FFT using the CMSIS library with this signal/sample to give me 64 frequency bins. The value from each of these bins will then dictate the colour of the led (there are 128 leds, 2 per frequency bin) in a later part of the project (just so you know), its sort of a spectrum analyser. 

Currently I believe my ADC is working in some form but it's doing dome strange things, meaning I've set something up thats not right or forgotten to. I'm using a pot form 0v to 3.3v, not an audio signal (the audio signal will be 3V pk-pk biased around 1.5V), currently, so I can monitor the change I'm putting into the system. 

I'm tracking the data being put into the arrays; audio_samples[ ] and adc_sample[ ], using the graph function so I can see all my samples
- Why are my values are all over the place? 
- How come adc_sample isn't exactly the same as audio_samples? Am i not just replicating the array outside of the interrupt?
- What would be the best sampling frequency to run at? 

Here's my graph: 

Here's my code; 

/*
 * main.c
 */
#include <stdint.h>
#include <stdbool.h>
#include "inc/hw_memmap.h"
#include "inc/hw_types.h"
#include "inc/hw_ints.h"
#include "driverlib/debug.h"
#include "driverlib/sysctl.h"
#include "driverlib/adc.h" //for the adc peripherals
#include "driverlib/gpio.h"
#include "driverlib/timer.h"
#include "driverlib/interrupt.h"

//#include "arm_math.h"

#define NUM_SAMPLES 512 //number of samples taken by the ADC


//declare ADC intrupt
extern void ADCIntHandler(void);

uint32_t g_ui32SysClock;
uint32_t audio_samples[NUM_SAMPLES];

int main(void)
 {
	volatile uint32_t ui32Loop;

	//set syst clk frequency
    g_ui32SysClock = SysCtlClockFreqSet((SYSCTL_XTAL_25MHZ   |
                                             SYSCTL_OSC_MAIN |
                                             SYSCTL_USE_PLL  |
											 SYSCTL_CFG_VCO_480), 120000000);

	/*// set up onboard LED on PN0
	SysCtlPeripheralEnable(SYSCTL_PERIPH_GPION);
	while(!SysCtlPeripheralReady(SYSCTL_PERIPH_GPION)){}
	GPIOPinTypeGPIOOutput(GPIO_PORTN_BASE, GPIO_PIN_0); */

	//set up pin PE0 for ADC input
	SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOE);
	while(!SysCtlPeripheralReady(SYSCTL_PERIPH_GPION)){}
	GPIOPinTypeADC(GPIO_PORTE_BASE, GPIO_PIN_0);

	//set up the ADC
	SysCtlPeripheralEnable(SYSCTL_PERIPH_ADC0); //enable the adc
	ADCClockConfigSet(ADC0_BASE, ADC_CLOCK_SRC_PLL | ADC_CLOCK_RATE_FULL, 1 );
	ADCSequenceDisable(ADC0_BASE, 3);
	ADCSequenceConfigure(ADC0_BASE, 3, ADC_TRIGGER_TIMER, 0);
	ADCSequenceStepConfigure(ADC0_BASE,3,0,ADC_CTL_CH3|ADC_CTL_IE|ADC_CTL_END);
	ADCSequenceEnable(ADC0_BASE, 3);
	ADCIntClear(ADC0_BASE, 3);

	//variable for adc value
	//uint32_t Value;

	//setup timer
	SysCtlPeripheralEnable(SYSCTL_PERIPH_TIMER0);
	TimerConfigure(TIMER0_BASE,TIMER_CFG_PERIODIC);
	TimerLoadSet(TIMER0_BASE,TIMER_A, g_ui32SysClock); //calculate this to be the right freq for audio sampling and fft
	TimerControlTrigger(TIMER0_BASE,TIMER_A,true);
	TimerEnable(TIMER0_BASE,TIMER_A);

	//set interupts
	IntRegister(INT_ADC0SS3,ADCIntHandler);
	ADCIntEnable(ADC0_BASE,3);
	IntEnable(INT_ADC0SS3);
	IntMasterEnable();

	while(1)
	{
	}
}

void ADCIntHandler(void) {

	uint32_t adc_sample[NUM_SAMPLES]; //dont really need this; just use the global variable
	uint32_t adc_sample_count;

	for(adc_sample_count=0; adc_sample_count<NUM_SAMPLES; adc_sample_count++){ //writes the sample value to array
		ADCSequenceDataGet(ADC0_BASE, 3, &adc_sample[adc_sample_count]);
		audio_samples[adc_sample_count] = adc_sample[adc_sample_count];
	}
	ADCIntClear(ADC0_BASE,3);
}

Sorry if these are basic questions, I'm new to microcontrollers and the launchpad, any help you could give me would be appreciated (dumbing it down might me help too)

Will:) x 

  • If you check your setup, I think you'll notice that you only tell the ADC to perform one conversion and in the interrupt attempt to read 512 conversions.

    Robert
  • Hello Will,

    There is another issue.

    ADCClockConfigSet(ADC0_BASE, ADC_CLOCK_SRC_PLL | ADC_CLOCK_RATE_FULL, 1 );

    The ADC is being configured for PLL but the clock is not being divided. This would mean that the ADC analog would be getting a conversion frequency much much greater than what it is speced for.

    Regards
    Amit
  • As always - does not poster's objective CRY OUT for "KISS?"

    Miscue/mis-fire on the early (UNCONFIRMED) set-up & basics condemns the project to randomness and other (sure) failures.

    One small, measureable, test/verified, systematic step at a time. (i.e. KISS)

    Is it not true that the "method's defects" far exceed the individual (bandaid) corrections?

  • Hello cb1,

    Good point sir. I believe we need to look at the signal conditioning front end as well.

    Regards
    Amit
  • Thank you Robert and Amit for having patience and helping, I really appreciate it;
    Both of you were right; results were still fairly jumpy (but over much less of a range) so used ADCHardwareOversampleConfigure(ADC0_BASE,8) to smooth them out.

    Is there a tool/method within CSS that I can see how long various processes are taking and if they'd affect other processes?

    cb1- I'm asking for help on things I'm finding difficult, whilst I appreciate this is the easiest thing and common sense to the folk here after many years of experience, it is not for me. I apologise if my stupidity offends you, but you have to start somewhere.
    If there are such issues with my code (as there's bound to be given my skill level), maybe constructive criticism is required here to enable me to become as good as you. What are the "method defects" you see?
  • No apology was sought - then nor now - and "nothing" you did (or are likely to do) offends me!

    KISS directs that small, simple steps - which "minimize your field of battle - be employed to insure that the more complex, larger objective has the best chance of succeeding.

    Was it not constructive to suggest that you employ a far simpler, divide & conquer, approach?

    Your report of a rather complex objective - and failure to detail: steps taken - when/how measurements were obtained - and confirmation of "Test/Verify"results reduces the ability of your remote staff to fully/properly & quickly assist.

    KISS (appears) banned here - yet is spectacularly memorable - and the final "S" does not (necessarily) have a negative connotation.  (i.e. Student, Searcher, SOLVER!)   Any (fair) read of the earlier post - or this one - surely reveals "constructive" as the central goal...

    And - to the issue @ hand - your use of "over-sample/HW averaging" greatly slows your "effective conversion rate" and may mask signals which (being out-of-range) may indicate trouble-spots and thus (should) be welcomed and given due attention...

  • Hello Will,

    You may use CCS profiling (I haven't done that). I normally uses GPIO toggle (if a scope is handy) around the function or a timer in one shot function to capture start and end time to find out execution time.

    Regards
    Amit