dta123 0 Posted September 25, 2014 Share Posted September 25, 2014 The project is to take samples from a sensor and place the returned value (from 0 to 1024) into an array. I want to take the samples using interrupts as I require uniform sampling of the sensor. I am using the TM4C123GXL board and have already tried multiple methods I have found online, such as http://www.ti.com/lit/an/spma001a/spma001a.pdf and a few from forums. Ideally I would use 8x oversampling to help with the accuracy as I will be running the gathered samples through a Fast Fourier Transform. I require a sampling frequency of over 7 kHz after oversampling. I have also read through the peripheral guide and the ROM guide provided by Texas Instruments. I was hoping somebody could help me get this working. Quote Link to post Share on other sites
igor 163 Posted September 25, 2014 Share Posted September 25, 2014 Note that the ADC on the Tiva launchpad is 12 bits (so samples start out as 0 to 4095) - easy enough to convert to 0 to 1023 (just >> 2). Also the Tiva can do more oversampling than mentioned in the technical report you link to. So what code do you have so far, what is working and what is not, and what are the symptoms. http://forum.stellarisiti.com/topic/2045-adc-peripheral-has-alot-of-fuctions/ http://forum.stellarisiti.com/topic/2007-maybe-add-support-for-adc-hardware-oversampling/ http://www.catb.org/esr/faqs/smart-questions.html Quote Link to post Share on other sites
madias 0 Posted September 25, 2014 Share Posted September 25, 2014 If you're not using hardware oversampling, you can also do a simple lowpass filtering: Where weight is the interpolating value (1= no interpolating, 0.01 strong interpolating) rawValue is the the current analog reading, lastValue the value before. // filter the current result using a weighted average filter: float filter(float rawValue, float weight, float lastValue) { // run the filter: float result = weight * rawValue + (1.0-weight)*lastValue; // return the result: return result; } I've found in my snipplet folder some fooling around with mixing energia and driverlib: example for using the energia analogRead with hardware oversampling: #include <stdint.h> #include <stdbool.h> #define PART_TM4C123GH6PM #include "inc/tm4c123gh6pm.h" #include "inc/hw_memmap.h" #include "inc/hw_types.h" #include "driverlib/debug.h" #include "driverlib/sysctl.h" #include "driverlib/adc.h" void setup() { SysCtlPeripheralEnable(SYSCTL_PERIPH_ADC0); ADCHardwareOversampleConfigure(ADC0_BASE,64); Serial.begin(9600); } void loop() { int sensorValue = analogRead(A3); // print out the value you read: Serial.println(sensorValue/16); delay(1); // delay in between reads for stability } Quote Link to post Share on other sites
dta123 0 Posted September 26, 2014 Author Share Posted September 26, 2014 I have worked on it more today and have managed to get the interrupts triggering. My code so far is: #include <CMSIS_DSP.h> #include "inc/hw_ints.h" #include "driverlib/interrupt.h" #include "driverlib/sysctl.h" #include "driverlib/timer.h" #include "driverlib/adc.h" #include "driverlib/gpio.h" /* ------------------------------------------------------------------- * External Input and Output buffer Declarations for FFT * ------------------------------------------------------------------- */ #define FFT_SIZE 512 static float32_t samplesInput[FFT_SIZE] = {0}; static float32_t fftOutput[FFT_SIZE*2] = {0}; /* ------------------------------------------------------------------ * Global variables for FFT calculations * ------------------------------------------------------------------- */ uint32_t ifftFlag = 0; uint32_t doBitReverse = 1; uint32_t maxIndex = 0; float32_t maxValue = 0; /* ------------------------------------------------------------------ * Global variables for FFT * ------------------------------------------------------------------- */ uint32_t fs = 7500; //Sampling frequency in HZ arm_status status = ARM_MATH_SUCCESS; arm_rfft_instance_f32 S; arm_cfft_radix4_instance_f32 S_CFFT; /* ------------------------------------------------------------------ * Pin Mapping * ------------------------------------------------------------------- */ int analogIn = A0; unsigned long g_ulAverage[8] = {0}; unsigned long average = 0; uint32_t sampleIndex = 0; boolean go = true; /* ------------------------------------------------------------------ * Initialise test frequency * ------------------------------------------------------------------- */ void setTestFreq() { //Test frequency int freq = 1500; double radFreq = 2*PI*freq; for(int i = 0; i < FFT_SIZE; i++) { samplesInput[i] = 1024*arm_sin_f32(i * radFreq / fs); } } void ADC0IntHandler(void) { average = 0; //Take readings from ADC ADCIntClear(ADC0_BASE, 0); ADCSoftwareOversampleDataGet(ADC0_BASE, 0, g_ulAverage, 8); //Take average for(int i = 0; i < 8; i++) { average += g_ulAverage[i]; } average /= 8; //Place values into input buffer samplesInput[sampleIndex] = average; sampleIndex++; if(sampleIndex == FFT_SIZE) { sampleIndex = 0; go = true; } } //Initialise the ADC void ADCInitialise() { Serial.println("Setting up ADC pin"); //Setup pin for ADC SysCtlPeripheralEnable(SYSCTL_PERIPH_ADC0); SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOB); GPIOPinTypeADC(GPIO_PORTB_BASE, GPIO_PIN_4); Serial.println("Setting up ADC"); //Setup ADC with 8x oversampling ADCSequenceConfigure(ADC0_BASE, 0, ADC_TRIGGER_TIMER, 0); ADCSoftwareOversampleConfigure(ADC0_BASE, 0, 8); ADCSoftwareOversampleStepConfigure(ADC0_BASE, 0, 0, (ADC_CTL_CH1 | ADC_CTL_IE | ADC_CTL_END)); Serial.println("Connecting to interrupt"); //Connect ADC to interrupt ADCIntRegister(ADC0_BASE, 0, ADC0IntHandler); Serial.println("Setting up timer"); SysCtlPeripheralEnable(SYSCTL_PERIPH_TIMER0); TimerConfigure(TIMER0_BASE, TIMER_CFG_PERIODIC); TimerLoadSet(TIMER0_BASE, TIMER_A, SysCtlClockGet() / fs); TimerControlTrigger(TIMER0_BASE, TIMER_A, true); Serial.println("Enabling everything"); //Enable everything ADCSequenceEnable(ADC0_BASE, 0); ADCIntEnable(ADC0_BASE, 0); IntEnable(INT_ADC0SS0); TimerEnable(TIMER0_BASE, TIMER_A); } /* ------------------------------------------------------------------ * Initialisation function * ------------------------------------------------------------------- */ void setup() { //Initalise serial Serial.begin(115200); //Test frequency //setTestFreq(); /* Initialize the CFFT/CIFFT module */ status = arm_rfft_init_f32(&S, &S_CFFT, FFT_SIZE, ifftFlag, doBitReverse); if(status != ARM_MATH_SUCCESS) { Serial.println("FFT Initialisation failed"); } Serial.println("Initialising ADC"); ADCInitialise(); pinMode(RED_LED, OUTPUT); pinMode(GREEN_LED, OUTPUT); pinMode(BLUE_LED, OUTPUT); analogWrite(RED_LED,140); analogWrite(GREEN_LED,0); analogWrite(BLUE_LED,0); } /* ------------------------------------------------------------------ * Return frequency at set index * ------------------------------------------------------------------- */ double fft_Index_To_Freq(int index) { return index * fs / FFT_SIZE; } int sampleNum = 0; /* ------------------------------------------------------------------ * Main loop * ------------------------------------------------------------------- */ void loop() { if(!go) return; for(int i = 0; i < FFT_SIZE; i++) { Serial.print (i); Serial.print (" "); Serial.println(samplesInput[i]); delay(11); } while(1); /* Process the data through the CFFT/CIFFT module */ arm_rfft_f32(&S, samplesInput, fftOutput); /* Process the data through the Complex Magnitude Module for calculating the magnitude at each bin */ arm_cmplx_mag_f32(fftOutput, fftOutput, FFT_SIZE); /* Calculates maxValue and returns corresponding BIN value */ arm_max_f32(fftOutput, FFT_SIZE, &maxValue, &maxIndex); go = false; double frequency = fft_Index_To_Freq(maxIndex); if (frequency > 1400 && frequency < 1600) { analogWrite(RED_LED,0); analogWrite(GREEN_LED,0); analogWrite(BLUE_LED,192); } else if (frequency > 2900 && frequency < 3100) { analogWrite(RED_LED,0); analogWrite(GREEN_LED,192); analogWrite(BLUE_LED,0); } else { analogWrite(RED_LED,192); analogWrite(GREEN_LED,0); analogWrite(BLUE_LED,0); } //Print out values Serial.print("Max value: "); Serial.println(maxValue); Serial.print("Max index: "); Serial.println(maxIndex); Serial.print("Frequency: "); Serial.println(fft_Index_To_Freq(maxIndex)); Serial.println(); //Loop infinitely //while(1); } As it is, the interrupt triggers but the values that go into the buffer are random, like white noise. The values are similar to when the input signal is disconnected. (ie reading noise rather than the signal) I am inputting a 0 - 3v sine wave at 3k Hz. Using the AnalogRead function shows the values reading correctly so the hardware is connected correctly, it just seems to be my interrupt function. The output I get is in the output file attached Output.txt Quote Link to post Share on other sites
igor 163 Posted September 26, 2014 Share Posted September 26, 2014 Have you tried your interrupt routine just taking a single sample (not using Energia). [Making sure that have the right channel, pin, etc. set up] Once get a single sample going, might try using hardware oversampling. Have you tried checking for underflow (to be sure are not reading too many samples)? Quote Link to post Share on other sites
Lyon 3 Posted September 26, 2014 Share Posted September 26, 2014 Hi, Some observations with your code: a) if posting results with a lot of data, it is better to attach a file instead - do not force the reader to scroll to end before reading the rest. If interested, he will open the results file. take into account the oversampling procedure in ADC: it is transparent to you and already delivers the average, so you don't need extra average calculation as you have done in the ADC interrupt routine. In fact, doing such is generating bad data - the ADC is triggered at sampling rate interval, takes eight samples spaced one microsecond apart and averages them, delivering one sample result. After eight samples, the interrupt occur. At this stage, extra averaging is meaningless... c) essentially, oversampling at high rates gives you from start bad results, since the waveform evoluates a lot from one microsecond to another - you may compute for instance this rate of change for a sin signal at crossing zero, since there is the biggest slope of change. What could you do is to take a needed sample number several times, triggered always at the same point and then averaging the corresponding bins, to reduce the noise, and then these could be applied for FFT. But this involves some extra hardware, depends on your application if it worths or not. L Quote Link to post Share on other sites
L.R.A 78 Posted September 28, 2014 Share Posted September 28, 2014 i didn't even read everything on the post but here:ADC1 interrupt on the tm4c123 has a problem: http://e2e.ti.com/support/microcontrollers/tiva_arm/f/908/p/294691/1308005.aspx#1308005 Quote Link to post Share on other sites
Recommended Posts
Join the conversation
You can post now and register later. If you have an account, sign in now to post with your account.