diff options
-rw-r--r-- | Makefile | 1 | ||||
-rw-r--r-- | libmaple/gpio.h | 26 | ||||
-rw-r--r-- | libmaple/timers.c | 84 | ||||
-rw-r--r-- | libmaple/timers.h | 210 | ||||
-rw-r--r-- | libraries/Servo/Servo.cpp | 138 | ||||
-rw-r--r-- | libraries/Servo/Servo.h | 116 | ||||
-rw-r--r-- | libraries/Servo/rules.mk | 35 | ||||
-rw-r--r-- | wirish/HardwareTimer.cpp | 150 | ||||
-rw-r--r-- | wirish/HardwareTimer.h | 176 | ||||
-rw-r--r-- | wirish/boards.h | 131 | ||||
-rw-r--r-- | wirish/comm/HardwareSerial.cpp | 11 | ||||
-rw-r--r-- | wirish/comm/HardwareSerial.h | 6 | ||||
-rw-r--r-- | wirish/ext_interrupts.h | 27 | ||||
-rw-r--r-- | wirish/io.h | 58 | ||||
-rw-r--r-- | wirish/pwm.c | 4 | ||||
-rw-r--r-- | wirish/time.h | 6 | ||||
-rw-r--r-- | wirish/wirish.c | 5 | ||||
-rw-r--r-- | wirish/wirish_digital.c | 32 | ||||
-rw-r--r-- | wirish/wirish_math.cpp | 5 | ||||
-rw-r--r-- | wirish/wirish_math.h | 7 |
20 files changed, 939 insertions, 289 deletions
@@ -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/gpio.h b/libmaple/gpio.h index 49360ee..53f77c4 100644 --- a/libmaple/gpio.h +++ b/libmaple/gpio.h @@ -42,15 +42,18 @@ * Alternate function open-drain * * - After reset, the alternate functions are not active and IO prts - * are set to Input Floating mode */ + * are set to Input Floating mode, EXCEPT for the Serial Wire and JTAG + * ports, which are in alternate function mode by default. */ -#define GPIOA_BASE (GPIO_Port*)0x40010800 -#define GPIOB_BASE (GPIO_Port*)0x40010C00 -#define GPIOC_BASE (GPIO_Port*)0x40011000 -#define GPIOD_BASE (GPIO_Port*)0x40011400 -#define GPIOE_BASE (GPIO_Port*)0x40011800 // High-density devices only -#define GPIOF_BASE (GPIO_Port*)0x40011C00 // High-density devices only -#define GPIOG_BASE (GPIO_Port*)0x40012000 // High-density devices only +#define AFIO_MAPR ((volatile uint32*)0x40010004) + +#define GPIOA_BASE ((GPIO_Port*)0x40010800) +#define GPIOB_BASE ((GPIO_Port*)0x40010C00) +#define GPIOC_BASE ((GPIO_Port*)0x40011000) +#define GPIOD_BASE ((GPIO_Port*)0x40011400) +#define GPIOE_BASE ((GPIO_Port*)0x40011800) // High-density devices only +#define GPIOF_BASE ((GPIO_Port*)0x40011C00) // High-density devices only +#define GPIOG_BASE ((GPIO_Port*)0x40012000) // High-density devices only #define GPIO_SPEED_50MHZ (0x3) @@ -109,6 +112,13 @@ static inline uint32 gpio_read_bit(GPIO_Port *port, uint8 gpio_pin) { return (port->IDR & BIT(gpio_pin) ? 1 : 0); } +/* For pins configured as output push-pull, reading the ODR returns + * the last value written in push-pull mode. + */ +static inline void gpio_toggle_pin(GPIO_Port *port, uint8 gpio_pin) { + port->ODR = port->ODR ^ BIT(gpio_pin); +} + #ifdef __cplusplus } // extern "C" #endif diff --git a/libmaple/timers.c b/libmaple/timers.c index 3a2cad7..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 channel, +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(channel > 0 && channel <= 4); - - switch(channel) { + 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 f5694ac..7589283 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,8 +188,8 @@ 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; @@ -192,18 +197,23 @@ typedef struct { } timer_port; /** - * All possible timer device numbers. + * 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, - TIMER2, - TIMER3, - TIMER4, - TIMER5, // High density only - TIMER6, // High density only; no compare - TIMER7, // High density only; no compare - TIMER8, // High density only -} timer_num_t; + 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 { @@ -216,7 +226,7 @@ struct timer_dev { extern struct timer_dev timer_dev_table[]; /** - * Turn on timer with prescale as the clock divisor. + * Initializes timer with prescale as the clock divisor. * * @param timer Timer number. Valid values are TIMER1, TIMER2, * TIMER3, TIMER4, and (on high-density devices) TIMER5, TIMER8. @@ -227,7 +237,7 @@ extern struct timer_dev timer_dev_table[]; * @see timer_set_prescaler() * @see timer_set_mode() */ -void timer_init(uint8 timer, uint16 prescale); +void timer_init(timer_dev_num, uint16); /** * Quickly disable all timers. Calling this function is faster than, @@ -241,8 +251,10 @@ void timer_disable_all(void); * with a low prescaler. * * @param timer the timer whose counter to return. + * + * @pre Timer has been initialized. */ -uint16 timer_get_count(uint8 timer); +uint16 timer_get_count(timer_dev_num); /** * Sets the counter value for the given timer. @@ -250,8 +262,10 @@ uint16 timer_get_count(uint8 timer); * @param timer the timer whose counter to set. * * @param value the new counter value. + * + * @pre Timer has been initialized. */ -void timer_set_count(uint8 timer, uint16 value); +void timer_set_count(timer_dev_num,uint16); /** * Stops the timer's counter from incrementing. Does not modify the @@ -260,8 +274,10 @@ void timer_set_count(uint8 timer, uint16 value); * @param timer the timer to pause. * * @see timer_resume() + * + * @pre Timer has been initialized. */ -void timer_pause(uint8 timer); +void timer_pause(timer_dev_num); /** * Starts the counter for the given timer. Does not modify the @@ -272,28 +288,45 @@ void timer_pause(uint8 timer); * @param timer the timer to resume. * * @see timer_pause() + * + * @pre Timer has been initialized. */ -void timer_resume(uint8 timer); +void timer_resume(timer_dev_num); /** - * Sets the prescaler for the given timer. The prescaler acts as a - * clock divider of the STM32 72MHz system clock, in that the timer's - * counter will subsequently increment with frequency equal to 72MHz / - * prescale. + * Returns the prescaler for the given timer. + * + * @see timer_set_prescaler() * - * Note that the timer will continue with its current prescaler until - * the next time its counter reaches its overflow value, starting a - * counting cycle. The new prescale value will be in effect for that - * subsequent counting cycle. + * @pre Timer has been initialized. + */ +uint16 timer_get_prescaler(timer_dev_num timer_num); + +/** + * Sets the prescaler for the given timer. This value goes into the + * PSC register, so it's 0-based (i.e., a prescale of 0 counts 1 tick + * per clock cycle). This prescale does not take effect until the + * next update event. * * @param timer the timer whose prescaler to set. * - * @param prescale the new prescaler, from 1--65,535. + * @param prescale the new prescaler. + * + * @pre Timer has been initialized. + */ +void timer_set_prescaler(timer_dev_num timer_num, uint16 prescale); + +/** + * Gets the reload value for the timer. + * + * @see timer_set_reload() + * + * @pre Timer has been initialized. */ -void timer_set_prescaler(uint8 timer, uint16 prescale); +uint16 timer_get_reload(timer_dev_num timer_num); /** - * Sets the reload value for the entire timer. + * Sets the reload value for the timer. * * After this function returns, the timer's counter will reset to 0 * after it has reached the value max_reload. @@ -301,8 +334,12 @@ void timer_set_prescaler(uint8 timer, uint16 prescale); * @param timer the timer whose reload to set. * * @param max_reload the new reload value. + * + * @pre Timer has been initialized. */ -void timer_set_reload(uint8 timer, uint16 max_reload); +void timer_set_reload(timer_dev_num timer_num, uint16 max_reload); + +/* TODO: timer_get_mode */ /** * Set the mode of an individual timer channel. @@ -320,8 +357,18 @@ void timer_set_reload(uint8 timer, uint16 max_reload); * @see TimerMode * * @see timer_disable_all() + * + * @pre Timer has been initialized. */ -void timer_set_mode(uint8 timer, uint8 channel, TimerMode mode); +void timer_set_mode(timer_dev_num timer_num, uint8 channel_num, uint8 mode); + +/** + * Get the compare value for the given timer channel. + * @see timer_set_compare_value() + * + * @pre Timer has been initialized. + */ +uint16 timer_get_compare_value(timer_dev_num timer_num, uint8 channel_num); /** * Sets the compare value for a given timer channel. Useful for @@ -339,8 +386,27 @@ void timer_set_mode(uint8 timer, uint8 channel, TimerMode mode); * @see timer_detach_interrupt() * * @see timer_set_reload() + * + * @pre Timer has been initialized. + */ +void timer_set_compare_value(timer_dev_num timer_num, uint8 channel_num, + uint16 value); + +/** + * Detach the interrupt handler for the given timer channel, if any. + * After this function returns, any handler attached to the given + * channel will no longer be called. + * + * @param timer the timer whose channel to detach the interrupt + * handler from. + * + * @param channel the channel from which to detach the interrupt handler. + * + * @see timer_attach_interrupt() + * + * @pre Timer has been initialized. */ -void timer_set_compare_value(uint8 timer, uint8 channel, uint16 compare); +void timer_detach_interrupt(timer_dev_num timer_num, uint8 channel_num); /** * Attach an interrupt handler for the given timer and channel. The @@ -365,22 +431,20 @@ void timer_set_compare_value(uint8 timer, uint8 channel, uint16 compare); * @see timer_detach_interrupt() * * @see timer_set_mode() + * + * @pre Timer has been initialized. */ -void timer_attach_interrupt(uint8 timer, uint8 channel, voidFuncPtr handler); +void timer_attach_interrupt(timer_dev_num timer_num, uint8 channel_num, + voidFuncPtr handler); /** - * Detach the interrupt handler for the given timer channel, if any. - * After this function returns, any handler attached to the given - * channel will no longer be called. - * - * @param timer the timer whose channel to detach the interrupt - * handler from. + * Programmatically generate an update event on the given timer. This + * updates the prescaler, reloads the compare value (in upcounting + * mode, etc.). * - * @param channel the channel from which to detach the interrupt handler. - * - * @see timer_attach_interrupt() + * @pre Timer has been initialized. */ -void timer_detach_interrupt(uint8 timer, uint8 channel); +void timer_generate_update(timer_dev_num timer_num); /** * Turn on PWM with duty_cycle. 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 <stdint.h> + +#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. @@ -90,6 +97,12 @@ class HardwareTimer { void resume(void); /** + * Returns the timer's prescale factor. + * @see HardwareTimer::setPrescaleFactor() + */ + uint16 getPrescaleFactor(); + + /** * Set the timer prescale. * * The prescaler acts as a clock divider to slow down the rate at @@ -112,6 +125,12 @@ class HardwareTimer { void setPrescaleFactor(uint16 factor); /** + * Gets the timer overflow value. + * @see HardwareTimer::setOverflow() + */ + uint16 getOverflow(); + + /** * Sets the timer overflow (or "reload") value. * * When the timer's counter reaches this, value it resets to @@ -122,11 +141,17 @@ 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. * * Note that there is some function call overhead associated with @@ -140,13 +165,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 * microseconds as possible. @@ -164,6 +182,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. * * Note: Timer1.setChannel1Mode(TIMER_PWM) may not work as @@ -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 0625d0a..84ff44f 100644 --- a/wirish/boards.h +++ b/wirish/boards.h @@ -57,24 +57,26 @@ 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; -/* LeafLabs Maple rev3, rev4 */ +/* LeafLabs Maple rev3, rev5 */ #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 static __attribute__ ((unused)) PinMapping PIN_MAP[NR_GPIO_PINS] = { /* D0/PA3 */ @@ -86,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 */ @@ -98,78 +100,80 @@ 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} }; -#endif + #define BOARD_INIT do { \ + } while(0) -/* LeafLabs Maple Native (prototype) */ -#ifdef BOARD_maple_native +#elif defined(BOARD_maple_native) + + /* 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] = { @@ -290,16 +294,18 @@ typedef struct PinMapping { /* D13/PA5 */ {EXTI5, EXTI_CONFIG_PORTA}, }; -#endif -#ifdef BOARD_maple_mini +#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 */ @@ -309,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 */ @@ -321,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 */ @@ -341,38 +347,45 @@ 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 + SW and JTAG debug support */ + /* don't use __clear_bits here! */ + #define BOARD_INIT \ + do { \ + *AFIO_MAPR = (*AFIO_MAPR | BIT(26)) & ~(BIT(25) | BIT(24)); \ + } while (0) -#endif +#else -#ifndef CYCLES_PER_MICROSECOND #error "Board type has not been selected correctly." + #endif #ifdef __cplusplus 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 fdfc06d..aad8aa7 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" /* NB: this class documented "by hand" (i.e., not using Doxygen) in: @@ -47,7 +49,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, @@ -55,7 +57,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 baud); void end(void); diff --git a/wirish/ext_interrupts.h b/wirish/ext_interrupts.h index d44978a..057c0d3 100644 --- a/wirish/ext_interrupts.h +++ b/wirish/ext_interrupts.h @@ -23,6 +23,7 @@ *****************************************************************************/ #include "libmaple_types.h" +#include "nvic.h" /** * @file ext_interrupts.h @@ -77,6 +78,32 @@ void attachInterrupt(uint8 pin, voidFuncPtr handler, ExtIntTriggerMode mode); */ void detachInterrupt(uint8 pin); +/** + * Re-enable interrupts. + * + * Call this after noInterrupts() to re-enable interrupt handling, + * after you have finished with a timing-critical section of code. + * + * @see noInterrupts() + */ +static inline __attribute__((always_inline)) void interrupts() { + nvic_globalirq_enable(); +} + +/** + * Disable interrupts. + * + * After calling this function, all user-programmable interrupts will + * be disabled. You can call this function before a timing-critical + * section of code, then call interrupts() to re-enable interrupt + * handling. + * + * @see interrupts() + */ +static inline __attribute__((always_inline)) void noInterrupts() { + nvic_globalirq_disable(); +} + #ifdef __cplusplus } #endif diff --git a/wirish/io.h b/wirish/io.h index 24f2611..547dc8e 100644 --- a/wirish/io.h +++ b/wirish/io.h @@ -33,6 +33,7 @@ #include "gpio.h" #include "adc.h" +#include "time.h" #ifdef __cplusplus extern "C"{ @@ -162,6 +163,63 @@ uint32 digitalRead(uint8); */ uint32 analogRead(uint8 pin); +/** + * Toggles the digital value at the given pin. + * + * The pin must have its mode set to OUTPUT. + * + * @param pin the pin to toggle. If the pin is HIGH, set it LOW. If + * it is LOW, set it HIGH. + * + * @see pinMode() + */ +void togglePin(uint8 pin); + +/** + * Toggle the LED. + * + * If the LED is on, turn it off. If it is off, turn it on. + * + * The LED must its mode set to OUTPUT. This can be accomplished + * portably over all LeafLabs boards by calling pinMode(BOARD_LED_PIN, + * OUTPUT) before calling this function. + * + * @see pinMode() + */ +static inline void toggleLED() { + togglePin(BOARD_LED_PIN); +} + +/** + * If the button is currently pressed, waits until the button is no + * longer being pressed, and returns true. Otherwise, returns false. + * + * The button pin must have its mode set to INPUT. This can be + * accomplished portably over all LeafLabs boards by calling + * pinMode(BOARD_BUTTON_PIN, INPUT). + * + * @see pinMode() + */ +uint8 isButtonPressed(); + +/** + * Wait until the button is pressed and released, timing out if no + * press occurs. + * + * The button pin must have its mode set to INPUT. This can be + * accomplished portably over all LeafLabs boards by calling + * pinMode(BOARD_BUTTON_PIN, INPUT). + * + * @param timeout_millis Number of milliseconds to wait until the + * button is pressed. If timeout_millis is 0, wait forever. + * + * @return true, if the button was pressed; false, if the timeout was + * reached. + * + * @see pinMode() + */ +uint8 waitForButtonPress(uint32 timeout_millis); + #ifdef __cplusplus } // extern "C" #endif diff --git a/wirish/pwm.c b/wirish/pwm.c index dc5cfab..072e4cd 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 18aef9a..c925f74 100644 --- a/wirish/time.h +++ b/wirish/time.h @@ -68,10 +68,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 0abec41..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); @@ -74,4 +74,7 @@ void init(void) { timer_init(TIMER8, 1); #endif setupUSB(); + + /* include the board-specific init macro */ + BOARD_INIT; } diff --git a/wirish/wirish_digital.c b/wirish/wirish_digital.c index d7da81f..6f9906d 100644 --- a/wirish/wirish_digital.c +++ b/wirish/wirish_digital.c @@ -103,3 +103,35 @@ void digitalWrite(uint8 pin, uint8 val) { gpio_write_bit(PIN_MAP[pin].port, PIN_MAP[pin].pin, val); } + +void togglePin(uint8 pin) { + gpio_toggle_pin(PIN_MAP[pin].port, PIN_MAP[pin].pin); +} + +uint8 isButtonPressed() { + if (digitalRead(BOARD_BUTTON_PIN)) { + while (digitalRead(BOARD_BUTTON_PIN)) + ; + return true; + } + return false; +} + +uint8 waitForButtonPress(uint32 timeout) { + uint32 start = millis(); + uint32 time; + if (timeout == 0) { + while (!isButtonPressed()) + ; + return true; + } + do { + time = millis(); + /* properly handle wrap-around */ + if ((start > time && time + (0xffffffffU - start) > timeout) || + time - start > timeout) { + return false; + } + } while (!isButtonPressed()); + return true; +} 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 9578eb4..4156722 100644 --- a/wirish/wirish_math.h +++ b/wirish/wirish_math.h @@ -78,9 +78,10 @@ long random(long min, long max); * @param toEnd the end of the value's mapped range. * @return the mapped value. */ -long map(long value, - long fromStart, long fromEnd, - long toStart, long toEnd); +/* 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 |