一、平台及配置
- MCU 型号 :STM32F407VG (Cortex‑M4)
- ADC :ADC1 + ADC2,Dual regular simultaneous only 模式
- 触发源 :TIM2 TRGO 外部触发
- ADC 时钟 :PCLK2=96 MHz,ADC prescaler=DIV2 → f<sub>ADC</sub>=48 MHz
- 采样时间 :15 ADC 时钟周期(最小)
- 分辨率 :12 位
- DMA :双通道交替存储 Regular 转换结果
二、理论计算
-
ADC 时钟
fADC=PCLK2prescaler=96 MHz2=48 MHzf_{\rm ADC} = \dfrac{\rm PCLK2}{\rm prescaler} = \dfrac{96\,\text{MHz}}{2} = 48\,\text{MHz}f**ADC****=prescalerPCLK2****=296MHz****=48MHz**
-
转换周期
最短采样时间设为 15 ADC 周期,加上 12.5 周期的转换时间,总共
Tconv=15+12.548 MHz≈0.571 μs T_{\rm conv}
= \frac{15 + 12.5}{48\,\text{MHz}}
\approx 0.571\ \mu\text{s}T**conv****=48MHz15+12.5****≈0.571 μs**
-
理论最高采样率
fsamp=1Tconv≈10.571 μs≈1.745 MSPS(每通道) f{\rm samp}
= \frac{1}{T{\rm conv}}
\approx \frac{1}{0.571\,\mu\text{s}}
\approx 1.745\ \text{MSPS(每通道)}f**samp****=Tconv****1****≈0.571μs1****≈1.745 MSPS(每通道)**
在 Dual regular simultaneous only 模式下,ADC1+ADC2 同步启动,虽然同时采样,但各自的吞吐量仍与单 ADC 相同,即每路 ≈1.745 MSPS。
三、实测现象
备注 :实测最高只能到 ≈96 kHz,而非理论的 ≈1.7 MSPS。可能原因包括:采样时间配置未真正生效(仍在用 480 周期)、HAL/DMA 调用了额外开销、ADC 启动同步仲裁逻辑等。
四、已排查项
- CubeMX/HAL 配置确认:Dual regular simultaneous only、采样时间 15、ADC prescaler DIV2;
- TIM2 已配置 Master Mode = Update → TRGO,每周期触发一次 ADC;
- DMA 流/通道、FIFO、半/全中断配置正确,无死锁或缓存溢出;
- 关掉其他外设、提高 DMA 优先级,均无改善;
- 软件触发模式下,理论速率下限仍远低于 960 kHz。
五、求助方向
-
Dual regular simultaneous only 模式 在硬件层面是否有隐藏的同步或仲裁开销?
-
除了采样时间和 f<sub>ADC</sub>,是否还有其他寄存器设置(如 ADC_CCR、中断/模拟看门狗)影响高速触发响应?
-
TIMx TRGO 在高触发频率下是否存在精度或同步瓶颈?
-
若要接近 1 MSPS 级别,有哪些最佳实践或寄存器必须开启/关闭?
-
有没有前辈实测能在该模式下超 96 kHz 的经验?
-
void HAL_ADC_ConvHalfCpltCallback(ADC_HandleTypeDef *hadc)
{
if(hadc->Instance == ADC1) // 以 ADC1 作为主 ADC(多模式中 ADC1 和 ADC2 同步)
{
// print_log("half\n");
// //打印GetTick
// print_log("GetTick: %d\n", HAL_GetTick());
// 处理 adcBuffer 前半部分的数据:下标 0 到 ADC_BUFFER_SIZE/2 - 1
processADCData(&adcBuffer[0], ADC_BUFFER_SIZE/2);
}
}
void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef *hadc)
{
if(hadc->Instance == ADC1)
{
// print_log("full\n");
// //打印GetTick
// print_log("GetTick: %d\n", HAL_GetTick());
// 处理 adcBuffer 后半部分的数据:下标 ADC_BUFFER_SIZE/2 到 ADC_BUFFER_SIZE - 1
processADCData(&adcBuffer[ADC_BUFFER_SIZE/2], ADC_BUFFER_SIZE/2);
}
}
void processADCData(uint32_t *buffer, uint32_t length)
{
// 例如每 40 个数据为一组(根据你的配置,每个通道40个样本)
uint32_t groupSize = 4;
uint32_t numGroups = length / groupSize;
for(uint32_t i = 0; i < numGroups; i++)
{
uint32_t startIndex = i * groupSize;
// 假设每组中丢弃前1个,处理后3个有效样本
uint32_t validSamples = 3;
int32_t sumDiff = 0;
for(uint32_t j = 1; j < 1 + validSamples; j++)
{
// 对于多模式 ADC,低16位是 ADC1 的数据,高16位是 ADC2 的数据
uint16_t adc1_value = buffer[startIndex + j] & 0xFFFF;
uint16_t adc2_value = (buffer[startIndex + j] >> 16) & 0xFFFF;
int16_t diff = (int16_t)adc1_value - (int16_t)adc2_value;
sumDiff += diff;
// if(j == 39 && i < 3)
// {
// print_log("ADC1: %d, ADC2: %d, Diff: %d\n", adc1_value, adc2_value, diff);
// }
}
// 此处 sumDiff 为该组采集数据的累加结果,可根据需要存储或后续处理
//processChannelResult(i, sumDiff);
//发送给BUF_NUM_S0+i
float sumDiff_f = (float)sumDiff;
if(i < 16)
sendDatatoBuf(BUF_NUM_S0+ i , (uint8_t*)&sumDiff_f, sizeof(sumDiff_f));
// 例如打印结果
// if(i < 3)
// print_log("Group %d: ADC1-ADC2 SumDiff = %d\n", i, sumDiff);
}
}
7.
8.
9.
|