I want to configure ADC with DMA on STM32(Nucleo-F401RE) and transmit the values through SPI to Basys 3 FPGA. Before transmission through SPI, when i read the values in memory realtime using STMSTudio, it is erratic.
In the past,I have tried increasing the sampling cycles, the issue persists.
Configured ADC without DMA with HAL_ADC_Start function and transferred the values to PC through UART, unable to retrieve the original signal. I'm unable to isolate where the problem lies.
uint32_t ADC1ConvertedValues[100];
int main(void) {
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
MX_DMA_Init();
MX_ADC1_Init();
MX_SPI1_Init();
while (1) {
HAL_GPIO_WritePin(GPIOA,GPIO_PIN_9,GPIO_PIN_SET);
if (HAL_ADC_Start_DMA(&hadc1, (uint32_t*)ADC1ConvertedValues, 100) == HAL_OK) {
HAL_GPIO_WritePin(GPIOA,GPIO_PIN_9,GPIO_PIN_RESET);
HAL_SPI_Transmit(&hspi1,(uint8_t*)(ADC1ConvertedValues),4,1);
HAL_GPIO_WritePin(GPIOA,GPIO_PIN_9,GPIO_PIN_SET);
}
}
}
void SystemClock_Config(void) {
RCC_OscInitTypeDef RCC_OscInitStruct = {0};
RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
__HAL_RCC_PWR_CLK_ENABLE();
__HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE2);
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI;
RCC_OscInitStruct.HSIState = RCC_HSI_ON;
RCC_OscInitStruct.HSICalibrationValue = RCC_HSICALIBRATION_DEFAULT;
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSI;
RCC_OscInitStruct.PLL.PLLM = 16;
RCC_OscInitStruct.PLL.PLLN = 336;
RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV4;
RCC_OscInitStruct.PLL.PLLQ = 7;
if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK) {
Error_Handler();
}
RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
|RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2;
RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;
if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2) != HAL_OK) {
Error_Handler();
}
}
static void MX_ADC1_Init(void) {
ADC_ChannelConfTypeDef sConfig = {0};
hadc1.Instance = ADC1;
hadc1.Init.ClockPrescaler = ADC_CLOCK_SYNC_PCLK_DIV4;
hadc1.Init.Resolution = ADC_RESOLUTION_8B;
hadc1.Init.ScanConvMode = ENABLE;
hadc1.Init.ContinuousConvMode = ENABLE;
hadc1.Init.DiscontinuousConvMode = DISABLE;
hadc1.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_NONE;
hadc1.Init.ExternalTrigConv = ADC_SOFTWARE_START;
hadc1.Init.DataAlign = ADC_DATAALIGN_RIGHT;
hadc1.Init.NbrOfConversion = 1;
hadc1.Init.DMAContinuousRequests = ENABLE;
hadc1.Init.EOCSelection = ADC_EOC_SINGLE_CONV;
if (HAL_ADC_Init(&hadc1) != HAL_OK) {
Error_Handler();
}
sConfig.Channel = ADC_CHANNEL_0;
sConfig.Rank = 1;
sConfig.SamplingTime = ADC_SAMPLETIME_3CYCLES;
if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK) {
Error_Handler();
}
}
static void MX_SPI1_Init(void) {
/* SPI1 parameter configuration*/
hspi1.Instance = SPI1;
hspi1.Init.Mode = SPI_MODE_MASTER;
hspi1.Init.Direction = SPI_DIRECTION_2LINES;
hspi1.Init.DataSize = SPI_DATASIZE_8BIT;
hspi1.Init.CLKPolarity = SPI_POLARITY_LOW;
hspi1.Init.CLKPhase = SPI_PHASE_1EDGE;
hspi1.Init.NSS = SPI_NSS_SOFT;
hspi1.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_4;
hspi1.Init.FirstBit = SPI_FIRSTBIT_MSB;
hspi1.Init.TIMode = SPI_TIMODE_DISABLE;
hspi1.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE;
hspi1.Init.CRCPolynomial = 10;
if (HAL_SPI_Init(&hspi1) != HAL_OK) {
Error_Handler();
}
}
static void MX_DMA_Init(void) {
__HAL_RCC_DMA2_CLK_ENABLE();
HAL_NVIC_SetPriority(DMA2_Stream0_IRQn, 0, 0);
HAL_NVIC_EnableIRQ(DMA2_Stream0_IRQn);
}
static void MX_GPIO_Init(void) {
GPIO_InitTypeDef GPIO_InitStruct = {0};
__HAL_RCC_GPIOA_CLK_ENABLE();
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_9, GPIO_PIN_RESET);
GPIO_InitStruct.Pin = GPIO_PIN_9;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
}
void Error_Handler(void) {
}
#ifdef USE_FULL_ASSERT
void assert_failed(uint8_t *file, uint32_t line) {
#endif /* USE_FULL_ASSERT */
EDIT 1: I used the arduino IDE to program NUCLEO-f401 RE and below is the code used :
#include <f401reMap.h>
float analogPin = pinMap(31); //PA0
float val = 0; // variable to store the value read
void setup() {
Serial.begin(115200); // setup serial
analogReadResolution(12);
}
void loop() {
val = analogRead(analogPin); // read the input pin
Serial.println(val); // debug value
}
It works for input signal frequency below 100Hz. How do I increase the throughput rate? My project requires conversion of analog signal between 500KHz to 900Khz.
Tried changing the DMA buffer size/speed uint32_t ADC1ConvertedValues[100]; reading about the DMA for this chip for my project I found that this sets the size of memory direct memory access allocated samples per clock? If it was I2C or if you would like to read about the timing concepts keep reading You need to find the ADC registers that set the spi baud rate and account for the setup requirements or re-initialization.
hadc1.Instance = ADC1;// this selects analog to digital circuit one
hadc1.Init.ClockPrescaler = ADC_CLOCK_SYNC_PCLK_DIV4; //"skip" 3 out of 4 clock steps in sync with time scale read on...
hadc1.Init.Resolution = ADC_RESOLUTION_8B; //use 8 bits to pack the numbers to send to the intergrated CPU of the f401
Background on the math
ADC and DMA are often classifyed in read rate at the spi level not at the analog level. So if the chip can do 8khz spi using 8 bits then we can calculate in bigO (8n+n) time that we should get just under 1khz read speed. HOWEVER you need to write 8 bits to receive 8bits so bigO time is now bigO(n16+n) . But because of continuous register I believe it could be as low as bigO (8n+n+8) or (8n+n+8setupbits). So using that we know the time consumed by the intermediate operations in terms of clock cycles note that the term n alone is to account for assumptions of unknown internal clock trigger conditions and should have a scalar that relative to theta if scale resolution is a absolute requirement. Also keep in mind that these frequencies you may be experiencing noise from impedance resistance and capacitance.
Related
I have stm32L053R8 nucleo64 board. I'm trying to get adc measure in low power run mode. It's not working correctly in Lprun mode but when I try in run mode it's working. In Lprun mode I only get 2 values. Half of the resolution and full of the resolution(12bit -> 2047 & 4095). Can you guys help me to figure out where am I doing wrong?
I tried to figure out why this happens and configured LFMEN, VREFEN, ULP bits but didn't worked.
ADC_HandleTypeDef hadc;
void SystemClock_Config(void);
static void MX_GPIO_Init(void);
static void MX_ADC_Init(void);
uint16_t adcVal = 0;
int main(void) {
HAL_Init();
MX_GPIO_Init();
MX_ADC_Init();
/*
// Bit 25 LFMEN: Low Frequency Mode enabled
ADC->CCR |= (1UL << 25);
// Bit 22 VREFEN: VREFINT enable
ADC->CCR |= (1UL << 22);
// Bit 9 ULP: Vrefint is ON in low power mode
PWR->CR &= ~(1UL << 9U);
*/
HAL_PWREx_EnableLowPowerRunMode();
while (1) {
HAL_Delay(500);
HAL_ADC_Start(&hadc);
HAL_ADC_PollForConversion(&hadc, 100);
adcVal = HAL_ADC_GetValue(&hadc);
HAL_ADC_Stop(&hadc);
}
}
/* #brief System Clock Configuration
* #retval None
*/
void SystemClock_Config(void) {
RCC_OscInitTypeDef RCC_OscInitStruct = {0};
RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
/* Configure the main internal regulator output voltage */
__HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1);
/* Initializes the RCC Oscillators according to the specified
*parameters
* in the RCC_OscInitTypeDef structure.
*/
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_MSI;
RCC_OscInitStruct.MSIState = RCC_MSI_ON;
RCC_OscInitStruct.MSICalibrationValue = 0;
RCC_OscInitStruct.MSIClockRange = RCC_MSIRANGE_2;
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_NONE;
if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK) {
Error_Handler();
}
/* Initializes the CPU, AHB and APB buses clocks */
RCC_ClkInitStruct.ClockType =RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
|RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_MSI;
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();
}
}
/*
* #brief ADC Initialization Function
* #param None
* #retval None
*/
static void MX_ADC_Init(void) {
/* USER CODE BEGIN ADC_Init 0 */
/* USER CODE END ADC_Init 0 */
ADC_ChannelConfTypeDef sConfig = {0};
/* USER CODE BEGIN ADC_Init 1 */
/* USER CODE END ADC_Init 1 */
/* Configure the global features of the ADC (Clock, Resolution, Data Alignment and number of conversion) */
hadc.Instance = ADC1;
hadc.Init.OversamplingMode = DISABLE;
hadc.Init.ClockPrescaler = ADC_CLOCK_SYNC_PCLK_DIV1;
hadc.Init.Resolution = ADC_RESOLUTION_12B;
hadc.Init.SamplingTime = ADC_SAMPLETIME_1CYCLE_5;
hadc.Init.ScanConvMode = ADC_SCAN_DIRECTION_FORWARD;
hadc.Init.DataAlign = ADC_DATAALIGN_RIGHT;
hadc.Init.ContinuousConvMode = DISABLE;
hadc.Init.DiscontinuousConvMode = DISABLE;
hadc.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_NONE;
hadc.Init.ExternalTrigConv = ADC_SOFTWARE_START;
hadc.Init.DMAContinuousRequests = DISABLE;
hadc.Init.EOCSelection = ADC_EOC_SINGLE_CONV;
hadc.Init.Overrun = ADC_OVR_DATA_PRESERVED;
hadc.Init.LowPowerAutoWait = DISABLE;
hadc.Init.LowPowerFrequencyMode = ENABLE;
hadc.Init.LowPowerAutoPowerOff = DISABLE;
if (HAL_ADC_Init(&hadc) != HAL_OK) {
Error_Handler();
}
/* Configure for the selected ADC regular channel to be converted. */
sConfig.Channel = ADC_CHANNEL_1;
sConfig.Rank = ADC_RANK_CHANNEL_NUMBER;
if (HAL_ADC_ConfigChannel(&hadc, &sConfig) != HAL_OK) {
Error_Handler();
}
}
I am new to interrupt-based programming.
In my current project, I need the interrupt generated exactly at 1us interval.
Below is the screenshot from the Clock Configuration tab in CubeMX.
I am using the TIM3 timer as it can generate the clock frequency of 1us.
Below is the TIM3 configuration code.
static void MX_TIM3_Init(void)
{
TIM_ClockConfigTypeDef sClockSourceConfig;
TIM_MasterConfigTypeDef sMasterConfig;
htim3.Instance = TIM3;
htim3.Init.Prescaler = 1-1 ;//0x00;// 0x36; || 0x00//1-1
htim3.Init.CounterMode = TIM_COUNTERMODE_UP;
htim3.Init.Period = 0xffff-1; //0x64; || 0xd7 //0xffff-1
htim3.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
htim3.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
if (HAL_TIM_Base_Init(&htim3) != HAL_OK)
{
_Error_Handler(__FILE__, __LINE__);
}
sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL;
if (HAL_TIM_ConfigClockSource(&htim3, &sClockSourceConfig) != HAL_OK)
{
_Error_Handler(__FILE__, __LINE__);
}
sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
if (HAL_TIMEx_MasterConfigSynchronization(&htim3, &sMasterConfig) != HAL_OK)
{
_Error_Handler(__FILE__, __LINE__);
}
}
I am calling the timer
HAL_TIM_IRQHandler(&htim3);
/* USER CODE BEGIN TIM3_IRQn 1 */
HAL_GPIO_TogglePin(GPIOB,GPIO_PIN_6);
I see that the interrupt of duration 1.2ms is generated.
Can anyone let me why is this happening and how can I reduce the interrupt to 1us duration?
Any change required in the timer frequency?
I am also using freeRTOS and other applications are also running on the microcontroller.
Any help in this is highly appreciated.
Thanks in Advance
If your requirement output an accurate 500KHz 1:1 mark/space signal (i.e. 1us high, 1us low), then doing that through interrupts while expecting your system to do other useful work is both impractical an unnecessary. The general purpose timers have a output-compare function that can drive a GPIO pin directly without interrupts or software overhead.
Only certain pins are connected to the Timer OC channels, so to drive PB6 in this case you would need to use TIM4 Channel 1.
Also rather than determining and hard-coding timer reload and pulse, you should use the available HAL RCC clock functions (HAL_RCC_GetPCLK1Freq() in this case) to calculate the values to avoid erros, and so that the code will be portable to other systems or will work correctly if you change your clock configuration.
static void MX_TIM4_Init(void)
{
cost uint32_t PULSE_WIDTH = HAL_RCC_GetPCLK1Freq() * 2 / 1000000 ;
htim4.Instance = TIM4 ;
htim4.Init.Prescaler = 0;
htim4.Init.CounterMode = TIM_COUNTERMODE_UP ;
htim4.Init.Period = PULSE_WIDTH * 2 ;
htim4.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1 ;
HAL_TIM_PWM_Init( &htim4 ) ;
TIM_MasterConfigTypeDef sMasterConfig ;
sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
HAL_TIMEx_MasterConfigSynchronization(&htim3, &sMasterConfig);
TIM_OC_InitTypeDef sConfigOC ;
sConfigOC.OCMode = TIM_OCMODE_PWM1;
sConfigOC.Pulse = PULSE_WIDTH ;
sConfigOC.OCPolarity = TIM_OCPOLARITY_HIGH;
sConfigOC.OCFastMode = TIM_OCFAST_DISABLE;
HAL_TIM_PWM_ConfigChannel(&htim4, &sConfigOC, TIM_CHANNEL_1);
}
Then elsewhere you need to configure PB6 as an output and start the timer:
LL_GPIO_InitTypeDef GPIO_InitStruct = {0} ;
GPIO_InitStruct.Pin = GPIO_PIN_6 ;
GPIO_InitStruct.Mode = LL_GPIO_MODE_ALTERNATE ;
GPIO_InitStruct.Speed = LL_GPIO_SPEED_FREQ_LOW ;
GPIO_InitStruct.OutputType = LL_GPIO_OUTPUT_PUSHPULL ;
GPIO_InitStruct.Pull = LL_GPIO_PULL_NO ;
GPIO_InitStruct.Alternate = LL_GPIO_AF_2 ;
LL_AHB2_GRP1_EnableClock(LL_AHB2_GRP1_PERIPH_GPIOB)
LL_GPIO_Init(GPIOB, &GPIO_InitStruct);
HAL_TIM_PWM_Start( &htim4, TIM_CHANNEL_1 ) ;
Thereafter the signal will be maintained indefinitely on PB6 with no GPIO access or interrupt handling.
I'm using DMA to Access the Data from my ADC. The value at the ADC changes permantenly.
I read I can use DMA so I can use the value of the ADC everytime and everywhere I want to.
Problem is that my Main while() Loop is not or just once execute. The DMA Interupt calls.
HAL_ADC_Start_DMA(&hadc, (uint32_t*) &buffer, 1);
Here is the Code for Start the DMA for the ADC. Mode is Circular.
Here is the Init:
void SystemClock_Config(void)
{
RCC_OscInitTypeDef RCC_OscInitStruct = {0};
RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
RCC_PeriphCLKInitTypeDef PeriphClkInit = {0};
/** Initializes the CPU, AHB and APB busses clocks
*/
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI|RCC_OSCILLATORTYPE_HSI14
|RCC_OSCILLATORTYPE_HSI48;
RCC_OscInitStruct.HSIState = RCC_HSI_ON;
RCC_OscInitStruct.HSI48State = RCC_HSI48_ON;
RCC_OscInitStruct.HSI14State = RCC_HSI14_ON;
RCC_OscInitStruct.HSICalibrationValue = RCC_HSICALIBRATION_DEFAULT;
RCC_OscInitStruct.HSI14CalibrationValue = 16;
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_NONE;
if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
{
Error_Handler();
}
/** Initializes the CPU, AHB and APB busses clocks
*/
RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
|RCC_CLOCKTYPE_PCLK1;
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_HSI48;
RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV2;
RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1;
if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_1) != HAL_OK)
{
Error_Handler();
}
PeriphClkInit.PeriphClockSelection = RCC_PERIPHCLK_USART1|RCC_PERIPHCLK_USART2
|RCC_PERIPHCLK_I2C1;
PeriphClkInit.Usart1ClockSelection = RCC_USART1CLKSOURCE_PCLK1;
PeriphClkInit.Usart2ClockSelection = RCC_USART2CLKSOURCE_PCLK1;
PeriphClkInit.I2c1ClockSelection = RCC_I2C1CLKSOURCE_HSI;
if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInit) != HAL_OK)
{
Error_Handler();
}
}
/**
* #brief ADC Initialization Function
* #param None
* #retval None
*/
static void MX_ADC_Init(void)
{
/* USER CODE BEGIN ADC_Init 0 */
/* USER CODE END ADC_Init 0 */
ADC_ChannelConfTypeDef sConfig = {0};
/* USER CODE BEGIN ADC_Init 1 */
/* USER CODE END ADC_Init 1 */
/** Configure the global features of the ADC (Clock, Resolution, Data Alignment and number of conversion)
*/
hadc.Instance = ADC1;
hadc.Init.ClockPrescaler = ADC_CLOCK_ASYNC_DIV1;
hadc.Init.Resolution = ADC_RESOLUTION_12B;
hadc.Init.DataAlign = ADC_DATAALIGN_RIGHT;
hadc.Init.ScanConvMode = ADC_SCAN_DIRECTION_FORWARD;
hadc.Init.EOCSelection = ADC_EOC_SINGLE_CONV;
hadc.Init.LowPowerAutoWait = DISABLE;
hadc.Init.LowPowerAutoPowerOff = DISABLE;
hadc.Init.ContinuousConvMode = ENABLE;
hadc.Init.DiscontinuousConvMode = DISABLE;
hadc.Init.ExternalTrigConv = ADC_SOFTWARE_START;
hadc.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_NONE;
hadc.Init.DMAContinuousRequests = ENABLE;
hadc.Init.Overrun = ADC_OVR_DATA_PRESERVED;
if (HAL_ADC_Init(&hadc) != HAL_OK)
{
Error_Handler();
}
/** Configure for the selected ADC regular channel to be converted.
*/
sConfig.Channel = ADC_CHANNEL_0;
sConfig.Rank = ADC_RANK_CHANNEL_NUMBER;
sConfig.SamplingTime = ADC_SAMPLETIME_1CYCLE_5;
if (HAL_ADC_ConfigChannel(&hadc, &sConfig) != HAL_OK)
{
Error_Handler();
}
/* USER CODE BEGIN ADC_Init 2 */
/* USER CODE END ADC_Init 2 */
}
static void MX_DMA_Init(void)
{
/* DMA controller clock enable */
__HAL_RCC_DMA1_CLK_ENABLE();
/* DMA interrupt init */
/* DMA1_Channel1_IRQn interrupt configuration */
HAL_NVIC_SetPriority(DMA1_Channel1_IRQn, 0, 0);
HAL_NVIC_EnableIRQ(DMA1_Channel1_IRQn);
}
The ADC reads a analog volage from I/O.
My while(1) Loop currently just contains blinking led code.
Check the following:
Inside Hal_MSP file use DMA_CIRCULAR
create the buffer like this -> __IO uint16_t buffer[1]
then use it like this -> HAL_ADC_Start_DMA(&hadc, (uint32_t*) &buffer, 1);
Its better to start DMA at the end of ADC init. You can place above line inside the USER CODE BEGIN ADC_Init 2 comment braces.
The ADC size is 12 bit of this controller so circular DMA will automatically overwrite after every conversion.
I am working on a project where a STM32H743 nucleo board and use of 16 ADC inputs are involved.
Obviously, these analog inputs are used once a time; read the value via polling mechanism and configure the next input... configure the ADC channel, start the ADC, read the value via polling and configure the next input... 16 times each 1 ms, as a Real-Time behaviour.
The problem I found is that I can´t start any of the 3 ADCs, it stuck at this line at
stm32h7xx_hal_adc.h (I think that I configure the clocks or another sort of things incorrectly) :
while(__HAL_ADC_GET_FLAG(hadc, ADC_FLAG_RDY) == 0UL)
This line is in the function:
HAL_StatusTypeDef ADC_Enable(ADC_HandleTypeDef *hadc)
That is being called by:
HAL_StatusTypeDef HAL_ADC_Start(ADC_HandleTypeDef *hadc)
Thanks in advance for the help, the source code is provided below.
The source code files are:
MAIN.C
#include "main.h"
#include "hwdrvlib.h"
#include "test.h"
volatile unsigned int systick_count = 0;
static volatile int systick_active = 1;
/**
* #brief The application entry point.
* #retval int
*/
int main(void)
{
#ifdef CPU_CACHE
/* Enable I-Cache---------------------------------------------------------*/
SCB_EnableICache();
/* Enable D-Cache---------------------------------------------------------*/
SCB_EnableDCache();
#endif
/* MCU Configuration--------------------------------------------------------*/
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
/* Configure the system clock */
SystemClock_Config(); //Located on hwdrvlid.h y hwdrvlib.c
/* SysTick is 0 at start */
tick = (uint8_t)0;
/* Initialize */
ADC_Init(); /* Initialize ADC peripherals and GPIO ADC inputs as analog */
//Located on hwdrvlid.h y hwdrvlib.c
/* Sample Time: 0.001 */
while (...) { /* Some comparison deleted for readability */
RT_Task(); //Located on Function file
} //End of while
}
hwdrvlib.c (The file that contains configuration functions)
ADC_HandleTypeDef hadc1 __attribute__((section(".ramd2")));
ADC_HandleTypeDef hadc2 __attribute__((section(".ramd2")));
ADC_HandleTypeDef hadc3 __attribute__((section(".ramd2")));
void ADC_Init(void)
{
GPIO_InitTypeDef GPIO_InitStruct;
/* ADC Clock Enable */
__HAL_RCC_ADC12_CLK_ENABLE();
__HAL_RCC_ADC3_CLK_ENABLE();
/* ADC Periph interface clock configuration */
__HAL_RCC_ADC_CONFIG(RCC_ADCCLKSOURCE_CLKP);//or PLL2
/* GPIO Ports Clock Enable */
__HAL_RCC_GPIOF_CLK_ENABLE();
__HAL_RCC_GPIOC_CLK_ENABLE();
__HAL_RCC_GPIOA_CLK_ENABLE();
__HAL_RCC_GPIOB_CLK_ENABLE();
/* ADCs GPIO as analog input */
/* System ADC Input number 1 PF9 */
/*##-2- Configure peripheral GPIO ##########################################*/
/* ADC Channel GPIO pin configuration */
GPIO_InitStruct.Pin = GPIO_PIN_9;
GPIO_InitStruct.Mode = GPIO_MODE_ANALOG;
GPIO_InitStruct.Pull = GPIO_NOPULL;
HAL_GPIO_Init(GPIOF, &GPIO_InitStruct);
/* Initialization of 16 analog inputs, hidden for readability */
/* System ADC Input number 16 PA3 */
/*##-2- Configure peripheral GPIO ##########################################*/
/* ADC Channel GPIO pin configuration */
GPIO_InitStruct.Pin = GPIO_PIN_3;
GPIO_InitStruct.Mode = GPIO_MODE_ANALOG;
GPIO_InitStruct.Pull = GPIO_NOPULL;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
/* ADC1 Config */
hadc1.Instance = ADC1;
if (HAL_ADC_DeInit(&hadc1) != HAL_OK) {
/* ADC1 de-initialization Error */
Error_Handler();
}
hadc1.Init.ClockPrescaler = ADC_CLOCK_ASYNC_DIV1;
hadc1.Init.Resolution = ADC_RESOLUTION_16B;
hadc1.Init.ScanConvMode = ADC_SCAN_DISABLE;
hadc1.Init.EOCSelection = ADC_EOC_SINGLE_CONV;
hadc1.Init.LowPowerAutoWait = DISABLE;
hadc1.Init.ContinuousConvMode = DISABLE;
hadc1.Init.NbrOfConversion = 1; /* Vector Support */
hadc1.Init.DiscontinuousConvMode = DISABLE;
hadc1.Init.NbrOfDiscConversion = 1;
hadc1.Init.ExternalTrigConv = ADC_SOFTWARE_START;
hadc1.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_NONE;
hadc1.Init.ConversionDataManagement = ADC_CONVERSIONDATA_DR;
hadc1.Init.Overrun = ADC_OVR_DATA_OVERWRITTEN;
hadc1.Init.LeftBitShift = ADC_LEFTBITSHIFT_NONE;
hadc1.Init.OversamplingMode = DISABLE;
if (HAL_ADC_Init(&hadc1) != HAL_OK) {
Error_Handler();
}
/* The same for ADC2 and ADC3 using hadc2 and hadc3 */
}
void ADC_Input_Select(ADC_HandleTypeDef *hadc,uint32_t Channel)
{
static ADC_ChannelConfTypeDef sConfig = { 0 };
/* Configure Regular Channel */
sConfig.Channel = Channel;
sConfig.Rank = ADC_REGULAR_RANK_1;
sConfig.SamplingTime = ADC_SAMPLETIME_387CYCLES_5;
sConfig.SingleDiff = ADC_SINGLE_ENDED;
sConfig.OffsetNumber = ADC_OFFSET_NONE;
sConfig.Offset = 0;
if (HAL_ADC_ConfigChannel(hadc, &sConfig) != HAL_OK) {
Error_Handler();
}
}
/**
* #brief System Clock Configuration
* #retval None
*
*
*
* Configure 480 MHz CPU Clock, 240 MHz APB1 and APB2 Clock
* flash latency 4
*/
void SystemClock_Config(void)
{
RCC_OscInitTypeDef RCC_OscInitStruct = { 0 };
RCC_ClkInitTypeDef RCC_ClkInitStruct = { 0 };
RCC_PeriphCLKInitTypeDef PeriphClkInitStruct = { 0 };
/** Supply configuration update enable
*/
HAL_PWREx_ConfigSupply(PWR_LDO_SUPPLY);
/* The voltage scaling allows optimizing the power consumption when the device is
clocked below the maximum system frequency, to update the voltage scaling value
regarding system frequency refer to product datasheet. */
__HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1);
while (!__HAL_PWR_GET_FLAG(PWR_FLAG_VOSRDY)) {
}
__HAL_RCC_SYSCFG_CLK_ENABLE();
__HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE0);
while (!__HAL_PWR_GET_FLAG(PWR_FLAG_VOSRDY)) {
}
__HAL_RCC_PLL_PLLSOURCE_CONFIG(RCC_PLLSOURCE_HSI);//HSE
/* a 480 MHz config */
/** Configure the main internal regulator output voltage
*/
__HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE0);
while (!__HAL_PWR_GET_FLAG(PWR_FLAG_VOSRDY)) {
}
/** Initializes the CPU, AHB and APB busses clocks
*/
#ifdef USB_VCP_SETUP
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI48 |
RCC_OSCILLATORTYPE_HSI;
RCC_OscInitStruct.HSIState = RCC_HSI_DIV1;
RCC_OscInitStruct.HSICalibrationValue = RCC_HSICALIBRATION_DEFAULT;
RCC_OscInitStruct.HSI48State = RCC_HSI48_ON;
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSI;
RCC_OscInitStruct.PLL.PLLM = 4;
RCC_OscInitStruct.PLL.PLLN = 60;
RCC_OscInitStruct.PLL.PLLP = 2;
RCC_OscInitStruct.PLL.PLLQ = 2;
RCC_OscInitStruct.PLL.PLLR = 2;
RCC_OscInitStruct.PLL.PLLRGE = RCC_PLL1VCIRANGE_3;
RCC_OscInitStruct.PLL.PLLVCOSEL = RCC_PLL1VCOWIDE;
RCC_OscInitStruct.PLL.PLLFRACN = 0;
#else
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI;
RCC_OscInitStruct.HSIState = RCC_HSI_DIV1;
RCC_OscInitStruct.HSICalibrationValue = RCC_HSICALIBRATION_DEFAULT;
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSI;
RCC_OscInitStruct.PLL.PLLM = 4;
RCC_OscInitStruct.PLL.PLLN = 60;
RCC_OscInitStruct.PLL.PLLP = 2;
RCC_OscInitStruct.PLL.PLLQ = 2;
RCC_OscInitStruct.PLL.PLLR = 2;
RCC_OscInitStruct.PLL.PLLRGE = RCC_PLL1VCIRANGE_3;
RCC_OscInitStruct.PLL.PLLVCOSEL = RCC_PLL1VCOWIDE;
RCC_OscInitStruct.PLL.PLLFRACN = 0;
#endif
if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK) {
Error_Handler();
}
/* End of old 480 MHz config */
/** Initializes the CPU, AHB and APB busses clocks
*/
RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
|RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2
|RCC_CLOCKTYPE_D3PCLK1|RCC_CLOCKTYPE_D1PCLK1;
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
RCC_ClkInitStruct.SYSCLKDivider = RCC_SYSCLK_DIV1;
RCC_ClkInitStruct.AHBCLKDivider = RCC_HCLK_DIV2;
RCC_ClkInitStruct.APB3CLKDivider = RCC_APB3_DIV2;
RCC_ClkInitStruct.APB1CLKDivider = RCC_APB1_DIV2;
RCC_ClkInitStruct.APB2CLKDivider = RCC_APB2_DIV2;
RCC_ClkInitStruct.APB4CLKDivider = RCC_APB4_DIV2;
if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_4) != HAL_OK) {
Error_Handler();
}
/* USB CLK Initialization if needed */
#ifdef USB_VCP_SETUP
PeriphClkInitStruct.PeriphClockSelection = RCC_PERIPHCLK_USB;
PeriphClkInitStruct.UsbClockSelection = RCC_USBCLKSOURCE_HSI48;//PLL
if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInitStruct) != HAL_OK) {
Error_Handler();
}
/** Enable USB Voltage detector
*/
HAL_PWREx_EnableUSBVoltageDetector();
#endif
}
FUNCTION FILE
(The function that contains the function to be executed, in order to read analog input data)
void RT_Task(void)
{
/* ADC3-IN2 PF9 */
ADC_Input_Select(&hadc3,ADC_CHANNEL_2);
HAL_ADC_Start(&hadc3); /* Execution stucks here :( */
if (HAL_ADC_PollForConversion(&hadc3,1000) != HAL_OK ) {
/* ADC conversion fails */
//Escribir aqui la salida de error
} else {
test2_B.VectorConcatenate[1] = HAL_ADC_GetValue(&hadc3) + 0;
}
/* More ADC reading hidden for readability */
}
Solution for this problem, remember that you can face this with any microcontroller:
I found the failure, it is something that I never imagine before.
The initialization function takes more time that the SysTick IRQ needs to trigger the first IRQ (the Real-Time task is called from the SysTick IRQ), and due to ADC peripherals are not initialized... its functions could not be executed properly.
I added an uint8 variable to detect if the initialization function ends at RT Task call start code. Also I need to enable ADC_ConfigureBoostMode(&hadc1); for each used ADC.
int main(void)
{
Init_finished = (uint8_t)0;
#ifdef CPU_CACHE
/* Enable I-Cache---------------------------------------------------------*/
SCB_EnableICache();
/* Enable D-Cache---------------------------------------------------------*/
SCB_EnableDCache();
#endif
/* MCU Configuration--------------------------------------------------------*/
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
/* Configure the system clock */
SystemClock_Config();
/* SysTick is 0 at start */
tick = (uint8_t)0;
/* Initialize model */
test_initialize(1);
Init_finished = (uint8_t)255;
and use these
ADC_ConfigureBoostMode(&hadc1); /* and for hadc2 and hadc3 */
I am currently working with a STM32f407G-DISC1 and trying to catch a buffer with the ADC and the DMA callback.
A Frequency generator is connected to the pin A0 and the board is grounded.
I verified that my wires weren't broken with an oscilloscope.
Now the problem is that after setting up my project and compiling it the DMA callback is called every time my buffer is filled. My problem is that the buffer is filled with the same values each call.
I've done exactly the same project on a STM32F401RE board. The code is approximately the same except the code generated with CUBEMX.
At first I though I was making a mistake in CUBEMX so I tried to generate another project using another ADC on the board. But I got exactly the same result.
Also, I tried to use another board (I have two of them); Same result.
ADC_HandleTypeDef hadc1;
volatile uint32_t ADCValue1[1];
void main(void)
{
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
MX_DMA_Init();
MX_ADC1_Init();
HAL_ADC_Start_DMA(&hadc1, (uint32_t *) ADCValue1, 1);
while (1) {}
return;
}
void SystemClock_Config(void)
{
RCC_OscInitTypeDef RCC_OscInitStruct = {0};
RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
/** Configure the main internal regulator output voltage
*/
__HAL_RCC_PWR_CLK_ENABLE();
__HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1);
/** Initializes the CPU, AHB and APB busses clocks
*/
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI;
RCC_OscInitStruct.HSIState = RCC_HSI_ON;
RCC_OscInitStruct.HSICalibrationValue = RCC_HSICALIBRATION_DEFAULT;
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSI;
RCC_OscInitStruct.PLL.PLLM = 8;
RCC_OscInitStruct.PLL.PLLN = 84;
RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV2;
RCC_OscInitStruct.PLL.PLLQ = 4;
if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
{
Error_Handler();
}
/** Initializes the CPU, AHB and APB busses clocks
*/
RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
|RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2;
RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;
if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2) != HAL_OK)
{
Error_Handler();
}
}
static void MX_ADC1_Init(void)
{
ADC_ChannelConfTypeDef sConfig = {0};
/** Configure the global features of the ADC (Clock, Resolution, Data Alignment and number of conversion)
*/
hadc1.Instance = ADC1;
hadc1.Init.ClockPrescaler = ADC_CLOCK_SYNC_PCLK_DIV4;
hadc1.Init.Resolution = ADC_RESOLUTION_12B;
hadc1.Init.ScanConvMode = DISABLE;
hadc1.Init.ContinuousConvMode = ENABLE;
hadc1.Init.DiscontinuousConvMode = DISABLE;
hadc1.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_NONE;
hadc1.Init.ExternalTrigConv = ADC_SOFTWARE_START;
hadc1.Init.DataAlign = ADC_DATAALIGN_RIGHT;
hadc1.Init.NbrOfConversion = 1;
hadc1.Init.DMAContinuousRequests = ENABLE;
hadc1.Init.EOCSelection = ADC_EOC_SEQ_CONV;
if (HAL_ADC_Init(&hadc1) != HAL_OK)
{
Error_Handler();
}
/** Configure for the selected ADC regular channel its corresponding rank in the sequencer and its sample time.
*/
sConfig.Channel = ADC_CHANNEL_0;
sConfig.Rank = 1;
sConfig.SamplingTime = ADC_SAMPLETIME_3CYCLES;
if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)
{
Error_Handler();
}
}
static void MX_DMA_Init(void)
{
/* DMA controller clock enable */
__HAL_RCC_DMA2_CLK_ENABLE();
/* DMA interrupt init */
/* DMA2_Stream0_IRQn interrupt configuration */
HAL_NVIC_SetPriority(DMA2_Stream0_IRQn, 0, 0);
HAL_NVIC_EnableIRQ(DMA2_Stream0_IRQn);
}
static void MX_GPIO_Init(void)
{
/* GPIO Ports Clock Enable */
__HAL_RCC_GPIOC_CLK_ENABLE();
__HAL_RCC_GPIOH_CLK_ENABLE();
__HAL_RCC_GPIOA_CLK_ENABLE();
}
/* This is the callback called each time my buffer is filled */
void DMA2_Stream0_IRQHandler(void)
{
if ((DMA2->LISR & DMA_LISR_TCIF0) && (DMA2_Stream0->CR & DMA_SxCR_TCIE))
DMA2->LIFCR = DMA_LIFCR_CTCIF0; // acknowledge interrupt
DMA_IRQHandler(); // The function where I want to process my buffer
return;
/* transmission complete interrupt */
}
void DMA_IRQHandler(void)
{
return;
}
Each time I enter the callback my buffer is filled with the exact same values instead of different values.
If you have any ideas...
If you need more code just ask and I'll provide it.
If the code that you didn't bother to post is working correctly, then the buffer would contain the most recent DMA readings every time the interrupt handler is called.
But the compiler has no way of knowing that the buffer is modified by the DMA hardware, because it's not declared as volatile.
There are two obvious problems:
DMA buffers must always be declared volatile or the compiler might do strange optimizations when it realizes that no software updates the variable. Why ST's bloatware doesn't take a volatile qualified parameter is an excellent question to ST - probably yet another bug in their libs.
The function declaration is
HAL_ADC_Start_DMA (ADC_HandleTypeDef *hadc, uint32_t *pData, uint32_t Length)
Meaning you must naturally pass a uint32_t * and not something else. Instead you pass a unsigned short* which you brute force convert to uint32_t*. This gives 2 bugs: the callback might write out of bounds and/or misaligned to your 16 bit variable. And it is also a strict aliasing violation.