code8825 / arduino

Automatically exported from code.google.com/p/arduino
Other
0 stars 0 forks source link

Timer library for configuration and dynamically allocating (and deallocating) timer interrupt handlers (overflow and compare match) #169

Open GoogleCodeExporter opened 9 years ago

GoogleCodeExporter commented 9 years ago
What change would like to see?

The core should define the timer interrupt handlers and provide an API for 
registering a listener.  

Why?

For example, the Servo library and the tone() function can't both be linked 
into a sketch because 
they both define a timer 1 comparison interrupt handler.

Would this cause any incompatibilities with previous versions?  If so, how
can these be mitigated?

There may be some timing issues.

Original issue reported on code.google.com by dmel...@gmail.com on 3 Jan 2010 at 11:18

GoogleCodeExporter commented 9 years ago
In addition to the allocation of timer handlers, there is a need for functions 
that 
dealocates timers and returns them back to their startup state after wiring 
Init. 

Original comment by memargolis@gmail.com on 13 Feb 2010 at 11:19

GoogleCodeExporter commented 9 years ago

Original comment by dmel...@gmail.com on 13 Feb 2010 at 11:47

GoogleCodeExporter commented 9 years ago

Original comment by dmel...@gmail.com on 6 May 2010 at 6:09

GoogleCodeExporter commented 9 years ago
Issue 249 has been merged into this issue.

Original comment by dmel...@gmail.com on 12 Jul 2010 at 11:13

GoogleCodeExporter commented 9 years ago

Original comment by dmel...@gmail.com on 16 Aug 2011 at 10:37

GoogleCodeExporter commented 9 years ago
This will likely take the form of something like Wiring's Timer library:
http://wiring.uniandes.edu.co/source/trunk/wiring/firmware/cores/AVR8Bit/WHardwa
reTimer.h?view=markup
http://wiring.uniandes.edu.co/source/trunk/wiring/firmware/cores/AVR8Bit/WHardwa
reTimer.cpp?view=markup

Or the one for the LeafLabs Maple board:
http://leaflabs.com/docs/lang/api/hardwaretimer.html

In particular, note that they both involve runtime registering of timer 
interrupt handlers via function pointer, which isn't necessarily the most 
efficient implementation.  Compile-time registration, however, has 
complications for user code syntax and compilation.

Original comment by dmel...@gmail.com on 18 Aug 2011 at 3:22

GoogleCodeExporter commented 9 years ago

Original comment by dmel...@gmail.com on 18 Aug 2011 at 3:44

GoogleCodeExporter commented 9 years ago
Issue 241 has been merged into this issue.

Original comment by dmel...@gmail.com on 18 Aug 2011 at 3:45

GoogleCodeExporter commented 9 years ago
There's another implementation to look in this ATtiny core for Arduino: 
http://code.google.com/p/arduino-tiny/

In particular: 
http://code.google.com/p/arduino-tiny/source/browse/trunk/hardware/tiny/cores/ti
ny/UserTimer.h
http://code.google.com/p/arduino-tiny/source/browse/trunk/hardware/tiny/cores/ti
ny/core_timers.h
http://code.google.com/p/arduino-tiny/source/browse/trunk/hardware/tiny/cores/ti
ny/PwmTimer.h
http://code.google.com/p/arduino-tiny/source/browse/trunk/hardware/tiny/cores/ti
ny/ToneTimer.h

I think I'd prefer the syntax used by Maple or Wiring, though.

Original comment by dmel...@gmail.com on 18 Aug 2011 at 6:51

GoogleCodeExporter commented 9 years ago
[deleted comment]
GoogleCodeExporter commented 9 years ago
AVR micros have a lot of different timers, just looking at the Atmega328 
datasheet we could see:
- an 8-bit timer
- a 16-bit timer
- an 8-bit timer with asynchronous operation.

Every timer have different registers and bits to set to make it work properly, 
so the first step is to write a class that handle all the basic operations with 
all this kinds of timers. IMHO The Wiring lib is the one that best fit this 
requirement. At the end of initialization we got a set of instances of 
HardwareTimer class called:

Timer0
Timer1
Timer2 etc..

I think we can easily save them in an array and build a very simple managemnt 
class called Timers with the following two methods:

HardwareTimer T = Timers.allocate();
/* ... do something with timer T ... */
Timers.free(T);

Another step could be to abstract HardwareTimer and declare an interface Timer 
common to other type of timers.

Original comment by c.mag...@bug.st on 18 Aug 2011 at 8:55

GoogleCodeExporter commented 9 years ago
I've tested here:
https://github.com/cmaglie/Arduino/commit/7f96c73553109c45950bcf226fc8d4b7101030
13
it's a draft but is a starting point for discussion.

The class HardwareTimer needs to be included in the core, because the core 
itself uses timers.

I've ported the initialization of the arduino timers to use the new 
HardwareTimer class. Unfortunately the init() function is a "C" function and 
cannot call "C++" methods. To solve this i provided a __timers_init() function 
inside HardwareTimer.cpp (with cdecl function format) that is called from 
init().

I tried it with this simple test program:

void setup() {
  Serial.begin(9600);
  Serial.print(NUM_TIMERS);
  Serial.println(" timers!");
}
void loop() {
  delay(200);
  Serial.println(millis());
}

and seems that delay and millis still works... (pheew!)

The HardwareTimer interface needs a little bit of refactoring (some costants 
needs to be added for example).

The final increase in size of the code is over a Kb, its very huge.

Any thought?

Original comment by c.mag...@bug.st on 18 Aug 2011 at 11:26

GoogleCodeExporter commented 9 years ago
This looks pretty reasonable to me.

I have a couple of small disagreements on style.  For example, I don't think we 
need both setInterrupt(), and enable / disableInterrupt().  Actually, I don't 
think we really need any of those, as you can just detach the interrupt to stop 
it from being called.  Also, I don't like the use of the name OCR; I'd prefer 
setCompare() or setOutputCompare() or something.  But I guess you can leave 
this and I can just change it when I merge your branch.

There's been some discussion on the developers list about whether we need to 
support ISRs with performance requirements that make a function call too slow.  
Can you look into making the ISR weakly linked (with a GCC attribute) so they 
can be overridden by a library?  Have you been watching the discussion?  If 
there's a conclusion, we can support it, but I'm still not convinced we need to 
do anything besides a function pointer (attachInterrupt()-style) and weakly 
linked ISR handlers.

Do you want to write the developers list and ask people to look this over?  Or 
would you prefer I do?

Original comment by dmel...@gmail.com on 19 Aug 2011 at 4:18

GoogleCodeExporter commented 9 years ago
David write to the developer list, i'll follow the discussion.

About allocation of ISR: the library for now use predefined ISR that calls 
function pointer inside Timer objects. Moreover, the access to timer register 
is done indirectly using pointers that are initialized during objects creation. 
This is the do_it_all_in_runtime approach that wastes most resources but its, 
indeed, the simplest. I'm courious about Paul Stoffregen idea about templates 
to allocate all the resources in compile time, I've tried similar approaches 
before, but failed because of the weirdness of avr-libraries.

Original comment by c.mag...@bug.st on 19 Aug 2011 at 6:33

GoogleCodeExporter commented 9 years ago
Here another implementation that exploits templates to optimize register access

https://github.com/cmaglie/Arduino/commit/35a986323f7d1668b2cd593f9735e2f6f6ceab
bb

this one is based on discussion on developer list:
- the first part defines a series of classes with static methods to access 
registers.
- the second part is a template class that implements a driver for an 8-bit 
timer. With the help of templates we can use the same code to drive two 
different timers with two different set of registers but keeping all the 
optimiziations.

the last part to resolve is the allocation of ISR, for now i used a macro, but 
this part is still to be done...

Original comment by c.mag...@bug.st on 22 Aug 2011 at 4:10

GoogleCodeExporter commented 9 years ago
Interesting.  So the idea would be to add something similar for the 16-bit 
registers?

How much optimization do the templates buy us?  As I understand, most of the 
efficiency concerns are about the execution speed of the handler itself, not so 
much about the functions for configuring the timers.  Is there a need to worry 
about the performance of the configuration too?  

Also, we need to think about how timers would be allocated between different 
libraries, based on the needs of the sketch.  For example, with the current 
scheme (hardcoded ISRs), we're picking at compile time the number of timers to 
dedicate to tone() and the number to Servo.  That means, for example, that we 
only get one timer for tone().  The macros don't really help with this, do they?

Do you have any thoughts on how we might allow the user to configure, in their 
sketch, how many timers to allocate to which library, without a confusing 
syntax?

In any case, can you respond to the email on the developers list with this?

Original comment by dmel...@gmail.com on 22 Aug 2011 at 7:55

GoogleCodeExporter commented 9 years ago
David,

you're right the optimization that templates give us in this version is limited 
on timer initialization that is not so relevant. Anyway it's interesting the 
fact that templates used this way will replace all those indirect pointers 
access:

      _tcntnh = NULL;
      _tcntnl = &TCNT0;
      _timskn = &TIMSK;
      _tccrna = &TCCR0;
      _tccrnb = &TCCR0;

with a direct register access, and all the work is done at compile-time.

Is still missing the 16Bit version for the timer, i'll did it after we discover 
some more pro/cons, and if the pros are more than the cons :). I'm going to 
post this to the dev-list.

Original comment by c.mag...@bug.st on 23 Aug 2011 at 8:15

GoogleCodeExporter commented 9 years ago
I don't think there's enough consensus on this to get it into Arduino 1.0.  
Since it's not really breaking existing functionality but adding new 
functionality, I don't think it should be a problem to add later.

Original comment by dmel...@gmail.com on 31 Aug 2011 at 7:47

GoogleCodeExporter commented 9 years ago
I would think that the cost to implement a seconds function would be quite low. 
The function would be like "millis()" except it would "Returns the number of 
seconds since the Arduino board began running the current program."  The 
function could be called something like "secs()".

With this functionality in place.  Future enhancements could easily implement a 
Real Time Clock.

Original comment by Randall....@gmail.com on 20 Mar 2012 at 11:22

GoogleCodeExporter commented 9 years ago
I have added the functions I was thinking about to the core system 
(arduino-1.0.1-rc1).  I compiled and tested it on a Nano that was handy.
Arduino.h
unsigned long secs(void);
void setSecs(unsigned long);

wiring.c
volatile unsigned long timer0_overflow_count = 0;
volatile unsigned long timer0_secs = 0;
volatile unsigned long timer0_millis = 0;
static unsigned char timer0_fract = 0;
static unsigned int  timer0_sec_fract = 0;

#if defined(__AVR_ATtiny24__) || defined(__AVR_ATtiny44__) || 
defined(__AVR_ATtiny84__)
SIGNAL(TIM0_OVF_vect)
#else
SIGNAL(TIMER0_OVF_vect)
#endif
{
  // copy these to local variables so they can be stored in registers
  // (volatile variables must be read from memory on every access)
  unsigned int s = timer0_sec_fract;
  unsigned long m = timer0_millis;
  unsigned char f = timer0_fract;

  m += MILLIS_INC; s += MILLIS_INC;
  f += FRACT_INC;
  if (f >= FRACT_MAX) {
    f -= FRACT_MAX;
    m += 1; s++;
  }

  if (s >= 1000) {
    s-=1000; timer0_secs++;
  }
  timer0_sec_fract = s;

  timer0_fract = f;
  timer0_millis = m;
  timer0_overflow_count++;
}

unsigned long secs()
{
  unsigned long s;
  uint8_t oldSREG = SREG;
  cli();
  s = timer0_secs;
  SREG = oldSREG;

  return s;
}

void setSecs(unsigned long s)
{
  uint8_t oldSREG = SREG;
  cli();
  timer0_secs = s;
  SREG = oldSREG;
}

After these updates, implementing software clocks and time functions become 
very simple.  Granted, the clock will drift but it should be fairly accurate.

Original comment by Randall....@gmail.com on 21 Mar 2012 at 3:06