Finding rise time and amplitude

I am trying to develop a time domain reflectometer. However am stuck one issue and that is to find the rise time of a sinosoidal wave. Attached picture shows the rise time iam trying to measure.



I can save the serial data, print it as a graph as shown in the picture. Lets say I have an array:
{20, 20, 20, 72, 73, 76, 79, 83, 84, 85, 86, 86, 86, 86, 86, 86, 86, 86, 86};
and each data is spaced 312millisecond. how do i find the rise from 3rd(20) to 10th(86) datapoint in this array? I understand that the minimum and maximum should be found to locate the rise time, but how to find the time interval confuses me.

I am using a CD4060 signal generator and NucleoL452 to get data. Can anyone help.

How is start and end of the rise defined?

hi, when the value (the orange curve) increases from its minimum (20) to its levelling (86).

Are these values fixed, for all environments?

no, they will change but i just want to understand how it can be calculated.

You take the start time and the end time and calculate the difference.

thanks but how in arduino? Like this

<strong>prevData = newData;//store previous value
//newData = ADCH;//get value from A0
if (prevData < 43 && newData >= 43){//if increasing and crossing midpoint
period = timer;//get period from current timer value
timer = 0;//reset timer
}</strong>

**timer++;//increment timer**

?

Rise time is normally measured as the time to go from the 10% amplitude threshold to 90% amplitude threshold of the waveform.

For your example:
Amplitude = 86 - 20 = 66
10% threshold = 20 + 6.6 = 26.6
90% threshold = 86 - 6.6 = 79.4

You are going to have to do some interpolation to determine the times wen the signal reaches 26.6 and 79.4 (or take the measurements more frequently).

Use millis() or micros() for the time.

experiment measuring the rise time of a 10Hz sine wave
ESP32 sampling every milliSecond

// ESP32 determine sine wave rise time

// the data acquired must be the rising edge of signal - see example sine wave plot

#include <driver/dac.h>
#include <driver/adc.h>

#define RXPIN 16

// Timer0 Configuration Pointer (Handle)
hw_timer_t *Timer0_Cfg = NULL;

// array to save 100 ADC values
#define SAMPLES 100
volatile int counter = 0, adcsIndex = 0;
;
volatile unsigned int adcs[SAMPLES] = { 0 };

// The Timer0 ISR Function (Executes Every Timer0 Interrupt Interval)
void IRAM_ATTR Timer0_ISR() {
  // read ADC output to DAC
  unsigned int adc = analogRead(35);            // read ADC pin 35
  // save SAMPLE ADC values to array
  if (adcsIndex < SAMPLES) adcs[adcsIndex++] = adc;
  dac_output_voltage(DAC_CHANNEL_1, adc >> 4);  // output to DAC
  counter++;
}

// interrupt handler - change in level on RXPIN
void IRAM_ATTR handleInterrupt2() {
  if (!digitalRead(RXPIN)) return;                // look for rising edge
  detachInterrupt(digitalPinToInterrupt(RXPIN));  // detach the interrupt
  timerAlarmEnable(Timer0_Cfg);                   // start timer
}

void setup() {
  Serial.begin(115200);
  delay(2000);
  pinMode(RXPIN, INPUT_PULLDOWN);
  attachInterrupt(digitalPinToInterrupt(RXPIN), handleInterrupt2, CHANGE);
  Serial.println("\nESP32 determine sine wave rise time");
  // Configure Timer0 Interrupt 1000/second
  Timer0_Cfg = timerBegin(0, 80, true);
  timerAttachInterrupt(Timer0_Cfg, &Timer0_ISR, true);
  timerAlarmWrite(Timer0_Cfg, 1000, true);
  //timerAlarmEnable(Timer0_Cfg);
  // Enable DAC1 Channel's Output
  dac_output_enable(DAC_CHANNEL_1);   // display on DAC?
  analogSetWidth(12);                 // ADC 12bit resolution
  analogSetClockDiv(2);               //  converion time
  while (adcsIndex < SAMPLES)  ;
}

void loop() {
  while (adcsIndex < SAMPLES)   ;  // wait for data samples
  static char ch = '1';
  if (ch == '1') Serial.println("enter 1 for data plus calculations, 2 for data only (for a plot)");
  if (ch == '1' || ch == '2') {
    // plot the ADC values in the array
    for (int i = 0; i < SAMPLES; i++) {
      if (ch == '1') {
        Serial.print(i);  // display array index
        Serial.print(' ');
      }
      Serial.println(adcs[i]);  // and data samples
    }
  }
  if (ch == '1') {  // display calculations as well a data samples
    int min = 10000, minSample = 0, max = 0, maxSample = 0;
    for (int i = 0; i < SAMPLES; i++)  // find minimum sample value and array index
      if (adcs[i] < min) {
        min = adcs[i];
        minSample = i;
        if (adcs[i + 1] > min) break;
      }
    for (int i = minSample; i < SAMPLES; i++)  // find maximum sample value and array index
      if (adcs[i] > max) {
        max = adcs[i];
        maxSample = i;
      }
    // display values and voltages
    Serial.printf("minSample %d min %d %fV\n", minSample, min, min * 3.3 / 4096);
    Serial.printf("maxSample %d max %d %fV\n", maxSample, max, max * 3.3 / 4096);
    // calculate amplidude of signal
    int amplitude = max - min, rise_start, rise_end;
    Serial.printf("ampliude %d\n", amplitude);
    // calculate rising edge start and end signal amplitude (assume 10% and 90%)
    Serial.printf("rise start %d rise end %d\n", rise_start = min + (amplitude / 10), rise_end = max - (amplitude / 10));
    // find the nearest sample array index to rise start and end - could use interpolation
    int rise_start_i = minSample, rise_end_i = minSample;
    while (adcs[rise_start_i++] < rise_start)   ;
    while (adcs[rise_end_i++] < rise_end)  ;
    // each start is 1mSec therefore calculate rise time
    Serial.printf("rise time %dmSec\n", rise_end_i - rise_start_i);
  }
  while (!Serial.available())   ;
  ch = Serial.read();
}


Serial plotter output

serial monitor output gives a rise time of 30mSec

enter 1 for data plus calculations, 2 for data only (for a plot)
0 2429
1 2365
2 2298
3 2224
4 2145
5 2067
6 1989
7 1904
8 1819
9 1748
10 1671
11 1583
12 1491
13 1415
14 1334
15 1249
16 1178
17 1104
18 1023
19 959
20 890
21 821
22 763
23 707
24 656
25 603
26 561
27 525
28 482
29 453
30 435
31 411
32 391
33 380
34 372
35 373
36 376
37 387
38 401
39 425
40 446
41 471
42 506
43 557
44 576
45 642
46 693
47 745
48 811
49 873
50 941
51 1007
52 1085
53 1158
54 1232
55 1310
56 1392
57 1470
58 1559
59 1643
60 1723
61 1813
62 1883
63 1967
64 2047
65 2130
66 2198
67 2274
68 2351
69 2421
70 2485
71 2545
72 2611
73 2666
74 2722
75 2768
76 2819
77 2855
78 2905
79 2925
80 2950
81 2975
82 2995
83 3002
84 3007
85 3007
86 3007
87 2994
88 2981
89 2966
90 2931
91 2902
92 2864
93 2823
94 2784
95 2735
96 2679
97 2628
98 2559
99 2499
minSample 34 min 372 0.299707V
maxSample 84 max 3007 2.422632V
ampliude 2635
rise start 635 rise end 2744
rise time 30mSec

oscilloscope rise time
image

the data acquisition must start to capture the rising edge of signal
the above uses the rising edge of a square wave (same frequency as sine wave but out of phase) to start the timer interrupts, e.g.
image

may give you some ideas!

Thanks a lot for the code and description. It certainly will help once i switch to ESP32 which I have to later. Meanwhile I am using STM32 NucleoL with input from generator CD4060. I have recently tried Interrupt to find the time between rising and falling but am not sure if it actually gives the time I am looking for (that is the falling time in the attached picture). Compared to the data i see in the excel sheet the times are round about similar. And when moisture of soil is increased the time also increases. What do you think about this approach, am I really getting the rise/fall time here?

volatile int pwm_value = 0;
volatile int prev_time = 0;

void setup() {
  Serial.begin(115200);
    analogReadResolution(12);

  // when pin D2 goes high, call the rising function
  attachInterrupt(PB0, rising, RISING);
  
}

void loop()
{
  }

void rising() {
  attachInterrupt(PB0, falling, FALLING);
  prev_time = micros();
}

void falling() {
  attachInterrupt(PB0, rising, RISING);
  pwm_value = micros()-prev_time;
  Serial.println(pwm_value);
}

Screenshot (138)
Thanks again.

what you are getting is the time between the rising and falling edges
running on an ESP32

#define PB0 16
volatile int pwm_value = 0;
volatile int prev_time = 0;

void setup() {
  Serial.begin(115200);
  analogReadResolution(12);

  // when pin D2 goes high, call the rising function
  attachInterrupt(PB0, rising, RISING);
}

void loop() {
  if (pwm_value > 0) {
    Serial.println(pwm_value);
    delay(1000);
  }
}

void rising() {
  attachInterrupt(PB0, falling, FALLING);
  prev_time = micros();
}

void falling() {
  attachInterrupt(PB0, rising, RISING);
  pwm_value = micros() - prev_time;
  //Serial.println(pwm_value);
}

input on GPIO16 is a square wave frequency 10Hz period 100mSec
image

serial monitor displays time between rising and falling edges as 50mSec

50002
50002
50001
50002
50002
50002
50002
50002

it is not recommended to call Serial.print() and similar functions in interrupt routines, e.g. they cause an exception on the ESP32
generally save data to volatile variables and print in loop() (as in the above code)

1 Like

Thanks. I tried your suggestion about serial in loop and it works. One more thing. Like your code I also have to find amplitude (to get electrical conductivity) but that will require finding the minimum and maximum and for that input data is required to be read by analogRead. I know it cannot be read on the same pin while interrupt is in use. I see in your code for ESP32 (mine right now is STM32) that you read the data first (to get amplitude) and then call the interrupts to find time. With my code how can I read the signal from the same pin?

you could feed the signal to a digital pin to detect edges and to an analogue pin to read voltage level
on a rising edge read the ADC? e.g.

#define PB0 16
volatile int pwm_value = 0;
volatile int prev_time = 0;
volatile float voltage=0.0;

void setup() {
  Serial.begin(115200);
  analogReadResolution(12);

  // when pin D2 goes high, call the rising function
  attachInterrupt(PB0, rising, RISING);
}

void loop() {
  if (pwm_value > 0) {
    Serial.println(pwm_value);
    Serial.printf("voltage %f\n", voltage);
    delay(1000);
  }
}

void rising() {
  attachInterrupt(PB0, falling, FALLING);
  prev_time = micros();
  voltage= analogRead(34) * 3.3/4096;  // read the input pin
}

void falling() {
  attachInterrupt(PB0, rising, RISING);
  pwm_value = micros() - prev_time;
  //Serial.println(pwm_value);
}

the serial monitor displays

voltage 2.457275
50002
voltage 2.506421
50002
voltage 2.501587
50002
voltage 2.501587

the oscilloscope says the voltage is 2.7volts the signal generator says it is 2.6volts

Tried that, changed the analog pin PB0 to D7 to get the pwm and for the voltage used PB0 but the code crashes after the first pwm value. It seems because of the analogRead inside the interrupt?

volatile int pwm_value = 0;
volatile int prev_time = 0;
volatile float voltage=0.0;

void setup() {
  Serial.begin(115200);
    analogReadResolution(12);

  // when pin D2 goes high, call the rising function
  attachInterrupt(PA8, rising, RISING);
  
}

void loop(){
  if (pwm_value > 0) {
    Serial.println(pwm_value);
    Serial.printf("voltage %f\n", voltage);
    delay(1000);
  }
 }

void rising() {
  attachInterrupt(PA8, falling, FALLING);
  prev_time = micros();
  voltage= analogRead(PB0) * 3.3/4096;  // read the input pin
}

void falling() {
  attachInterrupt(PA8, rising, RISING);
  pwm_value = micros()-prev_time;
//  Serial.println(pwm_value);
}

could be - one has to be careful what one calls in interrupt routines (depends on target processor)

try setting a flag in the interrupt routine and reading the ADC in loop()

#define PB0 16
volatile int pwm_value = 0;
volatile int prev_time = 0;
volatile float voltage=0.0;
volatile bool highLevel=false;

void setup() {
  Serial.begin(115200);
  analogReadResolution(12);

  // when pin D2 goes high, call the rising function
  attachInterrupt(PB0, rising, RISING);
}

void loop() {
  if(highLevel) {
    highLevel=false;
    voltage= analogRead(34) * 3.3/4096;  // read the input pin
  }
  if (pwm_value > 0) {
    Serial.println(pwm_value);
    Serial.printf("voltage %f\n", voltage);
    delay(1000);
  }
}

void rising() {
  attachInterrupt(PB0, falling, FALLING);
  prev_time = micros();
  highLevel=true;
  //voltage= analogRead(34) * 3.3/4096;  // read the input pin
}

void falling() {
  attachInterrupt(PB0, rising, RISING);
  pwm_value = micros() - prev_time;
  //Serial.println(pwm_value);
}

on ESP32 serial monitor displays

50002
voltage 2.505615
50002
voltage 2.524951

Thanks. It was crashing because of the Serial.printf("voltage %f\n", voltage);
maybe on execution it crashes there. Changed the printf to println and removed 'voltage %f\n' and it worked and gives both the pwm and the voltage. Amazing!

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.