From c46d6afa39a0775f205643f056feee404c6d67ba Mon Sep 17 00:00:00 2001 From: Ralf Schlatterbeck Date: Thu, 26 Jun 2014 18:37:13 +0200 Subject: [PATCH] Make Arduino timer stuff work on Contiki New discovery: Contiki also uses timer 0. With almost the same interface as Arduino. So we now completely get rid of wiring.c (only the main file, the other wiring_xxx stay) and implement Arduino timer, delay, etc in terms of the corresponding Contiki routines. Verified that now delay works as expected. The LED in examples/osd/arduino-sketch blinks! Before this, the arduino_init routine in wiring.c destroyed the timer-0 initialization of contiki, making both, contiki timer implementation *and* contiki timer implementation fail if the arduino_init routine was called. Now both work. Squashed with following bug-fix commit. --- apps/arduino/arduino-process.c | 4 +- .../osd/arduino-sketch/resource_led_pwm.c | 17 +- examples/osd/arduino-sketch/sketch.pde | 22 +- platform/osd-merkur/Makefile.osd-merkur | 2 +- platform/osd-merkur/dev/Arduino.h | 8 - platform/osd-merkur/dev/hw-arduino.h | 36 +++ platform/osd-merkur/dev/wiring.c | 245 ------------------ 7 files changed, 61 insertions(+), 273 deletions(-) delete mode 100644 platform/osd-merkur/dev/wiring.c diff --git a/apps/arduino/arduino-process.c b/apps/arduino/arduino-process.c index 5a42ae32e..b7a87c0b3 100644 --- a/apps/arduino/arduino-process.c +++ b/apps/arduino/arduino-process.c @@ -50,6 +50,8 @@ */ #include "arduino-process.h" +#include "hw_timer.h" +#include "hw-arduino.h" PROCESS(arduino_sketch, "Arduino Sketch Wrapper"); @@ -57,7 +59,7 @@ PROCESS_THREAD(arduino_sketch, ev, data) { PROCESS_BEGIN(); - arduino_init (); + arduino_pwm_timer_init (); setup (); while (1) { loop (); diff --git a/examples/osd/arduino-sketch/resource_led_pwm.c b/examples/osd/arduino-sketch/resource_led_pwm.c index c547a6426..d3363a891 100644 --- a/examples/osd/arduino-sketch/resource_led_pwm.c +++ b/examples/osd/arduino-sketch/resource_led_pwm.c @@ -62,6 +62,7 @@ led_pwm_handler const uint16_t *accept = NULL; uint16_t a_ctype = REST.type.APPLICATION_JSON; uint16_t c_ctype = REST.get_header_content_type (request); + uint32_t tmp = 0; /* Seems like accepted type is currently unsupported? */ n_acc = REST.get_header_accept (request, &accept); @@ -136,7 +137,11 @@ led_pwm_handler temp [sizeof (temp) - 1] = 0; } PRINTF ("GOT: %s\n", temp); - pwm = atoi (temp); + tmp = strtoul (temp, NULL, 10); + if (tmp > 255) { + tmp = 255; + } + pwm = tmp; PRINTF ("Setting: %d\n", pwm); REST.set_response_status(response, REST.status.CHANGED); } else { @@ -181,6 +186,7 @@ led_period_handler const uint16_t *accept = NULL; uint16_t a_ctype = REST.type.APPLICATION_JSON; uint16_t c_ctype = REST.get_header_content_type (request); + uint32_t tmp = 0; /* Seems like accepted type is currently unsupported? */ n_acc = REST.get_header_accept (request, &accept); @@ -255,7 +261,14 @@ led_period_handler temp [sizeof (temp) - 1] = 0; } PRINTF ("GOT: %s\n", temp); - period_100ms = (atoi (temp) + 50) / 100; + tmp = (strtoul (temp, NULL, 10) + 50) / 100; + if (tmp > 10) { + tmp = 10; + } + if (tmp == 0) { + tmp = 1; + } + period_100ms = tmp; PRINTF ("Setting: %dms\n", period_100ms * 100); REST.set_response_status(response, REST.status.CHANGED); } else { diff --git a/examples/osd/arduino-sketch/sketch.pde b/examples/osd/arduino-sketch/sketch.pde index e000a89c0..4a1250796 100644 --- a/examples/osd/arduino-sketch/sketch.pde +++ b/examples/osd/arduino-sketch/sketch.pde @@ -28,20 +28,10 @@ void setup (void) void loop (void) { - static uint8_t last_pwm = 0; - if (last_pwm != pwm) { - last_pwm = pwm; - analogWrite (LED_PIN, pwm); - printf - ( "TCNT3: %04X TCCR3A: %04X TCCR3B: %04X TCCR3C: %04X OCR3C: %04X\n" - , TCNT3, TCCR3A, TCCR3B, TCCR3C, OCR3C - ); - } - - // Originally I wanted to sleep here to make the LED blink. - // Sleeping currently doesn't work, something turns off the chip. - // Maybe a mechanism to guard agains proto-threads taking too long? - //clock_wait (CLOCK_SECOND * period_100ms / 10); - //analogWrite (LED_PIN, 0); - //printf ("After write\n"); + /* Use 255 - pwm, LED on merkur-board is wired to +3.3V */ + analogWrite (LED_PIN, 255 - pwm); + printf ("clock : %u\nmillis: %lu\n", clock_time (), millis ()); + delay (period_100ms * 100); + analogWrite (LED_PIN, 255); /* OFF: LED on merkur-board is wired to +3.3V */ + delay (period_100ms * 100); } diff --git a/platform/osd-merkur/Makefile.osd-merkur b/platform/osd-merkur/Makefile.osd-merkur index 6dacd0fca..9650a201b 100644 --- a/platform/osd-merkur/Makefile.osd-merkur +++ b/platform/osd-merkur/Makefile.osd-merkur @@ -27,7 +27,7 @@ CONTIKI_TARGET_SOURCEFILES += servo.c servo-sensor.c #Needed for Relay 1 to 4 CONTIKI_TARGET_SOURCEFILES += relay.c relay-sensor.c # Arduino -CONTIKI_TARGET_SOURCEFILES += wiring_digital.c wiring.c wiring_analog.c +CONTIKI_TARGET_SOURCEFILES += wiring_digital.c wiring_analog.c CONTIKIBOARD=. BOOTLOADER_START = 0x1F000 diff --git a/platform/osd-merkur/dev/Arduino.h b/platform/osd-merkur/dev/Arduino.h index 3a0ae046b..4d9208864 100644 --- a/platform/osd-merkur/dev/Arduino.h +++ b/platform/osd-merkur/dev/Arduino.h @@ -69,10 +69,6 @@ extern "C"{ #define interrupts() sei() #define noInterrupts() cli() -#define clockCyclesPerMicrosecond() ( F_CPU / 1000000L ) -#define clockCyclesToMicroseconds(a) ( (a) / clockCyclesPerMicrosecond() ) -#define microsecondsToClockCycles(a) ( (a) * clockCyclesPerMicrosecond() ) - #define lowByte(w) ((uint8_t) ((w) & 0xff)) #define highByte(w) ((uint8_t) ((w) >> 8)) @@ -102,10 +98,6 @@ int digitalRead(uint8_t); int analogRead(uint8_t); void analogReference(uint8_t mode); -unsigned long millis(void); -unsigned long micros(void); -void delay(unsigned long); -void delayMicroseconds(unsigned int us); unsigned long pulseIn(uint8_t pin, uint8_t state, unsigned long timeout); void shiftOut(uint8_t dataPin, uint8_t clockPin, uint8_t bitOrder, uint8_t val); diff --git a/platform/osd-merkur/dev/hw-arduino.h b/platform/osd-merkur/dev/hw-arduino.h index 7aa54d3d8..f59f8daf2 100644 --- a/platform/osd-merkur/dev/hw-arduino.h +++ b/platform/osd-merkur/dev/hw-arduino.h @@ -48,6 +48,12 @@ * */ +#ifdef __cplusplus +extern "C"{ +#endif + +#include "contiki.h" + /* * The OSD hardware only supports timer 3 for PWM, timer 2 is used by * contiki for sleep/wakeup timing and is not usable for PWM. @@ -65,9 +71,39 @@ #define arduino_pwm_timer_init() \ (hwtimer_ini (3, HWT_WGM_PWM_PHASE_8_BIT, HWT_CLOCK_PRESCALER_64, 0)) +/* + * micros on arduino takes timer overflows into account. + * We put in the seconds counter. To get a consistent seconds / ticks + * value we have to disable interrupts. + */ +static inline uint32_t micros (void) +{ + uint32_t ticks; + uint8_t sreg = SREG; + cli (); + ticks = clock_seconds () * 1000000L + + clock_time () * 1000L / CLOCK_SECOND; + SREG = sreg; + return ticks; +} +/* + * millis counts only internal timer ticks since start, not trying to do + * something about overflows. Note that we don't try to emulate overflow + * behaviour of arduino implementation. + */ +#define millis() (((uint32_t)clock_time())*1000L/CLOCK_SECOND) +#define micros() (clock_seconds()*1000L+ +#define delay(ms) clock_delay_msec(ms) +#define delayMicroseconds(us) clock_delay_usec(us) + + /* * VI settings, see coding style * ex:ts=8:et:sw=2 */ +#ifdef __cplusplus +} // extern "C" +#endif + /** @} */ diff --git a/platform/osd-merkur/dev/wiring.c b/platform/osd-merkur/dev/wiring.c deleted file mode 100644 index 1c6abc44d..000000000 --- a/platform/osd-merkur/dev/wiring.c +++ /dev/null @@ -1,245 +0,0 @@ -/* - wiring.c - Partial implementation of the Wiring API for the ATmega8. - Part of Arduino - http://www.arduino.cc/ - - Copyright (c) 2005-2006 David A. Mellis - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General - Public License along with this library; if not, write to the - Free Software Foundation, Inc., 59 Temple Place, Suite 330, - Boston, MA 02111-1307 USA - - $Id$ -*/ - -#include "wiring_private.h" -#include "hw-arduino.h" - -// the prescaler is set so that timer0 ticks every 64 clock cycles, and the -// the overflow handler is called every 256 ticks. -#define MICROSECONDS_PER_TIMER0_OVERFLOW (clockCyclesToMicroseconds(64 * 256)) - -// the whole number of milliseconds per timer0 overflow -#define MILLIS_INC (MICROSECONDS_PER_TIMER0_OVERFLOW / 1000) - -// the fractional number of milliseconds per timer0 overflow. we shift right -// by three to fit these numbers into a byte. (for the clock speeds we care -// about - 8 and 16 MHz - this doesn't lose precision.) -#define FRACT_INC ((MICROSECONDS_PER_TIMER0_OVERFLOW % 1000) >> 3) -#define FRACT_MAX (1000 >> 3) - -volatile unsigned long timer0_overflow_count = 0; -volatile unsigned long timer0_millis = 0; -static unsigned char timer0_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 long m = timer0_millis; - unsigned char f = timer0_fract; - - m += MILLIS_INC; - f += FRACT_INC; - if (f >= FRACT_MAX) { - f -= FRACT_MAX; - m += 1; - } - - timer0_fract = f; - timer0_millis = m; - timer0_overflow_count++; -} - -unsigned long millis() -{ - unsigned long m; - uint8_t oldSREG = SREG; - - // disable interrupts while we read timer0_millis or we might get an - // inconsistent value (e.g. in the middle of a write to timer0_millis) - cli(); - m = timer0_millis; - SREG = oldSREG; - - return m; -} - -unsigned long micros() { - unsigned long m; - uint8_t oldSREG = SREG, t; - - cli(); - m = timer0_overflow_count; -#if defined(TCNT0) - t = TCNT0; -#elif defined(TCNT0L) - t = TCNT0L; -#else - #error TIMER 0 not defined -#endif - - -#ifdef TIFR0 - if ((TIFR0 & _BV(TOV0)) && (t < 255)) - m++; -#else - if ((TIFR & _BV(TOV0)) && (t < 255)) - m++; -#endif - - SREG = oldSREG; - - return ((m << 8) + t) * (64 / clockCyclesPerMicrosecond()); -} - -void delay(unsigned long ms) -{ - uint16_t start = (uint16_t)micros(); - - while (ms > 0) { - if (((uint16_t)micros() - start) >= 1000) { - ms--; - start += 1000; - } - } -} - -/* Delay for the given number of microseconds. Assumes a 8 or 16 MHz clock. */ -void delayMicroseconds(unsigned int us) -{ - // calling avrlib's delay_us() function with low values (e.g. 1 or - // 2 microseconds) gives delays longer than desired. - //delay_us(us); -#if F_CPU >= 20000000L - // for the 20 MHz clock on rare Arduino boards - - // for a one-microsecond delay, simply wait 2 cycle and return. The overhead - // of the function call yields a delay of exactly a one microsecond. - __asm__ __volatile__ ( - "nop" "\n\t" - "nop"); //just waiting 2 cycle - if (--us == 0) - return; - - // the following loop takes a 1/5 of a microsecond (4 cycles) - // per iteration, so execute it five times for each microsecond of - // delay requested. - us = (us<<2) + us; // x5 us - - // account for the time taken in the preceeding commands. - us -= 2; - -#elif F_CPU >= 16000000L - // for the 16 MHz clock on most Arduino boards - - // for a one-microsecond delay, simply return. the overhead - // of the function call yields a delay of approximately 1 1/8 us. - if (--us == 0) - return; - - // the following loop takes a quarter of a microsecond (4 cycles) - // per iteration, so execute it four times for each microsecond of - // delay requested. - us <<= 2; - - // account for the time taken in the preceeding commands. - us -= 2; -#else - // for the 8 MHz internal clock on the ATmega168 - - // for a one- or two-microsecond delay, simply return. the overhead of - // the function calls takes more than two microseconds. can't just - // subtract two, since us is unsigned; we'd overflow. - if (--us == 0) - return; - if (--us == 0) - return; - - // the following loop takes half of a microsecond (4 cycles) - // per iteration, so execute it twice for each microsecond of - // delay requested. - us <<= 1; - - // partially compensate for the time taken by the preceeding commands. - // we can't subtract any more than this or we'd overflow w/ small delays. - us--; -#endif - - // busy wait - __asm__ __volatile__ ( - "1: sbiw %0,1" "\n\t" // 2 cycles - "brne 1b" : "=w" (us) : "0" (us) // 2 cycles - ); -} - -void arduino_init() -{ - // this needs to be called before setup() or some functions won't - // work there - - // on the ATmega168, timer 0 is also used for fast hardware pwm - // (using phase-correct PWM would mean that timer 0 overflowed half as often - // resulting in different millis() behavior on the ATmega8 and ATmega168) - /* - * RSC: Keep timer0 for now, until we decide how to implement - * millis() etc in a contiki-compatible way - */ - -#if defined(TCCR0A) && defined(WGM01) - sbi(TCCR0A, WGM01); - sbi(TCCR0A, WGM00); -#endif - // set timer 0 prescale factor to 64 -#if defined(__AVR_ATmega128__) - // CPU specific: different values for the ATmega128 - sbi(TCCR0, CS02); -#elif defined(TCCR0) && defined(CS01) && defined(CS00) - // this combination is for the standard atmega8 - sbi(TCCR0, CS01); - sbi(TCCR0, CS00); -#elif defined(TCCR0B) && defined(CS01) && defined(CS00) - // this combination is for the standard 168/328/1280/2560 - sbi(TCCR0B, CS01); - sbi(TCCR0B, CS00); -#elif defined(TCCR0A) && defined(CS01) && defined(CS00) - // this combination is for the __AVR_ATmega645__ series - sbi(TCCR0A, CS01); - sbi(TCCR0A, CS00); -#else - #error Timer 0 prescale factor 64 not set correctly -#endif - - // enable timer 0 overflow interrupt -#if defined(TIMSK) && defined(TOIE0) - sbi(TIMSK, TOIE0); -#elif defined(TIMSK0) && defined(TOIE0) - sbi(TIMSK0, TOIE0); -#else - #error Timer 0 overflow interrupt not set correctly -#endif - - /* - * All other PCM timers are initialized here in a - * platform-specific way - */ - arduino_pwm_timer_init (); - - /* - * Removed the rest which manipulates the serial pins - */ -}