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.
Related
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.
As a C beginner, I struggle alot to solve this problem of mine :
I am working on a project where I basicly have to program a PIC (microchip) in C using the mikroC platform.
What I'm trying to achieve is with only one switch/button, I'll have to switch from three different "modules" (as I call them "modules", they correspond to different lighting effects created by LEDs) depending on the time spent pressing the switch/button. In my case, after 500 ms module 1 is up, after 1500 ms module 2 goes up and after 3500 ms module 3 is up (and the whole thing has to be in an infinitely repeating loop since I have to be able to change a module at ANYTIME during the operation).
My only problem is getting the timer/clock to start runnning at the beginning of the program and keep counting time until it reaches a stop signal (like the end of a loop or something).
This may not be appropriate to ask to this community but here I am nonetheless.
I am concious that this is more of a 'algorithmics/logic' problem than anything but I have been trying for the last week without any clue on how to get past this problem...
No results since the code isn't ready at all.
There is a mikroC library for handling button presses. The following example (from the link.) provides a skeletal example of detecting a button push...
bit oldstate; // Old state flag
void main() {
ANSEL = 0; // Configure AN pins as digital I/O
ANSELH = 0;
C1ON_bit = 0; // Disable comparators
C2ON_bit = 0;
TRISB0_bit = 1; // set RB0 pin as input
TRISC = 0x00; // Configure PORTC as output
PORTC = 0xAA; // Initial PORTC value
oldstate = 0;
do {
if (Button(&PORTB, 0, 1, 1)) { // Detect logical one
oldstate = 1; // Update flag
}
if (oldstate && Button(&PORTB, 0, 1, 0)) { // Detect one-to-zero transition
PORTC = ~PORTC; // Invert PORTC
oldstate = 0; // Update flag
}
} while(1); // Endless loop
}
There is also a collection of MicroE Examples which include timer examples such as these, and this one. Each of these provide code base examples that might be adapted to create a function that can be wrapped around sections in the button press code to obtain time durations.
I hope this helps.
When the button gets pressed, you need to initialize timer module of respective microcontroller check the interrupt flag, and put it in a loop. increment the value until the button is released, and after that compare the resultant value with the specified limits, this was you can decide for how long a button is pressed, and consecutively you will be able to switch your output. This is the only way if you want to precisely monitor the delay without interrupting the current execution of the program.
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.
Arduino drone project, whenever output is "loaded" ("open-circuit" signal-in pin to ESC or even a cap to ground), the input "steering" command starts to glitch (go to really low values << 1000).
The motor speed is a function of both the steering command, as well as the throttle. (In this test-case with just one motor as seen in the code below, unMotorSpeed = unThrottleIn +/- unSteeringIn)
When hooked up to a scope, the physical input signals (steering and throttle coming from the receiver) are great, and I even swapped the input pins to make sure there wasn't a problem between the receiver and the arduino. The problem seems to be coming from the software, but it just doesn't make sense, since when there isn't a "load" attached, the input and output values are all fine and clean. (I put "load" in quotes because sometimes it's essentially an open circuit --> super high impedance input signal to the electronic speed controller (ESC), which I don't even ground to complete a circuit).
Would anyone be able to check out the code and see if I'm missing something?
At this point, a somewhat quick workaround would be to simply not write those new glitchy values to the motor and keep the old speed values for whenever the new speed is significantly lower than the old speed (and these are at over 50khz, so clearly a huge jump in just one step is a bit crazy).
Note: In the code, the total output of unMotorSpeed leaves from the servoThrottle pin. Just the original naming that I didn't end up changing... it's clear if you read the code and see all the variables.
UPDATE: Weird thing is, I just ran my setup without any changes and EVERYTHING WORKED...I reflashed the arduino multiple times to make sure it wasn't some lucky glitch, and it kept on working, with everything set up. Then I dropped my remote on the ground and moved a few of the wires on the breadboard around, and things went back to their wonky ways after putting the setup back together. Idk what to do!
// --> starting code found at: rcarduino.blogspot.com
// See related posts -
// http://rcarduino.blogspot.co.uk/2012/01/how-to-read-rc-receiver-with.html
with.html
#include <Servo.h>
// Assign your channel in pins
#define THROTTLE_IN_PIN 3
#define STEERING_IN_PIN 2
// Assign your channel out pins
#define THROTTLE_OUT_PIN 9
//#define STEERING_OUT_PIN 9
// Servo objects generate the signals expected by Electronic Speed Controllers and Servos
// We will use the objects to output the signals we read in
// this example code provides a straight pass through of the signal with no custom processing
Servo servoThrottle;
//Servo servoSteering;
// These bit flags are set in bUpdateFlagsShared to indicate which
// channels have new signals
#define THROTTLE_FLAG 1
#define STEERING_FLAG 2
// holds the update flags defined above
volatile uint8_t bUpdateFlagsShared;
// shared variables are updated by the ISR and read by loop.
// In loop we immediatley take local copies so that the ISR can keep ownership of the
// shared ones. To access these in loop
// we first turn interrupts off with noInterrupts
// we take a copy to use in loop and the turn interrupts back on
// as quickly as possible, this ensures that we are always able to receive new signals
volatile uint16_t unThrottleInShared;
volatile uint16_t unSteeringInShared;
// These are used to record the rising edge of a pulse in the calcInput functions
// They do not need to be volatile as they are only used in the ISR. If we wanted
// to refer to these in loop and the ISR then they would need to be declared volatile
uint32_t ulThrottleStart;
uint32_t ulSteeringStart;
//uint32_t ulAuxStart;
void setup()
{
Serial.begin(9600);
// attach servo objects, these will generate the correct
// pulses for driving Electronic speed controllers, servos or other devices
// designed to interface directly with RC Receivers
servoThrottle.attach(THROTTLE_OUT_PIN);
// using the PinChangeInt library, attach the interrupts
// used to read the channels
attachInterrupt(digitalPinToInterrupt(THROTTLE_IN_PIN), calcThrottle,CHANGE);
attachInterrupt(digitalPinToInterrupt(STEERING_IN_PIN), calcSteering,CHANGE);
}
void loop()
{
// create local variables to hold a local copies of the channel inputs
// these are declared static so that thier values will be retained
// between calls to loop.
static uint16_t unThrottleIn;
static uint16_t unSteeringIn;
static uint16_t difference;
static uint16_t unMotorSpeed; // variable that stores overall motor speed
static uint8_t bUpdateFlags; // local copy of update flags
// check shared update flags to see if any channels have a new signal
if(bUpdateFlagsShared)
{
noInterrupts(); // turn interrupts off quickly while we take local copies of the shared variables
// take a local copy of which channels were updated in case we need to use this in the rest of loop
bUpdateFlags = bUpdateFlagsShared;
// in the current code, the shared values are always populated
// so we could copy them without testing the flags
// however in the future this could change, so lets
// only copy when the flags tell us we can.
if(bUpdateFlags & THROTTLE_FLAG)
{
unThrottleIn = unThrottleInShared;
}
if(bUpdateFlags & STEERING_FLAG)
{
unSteeringIn = unSteeringInShared;
}
// clear shared copy of updated flags as we have already taken the updates
// we still have a local copy if we need to use it in bUpdateFlags
bUpdateFlagsShared = 0;
interrupts(); // we have local copies of the inputs, so now we can turn interrupts back on
// as soon as interrupts are back on, we can no longer use the shared copies, the interrupt
// service routines own these and could update them at any time. During the update, the
// shared copies may contain junk. Luckily we have our local copies to work with :-)
}
//Serial.println(unSteeringIn);
// do any processing from here onwards
// only use the local values unAuxIn, unThrottleIn and unSteeringIn, the shared
// variables unAuxInShared, unThrottleInShared, unSteeringInShared are always owned by
// the interrupt routines and should not be used in loop
// the following code provides simple pass through
// this is a good initial test, the Arduino will pass through
// receiver input as if the Arduino is not there.
// This should be used to confirm the circuit and power
// before attempting any custom processing in a project.
// we are checking to see if the channel value has changed, this is indicated
// by the flags. For the simple pass through we don't really need this check,
// but for a more complex project where a new signal requires significant processing
// this allows us to only calculate new values when we have new inputs, rather than
// on every cycle.
///// if-else chain commented out to determine/prove problem with steering signal --> buggy!
if(unSteeringIn < 1400) // if steering joystick moved left
{
difference = 1400 - unSteeringIn;
if(unThrottleIn - difference >= 0)
unMotorSpeed = unThrottleIn - difference;
}
else if(unSteeringIn > 1550) //if steering joystick moved right (needs to be tweaked, but works for now)
{
difference = unSteeringIn - 1600;
if(unThrottleIn + difference < 2000)
unMotorSpeed = unThrottleIn + difference;
}
else
{
unMotorSpeed = unThrottleIn;
}
//Serial.println(unMotorSpeed);
//Serial.println(unSteeringIn);
//Serial.println(unThrottleIn);
if(bUpdateFlags)
{
//Serial.println(servoThrottle.readMicroseconds());
if(servoThrottle.readMicroseconds() != unMotorSpeed)
{
servoThrottle.writeMicroseconds(unMotorSpeed);
Serial.println(unMotorSpeed);
}
}
bUpdateFlags = 0;
}
// simple interrupt service routine
void calcThrottle()
{
// if the pin is high, its a rising edge of the signal pulse, so lets record its value
if(digitalRead(THROTTLE_IN_PIN) == HIGH)
{
ulThrottleStart = micros();
}
else
{
// else it must be a falling edge, so lets get the time and subtract the time of the rising edge
// this gives use the time between the rising and falling edges i.e. the pulse duration.
unThrottleInShared = (uint16_t)(micros() - ulThrottleStart); // pulse duration
// use set the throttle flag to indicate that a new throttle signal has been received
bUpdateFlagsShared |= THROTTLE_FLAG;
}
}
void calcSteering()
{
if(digitalRead(STEERING_IN_PIN) == HIGH)
{
ulSteeringStart = micros();
}
else
{
unSteeringInShared = (uint16_t)(micros() - ulSteeringStart); // pulse duration
bUpdateFlagsShared |= STEERING_FLAG;
}
}
You should read the documentation of AttachInterrupt() - in the section "About Interrupt Service Routines" it gives information on how certain functions behave when called from an interrupt. For micros() it states:
micros() works initially, but will start behaving erratically after 1-2 ms.
I believe that means after the ISR has been running for more than 1ms, rather than just 1 ms in general, so may not apply in this case, but you might need to consider how you are doing the timing in the ISR. That's a problem with Arduino - terrible documentation!
One definite problem which may be a cause is the fact that unSteeringInShared is non-atomic. It is a 16 bit value on 8 bit hardware so requires multiple instructions to read and write and the process can be interrupted. It is therefore possible to read one byte of the value in the loop() context and then have both bytes changed by the interrupt context before you read the second byte, so you then up with two halves of two different values.
To resolve this problem you could either disable interrupts while reading:
noInterrupts() ;
unSteeringIn = unSteeringInShared ;
interrupts() ;
Or you can spin-lock the read:
do
{
unSteeringIn = unSteeringInShared ;
} while( unSteeringIn != unSteeringInShared ) ;
You should do the same for unThrottleInShared too, although why you do not see any problem with that is unclear - this is perhaps not the problem you are currently observing, but is definitely a problem in any case.
Alternatively if 8 bit resolution is sufficient you could encode the input as an atomic 8 bit value thus:
uint8_t unSteeringInShared ;
...
int32_t timeus = micros() - ulSteeringStart - 1000 ;
if( timeus < 0 )
{
unSteeringInShared = 0 ;
}
else if( timeus > 1000 )
{
unSteeringInShared = 255;
}
else
{
unSteeringInShared = (uint8_t)(time * 255 / 1000) ;
}
Of course changing your scale from 1000 to 2000 to 0 to 255 will need changes to the rest of the code. For example to convert a value x in the range 0 to 255 to a a servo pulse width:
pulsew = (x * 1000 / 256) + 1000 ;
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.