[讨论] 【STM32H5开发板】ADC采集和设置测试、串口通讯测试

bigbat   2023-5-17 16:57 楼主

要测试STM32H5的ADC,首先参考一下例程ADC_SingleConversion_TriggerSW_IT,这是cube自带的例程,打开cube先看一下设置,例程使用了引脚PC0,这个脚和板子的A1相连接。STM32H5有两个ADC核心,设置为ADC1和CH10,引脚信号为单端信号Single-ended

image.png

引脚设置关键的是 时钟速率Clock Prescaler和Resolution,速率为4分频,分辨率为12,中断需要开启。转换组设置ADC_Regular_ConversionMode为软件开启,而不是使用硬件联动触发的方式。

/* Private variables ---------------------------------------------------------*/
ADC_HandleTypeDef hadc1;

/* USER CODE BEGIN PV */

/* Variables for ADC conversion data */
__IO uint16_t uhADCxConvertedData = VAR_CONVERTED_DATA_INIT_VALUE;; /* ADC group regular conversion data */

/* Variables for ADC conversion data computation to physical values */
uint16_t uhADCxConvertedData_Voltage_mVolt = 0;  /* Value of voltage calculated from ADC conversion data (unit: mV) */

/* Variable to report status of ADC group regular unitary conversion          */
/*  0: ADC group regular unitary conversion is not completed                  */
/*  1: ADC group regular unitary conversion is completed                      */
/*  2: ADC group regular unitary conversion has not been started yet          */
/*     (initial state)                                                        */
__IO uint8_t ubAdcGrpRegularUnitaryConvStatus = 2; /* Variable set into ADC interruption callback */

/* USER CODE END PV */

程序定义了一个ADC的转换变量uhADCxConvertedData,注意:这里必须声明为"__IO uint16_t",这个是为了让编译器不去优化该变量,直接去地址寄存器取值,这个在高速MCU中很关键,尤其在开启ICACHE的状态下

还有一个状态变量ubAdcGrpRegularUnitaryConvStatus,用来做转换标准。

转换程序的演示程序,

/* Perform ADC calibration */
//校准开始
  if (HAL_ADCEx_Calibration_Start(&hadc1, ADC_SINGLE_ENDED) != HAL_OK)
  {
    /* Calibration Error */
    Error_Handler();
  }

  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
    /* Start ADC group regular conversion */
    //转换开始
    if (HAL_ADC_Start_IT(&hadc1) != HAL_OK)
    {
      /* Error: ADC conversion start could not be performed */
      Error_Handler();
    }

    /* For this example purpose, wait until conversion is done */
    //等待转换结束,出结果
    while (ubAdcGrpRegularUnitaryConvStatus != 1);

    /* Reset status variable of ADC group regular unitary conversion */
    ubAdcGrpRegularUnitaryConvStatus = 0;

    /* Toggle LED at each ADC conversion */
    BSP_LED_On(LED1);
    HAL_Delay(LED_BLINK_SLOW);
    BSP_LED_Off(LED1);
    HAL_Delay(LED_BLINK_SLOW);

    /* Note: ADC group regular conversions data are stored into array         */
    /*       "uhADCxConvertedData"                                            */
    /*       (for debug: see variable content into watch window).             */

    /* Note: ADC conversion data are computed to physical values              */
    /*       into array "uhADCxConvertedData_Voltage_mVolt" using             */
    /*       ADC LL driver helper macro "__LL_ADC_CALC_DATA_TO_VOLTAGE()"     */
    /*       (for debug: see variable content with debugger)                  */
    /*       in IRQ handler callback function.                                */

    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
  }
  /* USER CODE END 3 */
}

过程很简单,先初始化ADC,在测试前先校准ADC,然后开始转换ADC测量,等待ADC转换结束,这个程序用了中断程序,

/**
  * [url=home.php?mod=space&uid=159083]@brief[/url] This function handles ADC1 global interrupt.
  */
void ADC1_IRQHandler(void)
{
  /* USER CODE BEGIN ADC1_IRQn 0 */

  /* Customize process using LL interface to improve the performance          */
  /* (exhaustive feature management not handled).                             */

  /* ########## Starting from this point HAL API must not be used ########### */

  /* Check whether ADC group regular end of unitary conversion caused         */
  /* the ADC interruption.                                                    */
  if(LL_ADC_IsActiveFlag_EOC(ADC1) != 0)
  {
    /* Clear flag ADC group regular end of unitary conversion */
    LL_ADC_ClearFlag_EOC(ADC1);

    /* Call interruption treatment function */
    AdcGrpRegularUnitaryConvComplete_Callback();
  }

  /* Check whether ADC group regular overrun caused the ADC interruption */
  if(LL_ADC_IsActiveFlag_OVR(ADC1) != 0)
  {
    /* Clear flag ADC group regular overrun */
    LL_ADC_ClearFlag_OVR(ADC1);

    /* Call interruption treatment function */
    AdcGrpRegularOverrunError_Callback();
  }

  /* USER CODE END ADC1_IRQn 0 */
  /* USER CODE BEGIN ADC1_IRQn 1 */

  /* USER CODE END ADC1_IRQn 1 */
}

这个有个奇怪的地方,就是程序中出现了LL_XXX的函数,可是设置却是HAL库呀,这个有点不明白

image.png

中断处理也很简单,首先判断是不是ADC1的"测量规则组"转换是否结束是则清除,完成后调用AdcGrpRegularUnitaryConvComplete_Callback函数,该函数中对主函数中的标志变量ubAdcGrpRegularUnitaryConvStatus进行设置,我对这个函数处理有点不明白,为啥不放在中断函数中处理,而是放到BSP包中处理?

判断是否是ADC1中断转换完成标志,是则清除。 

/**
  * @brief  ADC group regular end of unitary conversion interruption callback
  * @retval None
  */
void AdcGrpRegularUnitaryConvComplete_Callback()
{
  /* Retrieve ADC conversion data */
  uhADCxConvertedData = LL_ADC_REG_ReadConversionData32(ADC1);

  /* Computation of ADC conversions raw data to physical values           */
  /* using helper macro.                                                  */
  uhADCxConvertedData_Voltage_mVolt = __LL_ADC_CALC_DATA_TO_VOLTAGE(VDDA_APPLI, uhADCxConvertedData, LL_ADC_RESOLUTION_12B);

  /* Update status variable of ADC unitary conversion                     */
  ubAdcGrpRegularUnitaryConvStatus = 1;
}

/**
  * @brief  ADC group regular overrun interruption callback
  * @note   This function is executed when ADC group regular
  *         overrun error occurs.
  * @retval None
  */
void AdcGrpRegularOverrunError_Callback(void)
{
  /* Note: Disable ADC interruption that caused this error before entering in
           infinite loop below. */

  /* In case of error due to overrun: Disable ADC group regular overrun interruption */
  LL_ADC_DisableIT_OVR(ADC1);

  /* Error reporting */
  Error_Handler();
}

这两个函数很是疑惑,不过我知道工作过程也没有过多考虑。例程中没有输出变脸,接下来的任务就是通过串口输出ADC1的值了。

image.png  根据板子的设计,将串口输出到USART3_PD9,USART3_PD8。硬件就完成了,这个我是通过新建立的项目完成的,主要是将ADC的函数拷贝到项目中,

/**
  * @brief USART3 Initialization Function
  * @param None
  * @retval None
  */
static void MX_USART3_UART_Init(void)
{

  /* USER CODE BEGIN USART3_Init 0 */

  /* USER CODE END USART3_Init 0 */

  /* USER CODE BEGIN USART3_Init 1 */

  /* USER CODE END USART3_Init 1 */
  huart3.Instance = USART3;
  huart3.Init.BaudRate = 115200;
  huart3.Init.WordLength = UART_WORDLENGTH_8B;
  huart3.Init.StopBits = UART_STOPBITS_1;
  huart3.Init.Parity = UART_PARITY_NONE;
  huart3.Init.Mode = UART_MODE_TX_RX;
  huart3.Init.HwFlowCtl = UART_HWCONTROL_NONE;
  huart3.Init.OverSampling = UART_OVERSAMPLING_16;
  huart3.Init.OneBitSampling = UART_ONE_BIT_SAMPLE_DISABLE;
  huart3.Init.ClockPrescaler = UART_PRESCALER_DIV1;
  huart3.AdvancedInit.AdvFeatureInit = UART_ADVFEATURE_NO_INIT;
  if (HAL_UART_Init(&huart3) != HAL_OK)
  {
    Error_Handler();
  }
  if (HAL_UARTEx_SetTxFifoThreshold(&huart3, UART_TXFIFO_THRESHOLD_1_8) != HAL_OK)
  {
    Error_Handler();
  }
  if (HAL_UARTEx_SetRxFifoThreshold(&huart3, UART_RXFIFO_THRESHOLD_1_8) != HAL_OK)
  {
    Error_Handler();
  }
  if (HAL_UARTEx_DisableFifoMode(&huart3) != HAL_OK)
  {
    Error_Handler();
  }
  /* USER CODE BEGIN USART3_Init 2 */

  /* USER CODE END USART3_Init 2 */

}

将例程拷贝到例程中,然后是printf函数的设置,main.c中包含 #include <stdio.h>

#if defined(__ICCARM__)
/* New definition from EWARM V9, compatible with EWARM8 */
int iar_fputc(int ch);
#define PUTCHAR_PROTOTYPE int putchar(int ch)
#elif defined ( __CC_ARM ) || defined(__ARMCC_VERSION)
/* ARM Compiler 5/6*/
#define PUTCHAR_PROTOTYPE int fputc(int ch, FILE *f)
#elif defined(__GNUC__)
#define PUTCHAR_PROTOTYPE int __io_putchar(int ch)
#endif /* __ICCARM__ */

还有就是声明PUTCHAR_PROTOTYPE宏

/* USER CODE BEGIN 4 */
/**
  * @brief  Retargets the C library printf function to the USART.
  * @param  None
  * @retval None
  */
PUTCHAR_PROTOTYPE
{
  /* Place your implementation of fputc here */
  /* e.g. write a character to the USART1 and Loop until the end of transmission */
  HAL_UART_Transmit(&huart3, (uint8_t *)&ch, 1, 0xFFFF);

  return ch;
}
/* USER CODE END 4 */

还需要一个keil的设置 MicroLib

image.png  完成这些设置后就可以使用printf函数了。

在主函数中printf("mVolt=%d \n",uhADCxConvertedData_Voltage_mVolt);  

while (1)
  {
    /* USER CODE END WHILE */
		/* Start ADC group regular conversion */
    if (HAL_ADC_Start_IT(&hadc1) != HAL_OK)
    {
      /* Error: ADC conversion start could not be performed */
      Error_Handler();
    }

    /* For this example purpose, wait until conversion is done */
    while (ubAdcGrpRegularUnitaryConvStatus != 1);
    printf("mVolt=%d \n",uhADCxConvertedData_Voltage_mVolt);  
    /* Reset status variable of ADC group regular unitary conversion */
    ubAdcGrpRegularUnitaryConvStatus = 0;
		HAL_Delay(500);
    /* USER CODE BEGIN 3 */
  }

下面是输出的结果

image.png  测试中我使用了我的一个宝贝,信号发生器。ADC的性能和以往的ST差不多。

 

回复评论 (3)

跟着楼主学习STM32H5的两个ADC核心是怎么玩的

点赞  2023-5-18 07:20

必须声明为"__IO uint16_t",这个是为了让编译器不去优化该变量,编译器的功能强大

点赞  2023-5-20 11:33
引用: 火辣西米秀 发表于 2023-5-20 11:33 必须声明为"__IO uint16_t",这个是为了让编译器不去优化该变量,编译器的功能强大

STM32H5更加的应该注意,内部是有cache的,编译器很可能把某些不必要的变量给优化掉。

点赞  2023-5-20 11:41
电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 京公网安备 11010802033920号
    写回复