From b67d281d85bd59a9738a9a43c4db1027f81d9208 Mon Sep 17 00:00:00 2001 From: Marti Bolivar Date: Wed, 8 Dec 2010 23:39:37 -0500 Subject: Servo library tested and debugged. Some additional HardwareTimer methods introduced to make this convenient; ancillary libmaple/timers.h changes resulted. --- Makefile | 1 + libmaple/timers.c | 84 +++++++++++++++----- libmaple/timers.h | 134 ++++++++++++++++++------------- libraries/Servo/Servo.cpp | 138 ++++++++++++++++++++++++++++++++ libraries/Servo/Servo.h | 116 +++++++++++++++++++++++++++ libraries/Servo/rules.mk | 35 ++++++++ wirish/HardwareTimer.cpp | 150 ++++++++++++++++++++++++----------- wirish/HardwareTimer.h | 176 +++++++++++++++++++++++++++-------------- wirish/boards.h | 101 ++++++++++++----------- wirish/comm/HardwareSerial.cpp | 11 ++- wirish/comm/HardwareSerial.h | 6 +- wirish/pwm.c | 4 +- wirish/time.h | 6 +- wirish/wirish.c | 2 +- wirish/wirish_math.cpp | 5 -- wirish/wirish_math.h | 5 +- 16 files changed, 722 insertions(+), 252 deletions(-) create mode 100644 libraries/Servo/Servo.cpp create mode 100644 libraries/Servo/Servo.h create mode 100644 libraries/Servo/rules.mk diff --git a/Makefile b/Makefile index 2c61bd7..7fc5738 100644 --- a/Makefile +++ b/Makefile @@ -66,6 +66,7 @@ endif # Set all submodules here LIBMAPLE_MODULES := $(SRCROOT)/libmaple LIBMAPLE_MODULES += $(SRCROOT)/wirish +LIBMAPLE_MODULES += $(SRCROOT)/libraries/Servo # call each module rules.mk $(foreach m,$(LIBMAPLE_MODULES),$(eval $(call LIBMAPLE_MODULE_template,$(m)))) diff --git a/libmaple/timers.c b/libmaple/timers.c index c369d1f..29aeeba 100644 --- a/libmaple/timers.c +++ b/libmaple/timers.c @@ -73,16 +73,20 @@ struct timer_dev timer_dev_table[] = { /* This function should probably be rewriten to take (timer_num, mode) * and have prescaler set elsewhere. The mode can be passed through to * set_mode at the end */ -void timer_init(uint8 timer_num, uint16 prescale) { +void timer_init(timer_dev_num timer_num, uint16 prescale) { /* TODO: doesn't catch 6+7 */ - ASSERT((timer_num != TIMER6) && (timer_num != TIMER7)); timer_port *timer = timer_dev_table[timer_num].base; uint8 is_advanced = 0; - if((timer_num == TIMER1) || (timer_num == TIMER8)) { + if (timer_num == TIMER1) { is_advanced = 1; } +#if NR_TIMERS >= 8 + if (timer_num == TIMER8) { + is_advanced = 1; + } +#endif rcc_clk_enable(timer_dev_table[timer_num].rcc_dev_num); @@ -125,48 +129,61 @@ void timer_init(uint8 timer_num, uint16 prescale) { } /* Stops the counter; the mode and settings are not modified */ -void timer_pause(uint8 timer_num) { +void timer_pause(timer_dev_num timer_num) { timer_port *timer = timer_dev_table[timer_num].base; timer->CR1 &= ~(0x0001); // CEN } /* Starts the counter; the mode and settings are not modified */ -void timer_resume(uint8 timer_num) { +void timer_resume(timer_dev_num timer_num) { timer_port *timer = timer_dev_table[timer_num].base; timer->CR1 |= 0x0001; // CEN } +/* Returns the current timer counter value. Probably very inaccurate + * if the counter is running with a low prescaler. */ +uint16 timer_get_count(timer_dev_num timer_num) { + timer_port *timer = timer_dev_table[timer_num].base; + + return timer->CNT; +} + /* This function sets the counter value via register for the specified * timer. Can't think of specific usecases except for resetting to * zero but it's easy to implement and allows for "creative" * programming */ -void timer_set_count(uint8 timer_num, uint16 value) { +void timer_set_count(timer_dev_num timer_num, uint16 value) { timer_port *timer = timer_dev_table[timer_num].base; timer->CNT = value; } -/* Returns the current timer counter value. Probably very inaccurate - * if the counter is running with a low prescaler. */ -uint16 timer_get_count(uint8 timer_num) { +/* Get the prescaler buffer value (remember, the actual prescaler + * doesn't get set until an update event). */ +uint16 timer_get_prescaler(timer_dev_num timer_num) { timer_port *timer = timer_dev_table[timer_num].base; - - return timer->CNT; + return timer->PSC; } /* Sets the prescaler */ -void timer_set_prescaler(uint8 timer_num, uint16 prescale) { +void timer_set_prescaler(timer_dev_num timer_num, uint16 prescale) { timer_port *timer = timer_dev_table[timer_num].base; timer->PSC = prescale; } +/* Get the reload value for the entire timer. */ +uint16 timer_get_reload(timer_dev_num timer_num) { + timer_port *timer = timer_dev_table[timer_num].base; + return timer->ARR; +} + /* This sets the "reload" or "overflow" value for the entire timer. We * should probably settle on either "reload" or "overflow" to prevent * confusion? */ -void timer_set_reload(uint8 timer_num, uint16 max_reload) { +void timer_set_reload(timer_dev_num timer_num, uint16 max_reload) { timer_port *timer = timer_dev_table[timer_num].base; timer->ARR = max_reload; @@ -211,7 +228,7 @@ void timer_disable_all(void) { } /* Sets the mode of individual timer channels, including a DISABLE mode */ -void timer_set_mode(uint8 timer_num, uint8 channel, TimerMode mode) { +void timer_set_mode(timer_dev_num timer_num, uint8 channel, TimerMode mode) { timer_port *timer = timer_dev_table[timer_num].base; ASSERT(channel >= 1); @@ -286,18 +303,36 @@ void timer_set_mode(uint8 timer_num, uint8 channel, TimerMode mode) { } } +uint16 timer_get_compare_value(timer_dev_num timer_num, uint8 channel_num) { + /* faster: just read TIMERx_CHy_CCR (see timers.h) */ + ASSERT(channel_num > 0 && channel_num <= 4); + timer_port *timer = timer_dev_table[timer_num].base; + switch(channel_num) { + case 1: + return timer->CCR1; + case 2: + return timer->CCR2; + case 3: + return timer->CCR3; + case 4: + return timer->CCR4; + default: /* in case ASSERT is disabled */ + return 0; + } +} + /* This sets the compare value (aka the trigger) for a given timer * channel */ -void timer_set_compare_value(uint8 timer_num, - uint8 compare_num, +void timer_set_compare_value(timer_dev_num timer_num, + uint8 channel_num, uint16 value) { + ASSERT(channel_num > 0 && channel_num <= 4); + /* The faster version of this function is the inline timer_pwm_write_ccr */ timer_port *timer = timer_dev_table[timer_num].base; - ASSERT(compare_num > 0 && compare_num <= 4); - - switch(compare_num) { + switch(channel_num) { case 1: timer->CCR1 = value; break; @@ -315,7 +350,7 @@ void timer_set_compare_value(uint8 timer_num, /* Stores a pointer to the passed usercode interrupt function and configures * the actual ISR so that it will actually be called */ -void timer_attach_interrupt(uint8 timer_num, +void timer_attach_interrupt(timer_dev_num timer_num, uint8 compare_num, voidFuncPtr handler) { ASSERT(compare_num > 0 && compare_num <= 4); @@ -327,7 +362,7 @@ void timer_attach_interrupt(uint8 timer_num, nvic_irq_enable(timer_dev_table[timer_num].nvic_dev_num); } -void timer_detach_interrupt(uint8 timer_num, uint8 compare_num) { +void timer_detach_interrupt(timer_dev_num timer_num, uint8 compare_num) { ASSERT(compare_num > 0 && compare_num <= 4); timer_port *timer = timer_dev_table[timer_num].base; @@ -336,6 +371,13 @@ void timer_detach_interrupt(uint8 timer_num, uint8 compare_num) { timer->DIER &= ~(1 << compare_num); // 1-indexed compare nums } +void timer_generate_update(timer_dev_num timer_num) { + /* cause update event by setting UG bit in EGR. updates prescaler + ratio etc. */ + timer_port *timer = timer_dev_table[timer_num].base; + timer->EGR |= 0x1; +} + /* The following are the actual interrupt handlers; 1 for each timer which must * determine which actual compare value (aka channel) was triggered. * diff --git a/libmaple/timers.h b/libmaple/timers.h index d180bab..1d929db 100644 --- a/libmaple/timers.h +++ b/libmaple/timers.h @@ -82,7 +82,7 @@ extern "C"{ #endif -typedef volatile uint32* TimerCCR; +typedef volatile uint16* TimerCCR; #define TIMER1_BASE 0x40012C00 #define TIMER2_BASE 0x40000000 @@ -96,41 +96,43 @@ typedef volatile uint32* TimerCCR; #define ARPE BIT(7) // Auto-reload preload enable #define NOT_A_TIMER 0 -#define TIMER_CCR(NUM,CHAN) TIMER ## NUM ## _CH ## CHAN ## _CRR +#define TIMER_CCR(NUM,CHAN) (TIMER ## NUM ## _CH ## CHAN ## _CRR) -#define TIMER1_CH1_CCR TIMER1_BASE + 0x34 -#define TIMER1_CH2_CCR TIMER1_BASE + 0x38 -#define TIMER1_CH3_CCR TIMER1_BASE + 0x3C -#define TIMER1_CH4_CCR TIMER1_BASE + 0x40 +/* Timers 1-4 are present on the entire STM32 line. */ -#define TIMER2_CH1_CCR TIMER2_BASE + 0x34 -#define TIMER2_CH2_CCR TIMER2_BASE + 0x38 -#define TIMER2_CH3_CCR TIMER2_BASE + 0x3C -#define TIMER2_CH4_CCR TIMER2_BASE + 0x40 +#define TIMER1_CH1_CCR ((TimerCCR)(TIMER1_BASE + 0x34)) +#define TIMER1_CH2_CCR ((TimerCCR)(TIMER1_BASE + 0x38)) +#define TIMER1_CH3_CCR ((TimerCCR)(TIMER1_BASE + 0x3C)) +#define TIMER1_CH4_CCR ((TimerCCR)(TIMER1_BASE + 0x40)) -#define TIMER3_CH1_CCR TIMER3_BASE + 0x34 -#define TIMER3_CH2_CCR TIMER3_BASE + 0x38 -#define TIMER3_CH3_CCR TIMER3_BASE + 0x3C -#define TIMER3_CH4_CCR TIMER3_BASE + 0x40 +#define TIMER2_CH1_CCR ((TimerCCR)(TIMER2_BASE + 0x34)) +#define TIMER2_CH2_CCR ((TimerCCR)(TIMER2_BASE + 0x38)) +#define TIMER2_CH3_CCR ((TimerCCR)(TIMER2_BASE + 0x3C)) +#define TIMER2_CH4_CCR ((TimerCCR)(TIMER2_BASE + 0x40)) -#define TIMER4_CH1_CCR TIMER4_BASE + 0x34 -#define TIMER4_CH2_CCR TIMER4_BASE + 0x38 -#define TIMER4_CH3_CCR TIMER4_BASE + 0x3C -#define TIMER4_CH4_CCR TIMER4_BASE + 0x40 +#define TIMER3_CH1_CCR ((TimerCCR)(TIMER3_BASE + 0x34)) +#define TIMER3_CH2_CCR ((TimerCCR)(TIMER3_BASE + 0x38)) +#define TIMER3_CH3_CCR ((TimerCCR)(TIMER3_BASE + 0x3C)) +#define TIMER3_CH4_CCR ((TimerCCR)(TIMER3_BASE + 0x40)) -/* Timer5 and Timer8 are in high-density devices only (such as Maple - Native). Timer6 and Timer7 in these devices have no output compare +#define TIMER4_CH1_CCR ((TimerCCR)(TIMER4_BASE + 0x34)) +#define TIMER4_CH2_CCR ((TimerCCR)(TIMER4_BASE + 0x38)) +#define TIMER4_CH3_CCR ((TimerCCR)(TIMER4_BASE + 0x3C)) +#define TIMER4_CH4_CCR ((TimerCCR)(TIMER4_BASE + 0x40)) + +/* Timers 5 and 8 are in high-density devices only (such as Maple + Native). Timers 6 and 7 in these devices have no output compare pins. */ -#define TIMER5_CH1_CCR TIMER5_BASE + 0x34 -#define TIMER5_CH2_CCR TIMER5_BASE + 0x38 -#define TIMER5_CH3_CCR TIMER5_BASE + 0x3C -#define TIMER5_CH4_CCR TIMER5_BASE + 0x40 +#define TIMER5_CH1_CCR ((TimerCCR)(TIMER5_BASE + 0x34)) +#define TIMER5_CH2_CCR ((TimerCCR)(TIMER5_BASE + 0x38)) +#define TIMER5_CH3_CCR ((TimerCCR)(TIMER5_BASE + 0x3C)) +#define TIMER5_CH4_CCR ((TimerCCR)(TIMER5_BASE + 0x40)) -#define TIMER8_CH1_CCR TIMER8_BASE + 0x34 -#define TIMER8_CH2_CCR TIMER8_BASE + 0x38 -#define TIMER8_CH3_CCR TIMER8_BASE + 0x3C -#define TIMER8_CH4_CCR TIMER8_BASE + 0x40 +#define TIMER8_CH1_CCR ((TimerCCR)(TIMER8_BASE + 0x34)) +#define TIMER8_CH2_CCR ((TimerCCR)(TIMER8_BASE + 0x38)) +#define TIMER8_CH3_CCR ((TimerCCR)(TIMER8_BASE + 0x3C)) +#define TIMER8_CH4_CCR ((TimerCCR)(TIMER8_BASE + 0x40)) /** * Used to configure the behavior of a timer. @@ -149,6 +151,8 @@ typedef enum TimerMode { } TimerMode; typedef struct { + /* Fields up to ARR common to general purpose (2,3,4,5), advanced + control (1,8) and basic (6, 7) timers: */ volatile uint16 CR1; uint16 RESERVED0; volatile uint16 CR2; @@ -173,8 +177,9 @@ typedef struct { uint16 RESERVED10; volatile uint16 ARR; uint16 RESERVED11; - volatile uint16 RCR; - uint16 RESERVED12; + /* Basic timers have none of the following: */ + volatile uint16 RCR; /* Advanced control timers only */ + uint16 RESERVED12; /* Advanced control timers only */ volatile uint16 CCR1; uint16 RESERVED13; volatile uint16 CCR2; @@ -183,25 +188,32 @@ typedef struct { uint16 RESERVED15; volatile uint16 CCR4; uint16 RESERVED16; - volatile uint16 BDTR; // Not used in general purpose timers - uint16 RESERVED17; // Not used in general purpose timers + volatile uint16 BDTR; /* Advanced control timers only */ + uint16 RESERVED17; /* Advanced control timers only */ volatile uint16 DCR; uint16 RESERVED18; volatile uint16 DMAR; uint16 RESERVED19; } timer_port; -/* timer device numbers */ -enum { - TIMER1, - TIMER2, - TIMER3, - TIMER4, - TIMER5, // High density only - TIMER6, // High density only; no compare - TIMER7, // High density only; no compare - TIMER8, // High density only -}; +/** + * Timer device numbers. See STM32 reference manual, chapters 13-15. + */ +/* several locations depend on TIMER1=0, etc.; don't change the + enumerator values to start at 1. */ +typedef enum { + TIMER1, /*< Advanced control timer TIM1 */ + TIMER2, /*< General purpose timer TIM2 */ + TIMER3, /*< General purpose timer TIM3 */ + TIMER4, /*< General purpose timer TIM4 */ +#if NR_TIMERS >= 8 + TIMER5, /*< General purpose timer TIM5; high density only */ + /* TIMER6, /\*< Basic timer TIM6; high density only *\/ */ + /* TIMER7, /\*< Basic timer TIM7; high density only *\/ */ + TIMER8, /*< Advanced control timer TIM8; high density only */ +#endif + TIMER_INVALID /* FIXME: this is starting to seem like a bad idea */ +} timer_dev_num; /* timer descriptor */ struct timer_dev { @@ -218,19 +230,33 @@ extern struct timer_dev timer_dev_table[]; * timer -> {1-4} * prescale -> {1-65535} */ -void timer_init(uint8, uint16); +void timer_init(timer_dev_num, uint16); void timer_disable_all(void); -uint16 timer_get_count(uint8); -void timer_set_count(uint8,uint16); -void timer_pause(uint8); -void timer_resume(uint8); -void timer_set_prescaler(uint8 timer_num, uint16 prescale); -void timer_set_reload(uint8 timer_num, uint16 max_reload); -void timer_set_mode(uint8 timer_num, uint8 compare_num, uint8 mode); -void timer_set_compare_value(uint8 timer_num, uint8 compare_num, uint16 value); -void timer_attach_interrupt(uint8 timer_num, uint8 compare_num, + +uint16 timer_get_count(timer_dev_num); +void timer_set_count(timer_dev_num,uint16); + +void timer_pause(timer_dev_num); +void timer_resume(timer_dev_num); + +uint16 timer_get_prescaler(timer_dev_num timer_num); +void timer_set_prescaler(timer_dev_num timer_num, uint16 prescale); + +uint16 timer_get_reload(timer_dev_num timer_num); +void timer_set_reload(timer_dev_num timer_num, uint16 max_reload); + +/* TODO: timer_get_mode */ +void timer_set_mode(timer_dev_num timer_num, uint8 channel_num, uint8 mode); + +uint16 timer_get_compare_value(timer_dev_num timer_num, uint8 channel_num); +void timer_set_compare_value(timer_dev_num timer_num, uint8 channel_num, uint16 value); + +void timer_attach_interrupt(timer_dev_num timer_num, uint8 channel_num, voidFuncPtr handler); -void timer_detach_interrupt(uint8 timer_num, uint8 compare_num); +void timer_detach_interrupt(timer_dev_num timer_num, uint8 channel_num); + +/* generate UEV */ +void timer_generate_update(timer_dev_num timer_num); /* Turn on PWM with duty_cycle on the specified channel in timer. * This function takes in a pointer to the corresponding CCR diff --git a/libraries/Servo/Servo.cpp b/libraries/Servo/Servo.cpp new file mode 100644 index 0000000..4f60fbe --- /dev/null +++ b/libraries/Servo/Servo.cpp @@ -0,0 +1,138 @@ +/****************************************************************************** + * The MIT License + * + * Copyright (c) 2010, LeafLabs, LLC. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + *****************************************************************************/ + +#include "boards.h" +#include "io.h" +#include "pwm.h" +#include "wirish_math.h" + +#include "Servo.h" + +// Configure prescaler and overflow for a 20msec period (could just +// use HardwareTimer::setPeriod(), but this lets conversions below +// happen more statically, in combination with an inlined map() -- a +// premature optimization? TODO profile speed/size tradeoff) +#define CYC_20MSEC (20000 * CYCLES_PER_MICROSECOND) +#define SERVO_PRE ((uint16)((CYC_20MSEC >> 16) + 1)) +#define SERVO_OVF ((uint16)((CYC_20MSEC / SERVO_PRE) - 1)) +#define SERVO_TAU_USEC \ + ((uint32)(((double)SERVO_OVF) * SERVO_PRE / CYCLES_PER_MICROSECOND + 0.5)) + +#define US_TO_COMPARE(us) ((uint16)map(us, 0, SERVO_TAU_USEC, 0, SERVO_OVF)) +#define COMPARE_TO_US(c) ((uint32)map(c, 0, SERVO_OVF, 0, SERVO_TAU_USEC)) + +#define ANGLE_TO_US(a) ((uint16)(map(a, 0, 180, this->min, this->max))) +#define US_TO_ANGLE(us) ((uint8)(map(us, this->min, this->max, 0, 180))) + +Servo::Servo() { + this->pin = NOT_ATTACHED; + this->timer = 0; + this->channel = TIMER_INVALID; + this->min = SERVO_DEFAULT_MIN_PW; + this->max = SERVO_DEFAULT_MAX_PW; +} + +bool Servo::attach(uint8_t pin) { + return this->attach(pin, SERVO_DEFAULT_MIN_PW, SERVO_DEFAULT_MAX_PW); +} + +bool Servo::attach(uint8_t pin, uint16_t min, uint16_t max) { + timer_dev_num timer_num = PIN_MAP[pin].timer_num; + uint32_t channel = PIN_MAP[pin].timer_chan; + if (timer_num == TIMER_INVALID) { + // don't reset any members or ASSERT(0), to keep driving any + // previously attach()ed servo. + return false; + } + this->pin = pin; + this->timer = getTimer(timer_num); + this->channel = channel; + this->min = min; + this->max = max; + + pinMode(pin, PWM); + + this->timer->pause(); + this->timer->setPrescaleFactor(SERVO_PRE); + this->timer->setOverflow(SERVO_OVF); + this->timer->generateUpdate(); + this->timer->resume(); + return true; +} + +bool Servo::detach() { + if (this->pin == NOT_ATTACHED) return false; + + this->timer->setChannelMode(this->channel, TIMER_DISABLED); + + this->pin = NOT_ATTACHED; + this->timer = 0; + this->channel = TIMER_INVALID; + this->min = SERVO_DEFAULT_MIN_PW; + this->max = SERVO_DEFAULT_MAX_PW; + + return true; +} + +void Servo::write(unsigned int value) { + if (value < SERVO_MAX_WRITE_ANGLE) { + this->writeMicroseconds(ANGLE_TO_US(value)); + } else { + this->writeMicroseconds(value); + } +} + +void Servo::writeMicroseconds(uint16_t pulseWidth) { + if (this->pin == NOT_ATTACHED) { + ASSERT(0); + return; + } + + pulseWidth = constrain(pulseWidth, this->min, this->max); + pwmWrite(this->pin, US_TO_COMPARE(pulseWidth)); +} + +int Servo::read() const { + if (this->pin == NOT_ATTACHED) { + ASSERT(0); + return 0; + } + + unsigned int pw = this->readMicroseconds(); + int a = US_TO_ANGLE(pw); + // map() round-trips in a weird way we correct for here + return a == 0 || a == 180 ? a : a + 1; +} + +uint16_t Servo::readMicroseconds() const { + if (this->pin == NOT_ATTACHED) { + ASSERT(0); + return 0; + } + + unsigned int compare = this->timer->getCompare(this->channel); + uint16_t c = COMPARE_TO_US(compare); + // map() round-trips in a weird way we correct for here + return c == 0 || c == 180 ? c : c + 1; +} diff --git a/libraries/Servo/Servo.h b/libraries/Servo/Servo.h new file mode 100644 index 0000000..d35572a --- /dev/null +++ b/libraries/Servo/Servo.h @@ -0,0 +1,116 @@ +/****************************************************************************** + * The MIT License + * + * Copyright (c) 2010, LeafLabs, LLC. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + *****************************************************************************/ + +#ifndef _SERVO_H_ +#define _SERVO_H_ + +#include + +#include "HardwareTimer.h" + +/* Note on Arduino compatibility: + + In the Arduino implementation, PWM is done "by hand" in the sense + that timer channels are hijacked in groups and an ISR is set which + toggles Servo::attach()ed pins using digitalWrite(). + + While this scheme allows any pin to drive a servo, it chews up + cycles and complicates the programmer's notion of when a particular + timer channel will be in use. + + This implementation only allows Servo instances to Servo::attach() + to pins that already have a timer channel associated with them, and + just uses pwmWrite() to drive the wave. + + This introduces an incompatibility: while the Arduino + implementation of attach() returns the affected channel on success + and 0 on failure, this one returns true on success and false on + failure. + + RC Servos expect a pulse every 20ms. Since periods are set for + entire timers, rather than individual channels, attach()ing a Servo + to a pin can interfere with other pins associated with the same + timer. As always, the pin mapping mega table is your friend. + */ + +// Pin number of unattached pins +#define NOT_ATTACHED (-1) + +// Maximum angle in degrees you can write(), exclusive. Value chosen +// for Arduino compatibility. +#define SERVO_MAX_WRITE_ANGLE (200) + +// Default min (0 deg)/max(180 deg) pulse widths, in microseconds. +// Value chosen for Arduino compatibility. +#define SERVO_DEFAULT_MIN_PW (544) +#define SERVO_DEFAULT_MAX_PW (2400) + +class Servo { +public: + Servo(); + + /* Pin has to have a timer channel associated with it already; + * sets pinMode to PWM and returns true iff successful (failure + * when pin doesn't support PWM). doesn't detach any ISRs + * associated with timer channel. */ + bool attach(uint8_t pin); + + /* Like attach(int), but with (inclusive) min (0 degree) and max + * (180 degree) pulse widths, in microseconds. + */ + bool attach(uint8_t pin, uint16_t min, uint16_t max); + + /* Return pin number if currently attach()ed to a pin, + NOT_ATTACHED otherwise. */ + int attached() const { return pin; } + + /* Stop driving the wave by disabling the output compare + interrupt. Returns true if this call did anything. */ + bool detach(); + + /* If value < MAX_WRITE_ANGLE, treated as an angle in degrees. + Otherwise, it's treated as a pulse width. */ + void write(unsigned int value); + + /* If outside of [min, max] determined by attach(), it is clamped + to lie in that range. */ + void writeMicroseconds(uint16_t pulseWidth); + + /* Return servo target angle, in degrees. This will lie between 0 + and 180. */ + int read() const; + + /* Returns the current pulse width, in microseconds. This will + lie within the [min, max] range. */ + uint16_t readMicroseconds() const; + +private: + int8_t pin; + HardwareTimer *timer; + int channel; + uint16_t min; + uint16_t max; +}; + +#endif /* _SERVO_H_ */ diff --git a/libraries/Servo/rules.mk b/libraries/Servo/rules.mk new file mode 100644 index 0000000..13cd364 --- /dev/null +++ b/libraries/Servo/rules.mk @@ -0,0 +1,35 @@ +# Standard things +sp := $(sp).x +dirstack_$(sp) := $(d) +d := $(dir) +BUILDDIRS += $(BUILD_PATH)/$(d) + +# Local flags +CFLAGS_$(d) := $(WIRISH_INCLUDES) $(LIBMAPLE_INCLUDES) + +# Local rules and targets +cSRCS_$(d) := + +# examples/UDPApp/udpapp.c \ +# examples/SocketApp/socketapp.c \ +# examples/WebClient/webclient.c \ +# examples/WebServer/webserver.c \ +# examples/Flash/webserver.c \ + +cppSRCS_$(d) := Servo.cpp + +cFILES_$(d) := $(cSRCS_$(d):%=$(d)/%) +cppFILES_$(d) := $(cppSRCS_$(d):%=$(d)/%) + +OBJS_$(d) := $(cFILES_$(d):%.c=$(BUILD_PATH)/%.o) \ + $(cppFILES_$(d):%.cpp=$(BUILD_PATH)/%.o) +DEPS_$(d) := $(OBJS_$(d):%.o=%.d) + +$(OBJS_$(d)): TGT_CFLAGS := $(CFLAGS_$(d)) + +TGT_BIN += $(OBJS_$(d)) + +# Standard things +-include $(DEPS_$(d)) +d := $(dirstack_$(sp)) +sp := $(basename $(sp)) \ No newline at end of file diff --git a/wirish/HardwareTimer.cpp b/wirish/HardwareTimer.cpp index 5b80ec1..92c6adf 100644 --- a/wirish/HardwareTimer.cpp +++ b/wirish/HardwareTimer.cpp @@ -29,13 +29,10 @@ #include "wirish.h" #include "HardwareTimer.h" -HardwareTimer::HardwareTimer(uint8 timerNum) { - ASSERT(timerNum <= NR_TIMERS && timerNum != 6 && timerNum != 7); +HardwareTimer::HardwareTimer(timer_dev_num timerNum) { + ASSERT(timerNum != TIMER_INVALID); this->timerNum = timerNum; - - // Need to remember over flow for bounds checking - this->overflow = 0xFFFF; } void HardwareTimer::resume(void) { @@ -46,122 +43,158 @@ void HardwareTimer::pause(void) { timer_pause(this->timerNum); } +uint16 HardwareTimer::getPrescaleFactor(void) { + return timer_get_prescaler(this->timerNum) + 1; +} + void HardwareTimer::setPrescaleFactor(uint16 factor) { // The prescaler register is zero-indexed timer_set_prescaler(this->timerNum, factor-1); } -void HardwareTimer::setOverflow(uint16 val) { - this->overflow = val; - timer_set_reload(this->timerNum, val); +uint16 HardwareTimer::getOverflow() { + return timer_get_reload(this->timerNum); } -void HardwareTimer::setCount(uint16 val) { - if(val > this->overflow) { - val = this->overflow; - } - timer_set_count(this->timerNum, val); +void HardwareTimer::setOverflow(uint16 val) { + timer_set_reload(this->timerNum, val); } uint16 HardwareTimer::getCount(void) { return timer_get_count(this->timerNum); } -uint16 HardwareTimer::setPeriod(uint32 microseconds) { - // XXX: 72MHz shouldn't be hard coded in here... global define? +void HardwareTimer::setCount(uint16 val) { + uint16 ovf = this->getOverflow(); + timer_set_count(this->timerNum, min(val, ovf)); +} +uint16 HardwareTimer::setPeriod(uint32 microseconds) { // Not the best way to handle this edge case? if(!microseconds) { setPrescaleFactor(1); setOverflow(1); - return this->overflow; + return this->getOverflow(); } + uint32 cycles = microseconds * CYCLES_PER_MICROSECOND; - // With a prescale factor of 1, there are 72counts/ms - uint16 ps = ((microseconds*72)/65536) + 1; + // With a prescale factor of 1, there are CYCLES_PER_MICROSECOND + // counts/ms + uint16 ps = (uint16)((cycles >> 16) + 1); setPrescaleFactor(ps); // Finally, this overflow will always be less than 65536 - setOverflow(((microseconds*72)/ps) - 1); - return this->overflow; + setOverflow((cycles/ps) - 1); + + return this->getOverflow(); +} + +inline void HardwareTimer::setChannelMode(int channel, TimerMode mode) { + timer_set_mode(this->timerNum, channel, mode); } void HardwareTimer::setChannel1Mode(TimerMode mode) { - timer_set_mode(this->timerNum,1,mode); + this->setChannelMode(1, mode); } void HardwareTimer::setChannel2Mode(TimerMode mode) { - timer_set_mode(this->timerNum,2,mode); + this->setChannelMode(2, mode); } void HardwareTimer::setChannel3Mode(TimerMode mode) { - timer_set_mode(this->timerNum,3,mode); + this->setChannelMode(3, mode); } void HardwareTimer::setChannel4Mode(TimerMode mode) { - timer_set_mode(this->timerNum,4,mode); + this->setChannelMode(4, mode); +} + +inline uint16 HardwareTimer::getCompare(int channel) { + return timer_get_compare_value(this->timerNum, channel); +} + +uint16 HardwareTimer::getCompare1() { + return this->getCompare(1); +} + +uint16 HardwareTimer::getCompare2() { + return this->getCompare(2); +} + +uint16 HardwareTimer::getCompare3() { + return this->getCompare(3); +} + +uint16 HardwareTimer::getCompare4() { + return this->getCompare(4); +} + +inline void HardwareTimer::setCompare(int channel, uint16 val) { + uint16 ovf = this->getOverflow(); + timer_set_compare_value(this->timerNum, channel, min(val, ovf)); } void HardwareTimer::setCompare1(uint16 val) { - if(val > this->overflow) { - val = this->overflow; - } - timer_set_compare_value(this->timerNum,1,val); + this->setCompare(1, val); } void HardwareTimer::setCompare2(uint16 val) { - if(val > this->overflow) { - val = this->overflow; - } - timer_set_compare_value(this->timerNum,2,val); + this->setCompare(2, val); } void HardwareTimer::setCompare3(uint16 val) { - if(val > this->overflow) { - val = this->overflow; - } - timer_set_compare_value(this->timerNum,3,val); + this->setCompare(3, val); } void HardwareTimer::setCompare4(uint16 val) { - if(val > this->overflow) { - val = this->overflow; - } - timer_set_compare_value(this->timerNum,4,val); + this->setCompare(4, val); +} + +inline void HardwareTimer::attachInterrupt(int channel, voidFuncPtr handler) { + timer_attach_interrupt(this->timerNum, channel, handler); } void HardwareTimer::attachCompare1Interrupt(voidFuncPtr handler) { - timer_attach_interrupt(this->timerNum,1,handler); + this->attachInterrupt(1, handler); } void HardwareTimer::attachCompare2Interrupt(voidFuncPtr handler) { - timer_attach_interrupt(this->timerNum,2,handler); + this->attachInterrupt(2, handler); } void HardwareTimer::attachCompare3Interrupt(voidFuncPtr handler) { - timer_attach_interrupt(this->timerNum,3,handler); + this->attachInterrupt(3, handler); } void HardwareTimer::attachCompare4Interrupt(voidFuncPtr handler) { - timer_attach_interrupt(this->timerNum,4,handler); + this->attachInterrupt(4, handler); +} + +inline void HardwareTimer::detachInterrupt(int channel) { + timer_detach_interrupt(this->timerNum, channel); } void HardwareTimer::detachCompare1Interrupt(void) { - timer_detach_interrupt(this->timerNum,1); + this->detachInterrupt(1); } void HardwareTimer::detachCompare2Interrupt(void) { - timer_detach_interrupt(this->timerNum,2); + this->detachInterrupt(2); } void HardwareTimer::detachCompare3Interrupt(void) { - timer_detach_interrupt(this->timerNum,3); + this->detachInterrupt(3); } void HardwareTimer::detachCompare4Interrupt(void) { - timer_detach_interrupt(this->timerNum,4); + this->detachInterrupt(4); +} + +void HardwareTimer::generateUpdate(void) { + timer_generate_update(this->timerNum); } + HardwareTimer Timer1(TIMER1); HardwareTimer Timer2(TIMER2); HardwareTimer Timer3(TIMER3); @@ -171,3 +204,24 @@ HardwareTimer Timer5(TIMER5); // High-density devices only HardwareTimer Timer8(TIMER8); // High-density devices only #endif +HardwareTimer* getTimer(timer_dev_num timerNum) { + switch (timerNum) { + case TIMER1: + return &Timer1; + case TIMER2: + return &Timer2; + case TIMER3: + return &Timer3; + case TIMER4: + return &Timer4; +#if NR_TIMERS >= 8 + case TIMER5: + return &Timer5; + case TIMER8: + return &Timer8; +#endif + default: + ASSERT(0); + return 0; + } +} diff --git a/wirish/HardwareTimer.h b/wirish/HardwareTimer.h index b05085f..c72175f 100644 --- a/wirish/HardwareTimer.h +++ b/wirish/HardwareTimer.h @@ -26,8 +26,10 @@ * @brief wirish timer class to manage the four 16-bit timer peripherals */ -#ifndef _TIMER_H_ -#define _TIMER_H_ +#ifndef _HARDWARETIMER_H_ +#define _HARDWARETIMER_H_ + +#include "timers.h" /** * Interface to one of the 16-bit timer peripherals. @@ -51,11 +53,16 @@ */ class HardwareTimer { private: - uint16 overflow; - uint8 timerNum; + timer_dev_num timerNum; public: - HardwareTimer(uint8 timer_num); + HardwareTimer(timer_dev_num timer_num); + + /** + * Return this timer's device number. For example, + * Timer1.getTimerNum() == TIMER1 + */ + timer_dev_num getTimerNum() { return timerNum; } /** * Stop the counter, without affecting its configuration. @@ -89,6 +96,12 @@ class HardwareTimer { */ void resume(void); + /** + * Returns the timer's prescale factor. + * @see HardwareTimer::setPrescaleFactor() + */ + uint16 getPrescaleFactor(); + /** * Set the timer prescale. * @@ -111,6 +124,12 @@ class HardwareTimer { */ void setPrescaleFactor(uint16 factor); + /** + * Gets the timer overflow value. + * @see HardwareTimer::setOverflow() + */ + uint16 getOverflow(); + /** * Sets the timer overflow (or "reload") value. * @@ -122,10 +141,16 @@ class HardwareTimer { * value for the channel compare values. * * @param val The new overflow value to set - * @see HardwareTimer::setOverflow() */ void setOverflow(uint16 val); + /** + * Retrieve the current timer count. + * + * @return The timer's current count value + */ + uint16 getCount(void); + /** * Set the current timer count. * @@ -139,13 +164,6 @@ class HardwareTimer { */ void setCount(uint16 val); - /** - * Retrieve the current timer count. - * - * @return The timer's current count value - */ - uint16 getCount(void); - /** * Configure the prescaler and overflow values to generate a timer * reload with a period as close to the given number of @@ -163,6 +181,18 @@ class HardwareTimer { */ uint16 setPeriod(uint32 microseconds); + /** + * Set the given channel of this timer to the given mode. + * + * Note: Timer1.setChannel1Mode(TIMER_PWM) may not work as + * expected; if you want PWM functionality on a channel make sure + * you don't set it to something else! + * + * @param channel Timer channel, from 1 to 4 + * @param mode Mode to set + */ + void setChannelMode(int channel, TimerMode mode); + /** * Set channel 1 of this timer to the given mode. * @@ -193,7 +223,25 @@ class HardwareTimer { void setChannel4Mode(TimerMode mode); /** - * Sets the compare value for channel 1. + * Gets the compare value for the given channel. + * @see HardwareTimer::setCompare() + */ + uint16 getCompare(int channel); + + /** Like getCompare(1) */ + uint16 getCompare1(); + + /** Like getCompare(2) */ + uint16 getCompare2(); + + /** Like getCompare(3) */ + uint16 getCompare3(); + + /** Like getCompare(4) */ + uint16 getCompare4(); + + /** + * Sets the compare value for the given channel. * * When the counter reaches this value the interrupt for this * channel will fire if channel 1 mode is TIMER_OUTPUTCOMPARE and @@ -210,49 +258,40 @@ class HardwareTimer { * overheads mean that the smallest increment rate is at least a * few microseconds. * + * @param channel the channel whose compare to set, from 1 to 4. * @param val The compare value to set. If greater than this * timer's overflow value, it will be truncated to the * overflow value. * * @see TimerMode - * @see HardwareTimer::setChannel1Mode() + * @see HardwareTimer::setChannelMode() */ - void setCompare1(uint16 val); + void setCompare(int channel, uint16 compare); /** - * Sets the compare value for channel 2. - * - * @param val The compare value to set. If greater than this - * timer's overflow value, it will be truncated to the - * overflow value. - * @see HardwareTimer::setCompare1() + * Like setCompare(1, compare). */ - void setCompare2(uint16 val); + void setCompare1(uint16 compare); /** - * Sets the compare value for channel 3. - * - * @param val The compare value to set. If greater than this - * timer's overflow value, it will be truncated to the - * overflow value. - * @see HardwareTimer::setCompare1() + * Like setCompare(2, compare). */ - void setCompare3(uint16 val); + void setCompare2(uint16 compare); /** - * Sets the compare value for channel 4. - * - * @param val The compare value to set. If greater than this - * timer's overflow value, it will be truncated to the - * overflow value. - * @see HardwareTimer::setCompare1() + * Like setCompare(3, compare). + */ + void setCompare3(uint16 compare); + + /** + * Like setCompare(4, compare). */ - void setCompare4(uint16 val); + void setCompare4(uint16 compare); /** - * Attach an interrupt handler to this timer's channel 1. This + * Attach an interrupt handler to the given channel. This * interrupt handler will be called when the timer's counter - * reaches its channel 1 compare value. + * reaches the given channel compare value. * * The argument should be a function which takes no arguments and * has no return value; i.e. it should have signature @@ -267,56 +306,75 @@ class HardwareTimer { * if you have a delay() in your interrupt routine, you're probably * doing it wrong. * - * @param handler The ISR to attach to channel 1. + * @param channel the channel to attach the ISR to, from 1 to 4. + * @param handler The ISR to attach to the given channel. * @see voidFuncPtr */ + void attachInterrupt(int channel, voidFuncPtr handler); + + /** + * Like attachCompareInterrupt(1, handler). + * @see HardwareTimer::attachCompareInterrupt() + */ void attachCompare1Interrupt(voidFuncPtr handler); /** - * Like attachCompare1Interrupt(), but for channel 2. - * @see HardwareTimer::attachCompare1Interrupt() + * Like attachCompareInterrupt(2, handler). + * @see HardwareTimer::attachCompareInterrupt() */ void attachCompare2Interrupt(voidFuncPtr handler); /** - * Like attachCompare1Interrupt(), but for channel 3. - * @see HardwareTimer::attachCompare1Interrupt() + * Like attachCompareInterrupt(3, handler). + * @see HardwareTimer::attachCompareInterrupt() */ void attachCompare3Interrupt(voidFuncPtr handler); /** - * Like attachCompare1Interrupt(), but for channel 4. - * @see HardwareTimer::attachCompare1Interrupt() + * Like attachCompareInterrupt(4, handler). + * @see HardwareTimer::attachCompareInterrupt() */ void attachCompare4Interrupt(voidFuncPtr handler); /** - * Remove the interrupt handler attached to channel 1, if any. - * The handler will no longer be called by this timer. - * @see HardwareTimer::attachCompare1Interrupt() + * Remove the interrupt handler attached to the given channel, if + * any. The handler will no longer be called by this timer. + * + * @param channel the channel whose interrupt to detach, from 1 to 4. + * @see HardwareTimer::attachInterrupt() + */ + void detachInterrupt(int channel); + + /** + * Like detachInterrupt(1). + * @see HardwareTimer::detachInterrupt() */ void detachCompare1Interrupt(void); /** - * Remove the interrupt handler attached to channel 2, if any. - * The handler will no longer be called by this timer. - * @see HardwareTimer::attachCompare1Interrupt() + * Like detachInterrupt(2). + * @see HardwareTimer::detachInterrupt() */ void detachCompare2Interrupt(void); /** - * Remove the interrupt handler attached to channel 3, if any. - * The handler will no longer be called by this timer. - * @see HardwareTimer::attachCompare1Interrupt() + * Like detachInterrupt(3). + * @see HardwareTimer::detachInterrupt() */ void detachCompare3Interrupt(void); /** - * Remove the interrupt handler attached to channel 4, if any. - * The handler will no longer be called by this timer. - * @see HardwareTimer::attachCompare1Interrupt() + * Like detachInterrupt(4). + * @see HardwareTimer::detachInterrupt() */ void detachCompare4Interrupt(void); + + /** + * Re-initializes the counter (to 0 in upcounting mode, which is + * the default), and generates an update of the prescale and + * overflow registers. + */ + void generateUpdate(void); }; /** Pre-instantiated timer for use by user code. */ @@ -336,5 +394,7 @@ extern HardwareTimer Timer5; extern HardwareTimer Timer8; #endif +HardwareTimer* getTimer(timer_dev_num timerNum); + #endif diff --git a/wirish/boards.h b/wirish/boards.h index 558e1c3..84ff44f 100644 --- a/wirish/boards.h +++ b/wirish/boards.h @@ -57,16 +57,15 @@ enum { ADC12, ADC13, ADC14, ADC15, ADC16, ADC17, ADC18, ADC19, ADC20, }; #define ADC_INVALID 0xFFFFFFFF -#define TIMER_INVALID 0xFFFFFFFF /* Types used for the tables below */ typedef struct PinMapping { GPIO_Port *port; uint32 pin; uint32 adc; - uint32 timer_channel; + TimerCCR timer_ccr; uint32 exti_port; - uint32 timer_num; + timer_dev_num timer_num; uint32 timer_chan; } PinMapping; @@ -74,7 +73,7 @@ typedef struct PinMapping { #ifdef BOARD_maple #define CYCLES_PER_MICROSECOND 72 - #define MAPLE_RELOAD_VAL 71999 /* takes a cycle to reload */ + #define SYSTICK_RELOAD_VAL 71999 /* takes a cycle to reload */ #define BOARD_BUTTON_PIN 38 #define BOARD_LED_PIN 13 @@ -89,7 +88,7 @@ typedef struct PinMapping { /* D3/PA1 */ {GPIOA_BASE, 1, ADC1, TIMER2_CH2_CCR, EXTI_CONFIG_PORTA, TIMER2, 2}, /* D4/PB5 */ - {GPIOB_BASE, 5, ADC_INVALID, TIMER_INVALID, EXTI_CONFIG_PORTB, TIMER_INVALID, TIMER_INVALID}, + {GPIOB_BASE, 5, ADC_INVALID, 0, EXTI_CONFIG_PORTB, TIMER_INVALID, TIMER_INVALID}, /* D5/PB6 */ {GPIOB_BASE, 6, ADC_INVALID, TIMER4_CH1_CCR, EXTI_CONFIG_PORTB, TIMER4, 1}, /* D6/PA8 */ @@ -101,69 +100,69 @@ typedef struct PinMapping { /* D9/PB7 */ {GPIOB_BASE, 7, ADC_INVALID, TIMER4_CH2_CCR, EXTI_CONFIG_PORTB, TIMER4, 2}, /* D10/PA4 */ - {GPIOA_BASE, 4, ADC4, TIMER_INVALID, EXTI_CONFIG_PORTA, TIMER_INVALID, TIMER_INVALID}, + {GPIOA_BASE, 4, ADC4, 0, EXTI_CONFIG_PORTA, TIMER_INVALID, TIMER_INVALID}, /* D11/PA7 */ {GPIOA_BASE, 7, ADC7, TIMER3_CH2_CCR, EXTI_CONFIG_PORTA, TIMER3, 2}, /* D12/PA6 */ {GPIOA_BASE, 6, ADC6, TIMER3_CH1_CCR, EXTI_CONFIG_PORTA, TIMER3, 1}, /* D13/PA5 */ - {GPIOA_BASE, 5, ADC5, TIMER_INVALID, EXTI_CONFIG_PORTA, TIMER_INVALID, TIMER_INVALID}, + {GPIOA_BASE, 5, ADC5, 0, EXTI_CONFIG_PORTA, TIMER_INVALID, TIMER_INVALID}, /* D14/PB8 */ {GPIOB_BASE, 8, ADC_INVALID, TIMER4_CH3_CCR, EXTI_CONFIG_PORTB, TIMER4, 3}, /* Little header */ /* D15/PC0 */ - {GPIOC_BASE, 0, ADC10, TIMER_INVALID, EXTI_CONFIG_PORTC, TIMER_INVALID, TIMER_INVALID}, + {GPIOC_BASE, 0, ADC10, 0, EXTI_CONFIG_PORTC, TIMER_INVALID, TIMER_INVALID}, /* D16/PC1 */ - {GPIOC_BASE, 1, ADC11, TIMER_INVALID, EXTI_CONFIG_PORTC, TIMER_INVALID, TIMER_INVALID}, + {GPIOC_BASE, 1, ADC11, 0, EXTI_CONFIG_PORTC, TIMER_INVALID, TIMER_INVALID}, /* D17/PC2 */ - {GPIOC_BASE, 2, ADC12, TIMER_INVALID, EXTI_CONFIG_PORTC, TIMER_INVALID, TIMER_INVALID}, + {GPIOC_BASE, 2, ADC12, 0, EXTI_CONFIG_PORTC, TIMER_INVALID, TIMER_INVALID}, /* D18/PC3 */ - {GPIOC_BASE, 3, ADC13, TIMER_INVALID, EXTI_CONFIG_PORTC, TIMER_INVALID, TIMER_INVALID}, + {GPIOC_BASE, 3, ADC13, 0, EXTI_CONFIG_PORTC, TIMER_INVALID, TIMER_INVALID}, /* D19/PC4 */ - {GPIOC_BASE, 4, ADC14, TIMER_INVALID, EXTI_CONFIG_PORTC, TIMER_INVALID, TIMER_INVALID}, + {GPIOC_BASE, 4, ADC14, 0, EXTI_CONFIG_PORTC, TIMER_INVALID, TIMER_INVALID}, /* D20/PC5 */ - {GPIOC_BASE, 5, ADC15, TIMER_INVALID, EXTI_CONFIG_PORTC, TIMER_INVALID, TIMER_INVALID}, + {GPIOC_BASE, 5, ADC15, 0, EXTI_CONFIG_PORTC, TIMER_INVALID, TIMER_INVALID}, /* External header */ /* D21/PC13 */ - {GPIOC_BASE, 13, ADC_INVALID, TIMER_INVALID, EXTI_CONFIG_PORTC, TIMER_INVALID, TIMER_INVALID}, + {GPIOC_BASE, 13, ADC_INVALID, 0, EXTI_CONFIG_PORTC, TIMER_INVALID, TIMER_INVALID}, /* D22/PC14 */ - {GPIOC_BASE, 14, ADC_INVALID, TIMER_INVALID, EXTI_CONFIG_PORTC, TIMER_INVALID, TIMER_INVALID}, + {GPIOC_BASE, 14, ADC_INVALID, 0, EXTI_CONFIG_PORTC, TIMER_INVALID, TIMER_INVALID}, /* D23/PC15 */ - {GPIOC_BASE, 15, ADC_INVALID, TIMER_INVALID, EXTI_CONFIG_PORTC, TIMER_INVALID, TIMER_INVALID}, + {GPIOC_BASE, 15, ADC_INVALID, 0, EXTI_CONFIG_PORTC, TIMER_INVALID, TIMER_INVALID}, /* D24/PB9 */ {GPIOB_BASE, 9, ADC_INVALID, TIMER4_CH4_CCR, EXTI_CONFIG_PORTB, TIMER_INVALID, TIMER_INVALID}, /* D25/PD2 */ - {GPIOD_BASE, 2, ADC_INVALID, TIMER_INVALID, EXTI_CONFIG_PORTD, TIMER_INVALID, TIMER_INVALID}, + {GPIOD_BASE, 2, ADC_INVALID, 0, EXTI_CONFIG_PORTD, TIMER_INVALID, TIMER_INVALID}, /* D26/PC10 */ - {GPIOC_BASE, 10, ADC_INVALID, TIMER_INVALID, EXTI_CONFIG_PORTC, TIMER_INVALID, TIMER_INVALID}, + {GPIOC_BASE, 10, ADC_INVALID, 0, EXTI_CONFIG_PORTC, TIMER_INVALID, TIMER_INVALID}, /* D27/PB0 */ - {GPIOB_BASE, 0, ADC8, TIMER3_CH3_CCR, EXTI_CONFIG_PORTB, TIMER3, 3}, + {GPIOB_BASE, 0, ADC8, TIMER3_CH3_CCR, EXTI_CONFIG_PORTB, TIMER3, 3}, /* D28/PB1 */ - {GPIOB_BASE, 1, ADC9, TIMER3_CH4_CCR, EXTI_CONFIG_PORTB, TIMER3, 4}, + {GPIOB_BASE, 1, ADC9, TIMER3_CH4_CCR, EXTI_CONFIG_PORTB, TIMER3, 4}, /* D29/PB10 */ - {GPIOB_BASE, 10, ADC_INVALID, TIMER_INVALID, EXTI_CONFIG_PORTB, TIMER_INVALID, TIMER_INVALID}, + {GPIOB_BASE, 10, ADC_INVALID, 0, EXTI_CONFIG_PORTB, TIMER_INVALID, TIMER_INVALID}, /* D30/PB11 */ - {GPIOB_BASE, 11, ADC_INVALID, TIMER_INVALID, EXTI_CONFIG_PORTB, TIMER_INVALID, TIMER_INVALID}, + {GPIOB_BASE, 11, ADC_INVALID, 0, EXTI_CONFIG_PORTB, TIMER_INVALID, TIMER_INVALID}, /* D31/PB12 */ - {GPIOB_BASE, 12, ADC_INVALID, TIMER_INVALID, EXTI_CONFIG_PORTB, TIMER_INVALID, TIMER_INVALID}, + {GPIOB_BASE, 12, ADC_INVALID, 0, EXTI_CONFIG_PORTB, TIMER_INVALID, TIMER_INVALID}, /* D32/PB13 */ - {GPIOB_BASE, 13, ADC_INVALID, TIMER_INVALID, EXTI_CONFIG_PORTB, TIMER_INVALID, TIMER_INVALID}, + {GPIOB_BASE, 13, ADC_INVALID, 0, EXTI_CONFIG_PORTB, TIMER_INVALID, TIMER_INVALID}, /* D33/PB14 */ - {GPIOB_BASE, 14, ADC_INVALID, TIMER_INVALID, EXTI_CONFIG_PORTB, TIMER_INVALID, TIMER_INVALID}, + {GPIOB_BASE, 14, ADC_INVALID, 0, EXTI_CONFIG_PORTB, TIMER_INVALID, TIMER_INVALID}, /* D34/PB15 */ - {GPIOB_BASE, 15, ADC_INVALID, TIMER_INVALID, EXTI_CONFIG_PORTB, TIMER_INVALID, TIMER_INVALID}, + {GPIOB_BASE, 15, ADC_INVALID, 0, EXTI_CONFIG_PORTB, TIMER_INVALID, TIMER_INVALID}, /* D35/PC6 */ - {GPIOC_BASE, 6, ADC_INVALID, TIMER_INVALID, EXTI_CONFIG_PORTC, TIMER_INVALID, TIMER_INVALID}, + {GPIOC_BASE, 6, ADC_INVALID, 0, EXTI_CONFIG_PORTC, TIMER_INVALID, TIMER_INVALID}, /* D36/PC7 */ - {GPIOC_BASE, 7, ADC_INVALID, TIMER_INVALID, EXTI_CONFIG_PORTC, TIMER_INVALID, TIMER_INVALID}, + {GPIOC_BASE, 7, ADC_INVALID, 0, EXTI_CONFIG_PORTC, TIMER_INVALID, TIMER_INVALID}, /* D37/PC8 */ - {GPIOC_BASE, 8, ADC_INVALID, TIMER_INVALID, EXTI_CONFIG_PORTC, TIMER_INVALID, TIMER_INVALID}, + {GPIOC_BASE, 8, ADC_INVALID, 0, EXTI_CONFIG_PORTC, TIMER_INVALID, TIMER_INVALID}, /* PC9 (BUT) */ - {GPIOC_BASE, 9, ADC_INVALID, TIMER_INVALID, EXTI_CONFIG_PORTC, TIMER_INVALID, TIMER_INVALID} + {GPIOC_BASE, 9, ADC_INVALID, 0, EXTI_CONFIG_PORTC, TIMER_INVALID, TIMER_INVALID} }; #define BOARD_INIT do { \ @@ -174,7 +173,7 @@ typedef struct PinMapping { /* LeafLabs Maple Native (prototype) */ #define CYCLES_PER_MICROSECOND 72 - #define MAPLE_RELOAD_VAL 71999 /* takes a cycle to reload */ + #define SYSTICK_RELOAD_VAL 71999 /* takes a cycle to reload */ // TODO: static __attribute__ ((unused)) PinMapping PIN_MAP[NR_GPIO_PINS] = { @@ -299,14 +298,14 @@ typedef struct PinMapping { #elif defined(BOARD_maple_mini) #define CYCLES_PER_MICROSECOND 72 - #define MAPLE_RELOAD_VAL 71999 /* takes a cycle to reload */ + #define SYSTICK_RELOAD_VAL 71999 /* takes a cycle to reload */ #define BOARD_BUTTON_PIN 32 #define BOARD_LED_PIN 33 static __attribute__ ((unused)) PinMapping PIN_MAP[NR_GPIO_PINS] = { /* D0/PC15 */ - {GPIOC_BASE, 15, ADC_INVALID, TIMER_INVALID, EXTI_CONFIG_PORTC, TIMER_INVALID, TIMER_INVALID}, + {GPIOC_BASE, 15, ADC_INVALID, 0, EXTI_CONFIG_PORTC, TIMER_INVALID, TIMER_INVALID}, /* D1/PA0 */ {GPIOA_BASE, 0, ADC0, TIMER2_CH1_CCR, EXTI_CONFIG_PORTA, TIMER2, 1}, /* D2/PA1 */ @@ -316,9 +315,9 @@ typedef struct PinMapping { /* D4/PA3 */ {GPIOA_BASE, 3, ADC3, TIMER2_CH4_CCR, EXTI_CONFIG_PORTA, TIMER2, 4}, /* D5/PA4 */ - {GPIOA_BASE, 4, ADC4, TIMER_INVALID, EXTI_CONFIG_PORTA, TIMER_INVALID, TIMER_INVALID}, + {GPIOA_BASE, 4, ADC4, 0, EXTI_CONFIG_PORTA, TIMER_INVALID, TIMER_INVALID}, /* D6/PA5 */ - {GPIOA_BASE, 5, ADC5, TIMER_INVALID, EXTI_CONFIG_PORTA, TIMER_INVALID, TIMER_INVALID}, + {GPIOA_BASE, 5, ADC5, 0, EXTI_CONFIG_PORTA, TIMER_INVALID, TIMER_INVALID}, /* D7/PA6 */ {GPIOA_BASE, 6, ADC6, TIMER3_CH1_CCR, EXTI_CONFIG_PORTA, TIMER3, 1}, /* D8/PA7 */ @@ -328,17 +327,17 @@ typedef struct PinMapping { /* D10/PB1 */ {GPIOB_BASE, 1, ADC9, TIMER3_CH4_CCR, EXTI_CONFIG_PORTB, TIMER3, 4}, /* D11/PB2 */ - {GPIOB_BASE, 2, ADC_INVALID, TIMER_INVALID, EXTI_CONFIG_PORTB, TIMER_INVALID, TIMER_INVALID}, + {GPIOB_BASE, 2, ADC_INVALID, 0, EXTI_CONFIG_PORTB, TIMER_INVALID, TIMER_INVALID}, /* D12/PB10 */ - {GPIOB_BASE, 10, ADC_INVALID, TIMER_INVALID, EXTI_CONFIG_PORTB, TIMER_INVALID, TIMER_INVALID}, + {GPIOB_BASE, 10, ADC_INVALID, 0, EXTI_CONFIG_PORTB, TIMER_INVALID, TIMER_INVALID}, /* D13/PB11 */ - {GPIOB_BASE, 11, ADC_INVALID, TIMER_INVALID, EXTI_CONFIG_PORTB, TIMER_INVALID, TIMER_INVALID}, + {GPIOB_BASE, 11, ADC_INVALID, 0, EXTI_CONFIG_PORTB, TIMER_INVALID, TIMER_INVALID}, /* D14/PB13 */ - {GPIOB_BASE, 13, ADC_INVALID, TIMER_INVALID, EXTI_CONFIG_PORTB, TIMER_INVALID, TIMER_INVALID}, + {GPIOB_BASE, 13, ADC_INVALID, 0, EXTI_CONFIG_PORTB, TIMER_INVALID, TIMER_INVALID}, /* D15/PB14 */ - {GPIOB_BASE, 14, ADC_INVALID, TIMER_INVALID, EXTI_CONFIG_PORTB, TIMER_INVALID, TIMER_INVALID}, + {GPIOB_BASE, 14, ADC_INVALID, 0, EXTI_CONFIG_PORTB, TIMER_INVALID, TIMER_INVALID}, /* D16/PB15 */ - {GPIOB_BASE, 15, ADC_INVALID, TIMER_INVALID, EXTI_CONFIG_PORTB, TIMER_INVALID, TIMER_INVALID}, + {GPIOB_BASE, 15, ADC_INVALID, 0, EXTI_CONFIG_PORTB, TIMER_INVALID, TIMER_INVALID}, /* D17/PA8 */ {GPIOA_BASE, 8, ADC_INVALID, TIMER1_CH1_CCR, EXTI_CONFIG_PORTB, TIMER1, 1}, /* D18/PA9 */ @@ -348,31 +347,31 @@ typedef struct PinMapping { /* D20/PA11 */ {GPIOA_BASE, 11, ADC_INVALID, TIMER1_CH4_CCR, EXTI_CONFIG_PORTA, TIMER1, 4}, /* D21/PA12 */ - {GPIOA_BASE, 12, ADC_INVALID, TIMER_INVALID, EXTI_CONFIG_PORTA, TIMER_INVALID, TIMER_INVALID}, + {GPIOA_BASE, 12, ADC_INVALID, 0, EXTI_CONFIG_PORTA, TIMER_INVALID, TIMER_INVALID}, /* D22/PA13 */ - {GPIOA_BASE, 13, ADC_INVALID, TIMER_INVALID, EXTI_CONFIG_PORTA, TIMER_INVALID, TIMER_INVALID}, + {GPIOA_BASE, 13, ADC_INVALID, 0, EXTI_CONFIG_PORTA, TIMER_INVALID, TIMER_INVALID}, /* D23/PA14 */ - {GPIOA_BASE, 14, ADC_INVALID, TIMER_INVALID, EXTI_CONFIG_PORTA, TIMER_INVALID, TIMER_INVALID}, + {GPIOA_BASE, 14, ADC_INVALID, 0, EXTI_CONFIG_PORTA, TIMER_INVALID, TIMER_INVALID}, /* D24/PA15 */ - {GPIOA_BASE, 15, ADC_INVALID, TIMER_INVALID, EXTI_CONFIG_PORTA, TIMER_INVALID, TIMER_INVALID}, + {GPIOA_BASE, 15, ADC_INVALID, 0, EXTI_CONFIG_PORTA, TIMER_INVALID, TIMER_INVALID}, /* D25/PB3 */ - {GPIOB_BASE, 3, ADC_INVALID, TIMER_INVALID, EXTI_CONFIG_PORTB, TIMER_INVALID, TIMER_INVALID}, + {GPIOB_BASE, 3, ADC_INVALID, 0, EXTI_CONFIG_PORTB, TIMER_INVALID, TIMER_INVALID}, /* D26/PB4 */ - {GPIOB_BASE, 4, ADC_INVALID, TIMER_INVALID, EXTI_CONFIG_PORTB, TIMER_INVALID, TIMER_INVALID}, + {GPIOB_BASE, 4, ADC_INVALID, 0, EXTI_CONFIG_PORTB, TIMER_INVALID, TIMER_INVALID}, /* D27/PB5 */ - {GPIOB_BASE, 5, ADC_INVALID, TIMER_INVALID, EXTI_CONFIG_PORTB, TIMER_INVALID, TIMER_INVALID}, + {GPIOB_BASE, 5, ADC_INVALID, 0, EXTI_CONFIG_PORTB, TIMER_INVALID, TIMER_INVALID}, /* D28/PB6 */ {GPIOB_BASE, 6, ADC_INVALID, TIMER4_CH1_CCR, EXTI_CONFIG_PORTB, TIMER4, 1}, /* D29/PB7 */ {GPIOB_BASE, 7, ADC_INVALID, TIMER4_CH2_CCR, EXTI_CONFIG_PORTB, TIMER4, 1}, /* D30/PC13 */ - {GPIOC_BASE, 13, ADC_INVALID, TIMER_INVALID, EXTI_CONFIG_PORTC, TIMER_INVALID, TIMER_INVALID}, + {GPIOC_BASE, 13, ADC_INVALID, 0, EXTI_CONFIG_PORTC, TIMER_INVALID, TIMER_INVALID}, /* D31/PC14 */ - {GPIOC_BASE, 14, ADC_INVALID, TIMER_INVALID, EXTI_CONFIG_PORTC, TIMER_INVALID, TIMER_INVALID}, + {GPIOC_BASE, 14, ADC_INVALID, 0, EXTI_CONFIG_PORTC, TIMER_INVALID, TIMER_INVALID}, /* D32/PB8 */ {GPIOB_BASE, 8, ADC_INVALID, TIMER4_CH3_CCR, EXTI_CONFIG_PORTB, TIMER4, 3}, /* D33/PB12 */ - {GPIOB_BASE, 12, ADC_INVALID, TIMER_INVALID, EXTI_CONFIG_PORTB, TIMER_INVALID, TIMER_INVALID}, + {GPIOB_BASE, 12, ADC_INVALID, 0, EXTI_CONFIG_PORTB, TIMER_INVALID, TIMER_INVALID}, }; /* since we want the Serial Wire/JTAG pins as GPIOs, disable both diff --git a/wirish/comm/HardwareSerial.cpp b/wirish/comm/HardwareSerial.cpp index 425c610..d6c7e82 100644 --- a/wirish/comm/HardwareSerial.cpp +++ b/wirish/comm/HardwareSerial.cpp @@ -34,9 +34,9 @@ #include "gpio.h" #include "timers.h" -HardwareSerial Serial1(USART1, 4500000UL, GPIOA_BASE, 9, 10, TIMER1, 2); -HardwareSerial Serial2(USART2, 2250000UL, GPIOA_BASE, 2, 3, TIMER2, 3); -HardwareSerial Serial3(USART3, 2250000UL, GPIOB_BASE, 10, 11, 0, 0); +HardwareSerial Serial1(USART1, 4500000UL, GPIOA_BASE, 9,10, TIMER1, 2); +HardwareSerial Serial2(USART2, 2250000UL, GPIOA_BASE, 2, 3, TIMER2, 3); +HardwareSerial Serial3(USART3, 2250000UL, GPIOB_BASE, 10,11, TIMER_INVALID, 0); // TODO: High density device ports HardwareSerial::HardwareSerial(uint8 usart_num, @@ -44,7 +44,7 @@ HardwareSerial::HardwareSerial(uint8 usart_num, GPIO_Port *gpio_port, uint8 tx_pin, uint8 rx_pin, - uint8 timer_num, + timer_dev_num timer_num, uint8 compare_num) { this->usart_num = usart_num; this->max_baud = max_baud; @@ -75,8 +75,7 @@ void HardwareSerial::begin(uint32 baud) { gpio_set_mode(gpio_port, tx_pin, GPIO_MODE_AF_OUTPUT_PP); gpio_set_mode(gpio_port, rx_pin, GPIO_MODE_INPUT_FLOATING); - if ((usart_num == USART1) || - (usart_num == USART2)) { + if (timer_num != TIMER_INVALID) { /* turn off any pwm if there's a conflict on this usart */ timer_set_mode(timer_num, compare_num, TIMER_DISABLED); } diff --git a/wirish/comm/HardwareSerial.h b/wirish/comm/HardwareSerial.h index 17be49f..f67f09a 100644 --- a/wirish/comm/HardwareSerial.h +++ b/wirish/comm/HardwareSerial.h @@ -31,6 +31,8 @@ #ifndef _HARDWARESERIAL_H_ #define _HARDWARESERIAL_H_ +#include "timers.h" + #include "Print.h" class HardwareSerial : public Print { @@ -40,7 +42,7 @@ class HardwareSerial : public Print { GPIO_Port *gpio_port; uint8 tx_pin; uint8 rx_pin; - uint8 timer_num; + timer_dev_num timer_num; uint8 compare_num; public: HardwareSerial(uint8 usart_num, @@ -48,7 +50,7 @@ class HardwareSerial : public Print { GPIO_Port *gpio_port, uint8 tx_pin, uint8 rx_pin, - uint8 timer_num, + timer_dev_num timer_num, uint8 compare_num); void begin(uint32); void end(void); diff --git a/wirish/pwm.c b/wirish/pwm.c index 0bf27aa..25a7415 100644 --- a/wirish/pwm.c +++ b/wirish/pwm.c @@ -38,9 +38,9 @@ void pwmWrite(uint8 pin, uint16 duty_cycle) { return; } - ccr = (TimerCCR)(PIN_MAP[pin].timer_channel); + ccr = PIN_MAP[pin].timer_ccr; - if (ccr == (TimerCCR)TIMER_INVALID) { + if (ccr == 0) { return; } diff --git a/wirish/time.h b/wirish/time.h index fad47a4..c3dfe2d 100644 --- a/wirish/time.h +++ b/wirish/time.h @@ -59,10 +59,10 @@ static inline uint32 micros(void) { nvic_globalirq_enable(); - /* MAPLE_RELOAD_VAL is 1 less than the number of cycles it actually - takes to complete a systick reload */ + /* SYSTICK_RELOAD_VAL is 1 less than the number of cycles it + actually takes to complete a SysTick reload */ res = (ms * US_PER_MS) + - (MAPLE_RELOAD_VAL + 1 - cycle_cnt)/CYCLES_PER_MICROSECOND; + (SYSTICK_RELOAD_VAL + 1 - cycle_cnt)/CYCLES_PER_MICROSECOND; return res; } diff --git a/wirish/wirish.c b/wirish/wirish.c index 3239838..db38050 100644 --- a/wirish/wirish.c +++ b/wirish/wirish.c @@ -62,7 +62,7 @@ void init(void) { rcc_set_prescaler(RCC_PRESCALER_APB2, RCC_APB2_HCLK_DIV_1); nvic_init(); - systick_init(MAPLE_RELOAD_VAL); + systick_init(SYSTICK_RELOAD_VAL); gpio_init(); adc_init(); timer_init(TIMER1, 1); diff --git a/wirish/wirish_math.cpp b/wirish/wirish_math.cpp index 12a21c3..5aa6510 100644 --- a/wirish/wirish_math.cpp +++ b/wirish/wirish_math.cpp @@ -47,8 +47,3 @@ long random(long howsmall, long howbig) { return random(diff) + howsmall; } -long map(long x, long in_min, long in_max, long out_min, long out_max) { - return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min; -} - - diff --git a/wirish/wirish_math.h b/wirish/wirish_math.h index 4543c1b..ae73d61 100644 --- a/wirish/wirish_math.h +++ b/wirish/wirish_math.h @@ -32,7 +32,10 @@ void randomSeed(unsigned int); long random(long); long random(long, long); -long map(long, long, long, long, long); +/* TODO: profile code bloat due to inlining this */ +inline long map(long x, long in_min, long in_max, long out_min, long out_max) { + return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min; +} #define PI 3.1415926535897932384626433832795 #define HALF_PI 1.5707963267948966192313216916398 -- cgit v1.2.3