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