Showing results for 
Search instead for 
Did you mean: 



I have a timer that generates an event out on:

1. DAC to generate sine wave.
2. ADC to read the sine wave and do FFT.

Question 1:
I find that the fundamental frequency of the sine wave is as follows:
Counter value = ((Timer peripheral clock/desired frequency) / block size) * 2
where is this derived from?

Question 2:
I have a hard time to read back the generated sine wave. When doing FFT, I can see that the coefficients are oscillating, instead of flat. While the absolute value of coefficients sis the same, I assume it's phase shifted. Please correct me. 
Upon tuning the ADC 14-bit with a oversampling ratio and sampling time, I've managed to get a stable FFT output (honestly by try and error). Changing the timer counter value requires another tuning. Is there a general idea/formula to get the sampling time right?

volatile int freq_array[number_of_frequencies] = {
float freq_gain[number_of_frequencies] = {

void generate_waveform() {

  for (int i = 0; i < tx_waveform_size; i++) {

    for (int j = 0; j < number_of_frequencies; j++) {
      tx_waveform[i] = tx_waveform[i] + (freq_gain[j] * sin(freq_array[j] * (2 * M_PI) * i / tx_waveform_size));


    tx_waveform[i] = tx_waveform[i] / number_of_frequencies;

    val[i] = (2048 * tx_waveform[i]) + 2048; //Need t0 times by 2^12 as it is 12 bit DAC and then add 2048 so negative becomes positive

    tx_waveform_buffer[i] = val[i]; //assign tx_waveform_buffer as it needs a uint value.
  SCB_CleanDCache(); //clean out all of the d-cache to ensure the waveform is in the ram and not cache

void HAL_ADC_ConvHalfCpltCallback(ADC_HandleTypeDef * adc) {

  //checks to see if DMA1_stream0 is the dma transfer is complete, if so copy data and change status
  if (HAL_NVIC_GetActive(DMA1_Stream0_IRQn) == 1) {
    adc_buf_status[0] = true;



void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef * adc) {

  //checks to see if DMA1_stream0 is the dma transfer is complete, if so copy data and change status
  if (HAL_NVIC_GetActive(DMA1_Stream0_IRQn) == 1) {
    adc_buf_status[1] = true;


int adc_fft_function(void) {

  //First if adc1 or 2 is half full. Will use 1 buffer as there will always be just one results processed at a time
  if (adc_buf_status[0] == true) {
    // do fft


ST Employee

Hello @user4, welcome to ST Community, 

For your first question, the formula for the counter value is derived from the basic timer operation.

The timer counts up until it matches the value in the counter register, at that point it generates an event. The frequency of these events is determined by the timer peripheral clock divided by the value. in the formula, the block size is multiplied to account for the number of samples in each block of the FFT. (the factor of 2 is there because each sample involves both a real and an imaginary component, effectively doubling the number of operations)

As for the ADC sampling time, it's generally recommended to sample at least twice as fast as the highest frequency component of your signal (Nyquist theorem). but it's often necessary to experiment with different settings, as you've done to find the optimal sampling time.

To give better visibility on the answered topics, please click on Accept as Solution on the reply which solved your issue or answered your question.

How does transmitting 4096 real values of sine involve real and imaginary components? Most tutorials don't have times 2, generating PWM or just a general timer work at the exact frequency calculated. What's the difference and why the need for times 2?

For ADC, how do we sample such that the phasor is calibrated at zero phase? (like tapping dac to adc, sinusoidal shouldn't change, FFT I vs Q is expected to be zero phase?