Timer "One-Pulse" mode

Post here first, or if you can't find a relevant section!
User avatar
RogerClark
Posts: 7680
Joined: Mon Apr 27, 2015 10:36 am
Location: Melbourne, Australia
Contact:

Timer "One-Pulse" mode

Post by RogerClark » Sat Feb 10, 2018 6:32 am

Guys

If anyone has used One-Pulse mode, could you let me know

I need this for one of my personal projects, so I'm attempting to write the code for it and add to the core

In One Pulse mode, an external signal can be used to trigger one of the timers, which in turn creates a pulse after a specific delay, with specific duration


Code: Select all

Let’s use TI2FP2 as trigger 1:
• Map TI2FP2 on TI2 by writing CC2S=01 in the TIMx_CCMR1 register.
• TI2FP2 must detect a rising edge, write CC2P=0 in the TIMx_CCER register.
• Configure TI2FP2 as trigger for the slave mode controller (TRGI) by writing TS=110 in
the TIMx_SMCR register.
• TI2FP2 is used to start the counter by writing SMS to ‘110 in the TIMx_SMCR register
(trigger mode).
The OPM waveform is defined by writing the compare registers (taking into account the
clock frequency and the counter prescaler).
• The tDELAY is defined by the value written in the TIMx_CCR1 register.
• The tPULSE is defined by the difference between the auto-reload value and the compare
value (TIMx_ARR - TIMx_CCR + 1).
• Let us say user wants to build a waveform with a transition from ‘0 to ‘1 when a
compare match occurs and a transition from ‘1 to ‘0 when the counter reaches the
auto-reload value. To do this enable PWM mode 2 by writing OC1M=111 in the
TIMx_CCMR1 register. The user can optionally enable the preload registers by writing
OC1PE=1 in the TIMx_CCMR1 register and ARPE in the TIMx_CR1 register. In this
case write the compare value in the TIMx_CCR1 register, the auto-reload value in the
TIMx_ARR register, generate an update by setting the UG bit and wait for external
trigger event on TI2. CC1P is written to ‘0 in this example.
In our example, the DIR and CMS bits in the TIMx_CR1 register should be low.
User only wants one pulse (Single mode), so write '1 in the OPM bit in the TIMx_CR1
register to stop the counter at the next update event (when the counter rolls over from the
auto-reload value back to 0). When OPM bit in the TIMx_CR1 register is set to '0', so the
Repetitive Mode is selected.
stm32_one_pulse_mode.png
stm32_one_pulse_mode.png (16.57 KiB) Viewed 193 times
So far my code looks like this

Code: Select all

void setup() 
{
  const uint16_t pulseDelay=1000;
  const uint16_t pulseWidth=500;
  
  pinMode(PA0,INPUT);
  pinMode(PA1,PWM);// Timer 2 channel 2 output pin
  Timer2.setPrescaleFactor(72);

  Timer2.setCompare(pulseDelay);// Delay value
  Timer2.setOverflow(pulseWidth + pulseDelay-1);
  Timer2.setMode(TIMER_CH2, TIMER_ONE_PULSE);


}
and I've added this to timer.c

Code: Select all

static void one_pulse_mode(timer_dev *dev,uint8 channel ) {
    timer_oc_set_mode(dev, channel, 0, TIMER_CCMR1_CC1S_INPUT_TI1);

	timer_slave_set_flags(dev,TIMER_SMCR_TS_TI2FP2 | TIMER_SMCR_SMS_EXTERNAL);
	// Setup auto reload values
    timer_oc_set_mode(dev, channel, TIMER_OC_MODE_PWM_2, TIMER_CCMR1_OC1PE);
	*bb_perip(&(dev->regs).bas->CR1, TIMER_CR1_CEN_BIT) = TIMER_CR1_ARPE;
	
}

But unfortunately I'm not getting any output on PA1

I think the problem is that I can't see how to configure Timer2 CH2

User avatar
RogerClark
Posts: 7680
Joined: Mon Apr 27, 2015 10:36 am
Location: Melbourne, Australia
Contact:

Re: Timer "One-Pulse" mode

Post by RogerClark » Sat Feb 10, 2018 6:40 am

Update

PWM is working OK because I can test the values using

Code: Select all

  
    const uint16_t pulseDelay=250;
  const uint16_t pulseWidth=1000;
  pinMode(PA1,PWM);// Timer 2 channel 2 output pin
  Timer2.setPrescaleFactor(72);
  Timer2.setCompare(2,pulseDelay);// Delay value
  Timer2.setOverflow(pulseWidth + pulseDelay-1);

In my original code

PA1 seems to be driven high as soon as the code starts, but is not being triggered by the input on PA0 :-(

stevestrong
Posts: 2050
Joined: Mon Oct 19, 2015 12:06 am
Location: Munich, Germany
Contact:

Re: Timer "One-Pulse" mode

Post by stevestrong » Sat Feb 10, 2018 5:33 pm

Roger, you forgot to setup the counter for one pulse mode, and for slave triggered by TI1.

Code: Select all

	// counter setup in one pulse mode, as slave triggered by TI1
	TIMER2_BASE->CR1  = ( TIMER_CR1_OPM ); // one pulse mode
	TIMER2_BASE->SMCR = ( TIMER_SMCR_TS_TI1FP1 | TIMER_SMCR_SMS_TRIGGER );
As I could not find any function in the core which would do this, direct register setting is needed.
But I welcome your initiative to define new function to write slave flags ;)


Here a working complete example (tested on bluepill):

Code: Select all

/*
 * Example of the Timer Input Capture mode combined with one pulse mode
 *  
 * This example uses:
 * - Timer2 channel 1 as capture input
 * - Timer2 channel 2 to generate the pulse,
 * - Timer3 channel 1 to generate a trigger signal for capture input (PWM)
 */

#include <Streaming.h>

void setup()
{
	// setup PA1 (Timer2 channel 2) to PWM (one pulse mode)
	pinMode(PA1, PWM);
	// setup PA0 (Timer 2 channel 1) as input (capture input mode)
	pinMode(PA0, INPUT);

	// stop the timers before configuring them
	Timer2.pause();
#if 0
	// this is limiting the channel compare input to the same input.
	// But channel 1 can have TI2 as input, and channel 2 can have TI1 as input
	Timer2.setMode(TIMER_CH1, TIMER_INPUT_CAPTURE);
#endif

	const uint16_t pulseDelay = 300;
	const uint16_t pulseWidth = 200;

	Timer2.setPrescaleFactor(72); // 1 microsecond resolution
	Timer2.setCompare(TIMER_CH2, pulseDelay);
	Timer2.setOverflow(pulseWidth + pulseDelay-1);

	// counter setup in one pulse mode, as slave triggered by TI1
	TIMER2_BASE->CR1  = ( TIMER_CR1_OPM ); // one pulse mode
	TIMER2_BASE->SMCR = ( TIMER_SMCR_TS_TI1FP1 | TIMER_SMCR_SMS_TRIGGER );

	// channel 1: capture input on rising edge, channel 2: PWM mode 2
	TIMER2_BASE->CCMR1 = ( TIMER_CCMR1_CC1S_INPUT_TI1 | TIMER_CCMR1_OC2M );
	// enable channels 1 and 2
	TIMER2_BASE->CCER  = ( TIMER_CCER_CC1E | TIMER_CCER_CC2E );
	// start timer 2
	Timer2.refresh();
	Timer2.resume(); // let timer 2 run

	// setup PA6 (Timer3 channel 1) to generate 1 ms period PWM with 10% DC
	pinMode(PA6, PWM);
	Timer3.pause();
	Timer3.setPrescaleFactor(72); // 1 µsecond resolution
	Timer3.setCompare(TIMER_CH1, 100);
	Timer3.setOverflow(1000);
	Timer3.refresh();
	Timer3.resume(); // let timer 3 run
}

uint32_t t;
void loop()
{
	if ( (millis()-t)>1000 )
	{
		t = millis();
		Serial << millis()
			<< ", TIM2->CCMR1: " << _HEX(TIMER2_BASE->CCMR1)
			<< ", TIM2->CCER: " << _HEX(TIMER2_BASE->CCER)
			<< ", TIM2->SMCR: " << _HEX(TIMER2_BASE->SMCR)
			<< ", TIM3->CCMR1: " << _HEX(TIMER3_BASE->CCMR1)
			<< ", TIM3->CCER: " << _HEX(TIMER3_BASE->CCER)
			<< ", TIM3->SMCR: " << _HEX(TIMER3_BASE->SMCR) << endl;
	}
}
Looking at the serial monitor output one can see that timer 3 channel 2 is somehow also set to PWM mode - which is not wanted...to be investigated.
It seems that this is done in startup phase in timer_default_config() (variants/<variant>/wirish/boards.cpp).

Code: Select all

10010, TIM2->CCMR1: 7001, TIM2->CCER: 11, TIM2->SMCR: 56, TIM3->CCMR1: 6868, TIM3->CCER: 1, TIM3->SMCR: 0

EDIT
For this to work the proposed patch is needed: https://github.com/rogerclarkmelbourne/ ... 2/pull/440

And btw, the new input capture mode function is limiting the input for the selected channel, and will not work for channels 2 and 4, it needs other defines to select other input options (I also commented on github for the commit)

User avatar
RogerClark
Posts: 7680
Joined: Mon Apr 27, 2015 10:36 am
Location: Melbourne, Australia
Contact:

Re: Timer "One-Pulse" mode

Post by RogerClark » Sat Feb 10, 2018 9:04 pm

Steve

Wow...

Thanks
I will test it on my mains dimmer board.


BTW.
On a slightly different subject...

I am not sure if perhaps the Input Capture may not function absolutely correctly

At the moment I need to store the last counter value recorded by the ISR and subtract it from the current value.

So the counter may actually be free runnnig.

I am using code supplied by @cesco but perhaps he omitted something to hold the count and also to reload with 0 at the start of the input pulse.

User avatar
RogerClark
Posts: 7680
Joined: Mon Apr 27, 2015 10:36 am
Location: Melbourne, Australia
Contact:

Re: Timer "One-Pulse" mode

Post by RogerClark » Sat Feb 10, 2018 11:12 pm

Steve

I've tried the code but its not doing what it should be :-(

On the rising edge of PA0, PA1 toggles state

I tried changing the setCompare and the setOverflow but it didn't make any difference

I think this is closer to what its supposed to be doing, but it doesnt seem to be quite working yet ;-)

stevestrong
Posts: 2050
Joined: Mon Oct 19, 2015 12:06 am
Location: Munich, Germany
Contact:

Re: Timer "One-Pulse" mode

Post by stevestrong » Sat Feb 10, 2018 11:21 pm

Well it kind of does it for me:
Timer input capture - one pulse mode.jpg
Timer input capture - one pulse mode.jpg (45.1 KiB) Viewed 114 times
Yellow is the trigger signal generated by timer3 ch 1.
Cyan is the one pulse train generated 300 ms after each yellow trigger rising edge = pulseDelay.
Each cyan pulse duration is 200 ms, as specified in the sketch = pulseWidth.

But it is still not clear to me what exactly do you expect. Please explain in more details

User avatar
RogerClark
Posts: 7680
Joined: Mon Apr 27, 2015 10:36 am
Location: Melbourne, Australia
Contact:

Re: Timer "One-Pulse" mode

Post by RogerClark » Sat Feb 10, 2018 11:30 pm

Steve

BTW.

I see what you mean about not setting up the mode at all

TIMER2_BASE->CR1 = ( TIMER_CR1_OPM ); // one pulse mode

The instructions to do that were in a completely different paragraph in the manual


Also.

I see that you were using the Timer3 output on PA6

I'll change my hardware and try that

dannyf
Posts: 228
Joined: Wed May 11, 2016 4:29 pm

Re: Timer "One-Pulse" mode

Post by dannyf » Sun Feb 11, 2018 12:35 am

It might be easier to just poke the registers. The timer needs to be in output compare mode, not own mode.

I still don't see much benefits in going one pulse here. A good old timeout will do.

User avatar
RogerClark
Posts: 7680
Joined: Mon Apr 27, 2015 10:36 am
Location: Melbourne, Australia
Contact:

Re: Timer "One-Pulse" mode

Post by RogerClark » Sun Feb 11, 2018 3:03 am

Steve

I got it working

Code: Select all

#include <Streaming.h>
uint16_t pulseDelay = 3000;
uint16_t pulseWidth = 100;
void setup()
{
  pinMode(PA1, PWM);  // setup PA1 (Timer2 channel 2) to PWM (one pulse mode)
  pinMode(PA0, INPUT);  // setup PA0 (Timer 2 channel 1) as input (capture input mode)

  Timer2.pause();  // stop the timers before configuring them

  timer_oc_set_mode(TIMER2, 2, TIMER_OC_MODE_PWM_2, TIMER_OC_PE);

  Timer2.setPrescaleFactor(72); // 1 microsecond resolution
  Timer2.setOverflow(pulseWidth + pulseDelay-1);
  Timer2.setCompare(TIMER_CH2, pulseDelay);


  // counter setup in one pulse mode, as slave triggered by External input for Timer 2
  TIMER2_BASE->CR1  = ( TIMER_CR1_OPM ); // one pulse mode
  TIMER2_BASE->SMCR = ( TIMER_SMCR_TS_ETRF | TIMER_SMCR_SMS_TRIGGER );

  TIMER2_BASE->CCER  = ( TIMER_CCER_CC1E | TIMER_CCER_CC2E );  // enable channels 1 and 2

  Timer2.refresh();  // start timer 2
  Timer2.resume(); // let timer 2 run

}

void updateDelay(uint16_t dly)
{
    Timer2.setOverflow(pulseWidth + dly-1);    
    Timer2.setCompare(TIMER_CH2, dly);
    Timer2.refresh();  // start timer 2
}

char buf[16];
char *bPos;
void loop()
{
  if (Serial.available())
  {
    bPos=buf;
    while(Serial.available())
    {
      *bPos++ = Serial.read();
    }
    *bPos++='\0';
    updateDelay(atoi(buf));
  }
}
My main problem(s) were.


1. Not setting Timer2 into OPM at all (my mistake, the example in the manual did not make that totally clear to me)
2. Using the example setting where the trigger or Timer2 was Timer1, however I wanted trigger to be external input


Note. In the code i posted, I still have some things to tidy up.... I don't know why, but I have to call pinMode(PA1, PWM);
even though later in the code I need to do

Code: Select all

  timer_oc_set_mode(TIMER2, 2, TIMER_OC_MODE_PWM_2, TIMER_OC_PE);
Which should be the same thing, except I need for the PWM pulse to go high after the Tdelay, rather than go low


Also..
One strange thing.. When I change the overflow and compare values.

I have to do it in this order

Code: Select all

void updateDelay(uint16_t dly)
{
    Timer2.setOverflow(pulseWidth + dly-1);    // MUST DO THIS FIRST
    Timer2.setCompare(TIMER_CH2, dly);
    Timer2.refresh();  // start timer 2
}
If I do it the other way around, then the pulse width becomes incorrect if I set a higher value for setCompare than is currently in the register
(The pulse seems to get much longer)

But I checked timer.h and what code is in setOverflow() and setCompare and they just update the registers.
Also I tried pausing the timer, but it doesn't make any difference.

All I need to do is the things in updateDelay and it seems to work fine

User avatar
Pito
Posts: 1734
Joined: Sat Mar 26, 2016 3:26 pm
Location: Rapa Nui

Re: Timer "One-Pulse" mode

Post by Pito » Sun Feb 11, 2018 11:19 am

Off topic:
I think you triggers the load "on" at zero crossing, and at specific time you switch off the load (with the pulse). Based on the timing you manages the power.
Does it creates EMI as well when switching off during an active period (off the zero)?
Would not be better to regulate the power by switching on/off at zero crossing? I mean by counting sine periods (like 100 periods = 1kW, 1 period = 10W)..
Pukao Hats Cleaning Services Ltd.

Post Reply