要测试STM32H5的ADC,首先参考一下例程ADC_SingleConversion_TriggerSW_IT,这是cube自带的例程,打开cube先看一下设置,例程使用了引脚PC0,这个脚和板子的A1相连接。STM32H5有两个ADC核心,设置为ADC1和CH10,引脚信号为单端信号Single-ended
引脚设置关键的是 时钟速率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库呀,这个有点不明白
中断处理也很简单,首先判断是不是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的值了。
根据板子的设计,将串口输出到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
在主函数中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 */
}
下面是输出的结果
测试中我使用了我的一个宝贝,信号发生器。ADC的性能和以往的ST差不多。
引用: 火辣西米秀 发表于 2023-5-20 11:33 必须声明为"__IO uint16_t",这个是为了让编译器不去优化该变量,编译器的功能强大
STM32H5更加的应该注意,内部是有cache的,编译器很可能把某些不必要的变量给优化掉。