Generating a sine wave

Sinusoidal signals are commonly used in signal processing applications and generating these waveforms provides an interesting project that is the focus of this recipe. A common approach is a direct method that stores the sinusoidal waveform samples in a look-up-table (LUT). This recipe is called dacSinusoid_c5v0.

Getting ready

First, we need to calculate the (12-bit) DAC values that will be stored in the LUT. We'll attempt to generate a 50 Hz sinusoidal signal and use a spreadsheet (for example, Microsoft Excel) to calculate the following values:

Smpl. No

Theta Rads

floor((sin(theta)+1)*4095/2)

0

0

2047

1

0.31415927

2680

2

0.62831853

3250

3

0.9424778

3703

4

1.25663706

3994

5

1.57079633

4095

6

1.88495559

3994

7

2.19911486

3703

8

2.51327412

3250

9

2.82743339

2680

10

3.14159265

2047

11

3.45575192

1414

12

3.76991118

844

13

4.08407045

391

14

4.39822972

100

15

4.71238898

0

16

5.02654825

100

17

5.34070751

391

18

5.65486678

844

19

5.96902604

1414

How to do it…

Follow the outlined steps to generate a sine wave:

  1. Create a new recipe called dacSinusoid_c5v0 by cloning timerISR_c5v0 from the Using timers to trigger conversions recipe.
  2. Replace timerISR.c with a file named dacSinusoid.c and add a declaration for an LUT:
    uint16_t dacLUT [] = {2047, 2680, 3250, 3703, 3994, 
                    4095, 3994, 3703, 3250, 2680,
                    2047, 1414,  844,  391,  100, 
                    0,     100,  391,  844, 1414 };
  3. Add an interrupt handler to service TIM2:
    /*------------------------------------------------
      TIM2 IRQ Handler
     *------------------------------------------------*/
    void TIM2_IRQHandler (void) {
      static uint8_t idx = 0;
      
      if (TIM2->SR & (1<<0)) {
        TIM2->SR &= ~(1<<0);        /* clear UIR flag */
                              /* write LUT val to DAC */
        DAC->DHR12R1 = dacLUT[idx++];
         idx %= 20;
         LED_Out (idx);           /* Write idx to LEDs */
        }

    Add the following main() function:

    /*---------------------------------------------------
      Main function
     *---------------------------------------------------*/
    int main (void) {
    
      HAL_Init();
      SystemClock_Config();
      
      LED_Initialize ();                    /* LED Init. */
      DAC_Initialize ();                     /* DAC Init */
      TIM2_Initialize ();
    
      while (1) {  
         /* empty statement */  ;
        }
    }
  4. Add dacSinusoid.c to the project.
  5. Only one statement in the TIM2_Initialize ( ) function (in the timer.c file) needs to be changed:
    /***************************************************
     * TIM2_Initialize ( )
     ***************************************************
     * Initializes TIM2 generates interrupts every 1ms
     * SystemCoreClock = 168 MHz - set by SystemInit ( )
     * Refer to Figure 134 of STM Reference Manual RM0090
     * TIMxCLK = SystemCoreClock/2
     * Hence ticks = 0.001 * 168,000,000 / 2 = 84,000
     * Prescaler = 84-1; ARR = 1000-1;
     ***************************************************/
    void TIM2_Initialize (void) {
      const uint16_t PSC_val = 84;
      const uint16_t ARR_val = 1000;
        
                                       /* En. TIM2 clk */
      RCC->APB1ENR |= RCC_APB1ENR_TIM2EN; 
    
      TIM2->PSC = PSC_val - 1;        /* set prescaler */
      TIM2->ARR = ARR_val - 1;      /* set auto-reload */
      TIM2->CR1 = (1UL << 0);      /* set command reg. */
      TIM2->DIER = (1UL << 0);         /* En. TIM2 IRQ */
      NVIC_EnableIRQ(TIM2_IRQn); /* En. NVIC TIM2 Int. */
    }
  6. Build, download, and run the program.

How it works…

The many techniques that could be used to generate a sinusoidal waveform are the subject of the digital signal processing literature. A common approach is a direct method that stores the sinusoidal waveform samples in a look-up-table (LUT). This may seem very crude but if the output is passed through an analog low-pass filter with a cut-off frequency set to the fundamental frequency of the output signal, then the result is a reasonably pure sinusoid. In fact, this approach works equally well for a triangular waveform (which can be generated by the DAC hardware), but the LUT approach will produce something that looks convincing when displayed on an oscilloscope without the need for a filter.

In theory, the minimum number of samples needed is determined by the Nyquist-Shannon Sampling Theorem. This states that we need a minimum of two samples per cycle. At this limit the raw samples describe a 50 Hz square wave that will produce a sinusoid when processed by a suitable low-pass output filter. However, as an ideal square wave contains only components of odd-integer harmonic frequencies (of the form 2π(2k-1)f), the order of the filter will need to be ~12 so that the harmonics are highly attenuated while the fundamental is unaffected. To achieve a satisfactory output with a much simpler second-order filter, the number of samples is usually increased by a factor of ~10.

We store the samples in an array, as follows:

uint32_t dacLUT [] = {2047, 2680, 3250, 3250, 3994,
                        4095, 3994, 3703, 3250, 2680,
						 2047, 1414,  844,  391,  100,
						    0,  100,  391,  844, 1414 };

Then, we use a timer to generate an interrupt every 1 ms (that is, the period of the sinusoid T = 20 ms; 1/20 ms = 50 Hz.). Please note that we could use any timer (in this case, we use TIM2; reusing code discussed previously but changing the prescaler value):

Uint16_t PSC_val = 84;

We write the sample to the DAC's Data Holding Register in the timer ISR (we postincrement idx), as follows:

DAC->DHR12R1 = dacLUT[idx++];

To ensure the index is incremented by modulo 20 (because the LUT array stores 20 values), we use the following:

idx %= 20;      

We output the idx variable to the LEDs just to give a visual check that the program is running. A screenshot of an oscilloscope connected to PortA4 is shown as follows:

How it works…

The lower trace shows the output (Vout) of the low-pass filter. The cut-off frequency for the low-pass filter is set to 50 Hz approximately, (refer to T. Floyd and D. Buchla, Electronics Applications Circuits Devices and Applications (8e), Pearson Education, 2014) which can be seen in the following figure:

How it works…
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.135.187.210