How to make a triangular wave with variable frequency by arduino software

Hi, someone know how to generate a triangular wave with variable frequency by arduino software?

(deleted)

i try to canghe this code to get triangular waveform with a different frequency but i c’ant do it

for (float i = 0; i <= 100; i = i + 1)//triangular waveform
{
Serial.println(i);
delay(1);
}
for (float i = 100; i >= 0; i = i - 1)
{
Serial.println(i);
delay(1);
}

there is no hope?

Without additional hardware, an Arduino can output a low (near 0 volts) and a high (near 3.3 or 5 volts depending upon the Arduino). The “analog” outputs generate a PWM signal that is high for some period of time and then low for some period of time (or is it vice versa?).

The PWM signal can be filtered to provide an approximation of an analog signal.

If you want more accuracy, you will need a DAC (digital to analog converter) connected to the SPI or the I2C interface… or you can connect a DAC up to any pins and bit-bang them yourself.

If you just want to lower the frequency of the prints that you are doing, use a bigger number in the delay(…) functions that you called.

In general, I do not like delay(…) and I prefer millis() or micros(), but in a simple program like this it does not matter.

There is hope but you are going to have to do some work, starting with a better explanation of what you are trying to accomplish.

In the future…

To post code and/or error messages:

  1. Use CTRL-T in the Arduino IDE to autoformat your complete code.
  2. Paste the complete autoformatted code between code tags (the </> button)
    so that we can easily see and deal with your code.
  3. Paste the complete error message between code tags (the </> button)
    so that we can easily see and deal with your messages.

Before posting again, you should read the three locked topics at the top of the Programming Questions forum, and any links to which these posts point.

If your project involves wiring, please provide a schematic and/or a wiring diagram and/or a photograph of the wiring.

Good Luck!

What range of frequencies are you talking about? You can use two constant current circuits to charge and discharge a capacitor. If the currents match, it can create a triangle. You use diodes and a bypass transistor to turn off each current circuit. The frequency and amplitude is controlled by the amount of current and the duration you let it ramp. It will require some external circuitry but can be done. Dwight

i try to canghe this code to get triangular waveform with a different frequency but i c’ant do it

for (float i = 0; i <= 100; i = i + 1)//triangular waveform
{
Serial.println(i);
delay(1);
}
for (float i = 100; i >= 0; i = i - 1)
{
Serial.println(i);
delay(1);
}

there is no hope?

Is that what you call a triangular waveform?? Printing numbers to the console ?

vaj4088: The PWM signal can be filtered to provide an approximation of an analog signal.

A bad approximation.

If you want more accuracy, you will need a DAC (digital to analog converter) connected to the SPI or the I2C interface... or you can connect a DAC up to any pins and bit-bang them yourself.

A slow method, unless you're using a parallel input DAC and writing to an entire pin register at once.

replace "float" with "byte"

If you want a variable frequency to produce a (bad) triangle wave, then the basic loop is:

uint32 wavelengthUs = 1000000L / frequency / 1024 / 2;

void loop() {
  static uint32_t currUs;
  static int level = 0;
  static int goingUp = true;

  uint32_t prevUs = currUs;
  currUs = micros();

  int prevLevel = level;

  while(currUs - prevUs > wavelengthUs) {
    if(goingUp) {
      if(++level >= 1024) {
        level = 1022;
        goingUp = false;
      }
    }
    else {
      if(--level < 0) {
        level = 1;
        goingUp = true;
      }
    }
    prevUs += wavelengthUs;
  }

  if(prevLevel != level) analogWrite(A0, level);
}

The while loop handles the case where other code takes so long that you have “skipped” a step. Note that this code will only handle frequencies that are about 1.024kHz or whole-number fractions of that frequency. You can get finer frequencies by using floating point - which will dither the output - but if you do that then you can’t just put in floating point variables in place of the uint32_t types above, because when numbers become big there are fewer floating points to choose from. You have to calculate “microseconds elapsed since last loop” as an integer, and then shave that off a floating point “microseconds before I need to do the next step”.

Actually - lets write that:

float wavelengthUs = (float)1000000L / (float)frequency / 1024.0 / 2.0;

void loop() {
  static uint32_t currUs;
  static int level = 0;
  static float timeToNextStepUs = wavelengthUs;
  static int goingUp = true;

  uint32_t prevUs = currUs;
  currUs = micros();

  int prevLevel = level;

  timeToNextStepUs -= currUs - prevUs;
  while(timeToNextStepUs <= 0) {
    if(goingUp) {
      if(++level >= 1024) {
        level = 1022;
        goingUp = false;
      }
    }
    else {
      if(--level < 0) {
        level = 1;
        goingUp = true;
      }
    }
    timeToNextStepUs += wavelengthUs;
  }

  if(prevLevel != level)  analogWrite(A0, level);
}

This will produce a horribly grainy, noisy, inaccurate triangle wave which will exhibit aliasing at higher frequencies … but this is (untested!) code which will do what you have asked.

PaulMurrayCbr:
If you want a variable frequency to produce a (bad) triangle wave, then the basic loop is:

uint32 wavelengthUs = 1000000L / frequency / 1024 / 2;

void loop() {
  static uint32_t currUs;
  static int level = 0;
  static int goingUp = true;

uint32_t prevUs = currUs;
  currUs = micros();

int prevLevel = level;

while(currUs - prevUs > wavelengthUs) {
    if(goingUp) {
      if(++level >= 1024) {
        level = 1022;
        goingUp = false;
      }
    }
    else {
      if(–level < 0) {
        level = 1;
        goingUp = true;
      }
    }
    prevUs += wavelengthUs;
  }

if(prevLevel != level) analogWrite(A0, level);
}




The while loop handles the case where other code takes so long that you have "skipped" a step. Note that this code will only handle frequencies that are about 1.024kHz or whole-number fractions of that frequency. You can get finer frequencies by using floating point - which will dither the output - but if you do that then you can't just put in floating point variables in place of the uint32_t types above, because when numbers become big there are fewer floating points to choose from. You have to calculate "microseconds elapsed since last loop" as an integer, and then shave that off a floating point "microseconds before I need to do the next step". 

Actually - lets write that:



float wavelengthUs = (float)1000000L / (float)frequency / 1024.0 / 2.0;

void loop() {
  static uint32_t currUs;
  static int level = 0;
  static float timeToNextStepUs = wavelengthUs;
  static int goingUp = true;

uint32_t prevUs = currUs;
  currUs = micros();

int prevLevel = level;

timeToNextStepUs -= currUs - prevUs;
  while(timeToNextStepUs <= 0) {
    if(goingUp) {
      if(++level >= 1024) {
        level = 1022;
        goingUp = false;
      }
    }
    else {
      if(–level < 0) {
        level = 1;
        goingUp = true;
      }
    }
    timeToNextStepUs += wavelengthUs;
  }

if(prevLevel != level)  analogWrite(A0, level);
}




This will produce a horribly grainy, noisy, inaccurate triangle wave which will exhibit aliasing at higher frequencies ... but this *is* (untested!) code which will do what you have asked.

analogWrite() does not make an analog or stepped voltage. It is a PWM output. It will not make a triangle shaped output. Dwight

dwightthinker: analogWrite() does not make an analog or stepped voltage. It is a PWM output. It will not make a triangle shaped output. Dwight

Yep - it will have to be filtered with some electronics.

thanks to everyone for your responses

So ,i am going to get triangular waveform only in serial monitor and not on any pin to the my board.
The frequency that i need is aproximaly about 600-1200 Hz.
I now d’ont use electical device like capacitor, resistor or other external circuit but only serial monitor and code.
I’d like get a waveform like that attached.

TKS at all

Triangular waveform.png

Savino_GM:
So ,i am going to get triangular waveform only in serial monitor and not on any pin to the my board.
The frequency that i need is aproximaly about 600-1200 Hz.
I now d’ont use electical device like capacitor, resistor or other external circuit but only serial monitor and code.
I’d like get a waveform like that attached.

TKS at all

so you have a program listening to values on the serial line and you want to “plot” something that has the shape of a triangle wave? is that what you want to do? (the frequency will obviously be then limited by the Serial output speed)

You could try something like this.

unsigned int period = 1000; // in ms
unsigned int samplingPeriod = 5; // print something every 5ms

double minValue = -10.0;
double maxValue = 10.0;

// precalculate some values
double halfPeriod = period / 2.0;
double a1 = (maxValue - minValue)/ halfPeriod;
double b1 = 2.0 * maxValue - minValue;

// time management
unsigned long currentTime, relativeTime, previousTime, lastFullPeriod;


void setup() {
  Serial.begin(115200);
  while (!Serial);
  lastFullPeriod = 0;
}

void loop() {
  double y;
  // check if it is time to display a new value
  if (((currentTime = millis()) - previousTime) >= samplingPeriod) {
    if ((relativeTime = currentTime - lastFullPeriod) >= period) {
      relativeTime = 0 ;
      lastFullPeriod = currentTime;
    }

    if (relativeTime < halfPeriod) {
      y = a1 * (double) relativeTime  + minValue;
    } else {
      y = -a1 * (double) relativeTime + b1 ;
    }

    Serial.print(currentTime);
    Serial.print("\t");
    Serial.println(y, 3);

    previousTime = millis();
  }
}

The program above will print the time in ms and then the Y value of the triangle wave.

There are four variables you can set to define the look / sampling rate of your triangle wave at the begining of the program:

unsigned int [color=blue][b]period[/b][/color] = 1000; // in ms
unsigned int [color=blue][b]samplingPeriod[/b][/color] = 5; // print something every 5ms
double [color=blue][b]minValue[/b][/color] = -10.0;
double [color=blue][b]maxValue[/b][/color] = 10.0;

The period which I initialized to 1000 ms in the code

The samplingPeriod which defines how often you want your program to output a value. I set it to print a point of your triangle wave every 5ms.
Given you print data at 115200 bauds, that’s roughly 11520 characters per second so 11 characters per millisecond. the program probably can go as fast as 1ms samplingPeriod for a while until the time value becomes too big, then you’ll have more than 11 chars printed at each loop and you will start missing some. if you don’t print the time and just the Y value, then you can probably go faster and would need to rewrite the code not using millis() but micros()

The minValue and maxValue you want the triangle to move in between. Here I set them to -10 and 10.

So, i want get series of data like function sin(x) in un void loop but not sin(x) but triangular waveform. For example i attached the frist 21 values of the my wave that i want.
I hope that it is all clear

have you read my post? have you tried? is that what you want?

if you don't read what we give you, you won't get anywhere and you'll loose all support here...

Hi L-M-L.
It was really useful. I can finally see my triangle wave with the frequency that I want. Now I’m going to work to adapt it to my problem. Thank you very much

Great - have fun!

I don't get it. Do you want to print a triangular wave form in the serial data or do you want to make a triangular wave form that one an oscilloscope. Data you print has no meaning to the term frequency. It is just data. To make an actual triangle wave, you need to either us a D/A or external circuitry. analogWrite() will not create a waveform of a triangle at the pin. Dwight