STM32F4扫频(Chirp)信号频率平滑渐变实现方案问询
问题描述
在STM32F4 Discovery开发板上,通过查找表(Look-up Table)及修改TIM2的Prescaler值生成扫频信号(频率随时间递增的正弦波)。但由于Prescaler仅支持整数值,频率切换无法实现平滑连续过渡:例如从500Hz扫频至800Hz时,信号并非连续覆盖全频段,而是跳变到620Hz等离散频率。
当前实现代码如下:
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(); }
当前生成的信号时域与频域(FFT)结果显示,信号存在明显跳变,无平滑过渡特性,需实现频率平滑渐变的可行方案。
可行解决方案
1. 联合调整预分频器与自动重载寄存器(ARR)
当前代码固定MyPeriod(ARR值)仅调整预分频器,导致频率步长过大。可同时调整两者,缩小频率离散步长:
- 频率计算公式:
Ft = MyClkFreq / ((Prescaler + 1) * (Period + 1) * NS) - 计算目标频率
Ft后,遍历可能的Prescaler值,找到使(Period + 1)最接近MyClkFreq / ((Prescaler + 1) * Ft * NS)的整数组合,大幅降低频率跳变间隔。 - 注意:STM32F4的TIM2是32位定时器,ARR取值范围更大,更适合该方案。
2. 脉冲吞入实现近似小数分频
利用脉冲吞入技术模拟小数分频,实现平均频率的微调:
- 当目标预分频器为小数
P = P_int + P_frac时,每N个周期中,用N*P_frac个周期使用P_int+1作为预分频,其余用P_int,平均预分频值接近目标小数。 - 实现时需维护累加器,每次定时器更新时累加小数部分,溢出时调整预分频器值。
3. 增大LUT采样点数(NS值)
当前LUT仅128个点,增大NS(如256、512)可降低频率步长:
- 频率步长公式:
ΔFt = MyClkFreq / ((Prescaler_min + 1) * (Period + 1) * NS) - MyClkFreq / ((Prescaler_min + 2) * (Period + 1) * NS) - 增大
NS会减小步长,让频率跳变更不明显,同时提升正弦波精度,但需注意RAM占用和DMA效率。
4. 实时计算正弦值替代LUT
放弃LUT,用实时计算正弦值输出到DAC,通过调整采样间隔实现平滑扫频:
- 利用定时器更新中断触发采样,中断中用STM32F4内置的CORDIC硬件加速计算正弦值,输出到DAC。
- 调整定时器周期(ARR)改变采样频率,32位ARR可实现极小步长的频率调整,扫频更平滑。该方案会占用部分CPU资源,但STM32F4完全能应对500-800Hz的扫频需求。
5. DMA循环模式加插值补点
在现有LUT基础上,通过插值补点增加输出采样率,减小频率跳变感知:
- 对原LUT相邻点做线性插值,生成额外点输出,增加每个正弦波的输出点数,从而减小频率调整步长。
- 在
HAL_DAC_ConvCpltCallbackCh1回调中动态生成插值后的缓冲区,再启动下一次DMA传输。
内容的提问来源于stack exchange,提问作者Bro Bro
相关产品推荐
相关产品推荐

