cancel
Showing results for 
Search instead for 
Did you mean: 

stm32f411 project TIM2 triggers ADC to sample 4 channels and DMA to transfer to main

PRenn.1
Associate III

Guys, it was really hard giving birth to this proof of concept: 
- Every 100ms the timer hits the ceiling , lessons leaned : trigger event selection setting is irrelevant, it'll work
- in the TIM2 ISR (more precisely : the user callback TIM2's ISR is calling) I start ADC-with-DMA (DMA destination buffer and length given)
- DMA shovels as much results ("conversions" ) as one has confiured in the ADC(!) to DMA Buffer, irrespective of DMA Buffer size.

The weird part is: you can have a 4 word DMA buffer and define only 1 conversion in the ADC setup.
Every call DMA-start of the ADC will fill the NEXT space in the DMA buffer! 
E.g. DMA memorizes, that 100ms ago, it shoveled the (single channel) ADC result at say buffer[2] as as it was instructed that the buffer were 4 words long.   Thus this time it will fill buffer[3] with the result value.  Next iteration, even if a whopping 100ms later it will loop back to buffer[0].

You can define an arbitrary number  of conversions at ADC, say, 4, all of them done from channel1 !
Then DMA will transfer 4 values (channel1, channel1, channel1, channel1) into your 4 word buffer for a single ADC start.
Or - what you probably want - 4 conversions for 4 different channels (ch1 - ch4). Then one start of the ADC will fill your buffer with the values of the four confiured channels.

What a crock you might think, well that was my knee-jerk reaction, too.    But no, it is very smart and flexible:
Setting up say two conversion (channel 1 and channel 2) and a DMA buffer of 8 words you'll get not only the "current" values of the two channels but you still have the 3 historic values of these channels in your DMAbuffer!

The only thing one would need is an indication of "buffer place written to recently".

How to discover the above:
Configure USB_CDC and use CDC_Transmit_FS to ship your debug into back to your PC.
That is what the soap-box ST_Link I've ordere should be able via SWO, the nifty, tiny chinese ST_Links do not feature the SWO backconnunication.

 

The hard part is to come up with the correct callcback function names :

void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef* htim)

void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* hadc)

 



Here is the sourcecode:

 

 

 

#include "main.h" #include "usb_device.h" #include "usbd_cdc_if.h" ADC_HandleTypeDef hadc1; DMA_HandleTypeDef hdma_adc1; TIM_HandleTypeDef htim2; char *buffer[ 200 ]; uint32_t adc_data[ ] = { 22L, 33l, 44l, 55l }; // recognizable initialisation ;) uint32_t adc_convs; uint32_t tim_convs; 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_TIM2_Init(void); void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef* htim){ tim_convs++; HAL_GPIO_WritePin(GPIOC, GPIO_PIN_13, GPIO_PIN_RESET); // LED on HAL_ADC_Start_DMA( &hadc1, adc_data, 4 ); } void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* hadc) // one call per group ! { adc_convs ++; HAL_GPIO_WritePin(GPIOC, GPIO_PIN_13, GPIO_PIN_SET); // LED off } int main(void) { uint32_t cnt = 0l; HAL_Init(); SystemClock_Config(); MX_GPIO_Init(); MX_DMA_Init(); MX_ADC1_Init(); MX_USB_DEVICE_Init(); MX_TIM2_Init(); HAL_TIM_Base_Start_IT(&htim2); // TIM2_Init does NOT start the timer while (1) { cnt ++; HAL_Delay( 20 ); // should be faster than the timer so monitoring doesn't miss sprintf( buffer, "LOOP %ld TIM %ld ADC %ld : %ld, %ld, %ld, %ld\r\n" , cnt, tim_convs, adc_convs , adc_data[0], adc_data[1], adc_data[2], adc_data[3] ); CDC_Transmit_FS( buffer, strlen( buffer ) ); } } 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_SCALE1); 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 = 192; RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV2; RCC_OscInitStruct.PLL.PLLQ = 4; 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_3) != 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_12B; hadc1.Init.ScanConvMode = ENABLE; hadc1.Init.ContinuousConvMode = DISABLE; 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 = 4; hadc1.Init.DMAContinuousRequests = DISABLE; hadc1.Init.EOCSelection = ADC_EOC_SEQ_CONV; if (HAL_ADC_Init(&hadc1) != HAL_OK) { Error_Handler(); } sConfig.Channel = ADC_CHANNEL_1; sConfig.Rank = 1; sConfig.SamplingTime = ADC_SAMPLETIME_3CYCLES; if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK) { Error_Handler(); } sConfig.Channel = ADC_CHANNEL_2; sConfig.Rank = 2; if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK) { Error_Handler(); } sConfig.Channel = ADC_CHANNEL_3; sConfig.Rank = 3; if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK) { Error_Handler(); } sConfig.Channel = ADC_CHANNEL_4; sConfig.Rank = 4; if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK) { Error_Handler(); } } static void MX_TIM2_Init(void) { TIM_ClockConfigTypeDef sClockSourceConfig = {0}; TIM_MasterConfigTypeDef sMasterConfig = {0}; htim2.Instance = TIM2; htim2.Init.Prescaler = 48000; htim2.Init.CounterMode = TIM_COUNTERMODE_UP; htim2.Init.Period = 200; htim2.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1; htim2.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE; if (HAL_TIM_Base_Init(&htim2) != HAL_OK) { Error_Handler(); } sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL; if (HAL_TIM_ConfigClockSource(&htim2, &sClockSourceConfig) != HAL_OK) { Error_Handler(); } sMasterConfig.MasterOutputTrigger = TIM_TRGO_UPDATE; sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE; if (HAL_TIMEx_MasterConfigSynchronization(&htim2, &sMasterConfig) != 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_GPIOC_CLK_ENABLE(); __HAL_RCC_GPIOH_CLK_ENABLE(); __HAL_RCC_GPIOA_CLK_ENABLE(); __HAL_RCC_GPIOB_CLK_ENABLE(); HAL_GPIO_WritePin(GPIOC, GPIO_PIN_13, GPIO_PIN_RESET); GPIO_InitStruct.Pin = GPIO_PIN_13; GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP; GPIO_InitStruct.Pull = GPIO_NOPULL; GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW; HAL_GPIO_Init(GPIOC, &GPIO_InitStruct); } void Error_Handler(void) { __disable_irq(); while (1) { } }
View more

 

 

 

 

0 REPLIES 0