2020-07-07 10:21 AM
Hi guys, I've searched the forum and found a handful of posts with a similar issue but the search doesn't seem to be very good for seeing previous posts...
Anyway, I've got a PWM output from TIM2 CH1 (200us perioud, 25% D.C.) and I have another PWM from TIM2 CH2 with 35% Duty Cycle (now currently set to OC mode but toggles at the same time). I want to enable the ADC through DMA to capture 50 samples from the falling edge of TIM2 CH2. The ADC is set up to be 1.8Msps. According to RM0410 Rev 4. 439/1954 I can indeed use TIM2 CH2 as an external trigger, but my question is how? I previously got this working just using interrupts and then enabling the ADC in the HAL_TIM_PWM_PulseFinishedCallback after checking the correct timer and channel source, but this took way too long and used up the CPU more than I wanted. I've made multiple attempts at solving this but my latest has been this:
Start ADC DMA in the main, enable the ADC external conversion source as Timer 2 CC2 event and then the trigger event selection TRGO as "Update Event"
I start DMA in the main:
int main(void)
{
/* 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_ADC1_Init();
MX_TIM2_Init();
MX_USART3_UART_Init();
/* USER CODE BEGIN 2 */
NVIC_SetPriorityGrouping(0);
SEGGER_SYSVIEW_Conf();
SEGGER_SYSVIEW_Start();
xTaskCreate(vTask_Pulse_ADC, "Pulse and ADC generation/read", 1024, NULL, 1, &xTaskHandler1);
xTaskCreate(vTask_Led, "Led_task", configMINIMAL_STACK_SIZE, NULL, 1, NULL);
xTaskCreate(vTask_LCD, "LCD_task", 1024, NULL, 1, &xTaskHandler3);
vTaskStartScheduler();
if(HAL_ADC_Start_DMA(&hadc1, (uint32_t*)ADC_Val, sizeof(ADC_Val))!= HAL_OK )
{
Error_Handler();
}
I trigger the PWM/OC in a task:
void vTask_Pulse_ADC(void *params)
{
while(1)
{
if(PWM_flag == 0)
{
if(HAL_TIM_PWM_Start_IT(&htim2, TIM_CHANNEL_1) != HAL_OK)
{
Error_Handler();
}
if(HAL_TIM_OC_Start_IT(&htim2, TIM_CHANNEL_2) != HAL_OK)
{
Error_Handler();
}
}
if(pulse_counter >= 4)
{
if(HAL_TIM_PWM_Stop_IT(&htim2, TIM_CHANNEL_1) != HAL_OK)
{
Error_Handler();
}
if(HAL_TIM_PWM_Stop_IT(&htim2, TIM_CHANNEL_2) != HAL_OK)
{
Error_Handler();
}
pulse_counter = 0;
}
}
}
void vTask_Led(void *params)
Here are the configurations for the Timer and ADC:
static void MX_ADC1_Init(void)
{
/* USER CODE BEGIN ADC1_Init 0 */
/* USER CODE END ADC1_Init 0 */
ADC_ChannelConfTypeDef sConfig = {0};
/* USER CODE BEGIN ADC1_Init 1 */
/* USER CODE END ADC1_Init 1 */
/** 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 = ADC_SCAN_DISABLE;
hadc1.Init.ContinuousConvMode = ENABLE;
hadc1.Init.DiscontinuousConvMode = DISABLE;
hadc1.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_RISINGFALLING;
hadc1.Init.ExternalTrigConv = ADC_EXTERNALTRIGCONV_T2_CC2;
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 = ADC_REGULAR_RANK_1;
sConfig.SamplingTime = ADC_SAMPLETIME_3CYCLES;
if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)
{
Error_Handler();
}
/* USER CODE BEGIN ADC1_Init 2 */
/* USER CODE END ADC1_Init 2 */
}
/**
* @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 = 0;
htim2.Init.CounterMode = TIM_COUNTERMODE_UP;
htim2.Init.Period = 20000;
htim2.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
htim2.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
if (HAL_TIM_PWM_Init(&htim2) != HAL_OK)
{
Error_Handler();
}
if (HAL_TIM_OC_Init(&htim2) != HAL_OK)
{
Error_Handler();
}
sMasterConfig.MasterOutputTrigger = TIM_TRGO_UPDATE;
sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
if (HAL_TIMEx_MasterConfigSynchronization(&htim2, &sMasterConfig) != HAL_OK)
{
Error_Handler();
}
sConfigOC.OCMode = TIM_OCMODE_PWM1;
sConfigOC.Pulse = 5000;
sConfigOC.OCPolarity = TIM_OCPOLARITY_HIGH;
sConfigOC.OCFastMode = TIM_OCFAST_DISABLE;
if (HAL_TIM_PWM_ConfigChannel(&htim2, &sConfigOC, TIM_CHANNEL_1) != HAL_OK)
{
Error_Handler();
}
sConfigOC.OCMode = TIM_OCMODE_TOGGLE;
sConfigOC.Pulse = 7500;
if (HAL_TIM_OC_ConfigChannel(&htim2, &sConfigOC, TIM_CHANNEL_2) != HAL_OK)
{
Error_Handler();
}
/* USER CODE BEGIN TIM2_Init 2 */
/* USER CODE END TIM2_Init 2 */
HAL_TIM_MspPostInit(&htim2);
}
CubeMX configurations for completeness
Pausing the debugger and checking the live expression ADC_Val[] shows 0 for every element in the buffer. What I am expecting is to have to enable the ADC DMA in the code initiially and then allow the hardware triggers to enable the ADC conversion in the background with little input from the CPU. Any help as to my inital query would be great. Thanks
2020-07-07 09:15 PM
if you can, use physical pins between timer and adc. it is not clear what event causes the burst of acquisitions. maybe you cab cook a non cyclic dma of 50 toggles on ch2 if your stm32 supports it?
2020-07-08 01:29 AM
Hi, thanks for the reply. My stm32 does support external interrupts to enable the ADC acquistion, and i could possibly get a pin to toggle at 1MHz by dropping the HAL and just toggling the pin with bare metal but this shouldn't be necessary as the datasheet does state that various timers and external interrupts will trigger the ADC. I also didn't want to bother the CPU with toggling a pin. I think I'll start a new simple project without the FreeRTOS to try and tackle this problem. Thanks for the suggestion
2020-07-08 05:54 AM
I have no idea how to do it with HAL or whether it is possible at all, the HAL documentation is too superficial for me to figure it out.
It is definitely possible according to the reference manual. You can try setting the EXTSEL field in ADC->CR2 either to TIM2 CH2 or TIM2 TRGO and set the master mode field in TIM2->CR2 to OC2REF. To get a trigger on the falling edge, use the other PWM mode and invert the channel output polarity.
I have done something similar on a STM32H743, but it has a somewhat different ADC controller, nevertheless you might be able to use it as a starting point to implement it based on the documentation in the reference manual.