2023-12-07 05:08 AM
I am generating a sweep signal (a sine wave whose frequency increases over time) on STM32F4 Discovery board by using a look-up table and changing the Prescaler value of TIM2. Unfortunately, the transition from one frequency to another is not smooth and continuous because Prescaler can only be an integer value. For example, if I want my sweep to start from 500Hz and reach 800Hz, I expect the output to be a continuous sinusoid which takes all values between this range. In my case, the generated wave seems to take only 500Hz, then it turns into a size with 620Hz, etc.
This is how I do that:
uint8_t Nchrip = 0; // Number of Completed Chirps [-] uint8_t Nrepeat = 5; double Fs = 500.0; //Chirp start frequency double Ff = 800.0; //Chirp final freq double Tc = 2.5; // Chirp Half Period [s] double alpha = 0; // Frequency Sweep Rate [Hz/s] double Cf = 0; // Maximum Allowable Frequency [Hz] double deltat = 0; // Time Period of Wave [s] double t = 0; // Time Variable [s] double Ft = 0; // Wave Instantaneous Frequency Variable [Hz] uint32_t MyClkFreq = 80000000; // Fixed APB1 Clock Frequency [Hz] uint32_t MyPrescaler = 1 - 1; // Minimum Prescaler Value [cycles] uint32_t MyPeriod = 80 - 1; // Fixed Period Value [cycles] #define NS 128 uint32_t Wave_LUT[NS] = {2048, 2149, 2250, 2350, 2450, 2549, 2646, 2742, 2837, 2929, 3020, 3108, 3193, 3275, 3355, 3431, 3504, 3574, 3639, 3701, 3759, 3812, 3861, 3906, 3946, 3982, 4013, 4039, 4060, 4076, 4087, 4094, 4095, 4091, 4082, 4069, 4050, 4026, 3998, 3965, 3927, 3884, 3837, 3786, 3730, 3671, 3607, 3539, 3468, 3394, 3316, 3235, 3151, 3064, 2975, 2883, 2790, 2695, 2598, 2500, 2400, 2300, 2199, 2098, 1997, 1896, 1795, 1695, 1595, 1497, 1400, 1305, 1212, 1120, 1031, 944, 860, 779, 701, 627, 556, 488, 424, 365, 309, 258, 211, 168, 130, 97, 69, 45, 26, 13, 4, 0, 1, 8, 19, 35, 56, 82, 113, 149, 189, 234, 283, 336, 394, 456, 521, 591, 664, 740, 820, 902, 987, 1075, 1166, 1258, 1353, 1449, 1546, 1645, 1745, 1845, 1946, 2047}; alpha = (Ff - Fs) / Tc; Cf = ((double) MyClkFreq / ((double) (MyPeriod + 1) * (double) NS)); HAL_DAC_Start_DMA(&hdac, DAC_CHANNEL_1, (uint32_t*) Wave_LUT (uint32_t) NS, DAC_ALIGN_12B_R); htim2.Init.Prescaler = MyPrescaler; htim2.Init.Period = MyPeriod; HAL_TIM_Base_Init(&htim2); sConfig.DAC_Trigger = DAC_TRIGGER_T2_TRGO; HAL_DAC_ConfigChannel(&hdac, &sConfig, DAC_CHANNEL_1); HAL_TIM_Base_Start(&htim2); if (Nchrip == Nrepeat) { HAL_DAC_Stop_DMA(&hdac, DAC_CHANNEL_1); HAL_TIM_Base_Stop(&htim2); } void MY_TIM_UPDATE() { htim2.Init.Prescaler = MyPrescaler; HAL_TIM_Base_Init(&htim2); } void MY_DAC_UPDATE(void) { MY_TIM_UPDATE(); deltat = (double) (((double) MyPrescaler + 1) / Cf); // Update Time Variable [s] (t > (2.0 * Tc)) ? (t = 0, Nchrip += 1) : (t += deltat); Ft = (double) (Fs + alpha / 2 * t); // Update Frequency Variable [Hz] (Ft > Ff) ? (Ft = Ff) : 1; // Check Frequency Variable (Ft > Cf) ? (Ft = Cf) : 1; // Check Frequency Variable MyPrescaler = (uint32_t)((Cf / Ft) - 1); // Compute Prescaler Value [-] } void HAL_DAC_ConvCpltCallbackCh1(DAC_HandleTypeDef *hdac) { MY_DAC_UPDATE(); }
Could you suggest something to have a gradual transition from one frequency to another to have a similar signal?
2023-12-07 06:00 AM
If you need it to be super smooth, the easiest way to do this is to generate new DAC values as you go. Use the HT and TC interrupts to signal that the first and second half of the buffer needs new values and populate them on the fly. Leave the update time alone.
2024-01-26 06:16 AM
Thanks for the reply! Could you please specify which values should I populate into the DAC buffer so that the frequency would increase linearly? If I leave the update time, letting the clock frequency to be 1MHz for example, how it is possible to change the signal frequency?
2024-01-26 07:01 AM
Say you want a sine wave.
y = sin(t / w)
(where w is frequency)
You can generate this incrementally:
y0 = sin(t0 / w) -->
y1 = sin((t0 + dt) / w)
So inside your sine function, the angle changes by dt / w at each increment.
If you want w to vary linearly, say
w(t) = w0 + dw * t
Then you generate your new values as such
theta0 = 0
y0 = sin(theta0)
theta1 = theta0 + dt * w(0) = theta0 + dt * w0
y1 = sin(theta1)
theta2 = theta1 + dt * w(dt) = theta1 + dt * (w0 + dw * dt)
y2 = sin(theta2)
theta3 = theta2 + dt * w(dt) = theta1 + dt * (w0 + 2 * dw * dt)
y3 = sin(theta3)
Basically increment the angle for each data point, but change how much it increments based on the current frequency at that point.