1733 views|3 replies

2867

Posts

4

Resources
The OP
 

[STM32H5 development board] ADC acquisition and setting test, serial port communication test [Copy link]

To test the ADC of STM32H5, first refer to the routine ADC_SingleConversion_TriggerSW_IT, which is a routine that comes with cube. Open cube and take a look at the settings. The routine uses pin PC0, which is connected to A1 of the board. STM32H5 has two ADC cores, set to ADC1 and CH10, and the pin signal is single-ended.

The key to the pin settings is the clock rate Clock Prescaler and Resolution, the rate is divided by 4, the resolution is 12, and the interrupt needs to be enabled. The conversion group sets ADC_Regular_ConversionMode to software enable instead of using hardware linkage triggering.

/* 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 */

The program defines an ADC conversion variable uhADCxConvertedData. Note: It must be declared as "__IO uint16_t" here. This is to prevent the compiler from optimizing the variable and directly fetching the value from the address register. This is critical in high-speed MCUs, especially when ICACHE is turned on.

There is also a status variable ubAdcGrpRegularUnitaryConvStatus, which is used as the conversion standard.

A demo program for the conversion program,

/* 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 */
}

The process is very simple. Initialize the ADC first, calibrate the ADC before testing, then start converting the ADC measurement, and wait for the ADC conversion to end. This program uses an interrupt program.

/**
  * [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 */
}

There is a strange thing about this, that is, the LL_XXX function appears in the program, but the setting is the HAL library, which is a bit confusing

Interrupt processing is also very simple. First, determine whether the conversion of the "measurement rule group" of ADC1 is completed, and clear it if it is. After completion, call the AdcGrpRegularUnitaryConvComplete_Callback function, in which the flag variable ubAdcGrpRegularUnitaryConvStatus in the main function is set. I don't quite understand the processing of this function. Why is it not processed in the interrupt function, but in the BSP package?

Determine whether it is the ADC1 interrupt conversion completion flag, if so, clear it.

/**
  * @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();
}

These two functions are very confusing, but I know the working process and I haven't thought too much about it. There is no output face change in the routine, and the next task is to output the value of ADC1 through the serial port.

According to the design of the board, the serial port is output to USART3_PD9 and USART3_PD8. The hardware is completed. I completed this through a newly created project, mainly copying the ADC function into the project.

/**
  * @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 */

}

Copy the routine into the routine, then set up the printf function, and include #include <stdio.h> in main.c

#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__ */

There is also the declaration of the PUTCHAR_PROTOTYPE macro

/* 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 */

You also need a keil setting MicroLib

After completing these settings, you can use the printf function.

In the main function 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 */
  }

Here is the output:

For the test I used one of my treasures, a signal generator. The performance of the ADC is about the same as the previous ST.

This post is from stm32/stm8

Latest reply

It must be declared as "__IO uint16_t" to prevent the compiler from optimizing the variable. The compiler is powerful.   Details Published on 2023-5-20 11:33
 

6580

Posts

0

Resources
2
 

Follow the host to learn how the two ADC cores of STM32H5 work

This post is from stm32/stm8
 
 

1665

Posts

0

Resources
3
 

It must be declared as "__IO uint16_t" to prevent the compiler from optimizing the variable. The compiler is powerful.

This post is from stm32/stm8

Comments

STM32H5 should be more careful because there is cache inside and the compiler is likely to optimize some unnecessary variables.  Details Published on 2023-5-20 11:41
 
 

2867

Posts

4

Resources
4
 
Hot Ximi Show posted on 2023-5-20 11:33 It must be declared as "__IO uint16_t" to prevent the compiler from optimizing the variable. The compiler is powerful

STM32H5 should be more careful because there is cache inside and the compiler is likely to optimize some unnecessary variables.

This post is from stm32/stm8
 
 
 

Just looking around
Find a datasheet?

EEWorld Datasheet Technical Support

EEWorld
subscription
account

EEWorld
service
account

Automotive
development
circle

Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号
快速回复 返回顶部 Return list