From 7e4bccd23f0ccea488c1525af2386d4970f35114 Mon Sep 17 00:00:00 2001 From: Nathael Pajani Date: Thu, 7 Nov 2013 03:02:09 +0100 Subject: [PATCH] Add support for timers / PWM /Counter Most has not been tested yet. --- drivers/timers.c | 234 +++++++++++++++++++++++++++++++++++ include/core/lpc_regs_12xx.h | 44 +++++++ include/drivers/timers.h | 162 ++++++++++++++++++++++++ 3 files changed, 440 insertions(+) create mode 100644 drivers/timers.c create mode 100644 include/drivers/timers.h diff --git a/drivers/timers.c b/drivers/timers.c new file mode 100644 index 0000000..1e03277 --- /dev/null +++ b/drivers/timers.c @@ -0,0 +1,234 @@ +/**************************************************************************** + * drivers/timers.c + * + * Copyright 2012 Nathael Pajani + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + *************************************************************************** */ + + + +/***************************************************************************** */ +/* Timers */ +/***************************************************************************** */ + +#include +#include "core/lpc_regs_12xx.h" +#include "core/lpc_core_cm0.h" +#include "core/system.h" +#include "lib/string.h" +#include "drivers/timers.h" +#include "drivers/gpio.h" + + +/* These are local to our file */ +struct timer_device +{ + struct lpc_timer* regs; + uint32_t power_bit; + uint32_t irq; +}; +static struct timer_device timer_devices[NUM_TIMERS] = { + { LPC_TMR16B0, LPC_SYS_ABH_CLK_CTRL_CT16B0, TIMER0_IRQ }, + { LPC_TMR16B1, LPC_SYS_ABH_CLK_CTRL_CT16B1, TIMER1_IRQ }, + { LPC_TMR32B0, LPC_SYS_ABH_CLK_CTRL_CT32B0, TIMER2_IRQ }, + { LPC_TMR32B1, LPC_SYS_ABH_CLK_CTRL_CT32B1, TIMER3_IRQ }, +}; + + + +/* Handlers */ +void TIMER_0_Handler(void) +{ + struct lpc_timer* timer = timer_devices[0].regs; + uint32_t intr_flags = timer->int_reg; + + /* Clear the interrupt */ + timer->int_reg = 0xFF; +} + + + +/* Start the timer : + * Remove the reset flag if present and set timer enable flag. + * Timer must be turned on and configured (no checks done here). + */ +void timer_start(uint32_t timer_num) +{ + if (timer_num >= NUM_TIMERS) + return; + /* Remove reset flag and set timer enable flag */ + timer_devices[timer_num].regs->timer_ctrl = LPC_TIMER_COUNTER_ENABLE; +} +/* Stops the timer counter + * FIXME: Does not issue reset ... need to check whether it is reseted or not. + */ +void timer_stop(uint32_t timer_num) +{ + if (timer_num >= NUM_TIMERS) + return; + /* Remove timer enable flag */ + timer_devices[timer_num].regs->timer_ctrl = 0; +} +/* Resets the timer and lets it count again imediately */ +void timer_restart(uint32_t timer_num) +{ + if (timer_num >= NUM_TIMERS) + return; + /* Set and remove timer reset flag */ + timer_devices[timer_num].regs->timer_ctrl |= LPC_TIMER_COUNTER_RESET; + timer_devices[timer_num].regs->timer_ctrl &= ~LPC_TIMER_COUNTER_RESET; +} + +uint32_t timer_get_capture_val(uint32_t timer_num, uint32_t channel) +{ + if (timer_num >= NUM_TIMERS) + return 0; + /* FIXME */ + return 0; +} + +/* Change the match value of a single timer channel */ +void timer_set_match(uint32_t timer_num, uint32_t channel, uint32_t val) +{ + if (timer_num >= NUM_TIMERS) + return; + if (channel > 2) + return; + + timer_devices[timer_num].regs->match_reg[channel] = val; +} + + +/***************************************************************************** */ +/* Timer Setup */ +/* Returns 0 on success + * Takes a timer number and a timer config structure as arguments. + * Refer to timer config structure for details. + */ +int timer_setup(uint32_t timer_num, struct timer_config* conf) +{ + struct timer_device* timer = NULL; + int i = 0; + if (timer_num >= NUM_TIMERS) + return -ENODEV; + timer = &(timer_devices[timer_num]); + + /* Configure the reset on capture functionality */ + if (conf->reset_on_capture != 0x00) { + timer->regs->count_ctrl = LPC_COUNTER_CLEAR_ON_EVENT_EN; + timer->regs->count_ctrl |= ((conf->reset_on_capture & 0x07) << LPC_COUNTER_CLEAR_ON_EVENT_SHIFT); + } + + switch (conf->mode) { + case LPC_TIMER_MODE_COUNTER: + if ((conf->config[0] & 0x03) == 0x00) { + return -EINVAL; + } + /* Must set capture chanel N config to 0b000 in capture control register, + (see remarks in user manual UM10441 page 268 section 14.7.11) */ + timer->regs->capture_ctrl &= ~LPC_TIMER_CAPTURE_ERASE(((conf->config[0] >> LPC_COUNTER_INC_INPUT_SHIFT) & 0x03) * 3); + /* Configure the counter */ + timer->regs->count_ctrl |= (conf->config[0] & 0x0F); + break; + case LPC_TIMER_MODE_CAPTURE: + timer->regs->capture_ctrl = 0; + for (i = 0; i < NUM_CHANS; i++) { + timer->regs->capture_ctrl |= ((conf->config[i] & 0x07) << LPC_TIMER_CAPTURE_SHIFT(i)); + } + break; + case LPC_TIMER_MODE_MATCH: + timer->regs->match_ctrl = 0; + timer->regs->external_match = 0; + for (i = 0; i < NUM_CHANS; i++) { + timer->regs->match_ctrl |= ((conf->config[i] & 0x07) << LPC_TIMER_MATCH_SHIFT(i)); + timer->regs->match_reg[i] = conf->match[i]; + timer->regs->external_match |= ((conf->ext_match_config[i] & 0x03) << (LPC_TIMER_EXT_MATCH0_SHIFT + i*2)); + } + break; + case LPC_TIMER_MODE_PWM: + if (conf->match[3] == 0) { + return -EINVAL; /* Force use of Channel 3 for PWM cycle length */ + } + /* Activate selected PWM channels 0 to 2 */ + timer->regs->pwm_ctrl = (conf->config[0] & 0x07); + /* Use Channel 3 for PWM cycle length as recommended in the manual */ + timer->regs->match_ctrl &= ~(LPC_TIMER_MATCH_ERASE(3)); + timer->regs->match_ctrl |= (LPC_TIMER_RESET_ON_MATCH << LPC_TIMER_MATCH_SHIFT(3)); + for (i = 0; i < NUM_CHANS; i++) { + timer->regs->match_reg[i] = conf->match[i]; + } + break; + case LPC_TIMER_MODE_PWD: + break; + } + return 0; /* Config OK */ +} + + +/* Setup one pin to use as match or capture pin + * This configure function can be used only for pins on port 0. + * Use LPC_TIMER_*B*_CHANNEL_*_PIN_* definitions for the pin number and either + * LPC_TIMER_PIN_FUNC_CAPTURE or LPC_TIMER_PIN_FUNC_MATCH for the function. + * Note: These pins are not setup using the usual system set default pins functions + * as most functions may be affected to many different pins, and the use of the + * timers does no require the use of pins. + */ +#define LPC_TIMER_PIN_CONFIG (LPC_IO_MODE_PULL_UP | LPC_IO_DIGITAL | LPC_IO_DRIVE_HIGHCURENT) +void timer_pins_setup(uint8_t port, uint8_t pin_num, uint32_t func) +{ + config_gpio(port, pin_num, (func | LPC_TIMER_PIN_CONFIG)); +} + +/* Power up a timer. + * Note that clkrate should be a divider of the main clock frequency chosed + * for your application as it will be used to divide the main clock to get + * the prescaler value. + * Set clkrate to 0 to disable the prescaler. + */ +void timer_on(uint32_t timer_num, uint32_t clkrate) +{ + struct timer_device* timer = NULL; + uint32_t prescale; /* The clock divider for the counter */ + + if (timer_num >= NUM_TIMERS) + return; + timer = &(timer_devices[timer_num]); + + NVIC_DisableIRQ( timer->irq ); + /* Power up the timer */ + subsystem_power(timer->power_bit, 1); + /* Reset counter on next PCLK positive edge, and disable counter */ + timer->regs->timer_ctrl = LPC_TIMER_COUNTER_RESET; + + /* Set the prescaler value */ + if (clkrate == 0) { + prescale = 0; + } else { + prescale = (get_main_clock() / clkrate) - 1; + } + timer->regs->prescale = prescale; + + NVIC_EnableIRQ(timer_devices[timer_num].irq); +} + +/* Removes the main clock from the selected timer block */ +void timer_off(uint32_t timer_num) +{ + if (timer_num >= NUM_TIMERS) + return; + NVIC_DisableIRQ( timer_devices[timer_num].irq ); + subsystem_power(timer_devices[timer_num].power_bit, 0); +} diff --git a/include/core/lpc_regs_12xx.h b/include/core/lpc_regs_12xx.h index d9dce50..5ae6797 100644 --- a/include/core/lpc_regs_12xx.h +++ b/include/core/lpc_regs_12xx.h @@ -605,6 +605,50 @@ struct lpc_timer #define LPC_TMR32B0 ((struct lpc_timer *) LPC_TIMER2_BASE) #define LPC_TMR32B1 ((struct lpc_timer *) LPC_TIMER3_BASE) +#define LPC_TIMER_COUNTER_ENABLE (1 << 0) /* CEN */ +#define LPC_TIMER_COUNTER_RESET (1 << 1) /* CRST */ + +/* Match internal configuration */ +#define LPC_TIMER_INTERRUPT_ON_MATCH 0x01 +#define LPC_TIMER_RESET_ON_MATCH 0x02 +#define LPC_TIMER_STOP_ON_MATCH 0x04 +#define LPC_TIMER_MATCH_ERASE(x) (0x07 << ((x) * 3)) +#define LPC_TIMER_MATCH_SHIFT(x) ((x) * 3) +/* Capture internal configuration */ +#define LPC_TIMER_CAP_ON_RISING_EDGE 0x01 +#define LPC_TIMER_CAP_ON_FALLING_EDGE 0x02 +#define LPC_TIMER_INTERRUPT_ON_CAPTURE 0x04 +#define LPC_TIMER_CAPTURE_ERASE(x) (0x07 << ((x) * 3)) +#define LPC_TIMER_CAPTURE_SHIFT(x) ((x) * 3) +/* Match external configuration */ +#define LPC_TIMER_NOTHING_ON_MATCH 0x00 +#define LPC_TIMER_CLEAR_ON_MATCH 0x01 +#define LPC_TIMER_SET_ON_MATCH 0x02 +#define LPC_TIMER_TOGGLE_ON_MATCH 0x03 +#define LPC_TIMER_EXT_MATCH0_SHIFT 4 +#define LPC_TIMER_EXT_MATCH1_SHIFT 6 +#define LPC_TIMER_EXT_MATCH2_SHIFT 8 +#define LPC_TIMER_EXT_MATCH3_SHIFT 10 +/* Counter */ +#define LPC_COUNTER_IS_TIMER 0x00 +#define LPC_COUNTER_INC_ON_RISING 0x01 +#define LPC_COUNTER_INC_ON_FALLING 0x02 +#define LPC_COUNTER_INC_ON_BOTH 0x03 +#define LPC_COUNTER_INC_INPUT_SHIFT 2 +#define LPC_COUNTER_INC_INPUT(x) (((x) & 0x03) << LPC_COUNTER_INC_INPUT_SHIFT) +#define LPC_COUNTER_CLEAR_ON_EVENT_EN (0x01 << 4) +#define LPC_COUNTER_CLEAR_ON_EVENT_SHIFT 5 +#define LPC_COUNTER_CLEAR_ON_CHAN0_RISE 0x00 +#define LPC_COUNTER_CLEAR_ON_CHAN0_FALL 0x01 +#define LPC_COUNTER_CLEAR_ON_CHAN1_RISE 0x02 +#define LPC_COUNTER_CLEAR_ON_CHAN1_FALL 0x03 +#define LPC_COUNTER_CLEAR_ON_CHAN2_RISE 0x04 +#define LPC_COUNTER_CLEAR_ON_CHAN2_FALL 0x05 +#define LPC_COUNTER_CLEAR_ON_CHAN3_RISE 0x06 +#define LPC_COUNTER_CLEAR_ON_CHAN3_FALL 0x07 +/* PWM */ +#define LPC_PWM_CHANNEL_ENABLE(x) (0x01 << (x)) + /***************************************************************************** */ diff --git a/include/drivers/timers.h b/include/drivers/timers.h new file mode 100644 index 0000000..48163a6 --- /dev/null +++ b/include/drivers/timers.h @@ -0,0 +1,162 @@ +/**************************************************************************** + * drivers/timers.h + * + * Copyright 2012 Nathael Pajani + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + *************************************************************************** */ + + +#include +#include "core/lpc_regs_12xx.h" + +/***************************************************************************** */ +/* Timers */ +/***************************************************************************** */ +/* Four timers are available, Two 16 bits and two 32 bits + * All timers have 4 channels though 32 bits timers have all 4 channels available + * on capture /match pins while 16bits ones have only two (channels 0 and 1). + */ +#define NUM_TIMERS 4 +#define NUM_CHANS 4 + +/* Timer numbers to be used for functions from this driver. */ +#define LPC_TIMER_16B0 0 +#define LPC_TIMER_16B1 1 +#define LPC_TIMER_32B0 2 +#define LPC_TIMER_32B1 3 + + +/* Timer modes for the "mode" timer config structure field */ +#define LPC_TIMER_MODE_COUNTER 1 +#define LPC_TIMER_MODE_CAPTURE 2 +#define LPC_TIMER_MODE_MATCH 3 +#define LPC_TIMER_MODE_PWM 4 /* Pulse Width Modulation */ +#define LPC_TIMER_MODE_PWD 5 /* Pulse Width Demodulation */ + +/* Structure used to pass parameters to configure a timer */ +/* Notes: + * In counter or PWM mode, the config is done using config[0] + * The field "reset_on_capture" must be set to LPC_COUNTER_CLEAR_ON_EVENT_EN ored with one + * of the LPC_COUNTER_CLEAR_ON_CHAN*_* to activate the clear timer on event functionality + */ +struct timer_config +{ + uint32_t mode; /* Counter, Timer Capture, Timer Match or PWM */ + uint8_t config[NUM_CHANS]; /* Configure the internal behavior when a capture or a match occurs */ + uint8_t ext_match_config[NUM_CHANS]; /* Configure the external behavior when a match occurs */ + uint32_t match[NUM_CHANS]; /* The match values if the timer is used in match mode */ + uint32_t reset_on_capture; +}; + + + +/* Start the timer : + * Remove the reset flag if present and set timer enable flag. + * Timer must be turned on and configured (no checks done here). + */ +void timer_start(uint32_t timer_num); + +/* Stops the timer counter + * FIXME: Does not issue reset ... need to check whether it is reseted or not. + */ +void timer_stop(uint32_t timer_num); + +/* Resets the timer and lets it count again imediately */ +void timer_restart(uint32_t timer_num); + +uint32_t timer_get_capture_val(uint32_t timer_num, uint32_t channel); + + +/* Change the match value of a single timer channel */ +void timer_set_match(uint32_t timer_num, uint32_t channel, uint32_t val); + +/***************************************************************************** */ +/* Timer Setup */ +/* Returns 0 on success + * Takes a timer number and a timer config structure as arguments. + * Refer to timer config structure for details. + * Note: use of channel 3 for PWM cycle length is enforced. + */ +int timer_setup(uint32_t timer_num, struct timer_config* conf); + + +/* Some alternate pins for timers are on port 2, not handled by our code as they are + * unavailable on the 48 pins LQFP package used in the GPIO Demo module. + * Some alternate pins for timers are on port 1, not handled by our code to get simpler code ... + * Note that the alternate function numbers for most capture and match functions on + * port 2 are 2 and 3 instead of 3 and 4 ! + */ +/* 16 bits timer 0 match / capture pins */ +#define LPC_TIMER_16B0_CHANNEL_0_PIN_28 28 +#define LPC_TIMER_16B0_CHANNEL_1_PIN_29 29 +#define LPC_TIMER_16B0_CHANNEL_0_PIN_11 11 +#define LPC_TIMER_16B0_CHANNEL_1_PIN_12 12 +/* Alternate on PIO2_0 and PIO2_1 */ + +/* 16 bits timer 1 match / capture pins */ +#define LPC_TIMER_16B1_CHANNEL_0_PIN_15 15 +#define LPC_TIMER_16B1_CHANNEL_1_PIN_16 16 +/* Alternate on PIO1_5 and PIO1_6 */ +/* Alternate on PIO2_2 and PIO2_3 */ + +/* 32 bits timer 0 match / capture pins */ +#define LPC_TIMER_32B0_CHANNEL_0_PIN_1 1 +#define LPC_TIMER_32B0_CHANNEL_1_PIN_2 2 +#define LPC_TIMER_32B0_CHANNEL_2_PIN_3 3 +#define LPC_TIMER_32B0_CHANNEL_3_PIN_4 4 +#define LPC_TIMER_32B0_CHANNEL_0_PIN_18 18 +#define LPC_TIMER_32B0_CHANNEL_1_PIN_19 19 +#define LPC_TIMER_32B0_CHANNEL_2_PIN_20 20 +#define LPC_TIMER_32B0_CHANNEL_3_PIN_21 21 +/* Alternate on PIO2_4 to PIO2_7 */ + +/* 32 bits timer 1 match / capture pins */ +#define LPC_TIMER_32B1_CHANNEL_0_PIN_6 6 +#define LPC_TIMER_32B1_CHANNEL_1_PIN_7 7 +#define LPC_TIMER_32B1_CHANNEL_2_PIN_8 8 +#define LPC_TIMER_32B1_CHANNEL_3_PIN_9 9 +#define LPC_TIMER_32B1_CHANNEL_0_PIN_23 23 +#define LPC_TIMER_32B1_CHANNEL_1_PIN_24 24 +#define LPC_TIMER_32B1_CHANNEL_2_PIN_25 25 +#define LPC_TIMER_32B1_CHANNEL_3_PIN_26 26 +/* Alternate on PIO2_8 to PIO2_11 */ + +/* Capture or match alternate function select for port 0 pins */ +#define LPC_TIMER_PIN_FUNC_CAPTURE LPC_IO_FUNC_ALT(3) +#define LPC_TIMER_PIN_FUNC_MATCH LPC_IO_FUNC_ALT(4) + + +/* Setup one pin to use as match or capture pin + * This configure function can be used only for pins on port 0. + * Use LPC_TIMER_*B*_CHANNEL_*_PIN_* definitions for the pin number and either + * LPC_TIMER_PIN_FUNC_CAPTURE or LPC_TIMER_PIN_FUNC_MATCH for the function. + * Note: These pins are not setup using the usual system set default pins functions + * as most functions may be affected to many different pins, and the use of the + * timers does no require the use of pins. + */ +void timer_pins_setup(uint8_t port, uint8_t pin_num, uint32_t func); + + +/* Power up a timer. + * Note that clkrate should be a divider of the main clock frequency chosed + * for your application as it will be used to divide the main clock to get + * the prescaler value. + * Set clkrate to 0 to disable the prescaler. + */ +void timer_on(uint32_t timer_num, uint32_t clkrate); + +/* Removes the main clock from the selected timer block */ +void timer_off(uint32_t timer_num); -- 2.43.0