44

IMXRT学习记录 – ADC家族

 4 years ago
source link: https://www.taterli.com/6781/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

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框图(基本不用怎么去理解他,看得多不代表功能多): C:%5CUsers%5CTaterLi%5CAppData%5CLocal%5CYNote%5Cdata%5Cdeltass@163.com%5C5bb3c42fd99341dcb03ede888e56775d%5Cclipboard.png

yAnmaa2.png!web

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 << 1;
  adcEtcTriggerChainConfig.ADCChannelSelect = 14;
  adcEtcTriggerChainConfig.InterruptEnable = kADC_ETC_Done0InterruptEnable;
  adcEtcTriggerChainConfig.enableIrq = true;
  ADC_ETC_SetTriggerChainConfig(ADC_ETC, 0U, 1U, &adcEtcTriggerChainConfig);

  EnableIRQ(ADC_ETC_IRQ0_IRQn);

  /* Start PIT channel0. */
  PIT->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可以选择不同的通道,中断发起.

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK