timer source from other timer ?
timer source from other timer ?
I'm struggling a bit with the timers on the STM32f4011. Max time for Tim2 (32 bit) seems to be between 30-60 seconds.
I would like to use the output of one timer as the clock source for several others, like an additional prescaler. I'd prefer to do it this way as it will have minimum impact on system performance.
Not sure if its possible though ...
Any examples or pointers to code about this much appreciated, tried googling but all I got was std timer stuff.
Any help much appreciated.
Best Regards
picclock
I would like to use the output of one timer as the clock source for several others, like an additional prescaler. I'd prefer to do it this way as it will have minimum impact on system performance.
Not sure if its possible though ...
Any examples or pointers to code about this much appreciated, tried googling but all I got was std timer stuff.
Any help much appreciated.
Best Regards
picclock
Re: timer source from other timer ?
you can try playing with the prescaler
https://github.com/stm32duino/wiki/wiki ... er-library
note that there are some dependencies, e.g. if you use uS or Hz for setOverflow(), the prescaler would likely be re-configured.
for very long periods, the problem is you could either have the low periods and the high frequencies would be limited. or target the higher frequencies, and limit the long periods. As if you really need to target long periods, one of them is to reduce the peripheral clocks, e.g. instead of running at 80 mhz to run at 40 mhz or lower, this would likely slow downn all other pheriperials as well, e.g. spi
another way is to daisy chain the timers, e.g. timer 1 drives timer 2 etc, the update events can be chained.
that would allow extra long periods or more elaborate patterns etc.
for that check in the ref manuals, more than likely. you may need to tweak the registers directly.
https://github.com/stm32duino/wiki/wiki ... er-library
note that there are some dependencies, e.g. if you use uS or Hz for setOverflow(), the prescaler would likely be re-configured.
for very long periods, the problem is you could either have the low periods and the high frequencies would be limited. or target the higher frequencies, and limit the long periods. As if you really need to target long periods, one of them is to reduce the peripheral clocks, e.g. instead of running at 80 mhz to run at 40 mhz or lower, this would likely slow downn all other pheriperials as well, e.g. spi
another way is to daisy chain the timers, e.g. timer 1 drives timer 2 etc, the update events can be chained.
that would allow extra long periods or more elaborate patterns etc.
for that check in the ref manuals, more than likely. you may need to tweak the registers directly.
Re: timer source from other timer ?
sounds like you wanted longer intervals.
Three suggestions:
1. chain the timers - read the datasheet;
2. maintain a MSB that you increment in the ISR.
3. if you want precise but long interval, use the compare feature of the timer and augment it with #2 above.
here is an example of how #3 can be done.
I have used something like this many times so it should work.
PS: it requires CMSIS.
Three suggestions:
1. chain the timers - read the datasheet;
2. maintain a MSB that you increment in the ISR.
3. if you want precise but long interval, use the compare feature of the timer and augment it with #2 above.
here is an example of how #3 can be done.
Code: Select all
//set up the timer output compare
//global variables
volatile uint_32 tmr2_ovf=0; //timer 2 overflow
volatile uint_32 tmr2_flg=0; //timer 2 flag. set when time expires
uint_64 tmr2_interval; //timer 2 desire interval - presummably much longer than 32-bit
//initialize variables
//stop tmr2
TIM2->CCR1 = TIM2->CNT; //baseline CCR1
TIM2->CCR1 += tmr2_interval; //use tmr2 compare channel1 - advance the compare register
tmr2_ovf = tmr2_interval >> 32; //tmr2 is a 32-bit timer
set tmr2 compare ch1
//start tmr2
//in tmr2 isr
if (TIM2->SR & TIM_SR_CC1IF) { //compare interrupt flag set
if (tmr2_ovf--==0) { //enough overflow has achieved
TIM2->CCR1 += tmr2_interval; //use tmr2 compare channel1 - advance the compare register
tmr2_ovf = tmr2_interval >> 32; //tmr2 is a 32-bit timer
tmr2_flg = 1; //set the flag
}
}
PS: it requires CMSIS.
Re: timer source from other timer ?
Here is an example for stm32G474re , TIM5 master to clock TIM 2&3
My advise, get yourself CubeMX installed with examples. Code generated with Cube is fully compatible for arduino IDE, since same HAL drivers.
Code: Select all
static void TIM5_Config(void)
{
TIM_HandleTypeDef htim5;
TIM_ClockConfigTypeDef sClockSourceConfig = {0};
TIM_MasterConfigTypeDef sMasterConfig = {0};
TIM_OC_InitTypeDef sConfigOC = {0};
__HAL_RCC_TIM5_CLK_ENABLE();
htim5.Instance = TIM5;
htim5.Init.Prescaler = 0;
htim5.Init.CounterMode = TIM_COUNTERMODE_UP;
htim5.Init.Period = 1;
htim5.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
htim5.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_ENABLE;
if (HAL_TIM_Base_Init(&htim5) != HAL_OK)
{
Error_Handler();
}
sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL;
if (HAL_TIM_ConfigClockSource(&htim5, &sClockSourceConfig) != HAL_OK)
{
Error_Handler();
}
sMasterConfig.MasterOutputTrigger = TIM_TRGO_UPDATE;
sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
if (HAL_TIMEx_MasterConfigSynchronization(&htim5, &sMasterConfig) != HAL_OK)
{
Error_Handler();
}
sConfigOC.OCMode = TIM_OCMODE_PWM2;
sConfigOC.Pulse = 1;
sConfigOC.OCPolarity = TIM_OCPOLARITY_HIGH;
sConfigOC.OCFastMode = TIM_OCFAST_DISABLE;
if (HAL_TIM_PWM_ConfigChannel(&htim5, &sConfigOC, TIM_CHANNEL_1) != HAL_OK)
{
Error_Handler();
}
HAL_TIM_Base_Start(&htim5);
if (HAL_TIM_PWM_Start(&htim5, TIM_CHANNEL_1) != HAL_OK)
{
Error_Handler();
}
}
static void TIM2_Config(void)
{
TIM_HandleTypeDef htim2;
// TIM_ClockConfigTypeDef sClockSourceConfig = {0};
TIM_SlaveConfigTypeDef sSlaveConfig = {0};
TIM_MasterConfigTypeDef sMasterConfig = {0};
TIM_OC_InitTypeDef sConfigOC = {0};
__HAL_RCC_TIM2_CLK_ENABLE();
htim2.Instance = TIM2;
htim2.Init.Prescaler = 0;
htim2.Init.CounterMode = TIM_COUNTERMODE_UP;
htim2.Init.Period = 62;
htim2.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
htim2.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_ENABLE;
if (HAL_TIM_Base_Init(&htim2) != HAL_OK)
{
Error_Handler();
}
/*
sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL;
if (HAL_TIM_ConfigClockSource(&htim2, &sClockSourceConfig) != HAL_OK)
{
Error_Handler();
}
*/
sSlaveConfig.SlaveMode = TIM_SLAVEMODE_EXTERNAL1;//TIM_SLAVEMODE_RESET;
sSlaveConfig.InputTrigger = TIM_TS_ITR4;
if (HAL_TIM_SlaveConfigSynchro(&htim2, &sSlaveConfig) != HAL_OK)
{
Error_Handler();
}
sMasterConfig.MasterOutputTrigger = TIM_TRGO_OC1REF;
sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
if (HAL_TIMEx_MasterConfigSynchronization(&htim2, &sMasterConfig) != HAL_OK)
{
Error_Handler();
}
sConfigOC.OCMode = TIM_OCMODE_PWM2;
sConfigOC.Pulse = 3;
sConfigOC.OCPolarity = TIM_OCPOLARITY_HIGH;
sConfigOC.OCFastMode = TIM_OCFAST_DISABLE;
if (HAL_TIM_PWM_ConfigChannel(&htim2, &sConfigOC, TIM_CHANNEL_1) != HAL_OK)
{
Error_Handler();
}
HAL_TIM_Base_Start(&htim2);
if (HAL_TIM_PWM_Start(&htim2, TIM_CHANNEL_1) != HAL_OK)
{
Error_Handler();
}
}
static void TIM3_Config(void)
{
TIM_HandleTypeDef htim3;
// TIM_ClockConfigTypeDef sClockSourceConfig = {0};
TIM_SlaveConfigTypeDef sSlaveConfig = {0};
TIM_MasterConfigTypeDef sMasterConfig = {0};
TIM_OC_InitTypeDef sConfigOC = {0};
__HAL_RCC_TIM3_CLK_ENABLE();
htim3.Instance = TIM3;
htim3.Init.Prescaler = 0;
htim3.Init.CounterMode = TIM_COUNTERMODE_UP;
htim3.Init.Period = 62;
htim3.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
htim3.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_ENABLE;
if (HAL_TIM_Base_Init(&htim3) != HAL_OK)
{
Error_Handler();
}
/*
sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL;
if (HAL_TIM_ConfigClockSource(&htim3, &sClockSourceConfig) != HAL_OK)
{
Error_Handler();
}
*/
sSlaveConfig.SlaveMode = TIM_SLAVEMODE_EXTERNAL1;//TIM_SLAVEMODE_RESET;
sSlaveConfig.InputTrigger = TIM_TS_ITR4;
if (HAL_TIM_SlaveConfigSynchro(&htim3, &sSlaveConfig) != HAL_OK)
{
Error_Handler();
}
sMasterConfig.MasterOutputTrigger = TIM_TRGO_OC1REF;
sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
if (HAL_TIMEx_MasterConfigSynchronization(&htim3, &sMasterConfig) != HAL_OK)
{
Error_Handler();
}
sConfigOC.OCMode = TIM_OCMODE_PWM2;
sConfigOC.Pulse = 3;
sConfigOC.OCPolarity = TIM_OCPOLARITY_HIGH;
sConfigOC.OCFastMode = TIM_OCFAST_DISABLE;
if (HAL_TIM_PWM_ConfigChannel(&htim3, &sConfigOC, TIM_CHANNEL_1) != HAL_OK)
{
Error_Handler();
}
HAL_TIM_Base_Start(&htim3);
if (HAL_TIM_PWM_Start(&htim3, TIM_CHANNEL_1) != HAL_OK)
{
Error_Handler();
}
}
Re: timer source from other timer ?
Not having a stm32 in front of me so I put this together on a PIC24 to demonstrate the basic concept - porting it over to any other chip isn't difficult.
you can see that it follows what I laid out earlier easily. we are using a 16-bit timer as the time base (this particular chip allows timers to be chained). myOC1ISR() tracks how many "output compare" overflow has taken place and then set a flag, which is to be processed in the main loop - in this case flip a led and write some messages over uart.
Obviously depending on your particular application, you may implement this differently but the basic concept is the same.
Code: Select all
//global defines
#define isDLY32() (dly_flg==1) //true is dly32 has expired
#define clrDLY32() dly_flg=0 //reset the flag
//global variables
uint32_t dly_duration = 0; //dly_duration desired. max 32-bit
volatile uint16_t dly_ovf=0; //overflow counter
uint16_t dly_inc=0; //OC increments: 16-bit time base
volatile int dly_flg=0; //dly_duration expiration flag: 1-> time is up; 0->not yet
//oc1 isr
//the default ISR has been modified so it does not advance OC1R
void myOC1ISR(void) {
if (dly_ovf--==0) { //enough cycles have been reached
dly_ovf = dly_duration >> 16; //reset overflow cycle counter -> 16-bit time base used
//oc1 default ISR modified so not to increment OC1R
//instead, OC1R is incremented here
OC1R += dly_inc; //advance OC1R to the next match point
dly_flg = 1; //set the flag
}
}
//initialize the dly_duration timer - called once in setup()
uint32_t initDLY32(uint32_t duration) {
dly_duration = duration; //remember the duration desired
dly_ovf = dly_duration >> 16; //reset overflow counter: 16-bit time base
dly_inc = dly_duration; //reset dly_duration: 16-bit time base
//set up the timer
//tmr2 free running by default
//nothing needs to be done here
//set up the oc1
//oc1 default isr modified so not to increment OC1R
oc1Init(dly_inc); //initialize oc1 to tmr2
oc1AttachISR(myOC1ISR); //install oc1ISR
//now running
return dly_ovf;
}
Code: Select all
//user defined main loop
void loop(void) {
static uint32_t tick0=0;
uint32_t tmp0;
//if enough time has elapsed
if (isDLY32()) { //if enough time has passed
clrDLY32(); //reset the flag
//tick0 += LED_DLY; //advance to the next match point
pinFlip(LED); //digitalWrite(LED, !digitalRead(LED)); //flip led, 105 ticks
//measure timing
tmp0=ticks();
//put some tasks here
tmp0=ticks() - tmp0;
//display something
u2Print("F_CPU= ", F_CPU);
u2Print("ticks= ", ticks());
u2Print("tmp0 = ", tmp0);
u2Println();
}
}
Re: timer source from other timer ?
for very long intervals/periods, say seconds to even hours, actually there is another way.
for that you can use the standard hardware timer
https://github.com/stm32duino/wiki/wiki ... er-library
the idea is this. you can attachInterrupt() and put your own function there.
so that if the period is longer than some intervals say 1 seconds (actually, i think hardware timer can easily address this).
Then you set the hardware timer period to fire/trigger at 1 seconds interval.
Next for instance, you can count this 1 seconds interval in your code, you can use an uint32_t that should give you some 4 billion seconds extended periods if you need to count that. This would easily solve the long periods problem.
for that you can use the standard hardware timer
https://github.com/stm32duino/wiki/wiki ... er-library
the idea is this. you can attachInterrupt() and put your own function there.
so that if the period is longer than some intervals say 1 seconds (actually, i think hardware timer can easily address this).
Then you set the hardware timer period to fire/trigger at 1 seconds interval.
Next for instance, you can count this 1 seconds interval in your code, you can use an uint32_t that should give you some 4 billion seconds extended periods if you need to count that. This would easily solve the long periods problem.
Re: timer source from other timer ?
here is a simplified version, similar to what ag123 proposed above.
isDLY32() detects if enough time has passed. Time is measured in DLY_TB (delay time base, in this case, 0.1 second), and DLY_DUR (delay duration) - in this example, we are looking for a duration of 1 second (10 * 0.1 sec).
The implementation is non-blocking. It will have small jitter - depending on how fast the main loop is executed. however, it is long-term accurate: a + jitter in one cycle will cause a - jitter the next cycle.
Code: Select all
//PIC32duino code
// - using PIC32MX1xx/2xx
// - free running core timer for ticks() (or tmr2, selected by the user)
// - free running tmr2 for pwm / output compare / input capture
// - details available here: https://github.com/dannyf00/Minimalist-32-bit-Arduino-Clone
//
// - version history
// - v2.0, 5/13/2021: original port
// - v2.1, 5/26/2021: implemented SystemCoreClockUpdate() that updates SystemCoreClock during initialization
// implemented systicks() from TMR2 ISR - optional (in addition to core timer)
// - v2.2, 5/27/2021: implemented i2c/rtcc
// - v2.3, 6/19/2021: added support for output compare (dah!)
// - v2.4, 5/29/2021: output compare routines no longer advance compare registers
// - v2.5, 5/24/2022: added support for C32 compiler
//
//
//hardware configuration
//
// PIC32MX1/2xx
// |=====================|
// Vcc | |
// | | AVdd |>---+--[1k]-->Vcc
// | | | [.1u]
// +-[10K]-<| MCLR AVss |>---+-------->GND
// | |
// | |
// +------->| OSCI Vcap |>--[.1u]-+->GND
// [Xtal] | | |
// +-------<| OSCO Vss |>--------+
// | |
// +------->| SOSCI RPB0 |>---------->Uart2TX
// [32,768Hz] | |
// +-------<| SOSCO RB7 |>---------->LED
// | |
// | |
// | |
// | |
// | |
// | |
// | |
// | |
// | |
// |=====================|
//
//
//
#include "pic32duino.h" //we use pic32duino
//hardware configuration
#define LED PB7 //led pin
#define DLY_DUR 10 //desired duration, in DLY_TB
//end hardware configuration
//global defines
#define DLY_TB (F_CPU/10) //0.1 second in timebase
//global variables
//user defined set up code
void setup(void) {
pinMode(LED, OUTPUT); //led as output pin
//initialize the uart
//uart1Init(UART_BR9600); //initialize uart1
uart2Init(UART_BR9600); //initialize uart2
//enable interrupts
ei();
}
//count the desired time base
//1-> time is up, 0->otherwise
int isDLY32(void) {
static uint32_t tick0=0; //tick counter
static uint32_t dly_cnt=DLY_DUR; //cycle counter
int flg = 0; //flag: 1-> time is up; 0-> otherwise
if (ticks() - tick0 > DLY_TB) { //if 1 second has passed
tick0 += DLY_TB; //advance to the next match point
if (--dly_cnt==0) { //enough seconds have passed
dly_cnt=DLY_DUR; //reset the counter
flg=1; //1->time is up; 0-> otherwise
}
}
return flg; //return 1 if enough cycle has passed; 0 otherwise
}
//user defined main loop
void loop(void) {
if (isDLY32()) { //if enough time has elapsed
//user tasks started
pinFlip(LED); //digitalWrite(LED, !digitalRead(LED)); //flip led, 105 ticks
//display something
u2Print("F_CPU= ", F_CPU);
u2Print("ticks= ", ticks());
//u2Print("tmp0 = ", tmp0);
u2Println();
//user tasks finished
}
}
The implementation is non-blocking. It will have small jitter - depending on how fast the main loop is executed. however, it is long-term accurate: a + jitter in one cycle will cause a - jitter the next cycle.
Re: timer source from other timer ?
ticks() for this particular implementation is the core timer on a MIPS chip (PIC32). For a STM32F1, you can use the DWT, or SysTick - be careful with the math if you use SysTick as it is 24-bit only.
Re: timer source from other timer ?
Many Thanks for all the replies and ingenious suggestions.
I had tried changing the prescaler as suggested, but I must have mucked it up.
With The prescaler set to 0xffff, the maximum delay for a 32bit is 1864 hours
. In my tests for seconds the setOverflow count must be set to n*640. So even with this prescaler setting granularity is still <2mS. For 16 bit timers this is ~102 secs max time period.
Best Regards
picclock
I had tried changing the prescaler as suggested, but I must have mucked it up.
With The prescaler set to 0xffff, the maximum delay for a 32bit is 1864 hours

Best Regards
picclock
Code: Select all
//STM32F401
HardwareTimer *MinTim = new HardwareTimer(TIM2);//32 bit timer
uint16_t mc=8;//time period in minutes
uint32_t mil;
void MinIsr(){ // Called by the MinTim Timer Interrupt
// called function here
Serial.println("mc function called"); // function to run after timeout
//MinTim ->pause(); //for once only .. .
}
void setup() {
Serial.setRx(PA10); // using pin name
Serial.setTx(PA9);
Serial.begin(9600);
delay(500);
Serial.println("n sec - Min ISR Test");
MinTim->pause(); // UV purge timer
MinTim->setPrescaleFactor(0xffff); // approx 640 counts per second
uint16_t n=60;// time in seconds
MinTim->setOverflow(n*640); // Period value in n seconds
MinTim->refresh();
MinTim->attachInterrupt(MinIsr);
MinTim->resume();
mil=millis();
}
void loop() {
// put your main code here, to run repeatedly:
}
Last edited by picclock on Tue Jun 14, 2022 11:40 am, edited 2 times in total.
Re: timer source from other timer ?
for very long time intervals, the usual trick is millis()
systick is always running which gives millis(). it is a 'classic' way to 'multi task' in loop().
in this way you can track n number of leds for instance and blink them at all different intervals.
the risk is duration can wrap around
i think firmwares like marlin (for 3d printing) still use a hardware timer instead to run the steppers as the demand for tight high frequency timing loops is still too tight.
Code: Select all
uint32_t begin = 0;
void setup() {
begin = millis();
}
void loop() {
uint32_t duration = millis() - begin;
}
in this way you can track n number of leds for instance and blink them at all different intervals.
the risk is duration can wrap around
i think firmwares like marlin (for 3d printing) still use a hardware timer instead to run the steppers as the demand for tight high frequency timing loops is still too tight.