aboutsummaryrefslogtreecommitdiffstats
path: root/libmaple
diff options
context:
space:
mode:
Diffstat (limited to 'libmaple')
-rw-r--r--libmaple/gpio.h26
-rw-r--r--libmaple/timers.c84
-rw-r--r--libmaple/timers.h210
3 files changed, 218 insertions, 102 deletions
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.