关于ADC的差分输入模式

在配置ADC时,将ADC1 Channel1, ADC1 Channel2配置为单端模式

将ADC1 Channel6,和Channel7配置为差分模式的正负极输入。

单端模式读入的数据正确,但是差分模式读入的数据看不懂。。。

差分输入为0V时,读到的数据是十进制2050左右;

差分输入0.75V时,读到的数据是2513左右;

差分输入1.88V时,读到的数据是3213左右;

差分输入2.27V时,读到数据是3456左右。


AD的参考电压是3.3V。目前从上面的数据中,获得了这样的规律,以输入为0V是,获取的数据2050为基准,假设差分输入电压为Uin,读取到的数据为Data则:

Uin-0=((Data-2050)/4095)*3.3*2

现在的疑惑是,为什么会是这样?

已邀请:

牙子男男

赞同来自:

找到原因了!!!

当ADC配置为单端模式时,GPIO的输入电压范围是0-Vref+,一般就是0-3.3V。分辨率为12位的话,转化结果为0-4095。

当ADC配置为单端模式时,两个差分电压输入引脚的电压差范围可以是-Vref+ 到Vref+。 也就是说,可以检测-3.3到3.3v的差分电压。

我目前的猜测是,将4096/2=2048这个值,作为基准点,也就是电压0点。大于这个值得,就是正电压;小于这个值的,就是负电压。这种情况下,其实0-3.3V的分辨率只有2^11,也就是2048了。于是,输入的差分电压Uin与转换结果Data计算如下:

Uin = 3.3*(Data-2047)/2047;

Data = (2047*Uin/3.3)+2047;    这里具体是用2048还是2047,我没仔细去想。问题应该不大。

测试差分输入为-0.65V时,理论计算的结果应是1643.8:转换结果为1644。

tmooc

赞同来自:

大佬,差分和单端计算方式一样啊,不都是测量的AD值*3.3除以65535么?你看我的测量值和代码微信图片_20190301105356.png

#include "main.h"

#include "stm32f3xx_hal.h"


uint32_t uhADCxConvertedValue = 0;


SDADC_HandleTypeDef hsdadc1;


void SystemClock_Config(void);

static void MX_GPIO_Init(void);

static void MX_SDADC1_Init(void);


int main(void)

{

  HAL_Init();


  SystemClock_Config();


  MX_GPIO_Init();

  MX_SDADC1_Init();


  HAL_SDADC_AssociateChannelConfig(&hsdadc1, SDADC_CHANNEL_6, SDADC_CONF_INDEX_0);      

  HAL_SDADC_ConfigChannel(&hsdadc1, SDADC_CHANNEL_6, SDADC_CONTINUOUS_CONV_OFF);                       

  HAL_SDADC_SelectRegularTrigger(&hsdadc1, SDADC_SOFTWARE_TRIGGER);                   

  HAL_SDADC_CalibrationStart(&hsdadc1, SDADC_CALIBRATION_SEQ_1);                      

  HAL_SDADC_PollForCalibEvent(&hsdadc1, HAL_MAX_DELAY);                          


  while (1)

  {

    HAL_SDADC_Start(&hsdadc1);                                                                 //此功能允许在轮询模式下开始常规转换。

    HAL_SDADC_PollForConversion(&hsdadc1,HAL_MAX_DELAY);                                       //此功能允许轮询常规转换的结束

    uhADCxConvertedValue=HAL_SDADC_GetValue(&hsdadc1);        

    HAL_Delay(1000);

  }

}


void SystemClock_Config(void)

{


  RCC_OscInitTypeDef RCC_OscInitStruct;

  RCC_ClkInitTypeDef RCC_ClkInitStruct;

  RCC_PeriphCLKInitTypeDef PeriphClkInit;


  RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;

  RCC_OscInitStruct.HSEState = RCC_HSE_ON;

  RCC_OscInitStruct.PLL.PLLState = RCC_PLL_NONE;

  if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)

  {

    _Error_Handler(__FILE__, __LINE__);

  }


  RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK

                              |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;

  RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_HSE;

  RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;

  RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1;

  RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;


  if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_0) != HAL_OK)

  {

    _Error_Handler(__FILE__, __LINE__);

  }


  PeriphClkInit.PeriphClockSelection = RCC_PERIPHCLK_SDADC;

  PeriphClkInit.SdadcClockSelection = RCC_SDADCSYSCLK_DIV2;

  if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInit) != HAL_OK)

  {

    _Error_Handler(__FILE__, __LINE__);

  }


  HAL_PWREx_EnableSDADC(PWR_SDADC_ANALOG1);


  HAL_SYSTICK_Config(HAL_RCC_GetHCLKFreq()/1000);


  HAL_SYSTICK_CLKSourceConfig(SYSTICK_CLKSOURCE_HCLK);


  HAL_NVIC_SetPriority(SysTick_IRQn, 0, 0);

}



static void MX_SDADC1_Init(void)

{


  SDADC_ConfParamTypeDef ConfParamStruct;


  hsdadc1.Instance = SDADC1;

  hsdadc1.Init.IdleLowPowerMode = SDADC_LOWPOWER_NONE;

  hsdadc1.Init.FastConversionMode = SDADC_FAST_CONV_DISABLE;

  hsdadc1.Init.SlowClockMode = SDADC_SLOW_CLOCK_DISABLE;

  hsdadc1.Init.ReferenceVoltage = SDADC_VREF_EXT;

  if (HAL_SDADC_Init(&hsdadc1) != HAL_OK)

  {

    _Error_Handler(__FILE__, __LINE__);

  }


  ConfParamStruct.InputMode = SDADC_INPUT_MODE_DIFF;

  ConfParamStruct.Gain = SDADC_GAIN_1;

  ConfParamStruct.CommonMode = SDADC_COMMON_MODE_VSSA;

  ConfParamStruct.Offset = 0;

  if (HAL_SDADC_PrepareChannelConfig(&hsdadc1, SDADC_CONF_INDEX_0, &ConfParamStruct) != HAL_OK)

  {

    _Error_Handler(__FILE__, __LINE__);

  }


}


static void MX_GPIO_Init(void)

{

  __HAL_RCC_GPIOF_CLK_ENABLE();

  __HAL_RCC_GPIOB_CLK_ENABLE();

}


void _Error_Handler(char *file, int line)

{

  while(1)

  {

  }

}


#ifdef  USE_FULL_ASSERT

void assert_failed(uint8_t* file, uint32_t line)

}

#endif /* USE_FULL_ASSERT */


牙子男男

赞同来自:

仔细看一下SDADC的采样电压范围这里的介绍。

文档里提到采样范围和增益有关系。 

  1. 你程序里面配置增益是1,那么采样范围是-1.65V~1.65V(如果参考电压是3.3V)。

  2. 根据我的理解,差分模式的时候,增益是1/2的时候,采样后的转化范围是0-65535(2^16-1); 增益是1的时候,采样后的转化范围是0-32767(2^15-1)。

  3. 根据上面两点,你现在的配置,-1.65~1.65V转化后范围是0-32767。 0V的转化值是2^14-1=16383。

  4. 所以转化后的值A和输入差分电压B之间关系应该是:A-16383 = (B-0)*16384/1.65,也就是说A= (B*16384/1.65)+16383

但我发现,通过我这个公式计算出来的值和你转化后的值还是有出入,具体什么问题我也不清楚了,只能帮你到这儿了。

image.png

龚向东

赞同来自:

我感到很迷茫,我将STM32L4XX配置成差分模式,结果它的值在正值时,为2048~4096之间,为负值时,为0~2048之间。围绕这个2048为中心值,这和我想象中可不一样。我原先想正电压的时候,输出为0~2047之间,负的时候,输出的是补码,而且扩展到16位。 我用ADS1120就是这样子的。可为什么STM32怎么会这样呢?也没有例子,只好自已试了。要是这样的话,我用单端来转换,结果也一样。何必用差分呢?

要回复问题请先登录注册