Jump to content

ADC interrupt sampling

Recommended Posts

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.

Link to post
Share on other sites

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.






Link to post
Share on other sites

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() {

void loop() {

  int sensorValue = analogRead(A3);
  // print out the value you read:
  delay(1); // delay in between reads for stability
Link to post
Share on other sites

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;
  if(sampleIndex == FFT_SIZE)
    sampleIndex = 0;
    go = true;

//Initialise the ADC
void ADCInitialise()
  Serial.println("Setting up ADC pin");
  //Setup pin for ADC
  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");
  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);
  TimerEnable(TIMER0_BASE, TIMER_A);

/* ------------------------------------------------------------------
* Initialisation function
* ------------------------------------------------------------------- */
void setup()
  //Initalise serial
  //Test frequency

  /* 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");
  pinMode(RED_LED, OUTPUT);
  pinMode(BLUE_LED, OUTPUT);

/* ------------------------------------------------------------------
* 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 (" ");
  /* 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)
  else if (frequency > 2900 && frequency < 3100)
  //Print out values
  Serial.print("Max value: ");
  Serial.print("Max index: ");
  Serial.print("Frequency: ");
  //Loop infinitely

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 




Link to post
Share on other sites


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.

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


Link to post
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

  • Create New...