千棵树 发表于 2018-4-2 14:49:23

STM32L476,CubeMx生成底层,AD转换误差较大,误差接近40mV。

一、主芯片选用STM32L476RCTx;
二、CubeMx版本为4.23;
三、配置如下:

butterflyspring 发表于 2018-4-2 14:49:24

建议参考一下例程,因为cubeMx只是生成底层库...

Repository\STM32Cube_FW_L4_V1.10.0\Projects\STM32L476G_EVAL\Examples\ADC\ADC_DMA_Transfer

/* ### - 2 - Start calibration ############################################ */
if (HAL_ADCEx_Calibration_Start(&AdcHandle, ADC_SINGLE_ENDED) !=HAL_OK)
{
    Error_Handler();
}

千棵树 发表于 2018-4-2 14:50:37

生成后的代码如下:
#include "adc.h"

#include "gpio.h"
#include "dma.h"

/* USER CODE BEGIN 0 */

/* USER CODE END 0 */

ADC_HandleTypeDef hadc1;
ADC_HandleTypeDef hadc2;
DMA_HandleTypeDef hdma_adc1;
DMA_HandleTypeDef hdma_adc2;

/* ADC1 init function */
void MX_ADC1_Init(void)
{
ADC_MultiModeTypeDef multimode;
ADC_ChannelConfTypeDef sConfig;

    /**Common config
    */
hadc1.Instance = ADC1;
hadc1.Init.ClockPrescaler = ADC_CLOCK_ASYNC_DIV2;
hadc1.Init.Resolution = ADC_RESOLUTION_12B;
hadc1.Init.DataAlign = ADC_DATAALIGN_RIGHT;
hadc1.Init.ScanConvMode = ADC_SCAN_ENABLE;
hadc1.Init.EOCSelection = ADC_EOC_SEQ_CONV;
hadc1.Init.LowPowerAutoWait = DISABLE;
hadc1.Init.ContinuousConvMode = DISABLE;
hadc1.Init.NbrOfConversion = 4;
hadc1.Init.DiscontinuousConvMode = DISABLE;
hadc1.Init.NbrOfDiscConversion = 1;
hadc1.Init.ExternalTrigConv = ADC_SOFTWARE_START;
hadc1.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_NONE;
hadc1.Init.DMAContinuousRequests = DISABLE;
hadc1.Init.Overrun = ADC_OVR_DATA_OVERWRITTEN;
hadc1.Init.OversamplingMode = DISABLE;
if (HAL_ADC_Init(&hadc1) != HAL_OK)
{
    _Error_Handler(__FILE__, __LINE__);
}

    /**Configure the ADC multi-mode
    */
multimode.Mode = ADC_MODE_INDEPENDENT;
if (HAL_ADCEx_MultiModeConfigChannel(&hadc1, &multimode) != HAL_OK)
{
    _Error_Handler(__FILE__, __LINE__);
}

    /**Configure Regular Channel
    */
sConfig.Channel = ADC_CHANNEL_5;
sConfig.Rank = 1;
sConfig.SamplingTime = ADC_SAMPLETIME_6CYCLES_5;
sConfig.SingleDiff = ADC_SINGLE_ENDED;
sConfig.OffsetNumber = ADC_OFFSET_NONE;
sConfig.Offset = 255;
if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)
{
    _Error_Handler(__FILE__, __LINE__);
}

    /**Configure Regular Channel
    */
sConfig.Channel = ADC_CHANNEL_6;
sConfig.Rank = 2;
if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)
{
    _Error_Handler(__FILE__, __LINE__);
}

    /**Configure Regular Channel
    */
sConfig.Channel = ADC_CHANNEL_7;
sConfig.Rank = 3;
if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)
{
    _Error_Handler(__FILE__, __LINE__);
}

    /**Configure Regular Channel
    */
sConfig.Channel = ADC_CHANNEL_8;
sConfig.Rank = 4;
if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)
{
    _Error_Handler(__FILE__, __LINE__);
}

}
/* ADC2 init function */
void MX_ADC2_Init(void)
{
ADC_ChannelConfTypeDef sConfig;

    /**Common config
    */
hadc2.Instance = ADC2;
hadc2.Init.ClockPrescaler = ADC_CLOCK_ASYNC_DIV2;
hadc2.Init.Resolution = ADC_RESOLUTION_12B;
hadc2.Init.DataAlign = ADC_DATAALIGN_RIGHT;
hadc2.Init.ScanConvMode = ADC_SCAN_ENABLE;
hadc2.Init.EOCSelection = ADC_EOC_SEQ_CONV;
hadc2.Init.LowPowerAutoWait = DISABLE;
hadc2.Init.ContinuousConvMode = DISABLE;
hadc2.Init.NbrOfConversion = 3;
hadc2.Init.DiscontinuousConvMode = DISABLE;
hadc2.Init.NbrOfDiscConversion = 1;
hadc2.Init.ExternalTrigConv = ADC_SOFTWARE_START;
hadc2.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_NONE;
hadc2.Init.DMAContinuousRequests = DISABLE;
hadc2.Init.Overrun = ADC_OVR_DATA_OVERWRITTEN;
hadc2.Init.OversamplingMode = DISABLE;
if (HAL_ADC_Init(&hadc2) != HAL_OK)
{
    _Error_Handler(__FILE__, __LINE__);
}

    /**Configure Regular Channel
    */
sConfig.Channel = ADC_CHANNEL_1;
sConfig.Rank = 1;
sConfig.SamplingTime = ADC_SAMPLETIME_12CYCLES_5;
sConfig.SingleDiff = ADC_SINGLE_ENDED;
sConfig.OffsetNumber = ADC_OFFSET_NONE;
sConfig.Offset = 0;
if (HAL_ADC_ConfigChannel(&hadc2, &sConfig) != HAL_OK)
{
    _Error_Handler(__FILE__, __LINE__);
}

    /**Configure Regular Channel
    */
sConfig.Channel = ADC_CHANNEL_2;
sConfig.Rank = 2;
if (HAL_ADC_ConfigChannel(&hadc2, &sConfig) != HAL_OK)
{
    _Error_Handler(__FILE__, __LINE__);
}

    /**Configure Regular Channel
    */
sConfig.Channel = ADC_CHANNEL_3;
sConfig.Rank = 3;
sConfig.SamplingTime = ADC_SAMPLETIME_24CYCLES_5;
if (HAL_ADC_ConfigChannel(&hadc2, &sConfig) != HAL_OK)
{
    _Error_Handler(__FILE__, __LINE__);
}

}

static uint32_t HAL_RCC_ADC_CLK_ENABLED=0;

void HAL_ADC_MspInit(ADC_HandleTypeDef* adcHandle)
{

GPIO_InitTypeDef GPIO_InitStruct;
if(adcHandle->Instance==ADC1)
{
/* USER CODE BEGIN ADC1_MspInit 0 */

/* USER CODE END ADC1_MspInit 0 */
    /* ADC1 clock enable */
    HAL_RCC_ADC_CLK_ENABLED++;
    if(HAL_RCC_ADC_CLK_ENABLED==1){
      __HAL_RCC_ADC_CLK_ENABLE();
    }

    /**ADC1 GPIO Configuration   
    PA0   ------> ADC1_IN5
    PA1   ------> ADC1_IN6
    PA2   ------> ADC1_IN7
    PA3   ------> ADC1_IN8
    */
    GPIO_InitStruct.Pin = GPIO_PIN_0|GPIO_PIN_1|GPIO_PIN_2|GPIO_PIN_3;
    GPIO_InitStruct.Mode = GPIO_MODE_ANALOG_ADC_CONTROL;
    GPIO_InitStruct.Pull = GPIO_NOPULL;
    HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);

    /* ADC1 DMA Init */
    /* ADC1 Init */
    hdma_adc1.Instance = DMA1_Channel1;
    hdma_adc1.Init.Request = DMA_REQUEST_0;
    hdma_adc1.Init.Direction = DMA_PERIPH_TO_MEMORY;
    hdma_adc1.Init.PeriphInc = DMA_PINC_DISABLE;
    hdma_adc1.Init.MemInc = DMA_MINC_ENABLE;
    hdma_adc1.Init.PeriphDataAlignment = DMA_PDATAALIGN_WORD;
    hdma_adc1.Init.MemDataAlignment = DMA_MDATAALIGN_WORD;
    hdma_adc1.Init.Mode = DMA_CIRCULAR;
    hdma_adc1.Init.Priority = DMA_PRIORITY_MEDIUM;
    if (HAL_DMA_Init(&hdma_adc1) != HAL_OK)
    {
      _Error_Handler(__FILE__, __LINE__);
    }

    __HAL_LINKDMA(adcHandle,DMA_Handle,hdma_adc1);

    /* ADC1 interrupt Init */
    HAL_NVIC_SetPriority(ADC1_2_IRQn, 0, 0);
    HAL_NVIC_EnableIRQ(ADC1_2_IRQn);
/* USER CODE BEGIN ADC1_MspInit 1 */

/* USER CODE END ADC1_MspInit 1 */
}
else if(adcHandle->Instance==ADC2)
{
/* USER CODE BEGIN ADC2_MspInit 0 */

/* USER CODE END ADC2_MspInit 0 */
    /* ADC2 clock enable */
    HAL_RCC_ADC_CLK_ENABLED++;
    if(HAL_RCC_ADC_CLK_ENABLED==1){
      __HAL_RCC_ADC_CLK_ENABLE();
    }

    /**ADC2 GPIO Configuration   
    PC0   ------> ADC2_IN1
    PC1   ------> ADC2_IN2
    PC2   ------> ADC2_IN3
    */
    GPIO_InitStruct.Pin = GPIO_PIN_0|GPIO_PIN_1|GPIO_PIN_2;
    GPIO_InitStruct.Mode = GPIO_MODE_ANALOG_ADC_CONTROL;
    GPIO_InitStruct.Pull = GPIO_NOPULL;
    HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);

    /* ADC2 DMA Init */
    /* ADC2 Init */
    hdma_adc2.Instance = DMA1_Channel2;
    hdma_adc2.Init.Request = DMA_REQUEST_0;
    hdma_adc2.Init.Direction = DMA_PERIPH_TO_MEMORY;
    hdma_adc2.Init.PeriphInc = DMA_PINC_DISABLE;
    hdma_adc2.Init.MemInc = DMA_MINC_ENABLE;
    hdma_adc2.Init.PeriphDataAlignment = DMA_PDATAALIGN_WORD;
    hdma_adc2.Init.MemDataAlignment = DMA_MDATAALIGN_WORD;
    hdma_adc2.Init.Mode = DMA_CIRCULAR;
    hdma_adc2.Init.Priority = DMA_PRIORITY_HIGH;
    if (HAL_DMA_Init(&hdma_adc2) != HAL_OK)
    {
      _Error_Handler(__FILE__, __LINE__);
    }

    __HAL_LINKDMA(adcHandle,DMA_Handle,hdma_adc2);

    /* ADC2 interrupt Init */
    HAL_NVIC_SetPriority(ADC1_2_IRQn, 0, 0);
    HAL_NVIC_EnableIRQ(ADC1_2_IRQn);
/* USER CODE BEGIN ADC2_MspInit 1 */

/* USER CODE END ADC2_MspInit 1 */
}
}

void HAL_ADC_MspDeInit(ADC_HandleTypeDef* adcHandle)
{

if(adcHandle->Instance==ADC1)
{
/* USER CODE BEGIN ADC1_MspDeInit 0 */

/* USER CODE END ADC1_MspDeInit 0 */
    /* Peripheral clock disable */
    /* Be sure that all peripheral instances that share the same clock need to be disabled */
    /**HAL_RCC_ADC_CLK_ENABLED--;
    *if(HAL_RCC_ADC_CLK_ENABLED==0){
    *    __HAL_RCC_ADC_CLK_DISABLE();
    **/

    /**ADC1 GPIO Configuration   
    PA0   ------> ADC1_IN5
    PA1   ------> ADC1_IN6
    PA2   ------> ADC1_IN7
    PA3   ------> ADC1_IN8
    */
    HAL_GPIO_DeInit(GPIOA, GPIO_PIN_0|GPIO_PIN_1|GPIO_PIN_2|GPIO_PIN_3);

    /* ADC1 DMA DeInit */
    HAL_DMA_DeInit(adcHandle->DMA_Handle);

    /* ADC1 interrupt Deinit */
/* USER CODE BEGIN ADC1:ADC1_2_IRQn disable */
    /**
    * Uncomment the line below to disable the "ADC1_2_IRQn" interrupt
    * Be aware, disabling shared interrupt may affect other IPs
    */
    /* HAL_NVIC_DisableIRQ(ADC1_2_IRQn); */
/* USER CODE END ADC1:ADC1_2_IRQn disable */

/* USER CODE BEGIN ADC1_MspDeInit 1 */

/* USER CODE END ADC1_MspDeInit 1 */
}
else if(adcHandle->Instance==ADC2)
{
/* USER CODE BEGIN ADC2_MspDeInit 0 */

/* USER CODE END ADC2_MspDeInit 0 */
    /* Peripheral clock disable */
    /* Be sure that all peripheral instances that share the same clock need to be disabled */
    /**HAL_RCC_ADC_CLK_ENABLED--;
    *if(HAL_RCC_ADC_CLK_ENABLED==0){
    *    __HAL_RCC_ADC_CLK_DISABLE();
    **/

    /**ADC2 GPIO Configuration   
    PC0   ------> ADC2_IN1
    PC1   ------> ADC2_IN2
    PC2   ------> ADC2_IN3
    */
    HAL_GPIO_DeInit(GPIOC, GPIO_PIN_0|GPIO_PIN_1|GPIO_PIN_2);

    /* ADC2 DMA DeInit */
    HAL_DMA_DeInit(adcHandle->DMA_Handle);

    /* ADC2 interrupt Deinit */
/* USER CODE BEGIN ADC2:ADC1_2_IRQn disable */
    /**
    * Uncomment the line below to disable the "ADC1_2_IRQn" interrupt
    * Be aware, disabling shared interrupt may affect other IPs
    */
    /* HAL_NVIC_DisableIRQ(ADC1_2_IRQn); */
/* USER CODE END ADC2:ADC1_2_IRQn disable */

/* USER CODE BEGIN ADC2_MspDeInit 1 */

/* USER CODE END ADC2_MspDeInit 1 */
}
}

/* USER CODE BEGIN 1 */

/* USER CODE END 1 */

/**
* @}
*/

/**
* @}
*/

千棵树 发表于 2018-4-2 14:55:17

上楼程序和CubeMx中配置了ADC1和ADC2,时钟为12M经过了2分频。不管使用哪路ADC,均存在误差。main函数无他,直接DMA处理AD转换,间隔时间比较长。

千棵树 发表于 2018-4-2 14:59:00

相关硬件原理图如下:

千棵树 发表于 2018-4-2 15:04:26

问题如下:
1、使用12位的AD,误差接近50mV;
2、用示波器测试基准为3.28V(虽然没有3.3V但是3.28V还是比较稳,无干扰征兆);
3、示波器检测VDD,VSS均很稳定,无干扰现象;
4、测试单片机的AD输入端,TEMP1,也很平稳,没发现干扰;
5、用VSS直接并到TEMP1,AD转换结果为0x000;
6、用VDD并到TEMP1,测试的结果并不为OxFFF,而是0xFBE附近;

千棵树 发表于 2018-4-2 15:10:47

自己的一些疑问:
一、STM32F103系列的AD转换,存在一个校准的过程,也就是在程序刚开始必须进行自校准,然后开启AD转换。但是STM32L476用CubeMx生成的库函数,仔细查看后没有留下相关的校准函数,目前还有待思考该过程?
二、外围电路,包括基准和跟随还有阻抗匹配,没有发现不良现象,不知道有没有大神提供一下思路?
三、目前不清楚是硬件原因还是软件原因导致12位AD误差接近50mV.希望大神伸出援助之手!

千棵树 发表于 2018-4-2 16:42:29

千棵树 发表于 2018-4-2 15:10
自己的一些疑问:
一、STM32F103系列的AD转换,存在一个校准的过程,也就是在程序刚开始必须进行自校准,然 ...

我也是刚解决这个问题,上来一看,发现还是有校准过程。也就是这个函数。
另外可以直接看系统自带的库函数:HAL_StatusTypeDef       HAL_ADCEx_Calibration_Start(ADC_HandleTypeDef* hadc, uint32_t SingleDiff);
uint32_t                HAL_ADCEx_Calibration_GetValue(ADC_HandleTypeDef *hadc, uint32_t SingleDiff);
HAL_StatusTypeDef       HAL_ADCEx_Calibration_SetValue(ADC_HandleTypeDef *hadc, uint32_t SingleDiff, uint32_t CalibrationFactor);

wenyangzeng 发表于 2018-4-2 16:46:45

本帖最后由 wenyangzeng 于 2018-4-2 16:49 编辑

千棵树 发表于 2018-4-2 14:50
生成后的代码如下:
ADC_SAMPLETIME_6CYCLES_5
采样时间好像太短了,就4个输入ADC,统一使用ADC1设置4个不同通道就够了,

bargagebaobei 发表于 2018-4-2 16:49:01

((TIME) == ADC_SAMPLETIME_28CYCLES)
((TIME) == ADC_SAMPLETIME_56CYCLES)
((TIME) == ADC_SAMPLETIME_84CYCLES)
((TIME) == ADC_SAMPLETIME_112CYCLES)
((TIME) == ADC_SAMPLETIME_144CYCLES)
((TIME) == ADC_SAMPLETIME_480CYCLES)
可以放到最大
页: [1]
查看完整版本: STM32L476,CubeMx生成底层,AD转换误差较大,误差接近40mV。