Change timer period without re initialize it STM32 - timer

I'm currently using a timer on my STM32F091VB as below
void MX_TIM3_Init(void)
{
htim3.Instance = TIM3;
htim3.Init.Prescaler = 400;
htim3.Init.Period = 1000;
HAL_TIM_PWM_Start(&htim3, TIM_CHANNEL_1);
}
...
__HAL_TIM_SET_COMPARE(&htim3, TIM_CHANNEL_1, 1000);
Is there a way to change the htim3.Init.Period on runtime?
I'm using IAR 9.20 as IDE for instance

You can change reload value while the timer is running. You can set it manually via TIM3->ARR = new_value;, where ARR stands for "auto reload register".
You should check reference manual of your MCU - Timer section - to see how many bits wide ARR is, it can be different for different timers. Also, in control register 1 (CR1) there is ARR preload buffer function. If it is enabled, it basically means "don't update ARR with newly written value until the next counter reload". So it will finish the current cycle and only after that change the ARR. If it's disabled, it can change ARR immediately whatever current value of the counter is.
Remember, that timer clock source is not the system clock, typically APBx, possibly with multiplier (2xAPBx is common in most APB clock configurations, it's messy). That is covered in RCC section of the reference manual.

Related

STM32F0 - Multiple interrupts from one Timer?

I'm trying to figure out if there is a way of using any of the timers to generate interrupts at more than one delay time.
E.G. I want to be able to start a timer, then have it cause interrupts at say 20us, 50us, 100us, 300us.
I can see that its perfectly possibly (and easy) to have timers cause an interrupt when the end of the count has elapsed (using HAL) but having trouble working out if I can do what I want using only one timer.
I notice some timers are 4 channel but not sure if they can be set as required.
I guess my fall back is to use one timer for each (but ideally I would like to keep other timers in case they are needed for other tasks).
I've read the docs but having trouble understanding if the device can be configured as I want it to.
The easiest way - is to change the timer period (write a different value to the ARR register, use __HAL_TIM_SET_AUTORELOAD macro if you prefer HAL) in each interrupt. This way every other period could be different.
Just keep in mind ARR buffering, or Auto-reload preload, see ARPE bit decription in TIMx_CR1. if it is enabled - the new value of ARR will be taken in account only after the next update event.
You can have more than one interrupt generated during one cycle. You need to use output compare registers and enable their interrupts.
In the timer interrupt handler routine you will need to check what has triggered the interrupt.
There are multiple ways you can achieve it. One a little blunt and simple, another one slightly more complicated, but once set up, very effective and quick to process.
Approach number 1, a little limited, but also a little simpler: use capture/compare (in this case, compare specifically) to trigger an interrupt. Since timers have up to 4 capture/compare values, you can have up to 4 interrupts. You will have to check which exactly interrupt fired tho in the handler. Which specifically compare value, I mean.
Approach number 2, more flexible, more precise, but a bit more hassle to set up: master-slave timers. You can have one timer (including basic) be the clock source for the other timers ("tick" on the rising edge of master slave setting). For example, master timer ticks at 10kHz, and its slave timer uses its output as a clock source and ticks to, say, 50. Or 100. You can have multiple slaves. Depending on what timer's interrupt fires, you know right away how much time exactly passed, no need to check values in compare register or anything.
Mixed method: slave timers can have capture/compare too by the way, if you're into that. You can create huge timer chains if you want to.
You have a dozen timers in your MCU, you can probably spare 1 or 2 more for this purpose. Once you get it working, should make your life very easy.
This is how I set up a timer that is a slave, but also a master to another timer. TMR is TIM3 with TIM1 as a master:
/*
* CR1 reset value = 0x00000000
* ARR is not buffered
* upcounter (default)
* update request source: only overflow/underflow
*
* */
TMR->CR1 = 0x00; //reset
TMR->CR1 |= TIM_CR1_URS;
/*
* 108MHz Clock, 64000 prescaler -> 2kHz Timer Clock
* Reload value 6, Period = 6/2000s = 3ms
* */
TMR->PSC = (108000U / 2U) - 1U; //APB1 is 54MHz, timer is 2x APB1 Freq, so now the timer is at 2kHz; 16-bit value!!! 65535 max!
TMR->ARR = 6U - 1U; //6 ticks at 2kHz is 3ms
TMR->CNT = 0x00; //start value
TMR->EGR |= TIM_EGR_UG; //force update event and load all values into registers
TMR->SR &= ~TIM_SR_UIF; //force clear update event
/*
* SMCR Slave Mode Control Register reset value = 0x00000000
* Trigger Selection - ITR0 (TIM1 as Master for TIM3)
* Slave Mode Selection - Trigger Mode - The counter starts at a rising edge of the trigger TRGI (but it is not
* reset). Only the start of the counter is controlled (0b0110)
*/
TMR->SMCR = 0x00; //reset
TMR->SMCR |= (0x00 << TIM_SMCR_TS_Pos) | (0x00 << 16U) | (0x06 << TIM_SMCR_SMS_Pos);
/*
* CR2 reset value = 0x00000000
* Master Mode Selection 1: OC1REF Triggers TRGO to start another timer
* Master Mode Selection 2: reset
* Compare value: 4 (output LOW: CNT = 0, 1, 2, 3; output HIGH CNT = 4, 5)
* Duty cycle: 33.33%
* Output compare 1 mode - PWM mode 2 (0b0111)
*
* */
TMR->CR2 = 0x00; //reset
TMR->CR2 |= (0x04 << TIM_CR2_MMS_Pos); //OC1REF as TRGO
TMR->CCR1 = 4U;
TMR->CCMR1 = 0x00;
TMR->CCMR1 |= (0x07 << TIM_CCMR1_OC1M_Pos);
/*
* Capture Compare Enable Register
* Polarity: default - active high
* Capture Compare Output Enable
* */
TMR->CCER = 0x00; //reset
TMR->CCER |= TIM_CCER_CC1E;
The rest of my example/training project, including screenshots of waveform, are here: Github Chained Timers Demo. There you will find TIM1 to be the master, TIM3 its slave and the master of TIM5, and so on. Timers are connected in different master-slave modes.

STM32 Timer auto-reload preload

The conditions to reproduce:
Here is my real life example that I would like to solve:I am developing an application on an stm32f411RET which needs to dynamically change the period of two PWM's.The two PWM's need to be synced and have exactly the same frequency but because of some pin restrictions I am using two different timers.In my main loop I calculate the period I want and I call:
TIM3->ARR = (uint16_t)period;
LL_TIM_OC_SetCompareCH4(TIM3, period/2);
TIM2->ARR=(uint16_t)period;
LL_TIM_OC_SetCompareCH3(TIM2, period/2);
Everything works great but what is obscure to me is the combination of initialization settings of the two timers :
LL_TIM_InitTypeDef TIM_InitStruct = {0};
LL_TIM_OC_InitTypeDef TIM_OC_InitStruct = {0};
LL_GPIO_InitTypeDef GPIO_InitStruct = {0};
LL_APB1_GRP1_EnableClock(LL_APB1_GRP1_PERIPH_TIM2);
NVIC_SetPriority(TIM2_IRQn, NVIC_EncodePriority(NVIC_GetPriorityGrouping(),0, 0));
NVIC_EnableIRQ(TIM2_IRQn);
TIM_InitStruct.Prescaler = 0;
TIM_InitStruct.CounterMode = LL_TIM_COUNTERMODE_UP;
TIM_InitStruct.Autoreload = 0;
TIM_InitStruct.ClockDivision = LL_TIM_CLOCKDIVISION_DIV1;
LL_TIM_EnableARRPreload(TIM2); //Important Line!!
LL_TIM_Init(TIM2, &TIM_InitStruct);
LL_TIM_OC_EnablePreload(TIM2, LL_TIM_CHANNEL_CH3);
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 = 0;
TIM_OC_InitStruct.OCPolarity = LL_TIM_OCPOLARITY_HIGH;
LL_TIM_OC_Init(TIM2, LL_TIM_CHANNEL_CH3, &TIM_OC_InitStruct);
LL_TIM_OC_DisableFast(TIM2, LL_TIM_CHANNEL_CH3);
LL_TIM_SetTriggerOutput(TIM2, LL_TIM_TRGO_RESET);
LL_TIM_DisableMasterSlaveMode(TIM2);
LL_AHB1_GRP1_EnableClock(LL_AHB1_GRP1_PERIPH_GPIOB);
GPIO_InitStruct.Pin = BBD_R_Pin;
GPIO_InitStruct.Mode = LL_GPIO_MODE_ALTERNATE;
GPIO_InitStruct.Speed = LL_GPIO_SPEED_FREQ_LOW;
GPIO_InitStruct.OutputType = LL_GPIO_OUTPUT_PUSHPULL;
GPIO_InitStruct.Pull = LL_GPIO_PULL_NO;
GPIO_InitStruct.Alternate = LL_GPIO_AF_1;
LL_GPIO_Init(BBD_R_GPIO_Port, &GPIO_InitStruct);
This is quite standard for the timer 2 and nearly the same code works for timer 3 with the only exception that LL_TIM_EnableARRPreload(TIM2); changes to LL_TIM_DisableARRPreload(TIM3);.
TLDR The actual question
When I change any of those two initialization functions the timer starts working but changing the frequency make the timer completely die.I have a grasp about what this function does from page 316 of the reference manual and also pages 320 and 321 that contain schematics but still I can't comprehend why this setting can cause the timers to freeze.
P.S.
It might be useful or it might not so I'll leave it here the ARR register of timer 2 is 32 bit long and the ARR of timer 3 is 16 ,that is not obvious from the configurations I posted but I doubt this affects the outcome.
For a start the same initialisation routine should work for both the timers used to generate the PWM signals you want, unless you using one timer in a different configuration to the other.
On thing that stands out is that the TIM_InitStruct.Autoreload is set to 0 during initialisation, the behaviour of the timer in counter mode/pwm mode with ARR set to 0 is undocumented in the reference manual. It would be wise to set the TIM_InitStruct.Autoreload to the UINT32_MAX or UINT16_MAX depending on the timer.
Further, looking at the initialisation routine shown in your question (For timer 2 channel 3), the call LL_TIM_EnableARRPreload enables a change to the ARR value to be buffered. When the ARR changes are buffered the ARR value is only updated on an update event (UEV). When buffered updates are disabled, LL_TIM_DisableARRPreload, the ARR value is updated with a new value immediately. The behaviour with and without buffering are shown by the following figures in the reference manual.
ARR buffered (LL_TIM_EnableARRPreload):
ARR un-buffered (LL_TIM_DisableARRPreload):
Where you are dynamically updating the ARR value (PWM period) and the compare counter value (PWM duty-cycle, CCRn) in a loop, it is generally a good idea to have both updates buffered/preloaded. The CCRn buffering is enabled with LL_TIM_OC_EnablePreload, as shown in your initialisation routine. Buffering the ARR changes, will maintain the integrity of PWM period between ARR updates avoiding any inadvertently long pulses; particularly, should the system find itself in the condition where ARR new < TIMx CNT < ARR old. Note, if you wish to keep the PWM signals in sync, it is important the same ARR preloading configuration is used for both timers.
Note, the following calls are superfluous if the timer hadn't been previously initialised for a different purpose.
LL_TIM_OC_DisableFast(TIM2, LL_TIM_CHANNEL_CH3);
LL_TIM_DisableMasterSlaveMode(TIM2);
LL_TIM_SetTriggerOutput(TIM2, LL_TIM_TRGO_RESET);
Beyond your question and more to the use-case; depending on how closely you want the PWM signals to be synchronised, you may want to consider the basic configuration of one timer operating as the master (TIMxCR2.MMS=001)and the other as the slave (TIMxSMCR.SMS=100) where the slave timer is enabled when the master is enabled.

How to take a snapshot of a changing variable (such as a timer) in C?

I am currently programming a Teensy micro-controller and want to setup a pause function for a game. I have been able to create a timer from ISR counter overflows, however I haven't been able to work out how to pause this counter. I have tried:
ISR(TIMER1_OVF_vect) {
if (paused == 0){
overflow_counter ++;
}
}
This seems to make no difference to the counter, the counter just keeps going no matter what instructions I put inside the function. I have tried a variety of if statements and they are ignored - the function just increases the counter for some reason (even if I put overflow_counter --)!
So I have tried to setup another variable that takes a snapshot of the time when the pause button is pressed, then when the game is un-paused it would take another snapshot and calculate the difference. This would then be taken away from the overall time.
double snapshot_time = 0;
double get_current_time(void){
double time = ( overflow_counter * 65536.0 + TCNT1 ) * PRESCALE / FREQ;
if (paused == 0 && switch_state[5] == 1){
snapshot_time = time;
}
return time;
}
I have tried setting snapshot_time as a global variable and making it equal to time thinking this may dynamically capture a static variable but unfortunately it doesn't. Can anyone offer a way to do this?
There are many aspects hidden in your question.
1. First of all, the counter variable should be marked volatile. The compiler is applying different optimizations to variables, so it may, say, load a variable into a register and continue to work with register, assuming it is only the place, where content of the variable is stored. If a variable is declared with the keyword volatile, then the compiler knows that it could be changed occasionally in any time, therefore the compiler will reload and/or rewrite the variable each time it is accessed. So, it may be declared like this
volatile uint16_t overflow_counter;
The same goes for the paused variable.
2. You should remember that, if interrupts are not disabled, then timer interrupt can occur between any two processor's instructions. Since processor is 8bit, it access the memory using 8-bit wide bus. That means, to read 16-bit data, it requires 2 instructions. Let's say we copy the counter value into a local variable:
uint16_t counter_snapshot = overflow_counter;
The local variable will allocate two registers and two memory read operations will be performed. Let's imagine the interrupt is happened after first of them, but before second. So, on output you'll have half of the number copied from it's previous value, while the second half from it's new. I.e. the value will be damaged. It will not be happened, if variable is 8-bit and copied by one instruction. But if it is wider, or if it is read-modified-written, then precautions should be taken:
uint8_t old_sreg = SREG; // SREG i/o register contains processor state flags, including "interrupt flag", which allows interrupt
cli(); // clear the "interrupt flag", preventing interrupts from happening
uint16_t counter_snapshot = overflow_counter; // safely taking the snapshot
SREG = old_sreg; // restoring the "interrupt flag" to it's previous state. Other flags also restored but we do not care about them.
3. As said above, interrupt can happen in any time. That means if you try to read overflow_counter and TCNT1 both, the interrupt can be happened in between, so, result will be not as expected. Especially if reading of those two values is separated by such a long operation as floating-point multiplication. So, workaround may be as follows:
uint8_t old_sreg = SREG; // saving state
cli(); // locking interrupts
// latching values we are interested in
uint16_t latch_overflow_counter = overflow_counter;
uint16_t latch_tcnt1 = TCNT1;
uint8_t latch_tifr1 = TIFR1;
SREG = old_sreg; // restoring interrupts
/* We are disabling interrupts, but it do not stop the timer from counting,
therefore TCNT1 value continue changing, and timer could overflow in any time
within that block above. But which moment exactly? Before we read TCNT1 or just after?
Let's assume if TCNT1 have high values then we got it's value just before the timer overflow;
otherwise, overflow happened before that */
if ((latch_tifr1 & (1 << TOV1)) && // we got the overflow flag set
(latch_tcnt < 32768) { // we are within the low values
latch_overflow_counter++; // increasing the latched value
}
double time = ( latch_overflow_counter * 65536.0 + latch_tcnt1 ) * PRESCALE / FREQ; // now using latched values to calculate...
By the way, throughput can be much improved, if avoid using floating point where it is not necessary.

PIC programing Interrupt malfunction.

I'm new to PIC programming and I'm using MPLAb. I have a question regarding interrupt..
so What I want to do, when I push a button then I want to turn on LED 0, and if I release the button then turn on LED 1. I thought the code I wrote making sense but it didn't work.
Here is what happens. Let say the initial state of interrupt pin is low (0), when a button is pushed. Then the LED 0 is on, and when I release the button then LED 1 is on. When I push the button again, I expect LED 0 is on, but LED 1 stays on, and never change the state.
I added last line to see the state of interrupt pin, and once the interrupt is high, it never change it to low.. Can you please advise me what is my misunderstanding?
Thanks in advance!
Here is my code:
void interrupt ISR(void)
{
if(INTCONbits.INTF)
{
nextLED = 1;
LATC = Output_Code_Buffer[nextLED];
__delay_ms(250);
}
else
{
nextLED = 0;
LATC = Output_Code_Buffer[nextLED];
__delay_ms(250);
}
nextLED = INTCONbits.INTF + 2;
LATC = Output_Code_Buffer[nextLED];
__delay_ms(250);
}
// Interrupt Enable settings
INTCONbits.INTE = 1;
INTCONbits.TMR0IE = 1; // Enable TMR0 interrupts
INTCONbits.TMR0IF = 0; // Clear TMR0 interrupt flag
INTCONbits.GIE = 1; // Enable global interrupts
You need to reset the interrupt flag in the ISR function or it will just keep triggering. Please read the datasheet, it should mention if this is necessary. So just add INTCONbits.INTF = 0; to the ISR and it should work as expected.
When setting up any peripheral or function of the mcu, you should go through the datasheet and use the description of the registers and what to set them. You'll also need to be careful with analogue ports, which often default to analogue instead of digital, causing interrupt not to fire as expected or causing unexpected interrupts. It's best to first setup the MCU config bits, set the TRIS and analogue selection registers (ANSELx or ANSELAx etc), then the registers for any peripheral you want to use. Then setup the interrupts, always reset all the interrupt flags you're going to use to start with a known state.
You also set TMR0IE = 1, which will do the same thing, trigger an interrupt. If you don't reset the TMR0 flag it will keep triggering, locking up your mcu or slow it down.

Accuracy of Timer1 as real time clock with PIC Interrupts on 16F*

I'm using C with the BoostC compiler. I'm worried how accurate my code is. The configuration below ticks at more or less 1Hz, (tested with an LED to the naked eye). (It uses an external watch crystal 32kHz for Timer1 on the 16f74).
I'm hoping someone can tell me...
Does my code below need any adjustments? What would be the easiest way of measuring the accuracy to the nearest CPU clock period? Will I need to get dirty with assembly to reliably ensure accuracy of the 1Hz signal?
I'm hoping the time taken to execute the timer handler (and others) doesn't even come into the picture, since the timer is always counting. So long as the handlers never take longer to execute than 1/32kHz seconds, will the 1Hz signal have essentially the accuracy of the 32kHz Crystal?
Thanks
#define T1H_DEFAULT 0x80
#define T1L_DEFAULT 0
volatile char T1H = T1H_DEFAULT;
volatile char T1L = T1L_DEFAULT;
void main(void){
// setup
tmr1h = T1H;
tmr1l = T1L;
t1con = 0b00001111; // — — T1CKPS1 T1CKPS0 T1OSCEN NOT_T1SYNC TMR1CS TMR1ON
// ...
// do nothing repeatedly while no interrupt
while(1){}
}
interrupt(void) {
// Handle Timer1
if (test_bit(pir1, TMR1IF) & test_bit(pie1, TMR1IE)){
// reset timer's 2x8 bit value
tmr1h = T1H;
tmr1l = T1L;
// do things triggered by this time tick
//reset T1 interrupt flag
clear_bit(pir1, TMR1IF);
} else
... handle other interrupts
}
I can see some improvements...
Your timer initiation inside interrupt isn't accurate.
When you set the timer counter in interrupt...
tmr1h = T1H;
tmr1l = T1L;
... then you override the current value what isn't good for accuracy.
... just use:
tmr1h = T1H; //tmr1h must be still 0!
Or even better, just set the 7th bit of tmr1h register.
The compiler must compile this order to single asm instruction like...
bsf tmr1h, 7
...to avoid losing data in tmr1 register. Because if this is made with more than one instructions the hardware can increment the counter value between execution of: read-modify-write.

Resources