Negative sine wave values using DC offset

I am working on a project to receive sine wave from signal generator. I want to get output which shows a full sine wave on serial plotter. Let's say if the input signal is 10hz at 2.0V pk to pk. I am not getting negative amplitude of the wave in Arduino. I understand that Arduino caps anything below "0V". I have tried using dc offset variable to shift the wave up but all it does is started the wave at the value set for offset and caps the values under that.

int gain = 10;
float offset = 2.5;
unsigned long startTime;

void setup() {
  // initialize serial communication at 9600 bits per second:
  Serial.begin(9600);
  
  // Set the start time:
  startTime = millis();
}

void loop() {
  // Check if 20 seconds have passed:
  if (millis() - startTime >= 10000) {
    // Exit the loop after 10 seconds:
    return;
  }
//
  // read the input on analog pin 0:
  int sensorValue = analogRead(A0);

  // Calculate the voltage based on the raw sensor value:
  float voltage = (sensorValue * 5.0 / 1023.0);

  // Apply the AC-coupled amplifier:
  float vout = (voltage - offset) * gain;

  // Shift the signal up and down using an op amp configuration:
  float vout_shifted = (vout * 2.0) - 2.5;

  // Print the voltage amplitude to the serial plotter:
  Serial.print(vout_shifted, 3);
  Serial.println(" V");

  delay(10);
}

*Update

I am using following method to get DC Offset from Arduino itself. I have put "gnd" wire to 3.3V and used this as reference

Here is updated code

void setup() {
  // initialize serial communication at 9600 bits per second:
  Serial.begin(9600);
  
}

void loop() {
   // read the input on analog pin 0:
  int sensorValue = analogRead(A0);
  // Convert the analog reading (which goes from 0 - 1023) to a voltage (0 - 5V):
  float voltage = sensorValue * (5.0 / 1023.0);
  //using 3.3V reference to get negative values 
  voltage = voltage - 3.3 ;
  // print out the value you read:
  Serial.println(voltage);
  
  delay(20);

  
}

This has helped to get a full wave and i am using the -3.3V to shift the wave for "better" visualisation.
Please let me know if this solution is good.

You're subtracting the offset after applying the gain.

It it was me, I'd just subtract the offset (512) from the raw reading. Of course the result has to be a signed integer, or a float.

I don't understand the "shifted" calculation.

What are you getting with no input (just the bias)?

изображение



void setup() {
  Serial.begin(115200);
}

void loop() {
  static unsigned long startTimestartTime = millis();

  // Check if 10 seconds have passed: //warm time?
  while (millis() - startTime < 10000);

 // Calculate the voltage based on the raw sensor value:
  float voltage = float(analogRead(A0) - 512) * 5.0 / 1024.0;

  // Print the voltage amplitude to the serial plotter:
  Serial.print(voltage, 3);
  Serial.println(" V");

  delay(10);
}
2 Likes

amritm-2k
Your sketch does work if you do actually have a 2.5V offset on your signal.
However your use of a low Baud rate and the 10ms delay means the output doesn't look very sinusoidal.

kolaha's sketch (modified to remove the 10ms delay) shows a nice sinewave.

1 Like

Actually he used a 10 Seconds delay so that would have appeared to have stopped and after a few minutes would show a few random points.

No you can not do this in software it has to be a real hardware shift voltage applied to the signal.

1 Like

As was pointed by @kolaha, @JohnLincoln and @Grumpy_Mike you need real DC offset.
The rest is trivial. Cheers

Or, DC restoration circuit:

I have tried subracting just 512 from raw value. The output signal still does not show a full sine wave. One way I have tried is that I have put one pin at a0 and other pin from signal generator to 3.3V instead of gnd. This helped me to receive the sort of DC offset. In my code I am subtracting the dc offset (3.3) from raw value.


void setup() {
  // initialize serial communication at 9600 bits per second:
  Serial.begin(9600);
  
}

void loop() {
   // read the input on analog pin 0:
  int sensorValue = analogRead(A0);
  // Convert the analog reading (which goes from 0 - 1023) to a voltage (0 - 5V):
  float voltage = sensorValue * (5.0 / 1023.0);
  //using 3.3V reference to get negative values 
  voltage = voltage - 3.3 ;
  // print out the value you read:
  Serial.println(voltage);
  
  delay(20);

  
}

I have looked into using this if my current solution is not trivial. Thanks for the suggestion :smiley:

Wasted sample time. Try a much higher baud rate.

With that delay you take snapshots of the sine wave, 20ms apart.
You should be sampling as fast as you can, so remove that delay.
Try printing in raw A/D values.
Serial.println(analogRead(A0)); // the only line in loop...
Leo..

Why are you not doing this properly like the diagram in post#3?
That is simple and will give you a true mid range offset, well within the tolerance of the two 100K resistors.

Here is a simple trick to get the A/D to sample faster. Add this line to your setup function, it speeds up the A/D clock by changing the value of the prescale divider.

Pre scaler division for about 36K sps (samples per second) for A/D clock
'''
// set up fast sampling mode
ADCSRA = (ADCSRA & 0xf8) | 0x04; // set 16 times division

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