aboutsummaryrefslogtreecommitdiffstats
path: root/libmaple/timers.c
diff options
context:
space:
mode:
authorMarti Bolivar <mbolivar@leaflabs.com>2011-03-22 16:59:29 -0400
committerMarti Bolivar <mbolivar@leaflabs.com>2011-03-24 07:25:14 -0400
commit61db54f52f32e63c895d775982fbcdcb67f2dde6 (patch)
treeb0513c712b5888ab0a4e6613fdee3db606b61aaf /libmaple/timers.c
parent6bc8cb7c1181e8005019e4ce1f2bea956c44e044 (diff)
downloadlibrambutan-61db54f52f32e63c895d775982fbcdcb67f2dde6.tar.gz
librambutan-61db54f52f32e63c895d775982fbcdcb67f2dde6.zip
Initial timer refactor.
Basic PWM works. Had some problems in testing that might be due to USART bugs. HardwareTimer has been removed from the build for now; I will re-implement it in terms of the new libmaple API, but consider it deprecated. Let's come up with something better. Servo is implemented in terms of HardwareTimer, so it also has been temporarily removed from the build. pwmWrite() likely got a little bit less inefficient due to indirection, but the PIN_MAPs shrank by a pointer per PinMapping.
Diffstat (limited to 'libmaple/timers.c')
-rw-r--r--libmaple/timers.c526
1 files changed, 0 insertions, 526 deletions
diff --git a/libmaple/timers.c b/libmaple/timers.c
deleted file mode 100644
index c561d39..0000000
--- a/libmaple/timers.c
+++ /dev/null
@@ -1,526 +0,0 @@
-/******************************************************************************
- * The MIT License
- *
- * Copyright (c) 2010 Perry Hung.
- *
- * 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.
- *****************************************************************************/
-
-/**
- * @brief General timer routines
- */
-
-/* TODO: actually support timer5 and timer8 */
-
-#include "libmaple.h"
-#include "rcc.h"
-#include "nvic.h"
-#include "timers.h"
-
-/* Timer descriptor table */
-struct timer_dev timer_dev_table[] = {
- [TIMER1] = {
- .base = (timer_port*)TIMER1_BASE,
- .rcc_dev_num = RCC_TIMER1,
- .nvic_dev_num = NVIC_TIMER1
- },
- [TIMER2] = {
- .base = (timer_port*)TIMER2_BASE,
- .rcc_dev_num = RCC_TIMER2,
- .nvic_dev_num = NVIC_TIMER2
- },
- [TIMER3] = {
- .base = (timer_port*)TIMER3_BASE,
- .rcc_dev_num = RCC_TIMER3,
- .nvic_dev_num = NVIC_TIMER3
- },
- [TIMER4] = {
- .base = (timer_port*)TIMER4_BASE,
- .rcc_dev_num = RCC_TIMER4,
- .nvic_dev_num = NVIC_TIMER4
- },
-#ifdef STM32_HIGH_DENSITY
- /* High density devices only (eg, Maple Native) */
- [TIMER5] = {
- .base = (timer_port*)TIMER5_BASE,
- .rcc_dev_num = RCC_TIMER5,
- .nvic_dev_num = NVIC_TIMER5
- },
- [TIMER8] = {
- .base = (timer_port*)TIMER8_BASE,
- .rcc_dev_num = RCC_TIMER8,
- .nvic_dev_num = NVIC_TIMER8
- },
-#endif
-};
-
-/* 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(timer_dev_num timer_num, uint16 prescale) {
- /* TODO: doesn't catch 6+7 */
-
- timer_port *timer = timer_dev_table[timer_num].base;
- uint8 is_advanced = 0;
-
- if (timer_num == TIMER1) {
- is_advanced = 1;
- }
-#ifdef STM32_HIGH_DENSITY
- if (timer_num == TIMER8) {
- is_advanced = 1;
- }
-#endif
-
- rcc_clk_enable(timer_dev_table[timer_num].rcc_dev_num);
-
- timer->CR1 = ARPE; // No clock division
- // Do not buffer auto-reload preload
- // Edge aligned
- // Upcounter
- // Do not stop counter at update event
- // Update events enabled (etc, see bits [1:2])
- // Counter disabled for now
-
- timer->PSC = prescale; // Prescaling by prescale (duh)
- timer->ARR = 0xFFFF; // Max reload cont
-
- /* initialize all the channels to 50% duty cycle,
- * TODO: none of them actually get output unless the gpio pin
- * is set, this will probably consume a bit more power but
- * we'll worry about that later. */
- timer->CCR1 = 0x8FFF; // PWM start value
- timer->CCMR1 |= 0x68; // PWM mode 1, enable preload register.
-
- timer->CCR2 = 0x8FFF; // PWM start value
- timer->CCMR1 |= (0x68 << 8);// PWM mode 1, enable preload register.
-
- timer->CCR3 = 0x8FFF; // PWM start value
- timer->CCMR2 |= 0x68; // PWM mode 1, enable preload register.
-
- timer->CCR4 = 0x8FFF; // PWM start value
- timer->CCMR2 |= (0x68 << 8);// PWM mode 1, enable preload register.
-
- /* Advanced timer? */
- if (is_advanced) {
- timer->BDTR = 0x8000; // moe enable
- }
-
- timer->SR = 0; // clear it
- timer->DIER = 0; // disable update interrupt
- timer->EGR = 1; // Initialize update event and shadow registers
- timer->CR1 |= 1; // Enable timer
-}
-
-/* Stops the counter; the mode and settings are not modified */
-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(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(timer_dev_num timer_num, uint16 value) {
- timer_port *timer = timer_dev_table[timer_num].base;
-
- timer->CNT = value;
-}
-
-/* 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->PSC;
-}
-
-/* Sets the prescaler */
-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(timer_dev_num timer_num, uint16 max_reload) {
- timer_port *timer = timer_dev_table[timer_num].base;
-
- timer->ARR = max_reload;
-}
-
-/* This quickly disables all 4 timers, presumably as part of a system shutdown
- * or similar to prevent interrupts and PWM output without 16 seperate function
- * calls to timer_set_mode */
-void timer_disable_all(void) {
- timer_port *timer;
-#ifdef STM32_HIGH_DENSITY
- timer_port *timers[6] = { (timer_port*)TIMER1_BASE,
- (timer_port*)TIMER2_BASE,
- (timer_port*)TIMER3_BASE,
- (timer_port*)TIMER4_BASE,
- (timer_port*)TIMER5_BASE,
- (timer_port*)TIMER8_BASE,
- };
- uint8 i;
- for (i = 0; i < 6; i++) {
- timer = timers[i];
- timer->CR1 = 0;
- timer->CCER = 0;
- }
-#else
- timer_port *timers[4] = { (timer_port*)TIMER1_BASE,
- (timer_port*)TIMER2_BASE,
- (timer_port*)TIMER3_BASE,
- (timer_port*)TIMER4_BASE,
- };
- uint8 i;
- for (i = 0; i < 4; i++) {
- timer = timers[i];
- timer->CR1 = 0;
- timer->CCER = 0;
- }
-#endif
-}
-
-/* Sets the mode of individual timer channels, including a DISABLE 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);
-
- switch(mode) {
- case TIMER_DISABLED:
- /* Disable the channel
- * Disable any interrupt
- * Clear interrupt SR? (TODO) */
- timer->DIER &= ~(1 << channel); // 1-indexed compare nums
- timer_detach_interrupt(timer_num, channel);
- timer->CCER &= ~(1 << (4*(channel - 1))); // 0-indexed
- break;
- case TIMER_PWM:
- /* Set CCMR mode
- * Keep existing reload value
- * Disable any interrupt
- * Clear interrupt SR? (TODO)
- * Enable channel */
- timer->DIER &= ~(1 << channel); // 1-indexed compare nums
- switch (channel) {
- case 1:
- timer->CCMR1 &= ~(0xFF);
- timer->CCMR1 |= 0x68; // PWM mode 1, enable preload register.
- break;
- case 2:
- timer->CCMR1 &= ~(0xFF00);
- timer->CCMR1 |= (0x68 << 8);// PWM mode 1, enable preload register.
- break;
- case 3:
- timer->CCMR2 &= ~(0xFF);
- timer->CCMR2 |= 0x68; // PWM mode 1, enable preload register.
- break;
- case 4:
- timer->CCMR2 &= ~(0xFF00);
- timer->CCMR2 |= (0x68 << 8);// PWM mode 1, enable preload register.
- break;
- default:
- ASSERT(0);
- }
- timer->CCER |= (1 << (4*(channel - 1))); // Enable
- break;
- case TIMER_OUTPUTCOMPARE:
- /* Set CCMR mode
- * Keep existing reload value
- * Don't modify interrupt (needs to be attached to enable)
- * Clear interrupt SR? (TODO)
- * Enable channel */
- switch (channel) {
- case 1:
- timer->CCMR1 &= ~(0xFF);
- timer->CCMR1 |= 0x0010; // PWM mode 1, enable preload register.
- break;
- case 2:
- timer->CCMR1 &= ~(0xFF00);
- timer->CCMR1 |= 0x1000; // PWM mode 1, enable preload register.
- break;
- case 3:
- timer->CCMR2 &= ~(0xFF);
- timer->CCMR2 |= 0x0010; // PWM mode 1, enable preload register.
- break;
- case 4:
- timer->CCMR2 &= ~(0xFF00);
- timer->CCMR2 |= 0x1000; // PWM mode 1, enable preload register.
- break;
- default:
- ASSERT(0);
- }
- timer->CCER |= (1 << (4*(channel - 1))); // Enable
- break;
- default:
- ASSERT(0);
- }
-}
-
-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(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;
-
- switch(channel_num) {
- case 1:
- timer->CCR1 = value;
- break;
- case 2:
- timer->CCR2 = value;
- break;
- case 3:
- timer->CCR3 = value;
- break;
- case 4:
- timer->CCR4 = value;
- break;
- }
-}
-
-/* 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(timer_dev_num timer_num,
- uint8 compare_num,
- voidFuncPtr handler) {
- ASSERT(compare_num > 0 && compare_num <= 4);
-
- timer_port *timer = timer_dev_table[timer_num].base;
-
- timer_dev_table[timer_num].handlers[compare_num-1] = handler;
- timer->DIER |= (1 << compare_num); // 1-indexed compare nums
- nvic_irq_enable(timer_dev_table[timer_num].nvic_dev_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;
-
- timer_dev_table[timer_num].handlers[compare_num-1] = 0;
- 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.
- *
- * These ISRs get called when the timer interrupt is enabled, the
- * timer is running, and the timer count equals any of the CCR
- * registers /or/ has overflowed.
- *
- * This is a rather long implementation... */
-void __irq_tim1_cc(void) {
- timer_port *timer = (timer_port*)TIMER1_BASE;
- uint16 sr_buffer;
- sr_buffer = timer->SR;
-
- /* Simply switch/case-ing here doesn't work because multiple
- * CC flags may be high. */
- if(sr_buffer & 0x10){ // CC4 flag
- timer->SR &= ~(0x10);
- if(timer_dev_table[TIMER1].handlers[3]) {
- timer_dev_table[TIMER1].handlers[3]();
- }
- }
- if(sr_buffer & 0x8){ // CC3 flag
- timer->SR &= ~(0x8);
- if(timer_dev_table[TIMER1].handlers[2]) {
- timer_dev_table[TIMER1].handlers[2]();
- }
- }
- if(sr_buffer & 0x4){ // CC2 flag
- timer->SR &= ~(0x4);
- if(timer_dev_table[TIMER1].handlers[1]) {
- timer_dev_table[TIMER1].handlers[1]();
- }
- }
- if(sr_buffer & 0x2){ // CC1 flag
- timer->SR &= ~(0x2);
- if(timer_dev_table[TIMER1].handlers[0]) {
- timer_dev_table[TIMER1].handlers[0]();
- }
- }
- if(sr_buffer & 0x1){ // Update flag
- timer->SR &= ~(0x1);
- //timer->EGR = 1;
- }
-}
-void __irq_tim2(void) {
- /* This is a rather long implementation... */
- timer_port *timer = (timer_port*)TIMER2_BASE;
- uint16 sr_buffer;
- sr_buffer = timer->SR;
-
- if(sr_buffer & 0x10){ // CC4 flag
- timer->SR &= ~(0x10);
- if(timer_dev_table[TIMER2].handlers[3]) {
- timer_dev_table[TIMER2].handlers[3]();
- }
- }
- if(sr_buffer & 0x8){ // CC3 flag
- timer->SR &= ~(0x8);
- if(timer_dev_table[TIMER2].handlers[2]) {
- timer_dev_table[TIMER2].handlers[2]();
- }
- }
- if(sr_buffer & 0x4){ // CC2 flag
- timer->SR &= ~(0x4);
- if(timer_dev_table[TIMER2].handlers[1]) {
- timer_dev_table[TIMER2].handlers[1]();
- }
- }
- if(sr_buffer & 0x2){ // CC1 flag
- timer->SR &= ~(0x2);
- if(timer_dev_table[TIMER2].handlers[0]) {
- timer_dev_table[TIMER2].handlers[0]();
- }
- }
- if(sr_buffer & 0x1){ // Update flag
- timer->SR &= ~(0x1);
- //timer->EGR = 1;
- }
-}
-void __irq_tim3(void) {
- /* This is a rather long implementation... */
- timer_port *timer = (timer_port*)TIMER3_BASE;
- uint16 sr_buffer;
- sr_buffer = timer->SR;
-
- if(sr_buffer & 0x10){ // CC4 flag
- timer->SR &= ~(0x10);
- if(timer_dev_table[TIMER3].handlers[3]) {
- timer_dev_table[TIMER3].handlers[3]();
- }
- }
- if(sr_buffer & 0x8){ // CC3 flag
- timer->SR &= ~(0x8);
- if(timer_dev_table[TIMER3].handlers[2]) {
- timer_dev_table[TIMER3].handlers[2]();
- }
- }
- if(sr_buffer & 0x4){ // CC2 flag
- timer->SR &= ~(0x4);
- if(timer_dev_table[TIMER3].handlers[1]) {
- timer_dev_table[TIMER3].handlers[1]();
- }
- }
- if(sr_buffer & 0x2){ // CC1 flag
- timer->SR &= ~(0x2);
- if(timer_dev_table[TIMER3].handlers[0]) {
- timer_dev_table[TIMER3].handlers[0]();
- }
- }
- if(sr_buffer & 0x1){ // Update flag
- timer->SR &= ~(0x1);
- //timer->EGR = 1;
- }
-}
-
-void __irq_tim4(void) {
- /* This is a rather long implementation... */
- timer_port*timer = (timer_port*)TIMER4_BASE;
- uint16 sr_buffer;
- sr_buffer = timer->SR;
-
- if(sr_buffer & 0x10){ // CC4 flag
- timer->SR &= ~(0x10);
- if(timer_dev_table[TIMER4].handlers[3]) {
- timer_dev_table[TIMER4].handlers[3]();
- }
- }
- if(sr_buffer & 0x8){ // CC3 flag
- timer->SR &= ~(0x8);
- if(timer_dev_table[TIMER4].handlers[2]) {
- timer_dev_table[TIMER4].handlers[2]();
- }
- }
- if(sr_buffer & 0x4){ // CC2 flag
- timer->SR &= ~(0x4);
- if(timer_dev_table[TIMER4].handlers[1]) {
- timer_dev_table[TIMER4].handlers[1]();
- }
- }
- if(sr_buffer & 0x2){ // CC1 flag
- timer->SR &= ~(0x2);
- if(timer_dev_table[TIMER4].handlers[0]) {
- timer_dev_table[TIMER4].handlers[0]();
- }
- }
- if(sr_buffer & 0x1){ // Update flag
- timer->SR &= ~(0x1);
- //timer->EGR = 1;
- }
-}