# How to generate a sawtooth and a triangle wave with arduino

Please let us in the comment zone any suggestions that you think will improve the article!

If you like the article click the follow button  to stay in touch with us!

In this post we will generate a sawtooth and a triangle wave signals. As you can see in this post, with an arduino  we have generated sine wave signals with the PWM option on an arduino.

The programs will be similar with the ones in the sine wave post. All the details about PWM and controling it you will find in previous articles.

Now we will discuss only the method to generate those two signals.

First example:

A sawtooth signal at 100Hz.

Our PWM signal has 31372Hz so, the period for this frequency is T1=31.8us. For 100Hz the period is T2=10ms so, T2/T1 =314 pulses from the PWM signal in each sawtooth siganl at 100Hz like in the picture below(is 31.8us not 318us).

Now the PWM pulses must have an increasing duty cycle. The program below will generate all the duty cycles for each pulse. Before that the maximum duty cycle is 255 so the last duty cycle must be 255. Because we have 314 pulses and the duty cycle must increase we choose a parameter that multiplied with maximum duty cycle give us the duty cycle for a specified pulse. Now the maximum value for that parameter at the 314th pulse is 1 (1×255=255). The parameter start from zero and increase with 0.0031(1/314 pulses).

The program is:

float x=0;
int y=0;
void setup() {
Serial.begin(9600);
}
// the loop function runs over and over again forever
void loop() {
y=x*250; // calculate duty cycle(250 not 255 because will help to turn off transistors)
delay(100);
//because the maximum duty cycle is 250 means that maximum value for x is 1
// for a 100Hz signal we have 1/314=0.0031 increasing step
x=x+0.0031;// increase the angle
Serial.println(y);// on the serial monitor will appear duty cycles between 0 and 90 deg
}

From serialprint you must take the values from 0 to 250 not bigger and put them in an array. With  an interrupt function the next program moves in the array and increase the duty cycle for each pulse. The program is:

int i=0;// position in mypwm vector
int m; // takes values from vector
int mypwm[]={0,1,2,3,3,4,5,6,6,7,8,9,10,11,12,13,14,15,16,17,17,18,19,20,20,21,22,23,24,25,26,27,27,28,29,30,31,31,32,
33,34,34,35,36,37,37,38,39,40,41,41,42,43,44,44,45,46,47,48,48,49,50,51,51,52,53,54,55,55,56,57,58,58,59,60,61,61,
62,63,64,65,65,66,67,68,68,69,70,71,72,72,73,74,75,75,76,77,78,79,79,80,81,82,82,83,84,85,86,86,87,88,89,89,90,91,
92,93,93,94,95,96,96,97,98,99,99,100,101,102,103,103,104,105,106,106,107,108,109,110,111,112,113,113,114,115,116,
117,117,118,119,120,120,121,122,123,124,124,125,126,127,127,128,129,130,130,131,132,133,134,135,136,137,137,138,
139,140,141,141,142,143,144,144,145,146,147,148,148,149,150,151,151,152,153,154,154,155,156,157,158,159,160,161,
161,162,163,164,165,165,166,167,168,168,169,170,171,172,172,173,174,175,175,176,177,178,179,179,180,181,182,183,
184,185,185,186,187,188,189,189,190,191,192,192,193,194,195,196,196,197,198,199,199,200,201,202,203,203,204,205,206,
206,207,208,209,210,210,211,212,213,213,214,215,216,216,217,218,219,220,220,221,222,223,223,224,225,226,227,227,228,
229,230,230,231,232,233,234,234,235,236,237,238,239,240,241,242,243,244,244,245,246,247,247,248,249};
// mypwm vector contains duty cycle values
void setup() {

Serial.begin(9600);

pinMode(5, OUTPUT);

cli();// stop interrupts

TCCR0A=0;
TCCR0B=0;
TCNT0=0;
TCCR0A=0b10100001;// |= (1 << WGM00);//phase correct pwm mode
TCCR0B=0b00000001;// |= (1<< CS00);// //no prescaling

TCCR1A=0;
TCCR1B=0;
TCNT1=0;
OCR1A=510;// here we have sincronized both timers (0 and 1)
//0b allow me to write bits in binary
TCCR1B=0b00001001;// |=(1 << WGM12);
//TCCR1B |= (1 << CS10);//0b00001010;// fara prescaler
TIMSK1 |=(1 << OCIE1A);

sei();
}

ISR(TIMER1_COMPA_vect){
if(i>(312)){// is n-1 because the vector is zero indexed
i=0; // we have only 313 elements in the array because in this way we have
// exactly 100.00Hz oscilloscope
}
m=mypwm[i];// the variable m takes values from vector
i=i+1; // increase the position in vector
OCR0B=m;// pin 5 on pwm with duty cycle from vector
}
void loop() {

}

The results are in the images below:

In this one is the signal without filter:

In this one with a low pass filter

The fileter applied is a lowpass filter formed by a resistor(R=220ohm) and a film  capacitor(C=1.02uF formed from 3 others small film capacitors mounted in parallel):

To modify the frequency of this signal, you must calculate the period of the chosen frequency and  it represent the T2 value from above. The number of pulses will be differnt and also the values from the vector(duty cycles).

For example for a 200Hz the period is 5ms so we have T2/T1=5ms/31.8us=157 pulses. After that in the program which calculate the duty cycle the increasing step is “x=x+1/157;”.

Second example:

A triangle wave at 100Hz.

For triangle wave  like for sine wave the half of the pulses increase and other half decrease. So the next program give us the duty cycles for a 100Hz triangle wave:

float x=0;
int y=0;
double OK=false;
void setup() {
Serial.begin(9600);
}
// the loop function runs over and over again forever
void loop() {
y=x*250; // calculate duty cycle(250 not 255 because will help to turn off transistors)
delay(100);
//because the maximum duty cycle is 250 means that maximum value for x is 1
// for a 100Hz signal we have 1/314=0.0031 increasing step
if(x<0.5 &&OK==false){
x=x+0.0031;// increase the angle on the half of the signal
}
if(x>0.5){//middle of the signal
OK=true;
}
if(OK==true){
x=x-0.0031;// decrease the angle on the other half of the signal
}
Serial.println(y);// on the serial monitor will appear duty cycles between 0 and 90 deg
}

The program for the triangle waves is:

int i=0;// position in mypwm vector
int m; // takes values from vector
int mypwm[]={0,1,2,3,3,4,5,6,6,7,8,9,10,11,12,13,14,15,16,17,17,18,19,20,20,21,22,23,24,25,26,27,27,28,29,30,31,31,32,
33,34,34,35,36,37,37,38,39,40,41,41,42,43,44,44,45,46,47,48,48,49,50,51,51,52,53,54,55,55,56,57,58,58,59,60,61,61,
62,63,64,65,65,66,67,68,68,69,70,71,72,72,73,74,75,75,76,77,78,79,79,80,81,82,82,83,84,85,86,86,87,88,89,89,90,91,
92,93,93,94,95,96,96,97,98,99,99,100,101,102,103,103,104,105,106,106,107,108,109,110,111,112,113,113,114,115,116,
117,117,118,119,120,120,121,122,123,124,124,124,124,123,122,121,120,120,119,118,117,117,116,115,114,113,113,112,111,
110,110,109,108,107,106,106,105,104,103,103,102,101,100,99,99,98,97,96,96,95,94,93,93,92,91,90,89,89,88,87,86,86,85,
84,83,82,82,81,80,79,79,78,77,76,75,75,74,73,72,72,71,70,69,68,68,67,66,65,65,64,63,62,61,61,60,59,58,58,57,56,55,55,
54,53,52,51,51,50,49,48,48,47,46,45,44,44,43,42,41,41,40,39,38,37,37,36,35,34,34,33,32,31,31,30,29,28,27,27,26,25,24,
24,23,22,21,20,20,19,18,17,17,16,15,14,13,13,12,11,10,10,9,8,7,6,6,5,4,3,3,2,1,0};
// mypwm vector contains duty cycle values
void setup() {

Serial.begin(9600);

pinMode(5, OUTPUT);

cli();// stop interrupts

TCCR0A=0;
TCCR0B=0;
TCNT0=0;
TCCR0A=0b10100001;// |= (1 << WGM00);//phase correct pwm mode
TCCR0B=0b00000001;// |= (1<< CS00);// //no prescaling

TCCR1A=0;
TCCR1B=0;
TCNT1=0;
OCR1A=510;// here we have sincronized both timers (0 and 1)
//0b allow me to write bits in binary
TCCR1B=0b00001001;// |=(1 << WGM12);
//TCCR1B |= (1 << CS10);//0b00001010;// fara prescaler
TIMSK1 |=(1 << OCIE1A);

sei();
}

ISR(TIMER1_COMPA_vect){
if(i>(312)){// is n-1 because the vector is zero indexed
i=0; // we have only 313 elements in the array because in this way we have
// exactly 100.00Hz oscilloscope
}
m=mypwm[i];// the variable m takes values from vector
i=i+1; // increase the position in vector
OCR0B=m;// pin 5 on pwm with duty cycle from vector
}
void loop() {

}

The results are:

Without filter:

With the same low pass filter like in the sawtooth example:

The method to change the frequency is like for the sawtooth example.

Also you can generate for example on pin 5 a sawtooth signal and on pin 6 a triangle signal, but both at the same frequency.

## One thought on “How to generate a sawtooth and a triangle wave with arduino”

1. veng

Hi, thank you so much for your posting!
For my understand, the posting one Sketch one PWM sampling one low frequency.
Could you make one Sketch have two low frequencies? In other word the programmer contains PWM 100Hz & PWM 60Hz, once upload to the chip, both PWM 100Hz & PWM 60H are ready by control external toggle switch.

Thank you again!!!!