Skip to content

Commit 70bd41e

Browse files
committed
Reworked timers.
micros now returns sensible (16bit) values over a wide range of CPU frequencies.
1 parent 67f821a commit 70bd41e

File tree

14 files changed

+154
-99
lines changed

14 files changed

+154
-99
lines changed

Makefile

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
1+
include Makefile.local
2+
13
OPTIMIZE = -O3
2-
DEFS = -I /usr/local/avr/avr/include -DF_CPU=16000000
4+
DEFS = -I /usr/local/avr/avr/include -DF_CPU=$(CPU_FREQUENCY)
35
LIBS = -B /usr/local/avr/avr/lib
46
CC = avr-gcc
57
CXX = avr-g++
@@ -11,8 +13,6 @@ CFLAGS = -g -Wall $(OPTIMIZE) -mmcu=$(MCU_TARGET) $(DEFS)
1113
CXXFLAGS = -g -Wall $(OPTIMIZE) -mmcu=$(MCU_TARGET) $(DEFS)
1214
LDFLAGS = -Wl,-Map,$@.map $(LIBS)
1315

14-
include Makefile.local
15-
1616
all: avr-ports.h .depend blink.bin blink.lst blink2.bin blink2.lst \
1717
test_enc28j60.bin test_enc28j60.lst onewire_test.bin onewire_test.lst \
1818
test_ip.bin test_ip.lst test_serial.bin test_serial.lst test_rf12.bin \
@@ -40,7 +40,7 @@ all: avr-ports.h .depend blink.bin blink.lst blink2.bin blink2.lst \
4040
$(CC) $(CFLAGS) $(LDFLAGS) -o $@ $<
4141

4242
avr-ports.h: get-ports.lst extract-ports.pl
43-
./extract-ports.pl < get-ports.lst > avr-ports.h
43+
./extract-ports.pl -f $(CPU_FREQUENCY) < get-ports.lst > avr-ports.h
4444

4545
clean:
4646
rm -f *.o *.map *.lst *.elf *.bin avr-ports.h .depend

Makefile.ben

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,3 @@
1+
CPU_FREQUENCY = 16000000
12
MCU_TARGET = atmega328p
3+

arduino++.h

Lines changed: 23 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -111,15 +111,10 @@ class Pin
111111
typedef Pin::B5 SPI_SCK;
112112
};
113113

114-
/** Don't use this directly, use Arduino16 or Arduino32 instead
115-
*/
116-
template<typename timeres_t>
117-
class _Arduino
114+
class Arduino
118115
{
119116
public:
120117

121-
typedef timeres_t time_res_t;
122-
123118
// The analog pins in Arduino numbering
124119
typedef Pin::C0 A0;
125120
typedef Pin::C1 A1;
@@ -224,7 +219,16 @@ class _Arduino
224219
UCSR0B = 0;
225220
#endif
226221
}
227-
222+
};
223+
224+
/** Don't use this directly, use Timer16 or Timer32 instead
225+
*/
226+
template<typename timeres_t>
227+
class _Timer
228+
{
229+
public:
230+
typedef timeres_t time_res_t;
231+
228232
static timeres_t millis()
229233
{
230234
const uint8_t oldSREG = SREG;
@@ -238,27 +242,28 @@ class _Arduino
238242
return m;
239243
}
240244

241-
static timeres_t micros()
245+
static uint16_t micros()
242246
{
243-
timeres_t m;
244-
uint16_t t;
245-
uint8_t oldSREG = SREG;
247+
uint8_t m;
248+
uint8_t t;
249+
const uint8_t oldSREG = SREG;
246250

247251
cli();
248252
t = TCNT0;
253+
254+
m = timer0_overflow_count % (1 << TIMER16_MICRO_SCALE);
249255

250256
#ifdef TIFR0
251257
if ((TIFR0 & _BV(TOV0)) && (t == 0))
252-
t = 256;
258+
m++;
253259
#else
254260
if ((TIFR & _BV(TOV0)) && (t == 0))
255-
t = 256;
261+
m++;
256262
#endif
257263

258-
m = timer0_overflow_count;
259264
SREG = oldSREG;
260265

261-
return ((m << 8) + t) * 64 / (F_CPU / 1000000L);
266+
return ((m << 8) + t) * (64 / (F_CPU / 1000000L));
262267
}
263268

264269
static void delay(timeres_t ms)
@@ -274,41 +279,9 @@ class _Arduino
274279
volatile static timeres_t timer0_millis;
275280
};
276281

277-
template<typename T> volatile T _Arduino<T>::timer0_overflow_count = 0;
278-
template<typename T> volatile T _Arduino<T>::timer0_clock_cycles = 0;
279-
template<typename T> volatile T _Arduino<T>::timer0_millis = 0;
280-
281-
/** This is an Arduino with 16bit timer resolution.
282-
283-
The value from Arduino16::millis() will wrap around after about 65 seconds
284-
and the value from Arduino16::micros() will wrap around after about 65 ms.
285-
286-
When you use Arduino16, you must #include "arduino++timer16.h", or
287-
Arduino16::millis() will always return 0.
288-
289-
The recommended way to use a timer value in user code is:
290-
291-
typename Arduino16::time_res_t now = Arduino16::millis();
292-
293-
Motivation: With Arduino16 instead of Arduino32, Lars has seen a code size
294-
reduction of 238 bytes with avr-gcc 4.6.1.
295-
*/
296-
typedef _Arduino<uint16_t> Arduino16;
297-
298-
/** This is an Arduino with 32bit timer resolution.
299-
300-
When you use Arduino32, you must #include "arduino++timer32.h", or
301-
Arduino32::millis() will always return 0.
302-
303-
The recommended way to use a timer value in user code is:
304-
305-
typename Arduino32::time_res_t now = Arduino32::millis();
306-
307-
The value from Arduino32::millis() will wrap around after about 49 days
308-
and the value from Arduino32::micros() will wrap around after about
309-
71 minutes.
310-
*/
311-
typedef _Arduino<uint32_t> Arduino32;
282+
template<typename T> volatile T _Timer<T>::timer0_overflow_count = 0;
283+
template<typename T> volatile T _Timer<T>::timer0_clock_cycles = 0;
284+
template<typename T> volatile T _Timer<T>::timer0_millis = 0;
312285

313286
void delayMicroseconds(unsigned int us)
314287
{

arduino++timer16.h

Lines changed: 0 additions & 23 deletions
This file was deleted.

blink.cc

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,12 @@
1111
int main(void)
1212
{
1313
// Arduino Pin D13 is an output
14-
Arduino16::D13::modeOutput();
14+
Arduino::D13::modeOutput();
1515

1616
while(true)
1717
{
1818
// toggle the pin
19-
Arduino16::D13::toggle();
19+
Arduino::D13::toggle();
2020
// wait
2121
_delay_ms(2000);
2222
}

blink2.cc

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,10 @@
22

33
int main(void)
44
{
5-
Arduino16::D13::modeOutput();
5+
Arduino::D13::modeOutput();
66
while(true)
77
{
8-
Arduino16::D13::toggle();
8+
Arduino::D13::toggle();
99
_delay_ms(200);
1010
}
1111
return 0;

extract-ports.pl

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,19 @@
33
use strict;
44

55
use Carp;
6+
use Getopt::Long;
7+
use POSIX;
8+
9+
my $f_cpu;
10+
GetOptions ("frequency=f" => \$f_cpu);
611

712
print "// Generated by $0\n\n";
813

14+
# 64 is the timer0 prescale value. We could make that also configurable
15+
# 16 is the bit width of uint16_t
16+
my $scale16 = 16 - ceil(log(64.0 / ($f_cpu / 1000000) * 2 ** 8)/log(2));
17+
print "#define TIMER16_MICRO_SCALE $scale16\n\n";
18+
919
while (my $line = <>) {
1020
chomp $line;
1121

pushbutton.h

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33

44
#include "arduino++.h"
55

6-
template<class Arduino_, class Pin, int debounce>
6+
template<class Timer_, class Pin, int debounce>
77
class PushButton
88
{
99
public:
@@ -21,13 +21,13 @@ class PushButton
2121
// aktivate pullup
2222
Pin::set();
2323
previous_ = !Pin::read();
24-
changed_ = Arduino_::millis();
24+
changed_ = Timer_::millis();
2525
duration_ = 0;
2626
}
2727

2828
event_type read()
2929
{
30-
const typename Arduino_::time_res_t now = Arduino_::millis();
30+
const typename Timer_::time_res_t now = Timer_::millis();
3131
// The button is active low
3232
const bool pressed = !Pin::read();
3333

@@ -36,7 +36,7 @@ class PushButton
3636
{
3737
// reset the debouncing timer
3838
previous_ = pressed;
39-
const typename Arduino_::time_res_t delta = now - changed_;
39+
const typename Timer_::time_res_t delta = now - changed_;
4040
changed_ = now;
4141

4242
if (delta > debounce)
@@ -55,12 +55,12 @@ class PushButton
5555
}
5656

5757
// duration is only valid after a keyup event
58-
typename Arduino_::time_res_t duration() { return duration_; }
58+
typename Timer_::time_res_t duration() { return duration_; }
5959

6060
private:
6161
bool previous_;
62-
typename Arduino_::time_res_t changed_;
63-
typename Arduino_::time_res_t duration_;
62+
typename Timer_::time_res_t changed_;
63+
typename Timer_::time_res_t duration_;
6464
};
6565

6666
#endif

test_micros.c

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
#include <stdint.h>
2+
#include <stdio.h>
3+
#include <stdlib.h>
4+
#include <math.h>
5+
6+
uint16_t micros(int freq, int overflow, uint8_t ticks)
7+
{
8+
return ((overflow << 8) + ticks) * (64 / freq);
9+
}
10+
11+
int main(int argc, char * argv[])
12+
{
13+
if (argc < 2)
14+
{
15+
printf("usage: ./test_micros freq(MHz) [overflow_count] [ticks]\n");
16+
exit(2);
17+
}
18+
19+
int freq = atoi(argv[1]);
20+
uint8_t scale = sizeof(uint16_t) * 8
21+
- ceil(log(64.0 / freq * (1 << 8))/log(2.0));
22+
23+
printf("CPU freq: %d MHz, scale: %d, resolution: %d\n", freq, scale,
24+
64/freq);
25+
printf("maximum microsecond value: %u\n",
26+
micros(freq, (1 << scale) - 1, 255));
27+
printf("maximum overflow count: %u\n", (1 << scale) - 1);
28+
29+
if (argc > 3)
30+
{
31+
uint16_t m = atoi(argv[2]);
32+
uint16_t t = atoi(argv[3]);
33+
34+
if (t == 256)
35+
++m;
36+
else if (t > 256)
37+
{
38+
printf("error: ticks may be at most 256");
39+
exit(2);
40+
}
41+
42+
m %= (1 << scale);
43+
44+
printf("micros: %u\n", micros(freq, m, t));
45+
}
46+
}

test_pushbutton.cc

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,23 @@
11
#include "arduino++.h"
2-
#include "arduino++timer16.h"
2+
#include "timer16.h"
33
#include "pushbutton.h"
44

5-
PushButton<Arduino16, Arduino16::D11, 20> Button;
5+
PushButton<Timer16, Arduino::D11, 20> Button;
66

77
int main(void)
88
{
9-
Arduino16::init();
10-
Arduino16::D13::modeOutput();
9+
Arduino::init();
10+
Arduino::D13::modeOutput();
1111

1212
Button.init();
1313

1414
for (;;)
1515
{
1616
if (Button.read() == Button.keyup)
1717
{
18-
Arduino16::D13::set();
19-
Arduino16::delay(Button.duration());
20-
Arduino16::D13::clear();
18+
Arduino::D13::set();
19+
Timer16::delay(Button.duration());
20+
Arduino::D13::clear();
2121
}
2222
}
2323
}

0 commit comments

Comments
 (0)