diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..d5cf463
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,31 @@
+# Precompiled python modules
+*.pyc
+
+# Eagle backups
+*.s#?
+*.b#?
+
+# Gerber files
+*.GBL
+*.GBO
+*.GBS
+*.GML
+*.GTL
+*.GTO
+*.GTP
+*.GTS
+*.TXT
+*.dri
+*.gpi
+*.pro
+
+# LaTeX compiled file
+*.pdf
+*.log
+*.aux
+
+# Build directory
+build/
+
+# OS X crap
+.DS_Stor?
diff --git a/README b/README
new file mode 100644
index 0000000..642fd0b
--- /dev/null
+++ b/README
@@ -0,0 +1 @@
+AVRil, a C++ template library for AVR microcontrollers.
\ No newline at end of file
diff --git a/__init__.py b/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/avrlib/README b/avrlib/README
new file mode 100644
index 0000000..a046177
--- /dev/null
+++ b/avrlib/README
@@ -0,0 +1,3 @@
+Hal stands for "Hardware access library". It provides an abstraction layer on top of all the features offered by the ATMega644p, with a focus on efficiency (speed and code size) and non-blocking operation.
+
+Naive examples based on the classes in this directory are given in avrlib/examples
\ No newline at end of file
diff --git a/avrlib/__init__.py b/avrlib/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/avrlib/adc.cc b/avrlib/adc.cc
new file mode 100755
index 0000000..1dbf1ca
--- /dev/null
+++ b/avrlib/adc.cc
@@ -0,0 +1,36 @@
+// Copyright 2009 Olivier Gillet.
+//
+// Author: Olivier Gillet (ol.gillet@gmail.com)
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+// This program 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 General Public License for more details.
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see .
+//
+// -----------------------------------------------------------------------------
+//
+// Interface to the onboard ADC converter, and analog multiplexer.
+
+#include "avrlib/adc.h"
+
+namespace avrlib {
+
+/* static */
+uint8_t Adc::admux_value_ = ADC_DEFAULT << 6;
+
+/* static */
+uint8_t AdcInputScanner::current_pin_;
+
+/* static */
+uint8_t AdcInputScanner::num_inputs_;
+
+/* static */
+int16_t AdcInputScanner::state_[8];
+
+} // namespace avrlib
diff --git a/avrlib/adc.h b/avrlib/adc.h
new file mode 100755
index 0000000..1a4c457
--- /dev/null
+++ b/avrlib/adc.h
@@ -0,0 +1,192 @@
+// Copyright 2009 Olivier Gillet.
+//
+// Author: Olivier Gillet (ol.gillet@gmail.com)
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+// This program 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 General Public License for more details.
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see .
+//
+// -----------------------------------------------------------------------------
+//
+// Interface to the onboard ADC converter, and analog multiplexer.
+
+#ifndef AVRLIB_ADC_H_
+#define AVRLIB_ADC_H_
+
+#include
+
+#include "avrlib/avrlib.h"
+
+namespace avrlib {
+
+enum AdcReference {
+ ADC_EXTERNAL = 0,
+ ADC_DEFAULT = 1,
+ ADC_INTERNAL = 3
+};
+
+enum AdcAlignment {
+ ADC_RIGHT_ALIGNED = 0,
+ ADC_LEFT_ALIGNED = 1
+};
+
+IORegister(ADCSRA);
+
+typedef BitInRegister AdcConvert;
+typedef BitInRegister AdcEnabled;
+
+class Adc {
+ public:
+ Adc() { }
+
+ static inline void Init() {
+ set_prescaler(7); // 128 -> 156kHz ADC clock.
+ Enable();
+ }
+ static inline void set_prescaler(uint8_t factor) {
+ ADCSRA = (ADCSRA & ~0x07) | (factor & 0x07);
+ }
+ static inline void set_reference(AdcReference reference) {
+ admux_value_ = (admux_value_ & 0x3f) | (reference << 6);
+ }
+ static inline void set_alignemnt(AdcAlignment alignment) {
+ admux_value_ &= 0xc0;
+ if (alignment == ADC_LEFT_ALIGNED) {
+ admux_value_ |= 0x20;
+ }
+ }
+ static inline void Enable() {
+ AdcEnabled::set();
+ }
+ static inline void Disable() {
+ AdcEnabled::clear();
+ }
+ static inline int16_t Read(uint8_t pin) {
+ StartConversion(pin);
+ Wait();
+ return ReadOut();
+ }
+
+ static inline void StartConversion(uint8_t pin) {
+ ADMUX = admux_value_ | (pin & 0x07);
+ AdcConvert::set();
+ }
+ static inline void Wait() {
+ while (AdcConvert::value());
+ }
+ static inline int16_t ReadOut() {
+ uint8_t low = ADCL;
+ uint8_t high = ADCH;
+ return (high << 8) | low;
+ }
+ // Only works when the ADC is left aligned
+ static inline uint8_t ReadOut8() {
+ return ADCH;
+ }
+
+ private:
+ static uint8_t admux_value_;
+
+ DISALLOW_COPY_AND_ASSIGN(Adc);
+};
+
+// Class that cycles through all the analog pins and read their value. Compared
+// to Adc::Read(), this class is wait-free as it doesn't block on the
+// ADSC bit value.
+class AdcInputScanner {
+ public:
+ enum {
+ buffer_size = 0,
+ data_size = 16,
+ };
+
+ AdcInputScanner() { }
+
+ static void Init() {
+ Adc::Init();
+ current_pin_ = 0;
+ Adc::StartConversion(0);
+ }
+
+ static inline void set_num_inputs(uint8_t num_inputs) {
+ num_inputs_ = num_inputs;
+ }
+
+ static inline int16_t Read(uint8_t pin) {
+ return state_[pin];
+ }
+
+ static uint8_t current_pin() {
+ return current_pin_;
+ }
+
+ static int16_t Sync(uint8_t pin) {
+ Adc::Wait();
+ int16_t value = Adc::Read(pin);
+ Adc::StartConversion(current_pin_);
+ return value;
+ }
+
+ static void Scan() {
+ Adc::Wait();
+ state_[current_pin_] = Adc::ReadOut();
+ ++current_pin_;
+ if (current_pin_ >= num_inputs_) {
+ current_pin_ = 0;
+ }
+ Adc::StartConversion(current_pin_);
+ }
+
+ private:
+ static uint8_t current_pin_;
+ static uint8_t num_inputs_;
+ static int16_t state_[8];
+
+ DISALLOW_COPY_AND_ASSIGN(AdcInputScanner);
+};
+
+template
+struct AnalogInput {
+ enum {
+ buffer_size = 0,
+ data_size = 16,
+ };
+ static void Init() { }
+ static int16_t Read() {
+ return Adc::Read(pin);
+ }
+};
+
+template
+class MuxedAnalogInput {
+ public:
+ enum {
+ buffer_size = 0,
+ data_size = 16,
+ };
+ static void Init() { }
+ static int16_t Read() {
+ return Adc::Read(current_pin_);
+ }
+ static void set_pin(uint8_t current_pin) {
+ current_pin_ = current_pin;
+ }
+
+ private:
+ static uint8_t current_pin_;
+};
+
+/* static */
+template
+uint8_t MuxedAnalogInput::current_pin_ = 0;
+
+} // namespace avrlib
+
+#endif // AVRLIB_ADC_H_
diff --git a/avrlib/audio_output.h b/avrlib/audio_output.h
new file mode 100755
index 0000000..7303eb2
--- /dev/null
+++ b/avrlib/audio_output.h
@@ -0,0 +1,98 @@
+// Copyright 2009 Olivier Gillet.
+//
+// Author: Olivier Gillet (ol.gillet@gmail.com)
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+// This program 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 General Public License for more details.
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see .
+//
+// -----------------------------------------------------------------------------
+//
+// Audio output. Supports PWM (through a PwmOutput object) and DAC (through a
+// Dac object, for example the one defined in mcp492x.h).
+
+#ifndef AVRLIB_AUDIO_OUTPUT_H_
+#define AVRLIB_AUDIO_OUTPUT_H_
+
+#include "avrlib/base.h"
+#include "avrlib/avrlib.h"
+#include "avrlib/ring_buffer.h"
+
+namespace avrlib {
+
+enum UnderrunPolicy {
+ EMIT_CLICK = 0,
+ HOLD_SAMPLE = 1
+};
+
+template
+class AudioOutput {
+ public:
+ AudioOutput() { }
+ enum {
+ buffer_size = buffer_size_,
+ data_size = OutputPort::data_size,
+ };
+ typedef AudioOutput Me;
+ typedef typename DataTypeForSize::Type Value;
+ typedef RingBuffer OutputBuffer;
+
+ static inline void Init() {
+ OutputPort::Init();
+ }
+
+ static inline void Write(Value v) { while (!writable()); Overwrite(v); }
+ static inline void Overwrite(Value v) { OutputBuffer::Overwrite(v); }
+
+ static inline uint8_t writable() { return OutputBuffer::writable(); }
+ static inline uint8_t writable_block() {
+ return OutputBuffer::writable() >= block_size;
+ }
+ static inline uint8_t NonBlockingWrite(Value v) {
+ if (!writable()) {
+ return 0;
+ }
+ Overwrite(v);
+ return 1;
+ }
+
+ // Called from data emission interrupt.
+ static inline void EmitSample() {
+ if (OutputBuffer::readable()) {
+ OutputPort::Write(Value(OutputBuffer::ImmediateRead()));
+ } else {
+ ++num_glitches_;
+ if (underrun_policy == EMIT_CLICK) {
+ // Introduces clicks to allow underruns to be easily detected.
+ OutputPort::Write(0);
+ }
+ }
+ }
+ static inline uint8_t num_glitches() { return num_glitches_; }
+ static inline void ResetGlitchCounter() { num_glitches_ = 0; }
+
+ private:
+ static uint8_t num_glitches_;
+
+ DISALLOW_COPY_AND_ASSIGN(AudioOutput);
+};
+
+/* static */
+template
+uint8_t AudioOutput::num_glitches_ = 0;
+
+} // namespace avrlib
+
+#endif // AVRLIB_AUDIO_OUTPUT_H_
diff --git a/avrlib/avrlib.h b/avrlib/avrlib.h
new file mode 100755
index 0000000..079ce09
--- /dev/null
+++ b/avrlib/avrlib.h
@@ -0,0 +1,164 @@
+// Copyright 2009 Olivier Gillet.
+//
+// Author: Olivier Gillet (ol.gillet@gmail.com)
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+// This program 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 General Public License for more details.
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see .
+//
+// -----------------------------------------------------------------------------
+//
+// Important: All buffer sizes are expected to be less than 256! (fit in 8
+// bits), and must be powers of 2.
+
+#ifndef AVRLIB_AVRLIB_H_
+#define AVRLIB_AVRLIB_H_
+
+#include
+
+#include "avrlib/base.h"
+#include "avrlib/size_to_type.h"
+
+namespace avrlib {
+
+enum DataOrder {
+ MSB_FIRST = 0,
+ LSB_FIRST = 1
+};
+
+enum DigitalValue {
+ LOW = 0,
+ HIGH = 1
+};
+
+static const uint16_t kInternalEepromSize = 2048;
+static const uint16_t kDefaultExternalEepromSize = 8192;
+
+// is full of useful defines, but they cannot be used as template
+// arguments because they are of the form: (*(volatile uint8_t *)(0x80))
+// The following define wraps this reference into a class to make it easier to
+// pass it as a template argument.
+#define IORegister(reg) struct reg##Register { \
+ static volatile uint8_t* ptr() { return ® } \
+ reg##Register& operator=(const uint8_t& value) { *ptr() = value; } \
+ uint8_t operator()(const uint8_t& value) { return *ptr(); } \
+};
+
+#define SpecialFunctionRegister(reg) struct reg##Register { \
+ static volatile uint8_t* ptr() { return &_SFR_BYTE(reg); } \
+ reg##Register& operator=(const uint8_t& value) { *ptr() = value; } \
+ uint8_t operator()(const uint8_t& value) { return *ptr(); } \
+};
+
+// Represents a bit in an i/o port register.
+template
+struct BitInRegister {
+ static void clear() {
+ *Register::ptr() &= ~_BV(bit);
+ }
+ static void set() {
+ *Register::ptr() |= _BV(bit);
+ }
+ static uint8_t value() {
+ return *Register::ptr() & _BV(bit) ? 1 : 0;
+ }
+};
+
+// These classes implement/define the basic input/output interface. The default
+// implementation is that of an infinite stream of incoming/outgoing 0s.
+struct Input {
+ enum {
+ buffer_size = 0, // Recommended buffer size, when using buffered input.
+ data_size = 0, // 0 for disabled port, 1 for digital, 8 for byte.
+ };
+ typedef uint8_t Value;
+
+ // Blocking!
+ static inline Value Read() { while (!readable()); return ImmediateRead(); }
+
+ // Number of bytes available for read.
+ static inline uint8_t readable() { return 1; }
+
+ // A byte, or -1 if reading failed.
+ static inline int16_t NonBlockingRead() { return readable() ? Read() : -1; }
+
+ // No check for ready state.
+ static inline Value ImmediateRead() { return 0; }
+
+ // Called in data reception interrupt.
+ static inline void Received() { }
+};
+
+struct Output {
+ enum {
+ buffer_size = 0, // Recommended buffer size, when using buffered output.
+ data_size = 0 // 0 for disabled port, 1 for digital, 8 for byte.
+ };
+ typedef uint8_t Value;
+
+ // Blocking!
+ static inline void Write(Value v) { while (!writable()); Overwrite(v); }
+
+ // Number of bytes that can be fed.
+ static inline uint8_t writable() { return 1; }
+
+ // 1 if success.
+ static inline uint8_t NonBlockingWrite(Value v) {
+ if (!writable()) {
+ return 0;
+ }
+ Overwrite(v);
+ return 1;
+ }
+
+ // No check for ready state.
+ static inline void Overwrite(Value) { return; }
+
+ // Called in data emission interrupt.
+ static inline Value Requested() { return 0; }
+};
+
+// An object capable both of input and output, composed from an Input and an
+// Output implementation.
+template
+struct InputOutput {
+ typedef I Input;
+ typedef O Output;
+
+ static inline void Write(typename O::Value v) { O::Write(v); }
+ static inline uint8_t writable() { return O::writable(); }
+ static inline uint8_t NonBlockingWrite(typename O::Value v ) {
+ return O::NonBlockingWrite(v);
+ }
+ static inline void Overwrite(typename O::Value v) { O::Overwrite(v); }
+ static inline typename O::Value Requested() { return O::Requested(); }
+ static inline typename I::Value Read() { return I::Read(); }
+ static inline uint8_t readable() { return I::readable(); }
+ static inline int16_t NonBlockingRead() { return I::NonBlockingRead(); }
+ static inline typename I::Value ImmediateRead() { return I::ImmediateRead(); }
+ static inline void Received() { I::Received(); }
+};
+
+
+// Dummy class that can be passed whenever we expect Input/Output types, and
+// which do not perform any IO.
+typedef Input DisabledInput;
+typedef Output DisabledOutput;
+typedef InputOutput DisabledInputOutput;
+
+enum PortMode {
+ DISABLED = 0,
+ POLLED = 1,
+ BUFFERED = 2
+};
+
+} // namespace avrlib
+
+#endif // AVRLIB_AVRLIB_H_
diff --git a/avrlib/base.h b/avrlib/base.h
new file mode 100755
index 0000000..55c63a3
--- /dev/null
+++ b/avrlib/base.h
@@ -0,0 +1,64 @@
+// Copyright 2009 Olivier Gillet.
+//
+// Author: Olivier Gillet (ol.gillet@gmail.com)
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+// This program 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 General Public License for more details.
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see .
+//
+// -----------------------------------------------------------------------------
+//
+// Base header.
+
+#ifndef AVRLIB_BASE_H_
+#define AVRLIB_BASE_H_
+
+#include
+
+#ifndef NULL
+#define NULL 0
+#endif
+
+typedef union {
+ uint16_t value;
+ uint8_t bytes[2];
+} Word;
+
+typedef union {
+ uint32_t value;
+ uint16_t words[2];
+} LongWord;
+
+struct uint24_t {
+ uint16_t integral;
+ uint8_t fractional;
+};
+
+struct uint24c_t {
+ uint8_t carry;
+ uint16_t integral;
+ uint8_t fractional;
+};
+
+
+#define abs(x) ((x) > 0 ? (x) : -(x))
+
+#define DISALLOW_COPY_AND_ASSIGN(TypeName) \
+ TypeName(const TypeName&); \
+ void operator=(const TypeName&)
+
+template
+inline void StaticAssertImplementation() {
+ char static_assert_size_mismatch[b] = { 0 };
+}
+
+#define STATIC_ASSERT(expression) StaticAssertImplementation<(expression)>()
+
+#endif // AVRLIB_BASE_H_
diff --git a/avrlib/boot.h b/avrlib/boot.h
new file mode 100755
index 0000000..5cb8145
--- /dev/null
+++ b/avrlib/boot.h
@@ -0,0 +1,52 @@
+// Copyright 2009 Olivier Gillet.
+//
+// Author: Olivier Gillet (ol.gillet@gmail.com)
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+// This program 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 General Public License for more details.
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see .
+//
+// -----------------------------------------------------------------------------
+//
+// Basic ATmega initialization.
+
+#ifndef AVRLIB_BOOT_H_
+#define AVRLIB_BOOT_H_
+
+#include "avrlib/adc.h"
+#include "avrlib/avrlib.h"
+#include "avrlib/time.h"
+#include "avrlib/timer.h"
+
+namespace avrlib {
+
+inline void Boot(bool init_timers) {
+ sei();
+
+ if (init_timers) {
+ Timer<1>::set_prescaler(3);
+ Timer<1>::set_mode(TIMER_PWM_PHASE_CORRECT);
+
+ Timer<2>::set_prescaler(3);
+ Timer<2>::set_mode(TIMER_PWM_PHASE_CORRECT);
+ InitClock();
+ }
+
+ // Neuter the UARTs.
+ UCSR0B = 0;
+
+#ifndef ATMEGA328P
+ UCSR1B = 0;
+#endif // ATMEGA328P
+}
+
+} // avr
+
+#endif // AVRLIB_BOOT_H_
diff --git a/avrlib/devices/buffered_display.h b/avrlib/devices/buffered_display.h
new file mode 100644
index 0000000..72c7c31
--- /dev/null
+++ b/avrlib/devices/buffered_display.h
@@ -0,0 +1,200 @@
+// Copyright 2009 Olivier Gillet.
+//
+// Author: Olivier Gillet (ol.gillet@gmail.com)
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+// This program 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 General Public License for more details.
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see .
+//
+// -----------------------------------------------------------------------------
+//
+// In-memory text page with on-demand copy to LCD display.
+//
+// Instead of queuing text drawing instruction in the LCD buffer (BAD), this
+// uses 2 text buffers, one containing the current status of the LCD, and one
+// containing the requested text page. The 2 buffer are compared in a background
+// process and differences are sent to the LCD display. This also manages a
+// software blinking cursor.
+
+#ifndef AVRLIB_DEVICES_BUFFERED_DISPLAY_H_
+#define AVRLIB_DEVICES_BUFFERED_DISPLAY_H_
+
+#include "avrlib/base.h"
+#include "avrlib/log2.h"
+#include "avrlib/time.h"
+
+namespace avrlib {
+
+static const uint8_t kLcdNoCursor = 0xff;
+static const uint8_t kLcdCursor = 0xff;
+static const uint8_t kLcdEditCursor = '_';
+
+template
+class BufferedDisplay {
+ public:
+ enum {
+ width = Lcd::lcd_width,
+ height = Lcd::lcd_height,
+ lcd_buffer_size = width * height,
+ lcd_buffer_size_wrap = width * height - 1
+ };
+
+ BufferedDisplay() { }
+
+ static void Init() {
+ memset(local_, ' ', lcd_buffer_size);
+ memset(remote_, '?', lcd_buffer_size);
+ scan_position_last_write_ = 255;
+ cursor_position_ = 255;
+ blink_ = 0;
+ }
+
+ static void Print(uint8_t line, const char* text) {
+ uint8_t row = width;
+ uint8_t* destination = local_ + (line << Log2::value);
+ while (*text && row) {
+ *destination++ = *text;
+ ++text;
+ --row;
+ }
+ }
+
+ // Use kLcdNoCursor (255) or any other value outside of the screen to hide.
+ static inline void set_cursor_position(uint8_t cursor) {
+ cursor_position_ = cursor;
+ }
+
+ static inline void set_cursor_character(uint8_t character) {
+ cursor_character_ = character;
+ }
+
+ static inline uint8_t cursor_position() {
+ return cursor_position_;
+ }
+
+ static inline void set_status(uint8_t status) {
+ status_ = status + 1;
+ }
+
+ static void Tick() {
+ // The following code is likely to write 4 bytes at most. If there are less
+ // than 4 bytes available for write in the output buffer, there's no reason
+ // to take the risk to continue.
+ if (Lcd::writable() < 4) {
+ return;
+ }
+ // It is now safe to assume that all writes of 4 bytes to the display buffer
+ // will not block.
+
+ if (previous_blink_counter_ > Lcd::blink_counter()) {
+ ++blink_;
+ status_ = 0;
+ }
+ previous_blink_counter_ = Lcd::blink_counter();
+
+ uint8_t character = 0;
+ // Determine which character to show at the current position.
+ // If the scan position is the cursor and it is shown (blinking), draw the
+ // cursor.
+ if (scan_position_ == cursor_position_ && (blink_ & 2)) {
+ character = cursor_character_;
+ } else {
+ // Otherwise, check if there's a status indicator to display. It is
+ // displayed either on the left or right of the first line, depending on
+ // the available space.
+ if (status_ && (scan_position_ == 0 || scan_position_ == (width - 1)) &&
+ local_[scan_position_] == ' ') {
+ character = status_ - 1;
+ } else {
+ character = local_[scan_position_];
+ }
+ }
+ // Check whether the screen really has to be updated to show the character.
+ if (character != remote_[scan_position_]) {
+ // There is a character to transmit!
+ // If the new character to transmit is just after the previous one, and on
+ // the same line, we're good, we don't need to reposition the cursor.
+ if ((scan_position_ == scan_position_last_write_ + 1) &&
+ (scan_position_ & (width - 1))) {
+ // We use overwrite because we have checked before that there is
+ // enough room in the buffer.
+ Lcd::WriteData(character);
+ } else {
+ // The character to transmit is at a different position, we need to move
+ // the cursor, and determine the cursor move command argument.
+ Lcd::MoveCursor(
+ scan_position_ >> Log2::value,
+ scan_position_ & (width - 1));
+ Lcd::WriteData(character);
+ }
+ // We can now assume that the remote display will be updated.
+ remote_[scan_position_] = character;
+ scan_position_last_write_ = scan_position_;
+ }
+ scan_position_ = (scan_position_ + 1) & lcd_buffer_size_wrap;
+ }
+
+ private:
+ // Character pages storing what the display currently shows (remote), and
+ // what it ought to show (local).
+ static uint8_t local_[width * height];
+ static uint8_t remote_[width * height];
+
+ // Position of the last character being transmitted.
+ static uint8_t scan_position_;
+ static uint8_t scan_position_last_write_;
+ static uint8_t blink_;
+ static uint8_t previous_blink_counter_;
+ static uint8_t cursor_position_;
+ static uint8_t cursor_character_;
+ static uint8_t status_;
+
+ DISALLOW_COPY_AND_ASSIGN(BufferedDisplay);
+};
+
+/* static */
+template
+uint8_t BufferedDisplay::local_[width * height];
+
+/* static */
+template
+uint8_t BufferedDisplay::remote_[width * height];
+
+/* static */
+template
+uint8_t BufferedDisplay::scan_position_;
+
+/* static */
+template
+uint8_t BufferedDisplay::scan_position_last_write_;
+
+/* static */
+template
+uint8_t BufferedDisplay::blink_;
+
+/* static */
+template
+uint8_t BufferedDisplay::previous_blink_counter_;
+
+/* static */
+template
+uint8_t BufferedDisplay::cursor_position_;
+
+/* static */
+template
+uint8_t BufferedDisplay::cursor_character_ = kLcdCursor;
+
+/* static */
+template
+uint8_t BufferedDisplay::status_;
+
+} // namespace avrlib
+
+#endif // AVRLIB_DEVICES_BUFFERED_DISPLAY_H_
diff --git a/avrlib/devices/debounce.h b/avrlib/devices/debounce.h
new file mode 100755
index 0000000..25f128e
--- /dev/null
+++ b/avrlib/devices/debounce.h
@@ -0,0 +1,65 @@
+// Copyright 2010 Olivier Gillet.
+//
+// Author: Olivier Gillet (ol.gillet@gmail.com)
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+// This program 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 General Public License for more details.
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see .
+//
+// -----------------------------------------------------------------------------
+//
+// Switch debouncing.
+
+#ifndef AVRLIB_DEVICES_DEBOUNCE_H_
+#define AVRLIB_DEVICES_DEBOUNCE_H_
+
+#include "avrlib/gpio.h"
+
+namespace avrlib {
+
+template
+class DebouncedSwitch {
+ public:
+ DebouncedSwitch() { }
+
+ static inline void Init() {
+ Input::set_mode(DIGITAL_INPUT);
+ if (pulled_up) {
+ Input::High();
+ }
+ state_ = 0xff;
+ }
+
+ // To be called at a rate < 1000 Hz.
+ static inline uint8_t Read() {
+ state_ = (state_ << 1) | Input::value();
+ return state_;
+ }
+
+ static inline uint8_t lowered() { return state_ == 0x80; }
+ static inline uint8_t raised() { return state_ == 0x7f; }
+ static inline uint8_t high() { return state_ == 0xff; }
+ static inline uint8_t low() { return state_ == 0x00; }
+ static inline uint8_t state() { return state_; }
+ static inline uint8_t immediate_value() { return Input::value(); }
+
+ private:
+ static uint8_t state_;
+
+ DISALLOW_COPY_AND_ASSIGN(DebouncedSwitch);
+};
+
+/* static */
+template
+uint8_t DebouncedSwitch::state_;
+
+} // namespace avrlib
+
+#endif // AVRLIB_DEVICES_DEBOUNCE_H_
diff --git a/avrlib/devices/external_eeprom.h b/avrlib/devices/external_eeprom.h
new file mode 100755
index 0000000..2245f3c
--- /dev/null
+++ b/avrlib/devices/external_eeprom.h
@@ -0,0 +1,207 @@
+// Copyright 2009 Olivier Gillet.
+//
+// Author: Olivier Gillet (ol.gillet@gmail.com)
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+// This program 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 General Public License for more details.
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see .
+//
+// -----------------------------------------------------------------------------
+//
+// Driver for Atmel I2C eeproms (AT24C64 for example).
+//
+// Supports an "auto-banking" mode in which the address space is split into
+// multiple chips. For example, if four 16k chips (AT24C128) are connected on the
+// bus, R/W to addresses 0x0000 - 0x4000 will be addressed to chip 1,
+// R/W to addresses 0x4000 - 0x8000 will be addressed to chip 2, etc.
+
+#ifndef AVRLIB_DEVICES_EXTERNAL_EEPROM_H_
+#define AVRLIB_DEVICES_EXTERNAL_EEPROM_H_
+
+#include "avrlib/i2c/i2c.h"
+#include "avrlib/time.h"
+
+namespace avrlib {
+
+template,
+ uint8_t base_address = 0,
+ bool auto_banking = false,
+ uint8_t block_size = 32>
+class ExternalEeprom {
+ public:
+ ExternalEeprom() { }
+
+ static void Init() {
+ Bus::Init();
+ }
+
+ static void Done() {
+ Bus::Done();
+ }
+
+ static void set_bank(uint8_t bank) {
+ bank_ = bank;
+ }
+
+ static inline uint16_t Read(uint16_t size, uint8_t* data) {
+ uint16_t read = 0;
+ while (size != 0) {
+ // Try to read as much as possible from the buffer from the previous op.
+ while (Bus::readable() && size) {
+ --size;
+ ++read;
+ *data++ = Bus::ImmediateRead();
+ }
+ // We need to request more data, but no more than the size of a block.
+ if (size) {
+ Bus::Wait();
+ uint8_t requested = size > block_size ? block_size : size;
+ Bus::Request((base_address + bank_) | 0x50, requested);
+ if (Bus::Wait() != I2C_ERROR_NONE) {
+ return size - read;
+ }
+ }
+ }
+ return read;
+ }
+
+ static inline uint8_t SetAddress(uint16_t address) {
+ uint8_t header[2];
+ if (auto_banking) {
+ bank_ = (address / eeprom_size);
+ address %= eeprom_size;
+ }
+ header[0] = address >> 8;
+ header[1] = address & 0xff;
+ if (Write(header, 2, NULL, 0) == 2) {
+ // Invalidate pending read operations.
+ Bus::FlushInputBuffer();
+ return 1;
+ } else {
+ Bus::FlushOutputBuffer();
+ return 0;
+ }
+ }
+
+ static inline uint16_t Write(
+ uint16_t address,
+ const uint8_t* data,
+ uint16_t size) {
+ uint16_t written = 0;
+ while (size != 0) {
+ uint8_t writable = block_size - (address % block_size);
+ if (writable > size) {
+ writable = size;
+ }
+ if (WriteWithinBlock(address, data, writable) != writable) {
+ break;
+ }
+ Delay(5);
+ written += writable;
+ address += writable;
+ data += writable;
+ size -= writable;
+ }
+ return written;
+ }
+
+ static inline uint8_t WriteWithinBlock(
+ uint16_t address,
+ const uint8_t* data,
+ uint8_t size) {
+ uint8_t header[2];
+ if (auto_banking) {
+ bank_ = (address / eeprom_size);
+ address %= eeprom_size;
+ }
+ header[0] = address >> 8;
+ header[1] = address & 0xff;
+ if (Write(header, 2, data, size) == size + 2) {
+ return size;
+ } else {
+ Bus::FlushOutputBuffer();
+ return 0;
+ }
+ }
+
+ static inline uint8_t Read() {
+ uint8_t data;
+ if (Read(1, &data) == 1) {
+ return data;
+ } else {
+ return 0xff;
+ }
+ }
+
+ static inline uint8_t Read(uint16_t address) {
+ if (!SetAddress(address)) {
+ return 0xff;
+ }
+ return Read();
+ }
+
+ static inline uint8_t Read(uint16_t address, uint8_t size, uint8_t* data) {
+ if (!SetAddress(address)) {
+ return 0;
+ } else {
+ return Read(size, data);
+ }
+ }
+
+ static inline uint8_t Write(uint16_t address, uint8_t byte) {
+ uint8_t data = byte;
+ return Write(&data, 1);
+ }
+
+ private:
+ static uint8_t Write(const uint8_t* header, uint8_t header_size,
+ const uint8_t* payload, uint8_t payload_size) {
+ uint8_t size = header_size + payload_size;
+ if (size >= Bus::Output::capacity()) {
+ return 0; // Hopeless, it won't fit in one write.
+ }
+ // Wait until the buffer is flushed, and write to the buffer.
+ while (Bus::writable() < size) { }
+ for (uint8_t i = 0; i < header_size; ++i) {
+ Bus::Output::Overwrite(header[i]);
+ }
+ for (uint8_t i = 0; i < payload_size; ++i) {
+ Bus::Overwrite(payload[i]);
+ }
+ // Wait until the line is available.
+ Bus::Wait();
+ // Send the data in the buffer.
+ if (Bus::Send((base_address + bank_) | 0x50)) {
+ uint8_t error = Bus::Wait();
+ if (error == I2C_ERROR_NONE) {
+ return size;
+ } else {
+ return 0;
+ }
+ } else {
+ return 0;
+ }
+ }
+
+ static uint8_t bank_;
+
+ DISALLOW_COPY_AND_ASSIGN(ExternalEeprom);
+};
+
+/* static */
+template
+uint8_t ExternalEeprom::bank_ = 0;
+
+} // namespace avrlib
+
+#endif // AVRLIB_DEVICES_EXTERNAL_EEPROM_H_
diff --git a/avrlib/devices/hd44780_lcd.h b/avrlib/devices/hd44780_lcd.h
new file mode 100755
index 0000000..17b1c84
--- /dev/null
+++ b/avrlib/devices/hd44780_lcd.h
@@ -0,0 +1,227 @@
+// Copyright 2009 Olivier Gillet.
+//
+// Author: Olivier Gillet (ol.gillet@gmail.com)
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+// This program 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 General Public License for more details.
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see .
+//
+// -----------------------------------------------------------------------------
+//
+// Driver for a HD44780 LCD display.
+
+#ifndef AVRLIB_DEVICES_HD44780_LCD_H_
+#define AVRLIB_DEVICES_HD44780_LCD_H_
+
+#include "avrlib/base.h"
+#include "avrlib/log2.h"
+#include "avrlib/software_serial.h"
+#include "avrlib/time.h"
+#include "avrlib/resources_manager.h"
+
+using avrlib::SimpleResourcesManager;
+
+namespace avrlib {
+
+enum LCD_FLAGS {
+ LCD_COMMAND = 0x00,
+ LCD_DATA = 0x10,
+
+ LCD_CLEAR = 0x01,
+ LCD_HOME = 0x02,
+ LCD_ENTRY_MODE = 0x04,
+ LCD_DISPLAY_STATUS = 0x08,
+ LCD_CURSOR = 0x10,
+ LCD_FUNCTION_SET = 0x20,
+ LCD_SET_CGRAM_ADDRESS = 0x40,
+ LCD_SET_DDRAM_ADDRESS = 0x80,
+
+ LCD_SHIFT = 0x01,
+ LCD_NO_SHIFT = 0x00,
+ LCD_CURSOR_INCREMENT = 0x02,
+ LCD_CURSOR_NO_INCREMENT = 0x00,
+ LCD_DISPLAY_ON = 0x04,
+ LCD_DISPLAY_OFF = 0x00,
+ LCD_CURSOR_ON = 0x02,
+ LCD_CURSOR_OFF = 0x00,
+ LCD_BLINKING_ON = 0x01,
+ LCD_BLINKING_OFF = 0x00,
+
+ LCD_8_BITS = 0x10,
+ LCD_4_BITS = 0x00,
+
+ LCD_2_LINE = 0x08,
+ LCD_1_LINE = 0x00,
+
+ LCD_LARGE_FONT = 0x04,
+ LCD_SMALL_FONT = 0x00,
+};
+
+template
+class Hd44780Lcd {
+ public:
+ enum {
+ buffer_size = 64,
+ data_size = 8,
+ };
+ enum {
+ lcd_width = width,
+ lcd_height = height,
+ };
+ typedef Hd44780Lcd Me;
+ typedef typename DataTypeForSize::Type Value;
+ typedef RingBuffer OutputBuffer;
+
+ Hd44780Lcd() { }
+
+ static inline void Init() {
+ RsPin::set_mode(DIGITAL_OUTPUT);
+ EnablePin::set_mode(DIGITAL_OUTPUT);
+ ParallelPort::set_mode(DIGITAL_OUTPUT);
+
+ RsPin::Low();
+ EnablePin::Low();
+
+ Delay(100); // Wait for warm up
+
+ // Set to 4 bit operation.
+ for (uint8_t i = 0; i < 3; ++i) {
+ SlowWrite((LCD_FUNCTION_SET | LCD_8_BITS) >> 4);
+ Delay(2);
+ }
+ SlowWrite((LCD_FUNCTION_SET | LCD_4_BITS) >> 4);
+
+ // Set number of lines and bus size.
+ if (height == 2) {
+ SlowCommand(LCD_FUNCTION_SET | LCD_4_BITS | LCD_2_LINE | LCD_SMALL_FONT);
+ } else {
+ SlowCommand(LCD_FUNCTION_SET | LCD_4_BITS | LCD_SMALL_FONT);
+ }
+ SlowCommand(LCD_DISPLAY_STATUS | LCD_DISPLAY_ON | LCD_CURSOR_OFF | LCD_BLINKING_OFF);
+ SlowCommand(LCD_ENTRY_MODE | LCD_CURSOR_INCREMENT | LCD_NO_SHIFT);
+ SlowCommand(LCD_CLEAR);
+ SlowCommand(LCD_HOME);
+ transmitting_ = 0;
+ }
+
+ static inline void Tick() {
+ ++blink_counter_;
+ if (transmitting_) {
+ EndWrite();
+ transmitting_ = 0;
+ } else {
+ if (OutputBuffer::readable()) {
+ transmitting_ = 1;
+ StartWrite(OutputBuffer::ImmediateRead());
+ }
+ }
+ }
+
+ static uint8_t WriteData(uint8_t c) {
+ if (OutputBuffer::writable() < 2) {
+ return 0;
+ }
+ OutputBuffer::Overwrite2(LCD_DATA | (c >> 4), LCD_DATA | (c & 0xf));
+ }
+
+ static uint8_t WriteCommand(uint8_t c) {
+ if (OutputBuffer::writable() < 2) {
+ return 0;
+ }
+ OutputBuffer::Overwrite2(LCD_COMMAND | (c >> 4), LCD_COMMAND | (c & 0x0f));
+ }
+ static inline uint8_t Write(uint8_t character) {
+ WriteData(character);
+ }
+
+ static inline void MoveCursor(uint8_t row, uint8_t col) {
+ WriteCommand(LCD_SET_DDRAM_ADDRESS | col | (row << 6));
+ }
+
+ static inline void SetCustomCharMap(
+ const uint8_t* data,
+ uint8_t num_characters,
+ uint8_t first_character) {
+ SlowCommand(LCD_SET_CGRAM_ADDRESS | (first_character << 3));
+ for (uint8_t i = 0; i < num_characters * 8; ++i) {
+ SlowData(*data++);
+ }
+ }
+
+ static inline void SetCustomCharMapRes(
+ const uint8_t* data,
+ uint8_t num_characters,
+ uint8_t first_character) {
+ SlowCommand(LCD_SET_CGRAM_ADDRESS | (first_character << 3));
+ for (uint8_t i = 0; i < num_characters * 8; ++i) {
+ SlowData(SimpleResourcesManager::Lookup(data, i));
+ }
+ }
+
+ static inline uint8_t writable() { return OutputBuffer::writable(); }
+ static inline uint8_t busy() { return transmitting_; }
+ static inline uint8_t blink_counter() { return blink_counter_; }
+
+ private:
+ static inline void StartWrite(uint8_t nibble) {
+ if (nibble & LCD_DATA) {
+ RsPin::High();
+ }
+ ParallelPort::Write(nibble & 0x0f);
+ EnablePin::High();
+ }
+
+ static inline void EndWrite() {
+ EnablePin::Low();
+ RsPin::Low();
+ }
+
+ static void SlowWrite(uint8_t nibble) {
+ StartWrite(nibble);
+ Delay(1);
+ EndWrite();
+ Delay(3);
+ }
+
+ static void SlowCommand(uint8_t value) {
+ SlowWrite(LCD_COMMAND | (value >> 4));
+ SlowWrite(LCD_COMMAND | (value & 0x0f));
+ }
+
+ static void SlowData(uint8_t value) {
+ SlowWrite(LCD_DATA | (value >> 4));
+ SlowWrite(LCD_DATA | (value & 0x0f));
+ }
+
+ static volatile uint8_t transmitting_;
+ static volatile uint8_t blink_counter_;
+
+ DISALLOW_COPY_AND_ASSIGN(Hd44780Lcd);
+};
+
+/* static */
+template
+volatile uint8_t Hd44780Lcd::transmitting_;
+
+/* static */
+template
+volatile uint8_t Hd44780Lcd::blink_counter_;
+
+} // namespace avrlib
+
+#endif // AVRLIB_DEVICES_HD44780_LCD_H_
diff --git a/avrlib/devices/input_array.h b/avrlib/devices/input_array.h
new file mode 100755
index 0000000..8b41b1c
--- /dev/null
+++ b/avrlib/devices/input_array.h
@@ -0,0 +1,135 @@
+// Copyright 2009 Olivier Gillet.
+//
+// Author: Olivier Gillet (ol.gillet@gmail.com)
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+// This program 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 General Public License for more details.
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see .
+//
+// -----------------------------------------------------------------------------
+//
+// Controller for an array of analog or digital inputs behind a multiplexer
+// controlled by a shift register. Stores an array reflecting the current value
+// of each input. A readout returns an "Event" object with the latest value of
+// the controller, a flag indicating how it changed with respect to the
+// previous value, and a timestamp.
+//
+// When event = EVENT_NONE, the time is the idle time (how many ms since an
+// event occurred).
+// When event != EVENT_NONE, the time is the time spent in the previous state.
+
+#ifndef AVRLIB_DEVICES_INPUT_ARRAY_H_
+#define AVRLIB_DEVICES_INPUT_ARRAY_H_
+
+#include "avrlib/size_to_type.h"
+#include "avrlib/time.h"
+
+namespace avrlib {
+
+enum InputEvent {
+ EVENT_NONE = 0,
+ EVENT_CHANGED = 1,
+};
+
+template
+class InputArray {
+ public:
+ InputArray() { }
+ typedef typename DataTypeForSize::Type T;
+ typedef struct {
+ uint8_t id;
+ uint8_t event; // Could have been InputEvent but I want the extra byte.
+ T value;
+ uint32_t time;
+ } Event;
+ static void Init() {
+ // No need to initialize anything - the first cycle of readouts will take
+ // care of this.
+ active_input_ = 0;
+ starting_up_ = num_inputs * 2;
+ Input::Init();
+ }
+ static void Lock(uint16_t threshold) {
+ for (uint8_t i = 0; i < num_inputs; ++i) {
+ thresholds_[i] = threshold;
+ }
+ }
+ static void Touch() {
+ last_event_time_ = milliseconds();
+ }
+ static Event Read() {
+ Event e;
+ e.id = active_input_;
+
+ // Read a value from the ADC and check if something occurred.
+ e.value = Input::Read(active_input_);
+ uint8_t same;
+ int16_t delta = static_cast(values_[active_input_]) -
+ static_cast(e.value);
+ same = abs(delta) < thresholds_[active_input_];
+ uint32_t now = milliseconds();
+ e.time = now - last_event_time_;
+ if (same) {
+ e.event = EVENT_NONE;
+ } else {
+ // Since the input has been touched and the event has been recorded,
+ // lower the threshold.
+ thresholds_[active_input_] = low_threshold;
+ values_[active_input_] = e.value;
+ last_event_time_ = now;
+ e.event = EVENT_CHANGED;
+ }
+
+ // The next call to Read() will read the next input.
+ ++active_input_;
+ if (active_input_ == num_inputs) {
+ active_input_ = 0;
+ }
+
+ // During the first cycle, do not raise any event - just record the values.
+ if (starting_up_) {
+ --starting_up_;
+ e.event = EVENT_NONE;
+ e.time = 0;
+ }
+ return e;
+ }
+ static uint8_t active_input() { return active_input_; }
+
+ private:
+ static T values_[num_inputs];
+ static T thresholds_[num_inputs];
+ static uint8_t active_input_;
+ static uint8_t starting_up_;
+ static uint32_t last_event_time_;
+
+ DISALLOW_COPY_AND_ASSIGN(InputArray);
+};
+
+template
+typename InputArray::T
+InputArray::values_[num_inputs];
+
+template
+typename InputArray::T
+InputArray::thresholds_[num_inputs];
+
+template
+uint8_t InputArray::active_input_;
+
+template
+uint32_t InputArray::last_event_time_;
+
+template
+uint8_t InputArray::starting_up_;
+
+} // namespace avrlib
+
+#endif // AVRLIB_DEVICES_INPUT_ARRAY_H_
diff --git a/avrlib/devices/mcp492x.h b/avrlib/devices/mcp492x.h
new file mode 100755
index 0000000..c05df4d
--- /dev/null
+++ b/avrlib/devices/mcp492x.h
@@ -0,0 +1,78 @@
+// Copyright 2009 Olivier Gillet.
+//
+// Author: Olivier Gillet (ol.gillet@gmail.com)
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+// This program 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 General Public License for more details.
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see .
+//
+// -----------------------------------------------------------------------------
+//
+// Driver for a MCP492x DAC (SPI single/dual 12-bits DAC).
+
+#ifndef AVRLIB_DEVICES_MCP492X_H_
+#define AVRLIB_DEVICES_MCP492X_H_
+
+#include "avrlib/spi.h"
+#include "avrlib/op.h"
+
+using namespace avrlib;
+
+namespace avrlib {
+
+static const uint8_t kDacSpeed = 2;
+
+enum DacVoltageReference {
+ BUFFERED_REFERENCE,
+ UNBUFFERED_REFERENCE
+};
+
+template
+class Dac {
+ public:
+ enum {
+ buffer_size = 0,
+ data_size = 8,
+ };
+ Dac() { }
+
+ static void Init() {
+ DacInterface::Init();
+ }
+
+ static void Write(uint8_t value) {
+ Write(value, 0);
+ }
+
+ static void Write(uint8_t value, uint8_t channel) {
+ value = Swap4(value);
+ uint8_t command;
+ command = (value & 0x0f) | 0x10;
+ if (channel) {
+ command |= 0x80;
+ }
+ if (voltage_reference == BUFFERED) {
+ command |= 0x40;
+ }
+ if (gain == 1) {
+ command |= 0x20;
+ }
+ DacInterface::WriteWord(command, value & 0xf0);
+ }
+
+ private:
+ typedef Spi DacInterface;
+};
+
+} // namespace avrlib
+
+#endif // AVRLIB_DEVICES_MCP492X_H_
diff --git a/avrlib/devices/output_array.h b/avrlib/devices/output_array.h
new file mode 100755
index 0000000..d6d52c6
--- /dev/null
+++ b/avrlib/devices/output_array.h
@@ -0,0 +1,180 @@
+// Copyright 2009 Olivier Gillet.
+//
+// Author: Olivier Gillet (ol.gillet@gmail.com)
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+// This program 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 General Public License for more details.
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see .
+//
+// -----------------------------------------------------------------------------
+//
+// Controller for an array of digital outputs behind a shift register. This
+// class maintains an internal array reflecting the status of each output. A
+// call to Output() writes the content of the array to a shift register, if the
+// content of the array has changed.
+//
+// The num_intensity_level template parameter allows basic PWM on the output
+// of the array. More specifically, each call to Output() will write a 0 or 1
+// depending on the "analog" level. This can be used for example to adjust
+// the brightness of a LED. More precisely, this is achieved in a PWM-like way
+// (we cycle N times through the array and at time i, we power only the outputs
+// with level above i/N).
+
+#ifndef AVRLIB_DEVICES_OUTPUT_ARRAY_H_
+#define AVRLIB_DEVICES_OUTPUT_ARRAY_H_
+
+#include
+
+#include "avrlib/devices/shift_register.h"
+#include "avrlib/size_to_type.h"
+
+namespace avrlib {
+
+// A generic, not very efficient implementation, especially for small bit
+// depths.
+template
+class OutputArray {
+ public:
+ OutputArray() { }
+ typedef ShiftRegisterOutput Register;
+ typedef typename DataTypeForSize::Type Value;
+ typedef typename DataTypeForSize::Type Index;
+ static inline void Init() {
+ if (safe) {
+ memset(values_, 0, sizeof(values_));
+ cycle_ = 0;
+ }
+ Register::Init();
+ }
+ static inline void set_value(Index output_index, Value intensity) {
+ values_[output_index] = intensity;
+ }
+ static inline Value value(Index output_index) {
+ return values_[output_index];
+ }
+ static inline void ShiftOutByte(uint8_t v) {
+ Register::ShiftOut(v);
+ }
+ static inline void Begin() {
+ Register::Begin();
+ }
+ static inline void End() {
+ Register::End();
+ }
+ static inline void Clear() { memset(values_, 0, sizeof(values_)); }
+
+ static inline void ShiftOut() {
+ Index c = 0;
+ Index bit = 1;
+ for (Index i = 0; i < size; ++i) {
+ if (values_[i] > cycle_ || values_[i] == ((1 << bit_depth) - 1)) {
+ c |= bit;
+ }
+ bit <<= 1;
+ }
+ cycle_ = (cycle_ + 1) & ((1 << bit_depth) - 1);
+ Register::ShiftOut(c);
+ }
+ static inline void Write() {
+ Begin();
+ ShiftOut();
+ End();
+ }
+ private:
+ static Value values_[size];
+ static Value cycle_;
+
+ DISALLOW_COPY_AND_ASSIGN(OutputArray);
+};
+
+template
+typename OutputArray::Value
+OutputArray::values_[size];
+
+template
+typename OutputArray::Value
+OutputArray::cycle_;
+
+
+// A specialization with only two states. In this case, we can write data to the
+// shift register only when a bit has changed in the array - there's no pseudo
+// PWM brightness modulation.
+template
+class OutputArray {
+ typedef ShiftRegisterOutput Register;
+ public:
+ typedef typename DataTypeForSize::Type T;
+ OutputArray() { }
+ static inline void Init() {
+ if (safe) {
+ last_bits_ = 1;
+ bits_ = 0;
+ }
+ Register::Init();
+ }
+ static inline void set_value(uint8_t output_index, uint8_t intensity) {
+ T mask = T(1) << output_index;
+ if (intensity) {
+ bits_ |= mask;
+ } else {
+ bits_ &= ~mask;
+ }
+ }
+ static inline void Clear() { bits_ = 0; }
+ static inline uint8_t value(uint8_t output_index) {
+ T mask = T(1) << output_index;
+ return T(bits_ & mask) ? 1 : 0;
+ }
+ static inline void ShiftOutByte(uint8_t v) {
+ Register::ShiftOut(v);
+ }
+ static inline void Begin() {
+ Register::Begin();
+ }
+ static inline void End() {
+ Register::End();
+ }
+ static inline void ShiftOut() {
+ if (bits_ == last_bits_) {
+ return;
+ }
+ Register::ShiftOut(bits_);
+ last_bits_ = bits_;
+ }
+ static inline void Write() {
+ Begin();
+ ShiftOut();
+ End();
+ }
+ private:
+ static T bits_;
+ static T last_bits_;
+
+ DISALLOW_COPY_AND_ASSIGN(OutputArray);
+};
+
+template
+typename OutputArray::T
+OutputArray::bits_;
+
+template
+typename OutputArray::T
+OutputArray::last_bits_;
+
+} // namespace avrlib
+
+#endif // AVRLIB_DEVICES_OUTPUT_ARRAY_H_
diff --git a/avrlib/devices/rotary_encoder.h b/avrlib/devices/rotary_encoder.h
new file mode 100755
index 0000000..d95762d
--- /dev/null
+++ b/avrlib/devices/rotary_encoder.h
@@ -0,0 +1,124 @@
+// Copyright 2010 Olivier Gillet.
+//
+// Author: Olivier Gillet (ol.gillet@gmail.com)
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+// This program 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 General Public License for more details.
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see .
+//
+// -----------------------------------------------------------------------------
+//
+// Driver for an external rotary encoder.
+
+#ifndef AVRLIB_DEVICES_ROTARY_ENCODER_H_
+#define AVRLIB_DEVICES_ROTARY_ENCODER_H_
+
+#include "avrlib/devices/debounce.h"
+#include "avrlib/gpio.h"
+#include "avrlib/time.h"
+
+namespace avrlib {
+
+template
+class RotaryEncoder {
+ public:
+ typedef DebouncedSwitch SwitchA;
+ typedef DebouncedSwitch SwitchB;
+ typedef DebouncedSwitch SwitchClick;
+
+ RotaryEncoder() { }
+
+ static void Init() {
+ SwitchA::Init();
+ SwitchB::Init();
+ SwitchClick::Init();
+ next_readout_ = 0;
+ }
+
+ static inline int8_t TimedRead() {
+ uint32_t t = milliseconds();
+ int8_t increment = 0;
+ if (t >= next_readout_) {
+ next_readout_ = t + debounce_time;
+ increment = Read();
+ }
+ return increment;
+ }
+
+ static inline int8_t Read() {
+ int8_t increment = 0;
+ uint8_t a = SwitchA::Read();
+ uint8_t b = SwitchB::Read();
+ if (a == 0x80 && ((b & 0xf0) == 0x00)) {
+ increment = 1;
+ } else {
+ if (b == 0x80 && (a & 0xf0) == 0x00) {
+ increment = -1;
+ }
+ }
+ SwitchClick::Read();
+ return increment;
+ }
+
+ static uint8_t clicked() { return SwitchClick::raised(); }
+ static uint8_t immediate_value() { return SwitchClick::immediate_value(); }
+
+ private:
+ static uint32_t next_readout_;
+
+ DISALLOW_COPY_AND_ASSIGN(RotaryEncoder);
+};
+
+template
+class RotaryEncoderBuffer {
+ public:
+ RotaryEncoderBuffer() { }
+
+ static void Init() {
+ Encoder::Init();
+ }
+
+ static inline void Read() {
+ if (!increment_) {
+ increment_ = Encoder::Read();
+ }
+ if (!clicked_) {
+ clicked_ = Encoder::clicked();
+ }
+ }
+
+ static inline uint8_t clicked() { return clicked_; }
+ static inline uint8_t increment() { return increment_; }
+ static inline uint8_t immediate_value() { return Encoder::immediate_value(); }
+ static void Flush() {
+ increment_ = 0;
+ clicked_ = 0;
+ }
+
+ private:
+ static int8_t increment_;
+ static uint8_t clicked_;
+
+ DISALLOW_COPY_AND_ASSIGN(RotaryEncoderBuffer);
+};
+
+/* static */
+template
+uint32_t RotaryEncoder::next_readout_;
+
+/* static */
+template uint8_t RotaryEncoderBuffer::clicked_;
+
+/* static */
+template int8_t RotaryEncoderBuffer::increment_;
+
+} // namespace avrlib
+
+#endif // AVRLIB_DEVICES_SHIFT_REGISTER_H_
diff --git a/avrlib/devices/shift_register.h b/avrlib/devices/shift_register.h
new file mode 100755
index 0000000..2cc009d
--- /dev/null
+++ b/avrlib/devices/shift_register.h
@@ -0,0 +1,169 @@
+// Copyright 2009 Olivier Gillet.
+//
+// Author: Olivier Gillet (ol.gillet@gmail.com)
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+// This program 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 General Public License for more details.
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see .
+//
+// -----------------------------------------------------------------------------
+//
+// Driver for a 8-bits shift register.
+
+#ifndef AVRLIB_DEVICES_SHIFT_REGISTER_H_
+#define AVRLIB_DEVICES_SHIFT_REGISTER_H_
+
+#include "avrlib/gpio.h"
+#include "avrlib/size_to_type.h"
+#include "avrlib/time.h"
+
+namespace avrlib {
+
+template
+struct BaseShiftRegisterOutput {
+ BaseShiftRegisterOutput() { }
+ static void Init() {
+ Clock::set_mode(DIGITAL_OUTPUT);
+ Latch::set_mode(DIGITAL_OUTPUT);
+ Data::set_mode(DIGITAL_OUTPUT);
+ Latch::High();
+ }
+};
+
+template
+struct ShiftRegisterOutput : public BaseShiftRegisterOutput {
+};
+
+template
+struct ShiftRegisterOutput
+ : public BaseShiftRegisterOutput {
+ ShiftRegisterOutput() { }
+ static void ShiftOut(typename DataTypeForSize::Type data) {
+ Data::Low();
+ for (uint8_t i = size; i > 0; --i) {
+ Clock::Low();
+ Data::set_value(data & 1);
+ data >>= 1;
+ Clock::High();
+ }
+ Clock::Low();
+ }
+ static void Begin() {
+ Latch::Low();
+ }
+ static void End() {
+ Latch::High();
+ }
+ static void Write(typename DataTypeForSize::Type data) {
+ Begin();
+ ShiftOut(data);
+ End();
+ }
+};
+
+template
+struct ShiftRegisterOutput
+ : public BaseShiftRegisterOutput {
+ ShiftRegisterOutput() { }
+ typedef typename DataTypeForSize::Type T;
+ static void ShiftOut(T data) {
+ Data::Low();
+ T mask = (T(1) << (size - 1));
+ for (uint8_t i = size; i > 0; --i) {
+ Clock::Low();
+ if (data & mask) {
+ Data::High();
+ } else {
+ Data::Low();
+ }
+ mask >>= 1;
+ Clock::High();
+ }
+ Clock::Low();
+ }
+ static void Begin() {
+ Latch::Low();
+ }
+ static void End() {
+ Latch::High();
+ }
+ static void Write(typename DataTypeForSize::Type data) {
+ Begin();
+ ShiftOut(data);
+ End();
+ }
+};
+
+template
+struct BaseShiftRegisterInput {
+ BaseShiftRegisterInput() { }
+ static void Init() {
+ Clock::set_mode(DIGITAL_OUTPUT);
+ Load::set_mode(DIGITAL_OUTPUT);
+ Data::set_mode(DIGITAL_INPUT);
+ Load::High();
+ Clock::Low();
+ }
+};
+
+template
+struct ShiftRegisterInput : public BaseShiftRegisterInput {
+};
+
+template
+struct ShiftRegisterInput
+ : public BaseShiftRegisterInput {
+ ShiftRegisterInput() { }
+ typedef typename DataTypeForSize::Type T;
+ static T Read() {
+ T data = 0;
+ // Strobe load pin.
+ Load::Low();
+ Load::High();
+ for (uint8_t i = size; i > 0; --i) {
+ data <<= 1;
+ data |= Data::value();
+ Clock::High();
+ Clock::Low();
+ }
+ return data;
+ }
+};
+
+template
+struct ShiftRegisterInput
+ : public BaseShiftRegisterInput {
+ ShiftRegisterInput() { }
+ typedef typename DataTypeForSize::Type T;
+ static T Read() {
+ T mask = 1;
+ T data = 0;
+ // Strobe load pin.
+ Load::Low();
+ Load::High();
+ for (uint8_t i = size; i > 0; --i) {
+ if (Data::value()) {
+ data |= mask;
+ }
+ Clock::High();
+ mask <<= 1;
+ Clock::Low();
+ }
+ return data;
+ }
+};
+
+} // namespace avrlib
+
+#endif // AVRLIB_DEVICES_SHIFT_REGISTER_H_
diff --git a/avrlib/devices/switch_array.h b/avrlib/devices/switch_array.h
new file mode 100755
index 0000000..4e74670
--- /dev/null
+++ b/avrlib/devices/switch_array.h
@@ -0,0 +1,151 @@
+// Copyright 2009 Olivier Gillet.
+//
+// Author: Olivier Gillet (ol.gillet@gmail.com)
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+// This program 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 General Public License for more details.
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see .
+//
+// -----------------------------------------------------------------------------
+//
+// Controller for an array of switches read through a parallel-in > serial out
+// shift register. Includes debouncing.
+
+#ifndef AVRLIB_DEVICES_SWITCH_ARRAY_H_
+#define AVRLIB_DEVICES_SWITCH_ARRAY_H_
+
+#include "avrlib/devices/shift_register.h"
+#include "avrlib/size_to_type.h"
+#include "avrlib/time.h"
+
+namespace avrlib {
+
+struct SwitchState {
+ uint8_t changed;
+ uint8_t state;
+ uint8_t debounced_state;
+ uint32_t time;
+};
+
+struct KeyEvent {
+ uint8_t id;
+ uint8_t shifted;
+ uint8_t hold_time;
+};
+
+template
+class SwitchArray {
+ typedef typename DataTypeForSize::Type T;
+ typedef ShiftRegisterInput<
+ Load, Clock, Data,
+ 8 * sizeof(T), LSB_FIRST> Register;
+
+ public:
+ SwitchArray() { }
+
+ static void Init() {
+ for (uint8_t i = 0; i < num_inputs; ++i) {
+ switch_state_[i].state = 0xff;
+ switch_state_[i].debounced_state = HIGH;
+ switch_state_[i].time = 0;
+ }
+ last_event_time_ = 0;
+ Register::Init();
+ }
+
+ static uint32_t last_event_time() { return last_event_time_; }
+ static uint32_t idle_time() { return milliseconds() - last_event_time_; }
+ static uint8_t shifted() { return switch_state_[shift].state == 0x00; }
+ static void InhibitShiftRelease() { inhibit_shift_release_ = 1; }
+ static const SwitchState& switch_state(uint8_t i) { return switch_state_[i]; }
+ static uint8_t released() {
+ for (uint8_t i = 0; i < num_inputs; ++i) {
+ if (switch_state_[i].state == 0x7f &&
+ (i != shift || !inhibit_shift_release_)) {
+ return 1;
+ }
+ }
+ return 0;
+ }
+
+ static void Touch() {
+ last_event_time_ = milliseconds();
+ }
+
+ static inline KeyEvent key_event() {
+ KeyEvent e;
+ e.id = num_inputs;
+ for (uint8_t i = 0; i < num_inputs; ++i) {
+ if (switch_state_[i].state == 0x7f) {
+ if (i == shift && inhibit_shift_release_) {
+ inhibit_shift_release_ = 0;
+ } else {
+ e.id = i;
+ e.shifted = shifted();
+ e.hold_time = static_cast(
+ last_event_time_ - switch_state_[i].time) >> 8;
+ if (e.shifted) {
+ inhibit_shift_release_ = 1;
+ }
+ }
+ }
+ }
+ return e;
+ }
+
+ static uint8_t Read() {
+ T value = Register::Read();
+ uint32_t now = milliseconds();
+ T mask = 1 << (num_inputs - 1);
+ for (uint8_t i = 0; i < num_inputs; ++i) {
+ switch_state_[i].state <<= 1;
+ if (value & mask) {
+ switch_state_[i].state |= 1;
+ }
+ if (switch_state_[i].state == 0x80) {
+ last_event_time_ = now;
+ switch_state_[i].debounced_state = LOW;
+ switch_state_[i].time = now;
+ inhibit_shift_release_ = 0;
+ } else if (switch_state_[i].state == 0x7f) {
+ last_event_time_ = now;
+ switch_state_[i].debounced_state = HIGH;
+ }
+ mask >>= 1;
+ }
+ }
+
+ private:
+ static uint32_t last_event_time_;
+ static SwitchState switch_state_[num_inputs];
+ static uint8_t inhibit_shift_release_;
+
+ DISALLOW_COPY_AND_ASSIGN(SwitchArray);
+};
+
+template
+SwitchState SwitchArray::switch_state_[num_inputs];
+
+template
+uint32_t SwitchArray::last_event_time_;
+
+template
+uint8_t SwitchArray::inhibit_shift_release_;
+
+
+} // namespace avrlib
+
+#endif // AVRLIB_DEVICES_SWITCH_ARRAY_H_
diff --git a/avrlib/examples/README b/avrlib/examples/README
new file mode 100644
index 0000000..aadb7ea
--- /dev/null
+++ b/avrlib/examples/README
@@ -0,0 +1,16 @@
+blink: blink a LED on pin 7.
+
+eeprom: interface with an external i2c eeprom chip.
+
+encoder: simple rotary encoder demo.
+
+lcd: shows how to use the non-blocking LCD class to drive a HD44780-compatible display.
+
+parallel: uses a block of 4 pins from port D for chasing lights, and another block for a binary counter.
+
+scan_pots: scans 4 potentiometers connected to the analog inputs, and dim 4 LEDs accordingly.
+
+serial_debug: echo one MIDI port to the other one.
+
+switches_and_leds: read some switches press through a multiplexer, and write to 12 LEDs through a shift register.
+
diff --git a/avrlib/examples/blink/blink.cc b/avrlib/examples/blink/blink.cc
new file mode 100755
index 0000000..a9935df
--- /dev/null
+++ b/avrlib/examples/blink/blink.cc
@@ -0,0 +1,37 @@
+// Copyright 2010 Olivier Gillet.
+//
+// Author: Olivier Gillet (ol.gillet@gmail.com)
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+// This program 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 General Public License for more details.
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see .
+
+#include "avrlib/gpio.h"
+#include "avrlib/boot.h"
+#include "avrlib/time.h"
+
+using namespace avrlib;
+
+Gpio<21> led;
+
+TIMER_0_TICK {
+ TickSystemClock();
+}
+
+int main(void) {
+ Boot(true);
+ led.set_mode(DIGITAL_OUTPUT);
+ while (1) {
+ led.High();
+ Delay(250);
+ led.Low();
+ Delay(250);
+ }
+}
diff --git a/avrlib/examples/blink/makefile b/avrlib/examples/blink/makefile
new file mode 100755
index 0000000..460a251
--- /dev/null
+++ b/avrlib/examples/blink/makefile
@@ -0,0 +1,18 @@
+# Copyright 2009 Olivier Gillet.
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+# This program 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 General Public License for more details.
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see .
+
+MCU_NAME = 644
+TARGET = blink
+PACKAGES = avrlib avrlib/devices avrlib/examples/blink
+
+include avrlib/makefile.mk
diff --git a/avrlib/examples/eeprom/eeprom_rw.cc b/avrlib/examples/eeprom/eeprom_rw.cc
new file mode 100755
index 0000000..6549d1a
--- /dev/null
+++ b/avrlib/examples/eeprom/eeprom_rw.cc
@@ -0,0 +1,63 @@
+// Copyright 2010 Olivier Gillet.
+//
+// Author: Olivier Gillet (ol.gillet@gmail.com)
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+// This program 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 General Public License for more details.
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see .
+
+#include "avrlib/devices/external_eeprom.h"
+#include "avrlib/boot.h"
+#include "avrlib/serial.h"
+#include "avrlib/time.h"
+#include "avrlib/output_stream.h"
+
+using namespace avrlib;
+using namespace avrlib;
+
+// Debug text output.
+typedef Serial Debug;
+OutputStream debug_output;
+
+ExternalEeprom<> eeprom;
+
+TIMER_0_TICK {
+ TickSystemClock();
+}
+
+int main(void) {
+ Boot(true);
+ Debug::Init();
+ eeprom.Init();
+
+ uint8_t buffer[32];
+ for (uint8_t i = 0; i < 32; ++i) {
+ buffer[i] = i + 100;
+ }
+
+ eeprom.WriteWithinBlock(0x20, buffer, 32);
+ debug_output << "Written" << endl;
+ Delay(4); // Write/read cycles delay.
+
+ debug_output << "Reading" << endl;
+ eeprom.SetAddress(0x20);
+ for (uint8_t i = 0; i < 32; ++i) {
+ debug_output << int(eeprom.Read()) << endl;
+ }
+ debug_output << "Done" << endl;
+
+ eeprom.SetAddress(0x20);
+ uint8_t buffer2[32];
+ eeprom.Read(0x20, 32, buffer2);
+ for (uint8_t i = 0; i < 32; ++i) {
+ debug_output << int(buffer2[i]) << endl;
+ }
+ debug_output << "Done" << endl;
+}
diff --git a/avrlib/examples/eeprom/makefile b/avrlib/examples/eeprom/makefile
new file mode 100755
index 0000000..5c15a6e
--- /dev/null
+++ b/avrlib/examples/eeprom/makefile
@@ -0,0 +1,18 @@
+# Copyright 2009 Olivier Gillet.
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+# This program 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 General Public License for more details.
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see .
+
+MCU_NAME = 644
+TARGET = eeprom_rw
+PACKAGES = avrlib avrlib/i2c hardware/avr avrlib/examples/eeprom
+
+include avrlib/makefile.mk
diff --git a/avrlib/examples/encoder/encoder.cc b/avrlib/examples/encoder/encoder.cc
new file mode 100755
index 0000000..a9c4f02
--- /dev/null
+++ b/avrlib/examples/encoder/encoder.cc
@@ -0,0 +1,50 @@
+// Copyright 2010 Olivier Gillet.
+//
+// Author: Olivier Gillet (ol.gillet@gmail.com)
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+// This program 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 General Public License for more details.
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see .
+
+#include "avrlib/devices/rotary_encoder.h"
+#include "avrlib/boot.h"
+#include "avrlib/serial.h"
+#include "avrlib/time.h"
+#include "avrlib/output_stream.h"
+
+using namespace avrlib;
+using namespace avrlib;
+
+typedef Serial Debug;
+typedef RotaryEncoder, Gpio<1>, Gpio<2> > Encoder;
+
+TIMER_0_TICK {
+ TickSystemClock();
+}
+
+int main(void) {
+ OutputStream debug_output;
+ Encoder encoder;
+ int16_t value = 0;
+
+ Boot(true);
+ Debug::Init();
+ encoder.Init();
+ while (1) {
+ int8_t delta = encoder.Read();
+ value += delta;
+ if (delta != 0) {
+ debug_output << value << " " << int(delta) << endl;
+ }
+ if (encoder.clicked()) {
+ debug_output << "clicked" << endl;
+ }
+ }
+}
diff --git a/avrlib/examples/encoder/makefile b/avrlib/examples/encoder/makefile
new file mode 100755
index 0000000..ec5e898
--- /dev/null
+++ b/avrlib/examples/encoder/makefile
@@ -0,0 +1,18 @@
+# Copyright 2009 Olivier Gillet.
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+# This program 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 General Public License for more details.
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see .
+
+MCU_NAME = 644
+TARGET = encoder
+PACKAGES = avrlib avrlib/devices avrlib/examples/encoder
+
+include avrlib/makefile.mk
diff --git a/avrlib/examples/lcd/lcd.cc b/avrlib/examples/lcd/lcd.cc
new file mode 100755
index 0000000..28444e4
--- /dev/null
+++ b/avrlib/examples/lcd/lcd.cc
@@ -0,0 +1,86 @@
+// Copyright 2010 Olivier Gillet.
+//
+// Author: Olivier Gillet (ol.gillet@gmail.com)
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+// This program 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 General Public License for more details.
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see .
+
+#include "avrlib/devices/buffered_display.h"
+#include "avrlib/devices/hd44780_lcd.h"
+#include "avrlib/gpio.h"
+#include "avrlib/parallel_io.h"
+#include "avrlib/boot.h"
+#include "avrlib/time.h"
+#include "avrlib/string.h"
+
+using namespace avrlib;
+using namespace avrlib;
+
+typedef Hd44780Lcd<
+ Gpio<18>,
+ Gpio<19>,
+ ParallelPort > Lcd;
+
+Lcd lcd;
+BufferedDisplay display;
+
+uint8_t chars[] = {
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 2, 3, 3, 2, 2, 14, 30, 12,
+ 0, 0, 0, 4, 4, 14, 17, 31,
+ 4, 14, 21, 4, 4, 4, 4, 0,
+ 0, 4, 4, 4, 4, 21, 14, 4,
+ 0, 0, 0, 8, 21, 2, 0, 0,
+ 8, 20, 9, 21, 3, 5, 17, 14,
+ 14, 17, 31, 17, 23, 21, 21, 0,
+};
+
+static uint8_t cpt;
+
+TIMER_2_TICK {
+ cpt = (cpt + 1) & 0x0f;
+ if (cpt == 0) {
+ lcd.Tick();
+ }
+}
+
+TIMER_0_TICK {
+ TickSystemClock();
+}
+
+int main(void) {
+ Boot(true);
+ lcd.Init();
+ Timer<2>::set_prescaler(1);
+ Timer<2>::set_mode(TIMER_PWM_PHASE_CORRECT);
+ Timer<2>::Start();
+
+ display.Init();
+
+ lcd.SetCustomCharMap(chars, 8, 0);
+
+ display.Print(0, "hello \x01\x02\x03\x04\x05\x06\x07");
+ uint16_t cpt = 0;
+ display.set_cursor_position(31);
+
+ while (1) {
+ cpt = (cpt + 1) & 0x07ff;
+ if (cpt == 1) {
+ display.set_status('@');
+ }
+ char buff[5];
+ display.Tick();
+ UnsafeItoa(cpt, 5, buff);
+ ++cpt;
+ display.Print(1, buff);
+ Delay(1);
+ }
+}
diff --git a/avrlib/examples/lcd/makefile b/avrlib/examples/lcd/makefile
new file mode 100755
index 0000000..3408c51
--- /dev/null
+++ b/avrlib/examples/lcd/makefile
@@ -0,0 +1,18 @@
+# Copyright 2009 Olivier Gillet.
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+# This program 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 General Public License for more details.
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see .
+
+MCU_NAME = 644
+TARGET = lcd
+PACKAGES = avrlib avrlib/devices avrlib/examples/lcd
+
+include avrlib/makefile.mk
diff --git a/avrlib/examples/parallel/makefile b/avrlib/examples/parallel/makefile
new file mode 100755
index 0000000..60a6e1c
--- /dev/null
+++ b/avrlib/examples/parallel/makefile
@@ -0,0 +1,18 @@
+# Copyright 2009 Olivier Gillet.
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+# This program 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 General Public License for more details.
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see .
+
+MCU_NAME = 644
+TARGET = parallel
+PACKAGES = avrlib avrlib/devices avrlib/examples/parallel
+
+include avrlib/makefile.mk
diff --git a/avrlib/examples/parallel/parallel.cc b/avrlib/examples/parallel/parallel.cc
new file mode 100755
index 0000000..8181faa
--- /dev/null
+++ b/avrlib/examples/parallel/parallel.cc
@@ -0,0 +1,41 @@
+// Copyright 2010 Olivier Gillet.
+//
+// Author: Olivier Gillet (ol.gillet@gmail.com)
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+// This program 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 General Public License for more details.
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see .
+
+#include "avrlib/boot.h"
+#include "avrlib/parallel_io.h"
+#include "avrlib/time.h"
+
+using namespace avrlib;
+
+TIMER_0_TICK {
+ TickSystemClock();
+}
+
+int main(void) {
+ Boot(true);
+
+ ParallelPort count;
+ ParallelPort scroll;
+ uint8_t counter = 0;
+
+ count.set_mode(DIGITAL_OUTPUT);
+ scroll.set_mode(DIGITAL_OUTPUT);
+
+ while (1) {
+ Delay(100);
+ count.Write((++counter) & 15);
+ scroll.Write(1 << (counter & 3));
+ }
+}
diff --git a/avrlib/examples/scan_pots/makefile b/avrlib/examples/scan_pots/makefile
new file mode 100755
index 0000000..20add60
--- /dev/null
+++ b/avrlib/examples/scan_pots/makefile
@@ -0,0 +1,18 @@
+# Copyright 2009 Olivier Gillet.
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+# This program 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 General Public License for more details.
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see .
+
+MCU_NAME = 644
+TARGET = scan_pots
+PACKAGES = avrlib avrlib/devices avrlib/examples/scan_pots
+
+include avrlib/makefile.mk
diff --git a/avrlib/examples/scan_pots/scan_pots.cc b/avrlib/examples/scan_pots/scan_pots.cc
new file mode 100755
index 0000000..81bf412
--- /dev/null
+++ b/avrlib/examples/scan_pots/scan_pots.cc
@@ -0,0 +1,89 @@
+// Copyright 2010 Olivier Gillet.
+//
+// Author: Olivier Gillet (ol.gillet@gmail.com)
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+// This program 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 General Public License for more details.
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see .
+
+#include "avrlib/boot.h"
+#include "avrlib/devices/input_array.h"
+#include "avrlib/serial.h"
+#include "avrlib/time.h"
+#include "avrlib/output_stream.h"
+
+using namespace avrlib;
+using namespace avrlib;
+
+typedef MuxedAnalogInput<0> PotsMux;
+typedef Serial Debug;
+typedef InputArray Pots;
+
+PwmOutput<12> a;
+PwmOutput<13> b;
+PwmOutput<3> c;
+PwmOutput<4> d;
+
+using namespace avrlib;
+
+TIMER_0_TICK {
+ TickSystemClock();
+}
+
+int main(void) {
+ uint8_t idle;
+ OutputStream debug_output;
+ Pots pots;
+
+ Boot(true);
+ Debug::Init();
+ pots.Init();
+ a.Init();
+ b.Init();
+ c.Init();
+ d.Init();
+
+ Timer<1>::set_prescaler(1);
+ Timer<1>::set_mode(TIMER_PWM_PHASE_CORRECT);
+ Timer<2>::set_prescaler(1);
+ Timer<2>::set_mode(TIMER_PWM_PHASE_CORRECT);
+
+ idle = 0;
+ debug_output << "Let's do some scanning" << endl;
+ while (1) {
+ Pots::Event event = pots.Read();
+ if (event.event == EVENT_NONE) {
+ if (event.time > 1500) {
+ if (!idle) {
+ debug_output << "Idle..." << endl;
+ }
+ idle = 1;
+ }
+ } else {
+ idle = 0;
+ debug_output << int(event.id) << ":" << event.value << endl;
+ switch (event.id) {
+ case 0:
+ a.Write(event.value >> 2);
+ break;
+ case 1:
+ b.Write(event.value >> 2);
+ break;
+ case 2:
+ c.Write(event.value >> 2);
+ break;
+ case 3:
+ d.Write(event.value >> 2);
+ break;
+ }
+ }
+ PotsMux::set_pin(pots.active_input());
+ }
+}
diff --git a/avrlib/examples/serial_debug/makefile b/avrlib/examples/serial_debug/makefile
new file mode 100755
index 0000000..70ab3f0
--- /dev/null
+++ b/avrlib/examples/serial_debug/makefile
@@ -0,0 +1,18 @@
+# Copyright 2009 Olivier Gillet.
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+# This program 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 General Public License for more details.
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see .
+
+MCU_NAME = 644
+TARGET = serial_debug
+PACKAGES = avrlib avrlib/devices avrlib/examples/serial_debug
+
+include avrlib/makefile.mk
diff --git a/avrlib/examples/serial_debug/serial_debug.cc b/avrlib/examples/serial_debug/serial_debug.cc
new file mode 100755
index 0000000..4a39c85
--- /dev/null
+++ b/avrlib/examples/serial_debug/serial_debug.cc
@@ -0,0 +1,37 @@
+// Copyright 2010 Olivier Gillet.
+//
+// Author: Olivier Gillet (ol.gillet@gmail.com)
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+// This program 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 General Public License for more details.
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see .
+
+#include "avrlib/boot.h"
+#include "avrlib/serial.h"
+#include "avrlib/output_stream.h"
+
+using namespace avrlib;
+using namespace avrlib;
+
+typedef Serial DebugPort;
+Serial midi;
+
+int main(void) {
+ OutputStream debug_output;
+
+ Boot(false);
+ DebugPort::Init();
+ midi.Init();
+ while (1) {
+ if (midi.readable()) {
+ debug_output << int(midi.ImmediateRead()) << endl;
+ }
+ }
+}
diff --git a/avrlib/examples/switches_and_leds/makefile b/avrlib/examples/switches_and_leds/makefile
new file mode 100755
index 0000000..5724d59
--- /dev/null
+++ b/avrlib/examples/switches_and_leds/makefile
@@ -0,0 +1,18 @@
+# Copyright 2009 Olivier Gillet.
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+# This program 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 General Public License for more details.
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see .
+
+MCU_NAME = 644
+TARGET = switches_and_leds
+PACKAGES = avrlib avrlib/devices avrlib/examples/switches_and_leds
+
+include avrlib/makefile.mk
diff --git a/avrlib/examples/switches_and_leds/switches_and_leds.cc b/avrlib/examples/switches_and_leds/switches_and_leds.cc
new file mode 100755
index 0000000..5f55b32
--- /dev/null
+++ b/avrlib/examples/switches_and_leds/switches_and_leds.cc
@@ -0,0 +1,98 @@
+// Copyright 2010 Olivier Gillet.
+//
+// Author: Olivier Gillet (ol.gillet@gmail.com)
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+// This program 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 General Public License for more details.
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see .
+
+#include "avrlib/boot.h"
+#include "avrlib/devices/output_array.h"
+#include "avrlib/devices/switch_array.h"
+#include "avrlib/serial.h"
+#include "avrlib/time.h"
+#include "avrlib/output_stream.h"
+
+using namespace avrlib;
+using namespace avrlib;
+
+typedef Serial Debug;
+OutputStream debug_output;
+
+TIMER_0_TICK {
+ TickSystemClock();
+}
+
+int main(void) {
+ Boot(true);
+ OutputArray, Gpio<7>, Gpio<23>, 8, 4, MSB_FIRST, false> leds;
+ SwitchArray, Gpio<7>, Gpio<6>, 6> switches;
+
+ uint8_t current_led = 0;
+ uint8_t intensity = 15;
+ uint8_t divide = 0;
+
+ Debug::Init();
+ leds.Init();
+ switches.Init();
+ uint8_t full = 0;
+ while (1) {
+ ++divide;
+ if ((divide & 3) == 0) {
+ switches.Read();
+ if (switches.released()) {
+ KeyEvent released = switches.key_event();
+ debug_output << "id: " << int(released.id);
+ debug_output << " hold time:" << int(released.hold_time);
+ debug_output << " shifted: " << int(released.shifted) << endl;
+ switch (released.id) {
+ case 5:
+ if (current_led > 0) {
+ --current_led;
+ }
+ break;
+
+ case 4:
+ if (current_led < 7) {
+ ++current_led;
+ }
+ break;
+
+ case 3:
+ if (intensity > 0) {
+ --intensity;
+ }
+ break;
+
+ case 2:
+ if (intensity < 15) {
+ ++intensity;
+ }
+ break;
+
+ case 1:
+ intensity = 15;
+ current_led = 0;
+ full = 0;
+ break;
+
+ case 0:
+ full = ~full;
+ break;
+ }
+ }
+ }
+ leds.Clear();
+ for (uint8_t i = 0; i < 8; ++i) {
+ leds.set_value(i, (i == current_led || full) ? intensity : 0);
+ }
+ leds.Write();
+ }
+}
diff --git a/avrlib/gpio.h b/avrlib/gpio.h
new file mode 100755
index 0000000..877a2df
--- /dev/null
+++ b/avrlib/gpio.h
@@ -0,0 +1,270 @@
+// Copyright 2009 Olivier Gillet.
+//
+// Author: Olivier Gillet (ol.gillet@gmail.com)
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+// This program 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 General Public License for more details.
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see .
+//
+// -----------------------------------------------------------------------------
+//
+// An alternative gpio library based on templates.
+//
+// Examples of use:
+//
+// Gpio<3>::set_mode(DIGITAL_INPUT)
+// Gpio<4>::set_mode(DIGITAL_OUTPUT)
+// Gpio<3>::value()
+// Gpio<4>::High()
+// Gpio<4>::Low()
+// Gpio<4>::set_value(1)
+// Gpio<4>::set_value(0)
+
+#ifndef AVRLIB_GPIO_H_
+#define AVRLIB_GPIO_H_
+
+#include
+
+#include "avrlib/avrlib.h"
+#include "avrlib/timer.h"
+
+namespace avrlib {
+
+enum PinMode {
+ DIGITAL_INPUT = 0,
+ DIGITAL_OUTPUT = 1,
+ ANALOG_OUTPUT = 2
+};
+
+// All the registers used in the following definitions are wrapped here.
+IORegister(DDRB);
+IORegister(DDRC);
+IORegister(DDRD);
+
+IORegister(PORTB);
+IORegister(PORTC);
+IORegister(PORTD);
+
+IORegister(PINB);
+IORegister(PINC);
+IORegister(PIND);
+
+// Represents a i/o port, which has input, output and mode registers.
+template
+struct Port {
+ typedef InputRegister Input;
+ typedef OutputRegister Output;
+ typedef ModeRegister Mode;
+};
+
+// Definition of I/O ports.
+typedef Port PortB;
+typedef Port PortC;
+typedef Port PortD;
+
+// The actual implementation of a pin, not very convenient to use because it
+// requires the actual parameters of the pin to be passed as template
+// arguments.
+template
+struct GpioImpl {
+ typedef BitInRegister ModeBit;
+ typedef BitInRegister OutputBit;
+ typedef BitInRegister InputBit;
+ typedef PwmChannel Pwm;
+
+ // Mode change.
+ static inline void set_mode(uint8_t mode) {
+ if (mode == DIGITAL_INPUT) {
+ ModeBit::clear();
+ } else if (mode == DIGITAL_OUTPUT || mode == ANALOG_OUTPUT) {
+ ModeBit::set();
+ }
+ if (mode == ANALOG_OUTPUT) {
+ PwmChannel::Start();
+ } else {
+ PwmChannel::Stop();
+ }
+ }
+
+ // Digital Write.
+ static inline void High() {
+ if (safe) {
+ set_mode(DIGITAL_OUTPUT);
+ }
+ OutputBit::set();
+ }
+ static inline void Low() {
+ if (safe) {
+ set_mode(DIGITAL_OUTPUT);
+ }
+ OutputBit::clear();
+ }
+ static inline void set_value(uint8_t value) {
+ if (value == 0) {
+ Low();
+ } else {
+ High();
+ }
+ }
+ static inline void set_analog_value(uint8_t value) {
+ if (safe) {
+ set_mode(ANALOG_OUTPUT);
+ }
+ if (PwmChannel::analog) {
+ PwmChannel::Write(value);
+ } else {
+ set_value(value);
+ }
+ }
+
+ // Digital read.
+ static inline uint8_t value() {
+ if (safe) {
+ set_mode(DIGITAL_INPUT);
+ }
+ return InputBit::value();
+ }
+};
+
+// A template that will be specialized for each pin, allowing the pin number to
+// be specified as a template parameter.
+template
+struct NumberedGpio { };
+
+// Macro to make the pin definitions (template specializations) easier to read.
+#define SetupGpio(n, port, timer, bit) \
+template struct NumberedGpio { \
+ typedef GpioImpl Impl; };
+
+#ifndef ATMEGA328P
+
+// Pin definitions for ATmega644p and ATmega1284p
+SetupGpio(0, PortB, NoPwmChannel, 0);
+SetupGpio(1, PortB, NoPwmChannel, 1);
+SetupGpio(2, PortB, NoPwmChannel, 2);
+SetupGpio(3, PortB, PwmChannel0A, 3);
+SetupGpio(4, PortB, PwmChannel0B, 4);
+SetupGpio(5, PortB, NoPwmChannel, 5);
+SetupGpio(6, PortB, NoPwmChannel, 6);
+SetupGpio(7, PortB, NoPwmChannel, 7);
+
+SetupGpio(8, PortD, NoPwmChannel, 0);
+SetupGpio(9, PortD, NoPwmChannel, 1);
+SetupGpio(10, PortD, NoPwmChannel, 2);
+SetupGpio(11, PortD, NoPwmChannel, 3);
+SetupGpio(12, PortD, PwmChannel1B, 4);
+SetupGpio(13, PortD, PwmChannel1A, 5);
+SetupGpio(14, PortD, PwmChannel2B, 6);
+SetupGpio(15, PortD, PwmChannel2A, 7);
+
+SetupGpio(16, PortC, NoPwmChannel, 0);
+SetupGpio(17, PortC, NoPwmChannel, 1);
+SetupGpio(18, PortC, NoPwmChannel, 2);
+SetupGpio(19, PortC, NoPwmChannel, 3);
+SetupGpio(20, PortC, NoPwmChannel, 4);
+SetupGpio(21, PortC, NoPwmChannel, 5);
+SetupGpio(22, PortC, NoPwmChannel, 6);
+SetupGpio(23, PortC, NoPwmChannel, 7);
+
+const uint8_t kSpiSlaveSelectPin = 4;
+const uint8_t kSpiDataOutPin = 5;
+const uint8_t kSpiDataInPin = 6;
+const uint8_t kSpiClockPin = 7;
+
+#else
+
+// Pin definitions for ATmega168p and ATmega328p
+SetupGpio(0, PortD, NoPwmChannel, 0);
+SetupGpio(1, PortD, NoPwmChannel, 1);
+SetupGpio(2, PortD, NoPwmChannel, 2);
+SetupGpio(3, PortD, PwmChannel2B, 3);
+SetupGpio(4, PortD, NoPwmChannel, 4);
+SetupGpio(5, PortD, PwmChannel0B, 5);
+SetupGpio(6, PortD, PwmChannel0A, 6);
+SetupGpio(7, PortD, NoPwmChannel, 7);
+SetupGpio(8, PortB, NoPwmChannel, 0);
+SetupGpio(9, PortB, PwmChannel1A, 1);
+SetupGpio(10, PortB, PwmChannel1B, 2);
+SetupGpio(11, PortB, PwmChannel2A, 3);
+SetupGpio(12, PortB, NoPwmChannel, 4);
+SetupGpio(13, PortB, NoPwmChannel, 5);
+SetupGpio(14, PortC, NoPwmChannel, 0);
+SetupGpio(15, PortC, NoPwmChannel, 1);
+SetupGpio(16, PortC, NoPwmChannel, 2);
+SetupGpio(17, PortC, NoPwmChannel, 3);
+SetupGpio(18, PortC, NoPwmChannel, 4);
+SetupGpio(19, PortC, NoPwmChannel, 5);
+
+const uint8_t kSpiSlaveSelectPin = 10;
+const uint8_t kSpiDataOutPin = 11;
+const uint8_t kSpiDataInPin = 12;
+const uint8_t kSpiClockPin = 13;
+
+#endif // ATMEGA328P
+
+// Two specializations of the numbered pin template, one which clears the timer
+// for each access to the PWM pins, as does the original Arduino wire lib,
+// the other that does not (use with care!).
+template
+struct Gpio {
+ typedef typename NumberedGpio::Impl Impl;
+ static void High() { Impl::High(); }
+ static void Low() { Impl::Low(); }
+ static void set_mode(uint8_t mode) { Impl::set_mode(mode); }
+ static void set_value(uint8_t value) { Impl::set_value(value); }
+ static void set_analog_value(uint8_t value) { Impl::set_analog_value(value); }
+ static uint8_t value() { return Impl::value(); }
+ static uint8_t number() { return n; }
+};
+
+template
+struct DigitalInput {
+ enum {
+ buffer_size = 0,
+ data_size = 1,
+ };
+ static void Init() {
+ Gpio::set_mode(DIGITAL_INPUT);
+ }
+ static void EnablePullUpResistor() {
+ Gpio::High();
+ }
+ static void DisablePullUpResistor() {
+ Gpio::Low();
+ }
+ static uint8_t Read() {
+ return Gpio::value();
+ }
+};
+
+template
+struct PwmOutput {
+ enum {
+ buffer_size = 0,
+ data_size = 8,
+ };
+ static void Init() {
+ Gpio::set_mode(ANALOG_OUTPUT);
+ }
+ static void Write(uint8_t value) {
+ return Gpio::set_analog_value(value);
+ }
+ static void Stop() {
+ Gpio::Impl::Pwm::Stop();
+ }
+ static void Start() {
+ Gpio::Impl::Pwm::Start();
+ }
+};
+
+} // namespace avrlib
+
+#endif // AVRLIB_GPIO_H_
diff --git a/avrlib/i2c/i2c.cc b/avrlib/i2c/i2c.cc
new file mode 100644
index 0000000..ff34839
--- /dev/null
+++ b/avrlib/i2c/i2c.cc
@@ -0,0 +1,35 @@
+// Copyright 2010 Olivier Gillet.
+//
+// Author: Olivier Gillet (ol.gillet@gmail.com)
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+// This program 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 General Public License for more details.
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see .
+//
+// -----------------------------------------------------------------------------
+//
+// Interrupt handler for I2C.
+
+#include "avrlib/i2c/i2c.h"
+
+#include
+
+#include "avrlib/gpio.h"
+
+using namespace avrlib;
+
+/* static, extern */
+void (*avrlib::i2c_handler_)() = 0;
+
+ISR(TWI_vect) {
+ if (i2c_handler_) {
+ (*i2c_handler_)();
+ }
+}
diff --git a/avrlib/i2c/i2c.h b/avrlib/i2c/i2c.h
new file mode 100644
index 0000000..1f26abf
--- /dev/null
+++ b/avrlib/i2c/i2c.h
@@ -0,0 +1,296 @@
+// Copyright 2010 Olivier Gillet.
+//
+// Author: Olivier Gillet (ol.gillet@gmail.com)
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+// This program 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 General Public License for more details.
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see .
+//
+// -----------------------------------------------------------------------------
+//
+// Implementation of the I2C protocol (Master mode only for now).
+//
+// Note that this file is not in the hal directory directly because I don't want
+// the interrupt handler code for TWI to be linked with every project.
+
+#ifndef AVRLIB_I2C_I2C_H_
+#define AVRLIB_I2C_I2C_H_
+
+#include
+
+#include "avrlib/gpio.h"
+#include "avrlib/avrlib.h"
+#include "avrlib/ring_buffer.h"
+
+namespace avrlib {
+
+IORegister(TWCR);
+IORegister(TWSR);
+typedef BitInRegister Prescaler0;
+typedef BitInRegister Prescaler1;
+typedef BitInRegister I2cEnable;
+typedef BitInRegister I2cInterrupt;
+typedef BitInRegister I2cAck;
+typedef BitInRegister I2cStart;
+typedef BitInRegister I2cStop;
+
+enum I2cState {
+ I2C_STATE_READY,
+ I2C_STATE_TRANSMITTING,
+ I2C_STATE_RECEIVING,
+};
+
+enum I2cError {
+ I2C_ERROR_NONE = 0xff,
+ I2C_ERROR_NO_ACK_FOR_ADDRESS = 0x01,
+ I2C_ERROR_NO_ACK_FOR_DATA = TW_MT_SLA_NACK,
+ I2C_ERROR_ARBITRATION_LOST = TW_MT_DATA_NACK,
+ I2C_ERROR_BUS_ERROR = 0xfe,
+};
+
+template
+class I2cOutput {
+ public:
+ I2cOutput() { }
+ enum {
+ buffer_size = output_buffer_size,
+ data_size = 8
+ };
+ typedef typename DataTypeForSize::Type Value;
+};
+
+template
+class I2cInput {
+ public:
+ I2cInput() { }
+ enum {
+ buffer_size = input_buffer_size,
+ data_size = 8
+ };
+ typedef typename DataTypeForSize::Type Value;
+};
+
+// I2C Handler.
+extern void (*i2c_handler_)();
+
+template
+class I2cMaster {
+ public:
+ I2cMaster() { }
+
+ typedef typename DataTypeForSize::data_size>::Type Value;
+
+ static void Init() {
+ // Prescaler is set to a factor of 1.
+ Prescaler0::clear();
+ Prescaler1::clear();
+ TWBR = (F_CPU / frequency - 16) / 2;
+
+ I2cEnable::set();
+ I2cInterrupt::set();
+ I2cAck::set();
+
+ state_ = I2C_STATE_READY;
+ i2c_handler_ = &Handler;
+ }
+
+ static void Done() {
+ I2cInterrupt::clear();
+ I2cEnable::clear();
+ I2cAck::clear();
+ i2c_handler_ = NULL;
+ }
+
+ static uint8_t Wait() {
+ while (state_ != I2C_STATE_READY) { }
+ return error_;
+ }
+
+ static uint8_t Send(uint8_t address) {
+ // The output buffer is empty, no need to do anything.
+ if (!Output::readable()) {
+ return 0;
+ }
+
+ // Sorry, data can be sent only when the line is not busy.
+ if (state_ != I2C_STATE_READY) {
+ return 0;
+ }
+
+ error_ = I2C_ERROR_NONE;
+ state_ = I2C_STATE_RECEIVING;
+ slarw_ = (address << 1) | TW_WRITE;
+
+ uint8_t size = Output::readable();
+ I2cStart::set();
+
+ return size;
+ }
+
+ static uint8_t Request(uint8_t address, uint8_t requested) {
+ // Make sure that we don't request more than the buffer can hold.
+ if (requested >= Input::writable()) {
+ requested = Input::writable() - 1;
+ }
+ // Sorry, data can be requested only when the line is not busy.
+ if (state_ != I2C_STATE_READY) {
+ return 0;
+ }
+
+ error_ = I2C_ERROR_NONE;
+ state_ = I2C_STATE_RECEIVING;
+ slarw_ = (address << 1) | TW_READ;
+ received_ = 0;
+ requested_ = requested;
+ I2cStart::set();
+
+ return requested;
+ }
+
+ // All the read/write operations are done on the buffer, so they do not
+ // block.
+ static inline void Write(Value v) { Output::Write(v); }
+ static inline uint8_t writable() { return Output::writable(); }
+ static inline uint8_t NonBlockingWrite(Value v) {
+ return Output::NonBlockingWrite(v);
+ }
+ static inline void Overwrite(Value v) { Output::Overwrite(v); }
+ static inline Value Read() { return Input::Read(); }
+ static inline uint8_t readable() { return Input::readable(); }
+ static inline int16_t NonBlockingRead() { return Input::NonBlockingRead(); }
+ static inline Value ImmediateRead() { return Input::ImmediateRead(); }
+
+ static inline void FlushInputBuffer() { Input::Flush(); }
+ static inline void FlushOutputBuffer() { Output::Flush(); }
+
+ private:
+ static inline void Continue(uint8_t ack) {
+ if (ack) {
+ TWCR = _BV(TWEN) | _BV(TWIE) | _BV(TWINT) | _BV(TWEA);
+ } else {
+ TWCR = _BV(TWEN) | _BV(TWIE) | _BV(TWINT);
+ }
+ }
+
+ static inline void Stop() {
+ I2cStop::set();
+ while (I2cStop::value()) { }
+ state_ = I2C_STATE_READY;
+ }
+
+ static inline void Abort() {
+ TWCR = _BV(TWEN) | _BV(TWIE) | _BV(TWEA) | _BV(TWINT);
+ state_ = I2C_STATE_READY;
+ }
+
+ static void Handler() {
+ switch (TW_STATUS) {
+ case TW_START:
+ case TW_REP_START:
+ TWDR = slarw_;
+ Continue(1);
+ break;
+
+ case TW_MT_DATA_ACK:
+ case TW_MT_SLA_ACK:
+ if (Output::readable()) {
+ TWDR = Output::ImmediateRead();
+ Continue(1);
+ } else {
+ Stop();
+ }
+ break;
+
+ case TW_MT_SLA_NACK:
+ case TW_MT_DATA_NACK:
+ error_ = TW_STATUS;
+ Stop();
+ break;
+
+ case TW_MT_ARB_LOST:
+ error_ = I2C_ERROR_ARBITRATION_LOST;
+ Abort();
+ break;
+
+ case TW_MR_DATA_ACK:
+ Input::Overwrite(TWDR);
+ ++received_;
+ case TW_MR_SLA_ACK:
+ if (received_ < requested_) {
+ Continue(1);
+ } else {
+ Continue(0);
+ }
+ break;
+
+ case TW_MR_DATA_NACK:
+ Input::Overwrite(TWDR);
+ ++received_;
+ case TW_MR_SLA_NACK:
+ Stop();
+ break;
+
+ case TW_NO_INFO:
+ break;
+
+ case TW_BUS_ERROR:
+ error_ = I2C_ERROR_BUS_ERROR;
+ Stop();
+ break;
+ }
+ }
+
+public:
+ typedef RingBuffer > Input;
+ typedef RingBuffer > Output;
+
+private:
+ static volatile uint8_t state_;
+ static volatile uint8_t error_;
+ static volatile uint8_t slarw_;
+ static volatile uint8_t received_;
+ static uint8_t requested_;
+
+ DISALLOW_COPY_AND_ASSIGN(I2cMaster);
+};
+
+/* static */
+template
+volatile uint8_t I2cMaster::state_;
+
+/* static */
+template
+volatile uint8_t I2cMaster::error_;
+
+/* static */
+template
+volatile uint8_t I2cMaster::slarw_;
+
+/* static */
+template
+volatile uint8_t I2cMaster::received_;
+
+/* static */
+template
+uint8_t I2cMaster::requested_;
+
+} // namespace avrlib
+
+#endif // AVRLIB_I2C_I2C_H_
diff --git a/avrlib/log2.h b/avrlib/log2.h
new file mode 100755
index 0000000..e55eb87
--- /dev/null
+++ b/avrlib/log2.h
@@ -0,0 +1,44 @@
+// Copyright 2009 Olivier Gillet.
+//
+// Author: Olivier Gillet (ol.gillet@gmail.com)
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+// This program 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 General Public License for more details.
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see .
+//
+// -----------------------------------------------------------------------------
+//
+// Template class for converting a power of 2 to its logarithm in base 2.
+// Using log might simply not work for template arguments.
+
+#ifndef AVRLIB_LOG2_
+#define AVRLIB_LOG2_
+
+namespace avrlib {
+
+template
+struct Log2 {
+ enum {
+ value = 0
+ };
+};
+
+template<> struct Log2<1> { enum { value = 0 }; };
+template<> struct Log2<2> { enum { value = 1 }; };
+template<> struct Log2<4> { enum { value = 2 }; };
+template<> struct Log2<8> { enum { value = 3 }; };
+template<> struct Log2<16> { enum { value = 4 }; };
+template<> struct Log2<32> { enum { value = 5 }; };
+template<> struct Log2<64> { enum { value = 6 }; };
+template<> struct Log2<128> { enum { value = 7 }; };
+
+} // namespace avrlib
+
+#endif // AVRLIB_LOG2_
diff --git a/avrlib/makefile.mk b/avrlib/makefile.mk
new file mode 100644
index 0000000..618c63f
--- /dev/null
+++ b/avrlib/makefile.mk
@@ -0,0 +1,172 @@
+# Copyright 2009 Olivier Gillet.
+#
+# Author: Olivier Gillet (ol.gillet@gmail.com)
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+# This program 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 General Public License for more details.
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see .
+
+AVRLIB_TOOLS_PATH = /usr/local/CrossPack-AVR/bin/
+AVRLIB_ETC_PATH = /usr/local/CrossPack-AVR/etc/
+# AVRLIB_TOOLS_PATH = /Applications/Arduino.app/Contents/Resources/Java/hardware/tools/avr/bin/
+# AVRLIB_ETC_PATH = /Applications/Arduino.app/Contents/Resources/Java/hardware/tools/avr/etc/
+BUILD_ROOT = build/
+BUILD_DIR = $(BUILD_ROOT)$(TARGET)/
+PROGRAMMER = avrispmkII
+
+MCU = atmega$(MCU_NAME)p
+DMCU = m$(MCU_NAME)p
+MCU_DEFINE = ATMEGA$(MCU_NAME)P
+F_CPU = 20000000
+
+VPATH = $(PACKAGES)
+CC_FILES = $(notdir $(wildcard $(patsubst %,%/*.cc,$(PACKAGES))))
+AS_FILES = $(notdir $(wildcard $(patsubst %,%/*.as,$(PACKAGES))))
+OBJ_FILES = $(CC_FILES:.cc=.o) $(AS_FILES:.S=.o)
+OBJS = $(patsubst %,$(BUILD_DIR)%,$(OBJ_FILES))
+DEPS = $(OBJS:.o=.d)
+
+TARGET_HEX = $(BUILD_DIR)$(TARGET).hex
+TARGET_ELF = $(BUILD_DIR)$(TARGET).elf
+TARGETS = $(BUILD_DIR)$(TARGET).*
+DEP_FILE = $(BUILD_DIR)depends.mk
+
+CC = $(AVRLIB_TOOLS_PATH)avr-gcc
+CXX = $(AVRLIB_TOOLS_PATH)avr-g++
+OBJCOPY = $(AVRLIB_TOOLS_PATH)avr-objcopy
+OBJDUMP = $(AVRLIB_TOOLS_PATH)avr-objdump
+AR = $(AVRLIB_TOOLS_PATH)avr-ar
+SIZE = $(AVRLIB_TOOLS_PATH)avr-size
+NM = $(AVRLIB_TOOLS_PATH)avr-nm
+AVRDUDE = $(AVRLIB_TOOLS_PATH)avrdude
+REMOVE = rm -f
+CAT = cat
+
+CPPFLAGS = -mmcu=$(MCU) -DF_CPU=$(F_CPU) -I. \
+ -g -Os -w -Wall \
+ -ffunction-sections -fdata-sections -D$(MCU_DEFINE) \
+ -DSERIAL_RX_0 -fno-move-loop-invariants \
+ -mcall-prologues
+CXXFLAGS = -fno-exceptions
+ASFLAGS = -mmcu=$(MCU) -I. -x assembler-with-cpp
+LDFLAGS = -mmcu=$(MCU) -lm -Wl,--gc-sections -Os
+
+# ------------------------------------------------------------------------------
+# Source compiling
+# ------------------------------------------------------------------------------
+
+$(BUILD_DIR)%.o: %.cc
+ $(CXX) -c $(CPPFLAGS) $(CXXFLAGS) $< -o $@
+
+$(BUILD_DIR)%.o: %.s
+ $(CC) -c $(CPPFLAGS) $(ASFLAGS) $< -o $@
+
+$(BUILD_DIR)%.d: %.cc
+ $(CXX) -MM $(CPPFLAGS) $(CXXFLAGS) $< -MF $@ -MT $(@:.d=.o)
+
+$(BUILD_DIR)%.d: %.s
+ $(CC) -MM $(CPPFLAGS) $(ASFLAGS) $< -MF $@ -MT $(@:.d=.o)
+
+
+# ------------------------------------------------------------------------------
+# Object file conversion
+# ------------------------------------------------------------------------------
+
+$(BUILD_DIR)%.hex: $(BUILD_DIR)%.elf
+ $(OBJCOPY) -O ihex -R .eeprom $< $@
+
+$(BUILD_DIR)%.eep: $(BUILD_DIR)%.elf
+ -$(OBJCOPY) -j .eeprom --set-section-flags=.eeprom="alloc,load" \
+ --change-section-lma .eeprom=0 -O ihex $< $@
+
+$(BUILD_DIR)%.lss: $(BUILD_DIR)%.elf
+ $(OBJDUMP) -h -S $< > $@
+
+$(BUILD_DIR)%.sym: $(BUILD_DIR)%.elf
+ $(NM) -n $< > $@
+
+# ------------------------------------------------------------------------------
+# AVRDude
+# ------------------------------------------------------------------------------
+
+AVRDUDE_CONF = $(AVRLIB_ETC_PATH)avrdude.conf
+AVRDUDE_COM_OPTS = -V -p $(DMCU)
+AVRDUDE_COM_OPTS += -C $(AVRDUDE_CONF)
+AVRDUDE_ISP_OPTS = -c $(PROGRAMMER) -P usb
+
+# ------------------------------------------------------------------------------
+# Main targets
+# ------------------------------------------------------------------------------
+
+all: $(BUILD_DIR) $(TARGET_HEX)
+
+$(BUILD_DIR):
+ mkdir -p $(BUILD_DIR)
+
+$(TARGET_ELF): $(OBJS)
+ $(CC) $(LDFLAGS) -o $@ $(OBJS) $(SYS_OBJS)
+
+$(DEP_FILE): $(BUILD_DIR) $(DEPS)
+ cat $(DEPS) > $(DEP_FILE)
+
+upload: $(TARGET_HEX)
+ $(AVRDUDE) $(AVRDUDE_COM_OPTS) $(AVRDUDE_ISP_OPTS) \
+ -U flash:w:$(TARGET_HEX):i
+
+clean:
+ $(REMOVE) $(OBJS) $(TARGETS) $(DEP_FILE) $(DEPS)
+
+depends: $(DEPS)
+ cat $(DEPS) > $(DEP_FILE)
+
+$(TARGET).size: $(TARGET_ELF)
+ $(SIZE) $(TARGET_ELF) > $(TARGET).size
+
+$(BUILD_DIR)$(TARGET).top_symbols: $(TARGET_ELF)
+ $(NM) $(TARGET_ELF) --size-sort -C -f bsd -r > $@
+
+size: $(TARGET).size
+ cat $(TARGET).size | awk '{ print $$1+$$2 }' | tail -n1 | figlet | cowsay -n -f moose
+
+size_report: build/$(TARGET)/$(TARGET).lss build/$(TARGET)/$(TARGET).top_symbols
+
+.PHONY: all clean depends upload
+
+include $(DEP_FILE)
+
+# ------------------------------------------------------------------------------
+# Midi files for firmware update
+# ------------------------------------------------------------------------------
+
+HEX2SYSEX = python hardware/tools/hex2sysex/hex2sysex.py
+
+$(BUILD_DIR)%.mid: $(BUILD_DIR)%.hex
+ $(HEX2SYSEX) -o $@ $<
+
+$(BUILD_DIR)%.syx: $(BUILD_DIR)%.hex
+ $(HEX2SYSEX) --syx -o $@ $<
+
+midi: $(BUILD_DIR)$(TARGET).mid
+
+syx: $(BUILD_DIR)$(TARGET).syx
+
+
+# ------------------------------------------------------------------------------
+# Publish a firmware version on the website
+# ------------------------------------------------------------------------------
+
+REMOTE_HOST = mutable-instruments.net
+REMOTE_USER = shruti
+REMOTE_PATH = public_html/static/firmware
+
+publish: $(BUILD_DIR)$(TARGET).mid $(BUILD_DIR)$(TARGET).hex
+ scp $(BUILD_DIR)$(TARGET).mid $(REMOTE_USER)@$(REMOTE_HOST):$(REMOTE_PATH)/$(TARGET)_$(VERSION).mid
+ scp $(BUILD_DIR)$(TARGET).hex $(REMOTE_USER)@$(REMOTE_HOST):$(REMOTE_PATH)//$(TARGET)_$(VERSION).hex
+ scp $(BUILD_DIR)$(TARGET).syx $(REMOTE_USER)@$(REMOTE_HOST):$(REMOTE_PATH)//$(TARGET)_$(VERSION).syx
diff --git a/avrlib/op.h b/avrlib/op.h
new file mode 100755
index 0000000..af328cd
--- /dev/null
+++ b/avrlib/op.h
@@ -0,0 +1,470 @@
+// Copyright 2009 Olivier Gillet.
+//
+// Author: Olivier Gillet (ol.gillet@gmail.com)
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+// This program 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 General Public License for more details.
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see .
+//
+// -----------------------------------------------------------------------------
+//
+// A set of basic operands, especially useful for fixed-point arithmetic, with
+// fast ASM implementations.
+
+#ifndef AVRLIB_OP_H_
+#define AVRLIB_OP_H_
+
+#include "avrlib/base.h"
+
+namespace avrlib {
+
+static inline int16_t Clip(int16_t value, int16_t min, int16_t max) {
+ return value < min ? min : (value > max ? max : value);
+}
+
+static inline int16_t Clip14(int16_t value) {
+ uint8_t msb = static_cast(value) >> 8;
+ if (msb & 0x80) {
+ return 0;
+ } if (msb & 0x40) {
+ return 16383;
+ }
+ return value;
+}
+
+static inline uint8_t AddClip(uint8_t value, uint8_t increment, uint8_t max) {
+ value += increment;
+ if (value > max) {
+ value = max;
+ }
+ return value;
+}
+
+// Optimized for 0-16384 range.
+static inline uint8_t ShiftRight8(int16_t value) {
+ return static_cast(value) >> 8;
+}
+
+#ifdef USE_OPTIMIZED_OP
+
+static inline uint24c_t Add24Carry(uint24_t a, uint24_t b) {
+ uint16_t a_int = a.integral;
+ uint16_t b_int = b.integral;
+ uint8_t a_frac = a.fractional;
+ uint8_t b_frac = b.fractional;
+ uint8_t a_carry = 0;
+ uint24c_t result;
+ asm(
+ "add %0, %6" "\n\t"
+ "adc %A1, %A7" "\n\t"
+ "adc %B1, %B7" "\n\t"
+ "adc %2, r1" "\n\t"
+ : "=r" (a_frac), "=r" (a_int), "=r" (a_carry)
+ : "0" (a_frac), "1" (a_int), "2" (a_carry), "a" (b_frac), "a" (b_int)
+ );
+ result.integral = a_int;
+ result.fractional = a_frac;
+ result.carry = a_carry;
+ return result;
+}
+
+static inline uint24_t Add24(uint24_t a, uint24_t b) {
+ uint16_t a_int = a.integral;
+ uint16_t b_int = b.integral;
+ uint8_t a_frac = a.fractional;
+ uint8_t b_frac = b.fractional;
+ uint24_t result;
+ asm(
+ "add %0, %4" "\n\t"
+ "adc %A1, %A5" "\n\t"
+ "adc %B1, %B5" "\n\t"
+ : "=r" (a_frac), "=r" (a_int)
+ : "0" (a_frac), "1" (a_int), "a" (b_frac), "a" (b_int)
+ );
+ result.integral = a_int;
+ result.fractional = a_frac;
+ return result;
+}
+
+static inline uint24_t Lsr24(uint24_t a) {
+ uint16_t a_int = a.integral;
+ uint8_t a_frac = a.fractional;
+ uint24_t result;
+ asm(
+ "lsr %B1" "\n\t"
+ "ror %A1" "\n\t"
+ "ror %0" "\n\t"
+ : "=r" (a_frac), "=r" (a_int)
+ : "0" (a_frac), "1" (a_int)
+ );
+ result.integral = a_int;
+ result.fractional = a_frac;
+ return result;
+}
+
+static inline uint24_t Lsl24(uint24_t a) {
+ uint16_t a_int = a.integral;
+ uint8_t a_frac = a.fractional;
+ uint24_t result;
+ asm(
+ "lsl %0" "\n\t"
+ "rol %A1" "\n\t"
+ "rol %B1" "\n\t"
+ : "=r" (a_frac), "=r" (a_int)
+ : "0" (a_frac), "1" (a_int)
+ );
+ result.integral = a_int;
+ result.fractional = a_frac;
+ return result;
+}
+
+static inline uint8_t Clip8(int16_t value) {
+ uint8_t result;
+ asm(
+ "mov %0, %A1" "\n\t" // by default, copy the value.
+ "or %B1, %B1" "\n\t" // load H to set flags.
+ "brpl .+4" "\n\t" // if positive, skip
+ "ldi %0, 0" "\n\t" // set to 0.
+ "rjmp .+4" "\n\t" // and jump.
+ "breq .+2" "\n\t" // if null, skip
+ "ldi %0, 255" "\n\t" // set to 255
+ : "=r" (result)
+ : "a" (value)
+ );
+ return result;
+}
+
+static inline int8_t SignedClip8(int16_t value) {
+ return Clip8(value + 128) + 128;
+}
+
+static inline uint8_t Mix(uint8_t a, uint8_t b, uint8_t balance) {
+ Word sum;
+ asm(
+ "mul %3, %2" "\n\t" // b * balance
+ "movw %A0, r0" "\n\t" // to sum
+ "com %2" "\n\t" // 255 - balance
+ "mul %1, %2" "\n\t" // a * (255 - balance)
+ "com %2" "\n\t" // reset balance to its previous value
+ "add %A0, r0" "\n\t" // add to sum L
+ "adc %B0, r1" "\n\t" // add to sum H
+ "eor r1, r1" "\n\t" // reset r1 after multiplication
+ : "&=r" (sum)
+ : "a" (a), "a" (balance), "a" (b)
+ );
+ return sum.bytes[1];
+}
+
+static inline uint8_t Mix(uint8_t a, uint8_t b, uint8_t gain_a, uint8_t gain_b) {
+ Word sum;
+ asm(
+ "mul %3, %4" "\n\t" // b * gain_b
+ "movw %A0, r0" "\n\t" // to sum
+ "mul %1, %2" "\n\t" // a * gain_a
+ "add %A0, r0" "\n\t" // add to sum L
+ "adc %B0, r1" "\n\t" // add to sum H
+ "eor r1, r1" "\n\t" // reset r1 after multiplication
+ : "&=r" (sum)
+ : "a" (a), "a" (gain_a), "a" (b), "a" (gain_b)
+ );
+ return sum.bytes[1];
+}
+
+static inline uint16_t Mix16(uint8_t a, uint8_t b, uint8_t balance) {
+ Word sum;
+ asm(
+ "mul %3, %2" "\n\t" // b * balance
+ "movw %A0, r0" "\n\t" // to sum
+ "com %2" "\n\t" // 255 - balance
+ "mul %1, %2" "\n\t" // a * (255 - balance)
+ "com %2" "\n\t" // reset balance to its previous value
+ "add %A0, r0" "\n\t" // add to sum L
+ "adc %B0, r1" "\n\t" // add to sum H
+ "eor r1, r1" "\n\t" // reset r1 after multiplication
+ : "&=r" (sum)
+ : "a" (a), "a" (balance), "a" (b)
+ );
+ return sum.value;
+}
+
+static inline uint8_t Mix4(uint8_t a, uint8_t b, uint8_t balance) {
+ uint16_t sum;
+ asm(
+ "mul %2, %1" "\n\t" // b * balance
+ "movw %A3, r0" "\n\t" // to sum
+ "com %1" "\n\t" // 255 - balance
+ "subi %1, 240" "\n\t" // 15 - balance
+ "mul %0, %1" "\n\t" // a * (15 - balance)
+ "subi %1, 16" "\n\t"
+ "com %1" "\n\t" // reset balance to its previous value
+ "add %A3, r0" "\n\t" // add to sum L
+ "adc %B3, r1" "\n\t" // add to sum H
+ "eor r1, r1" "\n\t" // reset r1 after multiplication
+ "andi %B3, 15" "\n\t" // keep 4 lowest bits of H
+ "andi %A3, 240" "\n\t" // keep 4 highest bits of L
+ "or %B3, %A3" "\n\t" // copy 4 high bits of L to H -> LLLLHHHH
+ "swap %B3" "\n\t" // swap to get HHHHLLLL
+ "mov %0, %B3" "\n\t" // move to output
+ : "=r" (a)
+ : "a" (balance), "a" (b), "a" (sum)
+ );
+ return a;
+}
+
+static inline uint16_t UnscaledMix4(uint8_t a, uint8_t b, uint8_t balance) {
+ uint16_t sum;
+ asm(
+ "mul %3, %2" "\n\t" // b * balance
+ "movw %A0, r0" "\n\t" // to sum
+ "com %2" "\n\t" // 255 - balance
+ "subi %2, 240" "\n\t" // 15 - balance
+ "mul %1, %2" "\n\t" // a * (15 - balance)
+ "subi %2, 16" "\n\t"
+ "com %2" "\n\t" // reset balance to its previous value
+ "add %A0, r0" "\n\t" // add to sum L
+ "adc %B0, r1" "\n\t" // add to sum H
+ "eor r1, r1" "\n\t" // reset r1 after multiplication
+ : "&=r" (sum)
+ : "a" (a), "a" (balance), "a" (b)
+ );
+ return sum;
+}
+
+static inline uint8_t ShiftLeft4(uint8_t a) {
+ uint8_t result;
+ asm(
+ "mov %0, %1" "\n\t"
+ "swap %0" "\n\t"
+ "andi %0, 240" "\n\t"
+ : "=r" (result)
+ : "a" (a)
+ );
+ return result;
+}
+
+static inline uint8_t Swap4(uint8_t a) {
+ uint8_t result;
+ asm(
+ "mov %0, %1" "\n\t"
+ "swap %0" "\n\t"
+ : "=r" (result)
+ : "a" (a)
+ );
+ return result;
+}
+
+static inline uint8_t ShiftRight4(uint8_t a) {
+ uint8_t result;
+ asm(
+ "mov %0, %1" "\n\t"
+ "swap %0" "\n\t"
+ "andi %0, 15" "\n\t"
+ : "=r" (result)
+ : "a" (a)
+ );
+ return result;
+}
+
+static inline uint8_t MulScale8(uint8_t a, uint8_t b) {
+ uint8_t result;
+ asm(
+ "mul %1, %2" "\n\t"
+ "mov %0, r1" "\n\t"
+ "eor r1, r1" "\n\t"
+ : "=r" (result)
+ : "a" (a), "a" (b)
+ );
+ return result;
+}
+
+static inline int8_t SignedMulScale8(int8_t a, uint8_t b) {
+ uint8_t result;
+ asm(
+ "mulsu %1, %2" "\n\t"
+ "mov %0, r1" "\n\t"
+ "eor r1, r1" "\n\t"
+ : "=r" (result)
+ : "a" (a), "a" (b)
+ );
+ return result;
+}
+
+static inline int16_t SignedUnsignedMul(int8_t a, uint8_t b) {
+ int16_t result;
+ asm(
+ "mulsu %1, %2" "\n\t"
+ "movw %0, r0" "\n\t"
+ "eor r1, r1" "\n\t"
+ : "=r" (result)
+ : "a" (a), "a" (b)
+ );
+ return result;
+}
+
+static inline uint16_t UnsignedUnsignedMul(uint8_t a, uint8_t b) {
+ uint16_t result;
+ asm(
+ "mul %1, %2" "\n\t"
+ "movw %0, r0" "\n\t"
+ "eor r1, r1" "\n\t"
+ : "=r" (result)
+ : "a" (a), "a" (b)
+ );
+ return result;
+}
+
+static inline int8_t SignedSignedMulScale8(int8_t a, int8_t b) {
+ uint8_t result;
+ asm(
+ "muls %1, %2" "\n\t"
+ "mov %0, r1" "\n\t"
+ "eor r1, r1" "\n\t"
+ : "=r" (result)
+ : "a" (a), "a" (b)
+ );
+ return result;
+}
+
+static inline uint16_t Mul16Scale8(uint16_t a, uint16_t b) {
+ uint16_t result;
+ uint32_t product;
+ asm(
+ "mul %A2, %A3" "\n\t"
+ "movw %A1, r0" "\n\t"
+ "mul %B2, %B3" "\n\t"
+ "movw %C1, r0" "\n\t"
+ "mul %B3, %A2" "\n\t"
+ "add %B1, r0" "\n\t"
+ "adc %C1, r1" "\n\t"
+ "eor r1, r1" "\n\t"
+ "adc %D1, r1" "\n\t"
+ "mul %B2, %A3" "\n\t"
+ "add %B1, r0" "\n\t"
+ "adc %C1, r1" "\n\t"
+ "eor r1, r1" "\n\t"
+ "adc %D1, r1" "\n\t"
+ "mov %A0, %B1" "\n\t"
+ "mov %B0, %C1" "\n\t"
+ : "=r" (result), "=&r" (product)
+ : "a" (a), "a" (b)
+ );
+ return result;
+}
+
+// The code generated by gcc for >> 6 is short but uses a loop. This saves
+// a couple of cycles. Note that this solution only works for operands with
+// a 14-bits resolution.
+static inline uint8_t ShiftRight6(uint16_t value) {
+ uint8_t b = value >> 8;
+ uint8_t a = value & 0xff;
+ uint8_t result;
+ asm(
+ "add %1, %1" "\n\t"
+ "adc %2, %2" "\n\t"
+ "add %1, %1" "\n\t"
+ "adc %2, %2" "\n\t"
+ : "=r" (result)
+ : "a" (a), "0" (b)
+ );
+ return result;
+}
+
+static inline uint8_t ShiftRight7(uint16_t value) {
+ uint8_t b = value >> 8;
+ uint8_t a = value & 0xff;
+ uint8_t result;
+ asm(
+ "add %1, %1" "\n\t"
+ "adc %2, %2" "\n\t"
+ : "=r" (result)
+ : "a" (a), "0" (b)
+ );
+ return result;
+}
+
+#else
+
+static inline uint8_t Clip8(int16_t value) {
+ return value < 0 ? 0 : (value > 255 ? 255 : value);
+}
+
+static inline int8_t SignedClip8(int16_t value) {
+ return value < -128 ? -128 : (value > 127 ? 127 : value);
+}
+
+static inline uint8_t Mix(uint8_t a, uint8_t b, uint8_t balance) {
+ return a * (255 - balance) + b * balance >> 8;
+}
+
+static inline uint8_t Mix(uint8_t a, uint8_t b, uint8_t gain_a, uint8_t gain_b) {
+ return a * gain_a + b * gain_b >> 8;
+}
+
+static inline uint16_t Mix16(uint8_t a, uint8_t b, uint8_t balance) {
+ return a * (255 - balance) + b * balance;
+}
+
+static inline uint8_t Mix4(uint8_t a, uint8_t b, uint8_t balance) {
+ return a * (15 - balance) + b * balance >> 4;
+}
+
+static inline uint8_t UnscaledMix4(uint8_t a, uint8_t b, uint8_t balance) {
+ return a * (15 - balance) + b * balance;
+}
+
+static inline uint8_t ShiftRight4(uint8_t a) {
+ return a >> 4;
+}
+
+static inline uint8_t ShiftLeft4(uint8_t a) {
+ return a << 4;
+}
+
+static inline uint8_t Swap4(uint8_t a) {
+ return (a << 4) | (a >> 4);
+}
+
+static inline uint8_t MulScale8(uint8_t a, uint8_t b) {
+ return a * b >> 8;
+}
+
+static inline int8_t SignedMulScale8(int8_t a, uint8_t b) {
+ return a * b >> 8;
+}
+
+static inline int16_t SignedUnsignedMul(int8_t a, uint8_t b) {
+ return a * b;
+}
+
+static inline uint16_t UnsignedUnsignedMul(uint8_t a, uint8_t b) {
+ return a * b;
+}
+
+static inline int8_t SignedSignedMulScale8(int8_t a, int8_t b) {
+ return a * b >> 8;
+}
+
+static inline uint16_t Mul16Scale8(uint16_t a, uint16_t b) {
+ return static_cast(a) * b >> 8;
+}
+
+static inline uint8_t ShiftRight6(uint16_t value) {
+ return value >> 6;
+}
+
+static inline uint8_t ShiftRight7(uint16_t value) {
+ return value >> 7;
+}
+
+#endif // USE_OPTIMIZED_OP
+
+} // namespace avrlib
+
+#endif // AVRLIB_OP_H_
\ No newline at end of file
diff --git a/avrlib/output_stream.h b/avrlib/output_stream.h
new file mode 100755
index 0000000..40f5f12
--- /dev/null
+++ b/avrlib/output_stream.h
@@ -0,0 +1,77 @@
+// Copyright 2009 Olivier Gillet.
+//
+// Author: Olivier Gillet (ol.gillet@gmail.com)
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+// This program 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 General Public License for more details.
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see .
+//
+// -----------------------------------------------------------------------------
+//
+// Output stream. Wraps any module implementing the basic Output interface
+// (in fact, just a Write method), and provide string and integer formatting
+// using the << stream operator.
+
+#ifndef AVRLIB_OUTPUT_STREAM_H_
+#define AVRLIB_OUTPUT_STREAM_H_
+
+#include "avrlib/string.h"
+
+using avrlib::Itoa;
+using avrlib::TypeInfo;
+
+namespace avrlib {
+
+enum EndOfLine {
+ endl = 0
+};
+
+template
+struct OutputStream {
+ static void Print(const char* string) {
+ while (*string) {
+ Output::Write(*string++);
+ }
+ }
+ static void Print(uint8_t byte) {
+ Output::Write(byte);
+ }
+ static void Print(char byte) {
+ Output::Write(byte);
+ }
+ static void Print(uint16_t value) {
+ char buffer[TypeInfo::max_size + 1];
+ Itoa(value, TypeInfo::max_size + 1, buffer);
+ Print(buffer);
+ }
+ static void Print(int16_t value) {
+ char buffer[TypeInfo::max_size + 1];
+ Itoa(value, TypeInfo::max_size + 1, buffer);
+ Print(buffer);
+ }
+ static void Print(uint32_t value) {
+ char buffer[TypeInfo::max_size + 1];
+ Itoa(value, TypeInfo::max_size + 1, buffer);
+ Print(buffer);
+ }
+ static void Print(EndOfLine e) {
+ Print('\r');
+ Print('\n');
+ }
+ template
+ inline OutputStream