How to synchronise timers

General discussions and questions abound development of code with MicroPython that is not hardware specific.
Target audience: MicroPython Users.
Post Reply
mosi
Posts: 28
Joined: Tue Oct 07, 2014 12:07 am

How to synchronise timers

Post by mosi » Thu Oct 08, 2015 9:18 pm

What is the easiest way to synchronise two timers, say Timer 1 and Timer 8 ?

At first sight, micropython is seemingly unable to guarantee realtime execution of two subsequent lines of code, which is crucial for exact timing sequences. Random time delays occur between the exectution, possibly to micropython higher priority interrupts, garbage collection and USB console.

This can be illustrated on the following example:
I am creating two timers, 1 and 8, each with exactly same parameters and observing both on oscilloscope.
The aim is to get them in sync.
It seems there is no way from current micropython to get them into sync.

Code: Select all

clk_src = pyb.freq()[2]*2
clk_freq=0.01 # kHz
t1_n=1 # timer 1 
t2_n=8 # timer 2
t1_freq=clk_freq*1000*2
t2_freq=clk_freq*1000*2
t1_ch=2
t2_ch=3

t1 = pyb.Timer(t1_n, freq=t1_freq, mode=pyb.Timer.UP, deadtime=256)
#t2 = pyb.Timer(t2_n, freq=t2_freq, mode=pyb.Timer.UP, div=1, , deadtime=2)
t2 = pyb.Timer(t2_n, freq=t2_freq, mode=pyb.Timer.UP, div=1)
ch1 = t1.channel(t1_ch, pyb.Timer.PWM, pin=pyb.Pin.board.B0, pulse_width=(t1.period() + 1) // 2)
ch2 = t2.channel(t2_ch, pyb.Timer.PWM_INVERTED, pin=pyb.Pin.board.B1, pulse_width=(t2.period() + 1) //2)
If you run the code, there seems to be a random phase shift between the two clocks.

What is the easiest way to synchronise two timers?

I have found the following in micropython STM HAL drivers in C:

Code: Select all

#define __TIM1_CLK_ENABLE()    (RCC->APB2ENR |= (RCC_APB2ENR_TIM1EN))
#define __TIM1_CLK_DISABLE()   (RCC->APB2ENR &= ~(RCC_APB2ENR_TIM1EN))
- How can this be implemented in a user C function in micropython source, exposing this to the micropython console?
- or alternatively, how can this be implemented in micropython assembler?

I have found ARM THUMB assembly functions for enabling / disabling RCC timer peripherals, but am lost in translation into
@micropython.asm_thumb

Here is the relevant ASM THUMB code :

Code: Select all

791 /**
//  792   * @brief  Enables or disables the Low Speed APB (APB1) peripheral clock.
//  793   * @param  RCC_APB1Periph: specifies the APB1 peripheral to gates its clock.
//  794   *   This parameter can be any combination of the following values:
//  795   *     RCC_APB1Periph_TIM2, RCC_APB1Periph_TIM3, RCC_APB1Periph_TIM4,
//  796   *     RCC_APB1Periph_TIM5, RCC_APB1Periph_TIM6, RCC_APB1Periph_TIM7,
//  797   *     RCC_APB1Periph_TIM12, RCC_APB1Periph_TIM13, RCC_APB1Periph_TIM14,
//  798   *     RCC_APB1Periph_WWDG, RCC_APB1Periph_SPI2, RCC_APB1Periph_SPI3,
//  799   *     RCC_APB1Periph_USART2, RCC_APB1Periph_USART3, RCC_APB1Periph_UART4,
//  800   *     RCC_APB1Periph_UART5, RCC_APB1Periph_I2C1, RCC_APB1Periph_I2C2,
//  801   *     RCC_APB1Periph_I2C3, RCC_APB1Periph_CAN11, RCC_APB1Periph_CAN12,
//  802   *     RCC_APB1Periph_PWR, RCC_APB1Periph_DAC
//  803   * @param  NewState: new state of the specified peripheral clock.
//  804   *   This parameter can be: ENABLE or DISABLE.
//  805   * @retval None
//  806   */

        SECTION `.text`:CODE:NOROOT(1)
        CFI Block cfiBlock24 Using cfiCommon0
        CFI Function RCC_APB1PeriphClockCmd
        THUMB
//  807 void RCC_APB1PeriphClockCmd(uint32_t RCC_APB1Periph, FunctionalState NewState)
//  808 {
//  809   /* Check the parameters */
//  810   assert_param(IS_RCC_APB1_PERIPH(RCC_APB1Periph));  
//  811   assert_param(IS_FUNCTIONAL_STATE(NewState));
//  812   if (NewState != DISABLE)
RCC_APB1PeriphClockCmd:
        UXTB     R1,R1            ;; ZeroExt  R1,R1,#+24,#+24
        CMP      R1,#+0
        BEQ.N    ??RCC_APB1PeriphClockCmd_0
//  813   {
//  814     RCC->APB1ENR |= RCC_APB1Periph;
        LDR.N    R1,??DataTable43_22  ;; 0x40023840
        LDR      R1,[R1, #+0]
        ORRS     R0,R0,R1
        LDR.N    R1,??DataTable43_22  ;; 0x40023840
        STR      R0,[R1, #+0]
        B.N      ??RCC_APB1PeriphClockCmd_1
//  815   }
//  816   else
//  817   {
//  818     RCC->APB1ENR &= ~RCC_APB1Periph;
??RCC_APB1PeriphClockCmd_0:
        LDR.N    R1,??DataTable43_22  ;; 0x40023840
        LDR      R1,[R1, #+0]
        BICS     R0,R1,R0
        LDR.N    R1,??DataTable43_22  ;; 0x40023840
        STR      R0,[R1, #+0]
//  819   }
//  820 }
??RCC_APB1PeriphClockCmd_1:
        BX       LR               ;; return
        CFI EndBlock cfiBlock24

User avatar
dhylands
Posts: 3821
Joined: Mon Jan 06, 2014 6:08 pm
Location: Peachland, BC, Canada
Contact:

Re: How to synchronise timers

Post by dhylands » Fri Oct 09, 2015 4:04 am

It's possible to use TIM2 as a master to drive both TIM1 and TIM8 which would make them synchronized.

You'd need to tweak the registers directly, using the stm module.

mosi
Posts: 28
Joined: Tue Oct 07, 2014 12:07 am

Re: How to synchronise timers

Post by mosi » Tue Oct 13, 2015 10:36 pm

Found this on timer synchronization,
but am lost in translation.

https://github.com/micropython/micropyt ... Triggering

Then this code:
https://www.rapitasystems.com/blog/chai ... er-stm32f4

Code: Select all

/* enable the timer peripherals: */
RCC->APB1ENR |= RCC_APB1Periph_TIM3;
RCC->APB2ENR |= RCC_APB2Periph_TIM9;
/* Master - TIM3 counts the lower 16 bits */
/* TIM3 should use the clock as its input by default.*/
TIM3->PSC = 0x0000; /* no prescaler. */
/* set clock division to zero: */
TIM3->CR1 &= (uint16_t)(~TIM_CR1_CKD);
TIM3->CR1 |= TIM_CKD_DIV1;
TIM3->CR2 |= 0x20; /* MMS (6:4) */
/* slave - TIM9 counter the upper 16 bits */
TIM9->PSC = 0x0000;
/* set clock division to zero: */
TIM9->CR1 &= (uint16_t)(~TIM_CR1_CKD);
TIM9->CR1 |= TIM_CKD_DIV1;
TIM9->SMCR |= (TIM_TS_ITR1 | TIM_SlaveMode_External1);
/* enable the two counters: */
TIM9->CR1 |= TIM_CR1_CEN;
TIM3->CR1 |= TIM_CR1_CEN;
Any help on translating the above into inline micropython assembler or other form would be much appreciated.

Looking further into things...

User avatar
dhylands
Posts: 3821
Joined: Mon Jan 06, 2014 6:08 pm
Location: Peachland, BC, Canada
Contact:

Re: How to synchronise timers

Post by dhylands » Tue Oct 13, 2015 10:49 pm

There is an stm module (which I don't think is documented anywhere) that allows direct register access.

You can find out the various constants which are available by doing"

Code: Select all

import stm
dir(stm)
I did an example where one timer was gated by another (slightly different from what you're doing, but shows an example of using the stm module). https://github.com/dhylands/upy-example ... r/gated.py

I looked at the timer initialization code that's done in C and only did the bare minimum that I needed to do that wasn't already being done by the C code.

For example, enabling the timers (RCC->APBxENR) is already done by the C code.

User avatar
pythoncoder
Posts: 5956
Joined: Fri Jul 18, 2014 8:01 am
Location: UK
Contact:

Re: How to synchronise timers

Post by pythoncoder » Wed Oct 14, 2015 5:27 am

@mosi To reduce the output to manageable proportions I do

Code: Select all

import stm
[p for p in dir(stm) if p[:3] == 'TIM']
In using stm it's worth noting that register addresses have a base and an offset. So a typical stm call looks like

Code: Select all

stm.mem32[stm.EXTI + stm.EXTI_IMR] |= BIT21
where EXTI is the base address and EXTI_IMR the offset. To make sense of this you need the RM0090 Reference manual http://www.st.com/st-web-ui/static/acti ... 031020.pdf: the stm mnemonics exactly mirror those used in the manual. If you're familiar with programming devices at the binary level the stm module soon makes sense.
Peter Hinch
Index to my micropython libraries.

User avatar
jgriessen
Posts: 191
Joined: Mon Sep 29, 2014 4:20 pm
Contact:

Re: How to synchronise timers

Post by jgriessen » Thu Aug 11, 2016 12:50 am

the stm mnemonics exactly mirror those used in the manual. If you're familiar with programming devices at the binary level the stm module soon makes sense.
Have you found any more docs, or is it just read the source for this?
John Griessen blog.kitmatic.com

User avatar
dhylands
Posts: 3821
Joined: Mon Jan 06, 2014 6:08 pm
Location: Peachland, BC, Canada
Contact:

Re: How to synchronise timers

Post by dhylands » Thu Aug 11, 2016 1:11 am

The stm module is still undocumented, but there are really just the following:

Code: Select all

stm.mem32[address] = value # Writes a 32-bit value to the 4-bytes starting at 'address'
value = stm.mem32[address] # Reads a 32-bit value from the 4-bytes starting at 'address'
You can replace mem32 with mem16 or mem8. mem32 does 32-bit reads/writes. mem16 does 16 bit reads/writes, and mem8 does 8-bit reads/writes.

The remainder of the stm module is constants which are machine generated from the appropriate cmsis/devinc header for the processor for your board. You can see what constants were defined by doing:

Code: Select all

dir(stm)
.

User avatar
jgriessen
Posts: 191
Joined: Mon Sep 29, 2014 4:20 pm
Contact:

Re: How to synchronise timers

Post by jgriessen » Thu Aug 11, 2016 1:55 am

Great! thanks,

that's enough to go on.

JG
John Griessen blog.kitmatic.com

Post Reply