ARTICLE DETAIL

资讯详情

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

TMS320C28346 ADC采样工程:从时钟配置到DMA数据搬运

TMS320C28346 ADC采样工程:从时钟配置到DMA数据搬运 简介一套基于TI TMS320C28346浮点DSP的ADC采样信号处理示例程序面向嵌入式DSP开发者及数字信号处理初学者。程序围绕ADC模块的配置与中断处理展开覆盖采样率设定、分辨率选择、转换时间控制、数字滤波与数据存储等关键环节并示范了中断服务程序如何保证数据流连续可直接在CCS集成开发环境中导入工程进行调试和单步学习。资源包共93个文件以h头文件、c源文件、asm汇编、cmd链接脚本为主另含工程配置文件、obj目标文件、map映射文件等整体仅421KB目录结构清晰方便按模块查看。配套DSP2834x标准外设库与ADC_XINTF.c主程序完整呈现外部接口扩展与模拟量采集流程。目前已有256人学习对于需要快速上手C28346 ADC开发、理解实时采集与中断机制的读者是一份高效实用的参考资料。1. TMS320C28346 ADC采样工程先看这套代码在调度什么作为一颗最高主频到300MHz的浮点DSPTMS320C28346内置的12位ADC往往被当成片上外设一笔带过真正决定采集质量的其实是输入时钟、采样窗口ACQ_PS以及参考电压这几项寄存器配置。这套名为ADC_dsp程序的工程延续了TI早期CCS工程的典型结构主逻辑集中在ADC_XINTF.c里从文件名和ExRam.obj这个目标文件可以判断ADC转换结果并不打算停在片内RAM而是通过XINTF外部接口落到扩展SRAM再交给上层处理。适合正在做28346或同系列DSP底层开发、想把采样链路跑通的人参考。下面按文件分组、时钟链路、ADC寄存器配置、结果搬运和调试经验这几条线拆开说。2. 工程文件分组与28346的外设时钟链路2.1 从压缩包文件名判断工程的性质压缩包里DSP2834x_开头的文件都是TI官方外设库的内容这一点先明确。真正属于这个项目自己写的代码核心是ADC_XINTF.c其余的DSP2834x_x.c全部是支撑文件负责把芯片的启动、中断和时钟先伺候好。可以把文件分成四组来看分组代表文件职责启动与中断DSP2834x_CodeStartBranch.asm、DSP2834x_DefaultIsr.c、DSP2834x_PieVect.c复位入口、默认中断服务函数、PIE向量表初始化系统控制DSP2834x_SysCtrl.c、DSP2834x_CpuTimers.cPLL配置、看门狗、CPU定时器数据传输DSP2834x_Xintf.c、DSP2834x_DMA.c、ADC_XINTF.c外部接口初始化、DMA通道配置、ADC采样主逻辑链接配置28346_RAM_lnk.cmd、DSP2834x_Headers_nonBIOS.cmd、CMD目录内存段分配、外设寄存器地址映射ADC_XINTF.c这个文件值得先说清楚它和TI官方外设库里的标准ADC初始化函数不是一回事。作者把AdcRegs寄存器配置和XintfRegs外部接口配置放在同一个文件里是因为这个工程的目标不是只采一组数看一眼而是让采样值直接落到外部内存空间。如果你打开工程只找传统的AdcRegs初始化代码会花掉不少时间先有这个全局认知会好很多。另外DSP2834x_Headers_nonBIOS.cmd将外设寄存器映射到固定地址例如AdcRegs位于0x00007100这个文件掉链子整个寄存器访问都会跑飞。2.2 InitSysCtrlPLL与HSPCLK的配置逻辑ADC模块的时钟源头是HSPCLK而HSPCLK又由SYSCLKOUT经HISPCP分频而来。28346最高可以跑300MHz实际项目里为了兼顾功耗和稳定性更多时候把SYSCLKOUT放在300MHz、HSPCLK放在150MHz。下面这段是InitSysCtrl里和ADC关系最密切的部分// DSP2834x_SysCtrl.c 中 InitSysCtrl 的核心段落 void InitSysCtrl(void) { DisableDog(); // 外部晶振30MHzPLLCR20SYSCLKOUT 30MHz * 20 / 2 300MHz SysCtrlRegs.PLLCR 0x0000; // 先复位PLL防止配置中途锁相不稳 SysCtrlRegs.PLLCR 0x0014; // 20倍频 // 等待PLL锁相完成PLLLOCKS置1后才能继续 while (SysCtrlRegs.PLLSTS.bit.PLLLOCKS ! 1) { } EALLOW; // HISPCP1 HSPCLK SYSCLKOUT / 2 150MHz SysCtrlRegs.HISPCP.all 0x0001; // ADC外设总线时钟使能 SysCtrlRegs.PCLKCR0.bit.ADCENCLK 1; EDIS; }PLLCR复位后再写入倍频系数是TI的常规操作直接改倍频可能让PLL进入不确定状态。PLLCR0x0014对应30MHz外部晶振输入经过二分频后再20倍频得到300MHz此时HISPCP必须设为1否则ADC输入时钟会超过技术手册上限。写完后可以顺手把SysCtrlRegs.PLLSTS和SysCtrlRegs.HISPCP放到CCS的Expressions窗口里确认PLLLOCKS位为1、HISPCP为1是正常状态。2.3 ADC外设时钟使能后还差一级模拟供电开关PCLKCR0里的ADCENCLK只是数字总线时钟它不代表ADC模块内部已经上电。ADC内部的带隙基准和模拟电路需要分别通过ADCTRL3寄存器的ADCPWDN位和ADCBGRFDN位打开。很多从28335往28346迁移的代码问题就出在这里ADCENCLK置了1ADCRESULT寄存器却一直读回零查了半天才发现是ADCTRL3里的模拟上电位没写。实际调试时建议按照这条链路逐级确认SYSCLKOUT(300MHz) → HISPCP分频 → HSPCLK(150MHz) → ADCCLKPS分频 → ADCCLK(最大25MHz)ADCCLKPS在ADCTRL3的bit7到bit5分频公式是ADCCLK HSPCLK / (ADCCLKPS 1)。150MHz的HSPCLK要得到25MHzADCCLKPS 5。这个值不是随便选的超过25MHz规格之后ADC内部比较器动作时间不够转换结果会出现码值跳变尤其在通道切换时表现得最明显。如果你只采一路慢信号ADCCLKPS可以放宽一些但25MHz是手册标称的推荐上限。3. ADC排序器、采样窗口与触发源的手工配置3.1 排序器结构双排序器还是级联16通道TMS320C28346的ADC有16个模拟输入通道通道名是ADCINA0到ADCINA7、ADCINB0到ADCINB7内部有两个采样保持器。排序器可以工作在两个独立的8通道模式也可以合并成一个16通道级联模式。级联模式的好处是一次SOC事件可以连续转完所有通道不用中途切换排序器缺点是排序器一旦跑起来中间要改变通道序列就得等这一轮结束。实际工程里我习惯在需要采集8路以内信号时用独立SEQ1需要采集8路以上或者希望一轮无论如何把16路都采一遍时用级联。此工程既然做了XINTF扩展多半要接多路传感器或者多路模拟量用级联模式更合理因为数据可以连续存到外部RAM不需要CPU干预排序过程。3.2 核心寄存器位域速查ADC配置涉及到的主要寄存器是ADCTRL1、ADCTRL2、ADCTRL3三个控制寄存器再加上ADCMAXCONV和ADCCHSELSEQx。关键位域整理如下位域所在寄存器常用值作用CONT_RUNADCTRL1[14]0启动-停止模式转换完一轮自动停SEQ_CASCADCTRL1[13]1级联模式SEQ1与SEQ2合并ACQ_PSADCTRL1[12:8]6采样窗口扩展宽度INT_ENA_SEQ1ADCTRL2[5]1SEQ1转换结束触发ADCINT1EPWM_SOCA_SEQ1ADCTRL2[8]1允许ePWM1的SOCA触发采样SOC_SEQ1ADCTRL2[6]1软件触发启动SEQ1ADCCLKPSADCTRL3[7:5]5ADCCLK HSPCLK / (ADCCLKPS1)ADCPWDNADCTRL3[4]1ADC模拟电路供电控制ADCBGRFDNADCTRL3[3]1内部带隙基准与参考缓冲上电这里最容易被低估的是ACQ_PS。ACQ_PS决定采样开关闭合的时间也就是采样保持电容充电的时间窗口公式是采样窗口 (ACQ_PS 1) × 2 × SYSCLKOUT周期。28346跑300MHz时SYSCLKOUT周期约3.33nsACQ_PS6对应约46ns的窗口对低阻抗信号源够用如果信号源内阻较大比如超过10kΩ这个窗口需要加大否则保持电容充不满转换结果的低位会呈现周期性波动。3.3 多通道级联初始化代码下面是一段适合直接抄进ADC_XINTF.c的初始化代码配置为级联模式、顺序采样、四路通道、软件触发void ADC_Init_MultiChannel(void) { // 复位ADC模块让寄存器回到已知状态 AdcRegs.ADCTRL1.bit.RESET 1; NOP(); // ADCTRL1: 级联模式 ACQ_PS6 启动-停止模式 AdcRegs.ADCTRL1.all 0x2000 | (6 8); // ADCTRL3: ADCCLKPS5 - ADCCLK150MHz/625MHz // ADCPWDN1 - ADC模拟部分上电 // ADCBGRFDN1 - 内部带隙基准上电 AdcRegs.ADCTRL3.all 0x00B8; // 级联模式下MAX_CONV13表示本轮转换4个通道 AdcRegs.ADCMAXCONV.all 0x0003; // 通道选择: CONV00A0, CONV01A1, CONV02A2, CONV03A3 AdcRegs.ADCCHSELSEQ1.all 0x0321; // 使能转换结束中断本次用软件触发启动 AdcRegs.ADCTRL2.bit.INT_ENA_SEQ1 1; AdcRegs.ADCTRL2.bit.SOC_SEQ1 1; }ADCTRL1.all 0x2000 | (6 8)这行0x2000对应SEQ_CASC68是把ACQ_PS写到bit8到bit12。ADCMAXCONV.all 0x0003级联模式下表示第一组转换序列里包含4个转换实际通道由ADCCHSELSEQ1寄存器的CONV00到CONV03四个字段决定这里按顺序选了ADCINA0到ADCINA3。如果你要采16路MAX_CONV1和MAX_CONV2都要设置例如0x0077。初始化完成后ADC结果寄存器ADCRESULT0到ADCRESULT3分别对应这四路。3.4 用ePWM触发实现固定采样周期软件触发适合调试但在真正的控制或采集场景中采样周期需要和PWM周期严格对齐这时候通常用ePWM模块的SOCA事件来触发ADC。下面是配合ePWM1的配置方法// ePWM1: 300MHz TBCLK下TBPRD3000则周期10us即100kHz EPwm1Regs.TBPRD 3000; EPwm1Regs.TBPHS.all 0; EPwm1Regs.TBCTR 0; // 向上计数模式 EPwm1Regs.TBCTL.bit.CTRMODE 0; // 在TBPRD周期点触发一次SOCA EPwm1Regs.ETSEL.bit.SOCAEN 1; EPwm1Regs.ETSEL.bit.SOCASEL 2; EPwm1Regs.ETPS.bit.SOCAPRD 1;这段代码把ePWM1的周期中断事件作为ADC启动信号ADC采样周期就等于ePWM周期。TBPRD3000这个值的前提是ePWM模块时间基准时钟等于SYSCLKOUT即300MHz如果你的工程里把TBCLK做了分频这个数要相应调整。ePWM触发的好处是采样点和PWM载波严格同步在开关电源、电机控制这类对采样时刻敏感的场合能避免在开关噪声峰值处采到错误电压。4. 中断服务、DMA搬运与XINTF外部存储接力4.1 ADC转换完成中断与PIE向量配置TMS320C28346的ADC中断属于PIE外设中断扩展模块ADCINT1对应PIE组1的第6个中断通道。初始化时需要同时打开PIE级中断使能和CPU级中断使能代码如下// 中断服务函数放在DSP2834x_DefaultIsr.c中 interrupt void ADCINT1_ISR(void) { Uint16 i; // 把当前这一轮结果读出来按12位有效值处理 for (i 0; i ADC_BUF_LEN; i) { g_adc_raw[i] AdcRegs.ADCRESULT0 4; } // 清除SEQ1中断标志 AdcRegs.ADCST.bit.INT_SEQ1_CLR 1; // 应答PIE组1允许下一次中断进入 PieCtrlRegs.PIEACK.all PIEACK_GROUP1; }ADCRESULT寄存器的高12位才是有效转换结果所以右移4位是常规操作。中断里做的工作越少越好28346虽然主频高但如果中断服务函数里处理大量浮点运算采样率上到100kHz以上时CPU占用率会明显上升。稳妥做法是把结果先搬到一个小的RAM缓冲区数据处理放到主循环或后台任务里。4.2 DMA通道把结果搬进XINTF外部内存当一轮ADC转换的通道数较多或者采样率比较高时中断方式逐次搬运会消耗大量CPU时间。28346的DMA模块可以从ADC结果寄存器直接搬运数据到指定地址触发源可以配置为ADCINT1。工程里已经带了DSP2834x_DMA.c说明作者预留了这条路径。// DMA通道1每次ADC转换结束后把结果寄存器搬到XINTF Zone6 DmaRegs.CH1.CONTROL.bit.PERINTSEL D_DMA_ADCINT1; // 触发源选择ADCINT1 DmaRegs.CH1.CONTROL.bit.PERINTEN 1; DmaRegs.CH1.SRC_BEG_ADDR (Uint32)AdcRegs.ADCRESULT0; DmaRegs.CH1.DST_BEG_ADDR 0x00100000; // XINTF Zone6起始地址 DmaRegs.CH1.SRC_ADDR (Uint32)AdcRegs.ADCRESULT0; DmaRegs.CH1.DST_ADDR 0x00100000; DmaRegs.CH1.TRANSFER_COUNT 15; // 突发搬运16次这段代码里PERINTSEL的具体索引值在DSP2834x_DMA_defines.h里有宏定义不同DMA通道对应的触发源映射不一样直接看头文件最准确。DMA配置完成后ADC每完成一轮转换CPU几乎不需要介入数据就自动出现在外部SRAM对应的地址上。主程序在需要的时候直接按指针读取即可。4.3 XINTF区域映射和时序参数28346的XINTF接口可以外扩多个区域的存储器或外设工程中看到28346_RAM_lnk.cmd和ExRam.obj基本可以确认使用了外部RAM。XINTF区域映射关系如下区域起始地址典型容量常见用途Zone00x0000200016K×16外扩异步外设或SRAMZone60x001000001M×16外扩SRAMADC数据缓冲Zone70x002000002M×16外扩Flash或双缓冲区XINTF时序通常要根据外接芯片的datasheet来配一个常见做法是写一个简单的内存读写循环用示波器看片选和读写信号的实际宽度再决定是否需要加大LEAD或ACTIVE参数。参考配置如下EALLOW; // XTIMCLK SYSCLKOUT / 2降低外部总线速率换取稳定 XintfRegs.XINTCNF2.bit.XTIMCLK 1; // Zone6读写时序LEAD2, ACTIVE6, TRAIL2 XintfRegs.XTIMING6.bit.XRDLEAD 2; XintfRegs.XTIMING6.bit.XRDACTIVE 6; XintfRegs.XTIMING6.bit.XRDTRAIL 2; XintfRegs.XTIMING6.bit.XWRLEAD 2; XintfRegs.XTIMING6.bit.XWRACTIVE 6; XintfRegs.XTIMING6.bit.XWRTRAIL 2; EDIS;实际项目里ACTIVE时间不是越长越好太长会拖慢整体数据吞吐。对低速SRAM比如100ns级别的芯片配合150MHz的XTIMCLKACTIVE取6到8比较稳对更快的异步SRAM可以压到3到4。调时序的通用验证方法是对外部地址连续写0x55和0xAA再读回来比较这个测试通过了再上ADC数据搬运逻辑。5. 现场调试数值漂移、参考电压与软件滤波5.1 采样值漂移最常见的三个根源ADC数据出现漂移的概率分布里三个根源几乎占了绝大多数。第一是参考电压不稳28346的VREFHI引脚附近如果退耦电容离得远内部参考缓冲会因为驱动能力不足产生缓慢的电压跌落表现为所有通道读数整体跟着漂。正确做法是在VREFHI和VREFLO之间放100nF陶瓷电容加10uF钽电容并联并且尽量靠近MCU引脚。第二是模拟电源和数字电源没有隔离ADC模拟供电引脚上的纹波会直接耦合进转换结果。第三是采样时钟的抖动时钟沿不干净时采样孔径抖动会以噪声形式出现在输出码值中这种噪声靠均值滤波也难以彻底消除。PCB布局上模拟地和数字地单点连接、ADC输入走线包地、采样时钟线远离开关节点这三条是规避时钟抖动和电源噪声最直接的布局要点。5.2 简单有效的两点校准如果发现读回的电压值和万用表测量值之间存在固定偏差或者比例偏差可以在实际电路中做两点校准。采集0V和接近满量程的两个点算出偏移量和增益系数之后每次读取都套用这个线性关系// 校准参数g_offset为零点偏移g_slope为实测增益系数 float adc_voltage(uint16_t code) { float v (float)(code 4); return (v - g_offset) * g_slope; }g_offset和g_slope的求法是在输入端接一个精密分压电阻用高精度万用表记录V0和V1两个电压同时记录ADC读到的code0和code1然后g_slope (V1 - V0) / (code1 - code0)g_offset V0 - g_slope * code0。需要留意的是校准只在同样温度和同样参考电压条件下有效温度变了或者换了一片芯片校准系数往往要重做。5.3 C语言实现对ADC原始值的滑动均值滤波ADC结果里的随机噪声通常采用滑动均值滤波来处理既不会像单次采样那样抖动明显响应速度也可以接受。下面是一个8点滑动均值滤波函数适合放在中断外的主循环里处理unsigned int adc_move_avg(unsigned int sample) { static unsigned int buf[8] {0}; static unsigned char idx 0; unsigned long sum 0; unsigned char i; buf[idx] sample; idx (idx 1) 0x07; // 环形缓冲区下标 for (i 0; i 8; i) { sum buf[i]; } return (unsigned int)(sum 3); // 除以8 }这段代码把最近8次采样值放在一个环形缓冲区里每次新值进来覆盖最旧的值然后对所有元素求和再右移3位。右移3位等价于除以8比整数除法指令更快DSP的C编译器通常也能识别这种写法并优化。滑动均值滤波对白噪声类干扰抑制明显但如果你的信号本身存在较大尖峰脉冲这种方法会把尖峰的影响保留到缓冲区滚出去为止这种情况下建议先做一个剔除最大最小值的处理再计算平均值。本文还有配套的精品资源点击获取
返回列表