2025-01-16 07:04 AM
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)
{
}
}