TMS320F28035-EP ADC与SPI模块实战配置与调试指南

📅 2026/7/15 6:40:37
TMS320F28035-EP ADC与SPI模块实战配置与调试指南
1. 项目概述从模拟到数字的桥梁在嵌入式系统开发尤其是工业控制、电机驱动和新能源领域TMS320F28035-EP这颗芯片可以说是老朋友了。作为TI Piccolo系列中的增强型工业级产品它集成了强大的数字信号处理能力和丰富的外设其中模数转换器ADC和串行外设接口SPI是两个使用频率极高、也最容易让人“踩坑”的模块。ADC负责将现实世界连续变化的电压信号比如电机电流、温度传感器输出电压转换成微控制器能处理的数字量而SPI则负责高效、可靠地将这些数据发送出去或者从外部传感器、存储器读取配置信息。很多人看数据手册觉得寄存器配置很简单照着写就行但实际调试中采样不准、时序错乱、通信失败的问题层出不穷其根源往往在于对这两个模块的“脾气秉性”理解不够透彻。这篇内容我就结合自己多年在电机控制和数字电源项目上使用F28035的经验抛开官方手册那些冰冷的参数列表重点聊聊ADC和SPI在实际工程中到底怎么配、为什么这么配以及那些手册上不会写但能让你少熬几个通宵的实战细节。无论你是正在评估这颗芯片还是已经深陷调试泥潭希望这些从项目里摸爬滚打出来的经验能给你带来一些实实在在的帮助。2. ADC模块深度解析不只是12位精度那么简单TMS320F28035-EP的ADC模块官方标称是12位精度最高60MHz时钟16个通道。这些参数看起来很美但直接照搬很可能得不到理想的性能。它的设计理念和早期的280x系列基于排序器的ADC有很大不同核心围绕SOC展开这带来了更高的灵活性也引入了更复杂的配置逻辑。2.1 核心架构与采样模式抉择这个ADC的核心是一个12位的转换器前端配了两个采样保持器。这直接引出了它的第一个关键特性支持同步采样。两个采样保持器意味着它可以同时对两路信号进行采样这对于需要严格同步测量的应用至关重要比如三相电机控制中的两相电流采样。采样模式的选择是第一个设计决策点顺序采样模式所有SOC最多16个按优先级依次转换。这是最常用的模式逻辑简单。在时序图上你会看到SOC0的采样窗口结束后经过一段固定的转换时间比如13个ADC时钟结果锁存然后SOC1才开始它的采样窗口。这种模式的总转换时间是可预测的等于所有激活的SOC的采样窗口转换时间之和。同步采样模式需要配对使用SOC。例如SOC0和SOC1可以配置为同步采样对分别采样ADCINA0和ADCINB0。在时序上配对的SOC共享同一个触发信号两个采样保持器同时捕获输入信号然后依次进行转换。这完美解决了多路信号相位同步的问题。实操心得在电机控制中我强烈推荐使用同步采样模式来采集相电流。即使你的硬件使用了两个独立的采样电阻和运放微小的时序差异也会在高速PWM开关下引入测量误差影响电流环的性能。同步采样能从根本上消除这个误差源。2.2 参考电压配置精度之源ADC的精度基石是参考电压。F28035-EP支持两种模式内部参考模式VREFLO内部连接到模拟地VSSAVREFHI固定为内部产生的3.3V与VDDA相关。此时输入电压范围是0-3.3V。数字值计算公式为Digital Value (Vinput / 3.3V) * 4095。这是最常用的模式方便但基准源的精度和温漂会直接影响整体精度。外部参考模式VREFHI和VREFLO引脚需要连接外部高精度、低温漂的基准源如REF5030。输入电压范围为VREFLO至VREFHI。数字值计算公式为Digital Value (Vinput - VREFLO) / (VREFHI - VREFLO) * 4095。这种方式能获得更高的绝对精度尤其适用于需要精确测量小信号或宽范围信号的场合。关键限制无论哪种模式VREFHI的电压都绝对不能超过VDDA模拟电源电压。在64引脚封装中VREFHI内部与ADCINA0引脚相连这意味着如果你使用内部参考ADCINA0通道的输入电压也必须控制在VDDA以下否则可能损坏芯片。2.3 SOC配置详解转换的指挥官SOC是理解此ADC的关键。每个SOC0-15都是一个独立的“转换任务”你需要为它配置三要素触发源谁来启动这次转换可以是软件立即触发、ePWM模块的SOCA/B事件、CPU定时器、外部GPIO中断等。在电机控制中最典型的就是用ePWM1/2/3的周期匹配或过零事件来触发ADC采样实现与PWM波形的严格同步。采样通道采哪一路信号从ADCINA0-7和ADCINB0-7中选择。采样窗口时间采样开关保持闭合对输入信号进行采样的时间有多长这需要根据信号源阻抗和ADC输入阻抗模型来计算。配置寄存器主要是ADCSOCxCTLx0~15。例如配置SOC0由ePWM1的SOCA触发采样ADCINA0采样窗口为15个ADCCLK周期// 假设 ADCCLK 60MHz EALLOW; // 解除寄存器保护 AdcRegs.ADCSOC0CTL.bit.CHSEL 0; // 选择 ADCINA0 AdcRegs.ADCSOC0CTL.bit.TRIGSEL 1; // 触发源选择1可能对应ePWM1 SOCA (需查具体TRIGSEL映射表) AdcRegs.ADCSOC0CTL.bit.ACQPS 14; // 采样窗口 ACQPS 1 个 ADCCLK周期这里为15个周期 EDIS; // 恢复寄存器保护2.4 采样窗口计算避免被忽略的误差这是ADC精度调试中最容易出错的地方。ADC输入端并非理想开路其等效模型是一个开关电阻典型值3.4kΩ串联一个采样电容1.6pF再加上管脚寄生电容约5pF。当采样开关闭合时外部信号需要通过源电阻Rs对这个RC网络充电。为了保证采样精度采样窗口时间必须足够长让采样电容上的电压建立到目标精度的误差范围内。对于12位精度1 LSB建立误差需小于1/4096 ≈ 0.024%。RC电路建立到目标精度所需的时间常数倍数N可以通过公式e^(-t/RC) 1/4096计算解得t -RC * ln(1/4096) ≈ 8.5 * RC。举例计算假设信号源阻抗Rs为100ΩADC输入阻抗Rin约为3.4kΩ总串联电阻R 100 3400 3500Ω。总电容C约为1.6pF 5pF 6.6pF。RC时间常数 τ R*C 3500 * 6.6e-12 ≈ 23.1ns。 那么所需的最小采样建立时间t_acq≈ 8.5 * 23.1ns ≈ 196.4ns。 如果ADCCLK为60MHz周期为16.67ns则需要的ADCCLK周期数至少为196.4ns / 16.67ns ≈ 11.8向上取整为12个周期。因此ACQPS应设置为至少11因为采样窗口周期数 ACQPS 1。踩坑记录我曾在一个电流采样电路中前端运放输出阻抗较低想当然地将ACQPS设为最小值如1。结果采样值在±20个LSB内随机跳动。后来用示波器观察ADC输入引脚发现在短暂的采样窗口内电压根本没稳定下来。将ACQPS增大到符合计算值后噪声立刻降到±2 LSB以内。教训永远不要忽略采样窗口的计算尤其是信号源阻抗较高或使用外部RC滤波时。3. ADC寄存器配置实战与中断管理理解了原理我们来看如何用代码把这些配置组合起来形成一个可用的ADC采集流程并高效地处理转换完成中断。3.1 ADC模块初始化流程一个稳健的ADC初始化流程通常遵循以下步骤我习惯将其封装成一个InitAdc()函数void InitAdc(void) { EALLOW; // 步骤1上电并等待稳定 AdcRegs.ADCCTL1.bit.ADCPWDNZ 1; // ADC模拟电路上电 AdcRegs.ADCCTL1.bit.ADCBGPWD 1; // 给内部带隙基准上电 AdcRegs.ADCCTL1.bit.ADCREFPWD 1; // 给参考电路上电 DELAY_US(1000); // 等待至少1ms让基准电压稳定这是手册要求的td(PWD)时间 // 步骤2配置ADC时钟和模式 AdcRegs.ADCCTL1.bit.ADCCLKPS 0; // ADC内核时钟分频0表示/1 AdcRegs.ADCCTL1.bit.SHCLOCKPS 0; // 采样保持时钟分频 AdcRegs.ADCCTL2.bit.CLKDIV2EN 0; // 不使用额外的/2分频 // 选择脉冲位置影响中断产生时机。0转换结束后产生1采样窗口结束后立即产生。 AdcRegs.ADCCTL1.bit.INTPULSEPOS 0; // 步骤3配置参考电压模式本例使用内部参考 AdcRegs.ADCCTL2.bit.REF_SEL 0; // 0使用内部参考1使用外部VREFHI/VREFLO // 步骤4配置SOC以SOC0, SOC1为例 // SOC0: 由ePWM1 SOCA触发采样ADCINA0采样窗口15个周期 AdcRegs.ADCSOC0CTL.bit.CHSEL 0; // 选择ADCINA0 AdcRegs.ADCSOC0CTL.bit.TRIGSEL 1; // 触发源选择需根据TRIGSEL映射表确认1对应ePWM1 SOCA AdcRegs.ADCSOC0CTL.bit.ACQPS 14; // 采样窗口 141 15 ADCCLK周期 // SOC1: 由ePWM1 SOCA触发采样ADCINB0用于同步采样电流 AdcRegs.ADCSOC1CTL.bit.CHSEL 8; // 选择ADCINB0 (通道号8) AdcRegs.ADCSOC1CTL.bit.TRIGSEL 1; // 同SOC0触发源 AdcRegs.ADCSOC1CTL.bit.ACQPS 14; // 步骤5配置采样模式本例为同步采样 AdcRegs.ADCSAMPLEMODE.bit.SIMULEN0 1; // 使能SOC0和SOC1为同步采样对 // 步骤6配置中断 // 假设我们想让SOC0转换完成后产生ADCINT1中断 AdcRegs.INTSEL1N2.bit.INT1SEL 0; // INT1对应SOC0 AdcRegs.INTSEL1N2.bit.INT1E 1; // 使能INT1 AdcRegs.INTSEL1N2.bit.INT1CONT 0; // 每次SOC0完成都产生中断 // 步骤7清除可能存在的旧标志位 AdcRegs.ADCINTFLGCLR.bit.ADCINT1 1; AdcRegs.ADCSOCFRC1.all 0x0000; EDIS; // 步骤8使能PIE级中断假设ADCINT1对应PIE组1中断4 PieCtrlRegs.PIEIER1.bit.INTx4 1; // 使能PIE组1的第4个中断ADCINT1 IER | M_INT1; // 使能CPU级INT1 EINT; // 全局中断使能 }3.2 中断服务程序ISR编写要点ADC中断是实时处理采样数据的关键。编写ISR时效率和安全同等重要。// ADC中断服务程序 (假设映射到PIE组1中断4) __interrupt void adc_isr(void) { // 1. 读取转换结果 AdcResult.Adc0 AdcRegs.ADCRESULT0; // 读取SOC0结果 AdcResult.Adc1 AdcRegs.ADCRESULT1; // 读取SOC1结果 // 2. 可选将12位右对齐结果转换为左对齐的16位整数方便计算 // 右对齐结果寄存器低12位有效高4位为0。 // 左对齐左移4位相当于乘以16将数值范围扩展到0-65520。 int16_t adc0_left_aligned (int16_t)AdcResult.Adc0 4; // 3. 转换为实际电压值内部3.3V参考 // 公式Voltage (AdcResult / 4095.0) * 3.3 // 为提高效率通常使用定点数运算。例如Q15格式 // #define ADC_TO_VOLTAGE_Q15(adc_val) (_IQ15mpy(adc_val 4, _IQ15(3.3/65520.0))) float voltage0 (AdcResult.Adc0 / 4095.0) * 3.3f; // 4. 清除ADC模块中断标志非常重要 AdcRegs.ADCINTFLGCLR.bit.ADCINT1 1; // 5. 清除PIE应答位以重新使能本中断 PieCtrlRegs.PIEACK.all PIEACK_GROUP1; // 6. 在此处进行你的控制算法计算例如电流环PI调节 // run_current_control_loop(adc0_left_aligned, adc1_left_aligned); }注意事项中断服务程序必须尽可能短小精悍。避免在ISR内进行浮点除法、打印等耗时操作。如果需要进行复杂的计算最好只做数据读取和标志位清除将计算任务交给后台主循环或更低优先级的任务。否则可能因中断处理时间过长错过下一次ADC触发导致数据丢失或系统时序混乱。4. SPI模块详解高速同步串行通信引擎SPI模块是F28035与外部世界进行高速数据交换的另一个利器。它全双工、同步、协议简单但时钟配置的灵活性也带来了初学者的困惑。4.1 SPI基础与主从模式SPI通信通常涉及一个主设备和一个或多个从设备。F28035的SPI模块可以配置为主模式或从模式。主模式由F28035产生时钟信号SPICLK并控制数据传输的起始和结束。它通过拉低从设备选择信号SPISTE来选中特定从设备。从模式F28035等待外部主设备提供的时钟并在被选中SPISTE为低时响应数据。四线制SPI引脚定义清晰SPISOMI从机输出/主机输入。数据从这个引脚进入主机当F28035为主机时或从这个引脚由从机输出当F28035为从机时。SPISIMO从机输入/主机输出。数据从这个引脚由主机输出或进入从机。SPICLK串行时钟由主设备产生。SPISTE从设备使能低电平有效。主设备用它来选择要通信的从设备。4.2 时钟相位与极性理解四种模式的本质这是SPI配置的核心也是最容易配错的地方。它由SPICCR.6 (CLKPOL)时钟极性和SPICTL.3 (CLKPHASE)时钟相位两个位共同决定四种模式。我习惯用“空闲状态”和“数据捕获边沿”来理解模式CLKPOLCLKPHASESPI时钟空闲状态数据捕获采样边沿数据输出改变边沿Mode 000低电平上升沿下降沿Mode 101低电平下降沿上升沿Mode 210高电平下降沿上升沿Mode 311高电平上升沿下降沿一个简单的记忆方法先看CLKPOL决定空闲电平0低1高。再看CLKPHASE如果为0则在第一个边沿即空闲状态改变的第一个边沿采样数据如果为1则在第二个边沿采样数据。数据总是在采样边沿的相反边沿改变。实战技巧绝大多数常见的SPI外设如Flash、ADC、传感器都工作在Mode 0或Mode 3。在连接一个新器件时第一件事就是查它的数据手册确认其SPI模式。如果手册没写可以先用Mode 0尝试因为这是最普遍的。如果通信失败再尝试Mode 3。Mode 1和2相对少见。4.3 波特率计算与FIFO使用SPI的通信速率由SPIBRR波特率寄存器控制。计算公式需要分情况讨论当3 ≤ SPIBRR ≤ 127时Baud Rate LSPCLK / (SPIBRR 1)当SPIBRR 0, 1, 2时Baud Rate LSPCLK / 4其中LSPCLK是低速外设时钟由系统时钟SYSCLKOUT分频而来通过PCLKCR0寄存器的SPIENCLK位和PCLKCR0的LOSPCP字段配置。假设系统时钟60MHzLOSPCP配置为/4则LSPCLK 60MHz / 4 15MHz。若需要1MHz的SPI波特率则SPIBRR LSPCLK / BaudRate - 1 15 / 1 - 1 14。F28035的SPI模块包含一个4级深度的硬件FIFO这是提升通信效率的关键。启用FIFO后你可以一次性写入最多4个数据到发送FIFOSPI模块会自动依次发送同时接收到的数据也会被压入接收FIFO直到你读取。这大大减少了CPU中断频率。配置FIFO的关键寄存器SPIFFTX发送FIFO控。SPIFFENA位使能FIFO功能TXFIFO位段设置发送中断级别例如当发送FIFO为空时产生中断。SPIFFRX接收FIFO控制。RXFIFO位段设置接收中断级别例如当接收FIFO中有4个数据时产生中断。SPIFFCTFIFO控制主要用来置发送缓冲器到移位寄存器之间的延迟以SPICLK周期为单位用于匹配不同速度的外设。5. SPI寄存器配置实战与通信例程让我们通过一个完整的例子将SPI配置为主机以Mode 0通信使用FIFO与一个SPI Flash存储器进行通信。5.1 SPI模块初始化主模式Mode 0void InitSpiA(void) { // 步骤1在初始化前将SPI引脚配置为外设功能而非GPIO EALLOW; // 假设使用GPIO16(SPISIMOA), GPIO17(SPISOMIA), GPIO18(SPICLKA), GPIO19(SPISTEA) GpioCtrlRegs.GPAPUD.bit.GPIO16 0; // 使能上拉 GpioCtrlRegs.GPAPUD.bit.GPIO17 0; GpioCtrlRegs.GPAPUD.bit.GPIO18 0; GpioCtrlRegs.GPAPUD.bit.GPIO19 0; GpioCtrlRegs.GPAMUX2.bit.GPIO16 1; // 配置为SPISIMOA功能 GpioCtrlRegs.GPAMUX2.bit.GPIO17 1; // 配置为SPISOMIA功能 GpioCtrlRegs.GPAMUX2.bit.GPIO18 1; // 配置为SPICLKA功能 GpioCtrlRegs.GPAMUX2.bit.GPIO19 1; // 配置为SPISTEA功能 EDIS; // 步骤2软件复位SPI SpiaRegs.SPICCR.bit.SPISWRESET 0; // 将SPI置于复位状态 DELAY_US(10); // 短暂延时 // 步骤3配置SPI控制寄存器 SpiaRegs.SPICCR.all 0x000F; // 配置SPICCR // bit 6 (CLKPOL) 0: 时钟低电平有效空闲低 // bit 5-4: 保留 // bit 3-0 (SPICHAR) 1111: 数据长度16位 (0xF 1 16) SpiaRegs.SPICTL.all 0x0006; // 配置SPICTL // bit 4 (OVERRUN INT ENA) 0: 禁止接收溢出中断初期调试可先关闭 // bit 3 (CLK PHASE) 0: 时钟相位为0 (Mode 0) // bit 2 (MASTER/SLAVE) 1: 主模式 // bit 1 (TALK) 1: 使能发送主模式下通常使能 // bit 0 (SPI INT ENA) 0: 先禁止SPI中断用FIFO中断 // 步骤4配置波特率 // 假设LSPCLK 15MHz目标波特率 1MHz // SPIBRR LSPCLK / BaudRate - 1 15 / 1 - 1 14 SpiaRegs.SPIBRR 14; // 步骤5配置并启用FIFO SpiaRegs.SPIFFTX.all 0xE040; // 使能发送FIFO复位TXFIFOTX中断级别设为0FIFO空时中断 // bit 15 (SPIRST) 1: 复位SPI // bit 14 (SPIFFENA) 1: 使能SPI FIFO增强功能 // bit 13 (TXFIFO) 1: 复位TXFIFO指针 // bit 8-7 (TXFFIL) 00: TX FIFO中断级别为0空时中断 SpiaRegs.SPIFFRX.all 0x204F; // 复位RXFIFORX中断级别设为15接近满时中断 // bit 13 (RXFIFO) 1: 复位RXFIFO指针 // bit 8-7 (RXFFIL) 01: RX FIFO中断级别为1有1个数据时中断 SpiaRegs.SPIFFCT.all 0x00; // FIFO发送延迟为0默认 // 步骤6退出复位状态启动SPI SpiaRegs.SPICCR.bit.SPISWRESET 1; // 步骤7配置PIE中断假设使用SPI TX空中断 // SpiaRegs.SPIFFTX.bit.TXFFIENA 1; // 在SPIFFTX中已通过TXFFIL间接使能 // PieCtrlRegs.PIEIER3.bit.INTx5 1; // 使能PIE组3的第5个中断SPITXINTA // IER | M_INT3; // EINT; }5.2 SPI数据收发函数与中断处理使用FIFO后数据收发变得高效。以下是一个查询式发送接收函数适用于非实时性要求高的场景uint16_t SpiA_TransmitReceive(uint16_t txData) { // 等待发送FIFO有空间非满 while(SpiaRegs.SPIFFTX.bit.TXFFST ! 0) { // 可以添加超时机制 } // 将数据写入发送缓冲器会自动压入TX FIFO SpiaRegs.SPITXBUF txData; // 等待接收FIFO有数据 while(SpiaRegs.SPIFFRX.bit.RXFFST 0) { // 可以添加超时机制 } // 从接收缓冲器读取数据 return SpiaRegs.SPIRXBUF; } // 更高效的批量传输使用中断 volatile uint16_t spi_tx_buffer[32]; volatile uint16_t spi_rx_buffer[32]; volatile uint16_t spi_tx_index 0; volatile uint16_t spi_tx_count 0; void SpiA_StartDmaTransmit(uint16_t *data, uint16_t length) { // 此函数模拟DMA将数据填入TX FIFO并启动传输 spi_tx_count length; for(spi_tx_index 0; spi_tx_index ((length4)?4:length); spi_tx_index) { SpiaRegs.SPITXBUF data[spi_tx_index]; } // 使能TX FIFO空中断在中断中继续填充数据 SpiaRegs.SPIFFTX.bit.TXFFIENA 1; } // SPI TX FIFO 空中断服务程序 __interrupt void spi_tx_isr(void) { if(spi_tx_index spi_tx_count) { SpiaRegs.SPITXBUF spi_tx_buffer[spi_tx_index]; } else { // 所有数据发送完毕禁用TX FIFO空中断 SpiaRegs.SPIFFTX.bit.TXFFIENA 0; // 可以设置一个完成标志位 } // 清除中断标志 SpiaRegs.SPIFFTX.bit.TXFFINTCLR 1; PieCtrlRegs.PIEACK.all PIEACK_GROUP3; // 假设中断在组3 }5.3 与SPI Flash通信的完整示例读取器件ID以读取Winbond W25Q32JV Flash的器件ID为例演示完整的SPI指令交互流程。#define SPI_FLASH_CMD_READ_ID 0x9F uint32_t SPI_Flash_ReadID(void) { uint32_t id 0; uint16_t temp; // 1. 拉低片选SPISTE GpioDataRegs.GPACLEAR.bit.GPIO19 1; // 假设SPISTE连接GPIO19低电平有效 // 2. 发送读ID指令 (0x9F) temp SpiA_TransmitReceive(SPI_FLASH_CMD_READ_ID); // 3. 接收3个字节的ID制造商ID存储器类型容量 // 注意SPI是全双工发送任意数据如0x00以产生时钟来读取 temp SpiA_TransmitReceive(0x00); // 读制造商ID如Winbond是0xEF id | (temp 0x00FF) 16; temp SpiA_TransmitReceive(0x00); // 读存储器类型 id | (temp 0x00FF) 8; temp SpiA_TransmitReceive(0x00); // 读容量 id | (temp 0x00FF); // 4. 拉高片选 GpioDataRegs.GPASET.bit.GPIO19 1; return id; }重要提示很多SPI外设的指令格式是8位的而我们的SPI可能配置为16位数据长度。在上面的例子中SpiA_TransmitReceive函数发送的是16位数据但Flash只识别低8位。因此发送指令0x9F时实际在总线上是先发送高8位0x00再发送低8位0x9F。如果外设期望先接收高字节这可能会有问题。解决方案要么将SPI配置为8位数据模式SPICCR.SPICHAR0x07要么在软件上调整数据字节的顺序。务必根据外设数据手册的时序图来调整。6. 常见问题排查与调试技巧实录即使配置看起来完全正确在实际硬件调试中ADC和SPI仍然可能出各种问题。下面是我总结的一些常见“坑点”和排查方法。6.1 ADC采样值不准或跳动大这是ADC调试中最常见的问题。可以按照以下清单逐步排查检查电源和地这是首要任务。用示波器测量VDDA和VSSA引脚确保模拟电源干净、稳定纹波尽可能小最好在50mV以内。数字电源的噪声很容易通过芯片内部耦合到模拟部分。确保模拟地和数字地在单点连接。检查参考电压如果使用内部参考测量VREFHI引脚可能需要在芯片外部加一个滤波电容到地的电压是否稳定在3.3V左右。如果使用外部参考确保基准源精度和驱动能力足够。验证采样窗口时间如前所述使用公式重新计算所需的最小ACQPS值。尤其是在信号路径上有串联电阻或使用了RC滤波电路时必须保证足够的采样时间。可以尝试逐步增大ACQPS观察采样值是否趋于稳定。检查输入信号范围确保输入信号在0-VREFHI之间。任何超过此范围的输入都会导致结果被钳位在0或4095。对于双极性信号如-1.65V ~ 1.65V需要外部运放进行电平移位。注意未使用的ADC引脚根据数据手册建议未使用的ADC输入引脚应连接到模拟地VSSA。如果该引脚复用了AIO功能则应通过一个1kΩ电阻接地防止被误配置为数字输出时产生冲突。开启过采样和平均对于噪声较大信号可以在软件中实现过采样和数字平均。例如快速连续采样16次然后取平均可以有效提高有效分辨率抑制随机噪声。校准偏移和增益误差F28035-EP的ADC支持自校准。上电后可以执行一次偏移校准将输入短接到VREFLO/地读取转换值作为偏移量和增益校准输入一个已知的精确电压如VREFHI的一半。在后续采样中用软件补偿这些误差。相关寄存器是ADCOFFTRIM和ADCREFTRIM但软件校准更灵活。6.2 SPI通信无响应或数据错误SPI通信是同步的时序要求严格。排查步骤如下确认电气连接这是最基础的。用万用表检查SPICLK,SPISIMO,SPISOMI,SPISTE四根线是否连接正确、有无短路/断路。确保主从设备共地。确认时钟模式这是出错的重灾区。用示波器同时测量SPICLK和SPISIMO主机发送信号。对照数据手册的时序图检查时钟空闲电平、数据采样边沿是否与从设备要求的模式一致。我强烈建议在调试初期使用示波器它是诊断SPI问题最直观的工具。检查片选信号确认SPISTE信号在通信开始时被正确拉低并在通信结束后拉高。有些从设备要求片选在数据帧之间保持低电平有些则要求每字节都翻转。仔细阅读从设备的数据手册。确认数据位序SPI通常采用MSB最高位先发送。但有些设备可能是LSB先传。检查SPICCR.7 (SPILBK)位和从设备要求。检查波特率过高的波特率可能导致时序裕量不足尤其是在长导线或高噪声环境中。尝试降低SPIBRR降低波特率看通信是否恢复。使用逻辑分析仪如果问题复杂逻辑分析仪是终极武器。它可以同时捕获四路SPI信号并解析出数据字节让你清晰地看到每一个时钟沿对应的数据位极易定位是哪个字节、哪一位出了问题。注意FIFO状态在使用FIFO时常见的错误是未正确判断FIFO状态就进行读写。发送前检查SPIFFTX.bit.TXFFST发送FIFO中数据的数量是否小于4未满。接收时检查SPIFFRX.bit.RXFFST是否大于0有数据。同时要妥善处理接收溢出标志SPIFFRX.bit.RXFFOVF。6.3 中断无法进入或进入过于频繁中断使能链检查这是最容易被忽略的。确保中断使能层层打开外设级例如ADC的INTSEL1N2.bit.INT1E 1SPI的SPIFFTX.bit.TXFFIENA 1。PIE级PieCtrlRegs.PIEIERx.bit.INTxy 1x为组号y为组内序号。CPU级IER | M_INTx使能对应的CPU中断线。全局EINT();开启全局中断。中断标志清除在中断服务程序ISR中必须清除对应的外设中断标志如AdcRegs.ADCINTFLGCLR.bit.ADCINT1 1和PIE应答位PieCtrlRegs.PIEACK.all PIEACK_GROUPx。忘记清除PIEACK位会导致该组所有后续中断被屏蔽。中断优先级与嵌套默认情况下F28035的中断是不可嵌套的。如果一个高优先级的中断服务程序执行时间过长可能会阻塞ADC或SPI这类需要快速响应的中断。如果确实需要可以配置中断嵌套但会增加软件复杂性。中断触发条件确认中断触发条件是否符合预期。例如ADC中断是配置为每个SOC完成都触发还是所有SOC完成才触发SPI中断是TX FIFO空触发还是RX FIFO有数据触发错误的条件会导致中断不产生或疯狂产生。调试是一个系统工程从原理图检查、寄存器配置验证到示波器/逻辑分析仪观测再到软件逻辑分析需要耐心和条理。把上面这些点都过一遍大部分ADC和SPI的问题都能找到根源。