IMXRT学习记录 – ADC家族

ADC家族分有ADC和ADC_ETC,在很多时候他们是结合一起使用的.ADC就真的只是ADC,除了转换相关的功能以外,其他功能几乎没有.

ADC功能:

  • 15个ADC_IN输入引脚
  • 采样相关(8/10/12B采样)
  • 硬件平均数(4/8/16/32次平均)
  • 连续转换/DMA/结果覆盖/中断申请
  • 自动校正/硬件比较/偏移量自动

ADC_ETC功能:

  • 为ADC服务的模块,因为不同Trigger和Chain可能公用一个ADC,需要特别注意.
  • 从XBAR输入触发,支持4个TRIG,每个TRIG又支持8条Chain,Chain中可以发起DONEx中断/Chain完成中断.
  • 提供多个结果寄存器,支持DMA发起.
  • 支持软件触发开始某条Chain.

ADC框图(基本不用怎么去理解他,看得多不代表功能多):

ADC采样时间:

由于ADC模块是通过胶水的方式放在芯片内部的,所有时钟域不太一样,互相呼唤需要一些时间差,具体描述就像下图一样.计算公式有点复杂.根据上面的图来带代入参数.

  • SFCAdder : 前后增加的时间
    • (异步时钟)当ADACKEN = 0 并且 ADICLK = 11b 时,所需时间是1.5us + 4 ADCK + 2总线时钟,常规条件(即总线时钟很快,快到不是一个量级,比如125MHz IPG)下用2.1us代替.
    • (正常情况)当其他情况的时候,所需时间是4 ADCK + 2总线时钟,常规条件(即总线时钟很快,快到不是一个量级,比如125MHz IPG)下用0.6us代替.
  • LSTAdder : Long Time Sample Adder (单位ADCK)
  • BCT : 基本转换时间(固定) 8B=>17 ADCK,10B => 21 ADCK,12B => 25ADCK
  • 平均次数 : 1 / 4 /8 / 16 / 32
  • LSTAdder + BCT才算是实际采样时间.
    • 10MHz 时,每ADCK 0.1us.
    • (LSTAdder = 3) + (BCT = 17) = 20 ADCK ≈ 2us
  • 转换时间 = SFCAdder + 平均次数*(BCT+LSTAdder)
    • 最短单次转换 2.0us + 0.6us = 2.6us
    • 为了减少转换时间应该减少在不同工作域下切换.12B模式,最长采样条件下,32份平均,则采样时间和取出时间是160.6us (大约6.2Ksps)

ADC的配置基本没什么需要做的.

int16_t value = 0;

static void MainTask(void *pvParameters)
{
  adc_config_t adcConfigStrcut;
  adc_channel_config_t adcChannelConfigStruct;
  adc_offest_config_t adcOffsetStrcut;

  /* Set configuration */
  CLOCK_EnableClock(kCLOCK_Adc1);

  IOMUXC_SetPinConfig(
      IOMUXC_GPIO_AD_14_GPIOMUX_IO28, /* GPIO_AD_14 PAD functional properties : */
      0xA0U);                         /* Slew Rate Field: Slow Slew Rate
                                                 Drive Strength Field: R0/4
                                                 Speed Field: fast(150MHz)
                                                 Open Drain Enable Field: Open Drain Disabled
                                                 Pull / Keep Enable Field: Pull/Keeper Disabled
                                                 Pull / Keep Select Field: Keeper
                                                 Pull Up / Down Config. Field: 100K Ohm Pull Down
                                                 Hyst. Enable Field: Hysteresis Disabled */

  /*
     *  config->enableAsynchronousClockOutput = true;
     *  config->enableOverWrite =               false;
     *  config->enableContinuousConversion =    false;
     *  config->enableHighSpeed =               false;
     *  config->enableLowPower =                false;
     *  config->enableLongSample =              false;
     *  config->referenceVoltageSource =        kADC_ReferenceVoltageSourceVref;
     *  config->samplePeriodMode =              kADC_SamplePeriod2or12Clocks;
     *  config->clockSource =                   kADC_ClockSourceAD;
     *  config->clockDriver =                   kADC_ClockDriver1;
     *  config->resolution =                    kADC_Resolution12Bit;
     */
  ADC_GetDefaultConfig(&adcConfigStrcut);
  ADC_Init(ADC1, &adcConfigStrcut);
  ADC_SetHardwareAverageConfig(ADC1, kADC_HardwareAverageCount32);

  adcOffsetStrcut.enableSigned = true;
  adcOffsetStrcut.offsetValue = 2048;
  ADC_SetOffsetConfig(ADC1, &adcOffsetStrcut);
  ADC_DoAutoCalibration(ADC1);

  adcChannelConfigStruct.channelNumber = 14U;
  adcChannelConfigStruct.enableInterruptOnConversionCompleted = false;

  for (;;)
  {
    ADC_SetChannelConfig(ADC1, 0U, &adcChannelConfigStruct);
    while (0U == ADC_GetChannelStatusFlags(ADC1, 0U))
    {
    }
    value = ADC_GetChannelConversionValue(ADC1, 0U);
    if (value == 0)
    {
      vTaskDelay(pdMS_TO_TICKS(1));
    }
  }
}

如果用ADC_ETC则可以配置触发,拿最简单的PIT来说事.

  • 先定义一个30ms的PIT.
  • 把PIT输出连到到ADC触发输入.
  • 初始化ADC触发.
  • 初始化ADC_ETC等待发生事件.
  • (反复)中断出取出数据.
volatile uint32_t g_AdcConversionValue0;
volatile uint32_t g_AdcConversionValue1;

void ADC_ETC_IRQ0_IRQHandler(void)
{
  ADC_ETC_ClearInterruptStatusFlags(ADC_ETC, kADC_ETC_Trg0TriggerSource, kADC_ETC_Done0StatusFlagMask);
  g_AdcConversionValue0 = ADC_ETC_GetADCConversionValue(ADC_ETC, 0U, 0U); /* 通道CH7结果. */
  g_AdcConversionValue1 = ADC_ETC_GetADCConversionValue(ADC_ETC, 0U, 1U); /* 通道CH14结果. */
  __DSB();
}

static void PIT_Configuration()
{
  CLOCK_EnableClock(kCLOCK_Pit);

  PIT->MCR = 0x00;
  PIT->CHANNEL[0].LDVAL = 1499999;
}

void XBARA_Configuration(void)
{
  XBARA_Init(XBARA);
  XBARA_SetSignalsConnection(XBARA, kXBARA1_InputPitTrigger0, kXBARA1_OutputAdcEtcTrig00);
}

void ADC_Configuration(void)
{
  adc_config_t adcConfigStrcut;
  adc_channel_config_t adcChannelConfigStruct;

  CLOCK_EnableClock(kCLOCK_Adc1);

  IOMUXC_SetPinConfig(IOMUXC_GPIO_AD_07_GPIOMUX_IO21, 0xA0U);
  IOMUXC_SetPinConfig(IOMUXC_GPIO_AD_14_GPIOMUX_IO28, 0xA0U);

  ADC_GetDefaultConfig(&adcConfigStrcut);
  ADC_Init(ADC1, &adcConfigStrcut);
  ADC_SetHardwareAverageConfig(ADC1, kADC_HardwareAverageCount32);
  //使用ADC_ETC关键就是加入这一个,使能硬件触发.
  ADC_EnableHardwareTrigger(ADC1, true);

  //使用ADC_ETC时候,采样哪个通道就是ADC_ETC说了算了.
  adcChannelConfigStruct.channelNumber = 16U;
  adcChannelConfigStruct.enableInterruptOnConversionCompleted = false;
  ADC_SetChannelConfig(ADC1, 0, &adcChannelConfigStruct);
  ADC_SetChannelConfig(ADC1, 1, &adcChannelConfigStruct);

  ADC_DoAutoCalibration(ADC1);
}

void ADC_ETC_Configuration(void)
{
  adc_etc_config_t adcEtcConfig;
  adc_etc_trigger_config_t adcEtcTriggerConfig;
  adc_etc_trigger_chain_config_t adcEtcTriggerChainConfig;

  /* 初始化ADC_ETC */
  ADC_ETC_GetDefaultConfig(&adcEtcConfig);
  adcEtcConfig.XBARtriggerMask = 1U; /* 使能 kXBARA1_OutputAdcEtcTrig00 */
  ADC_ETC_Init(ADC_ETC, &adcEtcConfig);

  /* 配置触发动作. */
  adcEtcTriggerConfig.enableSyncMode = false;
  adcEtcTriggerConfig.enableSWTriggerMode = false;
  adcEtcTriggerConfig.triggerChainLength = 1; /* 整条链长度2,填1即表示长度2. */
  adcEtcTriggerConfig.triggerPriority = 0U;
  adcEtcTriggerConfig.sampleIntervalDelay = 0U;
  adcEtcTriggerConfig.initialDelay = 0U;
  ADC_ETC_SetTriggerConfig(ADC_ETC, 0U, &adcEtcTriggerConfig);

  /* 背对背模式,如果是True,不等待延迟,佛祖额等待延迟. */
  adcEtcTriggerChainConfig.enableB2BMode = true;

  /* (Chain 0/Trigger 0)配置ADC_HC0,采样CH7. */
  adcEtcTriggerChainConfig.ADCHCRegisterSelect = 1U << 0;
  adcEtcTriggerChainConfig.ADCChannelSelect = 07;
  ADC_ETC_SetTriggerChainConfig(ADC_ETC, 0U, 0U, &adcEtcTriggerChainConfig);

  /* (Chain 1/Trigger 0)配置ADC_HC1,采样CH14,结束时发起中断,然后可以一次性取出结果. */
  adcEtcTriggerChainConfig.ADCHCRegisterSelect = 1U <CHANNEL[0].TCTRL |= PIT_TCTRL_TEN(1);
}

static void MainTask(void *pvParameters)
{

  /* Set PERCLK_CLK source to OSC_CLK*/
  CLOCK_SetMux(kCLOCK_PerclkMux, 1U);
  /* Set PERCLK_CLK divider to 1 */
  CLOCK_SetDiv(kCLOCK_PerclkDiv, 0U);

  ADC_Configuration();
  XBARA_Configuration();
  PIT_Configuration();
  ADC_ETC_Configuration();
  for (;;)
  {
    vTaskDelay(pdMS_TO_TICKS(1000));
  }
}

要说明一个概念:

  • ADC_ETC包含4个Trigger,有各自优先级.
  • 1个Trigger包含8条Chain,按顺序采集下去,可以采集同一个通道.
  • 每条Chain可以选择不同的通道,中断发起.