Loading...
 

SW4STM32 and SW4Linux fully supports the STM32MP1 asymmetric multicore Cortex/A7+M4 MPUs

   With System Workbench for Linux, Embedded Linux on the STM32MP1 family of MPUs from ST was never as simple to build and maintain, even for newcomers in the Linux world. And, if you install System Workbench for Linux in System Workbench for STM32 you can seamlessly develop and debug asymmetric applications running partly on Linux, partly on the Cortex-M4.
You can get more information from the ac6-tools website and download (registration required) various documents highlighting:

System Workbench for STM32


pwm with one shot timer

Hi

I’m sucessfully running a timer in STM32F103xx as PWM generator on 4 CHs to drive 4 servos.
This generates pulses of variable length between 1ms and 2ms.

My Problem now is the update of the pulse length. I get a HAL_TIM_PeriodElapsedCallback and HAL_TIM_PWM_PulseFinishedCallback for each channel. There is no notification to do the writes to TIM2->CCRx, setting the pulse lenght, just in time. So there is a latency which I try to avoid.

I would like to start the timer with interrupt generation in one shot mode, then do my calculations after HAL_TIM_PeriodElapsedCallback und then start the timer again.

How to do this with CubeMX?
Is “output compare CHx” mode “inactive on match” “one pulse mode” the right way?
How to restart/trigger the thing?

timer “output compare CHx” mode “inactive on match” “one pulse mode” in CubeMX gives:

void MX_TIM2_Init(void)
{
  TIM_MasterConfigTypeDef sMasterConfig;
  TIM_OC_InitTypeDef sConfigOC;

  htim2.Instance = TIM2;
  htim2.Init.Prescaler = 35;
  htim2.Init.CounterMode = TIM_COUNTERMODE_UP;
  htim2.Init.Period = 9999;
  htim2.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
  if (HAL_TIM_OC_Init(&htim2) != HAL_OK)
  {
    Error_Handler();
  }

  if (HAL_TIM_OnePulse_Init(&htim2, TIM_OPMODE_SINGLE) != HAL_OK)
  {
    Error_Handler();
  }

  sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
  sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
  if (HAL_TIMEx_MasterConfigSynchronization(&htim2, &sMasterConfig) != HAL_OK)
  {
    Error_Handler();
  }

  sConfigOC.OCMode = TIM_OCMODE_INACTIVE;
  sConfigOC.Pulse = 0;
  sConfigOC.OCPolarity = TIM_OCPOLARITY_HIGH;
  sConfigOC.OCFastMode = TIM_OCFAST_DISABLE;
  if (HAL_TIM_OC_ConfigChannel(&htim2, &sConfigOC, TIM_CHANNEL_1) != HAL_OK)
  {
    Error_Handler();
  }

  if (HAL_TIM_OC_ConfigChannel(&htim2, &sConfigOC, TIM_CHANNEL_2) != HAL_OK)
  {
    Error_Handler();
  }

  if (HAL_TIM_OC_ConfigChannel(&htim2, &sConfigOC, TIM_CHANNEL_3) != HAL_OK)
  {
    Error_Handler();
  }

  if (HAL_TIM_OC_ConfigChannel(&htim2, &sConfigOC, TIM_CHANNEL_4) != HAL_OK)
  {
    Error_Handler();
  }

  HAL_TIM_MspPostInit(&htim2);

}


When used like this there are interrupts but no pulses:

void main(void) {
...
    HAL_TIM_OC_Start_IT(&htim2, TIM_CHANNEL_1);
    HAL_TIM_OC_Start_IT(&htim2, TIM_CHANNEL_2);
    HAL_TIM_OC_Start_IT(&htim2, TIM_CHANNEL_3);
    HAL_TIM_OC_Start_IT(&htim2, TIM_CHANNEL_4);

    __HAL_TIM_ENABLE_IT(&htim2, TIM_IT_UPDATE);
...
    while (1)
    {

        if (PeriodElapsed == 1)  {

            ....

            TIM2->CCR1 = servos[0];
            TIM2->CCR2 = servos[1];
            TIM2->CCR3 = servos[2];
            TIM2->CCR4 = servos[3];

            HAL_TIM_OC_Start_IT(&htim2, TIM_CHANNEL_1);
            HAL_TIM_OC_Start_IT(&htim2, TIM_CHANNEL_2);
            HAL_TIM_OC_Start_IT(&htim2, TIM_CHANNEL_3);
            HAL_TIM_OC_Start_IT(&htim2, TIM_CHANNEL_4);
         }
    }
}


Sorry for the noise, since this is not an AC6 question.

Dieter

France

Hi,
What is the problem in setting the pulse length in HAL_TIM_PWM_PulseFinishedCallback? It will be valid for the next pulse, and I can’t see how it could be changed earlier; setting it in HAL_TIM_PeriodElapsedCallback may be too late for the current pulse (already started) and will thus have a longer latency.
By the way, what do you mean by “just in time”? As you can’t change the period of the PWM output, but only the cyclic/ration (period/pulse_length) you can not obtain an immediate effect, just an effect on the current (if a pulse is current) or on the next pulse (if between two pulses). Just setting CCRx when you decide what the next pulse length must be wil do its effect as soon as possible.
Bernard

start_of_pulse      Pulsefinishedcallback   Periodelapsedcallback and start of next pulse

|-----------------------|                                   |-----------------------------|
|                             |----------------/ /---- -- --|                                    |------------
calculate|               |update                        |effekt
              |


If doing the updates of CCRs when Periodelapsedcallback arrives the next pulse is already started isn’t it so?
If this is the case, the updates gets in effekt not until the over next pulse starts, because the calculation
of their lengths starts at Periodelapsedcallback.
Or is the next pulse started not until Periodelapsedcallback has terminated? I don’t think so, the hardware is going on.

So I update any CCR in the corresponding Pulsefinishedcallback.

But even then the updates gets in effekt not until the next pulse starts.

The pulses are repaeted with period 5ms. Minimum lenght of pulse is 1ms.

The calculation for the lengths last about 500us started right after Periodelapsedcallback.
Can I update the CCRs shadows for the current pulses while they are not jet finished? I don’t think so,

So, when the calculation is done, it lasts 4.5 ms until it gets in effekt. While inbetween
there are more recent values which could be a more recent foundation for a calculation of the
length of the pulses.

My idea now is to start the pulses by software right after the calculation is done and start
the next calculation right after the period has elapsed. The Period of the timer must be set
to 4.5 ms then to keep the overall period.

Maybe have a look at https://github.com/nichtgedacht/mini-sysQuestion

Regards,
Dieter

France

Hi Dieter,

If what you want is that each pulse length be calculated using the most recent data available, why not compute these about 1ms before the next pulse start? This can be done simply using output comare in another channel of the same timer. Then you will have 500µs for calculating the next pulse length and update the pulse length comparator value and you should have finished 500µs before the next update event (at the end of the period, just before the next pulse starts) having thus enough margin to be sure you will be OK.

However I’m afraid you may have to look at HAL timer code as I’m not sure there is standard APIs to do that. Perhaps a simple call to HAL_TIM_OC_Start_IT with another channel of the same timer will be enough, after having set the corresponding CCxR register to 4/5 of the timer period... (the same register you set to set the pulse length); then HAL_TIM_OC_DelayElapsedCallback should be called about 1ms before the next pulse start, so you had plenty of time to calculate its length...

However, while looking at the code, although the IRQ handler knows what channel has generated the interrupt, it does not pass this information to th eDelayElapsed callback (it even calls both the pulse terminated callback and the delay elapsed callback, as PWM is based on output compare...), so you must (in both callbacks) check if the channel that generate the IRQ is the one you are interested in, by
if(__HAL_TIM_GET_IT_SOURCE(htim, TIM_IT_CC1) !=RESET)


It’s a pity that, while there is 4 independent channels in a timer, the HAL layer does not support using more than 1 channel at the same time... We thus have to trick it to obtain what we need.

Bernard (Ac6)

France

Hi Dieter,

I’ve looked a bit more on the HAL way to do what I’ve sketched above.

I’ve missed at least two things:

  1. You should do a call to HAL_TIM_OC_ConfigChannel for th eoutput compare channel in your initialization routine
  2. In th einterrupt callbacks, you could know which channel is generating th einterrupt by looking at the htim->Channel field


Of course the initialzation part can be simplified a lot if you use CubeMX...

Bernard (Ac6)