diff --git a/Makefile.rules b/Makefile.rules index 661a6a0..5eb1861 100644 --- a/Makefile.rules +++ b/Makefile.rules @@ -28,13 +28,17 @@ ALL_CXXFLAGS = $(ALL_CFLAGS) -fno-exceptions LDFLAGS = $(TARGET_LDFLAGS) -Wl,--gc-sections -Wl,-Map=$@.map,--cref +.PRECIOUS: $(TARGETDIR)/%.o + +ifneq ($(OVERRIDE_RULES),1) $(TARGETDIR)/%.o: %.cpp mkdir -p $(TARGETDIR) $(CXX) $(ALL_CXXFLAGS) -c $^ -o $@ $(TARGETDIR)/%.o: %.c mkdir -p $(TARGETDIR) - $(CXX) $(ALL_CXXFLAGS) -c $^ -o $@ + $(CC) $(ALL_CXXFLAGS) -c $^ -o $@ +endif $(TARGETDIR)/%.s: %.cpp mkdir -p $(TARGETDIR) diff --git a/Makefile.rules.i686-linux-gnu b/Makefile.rules.i686-linux-gnu index 388549c..6da5d8c 100644 --- a/Makefile.rules.i686-linux-gnu +++ b/Makefile.rules.i686-linux-gnu @@ -1,2 +1,4 @@ OBJDUMP = objdump #TARGET_CFLAGS = -S +GCC_VER = -4.8 +TARGET_CFLAGS = -std=c++11 diff --git a/Makefile.rules.msp430-llvm b/Makefile.rules.msp430-llvm new file mode 100644 index 0000000..7936b79 --- /dev/null +++ b/Makefile.rules.msp430-llvm @@ -0,0 +1,30 @@ +LLVM_PATH = /home/pfalcon/projects-3rdparty/llvm-3.0.src/Debug/bin +OPT = $(LLVM_PATH)/opt +LLC = $(LLVM_PATH)/llc +CROSS_COMPILE = msp430- + +MCU = msp430g2553 +TARGET_FLAGS = -mmcu=$(MCU) +# Can select specific device if multiple connected at the same time, +# format "-s " or "-U ", see mspdebug --help +DEVICE_SELECT ?= + +CLANGFLAGS = -ccc-host-triple msp430 -D__MSP430G2553__ -I/usr/msp430/include -m32 -emit-llvm +OPTFLAGS = -std-compile-opts -strip-debug -disable-simplify-libcalls +LLCFLAGS = -march=msp430 + +OVERRIDE_RULES = 1 + +$(TARGETDIR)/%.o: %.cpp + mkdir -p $(TARGETDIR) + clang $(ALL_CXXFLAGS) $(CLANGFLAGS) -S -c $^ -o $@.ll + $(OPT) $(OPTFLAGS) -S $@.ll -f -o $@.opt.ll + + $(LLC) $(LLCFLAGS) $@.opt.ll -o $@.opt.s_ + + awk '/.globl\tmain/ { print "\t.section\t.init9,\"ax\",@progbits"; } \ + { print }' $@.opt.s_ > $@.opt.s + msp430-as $@.opt.s -o $@ + +deploy-%: $(TARGETDIR)/% + mspdebug $(DEVICE_SELECT) rf2500 "prog $^" diff --git a/Makefile.rules.msp430-llvm-c b/Makefile.rules.msp430-llvm-c new file mode 100644 index 0000000..44d242b --- /dev/null +++ b/Makefile.rules.msp430-llvm-c @@ -0,0 +1,33 @@ +LLVM_PATH = /home/pfalcon/projects-3rdparty/llvm-3.0.src/Debug/bin +OPT = $(LLVM_PATH)/opt +LLC = $(LLVM_PATH)/llc +CROSS_COMPILE = msp430- + +MCU = msp430g2553 +TARGET_FLAGS = -mmcu=$(MCU) +# Can select specific device if multiple connected at the same time, +# format "-s " or "-U ", see mspdebug --help +DEVICE_SELECT ?= + +CLANGFLAGS = -ccc-host-triple msp430 -D__MSP430G2553__ -I/usr/msp430/include -m32 -emit-llvm +OPTFLAGS = -std-compile-opts -strip-debug -disable-simplify-libcalls +LLCFLAGS = -march=msp430 + +OVERRIDE_RULES = 1 + +$(TARGETDIR)/%.o: %.cpp + mkdir -p $(TARGETDIR) + clang $(ALL_CXXFLAGS) $(CLANGFLAGS) -S -c $^ -o $@.ll + $(OPT) $(OPTFLAGS) -S $@.ll -f -o $@.opt.ll + + $(LLC) -march=c $@.opt.ll -o $@.cbackend.c + clang $(ALL_CXXFLAGS) $(CLANGFLAGS) -S -c $@.cbackend.c -o $@.cbackend.ll + $(OPT) $(OPTFLAGS) $@.cbackend.ll -f -S -o $@.cbackend.opt.ll + $(LLC) $(LLCFLAGS) $@.cbackend.opt.ll -o $@.opt.s_ + + awk '/.globl\tmain/ { print "\t.section\t.init9,\"ax\",@progbits"; } \ + { print }' $@.opt.s_ > $@.opt.s + msp430-as $@.opt.s -o $@ + +deploy-%: $(TARGETDIR)/% + mspdebug $(DEVICE_SELECT) rf2500 "prog $^" diff --git a/README b/README index 8a7b716..b8a4172 100644 --- a/README +++ b/README @@ -9,7 +9,7 @@ standard protocols to access peripheral devices, like SPI, I2C, UART, 1-Wire, etc. To achieve high-level efficiency, PTL is built using (C++) templated static -types, which allows to apply aggressive compile-time optimizations, +types, allowing to apply aggressive compile-time optimizations, achieving the efficiency level of the C code purposedly written for specific MCU. Using templates also allows to provide optimized protocol implementations utilizing specialized MCU hardware blocks like SPI/I2C diff --git a/docs/contributing.txt b/docs/contributing.txt index e906f57..4a69e55 100644 --- a/docs/contributing.txt +++ b/docs/contributing.txt @@ -1,5 +1,16 @@ Peripheral Template Library is currently licensed under the terms of LGPLv3, -though it's consider to either add linking exception clause or switch to +though it's considered to either add linking exception clause or switch to BSD-like license. To facilitate such change in the future, only BSD/MIT -licensed third-party code is merged. If you have suggestion regarding -licensing, please feel free to bring them up. +licensed third-party code is merged, and contributors are asked to license +their patches under dual LGPL3/BSD license (or alternatively, just under BSD +license). + +The rationale of this request is that, if such licensing change is carried +out in the future, all contributors will need to be contacted to ask their +permissions to relicense, with possible complications that some contributors +won't respond, or will be against such change. So, instead I'm asking all +potential contributors about BSD licensing right away. I hope this makes +sense and reasonably fair. + +If you have suggestions regarding licensing, please feel free to bring them +up. diff --git a/docs/data_model.markdown b/docs/data_model.markdown new file mode 100644 index 0000000..b3d3362 --- /dev/null +++ b/docs/data_model.markdown @@ -0,0 +1,144 @@ +Normal C++ objects keep their state in memory fields. However, for embedded +devices, state is usually held is special registers outside usual memory +range. And even an empty (with 0 fields) C++ object takes at least 1 byte +of a memory (as mandated by C++ standard). As PTL strives for the highest +possible efficiency, wasting a whole byte for nothing is not acceptable and +C++ objects are not used for basic hardware data model. (Note, C++ objects +are used for higher-level ("RTOS-like") functionality, as there's clear +need to keep additional state in memory). + +Instead, PTL uses C++ parametrized types (aka templates) to represent +hardware objects. There's rough correspondence between conventional runtime +C++ object model and typed-based schedule: + +Generic entity abstraction ("class"): + +Runtime OO: + + class Foo { + Foo(type1 param1, type2 param2); + ... + }; + +Type-based OO: + + template + class Foo {...}; + +Object instantiation: + +Runtime OO: + + Foo foo1(param1, param2); + +Type-based OO: + + typdef Foo foo1; + +Passing object as argument (to function or constructor of another object): + +Runtime OO: + + Bar bar1(foo1); + +Type-based OO: + + typedef Bar bar1; + +Dynamic allocation: + +Runtime OO: + + Foo *foo2 = new Foo(param1, param2); + +Type-based OO: + + In type-based schedule, objects take 0 bytes of conventional memory, + and keep state in implicit special memory location, so it doesn't make + sense to "dynamically allocate" them. + + + +Well, the last subsection about dynamic allocation is "almost true". Let's +first consider that besides efficiency PTL also strives to provide fully +generic and generalized abstraction of hardware devices, support arbitrary +possible implementations for them, and allow different abtract blocks to +be combined in structured manner. This poses interesting problem that +while typically hardware classes don't have own state (because they wrap +special hardware registers, and address them implicitly), for some +implementations of these classes there may be need to store additional +state. + +Let's take a GPIO pin as an example. Typically, a pin will be represented +by: + +typedef GPIO mypin1; + +Where PORT_A and PIN_0 are constants statically selecting particular port +and pin in it. + +But sometimes there may be need to have "dynamic" GPIO pin, which is not +bound to a particular physical pin, but rather configured with at runtime +to use arbitrary pin. This would be obvious usecase for conventional C++ +object: + +GPIO mypin2(port_var, pin_var); + +But we already selected type-based object representation, so mypin as defined +above won't be compatible with PTL standard model, in other word, you won't be +able to "pass" mypin2 where mypin1 is expected (per above comparison table, +type-based objects are passed as template arguments, where normal objects are +passed as normal arguments). + +So, we must retain type-based interface, and yet add runtime-modifiable state +to such type. There're 2 ways to resolve this situation: + +1. Split state from type-based object into "backing store": + +struct GPIOStore { + int port; + int pin; +}; + +And then parametrize type-based object with reference to particular instance +of backing store: + +template +class DynaGPIO { +}; + +2. Well, just store state in static fields, but making sure we parametrize +type-based object by unique values, so template instantiation leads to +distinct types (and thus distinct static variables for each type object). +For such unique values, initial/default values make work (they certainly +work for GPIO case - hopefully one won't create 2 distinct pin objects +which are actually the same pin). + +template +class DynaGPIO { + static int port; + static int pin; +}; + +template int Pin::port = port_; +template int Pin::pin = pin_; + +Note the explicit instantiation of templated static fields. + + +Second choice looks more "integral", because it doesn't require creation of +separate type, but it has mild tricks of requiring unique parametrization +and explicit template instantiation for static fields. And well, it's clear +that here state will be statically allocated. + +First choice looks more flexible, because it would be seem that backing +store might be allocated on stack. But that's not really case, because +template parameter must be (at least in C++03) compile time addressable, +like static varaible and unlike automatic or dynamic variable. + +So, choice 2 looks like clear winner as offering cleaner and more concise +interface. Choice 1 was initially implemented for DynaPin class, and +retained there for time being (to explore and see if more differences +between choices will be found), but choice 2 will be used for new classes. + + diff --git a/examples/Makefile b/examples/Makefile index ed53414..1480c69 100644 --- a/examples/Makefile +++ b/examples/Makefile @@ -2,11 +2,11 @@ PTL_PATH = .. include $(PTL_PATH)/Makefile.rules ALL = blink blink_static blink_dynamic blink_timer blink_ticks \ - blink_sleep blink_bus \ - uart_echo uart_echo_irq uart_echo_async \ + blink_sleep blink_bus blink_c blink_dynapin \ + uart_echo uart_echo_irq uart_echo_async uart_echo_buffered \ uart_printf \ spi i2c_24cxx 1wire adc \ - timer_irq timer_irq_dispatch irq_dispatch \ + timer_irq timer_irq_dispatch irq_dispatch irq_dispatch_simple \ flash \ perf_counter lcd lcd_nokia \ msp430_parse_tlv @@ -22,9 +22,12 @@ blink_timer: $(TARGETDIR)/blink_timer blink_ticks: $(TARGETDIR)/blink_ticks blink_sleep: $(TARGETDIR)/blink_sleep blink_bus: $(TARGETDIR)/blink_bus +blink_c: $(TARGETDIR)/blink_c +blink_dynapin: $(TARGETDIR)/blink_dynapin uart_echo: $(TARGETDIR)/uart_echo uart_echo_irq: $(TARGETDIR)/uart_echo_irq uart_echo_async: $(TARGETDIR)/uart_echo_async +uart_echo_buffered: $(TARGETDIR)/uart_echo_buffered uart_printf: $(TARGETDIR)/uart_printf spi: $(TARGETDIR)/spi i2c_24cxx: $(TARGETDIR)/i2c_24cxx @@ -32,6 +35,7 @@ i2c_24cxx: $(TARGETDIR)/i2c_24cxx adc: $(TARGETDIR)/adc timer_irq: $(TARGETDIR)/timer_irq timer_irq_dispatch: $(TARGETDIR)/timer_irq_dispatch +irq_dispatch_simple: $(TARGETDIR)/irq_dispatch_simple irq_dispatch: $(TARGETDIR)/irq_dispatch flash: $(TARGETDIR)/flash perf_counter: $(TARGETDIR)/perf_counter @@ -57,6 +61,8 @@ $(TARGETDIR)/blink_ticks.o: blink_ticks.cpp $(TARGETDIR)/blink_bus: $(TARGETDIR)/blink_bus.o $(TARGETDIR)/blink_bus.o: blink_bus.cpp +$(TARGETDIR)/blink_c: $(TARGETDIR)/blink_c.o $(TARGETDIR)/blink_c_api.o + $(TARGETDIR)/uart_echo: $(TARGETDIR)/uart_echo.o $(TARGETDIR)/uart_echo.o: uart_echo.cpp @@ -74,3 +80,6 @@ $(TARGETDIR)/adc.o: adc.cpp $(TARGETDIR)/lcd: $(TARGETDIR)/lcd.o $(TARGETDIR)/lcd.o: lcd.cpp + +$(TARGETDIR)/irq_dispatch: $(TARGETDIR)/irq_dispatch.o +$(TARGETDIR)/irq_dispatch_simple: $(TARGETDIR)/irq_dispatch_simple.o diff --git a/examples/blink_c.c b/examples/blink_c.c new file mode 100644 index 0000000..d7328af --- /dev/null +++ b/examples/blink_c.c @@ -0,0 +1,12 @@ +#include "blink_c_api.h" + +int main() +{ + init(); + while (1) { + led_high(); + delay(100000U); + led_low(); + delay(100000U); + } +} diff --git a/examples/blink_c_api.cpp b/examples/blink_c_api.cpp new file mode 100644 index 0000000..17e1325 --- /dev/null +++ b/examples/blink_c_api.cpp @@ -0,0 +1,31 @@ +#include +#include +#include +#include +#include "blink_c_api.h" + +using namespace PTL; + +typedef Delay delayer; + +void led_high() +{ + board::LED::high(); +} + +void led_low() +{ + board::LED::low(); +} + +void delay(uint32_t val) +{ + delayer::delay(val); +} + +void init() +{ + cpu::init(cpu::DEFAULT); + board::LED::port::enable(); + board::LED::output(); +} diff --git a/examples/blink_c_api.h b/examples/blink_c_api.h new file mode 100644 index 0000000..f6713b1 --- /dev/null +++ b/examples/blink_c_api.h @@ -0,0 +1,14 @@ +#include + +#ifdef __cplusplus +extern "C" { +#endif + +void init(); +void led_high(); +void led_low(); +void delay(uint32_t val); + +#ifdef __cplusplus +} +#endif diff --git a/examples/blink_dynapin.cpp b/examples/blink_dynapin.cpp new file mode 100644 index 0000000..54a0ada --- /dev/null +++ b/examples/blink_dynapin.cpp @@ -0,0 +1,27 @@ +#include +#include +#include +#include +#include +#include + +using namespace PTL; + +typedef TimeDelay delayer; + +//DynaPin_Store led_spec = {board::LED::port::base, board::LED::bit::value}; +DynaPin_Store led_spec(board::LED::port::no, board::LED::bit::shift); +typedef DynaPin dynaLED; + +int main() +{ + cpu::init(cpu::DEFAULT); + dynaLED::port::enable(); + dynaLED::output(); + while (true) { + dynaLED::high(); + delayer::delay_ms(500); + dynaLED::low(); + delayer::delay_ms(500); + } +} diff --git a/examples/hw_config_avr.hpp b/examples/hw_config_avr.hpp index cf8d41b..513c4cb 100644 --- a/examples/hw_config_avr.hpp +++ b/examples/hw_config_avr.hpp @@ -2,7 +2,8 @@ using namespace PTL; -typedef board::uart uart; +typedef board::uart hwuart; +typedef hwuart uart; typedef ParBus hd44780_data; typedef Signal hd44780_en_sig; diff --git a/examples/hw_config_lm4.hpp b/examples/hw_config_lm4.hpp deleted file mode 100644 index 713eb7d..0000000 --- a/examples/hw_config_lm4.hpp +++ /dev/null @@ -1,3 +0,0 @@ -using namespace PTL; - -typedef UART<16 MHZ, 115200, UART0_> uart; diff --git a/examples/hw_config_msp430.hpp b/examples/hw_config_msp430.hpp index 7064523..dcfc84e 100644 --- a/examples/hw_config_msp430.hpp +++ b/examples/hw_config_msp430.hpp @@ -5,6 +5,10 @@ using namespace PTL; +//#define LAUNCHPAD_MSP430_VER_1_4 + +#ifndef LAUNCHPAD_MSP430_VER_1_4 + /* MSP430 Launchpad hw. rev. 1.5 has different (swapped) pin assignments for "software" and "hardware" UART. This is rooted in the fact that hwrev. 1.4 had "software" assignment, but later released chips with hardware UART @@ -18,18 +22,23 @@ using namespace PTL; 1.4 support. More info: http://pfalcon-oe.blogspot.com/2012/07/ti-launchpad-14-vs-15-pin-swappery.html */ +typedef UART<1 MHZ, 9600, USCI_A> hwuart; //typedef UART uart; typedef UART<1 MHZ, 9600, USCI_A> uart; -/* MSP430 Launchpad hw. rev. 1.4 doesn't have hardware UART support at all, - and bitbang UART has to use swapped RXD/TXD pins. */ -//typedef UART, Pin, timer> uart; - -/* At this time, only USCI-based hardware SPI is supported, USI is still needs +/* At this time, only USCI-based hardware SPI is supported, USI still needs wrapping. Note that USCI vs USI SPI pins have the same swapping issue as UART above (great work, TI!) */ typedef SPI spi; +#else + +/* MSP430 Launchpad hw. rev. 1.4 doesn't have hardware UART support at all, + and bitbang UART has to use swapped RXD/TXD pins. */ +typedef UART, Pin, timer> uart; + +#endif + typedef ParBus< Pin, Pin, Pin, Pin > hd44780_data; typedef Signal< Pin > hd44780_en_sig; typedef Signal< Pin > hd44780_regsel_sig; diff --git a/examples/hw_config_stm32.hpp b/examples/hw_config_stm32.hpp new file mode 100644 index 0000000..53081a1 --- /dev/null +++ b/examples/hw_config_stm32.hpp @@ -0,0 +1 @@ +using namespace PTL; diff --git a/examples/hw_config_tm4.hpp b/examples/hw_config_tm4.hpp new file mode 100644 index 0000000..7667f97 --- /dev/null +++ b/examples/hw_config_tm4.hpp @@ -0,0 +1,6 @@ +#include + +using namespace PTL; + +typedef UART<16 MHZ, 115200, UART0_> hwuart; +typedef hwuart uart; diff --git a/examples/irq_dispatch.cpp b/examples/irq_dispatch.cpp index 016752a..0db73d4 100644 --- a/examples/irq_dispatch.cpp +++ b/examples/irq_dispatch.cpp @@ -4,7 +4,7 @@ #include #include #include -#include +#include #include HW_CONFIG @@ -35,9 +35,7 @@ class myspi : public spi } }; -// Explicit instantiation of IRQ dispatcher class -template class IrqDispatch; - +IRQ_DISPATCH(mytimer, myuart, myspi); int main() { diff --git a/examples/irq_dispatch_simple.cpp b/examples/irq_dispatch_simple.cpp new file mode 100644 index 0000000..5fce1f4 --- /dev/null +++ b/examples/irq_dispatch_simple.cpp @@ -0,0 +1,29 @@ +#include +#include +#include +#include +#include + +#include HW_CONFIG + +class mytimer : public timer +{ +public: + static void irq_reset() + { + board::LED::toggle(); + } +}; + +IRQ_DISPATCH(mytimer); + +int main() +{ + cpu::init(cpu::DEFAULT); + board::LED::port::enable(); + board::LED::output(); + mytimer::free_run(); + mytimer::enable_irq(); + cpu::enable_irq(); + while (true); +} diff --git a/examples/uart_echo_buffered.cpp b/examples/uart_echo_buffered.cpp new file mode 100644 index 0000000..d7fb91d --- /dev/null +++ b/examples/uart_echo_buffered.cpp @@ -0,0 +1,37 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +#include HW_CONFIG + +typedef BufferedUart buart; + +IRQ_DISPATCH(buart::uart_handlers); + +int main() +{ + cpu::init(cpu::DEFAULT); + board::LED::output(); + board::LED::low(); + buart::init(); + cpu::enable_irq(); + + while (true) { + // We have time to process only 3 incoming chars, ... + for (int i = 0; i < 3; i++) { + uint8_t b = buart::read(); + board::LED::toggle(); + buart::write(b); + } + // ..., and then we're busy with very important task + // The whole idea is that even if we don't process incoming + // chars regularly, they are not lost, but buffered in + // background. + StaticDelay::delay(500000U); + } +} diff --git a/examples/uart_echo_irq.cpp b/examples/uart_echo_irq.cpp index 9d008d8..7e8bdd8 100644 --- a/examples/uart_echo_irq.cpp +++ b/examples/uart_echo_irq.cpp @@ -3,7 +3,9 @@ #include #include -typedef UART<1 MHZ, 9600, USCI> uart; +#include HW_CONFIG + +namespace PTL { template <> void uart::uart_rx_irq_handler() @@ -12,13 +14,15 @@ void uart::uart_rx_irq_handler() board::LED::toggle(); } +} + int main() { cpu::init(cpu::DEFAULT); board::LED::output(); board::LED::low(); - uart::init(); - uart::enable_rx_irq(); + hwuart::init(); + hwuart::enable_rx_irq(); cpu::enable_irq(); while (true); diff --git a/include/algo/nearptr.hpp b/include/algo/nearptr.hpp new file mode 100644 index 0000000..907adae --- /dev/null +++ b/include/algo/nearptr.hpp @@ -0,0 +1,77 @@ +/* + * This file is part of the Peripheral Template Library project. + * + * Copyright (c) 2012-2013 Paul Sokolovsky + * + * This library is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This library 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library. If not, see . + */ +#ifndef _NEAR_PTR_HPP +#define _NEAR_PTR_HPP +// As C++ doesn't support overloading templates by parameters, we should name +// all templates differently. Also, "const char *" template parameter cannot +// be cast to int. So, we have 3 templates with the same implementation, +// difgering only in type of base template parameter. + +#define IMPL(name) \ +public: \ + storage_t off; \ + name() { off = 0; } \ + name(T *p) { off = ((int)p - (int)base) >> align; } \ + T& operator*() const { return *(T*)((int)base + (off << align)); } \ + operator T*() const { return (T*)((int)base + (off << align)); } + +// NearPtr against absolute base. This is what would be used on +// MCU platform. +// Params: +// base - base of the memoty region within which pointer will point +// storage_t - size of pointer value (uint8_t, uint16_t, etc.); can be +// signed type too if needed +// T - type to point to +// align - alignment of pointer, as power of 2 +// For example, 16-bit NearPtr with alignment of 16 can address 1MB of memory +template class AbsNearPtr +{ + IMPL(AbsNearPtr) +}; + +// NearPtr against statically allocated array (note: must have external +// linkage, i.e. defined globally w/o static keyword). +template class ArrayNearPtr +{ + IMPL(ArrayNearPtr) +}; + +// NearPtr against dynamic base store in a variable, for example, received +// from malloc(). +template class VarNearPtr +{ + IMPL(VarNearPtr) +}; + +#if 0 +// Example usage: + +const int base1 = 0x20000000; +char base2[256] = "foobar"; +char *base3 = (char*)malloc(256); + +template using Ptr1 = AbsNearPtr; +template using Ptr2 = ArrayNearPtr; +template using Ptr3 = VarNearPtr; + +Ptr2 p = &base2[3]; +printf("%c", *p); // Produces "b" +#endif + +#endif // _NEAR_PTR_HPP diff --git a/include/cortex-m/cpu_cortexm.hpp b/include/cortex-m/cpu_cortexm.hpp index e9a7314..587d53f 100644 --- a/include/cortex-m/cpu_cortexm.hpp +++ b/include/cortex-m/cpu_cortexm.hpp @@ -16,6 +16,9 @@ * You should have received a copy of the GNU Lesser General Public License * along with this library. If not, see . */ +#ifndef _CPU_CORTEXM_HPP +#define _CPU_CORTEXM_HPP + #include #include // Declare Cortex-M IRQ handlers @@ -25,6 +28,9 @@ namespace PTL { class CortexMCPU {}; +// Various blocks of MCU +enum { CORTEXM_NONE, CORTEXM_SYSTICK }; + template <> class CPU : public ICPU { @@ -40,3 +46,5 @@ class CPU : public ICPU }; } // namespace + +#endif // _CPU_CORTEXM_HPP diff --git a/include/cortex-m/irq_dispatch_cortexm.hpp b/include/cortex-m/irq_dispatch_cortexm.hpp new file mode 100644 index 0000000..8f9a4fd --- /dev/null +++ b/include/cortex-m/irq_dispatch_cortexm.hpp @@ -0,0 +1,66 @@ +/* + * This file is part of the Peripheral Template Library project. + * + * Copyright (c) 2012-2013 Paul Sokolovsky + * + * This library is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This library 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library. If not, see . + */ +#ifndef _IRQ_DISPATCH_CORTEXM_HPP +#define _IRQ_DISPATCH_CORTEXM_HPP + +#include + +namespace PTL { + +/** + ** Device-specific classes and macros for interrupt dispatching + **/ + +/* Implementation of function which will dispatch harware IRQ to all + blocks which have corresponding PTL handler. + What to change: name, condition, implementation. */ +IRQ_DISPATCH_HELPER(do_systick, block::block_type == CORTEXM_SYSTICK) +{ + block::irq_reset(); +} + +/* Class which encapsulates all hardware -> PTL dispatchers. Mostly + used to allow variable list of blocks to dispatch to. */ +template +class IrqDispatch +{ +public: + HANDLER(SysTick_, do_systick); +}; + +/* Dispatch IRQ from MCU and compiler specific "IRQ vector" function. This + is expected to be fully optimized out by inlining. + What to change: name/attributes of function defined. */ +#define CALL_HANDLER(dispatcher, vector) \ + void vector##Handler() \ + { \ + dispatcher::vector##Handler(); \ + } + +/* Macros to dispatch from MCU/compiler specific "IRQ vector function" + to IrqDispatch class. The latter should be passed in as a param. */ +#define IRQ_DISPATCH(blocks...) \ + template class IrqDispatch; \ + typedef IrqDispatch _irq_dispatch; \ + CALL_HANDLER(_irq_dispatch, SysTick_); + + +} // namespace + +#endif // _IRQ_DISPATCH_CORTEXM_HPP diff --git a/include/cortex-m/irq_dispatch_cortexm_v1.hpp b/include/cortex-m/irq_dispatch_cortexm_v1.hpp new file mode 100644 index 0000000..d142ee4 --- /dev/null +++ b/include/cortex-m/irq_dispatch_cortexm_v1.hpp @@ -0,0 +1,122 @@ +/* + * This file is part of the Peripheral Template Library project. + * + * Copyright (c) 2012-2013 Paul Sokolovsky + * + * This library is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This library 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library. If not, see . + */ +#ifndef _IRQ_DISPATCH_MSP430_HPP +#define _IRQ_DISPATCH_MSP430_HPP + +namespace PTL { + +class NullBlock +{ +public: + const static int block_type = 0; +}; + +/* + * We want to declare a function here whose body will be executed only + * when condition is true. We have to use metaprog conditionals here, + * because if condition is false, func body may not compile at all + * (for example because class it opeartes on lacks specific methods). + * So, we use 2 complementary implementations of func: one with empty + * body for case of condition == false, and one with the given body + * in case it's true. All is wrapped with macros for readability. + */ + +#define IRQ_DISPATCH_HELPER(func_name, cond) \ +template \ +inline void func_name(uint8_t val = 0, typename meta::enable_if<(!(cond))>::type* = 0) {} \ +\ +template \ +inline void func_name(uint8_t val = 0, typename meta::enable_if<(cond)>::type* = 0) \ + + +#if 0 +IRQ_DISPATCH_HELPER(do_timer_cc0, block::block_type == MSP430_TIMER) +{ + block::irq_capture_compare0(); +} + +IRQ_DISPATCH_HELPER(do_usci_rx, block::block_type == MSP430_USCI) +{ + uint8_t ifg2 = val; + if (block::usci::no == 'A' && (ifg2 & UCA0RXIFG)) + block::irq_rx(); + + if (block::usci::no == 'B' && (ifg2 & UCB0RXIFG)) + block::irq_rx(); +} + +IRQ_DISPATCH_HELPER(do_usci_tx, block::block_type == MSP430_USCI) +{ + uint8_t ifg2 = val; + if (block::usci::no == 'A' && (ifg2 & UCA0TXIFG)) + block::irq_tx(); + + if (block::usci::no == 'B' && (ifg2 & UCB0TXIFG)) + block::irq_tx(); +} +#endif + +#define APPLY_TO_ALL(func, b1, b2, b3) { func(); func(); func(); } +#define APPLY_TO_ALL_ARG(func, arg, b1, b2, b3) { func(arg); func(arg); func(arg); } + +#if 0 +#define HANDLER(vector, dispatch_func) \ + static interrupt(vector) vector##_handler() \ + { \ + APPLY_TO_ALL(dispatch_func, b1, b2, b3); \ + } + +#define HANDLER_CACHE_REG(vector, dispatch_func, reg) \ + static interrupt(vector) vector##_handler() \ + { \ + uint8_t cache = reg; \ + APPLY_TO_ALL_ARG(dispatch_func, cache, b1, b2, b3); \ + } + +template +class IrqDispatch +{ +public: + HANDLER(TIMER0_A1_VECTOR, do_timer_main); + HANDLER(TIMER0_A0_VECTOR, do_timer_cc0); + HANDLER_CACHE_REG(USCIAB0RX_VECTOR, do_usci_rx, IFG2); + HANDLER_CACHE_REG(USCIAB0TX_VECTOR, do_usci_tx, IFG2); +}; +#endif + + +#define HANDLER(vector, dispatch_func, blocks...) \ + void vector##Handler() \ + { \ + APPLY_TO_ALL(dispatch_func, blocks); \ + } + + +IRQ_DISPATCH_HELPER(do_systick, block::block_type == CORTEXM_SYSTICK) +{ + block::irq_reset(); +} + +#define IRQ_DISPATCH(blocks...) \ + HANDLER(SysTick_, do_systick, blocks); + + +} // namespace + +#endif // _IRQ_DISPATCH_MSP430_HPP diff --git a/include/cortex-m/irq_dispatch_cortexm_v2.hpp b/include/cortex-m/irq_dispatch_cortexm_v2.hpp new file mode 100644 index 0000000..28c39ef --- /dev/null +++ b/include/cortex-m/irq_dispatch_cortexm_v2.hpp @@ -0,0 +1,134 @@ +/* + * This file is part of the Peripheral Template Library project. + * + * Copyright (c) 2012-2013 Paul Sokolovsky + * + * This library is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This library 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library. If not, see . + */ +#ifndef _IRQ_DISPATCH_CORTEXM_HPP +#define _IRQ_DISPATCH_CORTEXM_HPP + +namespace PTL { + +class NullBlock +{ +public: + const static int block_type = 0; +}; + +/** + ** Device-independent helper macros for interrupt dispatching + **/ + +/* + * We want to declare a function whose body will be executed only + * when (static) condition is true. We have to use metaprog conditionals + * here, because if condition is false, func body may not compile at all + * (for example because class it opeartes on lacks specific methods). + * So, we use 2 complementary implementations of func: one with empty + * body for case of condition == false, and one with the given body + * in case it's true. All is wrapped with macros for readability. + */ + +#define IRQ_DISPATCH_HELPER(func_name, cond) \ +template \ +inline void func_name(uint8_t val = 0, typename meta::enable_if<(!(cond))>::type* = 0) {} \ +\ +template \ +inline void func_name(uint8_t val = 0, typename meta::enable_if<(cond)>::type* = 0) \ + +/* Apply templated function to list of template args. Note: this and other similar + funcs depend on particular number of arguments passed and must be updated to + pass more. */ +#define APPLY_TO_ALL(func, b1, b2, b3) { func(); func(); func(); } +/* Apply templated function to list of template args, also passing (runtime) argument + to a function. */ +#define APPLY_TO_ALL_ARG(func, arg, b1, b2, b3) { func(arg); func(arg); func(arg); } + +/* Define handler function for particular hardware IRQ. */ +#define HANDLER(vector, dispatch_func) \ + static void vector##Handler() \ + { \ + APPLY_TO_ALL(dispatch_func, b1, b2, b3); \ + } + +/* Define handler function for particular hardware IRQ, caching value + of a register allowing to demux multiplexed IRQs. This cached value + is passed to each PTL handler. */ +#define HANDLER_CACHE_REG(vector, dispatch_func, reg) \ + static void vector##Handler() \ + { \ + uint8_t cache = reg; \ + APPLY_TO_ALL_ARG(dispatch_func, cache, b1, b2, b3); \ + } + +/** + ** Device-specific classes and macros for interrupt dispatching + **/ + +/* Dispatch IRQ from MCU and compiler specific "IRQ vector" function. This + is expected to be fully optimized out by inlining. + What to change: name/attributes of function defined. */ +#define CALL_HANDLER(dispatcher, vector) \ + void vector##Handler() \ + { \ + dispatcher::vector##Handler(); \ + } + +/* Implementation of function which will dispatch harware IRQ to all + blocks which have corresponding PTL handler. + What to change: name, condition, implementation. */ +IRQ_DISPATCH_HELPER(do_systick, block::block_type == CORTEXM_SYSTICK) +{ + block::irq_reset(); +} + +/* Main IRQ dispatching class. User needs to instantiate this per + application needs (using explicit template instantiation). */ +template +class IrqDispatch +{ +public: + HANDLER(SysTick_, do_systick); +}; + +/* Macros to dispatch from MCU/compiler specific "IRQ vector function" + to IrqDispatch class. The latter should be passed in as a param. */ +#define IRQ_DISPATCH(dispatcher) \ + CALL_HANDLER(dispatcher, SysTick_); + +/* Usage example: */ +#if 0 + +class mytimer : public timer +{ +public: + void irq_reset() + { + // Do something + } +}; + +// Don't Repeat Yourself, and explicit template instantiation cannot +// be applied to a typedef name, so user old trusty defines. +#define irq_dispatch IrqDispatch +template class irq_dispatch; +IRQ_DISPATCH(irq_dispatch); + +#endif + + +} // namespace + +#endif // _IRQ_DISPATCH_CORTEXM_HPP diff --git a/include/cortex-m/timer_cortexm_cyccnt.hpp b/include/cortex-m/timer_cortexm_cyccnt.hpp new file mode 100644 index 0000000..cf5a252 --- /dev/null +++ b/include/cortex-m/timer_cortexm_cyccnt.hpp @@ -0,0 +1,49 @@ +/* + * This file is part of the Peripheral Template Library project. + * + * Copyright (c) 2012-2013 Paul Sokolovsky + * + * This library is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This library 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library. If not, see . + */ +#ifndef _TIMER_CORTEXM_SYSTICK_HPP +#define _TIMER_CORTEXM_SYSTICK_HPP + +#include + +// TODO: WIP, untested + +namespace PTL { + +class CCycCnt : public ITimer +{ +public: + typedef uint32_t width; + + static width value() { return DWT->CYCCNT; } + static void free_run() + { + // p.11-19 Cortex-M3 r2p0 TechRefMan + // The TRCENA bit of the Debug Exception and Monitor Control Register must be set + // before you can use the DWT. + CoreDebug->DEMCR |= CoreDebug_DEMCR_TRCENA; + if (DWT->CTRL == 0 || (DWT->CTRL & DWT_CTRL_NOCYCCNT)) { + abort(); + } + DWT->CTRL |= DWT_CTRL_CYCCNTENA; + } +}; + +} // namespace + +#endif // _TIMER_CORTEXM_SYSTICK_HPP diff --git a/include/cortex-m/timer_cortexm_systick.hpp b/include/cortex-m/timer_cortexm_systick.hpp index 8c678dd..e8a3e5b 100644 --- a/include/cortex-m/timer_cortexm_systick.hpp +++ b/include/cortex-m/timer_cortexm_systick.hpp @@ -20,6 +20,7 @@ #define _TIMER_CORTEXM_SYSTICK_HPP #include +#include #include namespace PTL { @@ -27,19 +28,22 @@ namespace PTL { class CSysTick : public ITimer { public: + const static int block_type = CORTEXM_SYSTICK; typedef uint32_t width; static width value() { return SysTick->VAL; } static void free_run() { SysTick->LOAD = 0xffffff; - // Set CLKSOURCE to get same clock as CPU core. Clock in case - // CLKSOURCE unset is implementation-dependent actually. + // Set CLKSOURCE to get same clock as CPU core. In case CLKSOURCE + // is unset, the clock used is implementation-dependent. SysTick->CTRL |= SysTick_CTRL_CLKSOURCE_Msk | SysTick_CTRL_ENABLE_Msk; } static void enable_irq() { SysTick->CTRL |= SysTick_CTRL_TICKINT_Msk; } static void disable_irq() { SysTick->CTRL &= ~SysTick_CTRL_TICKINT_Msk; } + + static void irq_reset() {} }; typedef CSysTick timer; diff --git a/include/cpu.hpp b/include/cpu.hpp index 0964347..26f8e0c 100644 --- a/include/cpu.hpp +++ b/include/cpu.hpp @@ -24,6 +24,8 @@ #include #elif defined(__EFM32__) #include +#elif defined(__TM4__) +#include #elif defined(__thumb__) || defined(__thumb2__) #include namespace PTL { diff --git a/include/gpio_base.hpp b/include/gpio_base.hpp index e697dea..f9fed53 100644 --- a/include/gpio_base.hpp +++ b/include/gpio_base.hpp @@ -27,15 +27,16 @@ namespace PTL { // Pin is abstraction of basic I/O signal -template +template class IPin { public: + typedef width_ width; + static void enable(); static void output(); static void input(); - // TODO: need better type - static int value(); + static width value(); static void high(); static void low(); static void toggle(); @@ -52,11 +53,14 @@ class IPin }; // Port is a collection of related pins -template +// no_ is thru-numbered relative port number (0 - very first GPIO port, etc.) +// Used mostly for defining numbering for dynapins. +template class IPort { public: typedef width_ width; + static const int no = no_; // Enable port for access (power on, set up clocks, etc.) static void enable() {} @@ -68,7 +72,12 @@ class IPort static void set_masked(width val, width mask) { set(value() & mask); } }; -// Pin which belongs to a port +// Pin which belongs to a (static) port +// Most GPIO pins in MCUs are grouped into ports, +// and this class captures pin's port and bit position +// within port. But note that all pins don't have +// to use this class (when there's no underlying "port", +// if such port is not static) and can use IPin directly. template class PortPin: public IPin { @@ -80,7 +89,10 @@ class PortPin: public IPin }; // Non-existent pin, can be used when a pin in some design is optional. -// All access will be optimized out by compiler. +// All access will be optimized out by compiler. Typical example +// is a LED - boards which have it, can define it to a specific pin, +// and boards which lack - to NullPin, and code will still compile +// (though indication obviously won't be present in second case). class NullPin : public IPin { public: diff --git a/include/irq_dispatch.hpp b/include/irq_dispatch.hpp new file mode 100644 index 0000000..39ff482 --- /dev/null +++ b/include/irq_dispatch.hpp @@ -0,0 +1,25 @@ +/* + * This file is part of the Peripheral Template Library project. + * + * Copyright (c) 2012-2013 Paul Sokolovsky + * + * This library is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This library 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library. If not, see . + */ +#ifdef __MSP430__ +#include +#elif defined(__ARM_ARCH_7M__) +#include +#else +#error Unknown platform in irq_dispatch.hpp +#endif diff --git a/include/irq_dispatch_base.hpp b/include/irq_dispatch_base.hpp new file mode 100644 index 0000000..840250f --- /dev/null +++ b/include/irq_dispatch_base.hpp @@ -0,0 +1,79 @@ +/* + * This file is part of the Peripheral Template Library project. + * + * Copyright (c) 2012-2013 Paul Sokolovsky + * + * This library is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This library 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library. If not, see . + */ +#ifndef _IRQ_DISPATCH_BASE_HPP +#define _IRQ_DISPATCH_BASE_HPP + +namespace PTL { + +/** + ** Device-independent helper macros/classes for interrupt dispatching + **/ + +/* Null MCU device block, used as a placeholder. */ +class NullBlock +{ +public: + const static int block_type = 0; +}; + +/* + * We want to declare a function whose body will be executed only + * when (static) condition is true. We have to use metaprog conditionals + * here, because if condition is false, func body may not compile at all + * (for example because class it opeartes on lacks specific methods). + * So, we use 2 complementary implementations of func: one with empty + * body for case of condition == false, and one with the given body + * in case it's true. All is wrapped with macros for readability. + */ + +#define IRQ_DISPATCH_HELPER(func_name, cond) \ +template \ +inline void func_name(uint8_t val = 0, typename meta::enable_if<(!(cond))>::type* = 0) {} \ +\ +template \ +inline void func_name(uint8_t val = 0, typename meta::enable_if<(cond)>::type* = 0) \ + +/* Apply templated function to list of template args. Note: this and other similar + funcs depend on particular number of arguments passed and must be updated to + pass more. */ +#define APPLY_TO_ALL(func, b1, b2, b3) { func(); func(); func(); } +/* Apply templated function to list of template args, also passing (runtime) argument + to a function. */ +#define APPLY_TO_ALL_ARG(func, arg, b1, b2, b3) { func(arg); func(arg); func(arg); } + +/* Define handler function for particular hardware IRQ. */ +#define HANDLER(vector, dispatch_func) \ + static void vector##Handler() \ + { \ + APPLY_TO_ALL(dispatch_func, b1, b2, b3); \ + } + +/* Define handler function for particular hardware IRQ, caching value + of a register allowing to demux multiplexed IRQs. This cached value + is passed to each PTL handler. */ +#define HANDLER_CACHE_REG(vector, dispatch_func, reg) \ + static void vector##Handler() \ + { \ + uint8_t cache = reg; \ + APPLY_TO_ALL_ARG(dispatch_func, cache, b1, b2, b3); \ + } + +} // namespace + +#endif // _IRQ_DISPATCH_BASE_HPP diff --git a/include/lpc/lpc800_bootrom.hpp b/include/lpc/lpc800_bootrom.hpp new file mode 100644 index 0000000..922304b --- /dev/null +++ b/include/lpc/lpc800_bootrom.hpp @@ -0,0 +1,34 @@ +#define BOOTROM 0x1fff0000 +#define BOOTROM_SIZE 8192 +// ROM In-Application Programming routine +// +1 to signify this is Thumb code +#define BOOTROM_IAP (BOOTROM + BOOTROM_SIZE - 16 + 1) +#define BOOTROM_DEVICE_DRIVER_TABLE (BOOTROM + BOOTROM_SIZE - 8) + +// cmd and result can point to same area of memory (UM10601 p.269) +typedef void (*IAPFuncPtr)(uint32_t *cmd, uint32_t *result); + +#define DEVICE_DRIVER_FUNC_TABLE(device_no) ((*(uint32_t**)BOOTROM_DEVICE_DRIVER_TABLE)[device_no]) + +struct LPC800_PWRD { + void (*set_pll)(unsigned int cmd[4], unsigned int resp[2]); + void (*set_power)(unsigned int cmd[4], unsigned int resp[2]); +}; + +struct LPC800_DeviceDrivers { + void *res0; + void *res1; + void *res2; + struct LPC800_PWRD *PWRD; + void *res4; + struct LPC800_I2CD *I2CD; + void *res6; + void *res7; + void *res8; + struct LPC800_USARTD *USARTD; +}; + +#define DeviceDrivers (*(struct LPC800_DeviceDrivers**)BOOTROM_DEVICE_DRIVER_TABLE) + +// Example usage: +//DeviceDrivers->PWRD->set_power(); diff --git a/include/msp430/delay_static_msp430.hpp b/include/msp430/delay_static_msp430.hpp index bd689f3..628ca69 100644 --- a/include/msp430/delay_static_msp430.hpp +++ b/include/msp430/delay_static_msp430.hpp @@ -72,6 +72,7 @@ static ALWAYS_INLINE void __delay_cycles2(long delay) quot = (delay - 4) / 4 - 1; // cycles stops on 0 -> -1 transition, i.e. 1 extra iteration rem = (delay - 4) % 4; +#ifndef __clang__ asm( "mov %A0, r14 \n" "mov %B0, r15 \n" @@ -81,7 +82,17 @@ static ALWAYS_INLINE void __delay_cycles2(long delay) "jc 1b \n" // 2 cycles, msp430 has weird C flag value for substracts : : "ir" (quot) : "r14", "r15" ); - +#else + asm( + "mov %0, r14 \n" + "mov %1, r15 \n" + "1: \n" + "sub #1, r14 \n" // 1 cycle + "subc #0, r15 \n" // 1 cycle + "jc 1b \n" // 2 cycles, msp430 has weird C flag value for substracts + : : "ir" ((uint16_t)(quot & 0xffff)), "ir" ((uint16_t)(quot >> 16)) : "r14", "r15" + ); +#endif // It might be possible to optimize these, but it's boring uint16_t low = (uint16_t)quot; uint16_t hiw = quot >> 16; diff --git a/include/msp430/gpio_msp430.hpp b/include/msp430/gpio_msp430.hpp index b8a4fb7..bdfe092 100644 --- a/include/msp430/gpio_msp430.hpp +++ b/include/msp430/gpio_msp430.hpp @@ -25,19 +25,20 @@ namespace PTL { -template -class Port : public IPort +template +class Port : public IPort { public: + static const int base = in_reg_; static const int in_reg = in_reg_; static const int out_reg = out_reg_; static const int dir_reg = dir_reg_; static const int resistor_reg = resistor_reg_; }; -typedef Port P1; -typedef Port P2; -typedef Port P3; +typedef Port P1; +typedef Port P2; +typedef Port P3; template class Pin : public PortPin< Pin, port, bit > diff --git a/include/msp430/gpiodyn_msp430.hpp b/include/msp430/gpiodyn_msp430.hpp new file mode 100644 index 0000000..deab8cc --- /dev/null +++ b/include/msp430/gpiodyn_msp430.hpp @@ -0,0 +1,134 @@ +/* + * This file is part of the Peripheral Template Library project. + * + * Copyright (c) 2012-2013 Paul Sokolovsky + * + * This library is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This library 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library. If not, see . + */ +#ifndef _GPIODYN_MSP430_HPP +#define _GPIODYN_MSP430_HPP + +#include +#include +#include + +namespace PTL { + +struct DynaPort_Store { + // msp430 SFR (including port) registers are located in + // first 256 bytes of address space, plus there's movzx + // instruction. So, we can optimize port base addres + // storage to byte w/o any overhead. + uint8_t base; + + DynaPort_Store() {} + DynaPort_Store(uint8_t port_no) { + set(port_no); + } + + void set(uint8_t port_no) { + switch (port_no) { + case 0: + base = P1::base; + break; + case 1: + base = P2::base; + break; + case 2: + base = P3::base; + break; + } + } +}; + +struct DynaPin_Store : DynaPort_Store { + // Pin mask, named for consistency with Bit<>::value + uint8_t value; + + DynaPin_Store(uint8_t port_no, uint8_t pin_no) { + set(port_no, pin_no); + } + + void set(uint8_t port_no, uint8_t pin_no) { + DynaPort_Store::set(port_no); + value = 1 << pin_no; + } +}; + +// Here's caveat: template param really should be DynaPort_Store&, +// but C++ in template class DynaPin "typedef DynaPort port;" then +// cannot cast from DynaPin_Store& to DynaPort_Store& - implicitly or +// explicitly. So, little choice: dynamic port backing storage takes +// as much as dynamic pin, and the other field is not really used. +// C++, grow smarter! +template +class DynaPort : public IPort { +public: + static void enable() { store.base; } +}; + +template +class DynaPin : public IPin, uint8_t> { + static int in_reg(int base) { return base; } + static int out_reg(int base) { return base + 1; } + static int dir_reg(int base) { return base + 2; } + static int ren_reg(int base) { return base == P3::base ? P3REN_ : base + 7; } + +public: + typedef uint8_t width; + typedef DynaPort port; + + static width value() + { + return _REG8(in_reg(store.base)) & store.value; + } + static void high() + { + _REG8(out_reg(store.base)) |= store.value; + } + static void low() + { + _REG8(out_reg(store.base)) &= ~store.value; + } + static void toggle() + { + _REG8(out_reg(store.base)) ^= store.value; + } + static void output() + { + _REG8(dir_reg(store.base)) |= store.value; + } + static void input() + { + _REG8(dir_reg(store.base)) &= ~store.value; + } + static void pulloff() + { + _REG8(ren_reg(store.base)) &= ~store.value; + } + static void pullup() + { + _REG8(ren_reg(store.base)) |= store.value; + _REG8(out_reg(store.base)) |= store.value; + } + static void pulldown() + { + _REG8(ren_reg(store.base)) |= store.value; + _REG8(out_reg(store.base)) &= ~store.value; + } +}; + +} // namespace + +#endif //_GPIODYN_MSP430_HPP diff --git a/include/msp430/irq_dispatch_msp430.hpp b/include/msp430/irq_dispatch_msp430.hpp index 2ae535e..58906e7 100644 --- a/include/msp430/irq_dispatch_msp430.hpp +++ b/include/msp430/irq_dispatch_msp430.hpp @@ -22,32 +22,12 @@ #include // interrupt(X) define. TODO: get rid of? #include +#include namespace PTL { -class NullBlock -{ -public: - const static int block_type = 0; -}; - -/* - * We want to declare a function here whose body will be executed only - * when condition is true. We have to use metaprog conditionals here, - * because if condition is false, func body may not compile at all - * (for example because class it opeartes on lacks specific methods). - * So, we use 2 complementary implementations of func: one with empty - * body for case of condition == false, and one with the given body - * in case it's true. All is wrapped with macros for readability. - */ - -#define IRQ_DISPATCH_HELPER(func_name, cond) \ -template \ -inline void func_name(uint8_t val = 0, typename meta::enable_if<(!(cond))>::type* = 0) {} \ -\ -template \ -inline void func_name(uint8_t val = 0, typename meta::enable_if<(cond)>::type* = 0) \ - +/* Functions to dispatch specific hardware IRQ down to a specific + MCU block and its PTL irq events. */ IRQ_DISPATCH_HELPER(do_timer_main, block::block_type == MSP430_TIMER) { @@ -94,23 +74,8 @@ IRQ_DISPATCH_HELPER(do_usci_tx, block::block_type == MSP430_USCI) block::irq_tx(); } - -#define APPLY_TO_ALL(func, b1, b2, b3) { func(); func(); func(); } -#define APPLY_TO_ALL_ARG(func, arg, b1, b2, b3) { func(arg); func(arg); func(arg); } - -#define HANDLER(vector, dispatch_func) \ - static interrupt(vector) vector##_handler() \ - { \ - APPLY_TO_ALL(dispatch_func, b1, b2, b3); \ - } - -#define HANDLER_CACHE_REG(vector, dispatch_func, reg) \ - static interrupt(vector) vector##_handler() \ - { \ - uint8_t cache = reg; \ - APPLY_TO_ALL_ARG(dispatch_func, cache, b1, b2, b3); \ - } - +/* Class which encapsulates all hardware -> PTL dispatchers. Mostly + used to allow variable list of blocks to dispatch to. */ template class IrqDispatch { @@ -121,6 +86,30 @@ class IrqDispatch HANDLER_CACHE_REG(USCIAB0TX_VECTOR, do_usci_tx, IFG2); }; + +/* Dispatch IRQ from MCU and compiler specific "IRQ vector" function. This + is expected to be fully optimized out by inlining. + What to change when porting: name/attributes of function defined. */ +#define CALL_HANDLER(dispatcher, vector) \ + interrupt(vector) vector##Handler() \ + { \ + dispatcher::vector##Handler(); \ + } + +/* Global IRQ dispatch declaration macro - that's what user uses in the + application code. Note that there can be at most one IRQ_DISPATCH + per application. Specifying all irq-receiving classes at once allows + to suffle/sort/group irq handlers to produce optimal code - that's + what all fucntions/classes/macros above do actually. + What to change when porting: list of CALL_HANDLER calls. */ +#define IRQ_DISPATCH(blocks...) \ + template class IrqDispatch; \ + typedef IrqDispatch _irq_dispatch; \ + CALL_HANDLER(_irq_dispatch, TIMER0_A1_VECTOR); \ + CALL_HANDLER(_irq_dispatch, TIMER0_A0_VECTOR); \ + CALL_HANDLER(_irq_dispatch, USCIAB0RX_VECTOR); \ + CALL_HANDLER(_irq_dispatch, USCIAB0TX_VECTOR); + } // namespace #endif // _IRQ_DISPATCH_MSP430_HPP diff --git a/include/msp430/irq_dispatch_msp430.hpp.old b/include/msp430/irq_dispatch_msp430.hpp.old new file mode 100644 index 0000000..2ae535e --- /dev/null +++ b/include/msp430/irq_dispatch_msp430.hpp.old @@ -0,0 +1,126 @@ +/* + * This file is part of the Peripheral Template Library project. + * + * Copyright (c) 2012-2013 Paul Sokolovsky + * + * This library is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This library 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library. If not, see . + */ +#ifndef _IRQ_DISPATCH_MSP430_HPP +#define _IRQ_DISPATCH_MSP430_HPP + +#include +// interrupt(X) define. TODO: get rid of? +#include + +namespace PTL { + +class NullBlock +{ +public: + const static int block_type = 0; +}; + +/* + * We want to declare a function here whose body will be executed only + * when condition is true. We have to use metaprog conditionals here, + * because if condition is false, func body may not compile at all + * (for example because class it opeartes on lacks specific methods). + * So, we use 2 complementary implementations of func: one with empty + * body for case of condition == false, and one with the given body + * in case it's true. All is wrapped with macros for readability. + */ + +#define IRQ_DISPATCH_HELPER(func_name, cond) \ +template \ +inline void func_name(uint8_t val = 0, typename meta::enable_if<(!(cond))>::type* = 0) {} \ +\ +template \ +inline void func_name(uint8_t val = 0, typename meta::enable_if<(cond)>::type* = 0) \ + + +IRQ_DISPATCH_HELPER(do_timer_main, block::block_type == MSP430_TIMER) +{ + // Reading status also acks IRQ + switch (block::irq_status()) { + // 1. MSP headers define TAIV bits per-timer, but they are actually + // the same for all timers (hopefully for all MCUs!) + // 2. Method calls below are expected to be inlined, and if + // one is empry, entire branch to be dead-code elimated. + case TA0IV_TAIFG: + block::irq_reset(); + break; + case TA0IV_TACCR1: + block::irq_capture_compare1(); + break; + case TA0IV_TACCR2: + block::irq_capture_compare2(); + break; + } +} + +IRQ_DISPATCH_HELPER(do_timer_cc0, block::block_type == MSP430_TIMER) +{ + block::irq_capture_compare0(); +} + +IRQ_DISPATCH_HELPER(do_usci_rx, block::block_type == MSP430_USCI) +{ + uint8_t ifg2 = val; + if (block::usci::no == 'A' && (ifg2 & UCA0RXIFG)) + block::irq_rx(); + + if (block::usci::no == 'B' && (ifg2 & UCB0RXIFG)) + block::irq_rx(); +} + +IRQ_DISPATCH_HELPER(do_usci_tx, block::block_type == MSP430_USCI) +{ + uint8_t ifg2 = val; + if (block::usci::no == 'A' && (ifg2 & UCA0TXIFG)) + block::irq_tx(); + + if (block::usci::no == 'B' && (ifg2 & UCB0TXIFG)) + block::irq_tx(); +} + + +#define APPLY_TO_ALL(func, b1, b2, b3) { func(); func(); func(); } +#define APPLY_TO_ALL_ARG(func, arg, b1, b2, b3) { func(arg); func(arg); func(arg); } + +#define HANDLER(vector, dispatch_func) \ + static interrupt(vector) vector##_handler() \ + { \ + APPLY_TO_ALL(dispatch_func, b1, b2, b3); \ + } + +#define HANDLER_CACHE_REG(vector, dispatch_func, reg) \ + static interrupt(vector) vector##_handler() \ + { \ + uint8_t cache = reg; \ + APPLY_TO_ALL_ARG(dispatch_func, cache, b1, b2, b3); \ + } + +template +class IrqDispatch +{ +public: + HANDLER(TIMER0_A1_VECTOR, do_timer_main); + HANDLER(TIMER0_A0_VECTOR, do_timer_cc0); + HANDLER_CACHE_REG(USCIAB0RX_VECTOR, do_usci_rx, IFG2); + HANDLER_CACHE_REG(USCIAB0TX_VECTOR, do_usci_tx, IFG2); +}; + +} // namespace + +#endif // _IRQ_DISPATCH_MSP430_HPP diff --git a/include/msp430/irqs_msp430.hpp b/include/msp430/irqs_msp430.hpp new file mode 100644 index 0000000..a24956b --- /dev/null +++ b/include/msp430/irqs_msp430.hpp @@ -0,0 +1,55 @@ +/* + * This file is part of the Peripheral Template Library project. + * + * Copyright (c) 2012 Paul Sokolovsky + * + * This library is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This library 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library. If not, see . + */ +#ifndef _IRQ_MSP430_HPP +#define _IRQ_MSP430_HPP + +namespace PTL { + +struct TimerIrqHandlers +{ + static void irq_reset(); + static void irq_capture_compare0(); + static void irq_capture_compare1(); + static void irq_capture_compare2(); +}; + +struct CommIrqHandlers +{ + static void irq_rx(); + static void irq_tx(); +}; + +struct ComparatorIrqHandlers +{ + static void irq_change(); +}; + +struct AdcIrqHandlers +{ + static void irq_done(); +}; + +struct GpioIrqHandlers +{ + static void irq_pin_change(); +}; + +} // namespace + +#endif // _IRQ_MSP430_HPP diff --git a/include/msp430/spi_msp430.hpp b/include/msp430/spi_msp430.hpp index 4612bd2..6ce9079 100644 --- a/include/msp430/spi_msp430.hpp +++ b/include/msp430/spi_msp430.hpp @@ -50,6 +50,7 @@ class SPI : public ISPI, public SPIBlockXfer< SPI >, public MSP4 UCB0BR0 = 1; UCB0BR1 = 0; + // Set SPI altfuncs for pins P1SEL |= SCLK | SDI | SDO; P1SEL2 |= SCLK | SDI | SDO; // Per MSP430 manual, direction of SCLK/MOSI/MISO is controlled @@ -60,6 +61,7 @@ class SPI : public ISPI, public SPIBlockXfer< SPI >, public MSP4 static void disable() { UCB0CTL1 = UCSWRST; + // Unset SPI altfuncs, back to GPIO mode P1SEL &= ~(SCLK | SDI | SDO); P1SEL2 &= ~(SCLK | SDI | SDO); } diff --git a/include/msp430/timer_irq_dispatch_msp430.hpp.function b/include/msp430/timer_irq_dispatch_msp430.hpp.function new file mode 100644 index 0000000..5cc411e --- /dev/null +++ b/include/msp430/timer_irq_dispatch_msp430.hpp.function @@ -0,0 +1,87 @@ +/* + * This file is part of the Peripheral Template Library project. + * + * Copyright (c) 2012 Paul Sokolovsky + * + * This library is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This library 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library. If not, see . + */ +#ifndef _TIMER_MSP430_DISPATCH_HPP +#define _TIMER_MSP430_DISPATCH_HPP + +#include +// interrupt(X) define. TODO: get rid of? +#include + +//template +template +class TimerIrqDispatch +{ + +public: + ALWAYS_INLINE static void handle_main() { + overflow(); + // Ack IRQ + switch (timer::irq_status()) { + // MSP headers define TAIV bits per-timer, but they are actually + // the same for all timers (hopefully for all MCUs!) + case TA0IV_TAIFG: + overflow(); + break; +#if 0 + case TA0IV_TACCR1: + if (cc1) cc1(); + break; + case TA0IV_TACCR2: + if (cc2) cc2(); + break; +#endif + } + } + + ALWAYS_INLINE static void handle_cc0() { +#if 0 + if (cc0) cc0(); +#endif + } + + // The idea would be to *define* *Timer's* irq_handler*() here (as a friend + // method). Unfortunately that's not supported by C++. So, we'll define + // irq handlers as ours, at least Timer::irq_no_* allows us to avoid duplication + // and abstract it a bit. + + // Unfortunately, this doesn't work with gcc 4.5 either + +#if 0 + static interrupt(Timer::irq_no_main) irq_handler() + { + handle_main(); + } + + static interrupt(Timer::irq_no_cc0) irq_handler_cc0() + } + if (cc0) cc0(); + } +#endif +}; + +#define irq_dispatch(timer, dispatcher) \ + template <> \ + void timer::irq_handler() \ + { dispatcher::handle_main(); } \ + template <> \ + void timer::irq_handler_cc0() \ + { dispatcher::handle_cc0(); } + + +#endif // _TIMER_MSP430_DISPATCH_HPP diff --git a/include/protocol/avr_isp.hpp b/include/protocol/avr_isp.hpp new file mode 100644 index 0000000..8c95802 --- /dev/null +++ b/include/protocol/avr_isp.hpp @@ -0,0 +1,67 @@ +/* + * This file is part of the Peripheral Template Library project. + * + * Copyright (c) 2012-2014 Paul Sokolovsky + * + * This library is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This library 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library. If not, see . + */ +#include +#include +#include + +// TODO: WIP, untested + +namespace PTL { + +template +class AVR_ISP +{ +public: + static void init() + { + reset_sig::output(); + reset_sig::deassert(); + } + + static void start() + { + reset_sig::assert(); + //AVR910 + delayer::delay_ms(20); + } + + static void send_cmd(uint16_t cmd, uint8_t b2, uint8_t b3) + { + spi::transfer(cmd >> 8); + spi::transfer(cmd & 0xff); + spi::transfer(b2); + return spi::transfer(b3); + } + + static void enable_program() + { + if (send_cmd(CMD_Program_Enable, 0x55, 0xaa) != 0x55) + return false; + return true; + } + + static void read_flash(uint16_t addr) + { + uint16_t cmd = addr & 1 ? CMD_Read_Flash_High : CMD_Read_Flash_Low; + addr >>= 1; + return send_cmd(cmd | (addr >> 8), addr & 0xff, 0); + } +}; + +} // namespace diff --git a/include/protocol/usb.hpp b/include/protocol/usb.hpp new file mode 100644 index 0000000..116b752 --- /dev/null +++ b/include/protocol/usb.hpp @@ -0,0 +1,48 @@ +/* + * This file is part of the Peripheral Template Library project. + * + * Copyright (c) 2012-2014 Paul Sokolovsky + * + * This library is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This library 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library. If not, see . + */ + +// TODO: WIP, untested + +namespace PTL { + +template <> +class USB +{ + PID _next_data = PID_DATA0; +public: + static void xact_in(); + static void xact_out(usb_addr_t addr, usb_endp_t endpoint, uint8_t *data, uint16_t len) + { + send_token(PID_OUT, addr, endpoint); + send_data(next_data(), data, len); + recv_handshake(); + } + static void xact_setup(); + + PID next_data() + { + PID r = _next_data; + if (_next_data == PID_DATA0) + _next_data == PID_DATA1; + else + _next_data == PID_DATA0; + } +}; + +} // namespace diff --git a/include/rtos/buffered_uart.hpp b/include/rtos/buffered_uart.hpp new file mode 100644 index 0000000..60f41d1 --- /dev/null +++ b/include/rtos/buffered_uart.hpp @@ -0,0 +1,95 @@ +/* + * This file is part of the Peripheral Template Library project. + * + * Copyright (c) 2012-2013 Paul Sokolovsky + * + * This library is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This library 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library. If not, see . + */ +#include +#include + +namespace PTL { + +template class buffer = PTL::CircularBuffer> +class BufferedUart +{ +public: + + static buffer read_buf; + static buffer write_buf; + + class uart_handlers : public uart + { + public: + static void irq_rx() + { + if (read_buf.full()) + overrun(); + else + read_buf.push(uart::read_async()); + } + static void irq_tx() + { +// assert(!store.write_buf.empty()); + uint8_t c = write_buf.pop(); + uart::write_async(c); + // If there's nothing more to send, disable TX IRQ + // (really "TX Buffer Empty IRQ") + if (write_buf.empty()) + uart::disable_tx_irq(); + } + }; + +public: + static void init() + { + uart::init(); + // We always should expect to receive something + uart::enable_rx_irq(); + // but in the beginning, there's nothing to send - + // interrupts will be enabled on first char to send + uart::disable_tx_irq(); + } + static uint8_t read() + { + while (read_buf.empty()); + uart::disable_rx_irq(); + uint8_t c = read_buf.pop(); + uart::enable_rx_irq(); + return c; + } + static void write(uint8_t c) + { + while (write_buf.full()); + uart::disable_tx_irq(); + write_buf.push(c); + // We added new char, so there's something to send - enable TX IRQ + uart::enable_tx_irq(); + } + + // Override + static void overrun() + { + } + +}; + +// Explicit instantiation of static fields +template class buffer> + buffer BufferedUart::read_buf; +template class buffer> + buffer BufferedUart::write_buf; + +} // namespace diff --git a/include/rtos/circular.hpp b/include/rtos/circular.hpp index 502303c..1d0fb31 100644 --- a/include/rtos/circular.hpp +++ b/include/rtos/circular.hpp @@ -18,6 +18,10 @@ */ namespace PTL { + +// Note: only methods marked "volatile" are thread/interrupt safe +// All other methods should be called only from critical section +// (e.g., with interrupts disabled). template class CircularBuffer { @@ -34,7 +38,7 @@ class CircularBuffer return _size; } - bool empty() const + bool empty() const volatile { return _size == 0; } @@ -56,7 +60,7 @@ class CircularBuffer return e; } - elem front() const + elem front() const volatile { return *head; } @@ -71,7 +75,7 @@ class CircularBuffer return max_size_ - _size; } - bool full() const + bool full() const volatile { return _size == max_size_; } @@ -102,6 +106,11 @@ class CircularBuffer return 0; return pop(); } + + /* Read/write interface */ + void write(elem b) { push_checked(b); } + // Checking that buffer contains enough elements is on the caller + elem read() { return pop(); } }; } // namespace diff --git a/include/rtos/virt_timer.hpp b/include/rtos/virt_timer.hpp new file mode 100644 index 0000000..9354a78 --- /dev/null +++ b/include/rtos/virt_timer.hpp @@ -0,0 +1,39 @@ +/* + * This file is part of the Peripheral Template Library project. + * + * Copyright (c) 2012 Paul Sokolovsky + * + * This library is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This library 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library. If not, see . + */ +#ifndef _VIRT_TIMER_HPP +#define _VIRT_TIMER_HPP + +#include + +namespace PTL { + +template +class VirtTimer +{ + typename timer::width start; + typename timer::width period; +public: + void start(typename timer::width period) { start = timer::value(); this->period = period; } + bool expired() + { return timer::elapsed(timer::value(), start) > period; } +}; + +} // namespace + +#endif //_VIRT_TIMER_HPP diff --git a/include/tm4/cpu_tm4.hpp b/include/tm4/cpu_tm4.hpp new file mode 100644 index 0000000..a0037fb --- /dev/null +++ b/include/tm4/cpu_tm4.hpp @@ -0,0 +1,35 @@ +/* + * This file is part of the Peripheral Template Library project. + * + * Copyright (c) 2012 Paul Sokolovsky + * + * This library is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This library 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library. If not, see . + */ +#pragma once +#include + +namespace PTL { + +class TM4CPU {}; + +enum { TM4_UART }; + +template <> +class CPU : public CPU +{ +}; + +typedef CPU cpu; + +} // namespace diff --git a/include/tm4/uart_tm4.hpp b/include/tm4/uart_tm4.hpp index 9c3f8ca..7657298 100644 --- a/include/tm4/uart_tm4.hpp +++ b/include/tm4/uart_tm4.hpp @@ -20,6 +20,7 @@ #define _UART_LM4_HPP #include +#include namespace PTL { @@ -35,6 +36,8 @@ class UART typedef Pin txpin; public: + const static int block_type = TM4_UART; + static void init() { SYSCTL->RCGCUART |= Bit0::value; diff --git a/scripts/msp430_rate.py b/scripts/msp430_rate.py new file mode 100644 index 0000000..f57abbd --- /dev/null +++ b/scripts/msp430_rate.py @@ -0,0 +1,23 @@ +def calc(freq, baud): + return ((freq * 8 * 2 / baud) - (freq / baud * 8 * 2) + 1) / 2 + +#for baud in (9600, 19200, 38400, 56000, 115200, 128000, 256000): + +MOD = [0, 0x02, 0x22, 0x2a, 0xaa, 0xae, 0xee, 0xfe] + + +for baud in (256000,): + div = 1000000 / baud + mod = calc(1000000, baud) + 0 + print baud, mod + t_ideal = 1.0 / baud + print "ideal bit length (s):", t_ideal + bit = 1 + err = 0.0 + while bit != 0x100: + t = (div + int(bool(MOD[mod] & bit))) / 1000000.0 + print t, t - t_ideal + err += t - t_ideal + bit <<= 1 + + print err / t_ideal * 100 diff --git a/tests/Makefile b/tests/Makefile index 259cb08..b8037f1 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -1,7 +1,7 @@ PTL_PATH = .. include $(PTL_PATH)/Makefile.rules -ALL = sub_mod circular +ALL = sub_mod circular nearptr .PHONY: $(ALL) .PRECIOUS: $(TARGETDIR)/%.o @@ -10,3 +10,4 @@ all: $(ALL) sub_mod: $(TARGETDIR)/sub_mod circular: $(TARGETDIR)/circular +nearptr: $(TARGETDIR)/nearptr diff --git a/tests/nearptr.cpp b/tests/nearptr.cpp new file mode 100644 index 0000000..6812f2a --- /dev/null +++ b/tests/nearptr.cpp @@ -0,0 +1,28 @@ +#include +#include +#include +#include + +const int base1 = 0x20000000; +char base2[256] = "foobar"; +char *base3 = (char*)malloc(256); + +//template using Ptr = AbsNearPtr; +template using Ptr = ArrayNearPtr; +//template using Ptr = VarNearPtr; + +void func(Ptr p) +{ + assert(sizeof(p) == 2); + assert((char*)p == base2 + 3); + assert(*p == 'b'); + assert(p[1] == 'a'); +} + +int main() +{ + Ptr p = &base2[3]; + assert(sizeof(p) == 2); + // Pass into a function to avoid inlining optimizations + func(p); +}