diff options
author | Marti Bolivar <mbolivar@mit.edu> | 2010-12-08 23:39:37 -0500 |
---|---|---|
committer | Marti Bolivar <mbolivar@mit.edu> | 2010-12-08 23:39:37 -0500 |
commit | b67d281d85bd59a9738a9a43c4db1027f81d9208 (patch) | |
tree | 1940b4743a945160d21eb37c8578fa46e9118d4e /libmaple | |
parent | b34f826ae1470aaa791bd8ed8dd66ccd4d96d82c (diff) | |
download | librambutan-b67d281d85bd59a9738a9a43c4db1027f81d9208.tar.gz librambutan-b67d281d85bd59a9738a9a43c4db1027f81d9208.zip |
Servo library tested and debugged.
Some additional HardwareTimer methods introduced to make this
convenient; ancillary libmaple/timers.h changes resulted.
Diffstat (limited to 'libmaple')
-rw-r--r-- | libmaple/timers.c | 84 | ||||
-rw-r--r-- | libmaple/timers.h | 134 |
2 files changed, 143 insertions, 75 deletions
diff --git a/libmaple/timers.c b/libmaple/timers.c index c369d1f..29aeeba 100644 --- a/libmaple/timers.c +++ b/libmaple/timers.c @@ -73,16 +73,20 @@ struct timer_dev timer_dev_table[] = { /* This function should probably be rewriten to take (timer_num, mode) * and have prescaler set elsewhere. The mode can be passed through to * set_mode at the end */ -void timer_init(uint8 timer_num, uint16 prescale) { +void timer_init(timer_dev_num timer_num, uint16 prescale) { /* TODO: doesn't catch 6+7 */ - ASSERT((timer_num != TIMER6) && (timer_num != TIMER7)); timer_port *timer = timer_dev_table[timer_num].base; uint8 is_advanced = 0; - if((timer_num == TIMER1) || (timer_num == TIMER8)) { + if (timer_num == TIMER1) { is_advanced = 1; } +#if NR_TIMERS >= 8 + if (timer_num == TIMER8) { + is_advanced = 1; + } +#endif rcc_clk_enable(timer_dev_table[timer_num].rcc_dev_num); @@ -125,48 +129,61 @@ void timer_init(uint8 timer_num, uint16 prescale) { } /* Stops the counter; the mode and settings are not modified */ -void timer_pause(uint8 timer_num) { +void timer_pause(timer_dev_num timer_num) { timer_port *timer = timer_dev_table[timer_num].base; timer->CR1 &= ~(0x0001); // CEN } /* Starts the counter; the mode and settings are not modified */ -void timer_resume(uint8 timer_num) { +void timer_resume(timer_dev_num timer_num) { timer_port *timer = timer_dev_table[timer_num].base; timer->CR1 |= 0x0001; // CEN } +/* Returns the current timer counter value. Probably very inaccurate + * if the counter is running with a low prescaler. */ +uint16 timer_get_count(timer_dev_num timer_num) { + timer_port *timer = timer_dev_table[timer_num].base; + + return timer->CNT; +} + /* This function sets the counter value via register for the specified * timer. Can't think of specific usecases except for resetting to * zero but it's easy to implement and allows for "creative" * programming */ -void timer_set_count(uint8 timer_num, uint16 value) { +void timer_set_count(timer_dev_num timer_num, uint16 value) { timer_port *timer = timer_dev_table[timer_num].base; timer->CNT = value; } -/* Returns the current timer counter value. Probably very inaccurate - * if the counter is running with a low prescaler. */ -uint16 timer_get_count(uint8 timer_num) { +/* Get the prescaler buffer value (remember, the actual prescaler + * doesn't get set until an update event). */ +uint16 timer_get_prescaler(timer_dev_num timer_num) { timer_port *timer = timer_dev_table[timer_num].base; - - return timer->CNT; + return timer->PSC; } /* Sets the prescaler */ -void timer_set_prescaler(uint8 timer_num, uint16 prescale) { +void timer_set_prescaler(timer_dev_num timer_num, uint16 prescale) { timer_port *timer = timer_dev_table[timer_num].base; timer->PSC = prescale; } +/* Get the reload value for the entire timer. */ +uint16 timer_get_reload(timer_dev_num timer_num) { + timer_port *timer = timer_dev_table[timer_num].base; + return timer->ARR; +} + /* This sets the "reload" or "overflow" value for the entire timer. We * should probably settle on either "reload" or "overflow" to prevent * confusion? */ -void timer_set_reload(uint8 timer_num, uint16 max_reload) { +void timer_set_reload(timer_dev_num timer_num, uint16 max_reload) { timer_port *timer = timer_dev_table[timer_num].base; timer->ARR = max_reload; @@ -211,7 +228,7 @@ void timer_disable_all(void) { } /* Sets the mode of individual timer channels, including a DISABLE mode */ -void timer_set_mode(uint8 timer_num, uint8 channel, TimerMode mode) { +void timer_set_mode(timer_dev_num timer_num, uint8 channel, TimerMode mode) { timer_port *timer = timer_dev_table[timer_num].base; ASSERT(channel >= 1); @@ -286,18 +303,36 @@ void timer_set_mode(uint8 timer_num, uint8 channel, TimerMode mode) { } } +uint16 timer_get_compare_value(timer_dev_num timer_num, uint8 channel_num) { + /* faster: just read TIMERx_CHy_CCR (see timers.h) */ + ASSERT(channel_num > 0 && channel_num <= 4); + timer_port *timer = timer_dev_table[timer_num].base; + switch(channel_num) { + case 1: + return timer->CCR1; + case 2: + return timer->CCR2; + case 3: + return timer->CCR3; + case 4: + return timer->CCR4; + default: /* in case ASSERT is disabled */ + return 0; + } +} + /* This sets the compare value (aka the trigger) for a given timer * channel */ -void timer_set_compare_value(uint8 timer_num, - uint8 compare_num, +void timer_set_compare_value(timer_dev_num timer_num, + uint8 channel_num, uint16 value) { + ASSERT(channel_num > 0 && channel_num <= 4); + /* The faster version of this function is the inline timer_pwm_write_ccr */ timer_port *timer = timer_dev_table[timer_num].base; - ASSERT(compare_num > 0 && compare_num <= 4); - - switch(compare_num) { + switch(channel_num) { case 1: timer->CCR1 = value; break; @@ -315,7 +350,7 @@ void timer_set_compare_value(uint8 timer_num, /* Stores a pointer to the passed usercode interrupt function and configures * the actual ISR so that it will actually be called */ -void timer_attach_interrupt(uint8 timer_num, +void timer_attach_interrupt(timer_dev_num timer_num, uint8 compare_num, voidFuncPtr handler) { ASSERT(compare_num > 0 && compare_num <= 4); @@ -327,7 +362,7 @@ void timer_attach_interrupt(uint8 timer_num, nvic_irq_enable(timer_dev_table[timer_num].nvic_dev_num); } -void timer_detach_interrupt(uint8 timer_num, uint8 compare_num) { +void timer_detach_interrupt(timer_dev_num timer_num, uint8 compare_num) { ASSERT(compare_num > 0 && compare_num <= 4); timer_port *timer = timer_dev_table[timer_num].base; @@ -336,6 +371,13 @@ void timer_detach_interrupt(uint8 timer_num, uint8 compare_num) { timer->DIER &= ~(1 << compare_num); // 1-indexed compare nums } +void timer_generate_update(timer_dev_num timer_num) { + /* cause update event by setting UG bit in EGR. updates prescaler + ratio etc. */ + timer_port *timer = timer_dev_table[timer_num].base; + timer->EGR |= 0x1; +} + /* The following are the actual interrupt handlers; 1 for each timer which must * determine which actual compare value (aka channel) was triggered. * diff --git a/libmaple/timers.h b/libmaple/timers.h index d180bab..1d929db 100644 --- a/libmaple/timers.h +++ b/libmaple/timers.h @@ -82,7 +82,7 @@ extern "C"{ #endif -typedef volatile uint32* TimerCCR; +typedef volatile uint16* TimerCCR; #define TIMER1_BASE 0x40012C00 #define TIMER2_BASE 0x40000000 @@ -96,41 +96,43 @@ typedef volatile uint32* TimerCCR; #define ARPE BIT(7) // Auto-reload preload enable #define NOT_A_TIMER 0 -#define TIMER_CCR(NUM,CHAN) TIMER ## NUM ## _CH ## CHAN ## _CRR +#define TIMER_CCR(NUM,CHAN) (TIMER ## NUM ## _CH ## CHAN ## _CRR) -#define TIMER1_CH1_CCR TIMER1_BASE + 0x34 -#define TIMER1_CH2_CCR TIMER1_BASE + 0x38 -#define TIMER1_CH3_CCR TIMER1_BASE + 0x3C -#define TIMER1_CH4_CCR TIMER1_BASE + 0x40 +/* Timers 1-4 are present on the entire STM32 line. */ -#define TIMER2_CH1_CCR TIMER2_BASE + 0x34 -#define TIMER2_CH2_CCR TIMER2_BASE + 0x38 -#define TIMER2_CH3_CCR TIMER2_BASE + 0x3C -#define TIMER2_CH4_CCR TIMER2_BASE + 0x40 +#define TIMER1_CH1_CCR ((TimerCCR)(TIMER1_BASE + 0x34)) +#define TIMER1_CH2_CCR ((TimerCCR)(TIMER1_BASE + 0x38)) +#define TIMER1_CH3_CCR ((TimerCCR)(TIMER1_BASE + 0x3C)) +#define TIMER1_CH4_CCR ((TimerCCR)(TIMER1_BASE + 0x40)) -#define TIMER3_CH1_CCR TIMER3_BASE + 0x34 -#define TIMER3_CH2_CCR TIMER3_BASE + 0x38 -#define TIMER3_CH3_CCR TIMER3_BASE + 0x3C -#define TIMER3_CH4_CCR TIMER3_BASE + 0x40 +#define TIMER2_CH1_CCR ((TimerCCR)(TIMER2_BASE + 0x34)) +#define TIMER2_CH2_CCR ((TimerCCR)(TIMER2_BASE + 0x38)) +#define TIMER2_CH3_CCR ((TimerCCR)(TIMER2_BASE + 0x3C)) +#define TIMER2_CH4_CCR ((TimerCCR)(TIMER2_BASE + 0x40)) -#define TIMER4_CH1_CCR TIMER4_BASE + 0x34 -#define TIMER4_CH2_CCR TIMER4_BASE + 0x38 -#define TIMER4_CH3_CCR TIMER4_BASE + 0x3C -#define TIMER4_CH4_CCR TIMER4_BASE + 0x40 +#define TIMER3_CH1_CCR ((TimerCCR)(TIMER3_BASE + 0x34)) +#define TIMER3_CH2_CCR ((TimerCCR)(TIMER3_BASE + 0x38)) +#define TIMER3_CH3_CCR ((TimerCCR)(TIMER3_BASE + 0x3C)) +#define TIMER3_CH4_CCR ((TimerCCR)(TIMER3_BASE + 0x40)) -/* Timer5 and Timer8 are in high-density devices only (such as Maple - Native). Timer6 and Timer7 in these devices have no output compare +#define TIMER4_CH1_CCR ((TimerCCR)(TIMER4_BASE + 0x34)) +#define TIMER4_CH2_CCR ((TimerCCR)(TIMER4_BASE + 0x38)) +#define TIMER4_CH3_CCR ((TimerCCR)(TIMER4_BASE + 0x3C)) +#define TIMER4_CH4_CCR ((TimerCCR)(TIMER4_BASE + 0x40)) + +/* Timers 5 and 8 are in high-density devices only (such as Maple + Native). Timers 6 and 7 in these devices have no output compare pins. */ -#define TIMER5_CH1_CCR TIMER5_BASE + 0x34 -#define TIMER5_CH2_CCR TIMER5_BASE + 0x38 -#define TIMER5_CH3_CCR TIMER5_BASE + 0x3C -#define TIMER5_CH4_CCR TIMER5_BASE + 0x40 +#define TIMER5_CH1_CCR ((TimerCCR)(TIMER5_BASE + 0x34)) +#define TIMER5_CH2_CCR ((TimerCCR)(TIMER5_BASE + 0x38)) +#define TIMER5_CH3_CCR ((TimerCCR)(TIMER5_BASE + 0x3C)) +#define TIMER5_CH4_CCR ((TimerCCR)(TIMER5_BASE + 0x40)) -#define TIMER8_CH1_CCR TIMER8_BASE + 0x34 -#define TIMER8_CH2_CCR TIMER8_BASE + 0x38 -#define TIMER8_CH3_CCR TIMER8_BASE + 0x3C -#define TIMER8_CH4_CCR TIMER8_BASE + 0x40 +#define TIMER8_CH1_CCR ((TimerCCR)(TIMER8_BASE + 0x34)) +#define TIMER8_CH2_CCR ((TimerCCR)(TIMER8_BASE + 0x38)) +#define TIMER8_CH3_CCR ((TimerCCR)(TIMER8_BASE + 0x3C)) +#define TIMER8_CH4_CCR ((TimerCCR)(TIMER8_BASE + 0x40)) /** * Used to configure the behavior of a timer. @@ -149,6 +151,8 @@ typedef enum TimerMode { } TimerMode; typedef struct { + /* Fields up to ARR common to general purpose (2,3,4,5), advanced + control (1,8) and basic (6, 7) timers: */ volatile uint16 CR1; uint16 RESERVED0; volatile uint16 CR2; @@ -173,8 +177,9 @@ typedef struct { uint16 RESERVED10; volatile uint16 ARR; uint16 RESERVED11; - volatile uint16 RCR; - uint16 RESERVED12; + /* Basic timers have none of the following: */ + volatile uint16 RCR; /* Advanced control timers only */ + uint16 RESERVED12; /* Advanced control timers only */ volatile uint16 CCR1; uint16 RESERVED13; volatile uint16 CCR2; @@ -183,25 +188,32 @@ typedef struct { uint16 RESERVED15; volatile uint16 CCR4; uint16 RESERVED16; - volatile uint16 BDTR; // Not used in general purpose timers - uint16 RESERVED17; // Not used in general purpose timers + volatile uint16 BDTR; /* Advanced control timers only */ + uint16 RESERVED17; /* Advanced control timers only */ volatile uint16 DCR; uint16 RESERVED18; volatile uint16 DMAR; uint16 RESERVED19; } timer_port; -/* timer device numbers */ -enum { - TIMER1, - TIMER2, - TIMER3, - TIMER4, - TIMER5, // High density only - TIMER6, // High density only; no compare - TIMER7, // High density only; no compare - TIMER8, // High density only -}; +/** + * Timer device numbers. See STM32 reference manual, chapters 13-15. + */ +/* several locations depend on TIMER1=0, etc.; don't change the + enumerator values to start at 1. */ +typedef enum { + TIMER1, /*< Advanced control timer TIM1 */ + TIMER2, /*< General purpose timer TIM2 */ + TIMER3, /*< General purpose timer TIM3 */ + TIMER4, /*< General purpose timer TIM4 */ +#if NR_TIMERS >= 8 + TIMER5, /*< General purpose timer TIM5; high density only */ + /* TIMER6, /\*< Basic timer TIM6; high density only *\/ */ + /* TIMER7, /\*< Basic timer TIM7; high density only *\/ */ + TIMER8, /*< Advanced control timer TIM8; high density only */ +#endif + TIMER_INVALID /* FIXME: this is starting to seem like a bad idea */ +} timer_dev_num; /* timer descriptor */ struct timer_dev { @@ -218,19 +230,33 @@ extern struct timer_dev timer_dev_table[]; * timer -> {1-4} * prescale -> {1-65535} */ -void timer_init(uint8, uint16); +void timer_init(timer_dev_num, uint16); void timer_disable_all(void); -uint16 timer_get_count(uint8); -void timer_set_count(uint8,uint16); -void timer_pause(uint8); -void timer_resume(uint8); -void timer_set_prescaler(uint8 timer_num, uint16 prescale); -void timer_set_reload(uint8 timer_num, uint16 max_reload); -void timer_set_mode(uint8 timer_num, uint8 compare_num, uint8 mode); -void timer_set_compare_value(uint8 timer_num, uint8 compare_num, uint16 value); -void timer_attach_interrupt(uint8 timer_num, uint8 compare_num, + +uint16 timer_get_count(timer_dev_num); +void timer_set_count(timer_dev_num,uint16); + +void timer_pause(timer_dev_num); +void timer_resume(timer_dev_num); + +uint16 timer_get_prescaler(timer_dev_num timer_num); +void timer_set_prescaler(timer_dev_num timer_num, uint16 prescale); + +uint16 timer_get_reload(timer_dev_num timer_num); +void timer_set_reload(timer_dev_num timer_num, uint16 max_reload); + +/* TODO: timer_get_mode */ +void timer_set_mode(timer_dev_num timer_num, uint8 channel_num, uint8 mode); + +uint16 timer_get_compare_value(timer_dev_num timer_num, uint8 channel_num); +void timer_set_compare_value(timer_dev_num timer_num, uint8 channel_num, uint16 value); + +void timer_attach_interrupt(timer_dev_num timer_num, uint8 channel_num, voidFuncPtr handler); -void timer_detach_interrupt(uint8 timer_num, uint8 compare_num); +void timer_detach_interrupt(timer_dev_num timer_num, uint8 channel_num); + +/* generate UEV */ +void timer_generate_update(timer_dev_num timer_num); /* Turn on PWM with duty_cycle on the specified channel in timer. * This function takes in a pointer to the corresponding CCR |