Remove digitalIOPerformance library as it is no longer used
This commit is contained in:
parent
746753a4da
commit
48c5350d63
|
@ -1,193 +0,0 @@
|
||||||
# digitalIOPerformance.h
|
|
||||||
|
|
||||||
digitalIOPerformance.h is a single-file Arduino library (header file)
|
|
||||||
that adds higher-performance digital I/O functions to Arduino
|
|
||||||
programs.
|
|
||||||
|
|
||||||
# Quick Start
|
|
||||||
|
|
||||||
* Copy the "digitalIOPerformance" directory to your [Arduino libraries folder](http://arduino.cc/en/Guide/Libraries).
|
|
||||||
|
|
||||||
* Add _#include "digitalIOPerformance.h"_ near the top of your sketch.
|
|
||||||
|
|
||||||
* Done! When you recompile, performance of digitalRead/digitalWrite &
|
|
||||||
pinMode should be substantially faster in most cases. However,
|
|
||||||
functionality should be otherwise identical to the original Arduino
|
|
||||||
functions.
|
|
||||||
|
|
||||||
* Your sketch's compiled size may also go down (depending on how much
|
|
||||||
digital I/O you do.)
|
|
||||||
|
|
||||||
## What Becomes Faster?
|
|
||||||
|
|
||||||
Any digital I/O when the pin number is known at compile time:
|
|
||||||
|
|
||||||
const int led_pin = 13;
|
|
||||||
digitalWrite(led_pin, HIGH); // <-- gets ~30x faster
|
|
||||||
|
|
||||||
#define RELAY_PIN 11
|
|
||||||
digitalWrite(RELAY_PIN, x>2); // <-- also ~30x faster
|
|
||||||
|
|
||||||
Not the case where the pin number isn't known at compile time:
|
|
||||||
|
|
||||||
int my_pin = 4; // <-- note this is a variable, no 'const' marker!
|
|
||||||
|
|
||||||
void loop() {
|
|
||||||
digitalWrite(my_pin, LOW); // <-- same speed as normal Arduino
|
|
||||||
}
|
|
||||||
|
|
||||||
## Option: More performance, smaller compiled size, on PWM Enabled pins
|
|
||||||
|
|
||||||
The Arduino's digital I/O functions deal with the
|
|
||||||
possibility that a pin is used for PWM output with
|
|
||||||
[analogWrite()](http://arduino.cc/en/Tutorial/PWM), and then the same
|
|
||||||
pin gets used again later for normal digital output with
|
|
||||||
digitalWrite(). Something like this:
|
|
||||||
|
|
||||||
analogWrite(MY_PIN, 120);
|
|
||||||
// ... do some stuff
|
|
||||||
digitalWrite(MY_PIN, LOW);
|
|
||||||
|
|
||||||
If you never mix analogWrite and digitalRead/Write on the same pin, you can
|
|
||||||
boost performance on PWM-enabled pins (making them equal with
|
|
||||||
non-PWM-enabled pins) by adding a second line above the first:
|
|
||||||
|
|
||||||
#define DIGITALIO_NO_MIX_ANALOGWRITE
|
|
||||||
#include "digitalIOPerformance.h
|
|
||||||
|
|
||||||
Digital read & write performance will be higher, and the compiled
|
|
||||||
sketch size will be smaller.
|
|
||||||
|
|
||||||
... if you still want to mix analogWrite() and
|
|
||||||
digitalRead/digitalWrite() on the same pin, but also use
|
|
||||||
DIGITALIO_NO_MIX_ANALOGWRITE, then you can use the noAnalogWrite()
|
|
||||||
function in between to turn off the PWM output:
|
|
||||||
|
|
||||||
analogWrite(MY_PIN, 120);
|
|
||||||
// ... do some stuff
|
|
||||||
noAnalogWrite(MY_PIN);
|
|
||||||
digitalWrite(MY_PIN, LOW);
|
|
||||||
|
|
||||||
## Option: Last shreds of performance
|
|
||||||
|
|
||||||
Under some circumstances the Arduino digital functions have to protect
|
|
||||||
against [interrupts](http://www.uchobby.com/index.php/2007/11/24/arduino-interrupts/) occuring while an I/O read or write is in progress. This can cause things to get out of sync if an I/O operation takes more than a single instruction to process ("interrupt unsafe".)
|
|
||||||
|
|
||||||
When using digitalIOPerformance, most writes only take one instruction anyway, so they are naturally "interrupt safe". However some cases have to be made "interrupt safe":
|
|
||||||
|
|
||||||
* Writing to certain pins on Arduino Mega models.
|
|
||||||
|
|
||||||
* Setting pinMode() to INPUT_PULLUP.
|
|
||||||
|
|
||||||
By default, "digitalIOPerformance" makes these cases interrupt safe,
|
|
||||||
in order to keep them as safe as the built-in Arduino
|
|
||||||
versions. However if your Arduino sketch doesn't use interrupts, or
|
|
||||||
you don't care about interrupt safety for digital I/O, then you can
|
|
||||||
add another line to get faster digital I/O on them:
|
|
||||||
|
|
||||||
#define DIGITALIO_NO_INTERRUPT_SAFETY
|
|
||||||
#include "digitalIOPerformance.h
|
|
||||||
|
|
||||||
Only do this one if you're very sure you don't need interrupt safe
|
|
||||||
digital reads & writes. If you're already using
|
|
||||||
DIGITALIO_NO_MIX_ANALOGWRITE then the only real improvement is on an
|
|
||||||
Arduino Mega, and even then only on some of the pins.
|
|
||||||
|
|
||||||
## Option: Disable automatic performance boost
|
|
||||||
|
|
||||||
If you don't want the library to automatically replace your
|
|
||||||
digitalRead/digitalWrite/pinMode function calls, you can do that as
|
|
||||||
well:
|
|
||||||
|
|
||||||
#define DIGITALIO_MANUAL
|
|
||||||
#include "digitalIOPerformance.h
|
|
||||||
|
|
||||||
You can still use the functions in the library if you call them by
|
|
||||||
their original names (given below.)
|
|
||||||
|
|
||||||
# Functions Defined
|
|
||||||
|
|
||||||
These functions are defined by the library:
|
|
||||||
|
|
||||||
## digitalWriteSafe / digitalReadSafe / pinModeSafe
|
|
||||||
|
|
||||||
These versions of digitalWrite/digitalRead & pinMode run faster
|
|
||||||
than the built-in Arduino versions, if the pin number is known at
|
|
||||||
compile time.
|
|
||||||
|
|
||||||
They are also just as safe as the built-in Arduino versions - they're
|
|
||||||
interrupt safe, and they disable any previous analogWrite() calls.
|
|
||||||
|
|
||||||
When you include the library, these functions automatically replace
|
|
||||||
the built-in digitalWrite & pinMode functions. If you don't want this
|
|
||||||
to happen, define DIGITALIO_MANUAL before including (as shown above.)
|
|
||||||
|
|
||||||
|
|
||||||
## digitalWriteFast / digitalReadFast / pinModeFast
|
|
||||||
|
|
||||||
These versions of digitalWrite/digitalRead & pinMode will usually
|
|
||||||
compile down to a single port register instruction (as fast as is
|
|
||||||
possible to be) if the pin number is known at compile time. If the pin
|
|
||||||
number is a variable then they fall through to the slower Arduino
|
|
||||||
version if the pin number is a variable.
|
|
||||||
|
|
||||||
You can have these functions automatically replace all Arduino
|
|
||||||
digitalWrite/digitalRead & pinMode functions if you include the
|
|
||||||
library thus:
|
|
||||||
|
|
||||||
#define DIGITALIO_NO_INTERRUPT_SAFETY
|
|
||||||
#define DIGITALIO_NO_MIX_ANALOGWRITE
|
|
||||||
#include "digitalIOPerformance.h
|
|
||||||
|
|
||||||
## noAnalogWrite
|
|
||||||
|
|
||||||
Using digitalWriteFast() will not automatically turn off a
|
|
||||||
previous analogWrite() to that port, unlike Arduino's digitalWrite().
|
|
||||||
|
|
||||||
If you are mixing analogWrite() and digitalWriteFast() on a port, call
|
|
||||||
this function after immediately before calling digitalWriteFast(), if
|
|
||||||
you had previously called analogWrite().
|
|
||||||
|
|
||||||
The "safe" methods already call noAnalogWrite() any time you access a
|
|
||||||
PWM-capable pin, unless you've defined DIGITALIO_NO_MIX_ANALOGWRITE.
|
|
||||||
|
|
||||||
|
|
||||||
# Status
|
|
||||||
|
|
||||||
New, untested, hacky, work in progress. :)
|
|
||||||
|
|
||||||
Please raise an issue if this doesn't work with your Arduino install,
|
|
||||||
or doesn't seem to inline properly (ie massively bloated code size
|
|
||||||
instead of shrinking code size!)
|
|
||||||
|
|
||||||
Minimal testing done with Windows Arduino 1.0.3 (gcc 4.3) and my
|
|
||||||
Ubuntu Arduino 1.0.3 (gcc 4.7.) Should work with any Arduino version
|
|
||||||
above 1.0 (I think.)
|
|
||||||
|
|
||||||
# Known Shortcomings
|
|
||||||
|
|
||||||
* No ARM support, does nothing at all on the Arduino Due.
|
|
||||||
|
|
||||||
# Internal Workings
|
|
||||||
|
|
||||||
digitalIOPerformance.h is code generated automatically from an
|
|
||||||
existing Arduino installation by the script generateDigitalIOHeader.py.
|
|
||||||
|
|
||||||
You shouldn't need to run the code generation script unless you have a
|
|
||||||
newer/different Arduino version than the one it was last run against.
|
|
||||||
|
|
||||||
However, having code generation means it should be simple to update
|
|
||||||
against future new boards like the Leonardo (assuming the file
|
|
||||||
formats don't change much.)
|
|
||||||
|
|
||||||
# Thanks
|
|
||||||
|
|
||||||
Big thanks to the authors of digitalWriteFast - Paul Stoffregen, Bill
|
|
||||||
Westfield, an John Raines. I wrote this instead of updating
|
|
||||||
digitalWriteFast.h to support the Leonardo (code generation was more
|
|
||||||
appealing than handwritten bit fiddles!)
|
|
||||||
|
|
||||||
Also thanks to Alastair D'Silva who told me a while ago about the
|
|
||||||
trick of preprocessing pins_arduino.h to extract Arduino pin
|
|
||||||
information, he uses this in the performance-oriented AVR library
|
|
||||||
[MHVLib](http://www.makehackvoid.com/project/MHVLib).
|
|
|
@ -1,70 +0,0 @@
|
||||||
/* Arduino board:
|
|
||||||
* %(id)s
|
|
||||||
* %(name)s
|
|
||||||
* MCU: %(build.mcu)s
|
|
||||||
*/
|
|
||||||
#if %(ifdef_clause)s
|
|
||||||
#ifdef _DIGITALIO_MATCHED_BOARD
|
|
||||||
#error "This header's Arduino configuration heuristics have matched multiple boards. The header may be out of date."
|
|
||||||
#endif
|
|
||||||
#define _DIGITALIO_MATCHED_BOARD
|
|
||||||
|
|
||||||
__attribute__((always_inline))
|
|
||||||
static inline void pinModeFast(uint8_t pin, uint8_t mode) {
|
|
||||||
if(!__builtin_constant_p(pin)) {
|
|
||||||
pinMode(pin, mode);
|
|
||||||
}
|
|
||||||
%(pinmode_clause)s
|
|
||||||
}
|
|
||||||
|
|
||||||
__attribute__((always_inline))
|
|
||||||
static inline void digitalWriteFast(uint8_t pin, uint8_t value) {
|
|
||||||
if(!__builtin_constant_p(pin)) {
|
|
||||||
digitalWrite(pin, value);
|
|
||||||
}
|
|
||||||
%(digitalwrite_clause)s
|
|
||||||
}
|
|
||||||
|
|
||||||
__attribute__((always_inline))
|
|
||||||
static inline int digitalReadFast(uint8_t pin) {
|
|
||||||
if(!__builtin_constant_p(pin)) {
|
|
||||||
return digitalRead(pin);
|
|
||||||
}
|
|
||||||
%(digitalread_clause)s
|
|
||||||
return LOW;
|
|
||||||
}
|
|
||||||
|
|
||||||
__attribute__((always_inline))
|
|
||||||
static inline void noAnalogWrite(uint8_t pin) {
|
|
||||||
if(!__builtin_constant_p(pin)) {
|
|
||||||
return; // noAnalogWrite is taken care of by digitalWrite() for variables
|
|
||||||
}
|
|
||||||
%(timer_clause)s
|
|
||||||
}
|
|
||||||
|
|
||||||
__attribute__((always_inline))
|
|
||||||
static inline bool _isPWMPin(uint8_t pin) {
|
|
||||||
%(ispwm_clause)s
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
__attribute__((always_inline))
|
|
||||||
static inline bool _directionIsAtomic(uint8_t pin) {
|
|
||||||
%(direction_clause)s
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
__attribute__((always_inline))
|
|
||||||
static inline bool _outputIsAtomic(uint8_t pin) {
|
|
||||||
%(output_clause)s
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
__attribute__((always_inline))
|
|
||||||
static inline bool _inputIsAtomic(uint8_t pin) {
|
|
||||||
%(input_clause)s
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
#endif
|
|
|
@ -1,13 +0,0 @@
|
||||||
#ifndef _DIGITALIO_MATCHED_BOARD
|
|
||||||
#error "This header's Arduino configuration heuristics couldn't match this board configuration. No fast I/O is available. The header may be out of date."
|
|
||||||
#endif
|
|
||||||
#undef _DIGITALIO_MATCHED_BOARD
|
|
||||||
|
|
||||||
#ifndef DIGITALIO_MANUAL
|
|
||||||
#define digitalWrite digitalWriteSafe
|
|
||||||
#define digitalRead digitalReadSafe
|
|
||||||
#define pinMode pinModeSafe
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif
|
|
||||||
#endif
|
|
|
@ -1,116 +0,0 @@
|
||||||
/*
|
|
||||||
*
|
|
||||||
* Header for high performance Arduino Digital I/O
|
|
||||||
*
|
|
||||||
* Automatically generated from the Arduino library setup (boards.txt & pins_arduino.h)
|
|
||||||
*
|
|
||||||
* See the accompanying file README.md for documentation.
|
|
||||||
*
|
|
||||||
* ****
|
|
||||||
*
|
|
||||||
* This header is a derived work of the Arduino microcontroller libraries, which are
|
|
||||||
* licensed under LGPL. Although as a header file it is not bound by the same usage
|
|
||||||
* clauses as the library itself (see "3. Object Code Incorporating Material from
|
|
||||||
* Library Header Files.)"
|
|
||||||
*
|
|
||||||
* Note that although the code generated functions below here look horrific,
|
|
||||||
* they're written to inline only very small subsets of themselves at compile
|
|
||||||
* time (they generate single port-register instructions when the parameters
|
|
||||||
* are constant.)
|
|
||||||
*
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifdef __AVR__
|
|
||||||
#ifndef _DIGITALIO_PERFORMANCE
|
|
||||||
#define _DIGITALIO_PERFORMANCE
|
|
||||||
|
|
||||||
#include "Arduino.h"
|
|
||||||
#include <util/atomic.h>
|
|
||||||
|
|
||||||
// Forward declarations for per-Arduino-board functions:
|
|
||||||
inline static void pinModeFast(uint8_t pin, uint8_t mode);
|
|
||||||
inline static void digitalWriteFast(uint8_t pin, uint8_t value);
|
|
||||||
inline static int digitalReadFast(uint8_t pin);
|
|
||||||
inline static void noAnalogWrite(uint8_t pin);
|
|
||||||
|
|
||||||
// These few per-board functions are designed for internal use, but
|
|
||||||
// you can call them yourself if you want.
|
|
||||||
inline static bool _isPWMPin(uint8_t pin);
|
|
||||||
inline static bool _directionIsAtomic(uint8_t pin);
|
|
||||||
inline static bool _outputIsAtomic(uint8_t pin);
|
|
||||||
inline static bool _inputIsAtomic(uint8_t pin);
|
|
||||||
|
|
||||||
#ifdef DIGITALIO_NO_INTERRUPT_SAFETY
|
|
||||||
#define DIGITALIO_NO_INTERRUPT_SAFETY 1
|
|
||||||
#else
|
|
||||||
#define DIGITALIO_NO_INTERRUPT_SAFETY 0
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef DIGITALIO_NO_MIX_ANALOGWRITE
|
|
||||||
#define DIGITALIO_NO_MIX_ANALOGWRITE 1
|
|
||||||
#else
|
|
||||||
#define DIGITALIO_NO_MIX_ANALOGWRITE 0
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// All the variables & conditionals in these functions should evaluate at
|
|
||||||
// compile time not run time...
|
|
||||||
|
|
||||||
__attribute__((always_inline))
|
|
||||||
static inline void pinModeSafe(uint8_t pin, uint8_t mode) {
|
|
||||||
if(!__builtin_constant_p(pin)) {
|
|
||||||
pinMode(pin, mode);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
if((mode == INPUT || mode == INPUT_PULLUP) && !DIGITALIO_NO_MIX_ANALOGWRITE)
|
|
||||||
noAnalogWrite(pin);
|
|
||||||
|
|
||||||
const bool write_is_atomic = DIGITALIO_NO_INTERRUPT_SAFETY
|
|
||||||
|| (__builtin_constant_p(mode)
|
|
||||||
&& mode == OUTPUT
|
|
||||||
&& _directionIsAtomic(pin));
|
|
||||||
if(write_is_atomic) {
|
|
||||||
pinModeFast(pin, mode);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
ATOMIC_BLOCK(ATOMIC_RESTORESTATE)
|
|
||||||
{
|
|
||||||
pinModeFast(pin, mode);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
__attribute__((always_inline))
|
|
||||||
static inline void digitalWriteSafe(uint8_t pin, uint8_t value) {
|
|
||||||
if(!__builtin_constant_p(pin)) {
|
|
||||||
digitalWrite(pin, value);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
if(!DIGITALIO_NO_MIX_ANALOGWRITE)
|
|
||||||
noAnalogWrite(pin);
|
|
||||||
|
|
||||||
if(DIGITALIO_NO_INTERRUPT_SAFETY || _outputIsAtomic(pin)) {
|
|
||||||
digitalWriteFast(pin, value);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
ATOMIC_BLOCK(ATOMIC_RESTORESTATE)
|
|
||||||
{
|
|
||||||
digitalWriteFast(pin, value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
__attribute__((always_inline))
|
|
||||||
static inline int digitalReadSafe(uint8_t pin) {
|
|
||||||
if(!__builtin_constant_p(pin)) {
|
|
||||||
return digitalRead(pin);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
if(!DIGITALIO_NO_MIX_ANALOGWRITE)
|
|
||||||
noAnalogWrite(pin);
|
|
||||||
return digitalReadFast(pin);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -1,309 +0,0 @@
|
||||||
#!/usr/bin/env python
|
|
||||||
"""
|
|
||||||
Code generation script to build digitalIOPerformance.h from the Arduino
|
|
||||||
boards.txt & pins_arduino.h files.
|
|
||||||
|
|
||||||
You shouldn't need to rerun this unless you have a new version of
|
|
||||||
Arduino installed, or you're using different boards to the default
|
|
||||||
installation.
|
|
||||||
|
|
||||||
See the README file for more details.
|
|
||||||
|
|
||||||
Copyright (c) 2013 Angus Gratton. Script licensed under the GNU Lesser
|
|
||||||
General Public License, version 2 or above.
|
|
||||||
|
|
||||||
"""
|
|
||||||
|
|
||||||
# TODO: command line options
|
|
||||||
ARDUINO_PATH="/Applications/Arduino.app/Contents/Resources/Java/"
|
|
||||||
|
|
||||||
import os, re, subprocess
|
|
||||||
|
|
||||||
def main():
|
|
||||||
boards = extract_boards()
|
|
||||||
for board in boards:
|
|
||||||
add_variant_macros(board)
|
|
||||||
find_unambiguous_macros(boards)
|
|
||||||
identifying_keys = find_unique_macro_keys(boards)
|
|
||||||
boards = merge_matching_boards(boards, identifying_keys)
|
|
||||||
for board in boards:
|
|
||||||
extract_portnames_pins(board)
|
|
||||||
with open("digitalIOPerformance.h", "w") as output:
|
|
||||||
generate_header_file(boards, identifying_keys, output)
|
|
||||||
|
|
||||||
def extract_boards():
|
|
||||||
""" Parse the Arduino boards file and return a list of all the boards,
|
|
||||||
with each board as a dictionary containing all their board-specific
|
|
||||||
key-value pairs
|
|
||||||
"""
|
|
||||||
with open(os.path.join(ARDUINO_PATH, "hardware/arduino/boards.txt")) as board_contents:
|
|
||||||
boards = dict()
|
|
||||||
|
|
||||||
RE_BOARDENTRY = re.compile(r"^([A-Za-z][^=]+)=(.+)$", re.MULTILINE)
|
|
||||||
|
|
||||||
for full_key,value in re.findall(RE_BOARDENTRY, board_contents.read()):
|
|
||||||
board = full_key.split(".")[0]
|
|
||||||
key = ".".join(full_key.split(".")[1:])
|
|
||||||
if not board in boards:
|
|
||||||
boards[board] = { "id" : board }
|
|
||||||
boards[board][key] = value
|
|
||||||
|
|
||||||
return list(boards.values())
|
|
||||||
|
|
||||||
def run_preprocessor(board, additional_args=[]):
|
|
||||||
"""
|
|
||||||
Run the C preprocessor over a particular board, and return
|
|
||||||
the contents of the processed file as a string
|
|
||||||
"""
|
|
||||||
source_path = board["pin_path"]
|
|
||||||
args = ["/Applications/Arduino.app/Contents/Resources/Java/hardware/tools/avr/bin/avr-gcc", "-DARDUINO_MAIN", "-E",
|
|
||||||
"-mmcu=%s" % board["build.mcu"],
|
|
||||||
"-DF_CPU=%s" % board["build.f_cpu"] ]
|
|
||||||
if "build.vid" in board:
|
|
||||||
args += [ "-DUSB_VID=%s" % board["build.vid" ] ]
|
|
||||||
if "build.pid" in board:
|
|
||||||
args += [ "-DUSB_PID=%s" % board["build.pid" ] ]
|
|
||||||
print args + additional_args + [ source_path ]
|
|
||||||
proc = subprocess.Popen(args + additional_args + [ source_path ],stdout=subprocess.PIPE)
|
|
||||||
proc.wait()
|
|
||||||
if proc.returncode != 0:
|
|
||||||
raise Error("Failed to run preprocessor")
|
|
||||||
return proc.stdout.read()
|
|
||||||
|
|
||||||
def add_variant_macros(board):
|
|
||||||
"""
|
|
||||||
Run the pin_arduinos.h header for this board through the preprocessor,
|
|
||||||
extract all defined macros and attach them to the board dict under key
|
|
||||||
'macros'
|
|
||||||
"""
|
|
||||||
print os.path.join(ARDUINO_PATH, "hardware/arduino/variants/%s/pins_arduino.h" % (board["build.variant"]))
|
|
||||||
board["pin_path"] = os.path.join(ARDUINO_PATH, "hardware/arduino/variants/%s/pins_arduino.h" % (board["build.variant"]))
|
|
||||||
macros = run_preprocessor(board, ["-dM"])
|
|
||||||
macros = [ re.sub(r"^#define ", "", macro) for macro in macros.split("\n") ]
|
|
||||||
macros = [ tuple(macro.split(" ", 1)) for macro in macros if " " in macro ]
|
|
||||||
board["macros"] = dict(macros)
|
|
||||||
|
|
||||||
def find_unambiguous_macros(boards):
|
|
||||||
"""
|
|
||||||
Trim the macros defined against any of the boards, to leave only those with
|
|
||||||
only numeric values (anything else is too tricky, especially with
|
|
||||||
token representation ambiguities.)
|
|
||||||
|
|
||||||
Modifies the board dict in-place.
|
|
||||||
"""
|
|
||||||
ambiguous_macros = set()
|
|
||||||
# build list of ambiguous macros
|
|
||||||
for board in boards:
|
|
||||||
for key,value in board["macros"].items():
|
|
||||||
if not re.match(r"^-?0?x?[\dA-Fa-f]+L?$", value.strip()):
|
|
||||||
ambiguous_macros.add(key)
|
|
||||||
# trim the ambiguous macros from any of the boards
|
|
||||||
for board in boards:
|
|
||||||
for ambiguous in ambiguous_macros:
|
|
||||||
if ambiguous in board["macros"]:
|
|
||||||
del board["macros"][ambiguous]
|
|
||||||
|
|
||||||
|
|
||||||
def find_unique_macro_keys(boards):
|
|
||||||
"""
|
|
||||||
Go through each board and find a small subset of unique macro
|
|
||||||
values that distinguish each board from the others.
|
|
||||||
|
|
||||||
This allows us to generate a header file that can automatically
|
|
||||||
determine which board profile it's being compiled against, at compile
|
|
||||||
time.
|
|
||||||
|
|
||||||
Returns a set of macro names.
|
|
||||||
"""
|
|
||||||
identifying_keys = set()
|
|
||||||
for board in boards:
|
|
||||||
duplicates = list( dup for dup in boards if dup != board )
|
|
||||||
for dup in duplicates:
|
|
||||||
# skip anything that's already unique as per existing keys
|
|
||||||
identified = False
|
|
||||||
for key in identifying_keys:
|
|
||||||
identified = identified or board["macros"].get(key,"") != dup["macros"].get(key, "")
|
|
||||||
if identified:
|
|
||||||
continue
|
|
||||||
|
|
||||||
# find something unique about this duplicate
|
|
||||||
uniques = set(board["macros"].items()) ^ set(dup["macros"].items())
|
|
||||||
uniques = [ key for key,value in uniques ]
|
|
||||||
# find the least selective key in the uniques
|
|
||||||
def selectiveness(key):
|
|
||||||
return len([d for d in duplicates if ( d["macros"].get(key, "") == board["macros"].get(key, ""))])
|
|
||||||
uniques = sorted(uniques, key=selectiveness)
|
|
||||||
if len(uniques) > 0:
|
|
||||||
identifying_keys.add(uniques[0])
|
|
||||||
|
|
||||||
return identifying_keys
|
|
||||||
|
|
||||||
def merge_matching_boards(boards, identifying_keys):
|
|
||||||
"""
|
|
||||||
Go through each board and merge together any that we can't unambiguously
|
|
||||||
identify by using the macros defined in identifying_keys.
|
|
||||||
|
|
||||||
We assume any matching boards will have matching 'variants' defined, otherwise
|
|
||||||
we throw an error.
|
|
||||||
|
|
||||||
Returns a new list of boards, with an ambiguously defined ones merged together.
|
|
||||||
"""
|
|
||||||
def key(board):
|
|
||||||
return str([ board["macros"].get(key,"") for key in identifying_keys ])
|
|
||||||
|
|
||||||
# Merge together any boards with identical keys (making composite names & ids)
|
|
||||||
unique_boards = []
|
|
||||||
for board in boards:
|
|
||||||
found = False
|
|
||||||
for unique in unique_boards:
|
|
||||||
if key(unique) == key(board):
|
|
||||||
if board["build.variant"] != unique["build.variant"]:
|
|
||||||
raise RuntimeError("Ambiguous boards %s / %s have matching variant! Can't distinguish reliably between them." % (board["id"], unique["id"]))
|
|
||||||
unique["id"] += " | " + board["id"]
|
|
||||||
unique["name"] += " | " + board["name"]
|
|
||||||
found = True
|
|
||||||
if not found:
|
|
||||||
unique_boards += [ board ]
|
|
||||||
return unique_boards
|
|
||||||
|
|
||||||
|
|
||||||
def extract_portnames_pins(board):
|
|
||||||
"""
|
|
||||||
Run the preprocessor over this boards' pin header file to pull
|
|
||||||
out port names and bitmasks.
|
|
||||||
"""
|
|
||||||
def extract_array(array_name):
|
|
||||||
""" Match a multiline C array with the given name (pretty clumsy.) """
|
|
||||||
content = re.search(array_name + ".+?\{(.+?)}", output, re.MULTILINE|re.DOTALL).group(1)
|
|
||||||
return [ e.strip() for e in content.split(",") if len(e.strip()) ]
|
|
||||||
|
|
||||||
output = run_preprocessor(board)
|
|
||||||
board["ports"] = extract_array("digital_pin_to_port_PGM")
|
|
||||||
# strip P prefix, ie PD becomes D
|
|
||||||
board["ports"] = [ e[1] if e[0] == "P" else None for e in board["ports"] ]
|
|
||||||
|
|
||||||
board["bitmasks"] = extract_array("digital_pin_to_bit_mask_PGM")
|
|
||||||
|
|
||||||
pin_to_timer = extract_array("digital_pin_to_timer_PGM")
|
|
||||||
board["timers"] = [ p if p != "NOT_ON_TIMER" else None for p in pin_to_timer ]
|
|
||||||
|
|
||||||
# do some sanity checks on the data we extracted
|
|
||||||
if len(board["ports"]) != len(board["bitmasks"]):
|
|
||||||
raise RuntimeError("Number of ports in %s doesn't match bitmask count - %d vs %d" % (board["id"], len(board["ports"]), len(board["bitmasks"])))
|
|
||||||
if len(board["ports"]) != int(board["macros"]["NUM_DIGITAL_PINS"]):
|
|
||||||
raise RuntimeError("Number of ports in %s doesn't match number of digital pins reported in header" % (board["id"], len(board["ports"]), board["macros"]["NUM_DIGITAL_PINS"]))
|
|
||||||
|
|
||||||
DIGITALWRITE_TEMPLATE = """
|
|
||||||
else if(pin == %(number)s && value) PORT%(port)s |= %(bitmask)s;
|
|
||||||
else if(pin == %(number)s && !value) PORT%(port)s &= ~%(bitmask)s;
|
|
||||||
""".lstrip("\n")
|
|
||||||
|
|
||||||
PINMODE_TEMPLATE = """
|
|
||||||
else if(pin == %(number)s && mode == INPUT) {
|
|
||||||
DDR%(port)s &= ~%(bitmask)s;
|
|
||||||
PORT%(port)s &= ~%(bitmask)s;
|
|
||||||
} else if(pin == %(number)s && mode == INPUT_PULLUP) {
|
|
||||||
DDR%(port)s &= ~%(bitmask)s;
|
|
||||||
PORT%(port)s |= %(bitmask)s;
|
|
||||||
} else if(pin == %(number)s) DDR%(port)s |= %(bitmask)s;
|
|
||||||
""".lstrip("\n")
|
|
||||||
|
|
||||||
DIGITALREAD_TEMPLATE = """
|
|
||||||
else if(pin == %(number)s) return PIN%(port)s & %(bitmask)s ? HIGH : LOW;
|
|
||||||
""".lstrip("\n")
|
|
||||||
|
|
||||||
TIMER_TEMPLATE = """
|
|
||||||
else if(pin == %(number)s) %(timer_reg)s &= ~_BV(%(timer_bit)s);
|
|
||||||
""".lstrip("\n")
|
|
||||||
|
|
||||||
ISPWM_TEMPLATE = """
|
|
||||||
if(pin == %(number)s)
|
|
||||||
return true;
|
|
||||||
""".lstrip("\n")
|
|
||||||
|
|
||||||
PORT_TEST_TEMPLATE = """
|
|
||||||
if(pin == %(number)s)
|
|
||||||
return _SFR_IO_REG_P(%(port)s);
|
|
||||||
""".lstrip("\n")
|
|
||||||
|
|
||||||
# Lookup table from the timer specifications given in pins_arduino.h
|
|
||||||
# to the actual timer register bits
|
|
||||||
#
|
|
||||||
# This is equivalent of the case statement in wiring_digital.c, so if that
|
|
||||||
# ever changes this needs to change too. :(
|
|
||||||
TIMER_LOOKUP = {
|
|
||||||
"TIMER1A": ("TCCR1A", "COM1A1"),
|
|
||||||
"TIMER1B": ("TCCR1A", "COM1B1"),
|
|
||||||
"TIMER2": ("TCCR2", "COM21"),
|
|
||||||
"TIMER0A": ("TCCR0A", "COM0A1"),
|
|
||||||
"TIMER0B": ("TCCR0A", "COM0B1"),
|
|
||||||
"TIMER2A": ("TCCR2A", "COM2A1"),
|
|
||||||
"TIMER2B": ("TCCR2A", "COM2B1"),
|
|
||||||
"TIMER3A": ("TCCR3A", "COM3A1"),
|
|
||||||
"TIMER3B": ("TCCR3A", "COM3B1"),
|
|
||||||
"TIMER3C": ("TCCR3A", "COM3C1"),
|
|
||||||
"TIMER4A": ("TCCR4A", "COM4A1"),
|
|
||||||
"TIMER4B": ("TCCR4A", "COM4B1"),
|
|
||||||
"TIMER4C": ("TCCR4A", "COM4C1"),
|
|
||||||
"TIMER4D": ("TCCR4C", "COM4D1"),
|
|
||||||
"TIMER5A": ("TCCR5A", "COM5A1"),
|
|
||||||
"TIMER5B": ("TCCR5A", "COM5B1"),
|
|
||||||
"TIMER5C": ("TCCR5A", "COM5C1"),
|
|
||||||
}
|
|
||||||
|
|
||||||
def generate_header_file(boards, identifying_keys, output):
|
|
||||||
"""
|
|
||||||
Write a header file with fast inlined methods for all the board types
|
|
||||||
"""
|
|
||||||
with open("components/board_template.cpp") as template:
|
|
||||||
BOARD_TEMPLATE = template.read()
|
|
||||||
with open("components/header.cpp") as header:
|
|
||||||
output.write(header.read())
|
|
||||||
for board in boards:
|
|
||||||
# Work out the macro conditional
|
|
||||||
ifdef_clause = []
|
|
||||||
for key in identifying_keys:
|
|
||||||
# all the +0 nonsense here is to detect defined-but-empty macros
|
|
||||||
# (Arduino-mk inserts them)
|
|
||||||
if key in board["macros"]:
|
|
||||||
ifdef_clause.append("defined(%(key)s) && (%(key)s+0) == %(value)s" %
|
|
||||||
{"key": key, "value": board["macros"][key]})
|
|
||||||
else:
|
|
||||||
ifdef_clause.append("(!defined(%s) || !(%s+0))" % (key,key))
|
|
||||||
ifdef_clause = " && ".join(ifdef_clause)
|
|
||||||
|
|
||||||
# Build up the macro conditionals
|
|
||||||
digitalwrite_clause = ""
|
|
||||||
digitalread_clause = ""
|
|
||||||
pinmode_clause = ""
|
|
||||||
timer_clause = ""
|
|
||||||
ispwm_clause = ""
|
|
||||||
direction_clause = ""
|
|
||||||
output_clause = ""
|
|
||||||
input_clause = ""
|
|
||||||
for number,port,bitmask,timer in zip(range(len(board["ports"])),
|
|
||||||
board["ports"],
|
|
||||||
board["bitmasks"],
|
|
||||||
board["timers"]):
|
|
||||||
if port is not None:
|
|
||||||
digitalwrite_clause += DIGITALWRITE_TEMPLATE % locals()
|
|
||||||
digitalread_clause += DIGITALREAD_TEMPLATE % locals()
|
|
||||||
pinmode_clause += PINMODE_TEMPLATE % locals()
|
|
||||||
if timer is not None:
|
|
||||||
timer_reg,timer_bit = TIMER_LOOKUP[timer]
|
|
||||||
timer_clause += TIMER_TEMPLATE % locals()
|
|
||||||
ispwm_clause += ISPWM_TEMPLATE % locals()
|
|
||||||
direction_clause += PORT_TEST_TEMPLATE % {"number":number,
|
|
||||||
"port":"DDR"+port}
|
|
||||||
output_clause += PORT_TEST_TEMPLATE % {"number":number,
|
|
||||||
"port":"PORT"+port}
|
|
||||||
input_clause += PORT_TEST_TEMPLATE % {"number":number,
|
|
||||||
"port":"PIN"+port}
|
|
||||||
|
|
||||||
output.write(BOARD_TEMPLATE % dict(locals(), **board))
|
|
||||||
with open("components/footer.cpp") as footer:
|
|
||||||
output.write(footer.read())
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
main()
|
|
Loading…
Reference in New Issue