aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMarti Bolivar <mbolivar@mit.edu>2010-12-14 15:41:17 -0500
committerMarti Bolivar <mbolivar@mit.edu>2010-12-14 15:41:17 -0500
commit74c8937446e1be4e0d21f69a8c098e2caf7814d5 (patch)
tree1c7b8e4d93a9512ee54cdd6c5bb7ede064f96b3e
parent5ceac644e90c929e77f05d357d1d35d45e673fac (diff)
parentb67d281d85bd59a9738a9a43c4db1027f81d9208 (diff)
downloadlibrambutan-74c8937446e1be4e0d21f69a8c098e2caf7814d5.tar.gz
librambutan-74c8937446e1be4e0d21f69a8c098e2caf7814d5.zip
Merge branch 'master' into newdoc
-rw-r--r--Makefile1
-rw-r--r--libmaple/gpio.h26
-rw-r--r--libmaple/timers.c84
-rw-r--r--libmaple/timers.h210
-rw-r--r--libraries/Servo/Servo.cpp138
-rw-r--r--libraries/Servo/Servo.h116
-rw-r--r--libraries/Servo/rules.mk35
-rw-r--r--wirish/HardwareTimer.cpp150
-rw-r--r--wirish/HardwareTimer.h176
-rw-r--r--wirish/boards.h131
-rw-r--r--wirish/comm/HardwareSerial.cpp11
-rw-r--r--wirish/comm/HardwareSerial.h6
-rw-r--r--wirish/ext_interrupts.h27
-rw-r--r--wirish/io.h58
-rw-r--r--wirish/pwm.c4
-rw-r--r--wirish/time.h6
-rw-r--r--wirish/wirish.c5
-rw-r--r--wirish/wirish_digital.c32
-rw-r--r--wirish/wirish_math.cpp5
-rw-r--r--wirish/wirish_math.h7
20 files changed, 939 insertions, 289 deletions
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/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