This sine table calculator lets you choose how many SPWM steps (pillars) you want, and then gives you the correct sineTable[] values (0 to 255 range) to paste directly into Arduino code.

The above image shows one half cycle example of an SPWM waveform, which is a PWM equivalent of a pure sine waveform.
In the above image we see 7 pillars or varying PWM pulses being generated per half wave SPWM cycle. 7 pulses may look good if an iron core transformer is used but the resultant sine waveform might not be pure enough.
Ideally, you must use at least 20 to 30 PWM pulses or pillars on each of these half cycle waveforms, meaning, the higher the number of these pulses, the purer the sine wave will be, and closer it will be to an actual sine waveform.
Features of the Calculator:
- Simple dropdown to select number of SPWM steps
- Calculates values using formula:
PWM = 127.5 + 127.5 × sin(angle) - Displays the array in Arduino-friendly format
What It Does:
When a user selects a value like 11, 21, or 31, it will instantly give you the corresponding duty cycle outputs:
int sineTable[21] = {128, 159, 185, 208, …, 128};You can copy-paste it directly into your Arduino sketch.
Example:
Consider the following Arduino Code of an SPWM generator:
int freq = 50; // Can change to 60
int steps = 21;
int sineTable[21] = {
128, 147, 167, 185, 202, 218, 231, 241, 249, 253, 255, 253, 249, 241, 231, 218, 202, 185, 167, 147, 128};
};
int pwmPinA = 8;
int pwmPinB = 9;
void setup() {
pinMode(pwmPinA, OUTPUT);
pinMode(pwmPinB, OUTPUT);
}
void loop() {
int microDelay = (1000000 / freq) / (2 * steps); // delay per step
for (int i = 0; i < steps; i++) {
analogWrite(pwmPinA, sineTable[i]);
analogWrite(pwmPinB, 0);
delayMicroseconds(50); // Dead time
delayMicroseconds(microDelay);
}
for (int i = 0; i < steps; i++) {
analogWrite(pwmPinA, 0);
analogWrite(pwmPinB, sineTable[i]);
delayMicroseconds(50); // Dead time
delayMicroseconds(microDelay);
}
}
The code is designed to generate 21 pillars or 21 steps, or 21 pulses on each SPWM half cycles. You can easily change the following two values in code in order to change the number of these pulses or pillars, simply by tweaking the following two lines in the code:
int steps = 21;
int sineTable[21] = {But that does not complete the procedures because then you will also need to change the following corresponding values which represent the PWM duty cycle levels that shape the sine wave.
128, 159, 185, 208, 230, 243, 250, 255, 250, 243,
230, 208, 185, 159, 128If you don't do this, then your code will not work and not compile.
So that is when you can use the above Sine Table Calculator to calculate these corresponding PWM duty cycle levels, so that your Arduino is able to generate the required SPWM code correctly and validly.
Improving the Code Even Further with Timer1 and Interrupt for SPWM
// ==== CONFIGURATION ====
// Frequency of sine wave output (can change to 60)
const int freq = 50;
// Number of steps in half sine wave
const int steps = 21;
// Dead time in microseconds
const int deadTime = 50;
// Output pins for SPWM (must be PWM capable)
const int pwmPinA = 9; // OC1A on Arduino Uno
const int pwmPinB = 10; // OC1B
// Lookup table for one half sine wave (0 to 255)
const byte sineTable[steps] = {
128, 147, 167, 185, 202, 218, 231, 241, 249, 253,
255, 253, 249, 241, 231, 218, 202, 185, 167, 147, 128
};
// Variables for ISR control
volatile int index = 0;
volatile bool halfCycle = false; // false = A active, true = B active
void setup() {
pinMode(pwmPinA, OUTPUT);
pinMode(pwmPinB, OUTPUT);
// Disable PWM on Timer1 temporarily
TCCR1A = 0;
TCCR1B = 0;
// Set Timer1 compare interrupt for step timing
// Total microseconds per step = 1 / (2 * freq * steps)
unsigned long stepTimeMicros = 1000000UL / (2UL * freq * steps);
// Convert to timer ticks (16 MHz / 8 prescaler = 2 MHz = 0.5 us per tick)
unsigned int ocr1a = stepTimeMicros * 2;
// Set Timer1 compare match value
OCR1A = ocr1a;
// Set Timer1 mode to CTC (Clear Timer on Compare Match)
TCCR1B |= (1 << WGM12);
// Set prescaler to 8
TCCR1B |= (1 << CS11);
// Enable compare match interrupt
TIMSK1 |= (1 << OCIE1A);
}
void loop() {
// Main loop does nothing, all work is done by ISR
}
// ==== INTERRUPT SERVICE ROUTINE ====
// Runs every stepTimeMicros microseconds
ISR(TIMER1_COMPA_vect) {
// First add dead time
analogWrite(pwmPinA, 0);
analogWrite(pwmPinB, 0);
delayMicroseconds(deadTime); // may cause slight jitter but works
// Now output PWM value based on current half-cycle
if (!halfCycle) {
// A active
analogWrite(pwmPinA, sineTable[index]);
analogWrite(pwmPinB, 0);
} else {
// B active
analogWrite(pwmPinA, 0);
analogWrite(pwmPinB, sineTable[index]);
}
index++;
// If we finish one half-cycle
if (index >= steps) {
index = 0;
halfCycle = !halfCycle; // switch side
}
}
How the Code Works
So first, in the configuration part:
We fix the sine wave frequency we want like 50 Hz, then how many steps we want to divide each half cycle into, like 21, so full cycle will have 42 steps. Then we put a small dead time like 50 microseconds to stop shoot-through.
We define the output pins A and B. We use PWM pins 9 and 10 on Uno which are OC1A and OC1B so compatible with Timer1. Then we define a lookup table for sine wave values scaled between 128 to 255 in 21 steps. This gives us a clean half sine shape.
In setup():
We first set the PWM pins as OUTPUT. Then we turn off Timer1’s PWM functions for now. Then we calculate how many microseconds each step should last.
For example for 50 Hz and 21 steps, each half cycle must be done in 10,000 microseconds (20 ms / 2), so 10,000 / 21 = around 476 microseconds per step.
Then we convert that into Timer1 tick counts. Since we will use prescaler 8 that gives us 2 million ticks per second or 0.5 µs per tick. So we multiply our microseconds by 2 to get ticks.
Then we load that value into OCR1A register of Timer1 and set it to CTC mode (Clear on Compare Match) so that interrupt will happen every step.
Finally we enable interrupt on compare match A.
In loop():
We do nothing here all work is done by the interrupt.
In ISR:
So when interrupt triggers, first we add the dead time by setting both PWM pins LOW and delay for dead time (just small 50 µs).
Then we check which half cycle we are in, if halfCycle is false then we are on A side, so we give PWM to pin A, pin B remains OFF. If halfCycle is true then we reverse, pin B gets PWM, pin A is OFF.
After writing the PWM, we increase the index for the sine step. Once we reach end of steps then we reset index to 0 and toggle halfCycle.
This keeps repeating forever, generating perfect alternate sine PWM on both pins.
Final Thoughts
So now this version is much better than previous code because:
- No blocking delays so other code can run parallel.
- Very precise sine wave step timing using Timer1.
- Dead time added manually.
- Easily expandable for more steps or frequency.
- Good for SPWM in inverters, UPS, motor control.
Need Help? Please Leave a Comment! We value your input—Kindly keep it relevant to the above topic!