ADXL - ADS1256 - Teensy 4.0 =>Python

frohr

Well-known member
Hi all,
I collect data from adxl1005, use 24bit ads1256 30k SPS and send data via usb to PC - Python app and plot data. But there are huge spikes.
Any suggestion where is problem?
Figure_1.png


My code is:

Code:
#include <SPI.h>

// PIN assignements
#define CS_PIN    21
#define DRDY_PIN  22
#define RST_PIN   8

// SPI
#define SPISPEED 1950000               

// ADS1256 registers
#define STATUS_REG   0x00
#define MUX_REG      0x01
#define ADCON_REG    0x02
#define DRATE_REG    0x03

// ADS1256 commands
#define WAKEUP_CMD   0x00  // completes SYNC and exits standby mode
#define RDATA_CMD    0x01  // read data
#define RREG_CMD     0x10  // read register (register ID in low nibble)
#define WREG_CMD     0x50  // write register (register ID in low nibble)
#define SELFCAL_CMD  0xF0  // offset and gain self-calibration
#define SYNC_CMD     0xFC  // synchronize the A/D conversion
#define STANDBY_CMD  0xFD  // begin standby mode
#define RESET_CMD    0xFE  // reset to power-up values

#define VREF            (2.5)   // for conversion of raw ADC data to Volts
#define BUFFER_SIZE     (30000) // samples

#define DRATE_15K       (0xE0)  // 15 kSPS
#define DRATE_30K       (0xF0)  // 30 kSPS

#define STATUS_REG_0x01 (0x01)  // MSB first, Auto-Cal Dsbl, Input Buffer Dsbl
#define STATUS_REG_0x03 (0x03)  // MSB first, Auto-Cal Dsbl, Input Buffer Enbl
#define STATUS_REG_0x07 (0x07)  // MSB first, Auto-Cal Enbl, Input Buffer Enbl
#define ADCON_REG_VALUE (0x21)  // 0 01 00 001 => Clock Out Freq = fCLKIN,
                                //             Sensor Detect OFF, gain 2 7.68MHz 
#define DRATE_REG_VALUE (BUFFER_SIZE==15000 ? DRATE_15K : DRATE_30K) 
#define MUX_REG_VALUE   (B00000111) // AIN7

bool dataReadyToSend = false; // Flag indicating if data is ready

volatile int DRDY_state = HIGH;
char inChar;

void writeRegister(uint8_t address, uint8_t value)
{
  SPI.transfer( WREG_CMD | address );
  SPI.transfer( 0x00 );
  SPI.transfer( value );
  delayMicroseconds( 100 );
}

volatile bool streaming = false; // make it volatile since it could be changed in interrupt context


void setup() {
  Serial.begin(9600);
  while (!Serial) {}
  START_SPI();
  attachInterrupt(digitalPinToInterrupt(DRDY_PIN), DRDY_Interrupt, FALLING);
}

void loop() {
  // Check if data is available to read from the serial port
  if (Serial.available() > 0) {
    char inChar = Serial.read();

    // Start streaming if 'r' is received
    if (inChar == 'r') {
      streaming = true;
    }
    // Stop streaming if 's' is received
    else if (inChar == 's') {
      streaming = false;
    }
  }

  if (streaming && DRDY_state == LOW) {
    float voltage = READ_ADC();
    Serial.write((const byte*)&voltage, sizeof(voltage));
  }
}

float READ_ADC()
{
  int32_t adc_raw;   
  SPI.beginTransaction(SPISettings(SPISPEED, MSBFIRST, SPI_MODE1));                             
  digitalWriteFast(CS_PIN, LOW);   
  
  SPI.transfer( RDATA_CMD );
  delayMicroseconds(5); // delay MUST be >= 5 us     
  adc_raw = 0;   
  adc_raw |= SPI.transfer(0);
  adc_raw <<= 8;                 
  adc_raw |= SPI.transfer(0);
  adc_raw <<= 8;               
  adc_raw |= SPI.transfer(0);   
  
  digitalWriteFast(CS_PIN, HIGH);     
  SPI.endTransaction(); 

  if (adc_raw & (1<<23)) {    // if 24-bit MSB == 1
    adc_raw |= 0xFF000000;    //   value is negative, sign-extend to 32 bits
  }
  float v = adc_raw * (float)(VREF / (1<<23));
  return( v );
} 

// DRDY falling-edge interrupt function
void DRDY_Interrupt()
{
  DRDY_state = LOW;
}
 
void waitDRDY()
{
  // wait for DRDY_state to be LOW
  while (DRDY_state == HIGH) {
    continue;
  }
  // then set it back to HIGH
  DRDY_state = HIGH;
}

void START_SPI ()
{
  // configure I/O pins
  pinMode(CS_PIN, OUTPUT);
  digitalWrite(CS_PIN, HIGH); // init CS high (disable)
  pinMode(DRDY_PIN, INPUT);
  pinMode(RST_PIN, OUTPUT);
 
  // hardware reset ADS1256 by toggling pin LOW then HIGH
  digitalWrite(RST_PIN, LOW);
  delay(1);
  digitalWrite(RST_PIN, HIGH);
  delay(500); //500
 
  // start the spi-bus
  SPI.begin();
  delay(500); //500
 
  // wait for DRDY signal LOW
  while (digitalRead(DRDY_PIN)) {}
  
  SPI.beginTransaction(SPISettings(SPISPEED, MSBFIRST, SPI_MODE1));
  digitalWriteFast(CS_PIN, LOW);
  delayMicroseconds(100);   //100

  // reset to power-up state
  SPI.transfer( RESET_CMD );
  delay(5);

  // configure registers
  writeRegister( STATUS_REG, STATUS_REG_0x03 );
  writeRegister( ADCON_REG, ADCON_REG_VALUE );
  writeRegister( DRATE_REG, DRATE_REG_VALUE );
  writeRegister( MUX_REG, MUX_REG_VALUE );

  // auto-calibrate (send command, wait DRDY = LOW, then wait DRDY = HIGH)
  SPI.transfer( SELFCAL_CMD );
  uint32_t t0 = micros();
  while (digitalReadFast(DRDY_PIN)!=LOW && micros()-t0 < 10000) {}
  while (digitalReadFast(DRDY_PIN)!=HIGH && micros()-t0 < 10000) {}
 
  digitalWriteFast(CS_PIN, HIGH);
  SPI.endTransaction();
}

In Python I have:

Code:
import serial
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation
from matplotlib.widgets import Button
ser = serial.Serial('COM8', 2000000, timeout=1)
streaming = False
filter_range = None 
fs = 30000
window_size = 30000 

fig, ax = plt.subplots()
plt.subplots_adjust(bottom=0.25) 
line, = ax.plot([], [], 'r-')
ax.set_ylim(-3, 3)

def fft_filter(data_array, fs, low_cut, high_cut):
    N = len(data_array)
    freq = np.fft.fftfreq(N, d=1/fs)
    fft_data = np.fft.fft(data_array)
    mask = ((freq > low_cut) & (freq < high_cut)) | ((freq < -low_cut) & (freq > -high_cut))
    filtered_fft = fft_data * mask
    filtered_signal = np.fft.ifft(filtered_fft).real
    return filtered_signal

def init():
    ax.set_xlim(0, window_size)
    ax.set_ylim(-3, 3)
    return line,

def update(frame):
    global y_data
    if streaming:
        data = ser.read(4 * window_size)
        if len(data) < 4 * window_size:
            return line, 
        y_data = np.frombuffer(data, dtype=np.float32).copy()
        y_data -= np.mean(y_data)  # Remove mean
        y_data /= 0.020  # Normalize to g               
        if filter_range:
            y_data = fft_filter(y_data, fs, *filter_range)
        line.set_data(np.arange(len(y_data)), y_data)
    return line,


def start(event):
    global streaming
    streaming = True
    ser.write(b'r')

def stop(event):
    global streaming
    streaming = False
    ser.write(b's')

def filter_10_1000(event):
    global filter_range
    filter_range = (10, 1000)

def filter_500_15000(event):
    global filter_range
    filter_range = (500, 15000)

def no_filter(event):
    global filter_range
    filter_range = None

# Adjusted button creation and placement
button_configs = [
    (0.05, 0.05, 'Start', start, 0.1),  # Increased width for longer labels
    (0.16, 0.05, 'Stop', stop, 0.1),
    (0.27, 0.05, '10-1000Hz', filter_10_1000, 0.15),  # Adjusted for label length
    (0.43, 0.05, '500-15000Hz', filter_500_15000, 0.17),  # Adjusted for label length
]

buttons = []
for x, y, label, action, width in button_configs:
    axbutton = plt.axes([x, 0.01, width, 0.075])
    button = Button(axbutton, label)
    if action:
        button.on_clicked(action)
    buttons.append(button)

ani = FuncAnimation(fig, update, init_func=init, blit=False, interval=100, cache_frame_data=False)

plt.show()

Thank you
 
Your code seems to interrogate the ADC without using the waitDRDY(). The sampling rate is set by a 5 us delay, and the SPI bus clock speed? Are you trying to read out too fast?
 
Back
Top