This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

[参考译文] TM4C123GH6PM:ADC 正确读取一次、随后读数高得多

Guru**** 2610745 points


请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

https://e2e.ti.com/support/microcontrollers/arm-based-microcontrollers-group/arm-based-microcontrollers/f/arm-based-microcontrollers-forum/761505/tm4c123gh6pm-adc-reads-correctly-once-then-is-much-higher-subsequently

器件型号:TM4C123GH6PM

我花了很多时间来调试这个问题。 让我快速解释一下、发布源代码并等待进一步的帮助:

我让 ADC0和 ADC1分别从引脚 PE0和 PB5读取样本。 当我单步执行环路时、您将在下面找到 ADC0 (PE0)结果、这正是我所期望的结果、 但是、在第一次通过循环期间、我观察到4位"下溢"标志被置位(ADC0 - USTAT)、并且所有后续调用返回一个大约0x1FF 过高的结果。 这不是预期结果的两倍、而是其他结果。

下图显示了我期望0x234作为十六进制(int((((VOUT/3/3)*4096))=0x234的结果。 通常、第一次通过循环时、我得到0x22D - 0x236。 后续结果介于0x41D 和0x429之间(不是预期结果的两倍)。 PE0上出现的电压经 o 示波器验证、符合预期(~0.456V)-其中预期结果为 VOUT = VIN*(R2/(R1+R2))= 0x4545V。

下面是进入 PE0的电阻分压器的简单示意图:

---输入电压(来自 USB 的5.0V)---

          R1 = 10K

          -------------- (VOUT) PE0

         R2 = 1K

------ GND--------

/******** //*
任务 */
******** /
空 BatteryMonitor (UArg arg0、UArg arg1)
{
uint32_t u32电压;
uint32_t u32Light;
ERROR_Block ebBatMonAccess;

int32_t 调试;

//任务 Hello
debugPrint ("BatteryMonitor Task started\n");

/*构造要用作资源锁定的信标对象,初始计数1 */
/*获取实例句柄*/
error_init (&ebBatMonAccess);
semBatMonAccess = semaphore_create (1、NULL、&ebBatMonAccess);
if (semBatMonAccess =NULL){
system_abort ("错误:BatteryMonitor:信标创建失败");
}

InitMonitorChannel();

debugPrint ("BatteryMonitor 任务初始化完成\n");

//任务循环
while (true){

//
//触发 ADC 转换。
//
ADCProcessorTrigger (ADC0_BASE、3);//电压
//
//等待采样序列完成
//
while (!ADCIntStatus (ADC0_BASE、3、false){}

//
//读取 ADC 值。
//
调试= ADCSequenceDataGet (ADC0_BASE、3、&u32Voltage);

//来自 ADC.c - ADCIntClear ()说明
//! \note 由于 Cortex-M 处理器中有一个写入缓冲区、它可能会
//! 在实际清除中断源之前需要几个时钟周期。
ADCIntClear (ADC0_BASE、3);

debugPrint ("INFO:BatteryMonitor:ADCSequenceDataGet ()=%d、u32Voltage = 0x%x\n"、debug、u32Voltage);

//类似互斥的等待。 如果其他人正在访问、请勿访问
Semaphore_pend (semBatMonAccess、BIOS_wait_forever);
adcVoltage =(uint16_t)(u32Voltage & 0xFFFF);
Semaphore_post (semBatMonAccess);

//
//触发 ADC 转换。
//
ADCProcessorTrigger (ADC1_base、0);// Current
//
//等待采样序列完成
//
while (!ADCIntStatus (ADC1_base、0、false)){}
//
//读取 ADC 值。
//
ADCSequenceDataGet (ADC1_base、0、&u32Light);

//来自 ADC.c - ADCIntClear ()说明
//! \note 由于 Cortex-M 处理器中有一个写入缓冲区、它可能会
//! 在实际清除中断源之前需要几个时钟周期。
ADCIntClear (ADC1_BASE、0);

debugPrint ("INFO:BatteryMonitor:u32Light = 0x%x\n"、u32Light);

//类似互斥的等待。 如果其他人正在访问、请勿访问
Semaphore_pend (semBatMonAccess、BIOS_wait_forever);
adcLight =(uint16_t)(u32Light & 0xFFFF);
Semaphore_post (semBatMonAccess);

Task_sleep (ADC_sleep);
}
}/*void

ADC0_ISR (void)
{
ADC0_ISC_R |= 0x01;//写入一个以清除中断
}

void ADC1_ISR (void)
{
ADC1_ISC_R |= 0x01;//写入一个以清除中断
}*/


******** //*
帮助程序功能 */
******** /
static void InitMonitorChannel (void)
{
uint32_t 调试;

SysCtlPeripheralEnable (SYSCTL_Periph_ADC0);//这用于电池监控
while (!SysCtlPeripheralReady (SYSCTL_Periph_ADC0)){}

ADCClockConfigSet (ADC0_BASE、ADC_CLOCK_SRC_PIOSC | ADC_CLOCK_RATE_FULL、1);

GPIOPinTypeGPIOInput (GPIO_Porte _BASE、GPIO_PIN_0);
GPIOPinTypeADC (GPIO_Porte _BASE、GPIO_PIN_0);//电池电压监控

ADCReferenceSet (ADC0_BASE、ADC_REF_INT);
ADCSequenceDisable (ADC0_BASE、3);//电压监视器
ADCSequenceConfigure (ADC0_BASE、3、ADC_TRIGGER_PROCESSOR、0);//序列3上的电压监视器
ADCSequenceStepConfigure (ADC0_BASE、3、0、ADC_CTL_IE | ADC_CTL_END | ADC_CTL_CH3);//电压监视器
ADCSequenceEnable (ADC0_BASE、3);//电压监视器


SysCtlPeripheralEnable (SYSCTL_Periph_ADC1);
while (!SysCtlPeripheralReady (SYSCTL_Periph_ADC1)){}

ADCClockConfigSet (ADC1_base、ADC_CClock_SRC_PIOSC | ADC_CClock_RATE_FULL、1);

GPIOPinTypeGPIOInput (GPIO_PORTB_BASE、GPIO_PIN_5);
GPIOPinTypeADC (GPIO_PORTB_BASE、GPIO_PIN_5);//电流监控

ADCReferenceSet (ADC1_base、ADC_REF_INT);
ADCSequenceDisable (ADC1_base、0);//电压监视器
ADCSequenceConfigure (ADC1_base、0、ADC_TRIGGER_PROCESSOR, 0);//序列4上的电流监视器
ADCSequenceStepConfigure (ADC1_base、0、0、ADC_CTL_IE | ADC_CTL_END | ADC_CTL_CH11);//电流监视器
ADCSequenceEnable (ADC1_base、0);//电流监视器

//调试:
调试= ADCReferenceGet (ADC0_BASE);
开关(调试){
ADC_REF_INT 案例:
debugPrint ("debug:BatteryMonitor:ADCReferenceGet (ADC0_BASE)= ADC_REF_INT\n"\});
中断;
ADC_REF_EXT_3V 案例:
debugPrint ("debug:BatteryMonitor:ADCReferenceGet (ADC0_BASE)= ADC_REF_EXT_3V\n");
中断;
ADC_REF_EXT_1V 案例:
debugPrint ("debug:BatteryMonitor:ADCReferenceGet (ADC0_BASE)= ADC_REF_EXT_1V\n");
中断;
默认值:
debugPrint ("Debug:BatteryMonitor: ADCReferenceGet (ADC0_BASE)=超级四分制\n");
中断;
}

调试= ADCReferenceGet (ADC1_BASE);
开关(调试){
ADC_REF_INT 案例:
debugPrint ("Debug:BatteryMonitor: ADCReferenceGet (ADC1_base)= ADC_REF_INT\n");
中断;
ADC_REF_EXT_3V 案例:
debugPrint ("debug:BatteryMonitor:ADCReferenceGet (ADC1_base)= ADC_REF_EXT_3V\n");
中断;
ADC_REF_EXT_1V 案例:
debugPrint ("debug:BatteryMonitor:ADCReferenceGet (ADC1_base)= ADC_REF_EXT_1V\n");
中断;
默认值:
debugPrint ("Debug:BatteryMonitor: ADCReferenceGet (ADC1_base)=超级四分制\n");
中断;
}
} 

  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。
    看起来您使用的是 RTOS。 ADC0是否用于任何其他测量? 如果采样时间对于源阻抗而言太短、并且在测量0.456V 之前测量到更高的电压、则 ADC 内的采样电容器将没有足够的时间稳定至新电压。 在这种情况下、您可以降低源阻抗或增加采样时间。
  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。
    ADC0仅连接到 PE0 (AIN3)。 ADC1用于 PB5 (AIN11)。 PE0线仅连接到输入电源、目前是5V USB 线、因此稳定时间等问题不会出现、因为它永远不会以有意义的方式发生变化。
  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

    要拆分硬件/软件问题、是否可以运行所附的程序? 它只是从 PE0在 AIN3上进行连续转换。 它在 UART0、PA1上输出文本。 如果该引脚不可用、则应移除对 ConfigureUART 和 UARTprintf 的调用。

    /cfs-file/__key/communityserver-discussions-components-files/908/3660.ADCwDMA.zip

    使用 Code Composer 的"文件"""导入"功能将此项目添加到您的工作区中。

  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

    变量"average1"和"average2"通常位于0x24F 附近、这与我们预期的差不多。

    ADC = 0x24F
    vadc = 3.3*(ADC/4096.0)
    // vadc -> 0.47615V
    R1 = 10000.0
    R2 = 1000.0
    VIN = vadc*(R1+R2)/R2
    // VIN -> 5.238V 

  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。
    我使用示波器测量了"vadc"(原理图中的 AIN3)、并快速估算出它为~476mV。 因此、您的代码会在我的硬件上生成准确的读数。
  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

    我找到了误差的来源、尽管我并不完全理解它。

    我禁用了 TI-RTOS 中的所有其他任务、ADC 错误消失了。 我逐个重新启用它们、直到它重新出现。 此时、我发现导致 ADC 读数误差的确切行:

    GPIOPinTypeGPIOOutput (GPIO_Porte _BASE、GPIO_PIN_3);//注意:切勿将此线路驱动为高电平。 仅限开漏驱动 

    如果我在此行后立即将此任务锁定在 while 循环中、则 ADC 误差会显示出来。 正如您在注释中看到的、我本应该使用以下代码:

    GPIOPinTypeGPIOOutputOD (GPIO_Porte _BASE、GPIO_PIN_3);//注意:切勿将此线路驱动为高电平。 仅限开漏驱动 

    一旦我固定了这条线并解锁了该任务并允许其运行、项目的其余部分和 ADC 读数就会按预期运行。 之前、我在 EK_TM4C123GXL.c 中配置了该引脚、如下所示:

    GPIO_PinConfig gpioPinConfigs[]={
    …
    GPIOTIVA_PE_3 | GPIO_CFG_OUT_OD_NOPULL | GPIO_CFG_OUT_STR_MED | GPIO_CFG_OUT_HIGH、
    
    ...} 

    我可能无法正确使用旧示例代码,方法是在该结构中配置引脚以及使用 GPIOPinTypeGPIOOutputOD(),但我不确定为什么它会以这种方式从端口中的其他引脚抛出。

    无论采用哪种方法、代码现在都可以正常工作。

  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。
    Mark、您好!
    如果您在 PE3上发生驱动冲突、微控制器驱动为高电平(而不是开漏)、而外部电路驱动为低电平、这可能会导致芯片上本地3.3V 电源电压下降。 这反过来可能会将内部基准下拉、从而导致转换值高于预期值。
  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

    所以、我仍然有问题。 之前、我想我已经解决了问题、因为我发现我将 PE3驱动为高电平、作为输出、而不是开漏输出。 但是、当时我没有将无线电安装到电路板上。 现在、我执行了该操作、对讲机将 PE3拉高(它是对讲机的复位线路)、我可以成功将其配置为开漏。

    问题是、如果该线路处于高电平、则 PE0上的 ADC 读数错误。 我已经拔下电路板上的芯片、寻找短路或 PE3 (PIN6)上出现的3V 可能影响 PE0 (引脚9)上的 ADC 读数的任何可能方法。 电路板上的走线不会彼此接近、根据我的示波器、PE0 (AIN3)上出现的电压仍然正确。 因此、无论我是否安装了对讲机、PE0上出现的电压都不会改变。 无论在哪种情况下、都没关系。 但是、如果我安装了无线电、则 ADC 读数是错误的。

    我已将效果隔离到无线电的复位线路(PE3)。 我断开了线路、直到确定哪个引脚导致了错误。  此外、如果我通过按住以下各行来将无线电保持在复位状态、则不会发生错误。

    PE3处出现的电压如何影响 PE0处的 ADC 读数?

    #define PORT_RADIO_CONTROL GPIO_Porte _BASE
    #define PIN_RADIO_RESET GPIO_PIN_3
    
    GPIOPinTypeGPIOOutputOD (PORT_RADIO_CONTROL、PIN_RADIO_RESET);//注意:切勿将此线路驱动为高电平。 仅开漏驱动
    器 GPIOPinWrite (PORT_RADIO_CONTROL、PIN_RADIO_RESET、0);//将复位线下拉! 无 ADC 误差。 让它上升,它将会死亡! 

  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。
    是否确定对讲机未驱动该线路? 也许可以尝试添加一个小型串联电阻器、以便计算 PE3灌入的电流。
  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

    对讲机正在驱动该线路。 对讲机的重置线路上有一个上拉电阻。 它们的数据表表明、我们应该将其连接到物理开关接地、或者使用开漏输出(我正在执行此操作)驱动它。

    让我澄清一下 TI 文档中没有的内容:

    PE3配置为开漏输出后、如果我执行以下操作、该引脚将保持悬空(由无线电拉高):

    GPIOPinWrite (PORT_RADIO_CONTROL、PIN_RADIO_RESET、PIN_RADIO_RESET);//允许引脚悬空 

    根据我的示波器、该引脚处于具有该设置的高电压。 它还会使我的 ADC 混乱。

    示波器显示该引脚通过以下方式被拉至 GND:

    GPIOPinWrite (PORT_RADIO_CONTROL、PIN_RADIO_RESET、0);//拉到复位状态 

    我对开漏输出的理解是否正确? 您首先使用对其进行配置

    GPIOPinTypeGPIOOutputOD (PORT_RADIO_CONTROL、PIN_RADIO_RESET);//注意:切勿将此线路驱动为高电平。 仅限开漏驱动 

    然后、您要么让它浮动到它从外部驱动到的任何电压、要么可以通过开漏将它连接到 GND。 对吧? 如果是这样、我认为使用 GPIOPinWrite()会产生相反的影响、其中"1"会将其连接到 GND、"0"会使其浮动、但示波器显示了上述内容。

  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。
    否、写入1会使引脚漏极开路、从而允许将引脚上拉至高电平。 写入0会导致引脚驱动为低电平。
  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。
    嗯、这绝对不是我所看到的。 当启动调试器时、所有引脚在配置前都是三态的、复位线会按预期上拉。 但上面的代码就是我看到的代码。 查看寄存器、GPIO_Porte -> GPIO_ODR = 0x08且 GPIO_DIR = 0x3E、这意味着引脚3 (PE3)绝对配置为开漏输出、PE0 (AIN3)是输入。

    有什么想法我做了什么错? 我是否正确使用了上述功能、或者开漏输出是否需要其他功能?

    我在这方面花费了大量时间、我倾向于报废电路板并将 ADC 引脚移至 PORTB。
  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

    我不知道您的设置有什么问题。 我从 C:\ti\TivaWare_C_Series-2.1.4.178\examples\peripherals\ADC 中获取了示例文件"single_ended.c"、并将其更改为在 PE0上读取 AIN3。 然后、我将 PE3配置为开漏输出。 我在 PE3上有一个外部上拉电阻器。 代码在 PE0上执行 ADC 转换时将 PE3切换为低电平。 它在5uS 内变为低电平、转换每250ms 发生一次。 所有器件均按预期工作、PE3开漏输出和 PE0模拟输入之间无干扰。

    /cfs-file/__key/communityserver-discussions-components-files/908/single_5F00_ended.zip