I need to generate a pulse burst train. In each burst, there are 120 pulse with pulse width of 1us. And in between the bursts, there is a interphase width in 130us.
I set TIM1 as 1MHz PWM with Duty cycle 50% for 120 pulses burst. Then set 1Mhz PWM with Duty cycle 0% for low voltage interphase width.
When I set the 1MHz PWM, the oscilloscope measures the wrong diagram. The pulse bursts are 360us with pulse width 1us and interphase width 390 us.
When I set 1 kHz, the program works fine. I guess that in the TIMx callback function, the operators need more time than 1us. If like that, I couldn't improve it. Could anyone help to support this debugging or give me another solution?
The following is my MCU setup of ST MCU BLUENRG-LP with system clock frequency of 64 MHz. The following is the setup of Prescaler 3 and Period 15.
/* - Set the pre-scaler value to have TIMx counter clock equal to 16 MHz */
/* - Set the auto-reload value to have a counter frequency of 1M Hz */
timxPrescaler = __LL_TIM_CALC_PSC(LL_TIM_GetPeriphClock(TIMx), 16000000);
timxPeriod = __LL_TIM_CALC_ARR(LL_TIM_GetPeriphClock(TIMx), timxPrescaler, 1000000);
The following is my TIM1 OC PWM settup in STM LL driver.
static void MX_TIMx_Init(void)
{
LL_TIM_InitTypeDef TIM_InitStruct = {0};
LL_TIM_OC_InitTypeDef TIM_OC_InitStruct = {0};
LL_GPIO_InitTypeDef GPIO_InitStruct;
/* Peripheral clock enable */
LL_EnableClock_TIMx();
LL_EnableClock_TIMx_CH1();
GPIO_InitStruct.Pin = TIMx_CH1_PIN;
GPIO_InitStruct.Mode = LL_GPIO_MODE_ALTERNATE;
GPIO_InitStruct.Speed = LL_GPIO_SPEED_FREQ_VERY_HIGH;
GPIO_InitStruct.OutputType = LL_GPIO_OUTPUT_PUSHPULL;
GPIO_InitStruct.Pull = LL_GPIO_PULL_UP;
GPIO_InitStruct.Alternate = TIMx_CH1_AF;
LL_GPIO_Init(TIMx_CH1_PORT, &GPIO_InitStruct);
/* TIMx interrupt Init */
NVIC_SetPriority(TIMx_IRQn, IRQ_MED_PRIORITY);
NVIC_EnableIRQ(TIMx_IRQn);
TIM_InitStruct.Prescaler = timxPrescaler;
TIM_InitStruct.CounterMode = LL_TIM_COUNTERMODE_UP;
TIM_InitStruct.Autoreload = timxPeriod;
TIM_InitStruct.ClockDivision = LL_TIM_CLOCKDIVISION_DIV1;
TIM_InitStruct.RepetitionCounter = 0;
LL_TIM_Init(TIMx, &TIM_InitStruct);
LL_TIM_EnableARRPreload(TIMx);
LL_TIM_OC_EnablePreload(TIMx, LL_TIM_CHANNEL_CH1);
TIM_OC_InitStruct.OCMode = LL_TIM_OCMODE_PWM1;
TIM_OC_InitStruct.OCState = LL_TIM_OCSTATE_DISABLE;
TIM_OC_InitStruct.OCNState = LL_TIM_OCSTATE_DISABLE;
TIM_OC_InitStruct.CompareValue = ((timxPeriod + 1 ) / 2);
TIM_OC_InitStruct.OCPolarity = LL_TIM_OCPOLARITY_HIGH;
TIM_OC_InitStruct.OCNPolarity = LL_TIM_OCPOLARITY_HIGH;
TIM_OC_InitStruct.OCIdleState = LL_TIM_OCIDLESTATE_HIGH;
TIM_OC_InitStruct.OCNIdleState = LL_TIM_OCIDLESTATE_LOW;
LL_TIM_OC_Init(TIMx, LL_TIM_CHANNEL_CH1, &TIM_OC_InitStruct);
LL_TIM_OC_DisableFast(TIMx, LL_TIM_CHANNEL_CH1);
}
The TIMx callback is as follows:
/**
* @brief Timer capture/compare interrupt processing
* @param None
* @retval None
*/
void TimerCaptureCompare_Callback(void)
{
uint32_t CNT, ARR;
CNT = LL_TIM_GetCounter(TIMx);
ARR = LL_TIM_GetAutoReload(TIMx);
if (LL_TIM_OC_GetCompareCH1(TIMx) > ARR )
{
/* If capture/compare setting is greater than autoreload, there is a counter overflow and counter restarts from 0.
Need to add full period to counter value (ARR+1) */
CNT = CNT + ARR + 1;
}
pulse_count = pulse_count - 1;
if(pulse_count == 0)
{
if(isPulseOn)
{
// When pulse burst done, set dutycycle 0%
LL_TIM_OC_SetCompareCH1(TIMx, 0);
isPulseOn = FALSE;
status = INTER;
pulse_count = pulseInfo.interPhaseWidth;
}
else
{
// Set duty cycle 50% and start pulse burst again.
LL_TIM_OC_SetCompareCH1(TIMx, 8);
isPulseOn = TRUE;
stm_stim_status = STIMULATION_PULSE;
pulse_count = pulseInfo.pulseWidth;
// Burst counts
burst_count = burst_count-1;
if( burst_count == 0)
{
burst_count = pulseInfo.burst_amount;
status = IDLE;
MX_TIMx_PWM_Disable(); // disable PWM
MX_GPIO_SetLow(); // GPIO set low
}
}
}
uwMeasuredDutyCycle = (CNT * 100) / ( ARR + 1 );
}
In main loop, the state machine is like this.
void RunDriver(void)
{
switch(status)
{
case INIT:
pulse_count = pulseInfo.pulseWidth;
burst_count = pulseInfo.burst_amount;
// Enable PWM: 1MHz
MX_TIMx_Init();
MX_TIMx_PWM_Enable();
status = PULSE;
break;
case PULSE:
// PWM for 50% duty cycle
break;
case INTER:
// PWM for 0% duty cycle
break;
case IDLE:
default:
driver_count = driver_count+1;
driver_count_time = driver_count * pulseInfo.burst_amount * (pulseInfo.pulseWidth+pulseInfo.interPhaseWidth)*2;
if(driver_count_time >= driver_total)
{
main_status = STM_IDLE;
driver_count = 0;
driver_count_time = 0;
}
else
{
status = INIT;
LL_uDelay(pulseInfo.pulsePeriod / 2, 64);
}
break;
}
}
}