STM32F0 - Multiple interrupts from one Timer? - 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.

Related

Erroneous Pulsetrain Timing

I am having some trouble with a timer on my Arduino atmega328p-pu with 16MHz clock.
I have a really simply program with only one timer, two ISRs, and one pin.
The program does the following:
Iterates through the bits of 'sequence' and sets pin4 high or low respectively. However it doesnt set the bit high for the entire period, only 1/12 of it. What you see below is a single timer that counts up from 0 to 340. There is ISRB at 28, and then ISRA happens at 340, then it loops (that is what CTC mode does, loops after ISRA). ISRB always turns off the pin, and ISRA handles whether or not the pin should be high.
Now then the problem. All the timing works for each bit, but for some reason the loopover event causes the pulse spacing to SHORTEN. Yes shorten, not widen (which if anything was what I would expect because of extra clock cycles for executing the loop event).
it makes waveforms that look like this.
_|_|_|_|_ _ _ _ _|_|_|_||_|_|_|_ _ _ _
You can see that the problem resides in the junction between two packets, but the rest of the timing is good. I cant seem to track down why.
#include <stdint.h>
uint32_t sequence =0b111100001111; // example data sequence
uint8_t packetlength = 12;
uint8_t index = 0;
void setup(){
DDRD = 0xFF; // all port D as input
bitSet(DDRD, 4); // board pin 4 output
bitSet(PORTD, 4); // start high
// initialize timer1
TCCR1A = 0; // zeros timer control register
TCCR1B = 0;
TCNT1 = 0; // sets timer counter to 0
OCR1A = 340; // compare match register 340*62.5ns = 21.25us
OCR1B = 28; // 28*62.5ns = 1.75us
TIMSK1 = 0;
TCCR1B |= (1 << WGM12); // CTC mode
TCCR1B |= (1 << CS10); // CS10 no prescaler (use CS12 for 256 prescaler for testing)
TIMSK1 |= (1 << OCIE1A); // enable timer compare A interrupt
TIMSK1 |= (1 << OCIE1B); // enable timer compare B interrupt
}
ISR(TIMER1_COMPA_vect){ // controls bit repeat rate
if (bitRead(sequence,index) == 1){
bitSet(PORTD, 4); //set high
}
index ++;
if (index == packetlength){ //loop over when end reached.
index = 0;
}
}
ISR(TIMER1_COMPB_vect){ // controls duty cycle
bitClear(PORTD, 4); // set low
}
void loop(){
//nothing
}
Edit: April 5. Scope photos demonstrating inter pulsetrain period shortening.
The important measurement value is BX-AX
Normal. 340 + 6 calculation clock cycles (best estimate from scope)
Bad. Timer only counting 284 cycles before interrupt is firing.
Also Bad, but not a huge problem. This pulse is far to wide to be reasonably explained by the clock cycles needed set the bit low. It appears to take 17, I would expect 3.
I do not see why you should expect precise timing at the bit output. Interrupts begin after a delay once requested which will vary depending on instruction execution time for each instruction being run in whatever is being interrupted. I suspect (without seeing evidence in your report of the problem) that the variation you see is identical to instruction execution time variation.
If you want precise hardware output timing, you must either use never-interrupted programmed I/O or use the various flip-bit-upon-timer-compare features of the uP's hardware peripheral set. The ISR can be used to set things up for the next compare, but not to directly flip output bits.
Once you've figured out how to setup the action to be performed by the hardware upon comparitor matches, it will be simpler to do it all in a single ISR. That service routine can arrange for both the conditional bit set and the following unconditional bit clear. You probably want the ISR to run during the lengthier part of the cycle so that latency in the actual running of your [a] ISR code does not cause the setup to be too late.
[a. In addition to your ISR code, the programming environment is causing some context save a restore to wrap what you wrote. This can add execution cycles that might not be expected. Auto-generated context save/restore often is extravagant about tucking away state so that naive programmers are not puzzled by strange foreground-background interactions. ]

How to get millisecond resolution from DS3231 RTC

How to get accurate milliseconds?
I need to calculate the delay of sending data from Arduino A to Arduino B. I tried to use DS3231 but I cannot get milliseconds. What should I do to get accurate milliseconds from DS3231?
The comment above is correct, but using millis() when you have a dedicated realtime clock makes no sense. I'll provide you with better instructions.
First thing in any hardware interfacing project is a close reading of the datasheet. The DS3231 datasheeet reveals that there are five possible frequencies of sub-second outputs (see page 13):
32 KHz
1 KHz
1.024 KHz
4.096 KHz
8.192 KHz
These last four options are achieved by various combinations of the RS1 and RS2 control bits.
So, for example, to get exact milliseconds, you'd target option 2, 1KHz. You set RS1 = 0 and RS2 = 0 (see page 13 of the datasheet you provided) and INTCN = 0 (page 9). Then you'd need an ISR to capture interrupts from the !INT/SQW pin of the device to a digital input pin on your Arduino.
volatile uint16_t milliseconds; // volatile important here since we're changing this variable inside an interrupt service routine:
ISR(INT0_vect) // or whatever pin/interrupt you choose
{
++milliseconds;
if(milliseconds == 999) // roll over to zero
milliseconds = 0;
}
OR:
const int RTCpin = 3; // use any digital pin you need.
void setup()
{
pinmode(RTCpin, INPUT);
// Global Enable INT0 interrupt
GICR |= ( 1 < < INT0);
// Signal change triggers interrupt
MCUCR |= ( 1 << ISC00);
MCUCR |= ( 0 << ISC01);
}
If these commands in setup() don't work on your Arduino, google 'Arduino external interrupt INT0'. I've shown you two ways, one with Arduino code and one in C.
Once you have this ISR working and pin3 of the DS3231 connected to a digital input pin of your choosing, that pin will be activated at 1KHz, or every millisecond. Perfect!
// down in main program now you have access to milliseconds, you might want to start off by setting:
// When 1-second RTC changes seconds:
milliseconds = 0; // So you can measure milliseconds since last second.
That's all there is to it. All you need to learn now is how to set the command register using I2C commands and you're all set.
The C code example gains 1ms every second. Should be:
{
if (milliseconds == 999) // roll over to zero
milliseconds = 0;
else
++milliseconds;
}

Reset timer on msp430

Using CCS, embededded programming, MSP430F63736A
In my app I´m usingTimer A to switching LED(ON/OFF) for various time.
For example:
Led is ON - 0.5 sec
Led is OFF- 3 sec
I need to reset timer when it reach the value in register TA1CCR0. These times(0.5 s and 3 s) are in the register TA1CCR0. The problem is, that if I change the value of register TA1CCR0 from 0.5 sec to 3 sec, it will count from 0.5 to 3 sec. And I need a full 3 sec so I need a reset timer.
Thats the interrupt routine where I´m switching LED
if (P4OUT == 0x00)
{
P4OUT ^= BIT6; // LED ON
TA1CCR0 = (sekunda*t1); //t1- 3 s.... pc 2s
}
else
{
P4OUT = 0x00;
TA1CCR0 = (sekunda*t2);
}
}
Which counter mode are you using? You need to set the mode by writing to the TA1CTL register. The modes are:
#define MC_0 (0x0000) /* Timer A mode control: 0 - Stop */
#define MC_1 (0x0010) /* Timer A mode control: 1 - Up to CCR0 */
#define MC_2 (0x0020) /* Timer A mode control: 2 - Continuous up */
#define MC_3 (0x0030) /* Timer A mode control: 3 - Up/Down */
"Continuous" mode is typically the most used one. It counts until the overflow of the timer register, then restarts from zero. If you have it enabled, add to CCR continuously rather than setting it to the interval value:
TA1CCR0 += (sekunda*t1);
You can also use the "up" mode. In that case the timer counter should automatically reset to zero after reaching CCR. If you're modifying CCR within the ISR and then apparently this reset does not happen, you can modify the timer counter register (TAR or TA1R) by subtracting the value of the shortest period. Do not just set the timer register to zero, as it's usually not a good coding practice: it opens doors for timing inaccuracy accumulation in case the ISR calls are occasionally delayed (e.g. because some other interrupt is execution for multiple ticks).
In your ISR add an offset to TA1CCR0 and not reset it:
Say you want a 1 sec delay after the first interrupt, and let's say to generate 1 sec delay the timer must count up to 50000. So what you need to do in you ISR is TA1CCR0 +=50000.
In your case:
if (P4OUT == 0x00)
{
P4OUT ^= BIT6; // LED ON
TA1CCR0 += (sekunda*t1); //t1- 3 s.... pc 2s
}
else
{
P4OUT = 0x00;
TA1CCR0 += (sekunda*t2);
}
}
For an a complete example please see

Setting up multiple timers with AVR

I am trying to set up two timer interrupt routines with Teensy 2.0 Microcontroller (which is based on ATMEGA32U4 8 bit AVR 16 MHz) for independent control of two servo motors
After much trial - I was able to set one up on pin 7 of port C, but
How do I set up the second ISR to be initialized and called independently of the first?
Do I need to setup the second timer and, if so, what would such code look like?
Here is the setup code:
int main(void)
{
DDRE = 0xFF;
TCCR1A |= 1 << WGM12; // Configure timer 1 for CTC mode
TCCR1B = (1<<WGM12) | (1<<CS11) ;
OCR1A = 1000; // initial
TIMSK1 |= 1 << OCIE1A; // Output Compare A Match Interrupt Enable
sei(); // enable interrupts
// ...code that sets pulseWidth based on app logic variable.
// Not showing as its not important
}
ISR(TIMER1_COMPA_vect)
{
if (0 == pulseWidth)
{
return;
}
static uint8_t state = 0;
int dutyTotal = 20*1000;
if (0 == state)
{
PORTC |= 0b10000000;
OCR1A = pulseWidth;
state = 1;
}
else if (1 == state)
{
PORTC &= 0b01111111;
OCR1A = dutyTotal - pulseWidth;
state = 0;
}
}
While it's difficult to give a definitive answer without knowing more about your application (e.g. what kind of servo/motor, - I'm guessing model RC type with 1-2ms pule?) there are two approaches to solving the problem:
Firstly, In your code you seem to be manually generating a PWM signal by toggling PC7. You could add another output by increasing your number of states - you need one more than the number of servos to give the gap which sets the pulse repetition frequency. This is a common technique when you need to drive a lot of servos, since most RC servos don't care about pulse phasing or frequency (within limits), only the pulse width, so you can generate a bunch of different pulses one after the other on different outputs while only using one timer like this (in a sort of pseudo-code state diagram):
State 0:
Turn on output 1
Set timer TOP to pulse duration 1.
Go to state 1:
State 1:
Turn off output 1
Turn on output 2
Set timer TOP to pulse duration 1.
Go to state 2:
State 2:
Turn off output 2
Set timer TOP to pulse duration 3.
Go to state 0:
"Pulse duration 3" sets the PRF (pulse repetition frequency). If you want get fancy,
you can set this to 1/f-pd1-pd2, to give a constant frequency.
[ "TOP" is AVR-speak for the thing that sets the wrap (overflow) rate of the timer. See data sheet. ]
Secondly, there is a much easier way if you're only using two servos - use the hardware PWM functionality of the timer. The AVR timers have a built-in PWM function to do the pin-toggling for you. Timer1 on the mega32 has two PWM output pins, which could work great for your two servos and you then don't (necessarily) need an interrupt handler at all.
This is also the right solution if you are PWM driving motors directly (e.g. through an H-Bridge.)
To do this, you need to put the timer into PWM mode and enable the OC1A and OC1B output pins, e.g.
/*
* Set fast PWM mode on OC1A and OC1B with ICR1 as TOP
* (Mode 14)
*/
TCCR1A = (1 << WGM11) | (1 << COM1B1) | (1 << COM1A1);
TCCR1B = (3 << WGM12);
/*
* Clock source internal, pre-scale by 8
* (i.e. count rate = 2MHz for 16MHz crystal)
*/
TCCR1B |= (1 << CS11);
/*
* Set counter TOP value to set pulse repetition frequency.
* E.g. 50Hz (good for RC servos):
* 2e6/50 = 40000. N.B. This must be less than 65535.
* We count from t down to 0 so subtract 1 for true freq.
*/
ICR1 = 40000-1;
/* Enable OC1A and OC1B PWM output */
DDRB |= (1 << PB5) | (1 << PB6);
/* Uncomment to enable TIMER1_OVF_vect interrupts at 50Hz */
/* TIMSK1 = (1 << TOV1); */
/*
* Set both servos to centre (1.5ms pulse).
* Value for OCR1x is 2000 per ms then subtract one.
*/
OCR1A = 3000-1;
OCR1B = 3000-1;
Disclaimer - this code fragment compiles but I have not checked it on an actual device so you may need to double-check the register values. See the full datasheet at http://www.atmel.com/Images/doc7766.pdf
Also, you perhaps have some typos in your code, bit WGM12 doesn't exist in TCC1A (you've actually set bit 3, which is FOC1A - "force compare", see datasheet.) Also, you are writing DDRE to enable outputs on port E but toggling a pin on port C.
Halzephron, thank you so much for your answer. I dont have the high enough reputation to mark your answer, hopefully someone else will.
Details below:
You are absolutely right about being able to use a single IRS to control a number of servos - embarrassing I did not think of that, but given how well your simpler solution worked - I'll just use that.
... Also, you are writing DDRE to enable outputs on port E but toggling a pin on port C.
Thanks, I commented out that line, and it works the same - (so I dont need to enable output at all?)
Bit WGM12 doesn't exist in TCC1A (you've actually set bit 3, which is FOC1A - "force compare", see datasheet.)
I removed that too, but leaving the rest of my code unchanged - it results in servos moving slower, with way less torque and jittering as they do, even after arriving at desired position. Servo makes a weird "shaky" noise (frequency ~10-20, I'd say) and the arm it trembling, so for the reasons I don't understand - setting this bit seems necessary.
I suspected that a timer per motor is highly inelegant, and so I really like your second approach (built - in timer-generated PWM),
... This is also the right solution if you are PWM driving motors directly (e.g. through an H-Bridge.)
Very curious, why? I.e. what's the difference between using this method vs, say, timer-generated PWM.
In your code, I had to change below line to get 50HZ, otherwise I was getting 25HZ before (verified with a scope)
ICR1 = 20000-1; // was 40000 - 1;
One other thing I noticed with scope was that the timer-generated PWM code I have - produces less "rectangular" shape than the PWM code snippet you attached. It takes about 0.5 milliseconds for the signal to drop off to 0 with my code, and its absolutely instantaneous with yours (which is great). This solved another problem I had been bashing my head against: I could get analog servos to work fine with my code (IRS-generated PWM), but any digital servo I tried - just did not move, as if it was broken. I guess the shape of the signal is critical for digital servos, I never read this anywhere. Or maybe its something else I dont know.
As a side note, another weirdness I spent a bunch of time on - I uncommented the TIMSK1 = (1 << TOV1); line, thinking I always needed it - but what happened, my main function would get frozen, blocked forever upon first call to delay_ms(...) not sure what it is - but keeping it commented out unblocks my main loop (where I read the servo positin values from the USB HID using Teensy's sample code)
Again, many thanks for the help.

Understanding timer and period of interrupts

I am having a hard time understanding some code I found for using a timer and interrupts on an ARM board I have. The timer basically toggles an LED every interrupt between on and off to make it flash.
void main(void) {
/* Pin direction */
led_init();
/* timer setup */
/* CTRL */
#define COUNT_MODE 1 /* Use rising edge of primary source */
#define PRIME_SRC 0xf /* Peripheral clock with 128 prescale (for 24 MHz = 187500 Hz)*/
#define SEC_SRC 0 /* Don't need this */
#define ONCE 0 /* Keep counting */
#define LEN 1 /* Count until compare then reload with value in LOAD */
#define DIR 0 /* Count up */
#define CO_INIT 0 /* Other counters cannot force a re-initialization of this counter */
#define OUT_MODE 0 /* OFLAG is asserted while counter is active */
*TMR_ENBL = 0; /* TMRS reset to enabled */
*TMR0_SCTRL = 0;
*TMR0_CSCTRL = 0x0040;
*TMR0_LOAD = 0; /* Reload to zero */
*TMR0_COMP_UP = 18750; /* Trigger a reload at the end */
*TMR0_CMPLD1 = 18750; /* Compare one triggered reload level, 10 Hz maybe? */
*TMR0_CNTR = 0; /* Reset count register */
*TMR0_CTRL = (COUNT_MODE<<13) |
(PRIME_SRC<<9) |
(SEC_SRC<<7) |
(ONCE<<6) |
(LEN<<5) |
(DIR<<4) |
(CO_INIT<<3) |
(OUT_MODE);
*TMR_ENBL = 0xf; /* Enable all the timers --- why not? */
led_on();
enable_irq(TMR);
while(1) {
/* Sit here and let the interrupts do the work */
continue;
};
}
Right now, the LED flashes at a rate that I cannot determine per second. I'd like it to flash once per second. However, I do not understand the whole comparison and reloading.
Could somebody better explain this code?
As timers are a vendor- and part-specific feature (not a part of the ARM architecture), I can only give general guidance unless you mention which CPU or microcontroller you are dealing with.
Timers have several features:
A size, for instance 16 bits, which means they can count up or down to/from 65535.
A clock input, given as a clock frequency (perhaps from the CPU clock or an external crystal), and a prescaler which divides this clock frequency to another value (or divide by 1).
An interrupt on overflow - when the timer wraps back to 0, there is usually an option to trigger an interrupt.
A compare interrupt - when the timer meets a set value it will issue an interrupt.
In your case, I can see that you are using the compare feature of your timer. By determining your timer clock input, and calculating new values for the prescalers and compare register, you should be able to achieve a 1 Hz rate.
Before trying to understand the code you found, please do understand how a Timer Peripheral Unit works, then understand how you can configure it's registers to get the desired output.
How a Timer Peripheral Unit works?
This is hardware module which is embedded into micro controller along with CPU and other peripherals. Every peripheral modules inside micro controller are synchronized with common clock source. With reference to the code, Timer peripheral clock is 24 MHz which is then pre-scaled by 128 which means it will work at 187500 Hz. Now this frequency will depend upon clock configuration and oscillator.
Now Timer unit has a counter register which can count up to it's bit-size which could be 8,16 or 32 generally. Once you enable counting, this counter starts up-counting or down-counting the rising or falling or on both edges. Now you have choices whether you want to up-count (from 0 towards 255, for 8-bit) or down count (from 255 towards 0) and you want to count on which clock edge.
Now, at 187500 Hz, 1 cycle = 5.333333 us, if you are counting once in 1 cycle either at rising or at falling edge and e.g, if counter value = 100 (Up counting), total time elapsed is 5.33333*100=533us. Now you have to set a compare value value for the counter to set this period which will depend upon your flash rate. This compare value will be compared against your counter value in by comparator of Timer and Once it matches it will send an interrupt signal if you have enabled interrupt generation on compare match, where you can toggle you LED.
I hope you have understood How a Timer works.
In your sample code, Timer is configured to obtain a compare match event at the rate of 10Hz. so compare value is 187500/10 = 18750., for 1sec you can keep it 187500/1.
you have Timer Control Register TMR0_CTRL, where you can configure whether you want to count up or down, count on falling/rising/both edges, count only once/continuous, count upto compare value and then reset or keep counting till it's limit. Refer to micro controller manual for details of each bit fields.

Resources