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& operator<<(const T value) { + Print(value); + return *this; + } +}; + +} // namespace avrlib + +#endif // AVRLIB_OUTPUT_STREAM_H_ diff --git a/avrlib/parallel_io.h b/avrlib/parallel_io.h new file mode 100755 index 0000000..82e221b --- /dev/null +++ b/avrlib/parallel_io.h @@ -0,0 +1,89 @@ +// 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 . +// +// ----------------------------------------------------------------------------- +// +// Templates for using full ports or half-ports for parallel output + +#ifndef AVRLIB_PARALLEL_H_ +#define AVRLIB_PARALLEL_H_ + +#include + +#include "avrlib/gpio.h" + +namespace avrlib { + +enum ParallelPortMode { + PARALLEL_BYTE, + PARALLEL_NIBBLE_HIGH, + PARALLEL_NIBBLE_LOW +}; + +template +struct ShiftMasks { + enum Masks { + mask = 0xff, + shift = 0 + }; +}; + +template<> +struct ShiftMasks { + enum Masks { + mask = 0xf0, + shift = 4, + }; +}; + +template<> +struct ShiftMasks { + enum Masks { + mask = 0x0f, + shift = 0, + }; +}; + +template +struct ParallelPort { + typedef ShiftMasks Masks; + + // Mode change. + static inline void set_mode(uint8_t mode) { + uint8_t preserve = (*Port::Mode::ptr() & ~Masks::mask); + if (mode == DIGITAL_INPUT) { + *Port::Mode::ptr() = preserve; + } else if (mode == DIGITAL_OUTPUT) { + *Port::Mode::ptr() = preserve | Masks::mask; + } + } + + static inline void Write(uint8_t value) { + uint8_t preserve = *Port::Output::ptr() & ~Masks::mask; + *Port::Output::ptr() = preserve | (value << Masks::shift); + } + + static inline void EnablePullUpResistors() { + Write(Masks::mask); + } + + static inline void Read(uint8_t value) { + return (*Port::Input::ptr() & Masks::mask) >> Masks::shift; + } +}; + +} // namespace avrlib + +#endif // AVRLIB_PARALLEL_H_ diff --git a/avrlib/random.cc b/avrlib/random.cc new file mode 100755 index 0000000..3b0d646 --- /dev/null +++ b/avrlib/random.cc @@ -0,0 +1,27 @@ +// 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 . +// +// ----------------------------------------------------------------------------- +// +// Random number generator. + +#include "avrlib/random.h" + +namespace avrlib { + +/* static */ +uint16_t Random::rng_state_ = 0x21; + +} // namespace avrlib diff --git a/avrlib/random.h b/avrlib/random.h new file mode 100755 index 0000000..f8cc202 --- /dev/null +++ b/avrlib/random.h @@ -0,0 +1,54 @@ +// 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 . +// +// ----------------------------------------------------------------------------- +// +// Fast 16-bit pseudo random number generator. + +#ifndef AVRLIB_RANDOM_H_ +#define AVRLIB_RANDOM_H_ + +#include "avrlib/base.h" + +namespace avrlib { + +class Random { + public: + static void Update() { + // Galois LFSR with feedback polynomial = x^16 + x^14 + x^13 + x^11. + // Period: 65535. + rng_state_ = (rng_state_ >> 1) ^ (-(rng_state_ & 1) & 0xb400); + } + + static inline uint16_t state() { return rng_state_; } + + static inline uint8_t state_msb() { + return static_cast(rng_state_ >> 8); + } + + static inline uint8_t GetByte() { + Update(); + return state_msb(); + } + + private: + static uint16_t rng_state_; + + DISALLOW_COPY_AND_ASSIGN(Random); +}; + +} // namespace avrlib + +#endif // AVRLIB_RANDOM_H_ diff --git a/avrlib/resources_manager.h b/avrlib/resources_manager.h new file mode 100755 index 0000000..3391074 --- /dev/null +++ b/avrlib/resources_manager.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 . +// +// ----------------------------------------------------------------------------- +// +// Resources manager. Support for lookup of values/strings in tables. Since +// one might not want this functionality and just use the plain program memory +// read/write function, an alias for a stripped down version without string +// table lookup is provided (SimpleResourcesManager). + +#ifndef AVRLIB_RESOURCES_MANAGER_H_ +#define AVRLIB_RESOURCES_MANAGER_H_ + +#include "avrlib/base.h" + +#include +#include + +namespace avrlib { + +template +struct ResourcesTables { + static inline const prog_char** string_table() { return strings; } + static inline const prog_uint16_t** lookup_table_table() { + return lookup_tables; + } +}; + +struct NoResourcesTables { + static inline const prog_char** string_table() { return NULL; } + static inline const prog_uint16_t** lookup_table_table() { return NULL; } +}; + +template +class ResourcesManager { + public: + static inline void LoadStringResource(ResourceId resource, char* buffer, + uint8_t buffer_size) { + if (!Tables::string_table()) { + return; + } + char* address = (char*)(pgm_read_word(&(Tables::string_table()[resource]))); + strncpy_P(buffer, address, buffer_size); + } + + template + static inline ResultType Lookup(ResourceId resource, IndexType i) { + if (!Tables::lookup_table_table()) { + return 0; + }; + uint16_t* address = (uint16_t*)( + pgm_read_word(&(Tables::lookup_table_table()[resource]))); + return ResultType(pgm_read_word(address + i)); + } + + template + static inline ResultType Lookup(const prog_char* p, IndexType i) { + return ResultType(pgm_read_byte(p + i)); + } + + template + static inline ResultType Lookup(const prog_uint8_t* p, IndexType i) { + return ResultType(pgm_read_byte(p + i)); + } + + template + static inline ResultType Lookup(const prog_uint16_t* p, IndexType i) { + return ResultType(pgm_read_word(p + i)); + } + + template + static void Load(const prog_char* p, uint8_t i, T* destination) { + memcpy_P(destination, p + i * sizeof(T), sizeof(T)); + } + + template + static void Load(const T* p, uint8_t* destination, uint16_t size) { + memcpy_P(destination, p, size); + } +}; + +typedef ResourcesManager<> SimpleResourcesManager; + +} // namespace avrlib + +#endif // AVRLIB_RESOURCES_MANAGER_H_ diff --git a/avrlib/ring_buffer.h b/avrlib/ring_buffer.h new file mode 100755 index 0000000..6d3cbef --- /dev/null +++ b/avrlib/ring_buffer.h @@ -0,0 +1,110 @@ +// 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_RING_BUFFER_H_ +#define AVRLIB_RING_BUFFER_H_ + +#include "avrlib/base.h" +#include "avrlib/avrlib.h" + +namespace avrlib { + +// Circular buffer, used for example for Serial input, Software serial output, +// Audio rendering... A buffer is created for each Owner - for example, +// Buffer represents the audio buffer used by AudioClient. +template +class RingBuffer : public Input, Output { + public: + typedef typename Owner::Value Value; + enum { + size = Owner::buffer_size, + data_size = Owner::data_size + }; + + RingBuffer() { } + + static inline uint8_t capacity() { return size; } + static inline void Write(Value v) { + while (!writable()); + Overwrite(v); + } + static inline uint8_t writable() { + return (read_ptr_ - write_ptr_ - 1) & (size - 1); + } + static inline uint8_t NonBlockingWrite(Value v) { + if (writable()) { + Overwrite(v); + return 1; + } else { + return 0; + } + } + static inline void Overwrite(Value v) { + uint8_t w = write_ptr_; + buffer_[w] = v; + write_ptr_ = (w + 1) & (size - 1); + } + static void Overwrite2(Value v1, Value v2) { + uint8_t w = write_ptr_; + buffer_[w] = v1; + buffer_[w + 1] = v2; + write_ptr_ = (w + 2) & (size - 1); + } + + static inline uint8_t Requested() { return 0; } + static inline Value Read() { + while (!readable()); + return ImmediateRead(); + } + static inline uint8_t readable() { + return (write_ptr_ - read_ptr_) & (size - 1); + } + static inline int16_t NonBlockingRead() { + if (readable()) { + return ImmediateRead(); + } else { + return -1; + } + } + static inline Value ImmediateRead() { + uint8_t r = read_ptr_; + Value result = buffer_[r]; + read_ptr_ = (r + 1) & (size - 1); + return result; + } + static inline void Flush() { + write_ptr_ = read_ptr_; + } + private: + static Value buffer_[size]; + static volatile uint8_t read_ptr_; + static volatile uint8_t write_ptr_; + + DISALLOW_COPY_AND_ASSIGN(RingBuffer); +}; + +// Static variables created for each buffer. +template volatile uint8_t RingBuffer::read_ptr_ = 0; +template volatile uint8_t RingBuffer::write_ptr_ = 0; +template typename T::Value RingBuffer::buffer_[]; + +} // namespace avrlib + +#endif // AVRLIB_RING_BUFFER_H_ diff --git a/avrlib/serial.cc b/avrlib/serial.cc new file mode 100755 index 0000000..14ef931 --- /dev/null +++ b/avrlib/serial.cc @@ -0,0 +1,43 @@ +// 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 . +// +// ----------------------------------------------------------------------------- +// +// Fast serial (for the onboard UART), using compile time optimizations. + +#include "avrlib/serial.h" + +#include + +#include "avrlib/gpio.h" + +using namespace avrlib; + +#ifdef SERIAL_RX_0 + +ISR(USART0_RX_vect) { + SerialInput::Received(); +} + +#endif // SERIAL_RX_0 + + +#ifdef SERIAL_RX_1 + +ISR(USART1_RX_vect) { + SerialInput::Received(); +} + +#endif // SERIAL_RX_1 diff --git a/avrlib/serial.h b/avrlib/serial.h new file mode 100755 index 0000000..6d58e19 --- /dev/null +++ b/avrlib/serial.h @@ -0,0 +1,288 @@ +// 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 . +// +// ----------------------------------------------------------------------------- +// +// Fast serial (for the onboard UART), using compile time optimizations. +// +// Can be used in buffered mode, or in polled mode (to save space or avoid +// interruptions if there is an even higher priority task than the serial I/O). +// +// Usage: +// +// Set-up: +// typedef Serial Serial; +// then, in the setup() hook: Serial::Init() +// +// Write: +// Serial::Write(40) // Will block until data is written. +// write_has_succeeded = Serial::NonBlockingWrite(40) // Will not block. +// +// Buffer manipulation (for buffered I/O): +// Serial::available() // Number of bytes ready to be read. For polled read too +// my_value = Serial::Read() // Will wait until data arrives. +// my_value = Serial::NonBlockingRead() // Will return -1 if no data is there. +// my_value = Serial::ImmediateRead() // Assumes you are sure about what you +// are doing and you know that data is here. +// +// Flushing a buffer: +// Serial::InputBuffer::Flush() +// +// TODO(pichenettes): Buffered writes not supported for now (should look up +// the right interrupt handler). + +#ifndef AVRLIB_SERIAL_H_ +#define AVRLIB_SERIAL_H_ + +#include "avrlib/avrlib.h" +#include "avrlib/gpio.h" +#include "avrlib/ring_buffer.h" + +namespace avrlib { + +const uint8_t kSerialOutputBufferSize = 32; +const uint8_t kSerialInputBufferSize = 32; + +// Low-level interface to the low-level UART registers. Several specializations +// may be declared for each serial port. This class could theoretically be used +// for non-blocking write or polling reads. +template +struct SerialPort { + typedef TxEnableBit Tx; + typedef RxEnableBit Rx; + typedef RxInterruptBit RxInterrupt; + typedef TurboBit Turbo; + enum { + input_buffer_size = input_buffer_size_, + output_buffer_size = output_buffer_size_ + }; + static inline void set_prescaler(uint16_t value) { + *PrescalerRegisterH::ptr() = value >> 8; + *PrescalerRegisterL::ptr() = value; + } + static inline uint8_t tx_ready() { return TxReadyBit::value(); } + static inline uint8_t rx_ready() { return RxReadyBit::value(); } + static inline uint8_t data() { return *DataRegister::ptr(); } + static inline void set_data(uint8_t value) { *DataRegister::ptr() = value; } +}; + +template +struct SerialInput : public Input { + enum { + buffer_size = SerialPort::input_buffer_size, + data_size = 8 + }; + 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 SerialPort::rx_ready(); } + + // 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 SerialPort::data(); } + + // Called in data reception interrupt. + static inline void Received() { + if (!readable()) { + return; + } + // This will discard data if the buffer is full. + RingBuffer >::NonBlockingWrite(ImmediateRead()); + } +}; + +template +struct SerialOutput : public Output { + enum { + buffer_size = SerialPort::output_buffer_size, + data_size = 8 + }; + 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 SerialPort::tx_ready(); } + + // 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 v) { SerialPort::set_data(v); } + + // Called in data emission interrupt. + static inline Value Requested() { + Value v = RingBuffer >::NonBlockingRead(); + if (v >= 0) { + Overwrite(v); + } + } +}; + +template +struct SerialImplementation { }; + +template +struct SerialImplementation { + typedef InputOutput IO; +}; +template +struct SerialImplementation { + typedef InputOutput > IO; +}; +template +struct SerialImplementation { + typedef RingBuffer > OutputBuffer; + typedef InputOutput IO; +}; +template +struct SerialImplementation { + typedef InputOutput, DisabledOutput> IO; +}; +template +struct SerialImplementation { + typedef InputOutput, SerialOutput > IO; +}; +template +struct SerialImplementation { + typedef RingBuffer > OutputBuffer; + typedef InputOutput, OutputBuffer> IO; +}; +template +struct SerialImplementation { + typedef RingBuffer > InputBuffer; + typedef InputOutput IO; +}; +template +struct SerialImplementation { + typedef RingBuffer > InputBuffer; + typedef InputOutput > IO; +}; +template +struct SerialImplementation { + typedef RingBuffer > InputBuffer; + typedef RingBuffer > OutputBuffer; + typedef InputOutput IO; +}; + +template +struct Serial { + typedef SerialImplementation Impl; + typedef uint8_t Value; + typedef typename Impl::IO::Input Input; + typedef typename Impl::IO::Output Output; + static inline void Init() { + Init(); + } + template + static inline void Init() { + if (turbo) { + SerialPort::Turbo::set(); + uint16_t prescaler = F_CPU / (8L * baud_rate) - 1; + SerialPort::set_prescaler(prescaler); + } else { + SerialPort::Turbo::clear(); + uint16_t prescaler = F_CPU / (16 * baud_rate) - 1; + SerialPort::set_prescaler(prescaler); + } + if (output != DISABLED) { + SerialPort::Tx::set(); + } + if (input != DISABLED) { + SerialPort::Rx::set(); + } + if (input == BUFFERED) { + SerialPort::RxInterrupt::set(); + } + } + static inline void Write(Value v) { Impl::IO::Write(v); } + static inline uint8_t writable() { return Impl::IO::writable(); } + static inline uint8_t NonBlockingWrite(Value v) { + return Impl::IO::NonBlockingWrite(v); + } + static inline void Overwrite(Value v) { Impl::IO::Overwrite(v); } + static inline Value Read() { return Impl::IO::Read(); } + static inline uint8_t readable() { return Impl::IO::readable(); } + static inline int16_t NonBlockingRead() { + return Impl::IO::NonBlockingRead(); + } + static inline Value ImmediateRead() { return Impl::IO::ImmediateRead(); } +}; + +IORegister(UBRR0H); +IORegister(UBRR0L); +IORegister(UCSR0A); +IORegister(UCSR0B); +IORegister(UDR0); + +typedef SerialPort< + BitInRegister, + BitInRegister, + BitInRegister, + BitInRegister, + BitInRegister, + BitInRegister, + UBRR0HRegister, + UBRR0LRegister, + UDR0Register, + kSerialOutputBufferSize, + kSerialInputBufferSize> SerialPort0; + +#ifndef ATMEGA328P + +IORegister(UBRR1H); +IORegister(UBRR1L); +IORegister(UCSR1A); +IORegister(UCSR1B); +IORegister(UDR1); + +typedef SerialPort< + BitInRegister, + BitInRegister, + BitInRegister, + BitInRegister, + BitInRegister, + BitInRegister, + UBRR1HRegister, + UBRR1LRegister, + UDR1Register, + kSerialOutputBufferSize, + kSerialInputBufferSize> SerialPort1; + +#endif // ATMEGA328P + +} // namespace avrlib + +#endif AVRLIB_SERIAL_H_ diff --git a/avrlib/size_to_type.h b/avrlib/size_to_type.h new file mode 100755 index 0000000..52da376 --- /dev/null +++ b/avrlib/size_to_type.h @@ -0,0 +1,42 @@ +// 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 an integer template argument to the +// corresponding size type. + +#ifndef AVRLIB_SIZE_TO_TYPE_H_ +#define AVRLIB_SIZE_TO_TYPE_H_ + +namespace avrlib { + +template +struct DataTypeForSize { + typedef uint16_t Type; +}; + +template<> struct DataTypeForSize<1> { typedef uint8_t Type; }; +template<> struct DataTypeForSize<2> { typedef uint8_t Type; }; +template<> struct DataTypeForSize<3> { typedef uint8_t Type; }; +template<> struct DataTypeForSize<4> { typedef uint8_t Type; }; +template<> struct DataTypeForSize<5> { typedef uint8_t Type; }; +template<> struct DataTypeForSize<6> { typedef uint8_t Type; }; +template<> struct DataTypeForSize<7> { typedef uint8_t Type; }; +template<> struct DataTypeForSize<8> { typedef uint8_t Type; }; + +} // namespace avrlib + +#endif // AVRLIB_SIZE_TO_TYPE_H_ diff --git a/avrlib/software_serial.h b/avrlib/software_serial.h new file mode 100755 index 0000000..f8a264e --- /dev/null +++ b/avrlib/software_serial.h @@ -0,0 +1,186 @@ +// 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 . +// +// ----------------------------------------------------------------------------- +// +// BufferedSoftwareSerialOutput, the name says it all: +// - It is software serial. +// - Contrary to the other Software Serial implementations out there which use +// fine-tuned busy loop to delay between bits, this implementation bangs bits +// in a timer interrupt ; while writes are done in an asynchronous, non-blocking +// way to a buffer. +// +// Caveats: +// - The timer rate must be a multiple of the baud rate otherwise this will +// miserably FAIL. +// - Tested only for 2400 (main timer at 31250 Hz) and 4800 (main timer at +// 62500 Hz) baud per seconds. +// - Please check the timing tolerance of your device's UART! +// +// SoftwareSerialOutput is a Vanilla blocking implementation, taken from the +// Arduino libs ; Copyright (c) 2006 David A. Mellis. + +#ifndef AVRLIB_SOFTWARE_SERIAL_H_ +#define AVRLIB_SOFTWARE_SERIAL_H_ + +#include "avrlib/avrlib.h" +#include "avrlib/gpio.h" +#include "avrlib/ring_buffer.h" + +namespace avrlib { + +enum SoftwareSerialState { + START_BIT = 0, + END_BIT = 9, + NEXT_BYTE = 10 +}; + +// Parameters: +// TxPin: digital pin used for transmission. +// timer_rate: frequency (Hz) of the timer which will drive the output. +// baud_rate: target baud rate. must be a divisor of timer_rate. +// buffer_size: prefered buffer size. +template +class BufferedSoftwareSerialOutput { + typedef BufferedSoftwareSerialOutput Me; + typedef RingBuffer OutputBuffer; + public: + typedef uint8_t Value; + enum { + prescaler_reset_value = timer_rate / baud_rate, + buffer_size = buffer_size_, + data_size = 8 + }; + static void Init() { + prescaler_counter_ = prescaler_reset_value; + tx_state_ = NEXT_BYTE; + TxPin::set_mode(DIGITAL_OUTPUT); + TxPin::High(); + } + static inline void Write(Value v) { + OutputBuffer::Write(v); + } + static inline uint8_t writable() { return OutputBuffer::writable(); } + static inline uint8_t NonBlockingWrite(Value v ) { + return OutputBuffer::NonBlockingWrite(v); + } + static inline void Overwrite(Value v) { OutputBuffer::Overwrite(v); } + static inline void Tick() { + --prescaler_counter_; + if (prescaler_counter_ > 0) { + return; + } + prescaler_counter_ = prescaler_reset_value; + // Check in which stage of the transmission we are. + if (tx_state_ == NEXT_BYTE) { + // Attempt to start the transmission of the next byte in the buffer, (if + // there is any). + int16_t next_byte = OutputBuffer::NonBlockingRead(); + if (next_byte >= 0) { + tx_byte_ = next_byte; + tx_state_ = START_BIT; + return; + } else { + return; + } + } else if (tx_state_ == START_BIT) { + TxPin::Low(); + tx_symbol_mask_ = 1; + } else if (tx_state_ == END_BIT) { + TxPin::High(); + } else { + TxPin::set_value(tx_byte_ & tx_symbol_mask_); + tx_symbol_mask_ <<= 1; + } + ++tx_state_; + } + + private: + static uint8_t prescaler_counter_; + + // Mask shifted every symbol, used to extract bits from the current character + // to transmit. + static uint8_t tx_symbol_mask_; + + static uint8_t tx_state_; + static uint8_t tx_byte_; + + DISALLOW_COPY_AND_ASSIGN(BufferedSoftwareSerialOutput); +}; + +template +uint8_t BufferedSoftwareSerialOutput::prescaler_counter_; + +template +uint8_t BufferedSoftwareSerialOutput::tx_symbol_mask_; + +template +uint8_t BufferedSoftwareSerialOutput::tx_state_; + +template +uint8_t BufferedSoftwareSerialOutput::tx_byte_; + + +// Parameters: +// TxPin: digital pin used for transmission. +// baud_rate: target baud rate. must be a divisor of timer_rate. +// Following code from NewSoftSerial, Copyright (c) 2006 David A. Mellis. +template +struct SoftwareSerialOutput { + static void Write(uint8_t tx_byte) { + uint8_t oldSREG = SREG; + cli(); + + TxPin::set_mode(DIGITAL_OUTPUT); + uint16_t delay = (F_CPU / baud_rate) / 7; + uint16_t tx_delay = delay - 5; + TxPin::Low(); + TunedDelay(delay); + for (uint8_t mask = 1; mask; mask <<= 1) { + TxPin::set_value(tx_byte & mask); + TunedDelay(tx_delay); + } + TxPin::High(); + SREG = oldSREG; + TunedDelay(delay); + } + + static inline void TunedDelay(uint16_t delay) { + uint8_t tmp = 0; + asm volatile( + "sbiw %0, 0x01" "\n\t" + "ldi %1, 0xff" "\n\t" + "cpi %A0, 0xff" "\n\t" + "cpc %B0, %1" "\n\t" + "brne .-10" "\n\t" + : "+r" (delay), "+a" (tmp) + : "0" (delay) + ); + } +}; + +} // namespace avrlib + +#endif AVRLIB_SOFTWARE_SERIAL_H_ diff --git a/avrlib/spi.h b/avrlib/spi.h new file mode 100644 index 0000000..327af1b --- /dev/null +++ b/avrlib/spi.h @@ -0,0 +1,115 @@ +// 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 . +// +// ----------------------------------------------------------------------------- +// +// Fast SPI communication (using the hardware implementation). This will take +// ownership of the pins 11 (data output), 12 (data input) and 13 (clock), + +// a user-definable pin for slave selection. Pin 10 should be kept as an output +// pin, since the SPI master/slave mode is based upon the value of this pin. +// +// This is a fairly basic implementation: +// - nothing is buffered, since the overhead of managing a circular buffer is +// around 15 cycles (not including the interrupt prelude/postlude), which is +// close to the transmission time at the fastest speed. +// - the atmega is always configured as a master. +// - no support for reading back from the slave. + +#ifndef AVRLIB_SPI_H_ +#define AVRLIB_SPI_H_ + +#include "avrlib/avrlib.h" +#include "avrlib/gpio.h" + +namespace avrlib { + +IORegister(SPSR); +typedef BitInRegister DoubleSpeed; +typedef BitInRegister TransferComplete; + +template +class Spi { + public: + enum { + buffer_size = 0, + data_size = 8 + }; + + static void Init() { + Clock::set_mode(DIGITAL_OUTPUT); + DataIn::set_mode(DIGITAL_INPUT); + DataOut::set_mode(DIGITAL_OUTPUT); + GlobalSlaveSelect::set_mode(DIGITAL_OUTPUT); // I'm a master! + SlaveSelect::set_mode(DIGITAL_OUTPUT); + SlaveSelect::High(); + GlobalSlaveSelect::High(); + + // SPI enabled, configured as master. + uint8_t configuration = _BV(SPE) | _BV(MSTR); + if (order == LSB_FIRST) { + configuration |= _BV(DORD); + } + switch (speed) { + case 2: + DoubleSpeed::set(); + case 4: + break; + case 8: + DoubleSpeed::set(); + case 16: + configuration |= _BV(SPR0); + break; + case 32: + DoubleSpeed::set(); + case 64: + configuration |= _BV(SPR1); + break; + case 128: + configuration |= _BV(SPR0); + configuration |= _BV(SPR1); + break; + } + SPCR = configuration; + } + + static inline void Write(uint8_t v) { + SlaveSelect::Low(); + SPDR = v; + while (!TransferComplete::value()); + SlaveSelect::High(); + } + + static inline void WriteWord(uint8_t a, uint8_t b) { + SlaveSelect::Low(); + SPDR = a; + while (!TransferComplete::value()); + SPDR = b; + while (!TransferComplete::value()); + SlaveSelect::High(); + } + + private: + typedef Gpio SlaveSelect; + typedef Gpio GlobalSlaveSelect; + typedef Gpio DataOut; + typedef Gpio DataIn; + typedef Gpio Clock; +}; + +} // namespace avrlib + +#endif AVRLIB_SPI_H_ diff --git a/avrlib/string.cc b/avrlib/string.cc new file mode 100755 index 0000000..d4a0ba5 --- /dev/null +++ b/avrlib/string.cc @@ -0,0 +1,53 @@ +// 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 . +// +// ----------------------------------------------------------------------------- +// +// Utility functions for string processing. + +#include "avrlib/string.h" + +#include + +namespace avrlib { + +size_t strnlen(const char* string, size_t maxlen) { + const char* end = (char*)memchr(string, '\0', maxlen); + return end ? (size_t) (end - string) : maxlen; +} + +void AlignRight(char* source, uint8_t width) { + uint8_t len = strnlen(source, width); + if (len == width) { + return; + } + char* destination = source + width - 1; + for (uint8_t i = 0; i < width; ++i) { + if (i < len) { + *destination-- = source[len - i - 1]; + } else { + *destination-- = ' '; + } + } +} + +void AlignLeft(char* source, uint8_t width) { + uint8_t len = strnlen(source, width); + while (len < width) { + source[len++] = ' '; + } +} + +} // namespace avrlib diff --git a/avrlib/string.h b/avrlib/string.h new file mode 100755 index 0000000..8214c1a --- /dev/null +++ b/avrlib/string.h @@ -0,0 +1,117 @@ +// 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 . +// +// ----------------------------------------------------------------------------- +// +// Utility functions for string processing. + +#ifndef AVRLIB_STRING_H_ +#define AVRLIB_STRING_H_ + +#include "avrlib/base.h" +#include + +namespace avrlib { + +size_t strnlen(const char* string, size_t maxlen); + +void AlignRight(char* source, uint8_t width); +void AlignLeft(char* source, uint8_t width); + +template +struct TypeInfo { + enum { + has_sign = 0, + max_size = 5 + }; +}; + +template<> struct TypeInfo { enum { has_sign = 0, max_size = 3 }; }; +template<> struct TypeInfo { enum { has_sign = 1, max_size = 4 }; }; +template<> struct TypeInfo { enum { has_sign = 0, max_size = 5 }; }; +template<> struct TypeInfo { enum { has_sign = 1, max_size = 6 }; }; +template<> struct TypeInfo { enum { has_sign = 0, max_size = 10 }; }; +template<> struct TypeInfo { enum { has_sign = 1, max_size = 11 }; }; + +static inline uint8_t NibbleToAscii(uint8_t digit) { + return digit < 10 ? digit + 48 : digit + 87; +} + +template +void Itoa(T i, uint8_t width, char* destination) { + unsigned char digits[TypeInfo::max_size + 1]; + if (width == 0) { + return; + } + if (i == 0) { + *destination++ = '0'; + width--; + } else { + if (TypeInfo::has_sign && i < 0) { + *destination++ = '-'; + width--; + i = -i; + } + uint8_t digit = 0; + while (i > 0) { + digits[digit++] = i % 10; + i /= 10; + } + while (digit) { + *destination++ = 48 + digits[--digit]; + width--; + } + } + if (width) { + *destination++ = '\0'; + } +} + +// A version of Itoa that does not allocate anything on the stack, and use +// a fixed chunk of memory for reversing the digits. Caveat: if two Itoa +// operations are taking place simultaneously, the results will be mixed. +template +void UnsafeItoa(T i, uint8_t width, char* destination) { + static unsigned char digits[TypeInfo::max_size + 1]; + if (width == 0) { + return; + } + if (i == 0) { + *destination++ = '0'; + width--; + } else { + if (TypeInfo::has_sign && i < 0) { + *destination++ = '-'; + width--; + i = -i; + } + uint8_t digit = 0; + while (i > 0) { + digits[digit++] = i % 10; + i /= 10; + } + while (digit) { + *destination++ = 48 + digits[--digit]; + width--; + } + } + if (width) { + *destination++ = '\0'; + } +} + +} // namespace avrlib + +#endif // AVRLIB_STRING_H_ diff --git a/avrlib/task.h b/avrlib/task.h new file mode 100755 index 0000000..ed2744d --- /dev/null +++ b/avrlib/task.h @@ -0,0 +1,130 @@ +// 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 . +// +// ----------------------------------------------------------------------------- +// +// Implementation of multitasking by coroutines, and naive deterministic +// scheduler. + +#ifndef AVRLIB_TASK_H_ +#define AVRLIB_TASK_H_ + +#include "avrlib/base.h" + +#define TASK_BEGIN static uint16_t state = 0; \ + switch(state) { \ +case 0:; + +// This is very unreliable because it assumes the line numbers will fit in an +// uint8_t. Don't use this unless you want to save a couple of bytes by using +// 8 bits comparisons instead of 16 bits comparisons. +#define TASK_BEGIN_NEAR static uint8_t state = 0; \ + switch(state) { \ +case 0:; + +#define TASK_RETURN(value) \ + do { \ + state = __LINE__; \ + return (value); \ +case __LINE__:; \ + } while (0) + +#define TASK_SWITCH \ + do { \ + state = __LINE__; \ + return; \ +case __LINE__:; \ + } while (0) + +#define TASK_END } return; + +namespace avrlib { + +typedef struct { + void (*code)(); + uint8_t priority; +} Task; + +// This naive deterministic scheduler stores an array of "slots", each element +// of which stores a 0 (nop) or a task id. During initialization, the array is +// filled in such a way that $task.priority occurrences of a task are present in +// the array, and are roughly evenly spaced. +// For example if the tasks/priority are: +// Task 1: 8 +// Task 2: 4 +// Task 3: 3 +// Task 4: 1 +// +// The slots will contain: +// 1 2 1 3 1 2 1 4 1 2 1 3 2 3 0 0 +// +// And the scheduler will execute the tasks in this sequence. +template +class NaiveScheduler { + public: + void Init() { + uint8_t slot = 0; + + // For a given task, occupy $priority available slots, spaced apart by + // #total slots / $priority. + for (uint8_t i = 0; i < sizeof(slots_); ++i) { + slots_[i] = 0; + } + + for (uint8_t i = 0; i < sizeof(tasks_) / sizeof(Task); ++i) { + for (uint8_t j = 0; j < tasks_[i].priority; ++j) { + // Search for the next available slot. + while (1) { + if (slot >= sizeof(slots_)) { + slot = 0; + } + if (slots_[slot] == 0) { + break; + } + ++slot; + } + slots_[slot] = i + 1; + slot += sizeof(slots_) / tasks_[i].priority; + } + } + } + + void Run() { + while (1) { + ++current_slot_; + if (current_slot_ >= sizeof(slots_)) { + current_slot_ = 0; + } + if (slots_[current_slot_]) { + tasks_[slots_[current_slot_] - 1].code(); + } + } + } + + private: + static Task tasks_[]; + static uint8_t slots_[num_slots]; + static uint8_t current_slot_; +}; + +template +uint8_t NaiveScheduler::slots_[num_slots]; + +template +uint8_t NaiveScheduler::current_slot_; + +} // namespace avrlib + +#endif // AVRLIB_TASK_H_ diff --git a/avrlib/time.cc b/avrlib/time.cc new file mode 100755 index 0000000..e4f1a5e --- /dev/null +++ b/avrlib/time.cc @@ -0,0 +1,50 @@ +// 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 . +// +// ----------------------------------------------------------------------------- +// +// Real time clock. Based on the code in the arduino core library +// by David A. Mellis. + +#include "avrlib/time.h" + +#include "avrlib/timer.h" + +namespace avrlib { + +volatile LongWord timer0_milliseconds = { 0 }; +uint8_t timer0_fractional = 0; + +uint32_t Delay(uint32_t delay) { + uint32_t t = milliseconds() + delay; + while (milliseconds() < t); +} + +uint32_t milliseconds() { + uint32_t m; + uint8_t oldSREG = SREG; + cli(); + m = timer0_milliseconds.value; + SREG = oldSREG; + return m; +} + +void InitClock() { + Timer<0>::set_prescaler(3); + Timer<0>::set_mode(TIMER_FAST_PWM); + Timer<0>::Start(); +} + +} // namespace avrlib diff --git a/avrlib/time.h b/avrlib/time.h new file mode 100755 index 0000000..81ab284 --- /dev/null +++ b/avrlib/time.h @@ -0,0 +1,82 @@ +// 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 . +// +// ----------------------------------------------------------------------------- +// +// Real time clock. + +#ifndef AVRLIB_TIME_H_ +#define AVRLIB_TIME_H_ + +#include "avrlib/base.h" + +namespace avrlib { + +uint32_t milliseconds(); +uint32_t Delay(uint32_t delay); + +void InitClock(); + +const uint32_t microseconds_per_timer0_overflow = + (64 * 256) / (F_CPU / 1000000L); +const uint32_t milliseconds_increment = + microseconds_per_timer0_overflow / 1000; + +const uint32_t fractional_increment = ( + microseconds_per_timer0_overflow % 1000) >> 3; + +const uint8_t fractional_max = 1000 >> 3; + +// The timer count is stored as an union instead of a mere uint32_t because we +// need access to the individual 16-bit parts of the value. +extern volatile LongWord timer0_milliseconds; + +extern uint8_t timer0_fractional; + +inline void TickSystemClock() { + // Compile-time optimization: with a 20Mhz clock rate, milliseconds_increment + // is always null, so we have to increment it only when there's a + // fractional overflow! + if (milliseconds_increment) { + timer0_milliseconds.value += milliseconds_increment; + } + timer0_fractional += fractional_increment; + if (timer0_fractional >= fractional_max) { + timer0_fractional -= fractional_max; + // The next lines are equivalent to: ++timer0_fractional. Why am I not + // using ++timer0_fractional? The reason is in the way gcc compiles this. + // 32-bits values are always loaded into contiguous registers. This code is + // called from an ISR, so this means 4 contiguous registers are going to + // be pushed/popped in the ISR. This costs 4 pairs of push/pops (16 cycles). + // On the other hand, this weird implementation only requires 2 adjacent + // registers, and they are probably already used for something else in the + // ISR. There's no free lunch, though: this code is less efficient than + // a++. However, when it is called every 16th or 32th entry in an ISR, the + // time saved by avoiding the extra push/pops makes it a better choice. + // + // Rule: when you *occasionnally* do something complicated from within an + // ISR, the code doing the complicated thing should really try to minimize + // the number of registers it uses, even if it takes more cycles to do + // the work. + ++timer0_milliseconds.words[0]; + if (timer0_milliseconds.words[0] == 0) { + ++timer0_milliseconds.words[1]; + } + } +} + +} // namespace avrlib + +#endif // AVRLIB_TIME_H_ diff --git a/avrlib/timer.h b/avrlib/timer.h new file mode 100755 index 0000000..e33ea45 --- /dev/null +++ b/avrlib/timer.h @@ -0,0 +1,199 @@ +// 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 . +// +// ----------------------------------------------------------------------------- +// +// Definitions of timer and related PWM registers. + +#ifndef AVRLIB_TIMER_H_ +#define AVRLIB_TIMER_H_ + +// interrupt.h is not strictly needed here, but .cc files including the timer +// classes are likely to also define interrupt handlers (and we have macros for +// that). +#include +#include + +#include "avrlib/avrlib.h" + +namespace avrlib { + +SpecialFunctionRegister(TCCR0A); +SpecialFunctionRegister(TCCR0B); +SpecialFunctionRegister(TCCR1A); +SpecialFunctionRegister(TCCR1B); +SpecialFunctionRegister(TCCR2A); +SpecialFunctionRegister(TCCR2B); +SpecialFunctionRegister(TIMSK0); +SpecialFunctionRegister(TIMSK1); +SpecialFunctionRegister(TIMSK2); +SpecialFunctionRegister(TCNT0); +SpecialFunctionRegister(TCNT1); +SpecialFunctionRegister(TCNT2); +SpecialFunctionRegister(OCR0A); +SpecialFunctionRegister(OCR0B); +SpecialFunctionRegister(OCR1A); +SpecialFunctionRegister(OCR1B); +SpecialFunctionRegister(OCR2A); +SpecialFunctionRegister(OCR2B); + +#ifdef ATMEGA1284P +SpecialFunctionRegister(TCCR3A); +SpecialFunctionRegister(TCCR3B); +SpecialFunctionRegister(TIMSK3); +SpecialFunctionRegister(TCNT3); +SpecialFunctionRegister(OCR3A); +SpecialFunctionRegister(OCR3B); +#endif // ATMEGA1284P + +enum TimerMode { + TIMER_NORMAL = 0, + TIMER_PWM_PHASE_CORRECT = 1, + TIMER_CTC = 2, + TIMER_FAST_PWM = 3, +}; + +template +struct TimerImpl { + typedef StatusRegisterA A; + typedef StatusRegisterB B; + + static inline uint8_t value() { + return *ValueRegister::ptr(); + } + + static inline void Start() { + *ModeRegister::ptr() |= 1; + } + static inline void Stop() { + *ModeRegister::ptr() &= ~1; + } + static inline void set_mode(TimerMode mode) { + // Sets the mode registers. + *StatusRegisterA::ptr() = (*StatusRegisterA::ptr() & 0xfc) | mode; + } + + // These are the values for MCUs clocked at 20 MHz + // + // Timer speed + // value | fast | accurate + // -------------------------------------- + // 1 | 78.125 kHz | 39.062 kHz + // 2 | 9.765 kHz | 4.882 kHz + // 3 | 1220.7 Hz | 610.3 Hz + // 4 | 305.2 Hz | 152.6 Hz + // 5 | 76.3 Hz | 38.1 Hz + static inline void set_prescaler(uint8_t prescaler) { + *StatusRegisterB::ptr() = (*StatusRegisterB::ptr() & 0xf8) | prescaler; + } +}; + +template +struct NumberedTimer { }; + +template<> struct NumberedTimer<0> { + typedef TimerImpl< + TCCR0ARegister, + TCCR0BRegister, + TIMSK0Register, + TCNT0Register> Impl; +}; + +template<> struct NumberedTimer<1> { + typedef TimerImpl< + TCCR1ARegister, + TCCR1BRegister, + TIMSK1Register, + TCNT1Register> Impl; +}; + +template<> struct NumberedTimer<2> { + typedef TimerImpl< + TCCR2ARegister, + TCCR2BRegister, + TIMSK2Register, + TCNT2Register> Impl; +}; + +#ifdef ATMEGA1284P +template<> struct NumberedTimer<3> { + typedef TimerImpl< + TCCR3ARegister, + TCCR3BRegister, + TIMSK3Register, + TCNT3Register> Impl; +}; +#endif // ATMEGA1284P + +template +struct Timer { + typedef typename NumberedTimer::Impl Impl; + static inline uint8_t value() { return Impl::value(); } + static inline void Start() { Impl::Start(); } + static inline void Stop() { Impl::Stop(); } + static inline void set_mode(TimerMode mode) { Impl::set_mode(mode); } + static inline void set_prescaler(uint8_t prescaler) { + Impl::set_prescaler(prescaler); + } +}; + +template +struct PwmChannel { + typedef BitInRegister EnabledBit; + enum { + analog = 1 + }; + static inline void Start() { + EnabledBit::set(); + } + static inline void Stop() { + EnabledBit::clear(); + } + static inline void Write(uint8_t value) { + *PwmRegister::ptr() = value; + } +}; + +struct NoPwmChannel { + enum { + analog = 0 + }; + static inline void Start() { } + static inline void Stop() { } + static inline void Write(uint8_t value) { } +}; + +typedef PwmChannel, COM0A1, OCR0ARegister> PwmChannel0A; +typedef PwmChannel, COM0B1, OCR0BRegister> PwmChannel0B; +typedef PwmChannel, COM1A1, OCR1ARegister> PwmChannel1A; +typedef PwmChannel, COM1B1, OCR1BRegister> PwmChannel1B; +typedef PwmChannel, COM2A1, OCR2ARegister> PwmChannel2A; +typedef PwmChannel, COM2B1, OCR2BRegister> PwmChannel2B; + +// Readable aliases for timer interrupts. +#define TIMER_0_TICK ISR(TIMER0_OVF_vect) +#define TIMER_1_TICK ISR(TIMER1_OVF_vect) +#define TIMER_2_TICK ISR(TIMER2_OVF_vect) + +#ifdef ATMEGA1284P +#define TIMER_3_TICK ISR(TIMER3_OVF_vect) +#endif // ATMEGA1284P + +} // namespace avrlib + +#endif // AVRLIB_TIMER_H_ diff --git a/avrlib/tools/__init__.py b/avrlib/tools/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/avrlib/tools/resources_compiler.py b/avrlib/tools/resources_compiler.py new file mode 100755 index 0000000..f2667d2 --- /dev/null +++ b/avrlib/tools/resources_compiler.py @@ -0,0 +1,215 @@ +#!/usr/bin/python2.5 +# +# 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 . +# +# ----------------------------------------------------------------------------- +# +# Generates .cc and .h files for string, lookup tables, etc. + +"""Compiles python string tables/arrays into .cc and .h files. + +TODO(pichenettes): rewrite this using the django templating engine. +""" + +import os +import string +import sys + +def Canonicalize(x): + """Returns a lower case version of the string with funky chars stripped.""" + in_chr = ''.join(map(chr, range(256))) + out_chr = range(256) + for i in range(256): + if chr(i) in string.uppercase: + out_chr[i] = ord(chr(i).lower()) + elif chr(i) in string.lowercase: + out_chr[i] = i + elif chr(i) in string.digits: + out_chr[i] = i + elif i < 10: + out_chr[i] = ord(str(i)) + elif chr(i) == '~': + out_chr[i] = ord('T') + elif chr(i) == '*': + out_chr[i] = ord('P') + elif chr(i) == '+': + out_chr[i] = ord('S') + elif chr(i) == '=': + out_chr[i] = ord('e') + elif chr(i) == '>': + out_chr[i] = ord('g') + elif chr(i) == '<': + out_chr[i] = ord('l') + elif chr(i) == '^': + out_chr[i] = ord('x') + elif chr(i) == '"': + out_chr[i] = ord('p') + else: + out_chr[i] = ord('_') + table = string.maketrans(in_chr, ''.join(map(chr, out_chr))) + bad_chars = '\t\n\r-:()[]"\',;' + return x.translate(table, bad_chars) + + +def CheckDups(collection): + counts = {} + for e in collection: + counts[e] = counts.get(e, 0) + 1 + for k, v in counts.items(): + if v > 1: + print 'Dupe:', k + return True + return False + + +def GenerateHeader(base_name, res): + max_num_resources = 0 + for resource in res.resources: + max_num_resources = max(max_num_resources, len(resource[0])) + + f = file(os.path.join(res.target, base_name + '.h'), 'wb') + header_guard = res.target.replace(os.path.sep, '_').upper() + f.write(res.header + '\n\n') + f.write('#ifndef %s_%s_H_\n' % (header_guard, base_name.upper())) + f.write('#define %s_%s_H_\n\n' % (header_guard, base_name.upper())) + f.write(res.includes + '\n\n') + if res.create_specialized_manager: + f.write('#include "avrlib/resources_manager.h"\n') + + if res.namespace: + f.write('namespace %s {\n\n' % res.namespace) + + f.write('typedef %s ResourceId;\n\n' % res.types[max_num_resources > 255]) + + for resource, table_name, prefix, c_type, python_type, ram in res.resources: + f.write('extern const %s* %s_table[];\n\n' % (c_type, table_name)) + + for resource, table_name, prefix, c_type, python_type, ram in res.resources: + if python_type != str: + canonical = {} + for name, data in resource: + if not tuple(data) in canonical: + name = '%s_%s' % (prefix.lower(), Canonicalize(name)) + args = (c_type, name, res.modifier) + f.write('extern const %s %s[] %s;\n' % args) + canonical[tuple(data)] = name + + for resource, table_name, prefix, c_type, python_type, ram in res.resources: + if python_type == str: + for i, string in enumerate(resource): + args = (prefix, Canonicalize(string).upper(), i, string) + f.write('#define %s_%s %d // %s\n' % args) + else: + for i, (name, data) in enumerate(resource): + f.write('#define %s_%s %d\n' % (prefix, Canonicalize(name).upper(), i)) + args = (prefix, Canonicalize(name).upper(), len(data)) + f.write('#define %s_%s_SIZE %d\n' % args) + + if res.create_specialized_manager: + f.write('typedef avrlib::ResourcesManager<\n') + f.write(' ResourceId,\n') + f.write(' avrlib::ResourcesTables<\n') + f.write(' %s_table,\n' % res.resources[0][1]) + f.write(' %s_table> > ResourcesManager; \n' % res.resources[1][1]) + + if res.namespace: + f.write('\n} // namespace %s\n' % res.namespace) + f.write('\n#endif // %s_%s_H_\n' % (header_guard, base_name.upper())) + + +def GenerateCc(base_name, res): + f = file(os.path.join(res.target, base_name + '.cc'), 'wb') + f.write(res.header + '\n\n') + f.write('#include "%s.h"\n' % base_name) + if res.namespace: + f.write('\nnamespace %s {\n\n' % res.namespace) + + for resource, table_name, prefix, c_type, python_type, ram in res.resources: + if python_type == str: + for string in resource: + args = (c_type, '%s_%s' % (prefix.lower(), Canonicalize(string)), + res.modifier, string.strip()) + f.write('static const %s %s[] %s = "%s";\n' % args) + if ram: + args = (c_type, table_name) + f.write('\n\nconst %s* %s_table[] = {\n' % args) + else: + args = (res.modifier, c_type, table_name) + f.write('\n\n%s const %s* %s_table[] = {\n' % args) + for string in resource: + f.write(' %s_%s,\n' % (prefix.lower(), Canonicalize(string))) + f.write('};\n\n') + else: + canonical = {} + for name, data in resource: + if not tuple(data) in canonical: + name = '%s_%s' % (prefix.lower(), Canonicalize(name)) + args = (c_type, name, res.modifier) + f.write('const %s %s[] %s = {\n' % args) + n_elements = len(data) + for i in xrange(0, n_elements, 8): + f.write(' '); + f.write(', '.join( + '%6d' % data[j] for j in xrange(i, min(n_elements, i + 8)))) + f.write(',\n'); + f.write('};\n') + canonical[tuple(data)] = name + if ram: + args = (c_type, table_name) + f.write('\n\nconst %s* %s_table[] = {\n' % args) + else: + args = (res.modifier, c_type, table_name) + f.write('\n\n%s const %s* %s_table[] = {\n' % args) + for name, data in resource: + f.write(' %s,\n' % canonical[tuple(data)]) + f.write('};\n\n') + + if res.namespace: + f.write('\n} // namespace %s\n' % res.namespace) + + +def Compile(path): + # A hacky way of loading the py file passed as an argument as a module + + # a descent along the module path. + base_name = os.path.splitext(path)[0] + sys.path += [os.path.abspath('.')] + res = __import__(base_name.replace('/', '.')) + for part in base_name.split('/')[1:]: + res = getattr(res, part) + + # Convert any big multi-line string into a list of string. + for i, resource_tuple in enumerate(res.resources): + if resource_tuple[-2] == str: + resource_tuple = list(resource_tuple) + resource_tuple[0] = [x for x in resource_tuple[0].split('\n') if x] + if CheckDups(resource_tuple[0]): + return + if CheckDups([Canonicalize(x) for x in resource_tuple[0]]): + return + res.resources[i] = tuple(resource_tuple) + + base_name = 'resources' #os.path.split(base_name)[-1] + GenerateHeader(base_name, res) + GenerateCc(base_name, res) + + +def main(argv): + for i in xrange(1, len(argv)): + Compile(argv[i]) + + +if __name__ == '__main__': + main(sys.argv) diff --git a/avrlib/watchdog_timer.h b/avrlib/watchdog_timer.h new file mode 100755 index 0000000..a501ae9 --- /dev/null +++ b/avrlib/watchdog_timer.h @@ -0,0 +1,37 @@ +// 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 . +// +// ----------------------------------------------------------------------------- +// +// Real time clock. + +#ifndef AVRLIB_WATCHDOG_TIMER_ +#define AVRLIB_WATCHDOG_TIMER_ + +#include + +#include "avrlib/base.h" + +namespace avrlib { + +// Note: this requires the bootloader to clear the Watchdog timer flags just +// after start-up. +inline void SystemReset(uint8_t interval) { + wdt_enable(interval); +} + +} // namespace avrlib + +#endif // AVRLIB_WATCHDOG_TIMER_