ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

AT32F403A实现双ADC纳秒级同步采样:TMR时钟输出与DMA触发方案详解

AT32F403A实现双ADC纳秒级同步采样:TMR时钟输出与DMA触发方案详解 1. 项目概述与核心需求解析最近在做一个多通道高速数据采集的项目核心需求是要同时采集两路模拟信号并且要求两路采样在时间上严格同步误差要控制在纳秒级别。这个需求在很多精密测量场合都很常见比如振动分析、声学阵列或者电力系统的多相电流电压监测。我手头的硬件平台是基于雅特力AT32F403A这款MCU传感器用的是两片AD9220——这是一款经典的12位、20MSPS的模数转换器。项目最大的挑战就在于如何让MCU精准地控制两片独立的ADC让它们在同一时刻启动采样并且把采集到的数据高效、无误地搬运到内存里后续才能做相关的算法处理。乍一看这个需求好像挺简单不就是用MCU的定时器产生一个采样时钟ADC_CLK给ADC然后等转换完成去读数据嘛。但仔细一想问题就来了。如果你用两片ADC但只用一个定时器通道输出一个时钟信号然后通过物理连线分给两片ADC理论上似乎能实现“同步”。但在高速场景下比如我这里的10MHz采样率走线长度、负载电容的微小差异都会导致时钟边沿到达两个ADC芯片的时间有偏差即时钟歪斜Skew这个偏差可能就有几纳秒直接导致两路采样时刻不同步数据也就失去了可比性。所以硬件上的“一驱二”并不是真正的同步。那么真正的同步方案是什么我的设计思路是利用MCU高级定时器TMR的两个独立通道分别输出两路完全同频、同相、且相位可精确控制的时钟信号各自驱动一片AD9220的时钟引脚。这样从源头就保证了驱动时钟的同步性。同时为了不让高速数据流堵塞CPU必须引入DMA直接存储器访问来搬运数据。这里又有一个关键点DMA的触发源。理想情况下我们希望ADC转换完成EOC信号能直接触发DMA请求把数据搬走。但AD9220是并行输出没有专用的数据就绪信号。因此我们需要巧妙地利用定时器更新事件Update Event作为DMA请求的触发源让定时器在输出时钟的“节奏”下同步地指挥DMA去搬运ADC数据线上的结果。这个方案的核心就是深入挖掘AT32定时器和DMA模块的联动功能。它不仅仅是配置几个寄存器那么简单涉及到对定时器时钟输出模式、DMA请求映射、触发时序的深刻理解。下面我就把这个从原理到寄存器配置再到代码实现和调试踩坑的全过程毫无保留地分享出来。2. 硬件架构与核心模块选型分析在动手写代码之前我们必须把硬件连接和模块职责理清楚。一个清晰的架构是成功的一半。2.1 主控与ADC选型考量我选择AT32F403A主要是看中了它的高主频最高240MHz和丰富的高级定时器资源。AD9220是12位并行输出的ADC其采样率最高20MSPS对于我的应用信号带宽小于5MHz来说绰绰有余。它需要外部提供一个时钟CLK来驱动其采样和转换过程。转换完成后数据会出现在12位数据总线上并持续到下一个转换周期开始。它没有输出使能或数据有效信号这意味着读取数据的时机必须由主控方MCU严格掌控。两片AD9220的硬件连接是对称的ADC_CLK引脚分别连接到MCU的两个GPIO这两个GPIO将被配置为对应高级定时器TMR1的通道1和通道2的输出功能。12位数据总线分别连接到MCU的两个GPIO端口例如PA0-PA11和PB0-PB11配置为浮空输入模式。电源与参考电压这部分是常规电路确保模拟部分干净稳定这里不赘述。关键点在于时钟线的连接。我们必须使用定时器的输出比较OC模式来产生时钟而不是简单的PWM。因为OC模式可以产生精确的、占空比可调的方波并且其输出极性、相位都可以通过寄存器灵活控制。2.2 定时器TMR的角色与模式选择AT32的高级定时器如TMR1, TMR8功能非常强大。在这个项目中它需要扮演两个角色精密时钟发生器产生两路同步的采样时钟。DMA请求发生器在每次定时器更新时自动触发一次DMA传输。为了实现第一点我们使用定时器的输出比较模式1OC1M110也就是PWM模式1。但注意我们并不需要它产生一个“脉冲”去驱动什么而是让它持续输出一个方波。通过设置捕获/比较寄存器CCRx的值我们可以精确控制输出波形的占空比。对于ADC时钟通常需要50%占空比的方波那么CCRx的值应设置为自动重载寄存器ARR值的一半。为了实现第二点我们需要启用定时器的更新DMA请求。在AT32中定时器可以配置为在更新事件计数器溢出/下溢发生时向DMA控制器发送一个请求信号。这个“更新事件”的发生频率就是我们输出时钟的频率。这样DMA的触发就和ADC的采样时钟在根源上同步了。为什么不用ADC的EOC中断来触发DMA因为AD9220没有这个信号。为什么不用定时器的捕获/比较事件触发DMA因为那样会过于频繁每个时钟边沿都触发而我们只需要在ADC转换完成后的某个确定时刻例如时钟上升沿后数据稳定时去读取一次数据。定时器的更新事件发生在计数器重载时通常对应输出时钟的某个边沿在时序上是一个完美的触发点。2.3 DMA的配置策略DMA在这里是数据搬运工。我们需要为每一片ADC配置一个独立的DMA通道或请求。DMA的配置有几个核心参数源地址Peripheral Address这里不是某个外设的数据寄存器而是GPIO端口的输入数据寄存器IDR。因为ADC的数据是直接挂在GPIO引脚上的。目标地址Memory Address我们开辟在内存中的缓冲区数组比如uint16_t adc1_buffer[1024]。数据宽度源和目标都设置为半字16位因为AD9220是12位数据我们用16位变量来存储高位补零。传输模式必须选择外设到存储器模式。循环模式必须开启。这样当DMA传输完预设的数据量比如1024个点后会自动重置传输计数器并等待下一次触发实现连续采集。触发源选择对应定时器TMR1的更新事件作为硬件触发。这是最关键的一步需要在DMA请求映射表中查找TMR1_UP对应哪个DMA流Stream和通道Channel。这种设计的好处是整个数据流由硬件自动完成定时器产生同步时钟驱动ADC转换 - 定时器更新事件触发DMA - DMA从GPIO口读取稳定数据存入内存。CPU在整个高速采集过程中完全被解放只需要在缓冲区满或半满时产生一个中断去处理已经到手的一批数据即可。3. 软件实现从寄存器配置到代码实操理论清晰了我们来看具体怎么实现。我会以AT32标准外设库为例进行说明但原理同样适用于HAL库或直接寄存器操作。3.1 时钟树与定时器基础配置首先要确保系统时钟和定时器时钟正确。AT32F403A的APB2总线时钟给高级定时器TMR1提供时钟源。假设系统时钟为240MHzAPB2预分频器为2则APB2时钟为120MHz。如果APB2预分频器不为1则定时器时钟会在APB2时钟基础上再倍频一次通常x2所以TMR1的实际时钟可能是240MHz。这一点一定要在RCC配置中确认清楚因为它直接决定了你能产生的最高时钟频率。假设我们需要产生F_CLK 10MHz的ADC采样时钟。定时器计数频率F_TIM 240MHz。 那么定时器的计数周期自动重载值ARR应为ARR F_TIM / F_CLK - 1 240 / 10 - 1 23。 为了得到50%占空比通道的捕获/比较寄存器值CCR应为CCR (ARR 1) / 2 12。注意这里ARR23意味着计数器从0计数到23总共24个计数周期对应240MHz下的一个10MHz时钟周期。这是一个非常关键的计算ARR值必须是整数否则无法产生精确的频率。如果计算出的ARR不是整数就需要微调定时器输入时钟或目标频率。3.2 定时器TMR1输出模式与DMA请求配置接下来是TMR1的具体配置步骤使能时钟使能TMR1和对应GPIO口的时钟。GPIO配置将用于输出TMR1_CH1和TMR1_CH2的引脚如PA8, PA9配置为复用推挽输出模式高速率。时基单元配置设置预分频器PSC 0不分频。设置自动重载寄存器ARR 23根据上述计算。计数模式为向上计数。时钟分频因子为1。输出比较通道配置以通道1为例模式选择PWM模式1TMR_OCMODE_PWM1。输出极性高电平有效TMR_OCPOLARITY_HIGH。这意味着当计数器值小于CCR时输出高电平。设置捕获/比较寄存器CCR1 12。使能通道的输出状态TMR_CCxCmd(TMR1, TMR_CHANNEL_1, ENABLE)。启用DMA请求我们需要的是更新事件的DMA请求。调用TMR_DMACmd(TMR1, TMR_DMA_UPD, ENABLE)。主输出使能对于高级定时器需要使能主输出MOE否则通道输出可能无效。TMR_CtrlPWMOutputs(TMR1, ENABLE)。启动定时器TMR_Cmd(TMR1, ENABLE)。关键技巧为了确保两个通道输出的时钟完全同步即第一个脉冲同时开始在配置完所有通道参数但尚未启动定时器之前可以生成一次定时器更新事件TMR_GenerateEvent(TMR1, TMR_EVENT_SOURCE_UPDATE)。这会将计数器清零并将所有预装载寄存器包括CCRx的值一次性更新到影子寄存器从而实现多个通道的同步启动。3.3 DMA通道配置与GPIO数据读取这是最具技巧性的一步。DMA要从GPIO的IDR寄存器读数。确定DMA请求映射查阅AT32F403A的数据手册DMA请求映射表。假设TMR1_UP事件映射到DMA1的Stream5通道6。配置DMA流外设地址(uint32_t)GPIOA-IDR对于连接到PA口的ADC1。存储器地址(uint32_t)adc1_buffer。方向外设到存储器。外设和存储器的数据宽度都设为半字16位。外设地址不递增存储器地址递增。循环模式使能。外设突发和存储器突发单次传输即可。最重要的触发源选择。设置触发源为TMR1更新事件。在标准库中这通常通过DMA_InitStructure.DMA_Channel 6;来指定通道而流选择为Stream5。同时要将传输模式设置为外设流控制器模式Peripheral Flow Controller并启用硬件触发。具体函数可能是DMA_ITConfig(DMA1_Stream5, DMA_IT_TC, ENABLE)并使能流。使能DMA流DMA_Cmd(DMA1_Stream5, ENABLE)。重要提示GPIO的IDR寄存器是只读的且读取操作就是加载当前引脚电平。DMA以硬件方式发起对该寄存器的读操作其时机完全由TMR1的更新事件触发。因此必须精确计算和调整这个触发时刻与ADC数据稳定的时刻之间的关系。通常我们设置定时器在计数器溢出更新事件时ADC的数据已经稳定了一段时间。这可能需要结合ADC的采样保持时间和转换时间通过调整定时器相位或DMA延迟来微调。3.4 主程序逻辑与缓冲区管理配置完成后整个采集链路就自动运行了。主程序或中断服务程序只需要关注数据缓冲区。// 定义缓冲区 #define BUFFER_SIZE 1024 __align(4) uint16_t adc1_buffer[BUFFER_SIZE]; __align(4) uint16_t adc2_buffer[BUFFER_SIZE]; // 在DMA传输完成中断或半传输完成中断中处理数据 void DMA1_Stream5_IRQHandler(void) { if(DMA_GetITStatus(DMA1_Stream5, DMA_IT_TCIF5)) { DMA_ClearITPendingBit(DMA1_Stream5, DMA_IT_TCIF5); // 缓冲区adc1_buffer已满处理这一批1024个数据 process_data(adc1_buffer, BUFFER_SIZE); // 注意由于是循环模式DMA会自动从头开始继续填充无需手动重设地址 } }主函数main()的流程就非常清晰了系统时钟、GPIO、DMA、TMR初始化。启动TMR1输出同步时钟。可选启动DMA传输。进入while(1)低功耗循环或执行其他任务。高速采集完全由硬件在后台完成。4. 同步性验证与关键调试技巧方案搭好了怎么证明两路采样是“同步”的呢这需要软硬件结合验证。4.1 硬件验证方法最直观的方法是用双通道示波器。时钟同步性验证将两个探头分别接到驱动两片AD9220的时钟引脚PA8和PA9。观察两个方波的频率、相位和上升沿时间。理想情况下两个波形应完全重合。你需要测量两个上升沿之间的时间差这个差值就是时钟同步误差。在10MHz下这个误差应远小于一个时钟周期100ns最好能达到1-2ns以内这取决于PCB布局和MCU驱动能力。数据采集时刻验证这是一个进阶验证。可以编写一个测试程序让MCU在DMA搬运数据的同时也在同一个DMA触发事件中断里快速翻转一个测试引脚比如置高再置低。用示波器同时观察这个测试引脚和ADC的时钟信号。你会发现测试引脚上出现一个非常窄的脉冲这个脉冲的位置就代表了DMA实际读取数据的时刻。调整定时器更新事件的相位通过调整计数器初始值或使用定时器的重复计数器可以微调这个读取时刻确保它落在ADC数据稳定的时间窗口内。4.2 软件验证与数据分析硬件验证了时钟同步软件上还要验证采集到的数据是否同步。注入测试信号给两片ADC输入一个完全相同的、已知的模拟信号例如同一个信号发生器输出的正弦波。采集与对比连续采集多组数据。计算互相关函数对采集到的两路数字序列adc1_data[n]和adc2_data[n]计算互相关。如果完全同步互相关函数的峰值应该出现在偏移量lag0的位置。如果峰值出现在lagkk不为0说明两路数据在时间上差了k个采样间隔这就需要回溯检查是时钟相位问题还是DMA触发延迟问题。检查缓冲区索引确保两路DMA的传输完成中断是独立且正确触发的。有时因为中断优先级或处理不当可能导致一路数据被覆盖而另一路正常。可以在中断里记录一个序列号和数据一起存储后期分析时检查序列号是否连续、两路是否匹配。4.3 常见问题与排查实录在实际调试中我遇到了几个典型问题这里分享出来供大家避坑问题一两路时钟输出不同步有固定相位差。现象示波器显示两个时钟频率相同但一个总是比另一个领先或落后几个纳秒到几十纳秒。原因GPIO负载不同或PCB走线长度差异。虽然软件配置是同步的但信号到达芯片引脚的时间有差异。解决优化PCB尽量让两路时钟走线等长并且远离干扰源。软件补偿AT32的高级定时器支持“刹车和死区”功能虽然主要用于电机驱动但其通道输出延迟补偿功能可以微调某个通道的输出相位。可以尝试通过配置TMRx_BDT寄存器中的死区时间或输出控制位对其中一路进行纳秒级的延迟补偿。这是一个高阶技巧需要仔细查阅参考手册。问题二DMA采集到的数据全是0或固定值。现象缓冲区里没有变化的数据。排查步骤检查时钟先用示波器看ADC的CLK引脚是否有正确的方波。如果没有回到TMR配置检查。检查GPIO模式确认连接ADC数据总线的GPIO口配置为浮空输入模式而不是输出或其他模式。检查DMA触发这是最可能的原因。确认DMA的硬件触发是否真正使能并且触发源选择正确。可以在DMA传输完成中断里加一个翻转测试引脚的操作用示波器看这个中断是否被触发。如果不触发检查DMA请求映射、流和通道的使能顺序通常要先配置DMA最后使能定时器的DMA请求和定时器本身。检查地址对齐确保DMA缓冲区地址是4字节对齐的使用__align(4)特别是当数据宽度为半字或字时不对齐可能导致访问错误。问题三数据错位或跳动。现象数据大部分正常但偶尔会出现一两个明显错误的值或者两路数据对应的索引对不上。原因DMA传输被意外打断或者缓冲区管理出现竞争条件。解决关闭中断在CPU处理缓冲区数据比如复制到另一个地方进行计算时如果此时DMA传输完成中断发生并重置了缓冲区索引就会导致数据混乱。一个简单的办法是在处理缓冲区前短暂关闭对应的DMA中断或流处理完再打开。更优雅的方法是使用双缓冲区Ping-Pong Buffer。检查中断优先级确保DMA中断的优先级足够高不会被其他长时间的中断阻塞。如果DMA中断得不到及时响应而DMA在循环模式下已经覆盖了旧数据就会丢失数据。使用半传输中断除了传输完成中断TC还可以使能半传输中断HT。这样当DMA填满一半缓冲区512点时就会产生中断你就有双倍的时间来处理数据降低了数据被覆盖的风险。问题四采样率达不到理论值。现象设定10MHz采样率实际测量只有5MHz或更低。原因定时器时钟算错最可能的原因。回顾第3.1节确认APB2预分频和定时器倍频关系。用示波器测量实际输出的时钟频率来反推。DMA或总线瓶颈在极高采样率下接近20MSPS每个采样点都触发一次DMADMA控制器和总线带宽可能成为瓶颈。虽然AT32的DMA能力很强但仍需注意。确保系统时钟足够高并且没有其他高优先级的总线主设备如另一个DMA或CPU密集访问Flash持续占用总线。代码中有耗时操作如果在DMA传输完成中断中执行了复杂的处理如浮点运算导致中断处理时间过长可能会间接影响系统性能虽然不直接影响DMA触发但可能导致整体系统不稳定。这个基于AT32 TMR输出同步时钟DMA请求的方案经过实际项目验证能够稳定实现两片AD9220的纳秒级同步采样。它的优势在于完全由硬件保证时序精度软件开销极低为后续的实时信号处理算法留出了充足的CPU资源。整个设计过程从时钟树计算、寄存器配置到同步性调试每一步都需要对硬件原理有清晰的认识。希望这篇详细的总结能帮助你在遇到类似的多通道同步采集需求时少走一些弯路。
返回列表