Add Arduino compatibility layer

We can now directly compile arduino sketches (.pde) files.
Arduino compatible analogWrite works now.
But there is still a long way to go, serial I/O and timer stuff (delay,
millis etc) currently don't work (not tested but I don't expect this to
work).
It can be used in an arduino sketch or in a normal contiki program.
We get a PWM frequency of 490.2 Hz (a period of 2.040 ms), that's
Arduino compatible. If you need different frequencies see native timer
usage in examples/osd/pwm-example
In a contiki program you have to call arduino_pwm_timer_init to
initialize the timer before pwm works. The arduino sketch wrapper
already does this.
For running a sketch, see examples/osd/arduino-sketch
This commit is contained in:
Ralf Schlatterbeck 2014-06-26 11:00:01 +02:00
parent 87903b2e89
commit e65dabb119
22 changed files with 999 additions and 361 deletions

View file

@ -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
CONTIKI_TARGET_SOURCEFILES += wiring_digital.c wiring.c wiring_analog.c
CONTIKIBOARD=.
BOOTLOADER_START = 0x1F000

View file

@ -1,6 +1,7 @@
#ifndef Arduino_h
#define Arduino_h
#include <hw-arduino.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
@ -88,14 +89,18 @@ typedef unsigned int word;
typedef uint8_t boolean;
typedef uint8_t byte;
void init(void);
/*
* This has been renamed from init to arduino_init, the original
* function name is way too generic. The arduino compatibility framework
* makes sure the correct function is called.
*/
void arduino_init(void);
void pinMode(uint8_t, uint8_t);
void digitalWrite(uint8_t, uint8_t);
int digitalRead(uint8_t);
int analogRead(uint8_t);
void analogReference(uint8_t mode);
void analogWrite(uint8_t, int);
unsigned long millis(void);
unsigned long micros(void);
@ -126,7 +131,6 @@ extern const uint16_t PROGMEM port_to_output_PGM[];
extern const uint8_t PROGMEM digital_pin_to_port_PGM[];
// extern const uint8_t PROGMEM digital_pin_to_bit_PGM[];
extern const uint8_t PROGMEM digital_pin_to_bit_mask_PGM[];
extern const uint8_t PROGMEM digital_pin_to_timer_PGM[];
// Get the bit location within the hardware port of the given virtual pin.
// This comes from the pins_*.c file for the active board configuration.
@ -135,7 +139,6 @@ extern const uint8_t PROGMEM digital_pin_to_timer_PGM[];
//
#define digitalPinToPort(P) ( pgm_read_byte( digital_pin_to_port_PGM + (P) ) )
#define digitalPinToBitMask(P) ( pgm_read_byte( digital_pin_to_bit_mask_PGM + (P) ) )
#define digitalPinToTimer(P) ( pgm_read_byte( digital_pin_to_timer_PGM + (P) ) )
#define analogInPinToBit(P) (P)
#define portOutputRegister(P) ( (volatile uint8_t *)( pgm_read_word( port_to_output_PGM + (P))) )
#define portInputRegister(P) ( (volatile uint8_t *)( pgm_read_word( port_to_input_PGM + (P))) )
@ -158,35 +161,15 @@ extern const uint8_t PROGMEM digital_pin_to_timer_PGM[];
#define PL 12
#endif
#define NOT_ON_TIMER 0
#define TIMER0A 1
#define TIMER0B 2
#define TIMER1A 3
#define TIMER1B 4
#define TIMER1C 5
#define TIMER2 6
#define TIMER2A 7
#define TIMER2B 8
#define TIMER3A 9
#define TIMER3B 10
#define TIMER3C 11
#define TIMER4A 12
#define TIMER4B 13
#define TIMER4C 14
#define TIMER4D 15
#define TIMER5A 16
#define TIMER5B 17
#define TIMER5C 18
#ifdef __cplusplus
} // extern "C"
#endif
#ifdef __cplusplus
#include "WCharacter.h"
#include "WString.h"
#include "HardwareSerial.h"
// look at this again when considering implementing serial
//#include "WCharacter.h"
//#include "WString.h"
//#include "HardwareSerial.h"
uint16_t makeWord(uint16_t w);
uint16_t makeWord(byte h, byte l);
@ -208,4 +191,6 @@ long map(long, long, long, long, long);
#include "pins_arduino.h"
#endif
#include "dev/arduino/arduino-compat.h"
#endif

View file

@ -0,0 +1,73 @@
/*
* Copyright (c) 2014, Ralf Schlatterbeck Open Source Consulting
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the Institute nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* This file is part of the Contiki operating system.
*/
/**
* \defgroup compatibility Arduino - Contiki
*
* This defines contiki-compatible hardware definitions for running
* arduino sketches (or just to call arduino-compatible function).
* For now only for osd hardware, a similar file should exist for each
* arduino-compatible hardware.
*
* @{
*/
/**
* \file
* Header file for arduino compatibility
* \author
* Ralf Schlatterbeck <rsc@runtux.com>
*
*/
/*
* 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.
*/
#define digitalPinToTimer(pin) \
( (pin) == 3 \
? TIMER3A \
: ( (pin) == 4 \
? TIMER3B \
: ((pin == 5) ? TIMER3C : NOT_ON_TIMER) \
) \
)
/* Only init timer 3 with phase correct pwm 8-bit and prescaler 64 */
#define arduino_pwm_timer_init() \
(hwtimer_ini (3, HWT_WGM_PWM_PHASE_8_BIT, HWT_CLOCK_PRESCALER_64, 0))
/*
* VI settings, see coding style
* ex:ts=8:et:sw=2
*/
/** @} */

View file

@ -215,48 +215,6 @@ const uint8_t PROGMEM digital_pin_to_bit_mask_PGM[] = {
// _BV( 7 ) , // PB 7 ** 35 ** D35 / LED2 / PWM
};
// !!!
const uint8_t PROGMEM digital_pin_to_timer_PGM[] = {
// TIMERS
// -------------------------------------------
NOT_ON_TIMER , // PE 1 ** 0 ** USART0_TX
NOT_ON_TIMER , // PE 0 ** 1 ** USART0_RX
TIMER3A , // PE 3 ** 2 ** D3 / PWM
TIMER3B , // PE 4 ** 3 ** D4 / PWM
TIMER3C , // PE 5 ** 4 ** D5 / PWM
NOT_ON_TIMER , // PE 6 ** 5 ** D6
NOT_ON_TIMER , // PD 3 ** 6 ** D21 / USART1_TX
NOT_ON_TIMER , // PD 2 ** 7 ** D20 / USART1_RX
NOT_ON_TIMER , // PD 0 ** 8 ** D15 / I2C_SCL
NOT_ON_TIMER , // PD 1 ** 9 ** D14 / I2C_SDA
NOT_ON_TIMER , // PB 0 ** 10 ** D10 / SPI_SSN
NOT_ON_TIMER , // PB 2 ** 11 ** D11 / SPI_MOSI
NOT_ON_TIMER , // PB 1 ** 12 ** D13 / SPI_SCK
NOT_ON_TIMER , // PB 3 ** 13 ** D12 / SPI_MISO
TIMER2A , // PB 4 ** 14 ** D9 / PWM
NOT_ON_TIMER , // PF 7 ** 15 ** A0 / D33
NOT_ON_TIMER , // PF 6 ** 16 ** A1 / D32
NOT_ON_TIMER , // PF 5 ** 17 ** A2 / D31
NOT_ON_TIMER , // PF 4 ** 18 ** A3 / D30
NOT_ON_TIMER , // PF 0 ** 19 ** A4 / D26
NOT_ON_TIMER , // PF 1 ** 20 ** A5 / D27
// NOT_ON_TIMER , // PF 2 ** 28 ** A6 / D28
// NOT_ON_TIMER , // PF 3 ** 29 ** A7 / D29
// NOT_ON_TIMER , // PE 2 ** 2 ** D2
// NOT_ON_TIMER , // PE 7 ** 7 ** D7
// TIMER1A , // PB 5 ** 8 ** D8 / PWM
// NOT_ON_TIMER , // PG 0 ** 16 ** D16
// NOT_ON_TIMER , // PG 1 ** 17 ** D17
// NOT_ON_TIMER , // PG 2 ** 18 ** D18
// TIMER0B , // PG 5 ** 19 ** D19 / PWM
// NOT_ON_TIMER , // PD 4 ** 22 ** D22
// NOT_ON_TIMER , // PD 5 ** 23 ** D23
// NOT_ON_TIMER , // PD 6 ** 24 ** D24
// NOT_ON_TIMER , // PD 7 ** 25 ** D25
// TIMER1B , // PB 6 ** 34 ** D34/ PWM
// TIMER1C , // PB 7 ** 35 ** D35 / PWM
};
#endif
#endif

View file

@ -23,6 +23,7 @@
*/
#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.
@ -186,20 +187,23 @@ void delayMicroseconds(unsigned int us)
);
}
void init()
void arduino_init()
{
// this needs to be called before setup() or some functions won't
// work there
sei();
// 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
@ -229,96 +233,13 @@ void init()
#error Timer 0 overflow interrupt not set correctly
#endif
// timers 1 and 2 are used for phase-correct hardware pwm
// this is better for motors as it ensures an even waveform
// note, however, that fast pwm mode can achieve a frequency of up
// 8 MHz (with a 16 MHz clock) at 50% duty cycle
/*
* All other PCM timers are initialized here in a
* platform-specific way
*/
arduino_pwm_timer_init ();
#if defined(TCCR1B) && defined(CS11) && defined(CS10)
TCCR1B = 0;
// set timer 1 prescale factor to 64
sbi(TCCR1B, CS11);
#if F_CPU >= 8000000L
sbi(TCCR1B, CS10);
#endif
#elif defined(TCCR1) && defined(CS11) && defined(CS10)
sbi(TCCR1, CS11);
#if F_CPU >= 8000000L
sbi(TCCR1, CS10);
#endif
#endif
// put timer 1 in 8-bit phase correct pwm mode
#if defined(TCCR1A) && defined(WGM10)
sbi(TCCR1A, WGM10);
#elif defined(TCCR1)
#warning this needs to be finished
#endif
// set timer 2 prescale factor to 64
#if defined(TCCR2) && defined(CS22)
sbi(TCCR2, CS22);
#elif defined(TCCR2B) && defined(CS22)
sbi(TCCR2B, CS22);
#else
#warning Timer 2 not finished (may not be present on this CPU)
#endif
// configure timer 2 for phase correct pwm (8-bit)
#if defined(TCCR2) && defined(WGM20)
sbi(TCCR2, WGM20);
#elif defined(TCCR2A) && defined(WGM20)
sbi(TCCR2A, WGM20);
#else
#warning Timer 2 not finished (may not be present on this CPU)
#endif
#if defined(TCCR3B) && defined(CS31) && defined(WGM30)
sbi(TCCR3B, CS31); // set timer 3 prescale factor to 64
sbi(TCCR3B, CS30);
sbi(TCCR3A, WGM30); // put timer 3 in 8-bit phase correct pwm mode
#endif
#if defined(TCCR4A) && defined(TCCR4B) && defined(TCCR4D) /* beginning of timer4 block for 32U4 and similar */
sbi(TCCR4B, CS42); // set timer4 prescale factor to 64
sbi(TCCR4B, CS41);
sbi(TCCR4B, CS40);
sbi(TCCR4D, WGM40); // put timer 4 in phase- and frequency-correct PWM mode
sbi(TCCR4A, PWM4A); // enable PWM mode for comparator OCR4A
sbi(TCCR4C, PWM4D); // enable PWM mode for comparator OCR4D
#else /* beginning of timer4 block for ATMEGA1280 and ATMEGA2560 */
#if defined(TCCR4B) && defined(CS41) && defined(WGM40)
sbi(TCCR4B, CS41); // set timer 4 prescale factor to 64
sbi(TCCR4B, CS40);
sbi(TCCR4A, WGM40); // put timer 4 in 8-bit phase correct pwm mode
#endif
#endif /* end timer4 block for ATMEGA1280/2560 and similar */
#if defined(TCCR5B) && defined(CS51) && defined(WGM50)
sbi(TCCR5B, CS51); // set timer 5 prescale factor to 64
sbi(TCCR5B, CS50);
sbi(TCCR5A, WGM50); // put timer 5 in 8-bit phase correct pwm mode
#endif
#if defined(ADCSRA)
// set a2d prescale factor to 128
// 16 MHz / 128 = 125 KHz, inside the desired 50-200 KHz range.
// XXX: this will not work properly for other clock speeds, and
// this code should use F_CPU to determine the prescale factor.
sbi(ADCSRA, ADPS2);
sbi(ADCSRA, ADPS1);
sbi(ADCSRA, ADPS0);
// enable a2d conversions
sbi(ADCSRA, ADEN);
#endif
// the bootloader connects pins 0 and 1 to the USART; disconnect them
// here so they can be used as normal digital i/o; they will be
// reconnected in Serial.begin()
#if defined(UCSRB)
UCSRB = 0;
#elif defined(UCSR0B)
UCSR0B = 0;
#endif
/*
* Removed the rest which manipulates the serial pins
*/
}

View file

@ -43,198 +43,6 @@ int analogRead(uint8_t pin)
return readADC(pin);
}
// Right now, PWM output only works on the pins with
// hardware support. These are defined in the appropriate
// pins_*.c file. For the rest of the pins, we default
// to digital output.
void analogWrite(uint8_t pin, int val)
{
// We need to make sure the PWM output is enabled for those pins
// that support it, as we turn it off when digitally reading or
// writing with them. Also, make sure the pin is in output mode
// for consistenty with Wiring, which doesn't require a pinMode
// call for the analog output pins.
pinMode(pin, OUTPUT);
if (val == 0)
{
digitalWrite(pin, LOW);
}
else if (val == 255)
{
digitalWrite(pin, HIGH);
}
else
{
switch(digitalPinToTimer(pin))
{
// XXX fix needed for atmega8
#if defined(TCCR0) && defined(COM00) && !defined(__AVR_ATmega8__)
case TIMER0A:
// connect pwm to pin on timer 0
sbi(TCCR0, COM00);
OCR0 = val; // set pwm duty
break;
#endif
#if defined(TCCR0A) && defined(COM0A1)
case TIMER0A:
// connect pwm to pin on timer 0, channel A
sbi(TCCR0A, COM0A1);
OCR0A = val; // set pwm duty
break;
#endif
#if defined(TCCR0A) && defined(COM0B1)
case TIMER0B:
// connect pwm to pin on timer 0, channel B
sbi(TCCR0A, COM0B1);
OCR0B = val; // set pwm duty
break;
#endif
#if defined(TCCR1A) && defined(COM1A1)
case TIMER1A:
// connect pwm to pin on timer 1, channel A
sbi(TCCR1A, COM1A1);
OCR1A = val; // set pwm duty
break;
#endif
#if defined(TCCR1A) && defined(COM1B1)
case TIMER1B:
// connect pwm to pin on timer 1, channel B
sbi(TCCR1A, COM1B1);
OCR1B = val; // set pwm duty
break;
#endif
#if defined(TCCR1A) && defined(COM1C1)
case TIMER1C:
// connect pwm to pin on timer 1, channel C
sbi(TCCR1A, COM1C1);
OCR1C = val; // set pwm duty
break;
#endif
#if defined(TCCR2) && defined(COM21)
case TIMER2:
// connect pwm to pin on timer 2
sbi(TCCR2, COM21);
OCR2 = val; // set pwm duty
break;
#endif
#if defined(TCCR2A) && defined(COM2A1)
case TIMER2A:
// connect pwm to pin on timer 2, channel A
sbi(TCCR2A, COM2A1);
OCR2A = val; // set pwm duty
break;
#endif
#if defined(TCCR2A) && defined(COM2B1)
case TIMER2B:
// connect pwm to pin on timer 2, channel B
sbi(TCCR2A, COM2B1);
OCR2B = val; // set pwm duty
break;
#endif
#if defined(TCCR3A) && defined(COM3A1)
case TIMER3A:
// connect pwm to pin on timer 3, channel A
sbi(TCCR3A, COM3A1);
OCR3A = val; // set pwm duty
break;
#endif
#if defined(TCCR3A) && defined(COM3B1)
case TIMER3B:
// connect pwm to pin on timer 3, channel B
sbi(TCCR3A, COM3B1);
OCR3B = val; // set pwm duty
break;
#endif
#if defined(TCCR3A) && defined(COM3C1)
case TIMER3C:
// connect pwm to pin on timer 3, channel C
sbi(TCCR3A, COM3C1);
OCR3C = val; // set pwm duty
break;
#endif
#if defined(TCCR4A)
case TIMER4A:
//connect pwm to pin on timer 4, channel A
sbi(TCCR4A, COM4A1);
#if defined(COM4A0) // only used on 32U4
cbi(TCCR4A, COM4A0);
#endif
OCR4A = val; // set pwm duty
break;
#endif
#if defined(TCCR4A) && defined(COM4B1)
case TIMER4B:
// connect pwm to pin on timer 4, channel B
sbi(TCCR4A, COM4B1);
OCR4B = val; // set pwm duty
break;
#endif
#if defined(TCCR4A) && defined(COM4C1)
case TIMER4C:
// connect pwm to pin on timer 4, channel C
sbi(TCCR4A, COM4C1);
OCR4C = val; // set pwm duty
break;
#endif
#if defined(TCCR4C) && defined(COM4D1)
case TIMER4D:
// connect pwm to pin on timer 4, channel D
sbi(TCCR4C, COM4D1);
#if defined(COM4D0) // only used on 32U4
cbi(TCCR4C, COM4D0);
#endif
OCR4D = val; // set pwm duty
break;
#endif
#if defined(TCCR5A) && defined(COM5A1)
case TIMER5A:
// connect pwm to pin on timer 5, channel A
sbi(TCCR5A, COM5A1);
OCR5A = val; // set pwm duty
break;
#endif
#if defined(TCCR5A) && defined(COM5B1)
case TIMER5B:
// connect pwm to pin on timer 5, channel B
sbi(TCCR5A, COM5B1);
OCR5B = val; // set pwm duty
break;
#endif
#if defined(TCCR5A) && defined(COM5C1)
case TIMER5C:
// connect pwm to pin on timer 5, channel C
sbi(TCCR5A, COM5C1);
OCR5C = val; // set pwm duty
break;
#endif
case NOT_ON_TIMER:
default:
if (val < 128) {
digitalWrite(pin, LOW);
} else {
digitalWrite(pin, HIGH);
}
}
}
}
/*
* analogWrite is now implemented in dev/arduino/arduino-compat.h
*/