ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

AURIX TOM触发ADC与DMA联动:实现PWM精确同步采样

AURIX TOM触发ADC与DMA联动:实现PWM精确同步采样 1. 从一个真实需求说起为什么PWM和ADC的联动值得单独拿出来讲做嵌入式电机控制或者电源管理的朋友大概率都遇到过这样一个场景MCU需要输出一路PWM去驱动功率级同时又要用ADC实时采集电流、电压或者位置反馈而且采样时刻必须和PWM的相位严格对齐。如果采样点选得不对轻则数据抖动大、控制环路震荡重则直接触发过流保护甚至炸管。我最早接触这类需求是在做无刷电机FOC控制的时候当时用的是STM32的TIM1互补输出加ADC注入通道后来换到英飞凌AURIX平台发现它的TOM模块在这块的设计思路和STM32完全不一样踩了不少坑才把整套机制摸清楚。TOM全称Timer Output Module是英飞凌AURIX系列里专门负责输出比较和PWM生成的定时器模块。它和STM32的通用定时器最大的区别在于TOM的每个通道都是独立配置的没有所谓的“主从”关系而且它和ADC、DMA之间的硬件联动是通过GTMGeneric Timer Module内部的触发矩阵来完成的不需要CPU干预。这意味着你可以做到PWM周期内任意时刻精确触发ADC采样采样完成后DMA自动搬运数据整个过程CPU只需要在最后读一下缓冲区就行。这套机制解决的核心问题是实时性与确定性的平衡。在电机控制里PWM频率通常在10kHz到20kHz也就是说每个周期只有50到100微秒。在这段时间内你既要完成电流采样又要跑完Clarke/Park变换和PID计算还要更新占空比。如果ADC触发和DMA搬运占用太多CPU时间控制环路就没办法在下一个周期到来之前算完。TOMADCDMA的硬件联动方案把采样和搬运的活儿全部交给硬件CPU只负责算这才是能跑高带宽控制环路的前提。这篇文章适合谁看如果你正在用AURIX做电机控制、数字电源、或者任何需要PWM和ADC同步采样的项目而且对GTM模块还不太熟悉那这篇内容应该能帮你省下不少翻手册的时间。如果你用的是STM32或者其他平台里面关于触发时序和DMA配置的思路也是通用的只是寄存器名字不一样而已。2. TOM模块的核心设计思路为什么它和普通定时器不一样2.1 TOM的通道架构与PWM生成逻辑TOM模块内部有多个通道每个通道本质上是一个独立的比较单元。它有一个共同的时基计数器TGC但每个通道有自己的比较寄存器CM0和CM1。PWM的生成逻辑是这样的计数器从0开始往上数当计数值等于CM0时输出翻转等于CM1时再翻转一次这样就形成了一个周期可调、占空比可调的方波。听起来和普通定时器的输出比较模式差不多但TOM的关键差异在于它的同步更新机制。在STM32里你写CCR寄存器的时候新值什么时候生效取决于ARPE位和更新事件。如果没配置好可能会出现占空比跳变或者丢脉冲。TOM的做法是每个通道有一个影子寄存器你在CPU侧写的是影子寄存器实际生效要等到下一个周期边界。这个机制是硬件强制执行的不需要你手动去等更新事件。我实测下来这个设计在电机控制里非常关键因为FOC算法算出来的新占空比必须在下一个PWM周期整周期生效否则电流波形会畸变。另外TOM的每个通道可以独立选择时钟源和分频系数。这意味着你可以在同一个TOM模块里让通道0跑20kHz的电机PWM通道1跑1kHz的蜂鸣器信号互不干扰。这种灵活性在STM32上需要多个定时器才能实现而TOM一个模块就搞定了。2.2 触发ADC的硬件路径解析TOM触发ADC的机制核心在于GTM内部的触发矩阵。每个TOM通道在计数器达到特定值时可以产生一个触发信号这个信号通过触发矩阵路由到ADC模块的触发输入。整个过程不需要CPU参与也不需要中断。具体来说TOM通道可以配置在CM0匹配、CM1匹配、或者周期结束时产生触发。在电机控制里最常用的是在CM0匹配时触发ADC采样。为什么因为CM0通常对应PWM周期的中点这时候开关管已经完成换流电流采样最稳定。如果你在周期边界触发正好赶上开关动作采样到的电流里全是开关噪声。触发信号的路由需要配置GTM的TOUTSEL寄存器把TOM通道的输出信号映射到ADC的触发源。这里有个坑AURIX不同系列的触发矩阵映射关系不一样TC3xx和TC2xx的寄存器地址和位定义都有差异。我第一次移植代码的时候直接抄了TC2xx的配置结果ADC死活不触发查了两天才发现是TOUTSEL的位偏移变了。2.3 为什么选择硬件触发而不是软件触发有人可能会问我用CPU在PWM中断里手动启动ADC不就行了吗为什么要搞这么复杂的硬件触发答案在抖动两个字。CPU响应中断的时间是不确定的取决于当前指令流水线的状态、是否有更高优先级的中断嵌套、甚至Flash等待周期。在20kHz的PWM频率下一个周期50微秒如果中断响应抖动个一两微秒采样点就偏了。对于电流环来说采样点偏移意味着你采到的不是同一时刻的电流Clarke变换出来的Ialpha和Ibeta会有相位误差控制效果直接打折扣。硬件触发的抖动是纳秒级的因为触发信号是纯组合逻辑从TOM计数器匹配到ADC启动转换中间只经过几个门电路的延迟。这个确定性是软件触发永远做不到的。我在做伺服驱动器的时候对比过两种方案软件触发的电流纹波比硬件触发大了将近30%而且转速越高差异越明显。3. ADC与DMA的配合从采样完成到数据就绪的全链路3.1 ADC采样时序与触发点选择ADC收到触发信号后并不是立刻开始采样而是要经过一个采样保持时间。这个时间取决于你配置的采样电容充电周期以及信号源的输出阻抗。在电机电流采样里通常用运放做信号调理输出阻抗比较低采样时间可以设短一点比如200ns到500ns。但如果你的信号源阻抗比较高比如直接分压采母线电压采样时间就要拉长否则采样电容充不满读数会偏低。采样完成后ADC开始逐次逼近转换。AURIX的ADC是逐次逼近型12位精度下转换时间大概在1微秒左右。转换完成后结果写入结果寄存器。这时候如果你开了ADC的中断CPU就会收到中断但更高效的做法是让DMA直接把结果搬走。触发点的选择有个经验公式触发时刻 PWM周期中点 - (采样时间 转换时间)/2。这样可以让采样窗口正好落在电流最稳定的区域。不过实际调试的时候我一般会在这个理论值附近扫一遍用示波器看采样时刻的电流波形找到纹波最小的那个点。不同功率级的开关特性不一样理论值只能作为起点。3.2 DMA传输配置的关键参数DMA的配置看起来简单但有几个参数如果设错了数据就会乱。首先是传输宽度ADC结果是12位的但结果寄存器通常是16位或32位对齐。如果你DMA设成8位传输那读出来的数据就是错的。我一般设成16位和ADC结果寄存器的低16位对齐。其次是源地址和目的地址的增量模式。源地址是ADC结果寄存器固定不变所以源地址增量要关掉。目的地址是RAM里的缓冲区每个通道采完都要往后挪一个位置所以目的地址增量要打开。如果目的地址增量忘了开那所有通道的数据都会覆盖到同一个地址你读到的永远是最后一个通道的值。还有一个容易忽略的参数是传输次数。在电机控制里通常一个PWM周期触发一次ADC采两个或三个通道比如两相电流加母线电压。DMA的传输次数要设成通道数这样每次触发搬完所有通道的数据后DMA会自动重新加载等待下一次触发。这个模式叫循环模式在STM32里对应DMA_CIRCULAR在AURIX里叫continuous mode。3.3 双缓冲机制在实时控制中的应用单缓冲的DMA有个问题如果DMA正在往缓冲区A写数据而CPU正好在读缓冲区A就会读到半新半旧的数据。在电流环里这会导致控制算法用错数据严重的时候会引发震荡。解决办法是双缓冲。DMA配置成在两个缓冲区之间交替写入第一个周期写A第二个周期写B第三个周期又写A。CPU在周期中断里读的时候读的是上一个周期写完的那个缓冲区。这样读写永远不会冲突。AURIX的DMA支持硬件双缓冲不需要CPU干预。STM32的DMA也有双缓冲模式叫double buffer mode。配置的时候要注意两个缓冲区的地址要分别写到DMA的基地址寄存器和备用地址寄存器里然后使能双缓冲模式。切换的时候硬件会自动改基地址你不需要手动去改。我在实际项目里双缓冲是必开的。有一次为了省事没开双缓冲结果电机在低速的时候还好一到高速就出现周期性抖动查了半天才发现是DMA和CPU抢同一个缓冲区导致的。从那以后不管什么项目只要用DMA搬ADC数据双缓冲是标配。4. 完整实操流程从寄存器配置到代码落地4.1 环境准备与工具链选择我用的开发环境是AURIX Development Studio编译器是Tasking。如果你用的是英飞凌的TriCore系列建议直接用官方IDE因为GTM模块的寄存器定义和链接脚本都是现成的省得自己配。调试器用miniWiggler或者DAP miniWiggler都行前者便宜但速度慢后者贵但稳定。代码结构上我习惯把GTM的初始化分成三层最底层是寄存器直接操作中间层是封装好的TOM配置函数最上层是应用层的PWM和ADC初始化。这样分层的好处是换芯片型号的时候只需要改最底层的寄存器定义中间层和应用层基本不用动。4.2 TOM通道配置的详细步骤第一步是使能GTM模块的时钟。AURIX的GTM挂在SPB总线上需要先配置CCUClock Control Unit里的GTM时钟分频。我一般把GTM时钟设成100MHz这样PWM的分辨率可以做到10ns对于20kHz的PWM来说占空比调节精度足够了。第二步是配置TOM的时基。TOM的计数器时钟源可以从GTM的CMUClock Management Unit里选也可以从外部引脚输入。电机控制里一般用内部时钟配置成连续递增模式。时基的周期值写到TGC的周期寄存器里计算公式是周期值 GTM时钟 / PWM频率 - 1。比如100MHz时钟20kHz PWM周期值就是5000-14999。第三步是配置通道的比较值。CM0设成周期值的一半这样触发点就在PWM中点。CM1设成占空比对应的值初始可以设成周期值的10%避免上电就满占空比输出。这里要注意CM0和CM1的值都要写到影子寄存器里等下一个周期边界才会生效。第四步是配置输出引脚。TOM通道的输出要路由到具体的GPIO引脚这需要配置端口控制器的ALT功能。AURIX的引脚复用比较灵活同一个引脚可以映射到多个GTM通道配置的时候要查数据手册里的引脚映射表。4.3 ADC触发与DMA搬运的代码实现ADC的配置分几步先配置ADC模块的时钟和分辨率然后配置通道的采样时间和触发源。触发源要选GTM的触发信号具体是哪个TOM通道取决于你在触发矩阵里的路由配置。DMA的配置相对独立。先使能DMA控制器然后配置通道的源地址、目的地址、传输宽度、传输次数和触发源。触发源要选ADC的转换完成信号。这里有个细节AURIX的DMA触发源是硬件信号不需要CPU写寄存器启动配置好之后DMA就自动等触发。下面是一段简化的配置代码展示TOM触发ADC和DMA搬运的核心逻辑/* TOM通道0配置20kHz PWM中点触发ADC */ #define GTM_CLK 100000000UL #define PWM_FREQ 20000UL #define PWM_PERIOD (GTM_CLK / PWM_FREQ - 1) /* 4999 */ void TOM0_Ch0_Init(void) { /* 配置时基 */ TOM0_TGC0_GLB_CTRL.B.UPEN_CTRL0 1; /* 使能通道0更新 */ TOM0_CH0_CM0.U PWM_PERIOD / 2; /* 中点触发 */ TOM0_CH0_CM1.U PWM_PERIOD / 10; /* 初始占空比10% */ TOM0_CH0_CTRL.B.CLK_SRC 0; /* 内部时钟 */ TOM0_CH0_CTRL.B.SL 1; /* 连续递增模式 */ /* 配置触发输出 */ TOM0_CH0_CTRL.B.TRIGOUT 1; /* CM0匹配时触发 */ /* 路由触发信号到ADC */ GTM_TOUTSEL0.B.SEL0 0x10; /* TOM0_Ch0映射到ADC触发源0 */ } /* DMA配置ADC结果搬运到缓冲区 */ void DMA_ADC_Init(void) { DMA_CH0_SADR.U (uint32)ADC0_RESULT0; /* 源地址ADC结果寄存器 */ DMA_CH0_DADR.U (uint32)adc_buffer; /* 目的地址RAM缓冲区 */ DMA_CH0_CTRL.B.TREL 2; /* 传输2个通道 */ DMA_CH0_CTRL.B.BLKM 1; /* 循环模式 */ DMA_CH0_CTRL.B.DW 1; /* 16位传输 */ DMA_CH0_CTRL.B.SINC 0; /* 源地址不增 */ DMA_CH0_CTRL.B.DINC 1; /* 目的地址递增 */ DMA_CH0_CTRL.B.TRIG 0x20; /* 触发源ADC转换完成 */ }这段代码里最关键的是TRIGOUT和TOUTSEL的配置。TRIGOUT决定什么时候产生触发信号TOUTSEL决定这个信号路由到哪个ADC触发源。这两个如果对不上ADC就不会触发。4.4 参数计算与验证方法PWM频率和ADC采样时刻的计算我一般用Excel拉一个表把GTM时钟、分频系数、周期值、CM0值、采样时间、转换时间都列进去然后算出来触发时刻对应的电流相位。这样在调试的时候心里有个预期值不用盲目扫参数。验证的时候我用示波器同时看三路信号PWM输出、ADC触发信号可以映射到GPIO引脚上、以及电流采样运放的输出。理想情况下ADC触发信号应该落在电流波形的平顶区域也就是开关噪声已经衰减完、电流还没开始下降的那个窗口。如果触发信号落在开关沿上那就需要调整CM0的值把触发点往后挪。5. 常见问题与排查技巧实录5.1 ADC不触发或触发频率不对这是最常见的问题排查思路按优先级排现象可能原因排查方法ADC完全不触发TOUTSEL路由配置错误检查GTM_TOUTSEL寄存器确认TOM通道映射到正确的ADC触发源触发频率是PWM频率的一半CM0和CM1配置反了确认CM0是触发点CM1是占空比点触发频率对但偶尔丢触发触发信号被其他通道覆盖检查是否有多个TOM通道映射到同一个ADC触发源ADC触发但DMA不搬DMA触发源配置错误确认DMA触发源选的是ADC转换完成信号不是ADC触发信号我遇到过一次特别隐蔽的问题ADC触发正常但DMA搬过来的数据全是0。查了半天发现是DMA的源地址写错了写成了ADC的控制寄存器地址而不是结果寄存器地址。结果DMA每次搬的都是控制寄存器的值当然是0。这种问题看代码很难发现因为地址都是宏定义看起来差不多。后来我养成了一个习惯配置完DMA后先用单次传输模式读一次确认数据正确再开循环模式。5.2 采样数据抖动大或噪声明显数据抖动大通常有三个来源采样时刻不对、采样时间太短、或者信号调理电路有问题。采样时刻的问题前面说过了调整CM0的值就能解决。采样时间太短的话采样电容充不满读数会偏低且不稳定。我一般会把采样时间设成信号源时间常数的5倍以上。比如运放输出阻抗100欧姆采样电容10pF时间常数就是1ns采样时间设200ns足够了。但如果信号源阻抗是10k欧姆时间常数就是100ns采样时间至少要500ns。信号调理电路的问题比较隐蔽。有一次我采母线电压发现读数一直偏高查了半天发现是分压电阻的温漂导致的。后来换成低温漂电阻问题就解决了。所以如果软件层面都排查完了还是不对就要怀疑硬件。5.3 DMA传输数据错位或覆盖DMA数据错位最常见的原因是目的地址增量没开或者传输次数设错了。如果目的地址增量没开所有通道的数据都会写到同一个地址你读到的永远是最后一个通道的值。如果传输次数设多了DMA会写到缓冲区外面去可能覆盖其他变量导致程序跑飞。我建议在DMA初始化之后先往缓冲区里填一些已知的值比如0xDEADBEEF然后启动一次传输看看哪些地址被改了改成了什么值。这样能很直观地看出DMA的搬运范围对不对。还有一个坑是缓冲区对齐。AURIX的DMA对地址对齐有要求16位传输要求目的地址是2字节对齐32位传输要求4字节对齐。如果缓冲区定义的时候没加对齐属性DMA可能会搬错地址。我一般用__attribute__((aligned(4)))来强制对齐。5.4 PWM输出异常与保护机制PWM输出异常比如占空比不对、频率不对、或者干脆没输出排查顺序是这样的先看时钟有没有使能再看时基配置对不对然后看通道输出有没有路由到引脚最后看有没有触发保护机制。AURIX的TOM模块有故障保护功能当检测到外部故障信号时可以自动把PWM输出拉到安全状态。这个功能在电机控制里很有用但如果配置不当可能会误触发。我有一次调试的时候PWM输出一直断断续续查了半天发现是故障保护引脚悬空了导致输入电平不确定随机触发保护。后来把故障保护引脚上拉到高电平问题就解决了。另外PWM接MOS管发热的问题通常不是PWM本身的问题而是驱动电路的问题。如果MOS管工作在放大区而不是开关区功耗会非常大。这时候要检查栅极驱动电压是否足够开关速度是否太快导致米勒效应引起误导通。我一般会在栅极串一个小电阻比如10欧姆来限制开关速度减少EMI和发热。6. 调试心得与进阶优化6.1 用GPIO翻转法测量中断和触发延迟在调试触发时序的时候我经常用GPIO翻转法来测量延迟。具体做法是在ADC触发信号产生的时候用一个空闲的GPIO输出高电平在DMA传输完成中断里把GPIO拉低。然后用示波器测量这个GPIO的高电平时间就是从触发到数据就绪的总延迟。这个方法看起来土但非常有效。我测过AURIX TC377的延迟从TOM触发到DMA完成大概在2微秒左右。其中ADC采样加转换占1.5微秒DMA搬运占0.5微秒。知道这个延迟之后你就能算出控制算法必须在多少微秒内算完才能赶上下一个PWM周期。6.2 双缓冲与缓存一致性的处理AURIX的TriCore架构有数据缓存DMA搬运的数据可能还在缓存里CPU读的时候读到的是旧数据。这个问题在STM32上不存在因为Cortex-M没有数据缓存。但在AURIX上如果你开了数据缓存就必须处理缓存一致性问题。解决办法有两种一是把DMA缓冲区定义在非缓存区域比如LMU或者DSPR里二是在读缓冲区之前手动无效化对应的缓存行。我一般用第一种方法因为简单可靠。在链接脚本里把DMA缓冲区分配到非缓存段CPU读的时候直接读RAM不经过缓存。6.3 从单通道到多通道的扩展思路单通道的PWM加ADC加DMA跑通之后扩展到多通道其实不难。TOM模块有多个通道每个通道可以独立配置触发点。ADC有多组结果寄存器DMA有多个通道。你只需要把每个TOM通道的触发信号路由到对应的ADC触发源然后为每个ADC触发源配置一个DMA通道就行。但要注意多个DMA通道同时搬运的时候会争抢总线带宽。如果PWM频率很高比如100kHz以上DMA搬运的数据量会很大可能影响CPU访问Flash和RAM的速度。这时候可以考虑降低ADC分辨率比如从12位降到10位减少数据量。或者把多个通道的结果打包成一个32位字一次DMA搬完减少传输次数。我在做六相电机控制的时候同时跑了6路PWM和6路ADCDMA用了3个通道每个通道搬2路数据。实测下来CPU负载在20%左右还有足够的余量跑控制算法。关键是要把DMA的优先级设对ADC的DMA通道优先级要高于串口和SPI的DMA通道否则ADC数据可能会被延迟搬运。6.4 实测性能数据与优化建议我在TC377上跑过一组实测数据PWM频率20kHzADC采样2路电流加1路母线电压12位精度DMA循环模式双缓冲。从TOM触发到DMA完成中断总延迟2.3微秒。CPU在中断里读双缓冲数据跑完Clarke/Park和PID耗时8微秒。整个控制环路在一个PWM周期内完成CPU占用率大概35%。如果要进一步优化可以从几个方向入手一是把ADC分辨率降到10位转换时间能缩短30%左右二是把DMA传输宽度从16位改成32位一次搬两个通道的数据减少传输次数三是把控制算法里的浮点运算改成定点运算TriCore的定点性能比浮点强不少。不过优化之前要先确认瓶颈在哪里。如果瓶颈在ADC转换时间那降分辨率有用如果瓶颈在算法计算那就要优化算法。我一般先用GPIO翻转法测出各段耗时然后再针对性地优化避免盲目改代码引入新问题。最后分享一个小技巧在调试触发时序的时候可以把ADC触发信号和PWM输出同时映射到两个相邻的GPIO引脚上用示波器的双通道同时看。这样触发点和PWM波形的相对关系一目了然比单独看触发信号要直观得多。我靠这个方法调过很多次采样点比盲扫参数快多了。
返回列表