ARTICLE DETAIL

资讯详情

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

S32K144 ADC+PDB+DMA 硬件触发采样全链路配置指南

S32K144 ADC+PDB+DMA 硬件触发采样全链路配置指南 1. 项目缘起与整体设计思路S32K144 这颗料在汽车电子和工业控制圈子里出镜率极高Cortex-M4F 内核跑 80MHz带 FPU外设资源对中低端控制场景来说相当够用。但很多人第一次用它做模拟量采集时会卡在一个地方ADC 单次转换读出来没问题一旦要连续采、高速采、还要不占 CPU就不知道怎么把 PDB、ADC、DMA 这三者串起来。我当初接手一个电机电流环采样项目时也踩过这个坑后来把整条链路从寄存器层面理清楚了才发现这套组合其实设计得挺优雅只是参考手册里分散在三个章节初学者不容易拼出全貌。这篇内容要解决的核心问题就一个让 ADC 在 PDB 的硬件触发下按固定节奏自动采样采到的数据由 DMA 自动搬进内存缓冲区全程 CPU 不参与单次搬运。适合谁看如果你已经能用 S32K144 点灯、配过 GPIO 和时钟但对 ADC 触发机制和 DMA 请求映射还比较模糊那这篇就是写给你的。如果你是从 STM32 转过来的习惯了 CubeMX 一键生成那更要看因为 S32K144 的 S32 SDK 虽然也能生成代码但触发链路的配置逻辑和 STM32 差别不小不理解底层很容易配错。先说整体思路。S32K144 的 ADC 支持多种触发源软件触发、PDB 触发、TRGMUX 交叉触发等。要做周期性精确采样PDB 是最自然的选择因为它本身就是一个可编程延时模块能产生精确的触发脉冲序列。ADC 每收到一个 PDB 触发就转换一次转换完成后产生 COCOConversion Complete标志这个标志可以映射成 DMA 请求。DMA 收到请求就把 ADC 结果寄存器里的值搬到用户定义的数组里搬够一定数量再产生中断通知 CPU 处理。整条链路是纯硬件的CPU 只在缓冲区满的时候被叫醒一次。为什么不用中断方式逐次读取算笔账就清楚了。假设采样率 10kHz每次 ADC 转换完成进一次中断中断服务程序里读结果、存数组、清标志保守估计 2 微秒。10kHz 就是每秒 10000 次中断光中断开销就吃掉 2% 的 CPU 时间这还没算中断响应延迟带来的抖动。如果采样率提到 50kHz 甚至 100kHz中断方式基本不可行。DMA 方式下搬运一个数据只占用总线一个周期CPU 完全无感这是质的区别。再说不理解寄存器直接上 SDK 的问题。S32 SDK 的 ADC 驱动封装得比较厚ADC_DRV_ConfigConverter之类的函数背后做了很多事但触发源选择、PDB 通道关联、DMA 请求使能这几个关键点如果配置结构体里填错了编译能过运行就是不出数据。我见过有人调了两天以为是硬件问题最后发现是 PDB 的预分频算错了触发频率差了 100 倍。所以这篇会从寄存器角度把每个环节讲透SDK 配置作为对照这样你既能理解原理也能快速落地。2. 核心外设原理与触发链路拆解2.1 PDB 到底在做什么可编程延时脉冲发生器PDB 全称 Programmable Delay Block直译就是可编程延时模块。很多人被这个名字误导以为它只是个延时器其实它的核心价值在于产生精确的、可重复的触发脉冲序列。你可以把它想象成一个节拍器设定好周期它就按这个周期不停地往外发脉冲每个脉冲告诉 ADC“该采样了”。PDB 内部有几个关键寄存器需要搞清楚。PDB_MOD是模数寄存器决定计数器的溢出值也就是触发周期的基准。PDB_CHnC1是通道配置寄存器里面的PDB_CHnC1_EN[EN]位使能通道PDB_CHnC1_TOS[TOS]位选择触发输出目标S32K144 里通常选 ADC。PDB_CHnDLY1是延时寄存器决定从计数器归零到发出触发脉冲之间隔多少个时钟。PDB_SC是状态控制寄存器里面的PDB_SC_PRESCALER决定预分频PDB_SC_MULT决定乘法因子PDB_SC_CONT决定是否连续模式。触发频率的计算公式是触发频率 PDB时钟源频率 / (预分频系数 × 乘法因子 × (MOD 1))这里有个容易踩的坑PDB_SC_MULT不是简单的倍数它对应的是 1、10、20、40 四档具体编码要查手册。我当初就是把这个 MULT 当成线性倍数结果算出来的频率和实际差了一个数量级。另外MOD是 16 位寄存器最大值 65535如果需要的周期超过这个范围就得靠预分频来降频。PDB 的时钟源通常来自系统时钟经过分频后的总线时钟。以 80MHz 总线时钟为例如果预分频设为 8MULT 设为 1MOD 设为 999那触发频率就是 80MHz / (8 × 1 × 1000) 10kHz。这个 10kHz 就是 ADC 的采样率每个触发对应一次转换。2.2 ADC 的硬件触发通道与转换时序S32K144 的 ADC 有两个ADC0 和 ADC1每个都有多个通道。硬件触发不是随便哪个触发源都能连到任意通道的ADC 内部有一个触发源选择字段ADC_SC2_ADTRG置 1 表示硬件触发置 0 表示软件触发。但具体是哪个硬件源要看SIM_ADCOPT寄存器里的ADC0TRGSEL或ADC1TRGSEL字段。这个字段决定了 ADC0 的触发源是来自 PDB0、PDB1 还是 TRGMUX 等。这里有个关键点PDB 的通道和 ADC 的通道是两回事。PDB 通道指的是 PDB 内部的触发通道比如 PDB0_CH0 可以触发 ADC0 的某个转换。ADC 通道指的是模拟输入引脚比如 ADC0_SE4 对应某个具体引脚。配置时要确保 PDB 的触发输出连到了正确的 ADC而 ADC 的转换通道选的是你要采的那个引脚。转换时序方面ADC 有一个ADC_CFG1_ADLSMP位决定采样时间长短ADC_CFG1_ADIV决定时钟分频ADC_CFG1_MODE决定分辨率8/10/12/16 位。采样时间不够会导致采样保持电容充不满读数偏低且不稳定。一般来说源阻抗越高需要的采样时间越长。我实测下来对于大多数传感器输出12 位模式下采样时间设成中等偏长比较稳妥具体值要结合 ADC 时钟频率算。转换完成后结果存在ADC_Rn寄存器里同时ADC_SC1_COCO标志置位。这个 COCO 标志就是 DMA 请求的源头。2.3 DMA 请求映射谁触发谁搬运S32K144 的 DMA 控制器是 eDMA每个通道有独立的源地址、目的地址、传输计数和配置。关键问题是ADC 的 COCO 标志怎么变成 DMA 请求这中间靠的是 DMAMUX 模块。DMAMUX 是一个请求路由器它把各个外设的 DMA 请求线映射到 eDMA 的通道上。每个 DMAMUX 通道有一个CHCFG寄存器里面的SOURCE字段选择请求源。S32K144 的 DMAMUX 请求源编号在参考手册里有完整列表ADC0 的 COCO 请求对应某个固定编号ADC1 对应另一个。配置 DMA 时先选一个空闲的 eDMA 通道然后在对应的 DMAMUX 通道里把 SOURCE 设成 ADC 的请求编号。这样 ADC 每次转换完成eDMA 就自动触发一次搬运。搬运的源地址是ADC_Rn寄存器的地址目的地址是用户数组的地址。源地址在搬运过程中不变因为每次都读同一个结果寄存器目的地址每次递增。eDMA 的 TCDTransfer Control Descriptor里要配置SADDR、DADDR、SOFF、DOFF、NBYTES、CITER、BITER等字段。SOFF设为 0 表示源地址不偏移DOFF设为 2 表示目的地址每次加 2 字节因为 ADC 结果是 16 位。NBYTES设为 2表示每次搬 2 字节。CITER和BITER设为缓冲区大小比如 100表示搬 100 次后触发完成中断。这里有个细节eDMA 的 minor loop 和 major loop 概念。每次 ADC 触发搬运一个数据叫一个 minor loop搬完 CITER 个数据叫一个 major loop。major loop 完成后可以产生中断也可以自动重新加载 CITER 继续搬这就是循环缓冲模式。做连续采集时通常用循环模式缓冲区满了产生中断CPU 处理完继续采数据不丢。3. 寄存器级配置实操全流程3.1 时钟与引脚准备先把地基打好在动 ADC 和 DMA 之前时钟必须配对。S32K144 的 ADC 时钟来自SIM_ADCFG寄存器里的分频配置DMA 时钟来自系统时钟。PDB 时钟来自总线时钟。如果用的是 S32 SDKCLOCK_DRV_Init会把这些都配好但如果你要手动调就得逐个确认。引脚方面假设我们用 ADC0_SE4对应某个具体引脚需要把该引脚复用功能设成模拟输入。S32K144 的 PORT 模块里PORTn_PCR[m]寄存器的MUX字段设为 0 就是模拟功能。同时要确保该引脚的输入缓冲关闭避免数字电路干扰模拟信号。注意模拟输入引脚不要配上下拉除非你的传感器输出是开漏的。浮空输入配合外部传感器输出是最常见的做法。3.2 ADC 寄存器配置触发源、分辨率、采样时间ADC 的配置寄存器主要有ADC_SC1、ADC_CFG1、ADC_CFG2、ADC_SC2、ADC_SC3。下面按顺序说。ADC_CFG1里ADICLK选时钟源MODE选分辨率ADIV选分频ADLSMP选采样时间。假设 ADC 输入时钟 40MHz要 12 位分辨率分频选 4 得到 10MHz ADC 时钟采样时间选长采样。ADC_CFG2里SMPLTS字段进一步细化采样周期数。ADC_SC2里ADTRG置 1 使能硬件触发DMAEN置 1 使能 DMA 请求。这两个位是关键少配一个都不行。ADC_SC3里可以配连续转换模式但硬件触发场景下通常用单次转换每次触发转一次。ADC_SC1里ADCH选通道号AIEN决定是否使能转换完成中断。用 DMA 时通常关掉 ADC 中断因为 DMA 完成中断已经够了两个中断都开反而增加复杂度。配置顺序建议先关 ADCADC_SC1_ADCH设为 31 表示禁用配CFG1、CFG2、SC2、SC3最后配SC1启动。这个顺序能避免配置过程中产生意外转换。3.3 PDB 寄存器配置算准触发周期PDB 配置的核心是算频率。假设我们要 10kHz 触发PDB 时钟 40MHz预分频选 8MULT 选 1那 MOD 40MHz / (8 × 1 × 10kHz) - 1 499。写入PDB_MOD就是 499。PDB_SC里PRESCALER写预分频编码MULT写乘法因子编码CONT置 1 使能连续模式PDBEN置 1 使能 PDB。注意PDBEN要最后置位否则配置过程中可能产生触发。PDB_CH0C1里EN置 1 使能通道TOS选触发目标。S32K144 里 PDB0_CH0 通常连到 ADC0具体编码查手册。PDB_CH0DLY1设延时一般设 0 表示计数器归零后立即触发也可以设一个小延时让 ADC 准备更充分。实操心得PDB 的MOD值不要设得太小否则触发频率过高ADC 可能来不及完成转换。ADC 单次转换时间 采样时间 转换时间12 位模式下通常几个微秒。触发周期必须大于这个时间否则会丢转换。3.4 DMA 通道配置源、目的、计数、中断DMA 配置分两步DMAMUX 路由和 eDMA 通道参数。DMAMUX 部分选一个通道比如 DMAMUX_CH0CHCFG_SOURCE写 ADC0 的请求编号。这个编号在手册的 DMAMUX 请求源表里查不同型号可能不同S32K144 的 ADC0 COCO 请求编号是固定的。eDMA 部分TCD_SADDR写ADC0-R[0]TCD_DADDR写用户数组地址TCD_SOFF写 0TCD_DOFF写 2TCD_NBYTES写 2TCD_CITER和TCD_BITER写缓冲区大小比如 100。TCD_CSR里INTMAJOR置 1 使能 major loop 完成中断START位在使能通道时置 1。DMA 通道使能通过DMA_ERQ寄存器或 eDMA 的SERQ寄存器。使能后ADC 每产生一次 COCODMA 就搬一次搬够 100 次触发中断。3.5 中断服务与缓冲区处理DMA major loop 完成中断里要做几件事清中断标志、处理数据、重置 DMA 的 CITER如果是非循环模式。如果用循环模式eDMA 会自动重载 CITER不需要手动重置但要注意缓冲区是双缓冲还是单缓冲。单缓冲的话CPU 处理数据期间 DMA 可能已经在写下一轮了会覆盖。稳妥做法是用双缓冲或者处理速度足够快。中断服务程序里读DMA-TCD[ch].DADDR可以知道当前搬到哪了但更常见的做法是固定缓冲区中断里直接处理整个数组。4. 常见问题与排查技巧实录4.1 采不到数据从触发链路逐级排查采不到数据是最常见的问题排查要按链路顺序来。先确认 ADC 是否在转换读ADC_SC1_COCO标志如果一直不置位说明触发没到。再查 PDB 是否在跑读PDB_SC的PDBIF标志如果不置位说明 PDB 没使能或时钟没开。再查 DMAMUX 路由是否正确读DMAMUX_CHCFG确认 SOURCE 字段。最后查 DMA 通道是否使能读DMA_ERQ或 eDMA 的SERQ。我遇到过一种情况PDB 配置都对ADC 也配了硬件触发但SIM_ADCOPT里的ADC0TRGSEL没设导致 ADC 的触发源默认是软件触发PDB 脉冲根本进不来。这个寄存器容易被忽略因为它不在 ADC 模块里而在 SIM 模块里。4.2 数据错位或跳动采样时间与触发频率匹配数据跳动大先看采样时间够不够。如果传感器输出阻抗高采样时间短了采样保持电容充不满读数会偏低且随信号变化。解决办法是加大ADLSMP和SMPLTS。另一个原因是触发频率超过了 ADC 转换能力导致部分转换被覆盖。算一下 ADC 单次转换时间确保触发周期大于它。还有一种情况是 DMA 搬运和 ADC 更新竞争。ADC 结果寄存器在转换完成后更新如果 DMA 在更新瞬间读取可能读到旧值或新值。eDMA 和 ADC 之间的握手通常能避免这个问题但如果发现偶发错位可以在 PDB 触发和 ADC 采样之间加一点延时让时序更从容。4.3 DMA 传输计数不对CITER 与 BITER 的关系eDMA 的CITER是当前剩余传输次数BITER是重载值。非循环模式下CITER减到 0 就停需要手动重写。循环模式下CITER减到 0 自动从BITER重载。如果发现只搬了一次就停检查CITER和BITER是否设对以及TCD_CSR里的DREQ位是否置位有些配置需要手动置位才能响应请求。4.4 中断进不去优先级与使能位DMA 完成中断进不去先查 NVIC 使能位和优先级。S32K144 的 DMA 通道中断在 NVIC 里有对应位置NVIC_ISER要置位NVIC_IPR要设优先级。另外 eDMA 的TCD_CSR_INTMAJOR要置 1DMA_ERQ或SERQ要使能通道。这几个条件缺一不可。下面这张表整理了我实际调试中遇到的典型问题和对应解法方便快速对照。现象可能原因排查方法解决ADC 无 COCO触发源未选对读 SIM_ADCOPT设 ADC0TRGSEL 为 PDBPDB 无脉冲PDBEN 未置位读 PDB_SC置 PDBEN检查时钟DMA 不搬DMAMUX 源错读 DMAMUX_CHCFG改为 ADC 请求编号只搬一次CITER 未重载读 TCD_CITER用循环模式或手动重载数据跳动采样时间短读 ADC_CFG1加大 ADLSMP/SMPLTS中断不进NVIC 未使能读 NVIC_ISER置对应中断位避坑技巧调试时先把 PDB 触发频率设低比如 1kHz用示波器或 GPIO 翻转确认触发确实在发生再逐步提高。直接上高频容易掩盖问题。5. SDK 配置对照与效率优化5.1 S32 SDK 里的等效配置如果你用 S32 SDKADC_DRV_ConfigConverter的配置结构体里triggerSource选ADC_TRIGGER_HARDWAREenableDMA置 true。PDB 部分用PDB_DRV_Init和PDB_DRV_ConfigAdcPreTrigger里面设频率和通道。DMA 部分用EDMA_DRV_ConfigChannel设源地址、目的地址、传输字节数和完成回调。SDK 的好处是封装了寄存器细节但坏处是出问题时不好定位。我的建议是先用 SDK 跑通再对照寄存器理解每一步做了什么。这样既快又扎实。5.2 提高采样效率的几个手段第一用 ADC 的硬件平均功能。S32K144 的 ADC 支持多次采样求平均ADC_SC3_AVGE置 1AVGS选平均次数。这样可以在不增加 CPU 负担的情况下提高信噪比。第二用 DMA 的 scatter-gather 模式。如果要在多个通道间轮询采样可以用多个 TCD 链接每个 TCD 对应一个通道DMA 自动切换。这比 CPU 干预高效得多。第三合理设置缓冲区大小。缓冲区太小中断太频繁太大数据延迟高。一般取 64 到 256 之间根据采样率和处理速度权衡。5.3 实测数据与性能对比我在 80MHz 主频下实测10kHz 采样率、12 位分辨率、DMA 搬运 100 个数据触发一次中断CPU 占用率不到 0.5%。同样的采样率用中断方式CPU 占用率约 3% 到 5%而且抖动明显。50kHz 时中断方式已经不稳定DMA 方式依然平稳。这个对比说明只要涉及连续采样DMA 几乎是必选项。最后分享一个小技巧调试 DMA 时可以在 DMA 完成中断里翻转一个 GPIO用示波器看中断周期是否和预期一致。如果周期不对说明搬运计数或触发频率有问题。这个方法比打印日志直观得多也不影响实时性。
返回列表