Sine wave generation using look up table

Thread Starter

Vihaan@123

Joined Oct 7, 2025
255
Made some progress, as a first step not thinking of fractional values i want to clarify with logic.
1. Considering 1024 points of 360 degrees, hence step degree = 0.3515Degree.
2. Creating sine look up table of 1024 points
1776141543054.png
3. To generate a sine wave of 50Hz with sampling rate of 50uS(20KHz).
1776141846507.png
Are my above calculations correct? But the table pointer (col P) is not an integer it is fractional, then which point in the table i have to consider? 2.56 can i consider 3, 5.12 as 5?
 

Thread Starter

Vihaan@123

Joined Oct 7, 2025
255
I have the proto code now
Code:
uint8_t phase_increment = 2; // for 50Hz signal

uint8_t pointer = 0;

int main()

{
while(1);
}

void Pwm_interrupt() {

CCR_Register = sinepwmtable[pointer]*ARR/ 100; // ARR is the frequency value corresponding to 20KHz

pointer += phase_increment;

if(pointer >= 1024){

pointer -= 1024;

}

}
I will start adding the actual code for testing and update the progress.
 

Thread Starter

Vihaan@123

Joined Oct 7, 2025
255
I have updated the code i could see the varying PWM duty cycle only issue is i have used 20kHz i could only see 1kHz will rectify it, i will try with DAC once the issue is rectified. How good the sine wave without distortions i have to confirm.
1776153856366.png
 

MrChips

Joined Oct 2, 2009
34,866
How to scale fractional numbers to integers

A sine function will give numbers ranging from -1 to +1.
v = sin(θ)

A 12-bit unipolar DAC expects inputs from 0 to 4095.
We need a sine wave amplitude of 2048 with positive offset of 2048.

v = 2048 + 2048 * sin(θ)

Then take the integer value.

v = int (v)

Check that there are no negative values and no values above 4095.
 

MrAl

Joined Jun 17, 2014
13,716
I have been struggling and tried everything possible to understand but no use, and i really need help here, I have created a sine look up table every 5 degree (i will modify to create a 1024-point sine look up table once i am clear with this). The array is like below, i have not added all the values, there will be some negative values as well.
Code:
sinearray[]={0.087155743    0.173648178    0.258819045    0.342020143    0.422618262    0.5    0.573576436    0.64278761    0.707106781    0.766044443    0.819152044    0.866025404    0.906307787, -0.087155743, -0.173648178, -0.258819045, -0.342020143, -0.422618262, -0.5}
I want to generate a sine wave using PWM signal duty cycle variation. Some of the understandings i have are
a. Make the sinearray negative values to positive values.
b. I can generate pwm frequency of ex: 10KHz and in the update interrupt i can update the duty cycle register.
Now the task is for example i have to generate 1KHz , 2KHz sine wave, what i have to do?
c. I am running the main clock at say 4MHz (after pre scaler) then for 10KHz the ARR register is 4MHz / 10KHz = 400 count.
How do i load the sinevalues into duty cycle register?
Hello there,

Are you saying that you make a different PWM pattern that repeats to get one part of the sine wave? For example, 50 percent duty cycle for 10 cycles.
If so, that's not the way it is usually done. Usually you vary EACH pulse to correspond to the sine amplitude. That means typically there will only be ONE pulse that has 50 percent duty cycle.
This also gets you to the highest frequency you can generate with a given pattern.

To analyze the output purity, you use a Fourier analysis. You can do that on the raw pulsed output first.
If you want to go a little further with this, you can add a filter on the output and get a cleaner looking sine wave. To figure out the purity then you have to use a spectrum analyzer, or you can do it in math on the computer by also considering the transfer function of the filter. That way you can still compute the purity which is usually the THD measurement.

This gets interesting because there are a lot of different ways to do this and trying to find the optimal PWM wave is also interesting.
 

skstrobel

Joined Nov 29, 2023
28
There are some big advantages to using DMA, if that fits your use case, especially as the update rate increases. But you can update at 10 KHz using an interrupt service routine (ISR) on even relatively slow MCUs.

There is a trick for getting very fine frequency resolution (albeit with a bit of jitter) that is used by direct digital synthesizers (DDS) for generating RF signals. I don't know if it is possible to do with DMA on a MCU or not, but you definitely can do it if you are using an ISR and software. I'll describe the low frequency resolution implementation first...

Use a "phase" variable (static or global in C) to index your 1024-element sine wave lookup table and remember the phase for the next update. To set the frequency, calculate and store an "increment" that gets added to the phase each time the ISR executes. If that happens at 10KHz, the lowest frequency you can generate is 10000/1024, or about 10Hz. That is also the resolution between each higher-frequency step. To get better frequency resolution, you can increase the size of the sine table, decrease the update rate, or see the next paragraph. Phase values above 1023 don't matter, as they just wrap around the sine table, but you do need to ignore them so you don't run off the end of the table. Use code like "phase & 0x3FF" in C to keep only the least-significant 10 bits, throwing away the upper six bits when doing the lookup.

Now consider what happens if you instead store the phase in the most significant bits of a 16-bit variable. You need to right-shift the phase value ( >> 6 ) to get the 10-bit index each time you do the sine table lookup; do that in a temporary variable and keep the phase for next time in the most significant bits. To generate the same 10 Hz now, you need to set the increment to 1 << 6, or 64. If you set it for 32, the index used for the sine table lookup will change every other time, giving you 5 Hz output. If you set it to 16, you get 2.5Hz, and so on. Note that the frequency resolution no longer depends on the size of the sine table, but only on the update rate and the max value of a 16-bit integer. So you get
10000/(65536), or 0.15Hz, resolution and all it costs in MCU time is copying the phase into a temporary and doing >> 6 before the sine table lookup.

So now you can make your sine table virtually any power of two and the only thing you have to change is how many bits you shift the phase right during the lookup and how you calculate the new increment when the desired output frequency changes. You might find that 256 elements in the sine table is plenty. Going the other way, if you need extreme accuracy in the output, you can use the lower-order phase bits to do linear interpolation between the stored sine-table values.

Note that there are at least four sources of error in the output. It might be surprising how easily you can hear them if you are generating audio.
  • The accuracy of the samples in the sine table. In your case, floating point numbers have abundant resolution so this isn't a concern.
  • The update interval. Generating a 1KHz sine wave using an ISR with a 10KHz update rate means there will be only 10 stairsteps in the whole wave. You can use a hardware filter to smooth that out, but increasing the sample rate is "free" if your MCU has spare processing time and makes the output a lot easier to filter.
  • The number of elements in the sine table. This is where interpolation can help. It probably won't provide a lot of benefit unless your output frequencies are very low compared with your update rate.
  • Jitter in the ISR timing. A DAC with a FIFO can remove this, but your MCU probably won't have that. Otherwise, making sure this ISR has a higher priority than any others is essential.

Finally note that all of the above is the same whether you are outputting through a DAC or with PWM, although PWM introduces yet another source of noise :)
 

ronsimpson

Joined Oct 7, 2019
4,705
will modify to create a 1024-point sine look up table
I did not read all 27 posts. Maybe this was already mentioned. Area-1's numbers are the same as Area-2, backwards. Area-3 is really the same as Area-1 but upside down. A 256 table + simple math, makes a 1024 table.

1776470327891.png
 

Futurist

Joined Apr 8, 2025
778
Thank you all for the clarifications and support, the board is NUCLEO-F446RE | Product - STMicroelectronics
I have just started putting together the logic, once i am clear will start thinking about implementing in the hardware board. Thank you once again.
When I began exploring MCUs I used that same board to use its DAC, to generate a Sin signal from a table, I did more or less what @MrChips advises above.
 
Top