1. 项目概述:为什么需要深入理解Stellaris ADC的采样序列?
在嵌入式系统开发中,模数转换器(ADC)是连接模拟物理世界与数字处理核心的桥梁。无论是读取温度传感器的微弱电压,还是监测电池的剩余电量,都离不开ADC的精准工作。然而,很多开发者在使用ADC时,往往停留在“调用库函数,读取结果”的层面,一旦遇到多通道、高频率、低噪声的复杂采集需求,就容易陷入性能瓶颈或数据错乱的困境。
德州仪器(TI)的Stellaris系列微控制器(如LM3S2965)提供了一个极具特色的ADC模块,其核心在于可编程采样序列器。这与我们常见的“单次触发-单次转换”或“自动扫描”模式有本质区别。简单来说,你可以把它想象成一个可以预先编排动作列表的“采集机器人”。你不需要在每次转换后都去告诉它“下一个采哪个通道”,而是可以一次性给它下达一个包含多个步骤(采样哪个通道、是否用差分模式、采完后要不要中断)的完整“剧本”。然后,只需一个触发信号(来自软件、定时器、PWM或外部GPIO),这个“机器人”就会自动、连续、无误地执行整个剧本,并将结果按顺序存入专属的FIFO队列中。
这种架构的价值在于确定性和高效率。在实时性要求高的系统中,它能确保采样时序的精确,并极大减轻CPU在数据采集上的负担,让CPU能专注于更复杂的算法处理。而要驾驭这种强大能力,关键在于透彻理解其背后的寄存器配置逻辑。本文将以LM3S2965的ADC模块为例,从寄存器位域解析出发,一步步拆解如何构建一个从单通道到多通道、从单次触发到连续采集的完整采样序列,并分享在实际项目中调试此类ADC的独家心得与避坑指南。
2. 核心架构与寄存器地图总览
在深入每个寄存器细节之前,我们必须先建立起对Stellaris ADC模块整体架构的认知。这有助于我们理解各个寄存器在数据流和控制流中所扮演的角色。
2.1 模块功能框图与数据流
根据技术手册中的框图,整个ADC模块可以划分为几个核心部分:
- 模拟前端与10位SAR ADC核心:负责实际的电压-数字量转换,内部使用3V参考电压。
- 四个可编程采样序列器(SS0-SS3):这是模块的“大脑”。每个序列器独立拥有一组配置寄存器(MUX, CTL)和一个结果FIFO。
- 硬件平均器:位于ADC核心和FIFO之间,可对最多64次转换结果进行累加平均,用吞吐量换取精度。
- 触发与仲裁逻辑:负责接收来自软件(ADCPSSI)、定时器、模拟比较器、PWM和GPIO的触发事件,并根据优先级(ADCSSPRI)仲裁哪个序列器先执行。
- 中断控制逻辑:管理由序列器产生的中断信号,经过掩码(ADCIM)后上报给CPU。
数据流的典型路径是:触发事件到来 -> 根据优先级选中一个就绪的序列器 -> 按序执行其预编程的采样步骤(每一步从ADCSSMUXn选定的通道采样)-> 转换结果经过(可选的)硬件平均 -> 存入该序列器对应的ADCSSFIFOn -> 如果该步骤配置了中断(IE位),则置位ADCRIS中的相应标志 -> 若ADCIM中对应中断使能,则向CPU产生中断。
2.2 关键寄存器分类与地址映射
LM3S2965的ADC模块寄存器基址为0x4003.8000。所有寄存器均为32位。为了高效编程,我们可以将其按功能分组:
| 寄存器类别 | 寄存器名 | 偏移地址 | 核心功能简述 |
|---|---|---|---|
| 全局控制与状态 | ADCACTSS | 0x000 | 激活(使能)各个采样序列器 |
| ADCRIS | 0x004 | 查看原始中断状态(Raw Interrupt Status) | |
| ADCIM | 0x008 | 中断掩码,控制哪些中断能上报给CPU | |
| ADCISC | 0x00C | 中断状态与清除寄存器(写1清除) | |
| ADCOSTAT | 0x010 | FIFO溢出状态 | |
| ADCUSTAT | 0x018 | FIFO下溢状态 | |
| 触发与优先级 | ADCEMUX | 0x014 | 为每个序列器选择触发事件源 |
| ADCSSPRI | 0x020 | 设置四个序列器的优先级(0最高,3最低) | |
| ADCPSSI | 0x028 | 软件触发寄存器(写1启动采样) | |
| 序列器专用配置 | ADCSSMUX0-3 | 0x040, 0x060... | 分别为SS0-SS3配置每个采样步的输入通道 |
| ADCSSCTL0-3 | 0x044, 0x064... | 分别为SS0-SS3配置每个采样步的控制位(中断使能、结束标志、差分模式等) | |
| 数据与FIFO状态 | ADCSSFIFO0-3 | 0x048, 0x068... | 读取对应序列器的转换结果(只读,读操作即弹出数据) |
| ADCSSFSTAT0-3 | 0x04C, 0x06C... | 查看对应序列器FIFO的满/空状态及数据个数 | |
| 其他功能 | ADCSAC | 0x030 | 硬件采样平均控制 |
| ADCTMLB | 0x100 | 测试模式回环控制(用于软件调试) |
注意:在访问任何ADC寄存器之前,必须确保已通过系统控制模块的RCGC0寄存器(地址
0x400F.E000)使能了ADC时钟(向bit 16写1),并且等待至少3个系统时钟周期。这是很多初学者容易忽略,导致配置不生效的“第一坑”。
3. 采样序列器的深度解析与寄存器配置
这是Stellaris ADC最核心、也最灵活的部分。每个采样序列器(Sample Sequencer)本质上是一个状态机,它按照我们预先在ADCSSMUXn和ADCSSCTLn寄存器中编好的“程序”来工作。
3.1 序列器能力与FIFO深度
不同序列器的“能力”不同,主要体现在其可编程的采样步数和FIFO深度上,这在设计采集任务时必须首先考虑:
| 序列器 | 最大采样步数 | FIFO深度 | 典型应用场景 |
|---|---|---|---|
| SS0 | 8 | 8 | 最复杂的多通道采集任务,如顺序采集8个传感器。 |
| SS1 | 4 | 4 | 中等复杂度任务,如采集4路电机电流。 |
| SS2 | 4 | 4 | 同SS1,可用于另一组信号。 |
| SS3 | 1 | 1 | 单次采样或最低优先级的中断触发采样。 |
重要经验:ADCSSMUXn和ADCSSCTLn寄存器都是32位,每4个比特(一个nibble)控制一个采样步。对于SS0,它使用低32位中的8个nibble(bit[3:0], bit[7:4], ..., bit[31:28])对应采样步0到7。对于SS1和SS2,只使用低16位(4个nibble)。对于SS3,只使用最低4位(1个nibble)。向高位未使用的nibble写入数据是无效的。
3.2 ADCSSMUXn – 输入通道选择寄存器
这个寄存器决定每个采样步从哪个模拟通道获取信号。每个nibble的值(0-3)对应一个模拟输入引脚(AIN0-AIN3)。此外,还有一个特殊值用于内部温度传感器。
位域定义(以每个4位nibble为单位):
值 0-3: 选择外部模拟输入通道 AIN0 - AIN3。值 8(二进制1000): 这是一个关键值,用于选择内部温度传感器。很多手册可能只提通道0-3,但温度传感器是通过这个特殊编码访问的。
配置示例:假设我们使用SS1,希望按顺序采样AIN1、温度传感器、AIN2、AIN0。
// ADCSSMUX1 寄存器地址:0x4003.8060 // 每个采样步用4位,SS1有4个步(采样步3,2,1,0) // 我们希望:步0 -> AIN1 (值1), 步1 -> 温度传感器(值8), 步2 -> AIN2(值2), 步3 -> AIN0(值0) // 所以 nibble3=0, nibble2=2, nibble1=8, nibble0=1 // 32位寄存器值 = (0 << 12) | (2 << 8) | (8 << 4) | (1 << 0) = 0x00000281 HWREG(ADC0_BASE + ADC_O_SSMUX1) = 0x281;3.3 ADCSSCTLn – 采样控制寄存器
这是配置采样行为的核心,每个nibble控制对应采样步的细节。其位定义(每个nibble内)如下:
| 位 | 名称 | 功能描述 |
|---|---|---|
| 3 | TS0 | 温度传感器选择。必须与ADCSSMUXn���对应nibble的值8配合使用。当此位为1且MUX选择为8时,采样内部温度传感器。 |
| 2 | IE0 | 中断使能。若此位置1,则该采样步转换完成后,会置位ADCRIS中的对应中断原始标志。 |
| 1 | END0 | 序列结束标志。若此位置1,则此采样步是当前序列的最后一步。转换完成后,序列器状态会复位,等待下一次触发。 |
| 0 | D0 | 差分输入模式。置1使能该采样步的差分采样模式。此时,ADCSSMUXn中对应的值应设置为差分对编号(0或1),而非单个通道号。 |
配置逻辑与示例:继续上面的例子,我们为SS1的四个采样步配置控制位。
- 假设要求:所有步均为单端输入(D=0);仅在最后一个采样步(步3,采样AIN0)完成后产生中断(IE3=1, END3=1);采样温度传感器的那一步需要设置TS位(TS1=1)。
// ADCSSCTL1 寄存器地址:0x4003.8064 // 每个采样步用4位控制:位[3]=TS, [2]=IE, [1]=END, [0]=D // 步3 (nibble3): 采样AIN0,单端,需中断并结束序列 -> 二进制 0b0110 = 0x6 (IE=1, END=1) // 步2 (nibble2): 采样AIN2,单端,无中断 -> 二进制 0b0000 = 0x0 // 步1 (nibble1): 采样温度传感器,单端,无中断,需TS位 -> 二进制 0b1000 = 0x8 (TS=1) // 步0 (nibble0): 采样AIN1,单端,无中断 -> 二进制 0b0000 = 0x0 // 32位寄存器值 = (0x6 << 12) | (0x0 << 8) | (0x8 << 4) | (0x0 << 0) = 0x00000680 HWREG(ADC0_BASE + ADC_O_SSCTL1) = 0x680;避坑指南1:
END位必须且只能在一个序列的最后一个采样步设置。如果你在8步的序列中只在第5步设置了END,那么序列执行完第5步后就会停止,第6、7步不会被采样。如果你完全忘了设置END位,序列器会在执行完所有步后进入不可预测的状态,可能导致后续触发无法正常启动新序列。这是一个非常常见的错误来源。
避坑指南2:
TS位和D位是互斥的吗?从逻辑上看,一个采样步要么是单端输入(包括温度传感器),要么是差分输入。技术手册并未明确说明同时设置TS=1和D=1的行为,但根据常识和硬件设计惯例,这很可能导致未定义行为。因此,在配置时,应确保每个nibble中TS和D不同时为1。
4. 从零构建一个完整的ADC采样应用
理论说得再多,不如一行代码。下面我们以一个实际应用场景为例,演示如何从初始化到数据读取,完整地配置并使用ADC采样序列。
场景:我们需要用LM3S2965同时监测系统电压(通过分压接在AIN0)和芯片温度(内部传感器),并以1Hz的频率(每秒一次)进行采样。我们选择使用SS0序列器,因为它深度足够。使用定时器触发,并采用中断方式读取数据。
4.1 模块初始化与时钟使能
这是所有操作的前提,顺序不能错。
#include "hw_types.h" #include "hw_memmap.h" #include "sysctl.h" #include "adc.h" void ADC_Init(void) { // 1. 使能ADC0模块的时钟(LM3S2965的ADC模块编号为0) // RCGC0寄存器的第16位对应ADC0 HWREG(SYSCTL_RCGC0) |= SYSCTL_RCGC0_ADC0; // 2. 插入少量延时,确保时钟稳定。至少等待3个系统周期,这里用循环保守处理。 // 通常调用一个简单的延时函数或插入几个NOP指令。 volatile uint32_t ui32Loop; for(ui32Loop = 0; ui32Loop < 16; ui32Loop++) { } // 3. (可选但推荐) 复位ADC模块,使其处于已知状态。通过RCGC0寄存器的ADC0复位位实现。 HWREG(SYSCTL_RCGC0) |= SYSCTL_RCGC0_ADC0RST; // 置位复位位 for(ui32Loop = 0; ui32Loop < 16; ui32Loop++) { } HWREG(SYSCTL_RCGC0) &= ~(SYSCTL_RCGC0_ADC0RST); // 清除复位位 for(ui32Loop = 0; ui32Loop < 16; ui32Loop++) { } }4.2 配置采样序列器SS0
我们将配置SS0执行两个采样步:第一步采AIN0(系统电压),第二步采内部温度传感器。完成后触发中断。
void ADC_Sequence0_Config(void) { // 第一步:在配置前,先禁用序列器0。这是一个非常重要的安全操作! // 防止配置过程中被意外触发,导致读取到半截的、错误的配置。 HWREG(ADC0_BASE + ADC_O_ACTSS) &= ~ADC_ACTSS_ASEN0; // 第二步:配置触发源。我们选择定时器触发(假设使用定时器0)。 // ADCEMUX寄存器中,为SS0选择触发源的位域是bit[3:0]。 // 0x0: 处理器(软件)触发, 0x1: 模拟比较器0,... 具体值需查手册。 // 假设定时器0触发对应的值为0x5。 HWREG(ADC0_BASE + ADC_O_EMUX) = (HWREG(ADC0_BASE + ADC_O_EMUX) & ~ADC_EMUX_EM0_M) | (0x5 << ADC_EMUX_EM0_S); // 第三步:配置采样输入通道(ADCSSMUX0)。 // 采样步0 (bit[3:0]): AIN0,对应值 0。 // 采样步1 (bit[7:4]): 内部温度传感器,对应值 8。 // 因为我们只用了两步,所以高位nibble保持为0即可。 HWREG(ADC0_BASE + ADC_O_SSMUX0) = (8 << 4) | (0 << 0); // 值为 0x80 // 第四步:配置采样控制(ADCSSCTL0)。 // 采样步1 (nibble1): 温度传感器,需要TS位=1。作为序列最后一步,需要END=1。我们也希望转换完成后中断,所以IE=1。 // 因此 nibble1 = TS(1) + IE(1) + END(1) + D(0) = 二进制 1101 = 0xD。 // 采样步0 (nibble0): AIN0,单端输入,非最后一步,无需中断。所以值为0。 HWREG(ADC0_BASE + ADC_O_SSCTL0) = (0xD << 4) | (0x0 << 0); // 值为 0xD0 // 第五步:使能SS0的中断。 HWREG(ADC0_BASE + ADC_O_IM) |= ADC_IM_MASK0; // 第六步:最后,使能序列器0本身。 HWREG(ADC0_BASE + ADC_O_ACTSS) |= ADC_ACTSS_ASEN0; }关键点分析:
- 禁用序列器再配置:这是铁律。
ADCACTSS.ASEN0位控制序列器状态机的运行。在它运行时修改其“程序”(MUX和CTL寄存器),后果是不可预知的。 - 触发源选择:
ADCEMUX寄存器为每个序列器独立选择触发源。除了定时器,常见的还有“始终触发”(Always),这会让序列器一旦就绪就连续运行,需谨慎使用,避免霸占ADC资源。 - 中断使能时机:我们在配置序列器之后、使能序列器之前使能中断(
ADCIM.MASK0)。这个顺序不是绝对的,但逻辑清晰:先准备好中断处理机制,再启动可能产生中断的设备。
4.3 中断服务程序与数据读取
当SS0序列完成(即第二步温度传感器转换完成,因为那一步我们设置了IE和END),ADCRIS.INR0位会被置1。由于我们使能了ADCIM.MASK0,所以ADCISC.IN0也会被置1,并向CPU申请中断。
// 假设已正确配置了NVIC,将ADC0中断向量指向此函数。 void ADC0_Sequence0_Handler(void) { uint32_t ui32Status; uint32_t ui32ADC0_Value, ui32Temp_Value; // 1. 读取中断状态并清除标志位 ui32Status = HWREG(ADC0_BASE + ADC_O_ISC); if(ui32Status & ADC_ISC_IN0) { // 2. 清除SS0的中断标志。写1清除。 HWREG(ADC0_BASE + ADC_O_ISC) = ADC_ISC_IN0; // 3. 从FIFO中读取数据。读取顺序必须与采样顺序一致! // 先读出的是采样步0的结果(AIN0),然后是采样步1的结果(温度传感器)。 ui32ADC0_Value = HWREG(ADC0_BASE + ADC_O_SSFIFO0); // 读取并弹出FIFO0中最早的数据 ui32Temp_Value = HWREG(ADC0_BASE + ADC_O_SSFIFO0); // 再次读取,得到下一个数据 // 4. 数据处理 // ADC结果为10位有效,存储在读取值的低10位(bit[9:0])。 ui32ADC0_Value &= 0x3FF; ui32Temp_Value &= 0x3FF; // 将ADC值转换为电压(假设3.3V参考电压,实际LM3S2965 ADC内部参考为3V,需根据电路确认) // float fVoltage = (ui32ADC0_Value / 1023.0) * 3.3f; // 温度传感器转换公式较为复杂,需参考数据手册中的典型曲线进行计算,此处略。 // ... } // 注意:如果使能了多个序列器中断,需要在此检查并处理其他中断标志。 }避坑指南3:FIFO读取的“坑”。
ADCSSFIFOn寄存器是只读的,且每次读操作都会从FIFO中“弹出”一个数据。你必须严格按照采样步的顺序和数量来读取。如果你在中断里只读了一次,但序列采集了8个点,那么剩下的7个数据会留在FIFO里。当下一次序列完成时,新的数据会追加在后面,导致FIFO溢出(ADCOSTAT.OV0置位)或数据顺序混乱。一个稳健的做法是,在中断中根据ADCSSFSTATn寄存器的CNT字段(数据个数)来循环读取,直到FIFO为空。
4.4 启动采样:软件触发与硬件触发
配置好后,如何启动第一次采样?
- 软件触发:通过写
ADCPSSI寄存器。例如,启动SS0:
写这个寄存器会立即向指定的序列器发送一个触发信号。这对于单次采集或初始化后的首次采集非常有用。HWREG(ADC0_BASE + ADC_O_PSSI) |= ADC_PSSI_SS0; - 硬件触发:在我们这个例子中,配置的是定时器触发。你需要配置对应的定时器(例如Timer0),使其以1Hz的频率产生触发输出(通常是ADC触发信号)。一旦定时器事件发生,ADC模块会自动启动SS0序列,无需软件干预。这是实现周期性自动采样的标准方法。
5. 高级功能与实战技巧
掌握了基本流程后,我们再来探讨几个提升应用可靠性和性能的高级功能。
5.1 硬件采样平均(ADCSAC寄存器)
噪声是ADC的大敌。Stellaris ADC内置的硬件平均器可以在不增加CPU负担的情况下有效抑制噪声。通过配置ADCSAC寄存器,你可以让硬件自动对多次转换结果进行累加平均,然后将一个平均值存入FIFO。
ADCSAC寄存器低3位(AVG位域)控制平均次数:
000: 不平均(默认)001: 2次平均010: 4次平均011: 8次平均100: 16次平均101: 32次平均110: 64次平均
配置示例:启用4次硬件平均。
HWREG(ADC0_BASE + ADC_O_SAC) = 0x2; // AVG = 010重要影响:
- 吞吐量下降:平均N次,则输出一个有效数据的时间变为原来的N倍。例如,1Msps的ADC,在64次平均下,有效采样率会降至约15.625ksps。
- 精度提升:平均能提高信噪比(SNR),理论提升幅度为10*log10(N) dB。对于量化噪声和随机白噪声效果显著。
- 适用于所有通道:平均器是全局的,对所有序列器和所有通道生效。
5.2 差分采样模式
当需要测量微小电压差或抑制共模噪声时,差分采样模式非常有用。在Stellaris ADC上,差分输入固定为成对使用:
- 差分对0:AIN0(正端), AIN1(负端)
- 差分对1:AIN2(正端), AIN3(负端)
配置步骤:
- 在
ADCSSMUXn中,将对应采样步的nibble设置为差分对编号(0或1),而不是单个通道号。 - 在
ADCSSCTLn中,将对应nibble的D位(bit 0)置1。 - 转换结果的含义不同:0x1FF对应差分为0V。正值表示正端电压高于负端,负值(以10位有符号数理解)表示负端电压更高。具体换算需参考手册中的电压范围图。
示例:配置SS1的采样步0为差分模式,测量AIN2与AIN3的电压差。
// 配置MUX:选择差分对1 (AIN2, AIN3) HWREG(ADC0_BASE + ADC_O_SSMUX1) = (1 << 0); // nibble0 = 1 // 配置CTL:使能差分模式(D0=1),假设不需要中断和结束标志。 HWREG(ADC0_BASE + ADC_O_SSCTL1) = (1 << 0); // nibble0 = 0x1 (D0=1)5.3 多序列器优先级与并发处理(ADCSSPRI寄存器)
当多个触发事件几乎同时到来时,ADCSSPRI寄存器决定了哪个序列器优先使用ADC转换器。优先级数值0最高,3最低。默认优先级是SS0=0, SS1=1, SS2=2, SS3=3。
应用场景:假设系统中有两个关键任务:一个高速电机控制环(需要快速ADC响应,用SS0),一个慢速温度监控(用SS1)。为了避免温度监控任务阻塞电机控制,可以将SS0设为最高优先级(0),SS1设为较低优先级(2)。
// 设置SS0优先级为0(最高),SS1优先级为2, SS2为1, SS3为3。 // ADCSSPRI寄存器中,每8位为一个序列器的优先级字段。 // bit[31:24]: SS3, bit[23:16]: SS2, bit[15:8]: SS1, bit[7:0]: SS0。 HWREG(ADC0_BASE + ADC_O_SSPRI) = (3 << 24) | (1 << 16) | (2 << 8) | (0 << 0);警告:技术手册明确指出,不要将两个活跃序列器设置为相同的优先级,否则会导致不可预测的行为。软件必须确保所有使能的序列器优先级唯一。
5.4 诊断与调试技巧
- 检查FIFO状态:在读取数据前或中断服务程序中,先查看
ADCSSFSTATn寄存器。FULL和EMPTY位指示FIFO状态,CNT字段(bit[3:0])告诉你FIFO中有几个数据待读。这能有效避免读空或溢出。 - 处理溢出/下溢:定期检查
ADCOSTAT和ADCUSTAT寄存器。如果发生溢出(OVx=1),说明FIFO已满但还有新数据要写入,最新数据被丢弃。这通常是因为CPU读取FIFO的速度跟不上ADC生产数据的速度。你需要优化中断响应时间,或降低采样率。清除标志位只需向对应位写1。 - 使用测试模式(ADCTMLB):在开发阶段,尤其是编写和调试ADC驱动时,可能没有实际的模拟信号输入。你可以启用数字回环测试模式。通过配置
ADCTMLB寄存器,ADC模块会内部生成测试数据填入FIFO,而不需要实际进行模数转换。这可以验证你的配置、中断和服务程序逻辑是否正确,是纯软件调试的利器。
6. 常见问题排查与实战心得
在实际项目中,ADC配置不出数据或数据异常是家常便饭。下面是一些我踩过的“坑”和解决方法。
问题1:配置了所有寄存器,但ADC完全没反应,读FIFO总是0。
- 检查清单:
- 时钟使能了吗?确认
RCGC0的ADC0位已置1,并且等待了足够延时(>3系统时钟)。这是最容易被忽略的第一步。 - 序列器使能了吗?确认
ADCACTSS.ASENx位在你配置完成后被置1。 - GPIO模拟功能配置了吗?对于外部模拟输入引脚(如AIN0),除了配置为ADC功能,必须将其对应的GPIO数字输入功能禁用(清除
GPIODEN寄存器中相应位)。否则,数字输入缓冲器可能会钳位或干扰模拟信号。 - 触发发生了吗?如果是软件触发,确认写了
ADCPSSI寄存器。如果是硬件触发(如定时器),用示波器或调试器确认触发信号是否真的到达了ADC模块。
- 时钟使能了吗?确认
问题2:ADC读数不稳定,跳动很大。
- 排查方向:
- 电源与地噪声:ADC的模拟电源(
AVDD)和数字电源(DVDD)最好分开,并使用磁珠或0Ω电阻隔离。在AVDD和地之间靠近芯片引脚处放置10uF和0.1uF的退耦电容。 - 参考电压噪声:如果使用外部参考电压,确保其干净、稳定。LM3S2965使用内部3V参考,相对稳定,但也要确保模拟电源干净。
- 输入信号阻抗:ADC输入端对动态阻抗有要求。如果信号源阻抗过高(如>10kΩ),采样保持电容可能无法在采样时间内充放电到稳定值。需要在输入端并联一个小电容(如100pF)或使用运放进行缓冲。
- 启用硬件平均:如5.1节所述,启用4次或8次硬件平均能显著平滑读数,代价是采样率下降。
- 检查采样时间:虽然Stellaris ADC的采样时间通常是固定的,但在某些高阻抗源下可能不足。确保信号在ADC采样窗口内已稳定。
- 电源与地噪声:ADC的模拟电源(
问题3:多序列��同时工作时,低优先级序列器的数据偶尔丢失。
- 原因分析:这很可能是因为高优先级序列器被频繁触发(例如配置了“Always”触发),几乎霸占了ADC资源,导致低优先级序列器始终得不到执行机会,其FIFO因超时未读而溢出。
- 解决方案:
- 审查触发源配置,避免不必要的“Always”触发。
- 合理设置优先级,确保关键任务序列器优先级高,但非关键任务也不能被完全“饿死”。
- 增加低优先级序列器FIFO的深度(如果可能),或者提高CPU读取其FIFO的频率。
- 在中断服务程序中,不仅检查本序列器的中断,也定期轮询其他低优先级序列器的
ADCSSFSTATn寄存器,及时取走数据。
问题4:差分采样结果与万用表测量值对不上。
- 关键检查点:
- 共模电压范围:差分输入的两端,其电压必须各自在0V到3V(AVDD)之间。即使差值很小,如果任一端的绝对电压超出了这个范围,内部电路会将其钳位到0V或3V,导致转换错误。
- 极性:记住差分对是(偶数通道为正,奇数通道为负)。测量
V(AIN0) - V(AIN1),结果0x1FF对应0V差。 - 换算公式:差分模式下的数字输出
D_out与电压差V_diff的关系不是简单的线性。需要参考手册中的图表(如图11-2, 11-3, 11-4)。基本关系是:V_diff = (V_pos - V_neg) = (D_out - 0x1FF) * (V_ref / 1024),但前提是V_pos和V_neg都在有效输入范围内。
个人心得:Stellaris的ADC序列器架构非常强大,但与之对应的是较高的配置复杂度。最好的开发习惯是模块化和增量测试。先写一个最简单的单通道、软件触发、轮询读取的函数,确保基础通路是通的。然后逐步增加功能:改为中断读取、添加第二个通道、改为硬件触发、启用硬件平均、配置差分模式。每增加一个功能,就测试一次,这样当问题出现时,你能快速定位到是哪个新引入的配置导致的。最后,善用芯片的仿真和调试功能,在IDE中观察关键寄存器的值,比盲目修改代码要高效得多。