I am attempting to use the STM32F411RE DMA with ADC to obtain 2048 samples each for 3 input channels / pins. The A0, A1 and A2 pins are connected to a sensor for which image processing (Fourier Transform) is to be used.
I thus use the DMA / ADC to fill the adc_buffer[6144] with all the 2048 samples from the 3 pins, using an interrupt to find when this buffer is full. When the buffer is full the data should be converted to voltages and put into the correct bin (data goes x, y, z, x, y, z).
Whilst I have all of that working for some reason my DMA circular buffer does not fill up with data. In a previous program this worked fine but I can now no longer make this work: adc_buffer remains 0 throughout program run and as such the ISR HAL_ADC_ConvCpltCallback is never triggered.
#include "main.h"
#include "arm_const_structs.h"
#include "core_cm4.h"
#include "math.h"
#include "arm_math.h"
#include <stdio.h>
#include <stdbool.h>
#include "main.h"
ADC_HandleTypeDef hadc1;
DMA_HandleTypeDef hdma_adc1;
UART_HandleTypeDef huart2;
void SystemClock_Config(void);
static void MX_GPIO_Init(void);
static void MX_DMA_Init(void);
static void MX_ADC1_Init(void);
static void MX_USART2_UART_Init(void);
float adc_buffer[6144] = {0}; // 2048 samples * 3 channels
/*
* Interrupt called when the input buffer adc_buffer is full.
* TO DO: Add void HAL_ADC_ConvHalfCpltCallback(ADC_HandleTypeDef* hadc) for double buffering, when conversion is half complete also run Fourier transform
*/
void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* hadc)
{
if(hadc->Instance == ADC1) // if(hadc->Instance == ADC1 && !conversionPaused)
{
conversionPaused = 1; // Temporarily disable the conversion process
for (int samples= 0; samples < fftLen; samples++) // Allocates data from the 1 ADC buffer into the 3 separate axis buffers
{
Xin[samples] = ((3.3-0)/4096) * adc_buffer[0+3*samples]; // Allocate samples to X array, whilst transforming them into voltage: every 3rd value starting at 0 (0, 3, 6, 9)
Yin[samples] = ((3.3-0)/4096) * adc_buffer[1+3*samples]; // Allocate samples to X array, whilst transforming them into voltage: every 3rd value starting at 1 (1, 4, 7, 10)
Zin[samples] = ((3.3-0)/4096) * adc_buffer[2+3*samples]; // Allocate samples to X array, whilst transforming them into voltage: every 3rd value starting at 2 (2, 5, 8, 11)
}
}
}
int main(void)
{
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
MX_DMA_Init();
MX_ADC1_Init();
MX_USART2_UART_Init();
HAL_ADC_Start_IT(&hadc1);
HAL_ADC_Start_DMA(&hadc1, adc_buffer, bufferLen); // Maybe DMA and ADC IT are mutually exclusive?
while (1)
{
// Signal processing on Xin, Yin, Zin
}
/**
* #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_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 = 16;
RCC_OscInitStruct.PLL.PLLN = 393;
RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV4;
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_DIV8;
RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV8;
if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_3) != HAL_OK)
{
Error_Handler();
}
}
/**
* #brief ADC1 Initialization Function
* #param None
* #retval None
*/
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_DIV8;
hadc1.Init.Resolution = ADC_RESOLUTION_12B;
hadc1.Init.ScanConvMode = ENABLE;
hadc1.Init.ContinuousConvMode = ENABLE; // Maybe this needs to be set to off for use with the circular buffer?
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 = 3;
hadc1.Init.DMAContinuousRequests = DISABLE;
hadc1.Init.EOCSelection = DISABLE; //ADC_EOC_SINGLE_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_56CYCLES;
if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != 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_1;
sConfig.Rank = 2;
if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != 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_4;
sConfig.Rank = 3;
if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)
{
Error_Handler();
}
}
/**
* #brief USART2 Initialization Function
* #param None
* #retval None
*/
static void MX_USART2_UART_Init(void)
{
huart2.Instance = USART2;
huart2.Init.BaudRate = 115200;
huart2.Init.WordLength = UART_WORDLENGTH_8B;
huart2.Init.StopBits = UART_STOPBITS_1;
huart2.Init.Parity = UART_PARITY_NONE;
huart2.Init.Mode = UART_MODE_TX_RX;
huart2.Init.HwFlowCtl = UART_HWCONTROL_NONE;
huart2.Init.OverSampling = UART_OVERSAMPLING_16;
if (HAL_UART_Init(&huart2) != HAL_OK)
{
Error_Handler();
}
}
/**
* Enable DMA controller clock
*/
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);
}
/**
* #brief GPIO Initialization Function
* #param None
* #retval None
*/
static void MX_GPIO_Init(void)
{
/* GPIO Ports Clock Enable */
__HAL_RCC_GPIOA_CLK_ENABLE();
}
I suspect that the problem lies in the configuration of the ADC and DMA, on which I can find little literature. A number of examples 1 2 3 4 are available, but this offers little remedy as their code appears the same as mine.
As an edit to the response by Tarick Welling I do not seem to get the full package when generating my code in CubeMX. Particularly I am missing the full MX_DMA_Init() as mine only generates the following:
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);
}
However this is despite my settings in CubeMX being configured so that this should be generated, as per the image below:
I am thoroughly confused as to why this code is not being generated in CubeMX for me, whilst for others it apparently is.
MX_DMA_Init doesn't initialize hdma_adc1. So the DMA isn't able to be used.
hadc1.Init.DMAContinuousRequests = DISABLE; disables continuous use of the DMA so it will be a single shot transfer.
uint32_t DMAContinuousRequests; Specify whether the DMA requests are performed in one shot mode (DMA transfer stops when number of conversions is reached)
or in continuous mode (DMA transfer unlimited, whatever number of conversions).
This parameter can be set to ENABLE or DISABLE.
Note: In continuous mode, DMA must be configured in circular mode. Otherwise an overrun will be triggered when DMA buffer maximum pointer is reached.
The ADC isn't initialized to use the DMA so it doesn't even know to use it.
typedef struct
{
ADC_TypeDef *Instance; /*!< Register base address */
ADC_InitTypeDef Init; /*!< ADC required parameters */
DMA_HandleTypeDef *DMA_Handle; /*!< Pointer DMA Handler */
HAL_LockTypeDef Lock; /*!< ADC locking object */
__IO uint32_t State; /*!< ADC communication state (bitmap of ADC states) */
__IO uint32_t ErrorCode; /*!< ADC Error code */
}ADC_HandleTypeDef;
DMA_Handle needs to be initialized for the ADC to use the DMA.
This can be set up inside CubeMX by adding a line to DMA Settings of your ADC configuration on the Configuration tab:
Further settings can be set by selecting this DMA transfer and configuring its parameters in the bottom part of the dialog.
Please note that ST makes use of a form of delegated initialization. HAL_ADC_Init calls a user defined HAL_ADC_MspInit. This is defined inside stm32f4xx_hal_msp.c and should link the ADC to the DMA. It should contain a line similar to: __HAL_LINKDMA(hadc,DMA_Handle,hdma_adc1);
Related
Im using the STM32 Cube IDE. What I tried now is enable MSM in TIM2 and output_compare_no_output on Channel 1 and select "Reset" as the Trigger Event. Then I went to ADC1 and enabled Regular_Conversion_Mode, set Number_Of_Conversions to 1 and the External_Trigger_Conversion_Source to Timer 2 Trigger Out event. After that I set up a DMA in circular mode that pushes half-words to a RAM buffer. For testing I've set the frequency of the timer a lot lower (10Hz) and send some ADC readings from the buffer via UART in the ConvHalfCoplt and ConvCoplt complete callbacks. But at the moment it does not work. Can you think about any mistakes in my approach ?
#include "main.h"
#include <stdio.h>
#include <string.h>
#define ADC_BUF_LEN 4096
ADC_HandleTypeDef hadc1;
DMA_HandleTypeDef hdma_adc1;
DAC_HandleTypeDef hdac1;
DMA_HandleTypeDef hdma_dac1_ch1;
TIM_HandleTypeDef htim2;
UART_HandleTypeDef huart2;
/* USER CODE BEGIN PV */
uint8_t adc_buf[ADC_BUF_LEN];
char msg[16];
/* USER CODE END PV */
/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
static void MX_GPIO_Init(void);
static void MX_DMA_Init(void);
static void MX_USART2_UART_Init(void);
static void MX_ADC1_Init(void);
static void MX_DAC1_Init(void);
static void MX_TIM2_Init(void);
/* Private user code ---------------------------------------------------------*/
/**
* #brief The application entry point.
* #retval int
*/
int main(void)
{
/* MCU Configuration--------------------------------------------------------*/
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
/* Configure the system clock */
SystemClock_Config();
/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_DMA_Init();
MX_USART2_UART_Init();
MX_ADC1_Init();
MX_DAC1_Init();
MX_TIM2_Init();
/* USER CODE BEGIN 2 */
HAL_TIM_Base_Start(&htim2);
HAL_ADC_Start_DMA(&hadc1, (uint32_t*) adc_buf, ADC_BUF_LEN);
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */
}
/**
* #brief System Clock Configuration
* #retval None
*/
void SystemClock_Config(void)
{
RCC_OscInitTypeDef RCC_OscInitStruct = {0};
RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
RCC_PeriphCLKInitTypeDef PeriphClkInit = {0};
/** Initializes the RCC Oscillators according to the specified parameters
* in the RCC_OscInitTypeDef structure.
*/
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.PLLMUL = RCC_PLL_MUL4;
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_HSI;
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();
}
PeriphClkInit.PeriphClockSelection = RCC_PERIPHCLK_ADC12;
PeriphClkInit.Adc12ClockSelection = RCC_ADC12PLLCLK_DIV16;
if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInit) != HAL_OK)
{
Error_Handler();
}
}
/**
* #brief ADC1 Initialization Function
* #param None
* #retval None
*/
static void MX_ADC1_Init(void)
{
ADC_MultiModeTypeDef multimode = {0};
ADC_ChannelConfTypeDef sConfig = {0};
/** Common config
*/
hadc1.Instance = ADC1;
hadc1.Init.ClockPrescaler = ADC_CLOCK_ASYNC_DIV1;
hadc1.Init.Resolution = ADC_RESOLUTION_12B;
hadc1.Init.ScanConvMode = ADC_SCAN_DISABLE;
hadc1.Init.ContinuousConvMode = DISABLE;
hadc1.Init.DiscontinuousConvMode = DISABLE;
hadc1.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_RISING;
hadc1.Init.ExternalTrigConv = ADC_EXTERNALTRIGCONV_T2_TRGO;
hadc1.Init.DataAlign = ADC_DATAALIGN_RIGHT;
hadc1.Init.NbrOfConversion = 1;
hadc1.Init.DMAContinuousRequests = DISABLE;
hadc1.Init.EOCSelection = ADC_EOC_SINGLE_CONV;
hadc1.Init.LowPowerAutoWait = DISABLE;
hadc1.Init.Overrun = ADC_OVR_DATA_OVERWRITTEN;
if (HAL_ADC_Init(&hadc1) != HAL_OK)
{
Error_Handler();
}
/** Configure the ADC multi-mode
*/
multimode.Mode = ADC_MODE_INDEPENDENT;
if (HAL_ADCEx_MultiModeConfigChannel(&hadc1, &multimode) != HAL_OK)
{
Error_Handler();
}
/** Configure Regular Channel
*/
sConfig.Channel = ADC_CHANNEL_1;
sConfig.Rank = ADC_REGULAR_RANK_1;
sConfig.SingleDiff = ADC_SINGLE_ENDED;
sConfig.SamplingTime = ADC_SAMPLETIME_1CYCLE_5;
sConfig.OffsetNumber = ADC_OFFSET_NONE;
sConfig.Offset = 0;
if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)
{
Error_Handler();
}
}
/**
* #brief DAC1 Initialization Function
* #param None
* #retval None
*/
/**
* #brief TIM2 Initialization Function
* #param None
* #retval None
*/
static void MX_TIM2_Init(void)
{
/* USER CODE BEGIN TIM2_Init 0 */
/* USER CODE END TIM2_Init 0 */
TIM_MasterConfigTypeDef sMasterConfig = {0};
TIM_OC_InitTypeDef sConfigOC = {0};
/* USER CODE BEGIN TIM2_Init 1 */
/* USER CODE END TIM2_Init 1 */
htim2.Instance = TIM2;
htim2.Init.Prescaler = 800 - 1;
htim2.Init.CounterMode = TIM_COUNTERMODE_UP;
htim2.Init.Period = 1000 - 1;
htim2.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
htim2.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
if (HAL_TIM_OC_Init(&htim2) != HAL_OK)
{
Error_Handler();
}
sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_ENABLE;
if (HAL_TIMEx_MasterConfigSynchronization(&htim2, &sMasterConfig) != HAL_OK)
{
Error_Handler();
}
sConfigOC.OCMode = TIM_OCMODE_TIMING;
sConfigOC.Pulse = 0;
sConfigOC.OCPolarity = TIM_OCPOLARITY_HIGH;
sConfigOC.OCFastMode = TIM_OCFAST_DISABLE;
if (HAL_TIM_OC_ConfigChannel(&htim2, &sConfigOC, TIM_CHANNEL_1) != HAL_OK)
{
Error_Handler();
}
/* USER CODE BEGIN TIM2_Init 2 */
/* USER CODE END TIM2_Init 2 */
}
/**
* #brief USART2 Initialization Function
* #param None
* #retval None
*/
static void MX_USART2_UART_Init(void)
{
/* USER CODE BEGIN USART2_Init 0 */
/* USER CODE END USART2_Init 0 */
/* USER CODE BEGIN USART2_Init 1 */
/* USER CODE END USART2_Init 1 */
huart2.Instance = USART2;
huart2.Init.BaudRate = 38400;
huart2.Init.WordLength = UART_WORDLENGTH_8B;
huart2.Init.StopBits = UART_STOPBITS_1;
huart2.Init.Parity = UART_PARITY_NONE;
huart2.Init.Mode = UART_MODE_TX_RX;
huart2.Init.HwFlowCtl = UART_HWCONTROL_NONE;
huart2.Init.OverSampling = UART_OVERSAMPLING_16;
huart2.Init.OneBitSampling = UART_ONE_BIT_SAMPLE_DISABLE;
huart2.AdvancedInit.AdvFeatureInit = UART_ADVFEATURE_NO_INIT;
if (HAL_UART_Init(&huart2) != HAL_OK)
{
Error_Handler();
}
/* USER CODE BEGIN USART2_Init 2 */
/* USER CODE END USART2_Init 2 */
}
/**
* Enable DMA controller clock
*/
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);
/* DMA1_Channel3_IRQn interrupt configuration */
HAL_NVIC_SetPriority(DMA1_Channel3_IRQn, 0, 0);
HAL_NVIC_EnableIRQ(DMA1_Channel3_IRQn);
}
/**
* #brief GPIO Initialization Function
* #param None
* #retval None
*/
static void MX_GPIO_Init(void)
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
/* GPIO Ports Clock Enable */
__HAL_RCC_GPIOF_CLK_ENABLE();
__HAL_RCC_GPIOA_CLK_ENABLE();
__HAL_RCC_GPIOB_CLK_ENABLE();
/*Configure GPIO pin Output Level */
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_3, GPIO_PIN_RESET);
/*Configure GPIO pin : PB3 */
GPIO_InitStruct.Pin = GPIO_PIN_3;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
}
/* USER CODE BEGIN 4 */
// Called when first half of buffer is filled
void HAL_ADC_ConvHalfCpltCallback(ADC_HandleTypeDef* hadc){
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_3, GPIO_PIN_SET);
sprintf(msg, "%ho\r\n", adc_buf[0]);
HAL_UART_Transmit(&huart2, (uint8_t*) msg, strlen(msg), HAL_MAX_DELAY);
}
// Called when buffer is completely filled
void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* hadc){
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_3, GPIO_PIN_RESET);
sprintf(msg, "%ho\r\n", adc_buf[ADC_BUF_LEN / 2]);
HAL_UART_Transmit(&huart2, (uint8_t*) msg, strlen(msg), HAL_MAX_DELAY);
}
/* USER CODE END 4 */
/**
* #brief This function is executed in case of error occurrence.
* #retval None
*/
void Error_Handler(void)
{
/* USER CODE BEGIN Error_Handler_Debug */
/* User can add his own implementation to report the HAL error return state */
/* USER CODE END Error_Handler_Debug */
}
#ifdef USE_FULL_ASSERT
/**
* #brief Reports the name of the source file and the source line number
* where the assert_param error has occurred.
* #param file: pointer to the source file name
* #param line: assert_param error line source number
* #retval None
*/
void assert_failed(uint8_t *file, uint32_t line)
{
/* USER CODE BEGIN 6 */
/* User can add his own implementation to report the file name and line number,
tex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */
/* USER CODE END 6 */
}
#endif /* USE_FULL_ASSERT */
##############################################################################
Old:
##############################################################################
What I tried so far is configuring TIM2 to reset every microsecond and start a conversion in the interupt callback:
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim){
// Check which timer triggered this callback
if (htim == &htim2){
HAL_ADC_Start(&hadc1);
HAL_ADC_PollForConversion(&hadc1, HAL_MAX_DELAY);
adc_val = HAL_ADC_GetValue(&hadc1);
}
}
But as far as I know PollForConversion can take some time.
Is it better to create a buffer and use DMA to constantly transfer data from the ADC to the buffer and read a value from there every microsecond ?
Wouldn't I read "old" data that way ?
Running an ADC conversion every 1us is quite a challenging task, with the STM32F3 MCU core running at max. 72MHz "only". Therefore you should solve this task using hardware functionality only:
set up a timer to create a trigger output event every 1us (see description of Master mode selection in TIM control register of Reference Manual). Instead of generating an interrupt your timer can generate a trigger output on an update event:
set Master mode selection bits MSM in TIM2_CR2 to 010 (Update).
bit MSM in TIM2_SMCR should stay at 0
set up the ADC to run a conversion when triggered by the external trigger generated by the timer (see section Conversion on external trigger in ADC chapter of Reference Manual):
set EXTEN to 01 (HW trigger on rising edge) in ADC1_CFGR
set EXTSEL to 1011 (TIM2_TRGO event) in ADC1_CFGR
set up the ADC to generate a DMA request after each conversion (see section Managing conversions using the DMA in ADC chapter of Reference Manual)
set up DMA to store data read from ADC into a RAM buffer (see chapter on DMA controller in Reference Manual). I recommend running the DMA channel in circular mode on a large RAM buffer. This avoids any necessity to reconfigure the DMA during runtime.
With this setup, you can use all MCU clock cycles on processing the large amount of data generated by the ADC in this setup (1 MByte / s). You can either poll the DMA controller to check for new data or use the DMA flags Half Transfer Complete and Transfer Complete to be notified by IRQ each time half of the buffer is filled with new data.
You will have to study the documentation of ADC, Timer and DMA quite a lot to get this setup running - but it is worth the effort since it will solve your task neatly!
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.
I use STM32F405RGT6.
To exchange data between MCU and sensor I need 2 wires: clock wire and signal synchronized with my clock.
Using timer in output compare (OC) mode with update interruptions seems the most suitable solution. But there is a thing, which I can't understand.
I have configured TIM3 to work in OC mode with PB8 pin, which is toggled in the middle of the period.
Timer cause an interruption at every update. Handler of this interruption toggles another pin (PB4).
So I expect to have on my wires the same signals with phase shift in a half of period. It's all right when TIM3 period takes 240ms, but I have significant delay at shorter periods, until synchronization totally fails at period 1μs. TIM3 uses 84MHz APB1 source. Prescaler: /1.
main.c code:
/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "stm32f4xx_hal.h"
/* USER CODE BEGIN Includes */
/* USER CODE END Includes */
/* Private variables ---------------------------------------------------------*/
RTC_HandleTypeDef hrtc;
TIM_HandleTypeDef htim4;
/* USER CODE BEGIN PV */
/* Private variables ---------------------------------------------------------*/
/* USER CODE END PV */
/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
void Error_Handler(void);
static void MX_GPIO_Init(void);
static void MX_TIM4_Init(void);
static void MX_RTC_Init(void);
void HAL_TIM_MspPostInit(TIM_HandleTypeDef *htim);
/* USER CODE BEGIN PFP */
/* Private function prototypes -----------------------------------------------*/
/* USER CODE END PFP */
/* USER CODE BEGIN 0 */
/* USER CODE END 0 */
int main(void)
{
/* USER CODE BEGIN 1 */
/* USER CODE END 1 */
/* MCU Configuration----------------------------------------------------------*/
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
/* Configure the system clock */
SystemClock_Config();
/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_TIM4_Init();
MX_RTC_Init();
/* USER CODE BEGIN 2 */
__HAL_TIM_ENABLE_IT(&htim4, TIM_IT_UPDATE);
HAL_TIM_OC_Start(&htim4, TIM_CHANNEL_3);
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */
}
/** System Clock Configuration
*/
void SystemClock_Config(void)
{
RCC_OscInitTypeDef RCC_OscInitStruct;
RCC_ClkInitTypeDef RCC_ClkInitStruct;
RCC_PeriphCLKInitTypeDef PeriphClkInitStruct;
/**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_LSI|RCC_OSCILLATORTYPE_HSE;
RCC_OscInitStruct.HSEState = RCC_HSE_ON;
RCC_OscInitStruct.LSIState = RCC_LSI_ON;
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
RCC_OscInitStruct.PLL.PLLM = 16;
RCC_OscInitStruct.PLL.PLLN = 224;
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_DIV4;
RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV2;
if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_5) != HAL_OK)
{
Error_Handler();
}
PeriphClkInitStruct.PeriphClockSelection = RCC_PERIPHCLK_RTC;
PeriphClkInitStruct.RTCClockSelection = RCC_RTCCLKSOURCE_LSI;
if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInitStruct) != HAL_OK)
{
Error_Handler();
}
/**Configure the Systick interrupt time
*/
HAL_SYSTICK_Config(HAL_RCC_GetHCLKFreq()/1000);
/**Configure the Systick
*/
HAL_SYSTICK_CLKSourceConfig(SYSTICK_CLKSOURCE_HCLK);
/* SysTick_IRQn interrupt configuration */
HAL_NVIC_SetPriority(SysTick_IRQn, 0, 0);
}
/* RTC init function */
static void MX_RTC_Init(void)
{
/**Initialize RTC Only
*/
hrtc.Instance = RTC;
hrtc.Init.HourFormat = RTC_HOURFORMAT_24;
hrtc.Init.AsynchPrediv = 127;
hrtc.Init.SynchPrediv = 255;
hrtc.Init.OutPut = RTC_OUTPUT_DISABLE;
hrtc.Init.OutPutPolarity = RTC_OUTPUT_POLARITY_HIGH;
hrtc.Init.OutPutType = RTC_OUTPUT_TYPE_OPENDRAIN;
if (HAL_RTC_Init(&hrtc) != HAL_OK)
{
Error_Handler();
}
}
/* TIM4 init function */
static void MX_TIM4_Init(void)
{
TIM_MasterConfigTypeDef sMasterConfig;
TIM_OC_InitTypeDef sConfigOC;
htim4.Instance = TIM4;
htim4.Init.Prescaler = 0;
htim4.Init.CounterMode = TIM_COUNTERMODE_UP;
htim4.Init.Period = 99;
htim4.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
if (HAL_TIM_OC_Init(&htim4) != HAL_OK)
{
Error_Handler();
}
sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
if (HAL_TIMEx_MasterConfigSynchronization(&htim4, &sMasterConfig) != HAL_OK)
{
Error_Handler();
}
sConfigOC.OCMode = TIM_OCMODE_TOGGLE;
sConfigOC.Pulse = 49;
sConfigOC.OCPolarity = TIM_OCPOLARITY_HIGH;
sConfigOC.OCFastMode = TIM_OCFAST_ENABLE;
if (HAL_TIM_OC_ConfigChannel(&htim4, &sConfigOC, TIM_CHANNEL_3) != HAL_OK)
{
Error_Handler();
}
HAL_TIM_MspPostInit(&htim4);
}
/** Configure pins as
* Analog
* Input
* Output
* EVENT_OUT
* EXTI
*/
static void MX_GPIO_Init(void)
{
GPIO_InitTypeDef GPIO_InitStruct;
/* GPIO Ports Clock Enable */
__HAL_RCC_GPIOH_CLK_ENABLE();
__HAL_RCC_GPIOB_CLK_ENABLE();
/*Configure GPIO pin Output Level */
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_4, GPIO_PIN_RESET);
/*Configure GPIO pin : PB4 */
GPIO_InitStruct.Pin = GPIO_PIN_4;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
}
/* USER CODE BEGIN 4 */
/* USER CODE END 4 */
/**
* #brief Period elapsed callback in non blocking mode
* #note This function is called when TIM1 interrupt took place, inside
* HAL_TIM_IRQHandler(). It makes a direct call to HAL_IncTick() to increment
* a global variable "uwTick" used as application time base.
* #param htim : TIM handle
* #retval None
*/
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
/* USER CODE BEGIN Callback 0 */
/* USER CODE END Callback 0 */
if (htim->Instance == TIM1) {
HAL_IncTick();
}
/* USER CODE BEGIN Callback 1 */
else if (htim->Instance == TIM4)
{
HAL_GPIO_TogglePin(GPIOB, GPIO_PIN_4);
}
/* USER CODE END Callback 1 */
}
/**
* #brief This function is executed in case of error occurrence.
* #param None
* #retval None
*/
void Error_Handler(void)
{
/* USER CODE BEGIN Error_Handler */
/* User can add his own implementation to report the HAL error return state */
while(1)
{
}
/* USER CODE END Error_Handler */
}
#ifdef USE_FULL_ASSERT
/**
* #brief Reports the name of the source file and the source line number
* where the assert_param error has occurred.
* #param file: pointer to the source file name
* #param line: assert_param error line source number
* #retval None
*/
void assert_failed(uint8_t* file, uint32_t line)
{
/* USER CODE BEGIN 6 */
/* User can add his own implementation to report the file name and line number,
ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */
/* USER CODE END 6 */
}
#endif
Here is my clock configuration.
clock_conf
TIM3 uses 84MHz APB1 bus, so its minimum period is less than 50ns.
The waveforms at different periods (1 count means 12ns, yellow is for PB4 signal, blue for PB8 - OC):1 20000 counts 2 2000 counts 3 1500 counts 4 1000 counts 5 400 counts 6 200 counts 7 100 counts
Why I can't reach correct synchronization at such rates? MCU, timer and GPIO work at sufficient frequency.
Sorry for my English, it's not my native language.
It's the overhead from the HAL libraries.
Toggling a pin in a timer interrupt would be 2 lines of code
/*
* EDIT
*
* Resetting the status register in the very last statement of an interrupt
* handler might not reach the interrupt controller in time, and the handler
* would be invoked once again. Swapping the two lines would solve it.
*
* wrong order:
*
* void TIM4_IRQHandler() {
* GPIOB->ODR |= (1 << 4);
* TIM4->SR = 0;
* }
*
* right order:
*/
void TIM4_IRQHandler() {
TIM4->SR = 0;
GPIOB->ODR |= (1 << 4);
}
Now look at what an interrupt handler calling HAL is doing.
Loads a handle, and passes it to HAL_TIM_IRQHandler().
HAL_TIM_IRQHandler() retrieves the UART base address from the structure pointed to by the handle.
Loads the timer status register to check if there is a channel 1 capture/compare event. There isn't.
Reloads the timer status register to check if there is a channel 2 capture/compare event. There isn't.
Reloads the timer status register to check if there is a channel 3 capture/compare event. There is one, because the channel is in output compare mode
Reloads the timer status register to check if channel 3 capture/compare interrupt is enabled. It isn't.
Reloads the timer status register to check if there is a channel 4 capture/compare event. There isn't.
Reloads the timer status register to check if there is an update event. Yes there is.
Reloads the timer status register to check if the update interrupt is enabled. It is.
Clears SR.
Calls the callback function.
The callback loads the UART base address from the structure pointed by the handle.
Checks if the interrupt is coming from TIM1. It isn't.
Checks if the interrupt is coming from TIM4. It is.
Finally it calls a function to toggle the pin.
Then it returns to the HAL handler, which checks for 3 more events, which are not even possible on TIM4, reloading the status register for each check.
The MCU was designed with efficient interrupt handling in mind, but using HAL negates this advantage, along with the programmers knowledge of the actual features used. The MCU has an interrupt vector dedicated to each peripheral, but the HAL has a common handler for all timers, which accesses the peripheral through double indirection (pointer to a structure which has a pointer to the registers, and can't be const), making it impossible for a compiler to optimize away the walking through the pointers. You know that only the update interrupt is enabled, but HAL checks all 8 possible events.
I have been trying to understand how things work on the stm32f69 micro-controller board with cortex m7. I found a tutorial writing hello world on the display but I can get it to work on my board. Here is the code I am using
#include "stm32f769xx.h"
#include "stm32f769i_discovery.h"
#include "stm32f769i_discovery_lcd.h"
#include "stm32f7xx_hal.h"
/* Private function prototypes -----------------------------------------------*/
static void LCD_Config(void);
static void SystemClock_Config(void);
static void Error_Handler(void);
static void CPU_CACHE_Enable(void);
int main(void)
{
// enable cpu cache
CPU_CACHE_Enable();
//
HAL_Init();
//
LCD_Config();
/* Our main starts here */
uint16_t ypos = 0, ymax = 0;
int8_t yincr = 1;
BSP_LCD_SetTextColor(LCD_COLOR_WHITE);
BSP_LCD_SetBackColor(LCD_COLOR_BLACK);
while(1) {
if(ypos == 0) {
yincr = 1;
ymax = BSP_LCD_GetYSize();
} else {
yincr = -1;
ymax = 0;
}
for(;yincr == 1 ? ypos < BSP_LCD_GetYSize() - Font24.Height : ypos > 0; ypos+=yincr) {
BSP_LCD_DisplayStringAt(0, ypos, (uint8_t*)"Hello to everyone!", CENTER_MODE);
}
}
for(;;);
}
/**
* #brief System Clock Configuration
* #param None
* #retval None
*/
void SystemClock_Config(void)
{
RCC_ClkInitTypeDef RCC_ClkInitStruct;
RCC_OscInitTypeDef RCC_OscInitStruct;
HAL_StatusTypeDef ret = HAL_OK;
/* Enable HSE Oscillator and activate PLL with HSE as source */
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
RCC_OscInitStruct.HSEState = RCC_HSE_ON;
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
RCC_OscInitStruct.PLL.PLLM = 25;
RCC_OscInitStruct.PLL.PLLN = 400;
RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV2;
RCC_OscInitStruct.PLL.PLLQ = 8;
ret = HAL_RCC_OscConfig(&RCC_OscInitStruct);
if(ret != HAL_OK)
{
while(1) { ; }
}
/* Activate the OverDrive to reach the 200 MHz Frequency */
ret = HAL_PWREx_EnableOverDrive();
if(ret != HAL_OK)
{
while(1) { ; }
}
/* Select PLL as system clock source and configure the HCLK, PCLK1 and PCLK2 clocks dividers */
RCC_ClkInitStruct.ClockType = (RCC_CLOCKTYPE_SYSCLK | RCC_CLOCKTYPE_HCLK | RCC_CLOCKTYPE_PCLK1 | RCC_CLOCKTYPE_PCLK2);
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV4;
RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV2;
ret = HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_6);
if(ret != HAL_OK)
{
while(1) { ; }
}
}
static void LCD_Config(void)
{
/* LCD Initialization */
BSP_LCD_Init();
/* LCD Initialization */
BSP_LCD_LayerDefaultInit(0, LCD_FB_START_ADDRESS);
/* Enable the LCD */
BSP_LCD_DisplayOn();
/* Select the LCD Background Layer */
BSP_LCD_SelectLayer(0);
/* Clear the Background Layer */
BSP_LCD_Clear(LCD_COLOR_BLACK);
/* Configure the transparency for background */
BSP_LCD_SetTransparency(0, 100);
}
/**
* #brief This function is executed in case of error occurrence.
* #param None
* #retval None
*/
static void Error_Handler(void)
{
/* Turn LED1 on */
BSP_LED_On(LED1);
while(1)
{
}
}
/**
* #brief CPU L1-Cache enable.
* #param None
* #retval None
*/
static void CPU_CACHE_Enable(void)
{
/* Enable I-Cache */
SCB_EnableICache();
/* Enable D-Cache */
SCB_EnableDCache();
}
When I examined I kept pausing the execution I noticed that that there is one function specifically stalling the execution which is called HAL_DSI_ShortWrite(). Within this function it gets stuck in the following loop
/* Wait for Command FIFO Empty */
while((hdsi->Instance->GPSR & DSI_GPSR_CMDFE) == 0)
{
/* Check for the Timeout */
if((HAL_GetTick() - tickstart ) > DSI_TIMEOUT_VALUE)
{
/* Process Unlocked */
__HAL_UNLOCK(hdsi);
return HAL_TIMEOUT;
}
}
which is provided inside the library supplied by the st board manufactor. is there something I should have done before calling BSP_LCD_Init ?
Here is a sample of the stack
So, you garner more information from this (pg 1964), but the long and the short is that you need to configure the clocks. According to the document,
**What programming model sequence should I follow to use HAL drivers ? **
Follow the sequence below to use the APIs provided in the HAL drivers:
Call HAL_Init() function to initialize the system (data cache, NVIC priority,...).
Initialize the system clock by calling HAL_RCC_OscConfig() followed by
HAL_RCC_ClockConfig().
Add HAL_IncTick() function under SysTick_Handler() ISR function to enable polling process when using HAL_Delay() function
Start initializing your peripheral by calling HAL_PPP_Init().
Implement the hardware low level initialization (Peripheral clock, GPIO, DMA
,..) by calling HAL_PPP_MspInit() inxx_hal_msp.c
Start your process operation by calling IO operation functions.
This will look something like:
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
RCC_OscInitTypeDef RCC_OscInitStruct;
/* Enable HSE Oscillator and activate PLL with HSE as source */
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
RCC_OscInitStruct.HSEState = RCC_HSE_ON;
RCC_OscInitStruct.HSIState = RCC_HSI_OFF;
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
RCC_OscInitStruct.PLL.PLLM = 25;
RCC_OscInitStruct.PLL.PLLN = 432;
RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV2;
RCC_OscInitStruct.PLL.PLLQ = 9;
HAL_RCC_OscConfig(&RCC_OscInitStruct);
RCC_ClkInitTypeDef RCC_ClkInitStruct;
RCC_ClkInitStruct.ClockType = (RCC_CLOCKTYPE_SYSCLK | RCC_CLOCKTYPE_HCLK | RCC_CLOCKTYPE_PCLK1 | RCC_CLOCKTYPE_PCLK2);
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV4;
RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV2;
HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_7);
BSP_LCD_Init();
Depending on your requirements.