2024-10-08 12:50 PM - last edited on 2024-10-09 01:24 AM by SofLit
Is the minimum response time of a OPM trigger defined anywhere? I'm measuring a delay of 50-100ns. I was hoping it would be at maximum a couple clock cycles, but I may be naive.
Running on CM4 at 200MHz of the STM32H757i-eval, using Timer8, Trigger source TI1FP1, output on CH2.
In this example code I'm feeding a PWM channel from TIM1 to TIM8 TI1FP1 with a jumper.
I need to generate a short pulse (~25ns) from the rising edge of an external signal, but it seems like the hardware is not responsive enough to satisfy my requirements. ARR = 5, CCR = 0.
main.c:
/* USER CODE BEGIN Header */
/**
******************************************************************************
* @file : main.c
* @brief : Main program body
******************************************************************************
* @attention
*
* Copyright (c) 2024 STMicroelectronics.
* All rights reserved.
*
* This software is licensed under terms that can be found in the LICENSE file
* in the root directory of this software component.
* If no LICENSE file comes with this software, it is provided AS-IS.
*
******************************************************************************
*/
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "main.h"
/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
/* USER CODE END Includes */
/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */
/* USER CODE END PTD */
/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
#ifndef HSEM_ID_0
#define HSEM_ID_0 (0U) /* HW semaphore 0*/
#endif
/* USER CODE END PD */
/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */
/* USER CODE END PM */
/* Private variables ---------------------------------------------------------*/
TIM_HandleTypeDef htim1;
TIM_HandleTypeDef htim8;
/* USER CODE BEGIN PV */
/* USER CODE END PV */
/* Private function prototypes -----------------------------------------------*/
static void MX_GPIO_Init(void);
static void MX_TIM1_Init(void);
static void MX_TIM8_Init(void);
/* USER CODE BEGIN PFP */
/* USER CODE END PFP */
/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
/* USER CODE END 0 */
/**
* @brief The application entry point.
* @retval int
*/
int main(void)
{
/* USER CODE BEGIN 1 */
/* USER CODE END 1 */
/* USER CODE BEGIN Boot_Mode_Sequence_1 */
/*HW semaphore Clock enable*/
__HAL_RCC_HSEM_CLK_ENABLE();
/* Activate HSEM notification for Cortex-M4*/
HAL_HSEM_ActivateNotification(__HAL_HSEM_SEMID_TO_MASK(HSEM_ID_0));
/*
Domain D2 goes to STOP mode (Cortex-M4 in deep-sleep) waiting for Cortex-M7 to
perform system initialization (system clock config, external memory configuration.. )
*/
HAL_PWREx_ClearPendingEvent();
HAL_PWREx_EnterSTOPMode(PWR_MAINREGULATOR_ON, PWR_STOPENTRY_WFE, PWR_D2_DOMAIN);
/* Clear HSEM flag */
__HAL_HSEM_CLEAR_FLAG(__HAL_HSEM_SEMID_TO_MASK(HSEM_ID_0));
/* USER CODE END Boot_Mode_Sequence_1 */
/* MCU Configuration--------------------------------------------------------*/
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
/* USER CODE BEGIN Init */
/* USER CODE END Init */
/* USER CODE BEGIN SysInit */
/* USER CODE END SysInit */
/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_TIM1_Init();
MX_TIM8_Init();
/* USER CODE BEGIN 2 */
HAL_TIM_PWM_Start(&htim1, TIM_CHANNEL_2);
HAL_TIM_OnePulse_Start(&htim8, TIM_CHANNEL_2);
/* 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 TIM1 Initialization Function
* @PAram None
* @retval None
*/
static void MX_TIM1_Init(void)
{
/* USER CODE BEGIN TIM1_Init 0 */
/* USER CODE END TIM1_Init 0 */
TIM_MasterConfigTypeDef sMasterConfig = {0};
TIM_OC_InitTypeDef sConfigOC = {0};
TIM_BreakDeadTimeConfigTypeDef sBreakDeadTimeConfig = {0};
/* USER CODE BEGIN TIM1_Init 1 */
/* USER CODE END TIM1_Init 1 */
htim1.Instance = TIM1;
htim1.Init.Prescaler = 0;
htim1.Init.CounterMode = TIM_COUNTERMODE_UP;
htim1.Init.Period = 5000;
htim1.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
htim1.Init.RepetitionCounter = 0;
htim1.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
if (HAL_TIM_PWM_Init(&htim1) != HAL_OK)
{
Error_Handler();
}
sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
sMasterConfig.MasterOutputTrigger2 = TIM_TRGO2_RESET;
sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
if (HAL_TIMEx_MasterConfigSynchronization(&htim1, &sMasterConfig) != HAL_OK)
{
Error_Handler();
}
sConfigOC.OCMode = TIM_OCMODE_PWM1;
sConfigOC.Pulse = 500;
sConfigOC.OCPolarity = TIM_OCPOLARITY_HIGH;
sConfigOC.OCNPolarity = TIM_OCNPOLARITY_HIGH;
sConfigOC.OCFastMode = TIM_OCFAST_DISABLE;
sConfigOC.OCIdleState = TIM_OCIDLESTATE_RESET;
sConfigOC.OCNIdleState = TIM_OCNIDLESTATE_RESET;
if (HAL_TIM_PWM_ConfigChannel(&htim1, &sConfigOC, TIM_CHANNEL_2) != HAL_OK)
{
Error_Handler();
}
sBreakDeadTimeConfig.OffStateRunMode = TIM_OSSR_DISABLE;
sBreakDeadTimeConfig.OffStateIDLEMode = TIM_OSSI_DISABLE;
sBreakDeadTimeConfig.LockLevel = TIM_LOCKLEVEL_OFF;
sBreakDeadTimeConfig.DeadTime = 0;
sBreakDeadTimeConfig.BreakState = TIM_BREAK_DISABLE;
sBreakDeadTimeConfig.BreakPolarity = TIM_BREAKPOLARITY_HIGH;
sBreakDeadTimeConfig.BreakFilter = 0;
sBreakDeadTimeConfig.Break2State = TIM_BREAK2_DISABLE;
sBreakDeadTimeConfig.Break2Polarity = TIM_BREAK2POLARITY_HIGH;
sBreakDeadTimeConfig.Break2Filter = 0;
sBreakDeadTimeConfig.AutomaticOutput = TIM_AUTOMATICOUTPUT_DISABLE;
if (HAL_TIMEx_ConfigBreakDeadTime(&htim1, &sBreakDeadTimeConfig) != HAL_OK)
{
Error_Handler();
}
/* USER CODE BEGIN TIM1_Init 2 */
/* USER CODE END TIM1_Init 2 */
HAL_TIM_MspPostInit(&htim1);
}
/**
* @brief TIM8 Initialization Function
* @PAram None
* @retval None
*/
static void MX_TIM8_Init(void)
{
/* USER CODE BEGIN TIM8_Init 0 */
/* USER CODE END TIM8_Init 0 */
TIM_SlaveConfigTypeDef sSlaveConfig = {0};
TIM_MasterConfigTypeDef sMasterConfig = {0};
TIM_OC_InitTypeDef sConfigOC = {0};
TIM_BreakDeadTimeConfigTypeDef sBreakDeadTimeConfig = {0};
/* USER CODE BEGIN TIM8_Init 1 */
/* USER CODE END TIM8_Init 1 */
htim8.Instance = TIM8;
htim8.Init.Prescaler = 0;
htim8.Init.CounterMode = TIM_COUNTERMODE_UP;
htim8.Init.Period = 5;
htim8.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
htim8.Init.RepetitionCounter = 0;
htim8.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
if (HAL_TIM_Base_Init(&htim8) != HAL_OK)
{
Error_Handler();
}
if (HAL_TIM_OC_Init(&htim8) != HAL_OK)
{
Error_Handler();
}
if (HAL_TIM_OnePulse_Init(&htim8, TIM_OPMODE_SINGLE) != HAL_OK)
{
Error_Handler();
}
sSlaveConfig.SlaveMode = TIM_SLAVEMODE_TRIGGER;
sSlaveConfig.InputTrigger = TIM_TS_TI1FP1;
sSlaveConfig.TriggerPolarity = TIM_TRIGGERPOLARITY_RISING;
sSlaveConfig.TriggerFilter = 0;
if (HAL_TIM_SlaveConfigSynchro(&htim8, &sSlaveConfig) != HAL_OK)
{
Error_Handler();
}
sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
sMasterConfig.MasterOutputTrigger2 = TIM_TRGO2_RESET;
sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
if (HAL_TIMEx_MasterConfigSynchronization(&htim8, &sMasterConfig) != HAL_OK)
{
Error_Handler();
}
sConfigOC.OCMode = TIM_OCMODE_RETRIGERRABLE_OPM2;
sConfigOC.Pulse = 0;
sConfigOC.OCPolarity = TIM_OCPOLARITY_HIGH;
sConfigOC.OCNPolarity = TIM_OCNPOLARITY_HIGH;
sConfigOC.OCFastMode = TIM_OCFAST_DISABLE;
sConfigOC.OCIdleState = TIM_OCIDLESTATE_RESET;
sConfigOC.OCNIdleState = TIM_OCNIDLESTATE_RESET;
if (HAL_TIM_OC_ConfigChannel(&htim8, &sConfigOC, TIM_CHANNEL_2) != HAL_OK)
{
Error_Handler();
}
sBreakDeadTimeConfig.OffStateRunMode = TIM_OSSR_DISABLE;
sBreakDeadTimeConfig.OffStateIDLEMode = TIM_OSSI_DISABLE;
sBreakDeadTimeConfig.LockLevel = TIM_LOCKLEVEL_OFF;
sBreakDeadTimeConfig.DeadTime = 0;
sBreakDeadTimeConfig.BreakState = TIM_BREAK_DISABLE;
sBreakDeadTimeConfig.BreakPolarity = TIM_BREAKPOLARITY_HIGH;
sBreakDeadTimeConfig.BreakFilter = 0;
sBreakDeadTimeConfig.Break2State = TIM_BREAK2_DISABLE;
sBreakDeadTimeConfig.Break2Polarity = TIM_BREAK2POLARITY_HIGH;
sBreakDeadTimeConfig.Break2Filter = 0;
sBreakDeadTimeConfig.AutomaticOutput = TIM_AUTOMATICOUTPUT_DISABLE;
if (HAL_TIMEx_ConfigBreakDeadTime(&htim8, &sBreakDeadTimeConfig) != HAL_OK)
{
Error_Handler();
}
/* USER CODE BEGIN TIM8_Init 2 */
/* USER CODE END TIM8_Init 2 */
HAL_TIM_MspPostInit(&htim8);
}
/**
* @brief GPIO Initialization Function
* @PAram None
* @retval None
*/
static void MX_GPIO_Init(void)
{
/* USER CODE BEGIN MX_GPIO_Init_1 */
/* USER CODE END MX_GPIO_Init_1 */
/* GPIO Ports Clock Enable */
__HAL_RCC_GPIOJ_CLK_ENABLE();
/* USER CODE BEGIN MX_GPIO_Init_2 */
/* USER CODE END MX_GPIO_Init_2 */
}
/* USER CODE BEGIN 4 */
/* 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 */
__disable_irq();
while (1)
{
}
/* 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,
ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */
/* USER CODE END 6 */
}
#endif /* USE_FULL_ASSERT */
2024-10-08 01:32 PM - edited 2024-10-08 01:33 PM
Try : setting TIM8 first , then set TIM1 to gen the pwm.
/* USER CODE BEGIN 2 */
HAL_TIM_OnePulse_Start(&htim8, TIM_CHANNEL_2);
HAL_TIM_PWM_Start(&htim1, TIM_CHANNEL_2);
/* USER CODE END 2 */
2024-10-08 01:40 PM
Good catch! That would definitely have affected the first pulse -- but I have it setup as retrigger-able, and am still seeing the same delay in response time for all subsequent pulses.
2024-10-09 01:12 AM
>Good catch!
Yes - but still some delay... -> see Tim input circuit:
+ the inputs need to be sync to internal clock , so they go to one or two flip-flops first, to be sync .
All this running on the timer clock...so at first: higher clock -> less "delay" (for symc circuit).
Then Filter : off. (delay much more, depending on setting).
Then...maybe shortest, you can get this way.
+ Some "sync circuit" you have on all inputs, that come to the internal circuits, to avoid race conditions.
If you need the pulse without any delay, use a simple external gate; maybe controlled by a cpu pin, to en-/disable it.
2024-10-09 12:36 PM - edited 2024-10-09 12:37 PM
Thank you for the reply. I do have filter off, and I could potentially run the CM4 at 240MHz, hopefully yielding ~20% improvement. The delay is still larger than I would have expected.
We're considering an external pulse generator instead of having the H7 do this. I was hoping the H7 would be up to the task, especially because all the pulse generator ICs I've used in the past seem to be going obsolete, such as: https://pdfserv.maximintegrated.com/en/ds/DS1040.pdf . Might have to consider an FPGA for this signal conditioning?
I updated the pin output speeds and might have improved it to ~40ns delay, but that might be the best I can do. Though putting on the scope, on this STM32H757i-eval board I've got some slow rise time on the TIM1 yellow PWM output and voltage drop when I connect TIM1 PWM output to TIM8 TI1FP1.
2024-10-09 01:16 PM
>on the scope
is a DSOX4024A - right ? So you have probes at 10:1 ? and hi-speed probes ? adjusted/calibrated?
because getting about 30ns rise rime is way too slow , for this scope.
Pin output speed H7 at very hi ? (is about 1ns rise time then...so something wrong on your measurement.)
And clock for tim1 is high ? (at 120MHz , APB timer clocks , i think. Makes about 16ns for two FF to set.)
2024-10-09 01:55 PM - edited 2024-10-09 02:04 PM
>consider an FPGA
Come on - an FPGA for one pulse ?
Just 2 UHC nand gates...
...and you have the 20ns pulse, at about 3ns delay; first gate v+ is control: enable or disable (lo) the pulse.
gates:
https://www.mouser.de/ProductDetail/onsemi-Fairchild/NC7WZ38K8X?qs=jumAldekxNB%252BQGRGFjackg%3D%3D
or mini..
(i like this series...)
2024-10-09 09:28 PM
Tested on my nucleo-H743zi2, get 27 nanoseconds delay. uCPU running at 480 MHz.
Reference manual suggest to set TIM_SLAVEMODE_COMBINED_RESETTRIGGER, but I see no difference with
TIM_SLAVEMODE_TRIGGER.
I also checked bits:
00100: TI1 Edge Detector (TI1F_ED) -> dual edge, same delay
00101: Filtered Timer Input 1 (TI1FP1) <- C6
00111: External Trigger input (ETRF) <- A0
to verify if filter logic introduces any additional clock in pipe line, but non. Same delay with ETRF pin.
2024-10-10 11:34 AM
@AScha.3 Thank you for the gate suggestion -- we might have to move in that direction, very concise.
@MasterT Thank you so much for verifying that the extra test cases as well -- you're running 2x+ as fast as my setup, so your 27nanosecond delay tracks with what I'm seeing. I don't see a path forward at this point using the STM32 alone.
This entire exercise started because once turned on and free running, the STM32 SPI peripheral HW timing at 100MHz does satisfy the requirements of a chip we are using.
The tdcnvsckl at 100MHZ is ~ 6ns and can't be modified in the STM32 SPI peripheral. So the idea was to increase the SPI_CS CNV, MIDI[3:0]: master Inter-Data Idleness time a little, then use that to trigger a smaller pulse. But 26-60ns lag time won't work.
marker 0:0, = 6ns
2024-10-10 12:05 PM - edited 2024-10-10 12:15 PM
I need to generate a short pulse (~25ns) from the rising edge of an external signal
For electric solution simple CR circuit is solution... delay is zero .
For MCU you require some with analog comparator for example G4 series