Error trying to implement an ADC + Timestamp timer logger using Ringbuf

Drodru

Member
I need a 100 KHz ADC sampling data rate for a pressure transducer (Analog pin), but my code keeps crashing. Everything goes well until I set performance_status = true. In that moment, Teensy 4.1 crashes and I cannot find the bug. I set all these variables as volatiles, if anyone wonders. I share the important parts of the code below.

Code:
// Transducer variables
const uint16_t transducer_max_pressure = 250;
const uint16_t transducer_min_value = 496;
const uint16_t transducer_max_value = 2482;
const float transducer_const = transducer_max_pressure / (transducer_max_value - transducer_min_value);
volatile uint32_t transducer_counter = 0;
volatile uint32_t transducer_freq = 0;
volatile uint16_t transducer_raw = 0;
volatile float transducer_avg = 0;
volatile float transducer_pressure = 0;

// SD
const uint32_t RB_size = 400 * 512;
const uint32_t file_size = 10 * 100000 * 600;
SdFs SDF;
FsFile file_pressure;
RingBuf<FsFile, RB_size> RB_pressure;

ADC *adc = new ADC();

void setup()
{
    adc->adc0->setAveraging(8);
    adc->adc0->setResolution(12); // set bits of resolution
    adc->adc0->setConversionSpeed(ADC_CONVERSION_SPEED::VERY_HIGH_SPEED); // change the conversion speed
    adc->adc0->setSamplingSpeed(ADC_SAMPLING_SPEED::VERY_HIGH_SPEED); // change the sampling speed
    transducer_set_high_speed(true);
  
    SDF.begin(SdioConfig(FIFO_SDIO))
    file_open();
}

bool file_open()
{
    int i = 0;
    String newName = i + file_data_name;
    while (SDF.exists(newName.c_str())) newName = ++i + file_pressure_name;

    if (!file_pressure.open(newName.c_str(), O_RDWR | O_CREAT))
    {
        send_order(15);
        error_warning();
        SD_ready = false;
        return true;
     }
      file_pressure.println("Time, Chamber_Pressure, Raw_Transducer");
      file_pressure.preAllocate(file_size);
      RB_pressure.begin(&file_pressure);
}

void transducer_set_high_speed(bool enable)
{
    adc->adc0->stopTimer();
    adc->adc0->startSingleRead(PIN_TRANSDUCER); // call this to setup everything before the Timer starts, differential is also possible
    adc->adc0->enableInterrupts(transducer_measure);

    if (!enable)
    {
      adc->adc0->setAveraging(100); // set number of averages
      adc->adc0->startTimer(10000); //frequency in Hz
    }
    else
    {
      adc->adc0->setAveraging(4); // set number of averages
      adc->adc0->startTimer(1000000 / transducer_timer); //frequency in Hz
    }
}

void transducer_measure()
{
    // Read analog pin and change it to bars
    transducer_raw = adc->adc0->readSingle();

    transducer_pressure = transducer_raw * transducer_const - transducer_offset;
 
    transducer_avg += transducer_pressure;
    ++transducer_counter;
    ++transducer_freq;

    // Write in SD
    if (performance_status) file_pressure_update();
}

void file_pressure_update()
{
    if (!SD_ready) {return;}

    // Transducer time (in s)
    float transducer_time = micros() / 10000000. - last_reset_micros;
    RB_pressure.print(transducer_time, 6);
    RB_pressure.print(", ");

    // Transducer measure already converted
    RB_pressure.print(transducer_pressure);
    RB_pressure.print(", ");

    // Raw transducer data
    RB_pressure.println(transducer_raw);

    // Write it in the round buffer
    if (RB_pressure.bytesUsed() >= 512 && !file_pressure.isBusy())
    {
      RB_pressure.writeOut(512);
    }
}
 
Last edited:
Have you tried to include CrashReport() support in the setup() function as follows:

Code:
while(!Serial) ;
if (CrashReport)
Serial.print(CrashReport);


Note that with the Crash Report monitor written this way (specifically, the while(!Serial);), it will wait forever if the Serial Monitor is not connected, so this added code should be commented out and/or removed after you've identified & resolved the cause of your crash.

Mark J Culross
KD5RXT
 
Below still not the best to leave in when not on hand for connect to SerMon - but if the Crash is repeating the Teensy will be on an 8 second restart cycle - versus the below silently stop with no SerMon.

Perhaps if SerMon not always needed - and not always crashing:
Code:
while(!Serial && CrashReport) ;
if (CrashReport)
    Serial.print(CrashReport);
 
I need a 100 KHz ADC sampling data rate for a pressure transducer (Analog pin), but my code keeps crashing. Everything goes well until I set performance_status = true. In that moment, Teensy 4.1 crashes and I cannot find the bug. I set all these variables as volatiles, if anyone wonders. I share the important parts of the code below.
When you set performance_status = true, you begin logging to SD, which you do from transducer_measure(), which is called from the ADC ISR. In transducer_measure, you write to the RingBuf, which you do via its print() method, then read from the RingBuf and write to the SD file. You are trying to do all of this at 100 kHz, so there is something less than 10 us available, and I think the processor is simply running out of time. The minimum time for the SD write is 5-6 us, which leaves only 4 us for the averaged ADC read and the RingBuf write, and I don't think that's enough. Try reducing the frequency and the ADC averaging and see if it still crashes. If it doesn't, then you know you're trying to do too much. If you want to get to 100 kHz, you likely need to write to SD from loop() instead of from the ISR, so the system looks like this:

Code:
[ISR]--->[RB]---->[loop]---->[SD]

You could probably speed up the ISR substantially by using sprintf() to write a string to a char array and then writing that string to RB using memcpyIn() rather than print().
 
When you set performance_status = true, you begin logging to SD, which you do from transducer_measure(), which is called from the ADC ISR. In transducer_measure, you write to the RingBuf, which you do via its print() method, then read from the RingBuf and write to the SD file. You are trying to do all of this at 100 kHz, so there is something less than 10 us available, and I think the processor is simply running out of time. The minimum time for the SD write is 5-6 us, which leaves only 4 us for the averaged ADC read and the RingBuf write, and I don't think that's enough. Try reducing the frequency and the ADC averaging and see if it still crashes. If it doesn't, then you know you're trying to do too much. If you want to get to 100 kHz, you likely need to write to SD from loop() instead of from the ISR, so the system looks like this:

Code:
[ISR]--->[RB]---->[loop]---->[SD]

You could probably speed up the ISR substantially by using sprintf() to write a string to a char array and then writing that string to RB using memcpyIn() rather than print().
Using printf() or sprintf() inside an ISR sounds risky to me. I think it would be better to store the binary values in the buffer and do the sprint() or printf from loop(). That would free up more time in the ISR also. Eliminating the ADC averaging may also be needed.

I also wonder if 100KHz sampling is required. Most low-cost analog pressure sensors have an output bandwidth of only a few KHz.
 
Thanks for all the anwsers, I'll try to implement it as soon as I fix my teensy or replace it.
Anyways, would be possible to do change the code to implement a DMA timestamp (there is no examples about this, just an analog buffer in ADC examples).
 
Back
Top