--- /dev/null
+/****************************************************************************
+ * drivers/countertimers.c
+ *
+ * Copyright 2012 Nathael Pajani <nathael.pajani@ed3l.fr>
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ *
+ *************************************************************************** */
+
+
+
+/***************************************************************************** */
+/* Timers */
+/***************************************************************************** */
+
+/* Timers driver for the integrated timers of the LPC122x.
+ * The LPC122x Has two 16bits timers and two 32bits timers.
+ * Refer to LPC122x documentation (UM10441.pdf) for more information.
+ */
+
+#include "core/system.h"
+#include "lib/errno.h"
+#include "drivers/timers.h"
+#include "drivers/countertimers.h"
+
+
+/* These are local to our file */
+struct countertimer_device
+{
+ struct lpc_timer* regs;
+ uint32_t power_bit;
+ uint8_t irq;
+ uint8_t configured;
+ void (*callback)(uint32_t); /* Possible RX callback */
+};
+
+static struct countertimer_device countertimers[NUM_COUNTERTIMERS] = {
+ {
+ .regs = LPC_TMR16B0,
+ .power_bit = LPC_SYS_ABH_CLK_CTRL_CT16B0,
+ .irq = TIMER0_IRQ,
+ .configured = 0,
+ .callback = NULL,
+ },
+ {
+ .regs = LPC_TMR16B1,
+ .power_bit = LPC_SYS_ABH_CLK_CTRL_CT16B1,
+ .irq = TIMER1_IRQ,
+ .configured = 0,
+ .callback = NULL,
+ },
+ {
+ .regs = LPC_TMR32B0,
+ .power_bit = LPC_SYS_ABH_CLK_CTRL_CT32B0,
+ .irq = TIMER2_IRQ,
+ .configured = 0,
+ .callback = NULL,
+ },
+ {
+ .regs = LPC_TMR32B1,
+ .power_bit = LPC_SYS_ABH_CLK_CTRL_CT32B1,
+ .irq = TIMER3_IRQ,
+ .configured = 0,
+ .callback = NULL,
+ },
+};
+
+/* Handlers */
+void TIMER_Handler(struct countertimer_device* timer)
+{
+ uint32_t intr_flags = timer->regs->int_reg; /* Backup the flags */
+
+ /* Clear the interrupt */
+ timer->regs->int_reg = intr_flags;
+ /* And call the user routine if one has been registered */
+ if (timer->callback != NULL) {
+ timer->callback(intr_flags);
+ }
+}
+void TIMER_0_Handler(void)
+{
+ TIMER_Handler(&countertimers[0]);
+}
+void TIMER_1_Handler(void)
+{
+ TIMER_Handler(&countertimers[1]);
+}
+void TIMER_2_Handler(void)
+{
+ TIMER_Handler(&countertimers[2]);
+}
+void TIMER_3_Handler(void)
+{
+ TIMER_Handler(&countertimers[3]);
+}
+
+
+
+/* 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 countertimer_start(uint8_t timer_num)
+{
+ /* Remove reset flag and set timer enable flag */
+ countertimers[timer_num].regs->timer_ctrl = LPC_TIMER_COUNTER_ENABLE;
+}
+
+/* Pause the timer counter, does not reset */
+void countertimer_pause(uint8_t timer_num)
+{
+ /* Remove timer enable flag */
+ countertimers[timer_num].regs->timer_ctrl = 0;
+}
+
+/* Stops and resets the timer counter */
+void countertimer_stop(uint8_t timer_num)
+{
+ /* Remove timer enable flag and request reset */
+ countertimers[timer_num].regs->timer_ctrl = LPC_TIMER_COUNTER_RESET;
+ /* Remove reset flag */
+ countertimers[timer_num].regs->timer_ctrl = 0;
+}
+
+/* Resets the timer and lets it count again imediately */
+void countertimer_restart(uint8_t timer_num)
+{
+ /* Set timer reset flag */
+ countertimers[timer_num].regs->timer_ctrl = LPC_TIMER_COUNTER_RESET;
+ /* Remove reset flag and start counter */
+ countertimers[timer_num].regs->timer_ctrl = LPC_TIMER_COUNTER_ENABLE;
+}
+
+int countertimer_get_counter_val(uint8_t timer_num, uint32_t* val)
+{
+ *val = countertimers[timer_num].regs->timer_counter;
+ return 0;
+}
+
+int countertimer_get_capture_val(uint8_t timer_num, uint8_t channel, uint32_t* val)
+{
+ if (channel >= MAX_CHANNELS) {
+ return -EINVAL;
+ }
+ *val = countertimers[timer_num].regs->capture_reg[channel];
+ return 0;
+}
+
+
+/* Change the match value of a single timer channel */
+int countertimer_set_match(uint8_t timer_num, uint8_t channel, uint32_t val)
+{
+ if (channel > NUM_COUNTERTIMERS_CHANS)
+ return -EINVAL;
+
+ countertimers[timer_num].regs->match_reg[channel] = val;
+ return 0;
+}
+
+struct common_operations countertimer_ops = {
+ .start = countertimer_start,
+ .cont = countertimer_start,
+ .pause = countertimer_pause,
+ .stop = countertimer_stop,
+ .restart = countertimer_restart,
+ .halt = countertimer_stop,
+ .get_counter = countertimer_get_counter_val,
+ .get_capture = countertimer_get_capture_val,
+ .set_match = countertimer_set_match,
+};
+
+
+
+/*******************************************************************************/
+/* Configuration operations */
+
+
+/* Timer Setup as PWM */
+/* Returns 0 on success
+ * Takes a timer number and a timer PWM config structure as arguments.
+ * Refer to timer PWM config structure for details.
+ */
+int countertimer_pwm_setup(uint8_t timer_num, const struct lpc_timer_pwm_config* conf)
+{
+ struct countertimer_device* timer = &(countertimers[timer_num]);
+ uint8_t max_chan = 0;
+ uint8_t active_chans = 0;
+ int i = 0;
+
+ /* Make sure we have a PWM cycle length */
+ if (conf->period == 0) {
+ return -EINVAL;
+ }
+ switch (timer_num) {
+ case LPC_TIMER_16B0:
+ case LPC_TIMER_16B1:
+ if (conf->nb_channels > NUM_COUNTERTIMERS_16B_PWM_CHANS) {
+ return -EINVAL;
+ }
+ max_chan = NUM_COUNTERTIMERS_16B_PWM_CHANS;
+ break;
+ case LPC_TIMER_32B0:
+ case LPC_TIMER_32B1:
+ if (conf->nb_channels > NUM_COUNTERTIMERS_32B_PWM_CHANS) {
+ return -EINVAL;
+ }
+ max_chan = NUM_COUNTERTIMERS_32B_PWM_CHANS;
+ break;
+ }
+
+ if (conf->period_chan >= MAX_CHANNELS) {
+ return -EINVAL;
+ }
+ /* Setup selected PWM channels */
+ for (i = 0; i < conf->nb_channels; i++) {
+ if (conf->outputs[i] >= max_chan) {
+ continue;
+ }
+ timer->regs->match_reg[ conf->outputs[i] ] = conf->match_values[i];
+ /* Mark channel as active */
+ active_chans |= LPC_PWM_CHANNEL_ENABLE(conf->outputs[i]);
+ }
+
+ /* Setup period */
+ timer->regs->match_reg[ conf->period_chan ] = conf->period;
+ /* Setup selected channel as PWM cycle length control */
+ timer->regs->match_ctrl &= ~(LPC_TIMER_MATCH_ERASE(conf->period_chan));
+ timer->regs->match_ctrl |= (LPC_TIMER_RESET_ON_MATCH << LPC_TIMER_MATCH_SHIFT(conf->period_chan));
+ active_chans |= LPC_PWM_CHANNEL_ENABLE(conf->period_chan);
+
+ /* Setup count mode as timer */
+ timer->regs->count_ctrl = LPC_COUNTER_IS_TIMER;
+
+ /* Activate selected PWM channels and period channel */
+ timer->regs->pwm_ctrl = active_chans;
+
+ return 0; /* Config OK */
+}
+
+
+/* Timer Setup in timer or counter mode, with optionnal capture and match configuration
+ * Takes a timer number and a timer counter config structure as arguments.
+ * Returns 0 on success
+ */
+int countertimer_tc_setup(uint8_t timer_num, const struct lpc_tc_config* conf)
+{
+ struct countertimer_device* timer = &(countertimers[timer_num]);
+ int i = 0;
+
+ if (conf->mode & LPC_TIMER_MODE_SPECIFIC) {
+ return -EINVAL;
+ }
+ /* Erase existing configuration */
+ timer->regs->capture_ctrl = 0;
+ timer->regs->match_ctrl = 0;
+ timer->regs->external_match = 0;
+
+ /* Override the prescaler value if requested */
+ if (conf->prescale_val != 0) {
+ timer->regs->prescale = conf->prescale_val;
+ }
+ /* Select between timer (counts on PCLK) and counter mode (counts on CAP events) */
+ if (conf->mode & LPC_TIMER_MODE_COUNTER) {
+ /* Configure the counter */
+ timer->regs->count_ctrl = (conf->count_control & 0x0F);
+ timer->regs->count_ctrl |= LPC_COUNTER_INC_INPUT(conf->count_chan);
+ } else {
+ /* Timer mode */
+ timer->regs->count_ctrl = LPC_COUNTER_IS_TIMER;
+ }
+
+ /* Configure the reset on capture functionality when selected */
+ if (conf->reset_on_cap & LPC_COUNTER_CLEAR_ON_EVENT_EN) {
+ timer->regs->count_ctrl = LPC_COUNTER_CLEAR_ON_EVENT_EN;
+ timer->regs->count_ctrl |= ((conf->reset_on_cap & 0x07) << LPC_COUNTER_CLEAR_ON_EVENT_SHIFT);
+ }
+
+ if (conf->mode & LPC_TIMER_MODE_CAPTURE) {
+ for (i = 0; i < MAX_CHANNELS; i++) {
+ timer->regs->capture_ctrl |= ((conf->cap_control[i] & 0x07) << LPC_TIMER_CAPTURE_SHIFT(i));
+ }
+ }
+
+ if (conf->mode & LPC_TIMER_MODE_MATCH) {
+ for (i = 0; i < MAX_CHANNELS; i++) {
+ timer->regs->match_ctrl |= ((conf->match_control[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_MATCH_SHIFT(i));
+ }
+ }
+
+ /* Ensure that counter input chan has not been configured to reset or stop the timer or as a
+ * capture channel (See remarks in user manual UM10441 page 268 section 14.7.11) when the ct is
+ * configured in counter mode. */
+ if (conf->mode & LPC_TIMER_MODE_COUNTER) {
+ timer->regs->match_ctrl &= ~LPC_TIMER_MATCH_ERASE(conf->count_chan);
+ timer->regs->capture_ctrl &= ~LPC_TIMER_CAPTURE_ERASE(conf->count_chan);
+ timer->regs->external_match &= ~LPC_TIMER_EXT_MATCH_ERASE(conf->count_chan);
+ }
+
+ return 0; /* Config OK */
+}
+
+struct config_operations countertimer_cfg_ops = {
+ .pwm_config = countertimer_pwm_setup,
+ .tc_config = countertimer_tc_setup,
+};
+
+
+
+
+/*******************************************************************************/
+/* Init operations */
+
+/* Power up a timer.
+ * clkrate is the desired timer clock rate. It will be used to divide the main clock
+ * to get the timer prescaler value.
+ * Set clkrate to 0 to disable the prescaler.
+ */
+int countertimer_on(uint8_t timer_num, uint32_t clkrate, void (*callback)(uint32_t))
+{
+ struct countertimer_device* timer = NULL;
+ uint32_t prescale; /* The clock divider for the counter */
+
+ timer = &(countertimers[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;
+
+ /* Store the callback, OK even if none given */
+ timer->callback = callback;
+
+ /* Set the prescaler value */
+ if (clkrate == 0) {
+ prescale = 0;
+ } else {
+ prescale = (get_main_clock() / clkrate) - 1;
+ }
+ timer->regs->prescale = prescale;
+
+ NVIC_EnableIRQ(countertimers[timer_num].irq);
+ return 0;
+}
+
+/* Removes the main clock from the selected timer block */
+int countertimer_off(uint8_t timer_num)
+{
+ NVIC_DisableIRQ( countertimers[timer_num].irq );
+ subsystem_power(countertimers[timer_num].power_bit, 0);
+ return 0;
+}
+
+struct init_operations countertimer_init_ops = {
+ .timer_on = countertimer_on,
+ .timer_off = countertimer_off,
+};
+
/* Timers */
/***************************************************************************** */
-/* Timers driver for the integrated timers of the LPC1224.
- * The LPC1224 Has two 16bits timers and 2 32bits timers.
- * Refer to LPC1224 documentation (UM10441.pdf) for more information.
+/* Driver for the different kinds of timers available in the LPC122x
+ *
+ * This inludes :
+ * - 32 bits Timers driver
+ * The LPC122x has two 32 bits Timer.
+ * - 16 bits Timers driver
+ * The LPC122x has two 16 bits Timer.
+ *
+ * Refer to LPC122x documentation (UM10441.pdf) for more information.
+ *
+ * All common functions are available using a common interface. Only specific
+ * functions have a specific interface, described in separate headers files.
*/
/* These are local to our file */
-struct timer_device
-{
- struct lpc_timer* regs;
- uint32_t power_bit;
- uint32_t irq;
- void (*callback)(uint32_t); /* Possible RX callback */
+struct timer_device {
+ uint8_t num;
+ struct common_operations* ops;
+ struct config_operations* cfg_ops;
+ struct init_operations* init_ops;
};
-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_Handler(struct timer_device* timer)
-{
- uint32_t intr_flags = timer->regs->int_reg; /* Backup the flags */
+/* 32 bits and 16 bits Counter Timers */
+extern struct common_operations countertimer_ops;
+extern struct config_operations countertimer_cfg_ops;
+extern struct init_operations countertimer_init_ops;
+
+
+struct timer_device timers[NUM_TIMERS] = {
+ {
+ .num = LPC_TIMER_16B0,
+ .ops = &countertimer_ops,
+ .cfg_ops = &countertimer_cfg_ops,
+ .init_ops = &countertimer_init_ops,
+ },
+ {
+ .num = LPC_TIMER_16B1,
+ .ops = &countertimer_ops,
+ .cfg_ops = &countertimer_cfg_ops,
+ .init_ops = &countertimer_init_ops,
+ },
+ {
+ .num = LPC_TIMER_32B0,
+ .ops = &countertimer_ops,
+ .cfg_ops = &countertimer_cfg_ops,
+ .init_ops = &countertimer_init_ops,
+ },
+ {
+ .num = LPC_TIMER_32B1,
+ .ops = &countertimer_ops,
+ .cfg_ops = &countertimer_cfg_ops,
+ .init_ops = &countertimer_init_ops,
+ },
+};
- /* Clear the interrupt */
- timer->regs->int_reg = intr_flags;
- /* And call the user routine if one has been registered */
- if (timer->callback != NULL) {
- timer->callback(intr_flags);
- }
-}
-void TIMER_0_Handler(void)
-{
- TIMER_Handler(&timer_devices[0]);
-}
-void TIMER_1_Handler(void)
-{
- TIMER_Handler(&timer_devices[1]);
-}
-void TIMER_2_Handler(void)
-{
- TIMER_Handler(&timer_devices[2]);
-}
-void TIMER_3_Handler(void)
-{
- TIMER_Handler(&timer_devices[3]);
-}
+/*******************************************************************************/
+/* Wrappers to common functions */
+#define GENERIC_TIMER_OPS(name) \
+ void timer_ ## name(uint8_t timer_num) \
+ { \
+ if (timer_num >= NUM_TIMERS) \
+ return; \
+ if (timers[timer_num].ops && timers[timer_num].ops->name) { \
+ timers[timer_num].ops->name(timer_num); \
+ } \
+ }
-/* 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).
+/* Start a timer */
+GENERIC_TIMER_OPS(start);
+/* Pause timer operation */
+GENERIC_TIMER_OPS(pause);
+/* Continue timer operation when the timer got paused */
+GENERIC_TIMER_OPS(cont);
+/* Reset the timer and let it count again imediately */
+GENERIC_TIMER_OPS(restart);
+/* Stop timer counting and reset timer counter to reload value / initial state */
+GENERIC_TIMER_OPS(stop);
+/* Same as stop */
+GENERIC_TIMER_OPS(halt);
+
+
+/* Get the current counter value
+ * Return 0 if the value is valid.
*/
-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;
-}
-void timer_continue(uint32_t timer_num) __attribute__ ((alias ("timer_start")));
-/* Pause the timer counter, does not reset */
-void timer_pause(uint32_t timer_num)
-{
- if (timer_num >= NUM_TIMERS)
- return;
- /* Remove timer enable flag */
- timer_devices[timer_num].regs->timer_ctrl = 0;
-}
-/* Stops and resets the timer counter */
-void timer_stop(uint32_t timer_num)
-{
- if (timer_num >= NUM_TIMERS)
- return;
- /* Remove timer enable flag and request reset */
- timer_devices[timer_num].regs->timer_ctrl = LPC_TIMER_COUNTER_RESET;
- /* Remove reset 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)
+int timer_get_counter_val(uint8_t timer_num, uint32_t* val)
{
if (timer_num >= NUM_TIMERS)
- return;
- /* Set timer reset flag */
- timer_devices[timer_num].regs->timer_ctrl = LPC_TIMER_COUNTER_RESET;
- /* Remove reset flag and start counter */
- timer_devices[timer_num].regs->timer_ctrl = LPC_TIMER_COUNTER_ENABLE;
+ return -EINVAL;
+ if (timers[timer_num].ops && timers[timer_num].ops->get_counter) {
+ return timers[timer_num].ops->get_counter(timer_num, val);
+ }
+ return -ENODEV;
}
-uint32_t timer_get_capture_val(uint32_t timer_num, uint32_t channel)
+
+/* Get the value of the timer when the capture event last triggered
+ * Return 0 if the value is valid.
+ */
+int timer_get_capture_val(uint8_t timer_num, uint8_t channel, uint32_t* val)
{
if (timer_num >= NUM_TIMERS)
- return 0;
- /* FIXME */
- return 0;
+ return -EINVAL;
+ if (timers[timer_num].ops && timers[timer_num].ops->get_capture) {
+ return timers[timer_num].ops->get_capture(timer_num, channel, val);
+ }
+ return -ENODEV;
}
-uint32_t timer_get_counter_val(uint32_t timer_num)
+
+
+/* Change the match value of a single timer channel */
+int timer_set_match(uint8_t timer_num, uint8_t channel, uint32_t val)
{
if (timer_num >= NUM_TIMERS)
- return 0;
- return timer_devices[timer_num].regs->timer_counter;
+ return -EINVAL;
+ if (timers[timer_num].ops && timers[timer_num].ops->set_match) {
+ return timers[timer_num].ops->set_match(timer_num, channel, val);
+ }
+ return -ENODEV;
}
-/* Change the match value of a single timer channel */
-void timer_set_match(uint32_t timer_num, uint32_t channel, uint32_t val)
+
+/*******************************************************************************/
+/* Configuration */
+
+/* Configure the timer as PWM. Call to timer-specific function */
+int timer_pwm_config(uint8_t timer_num, const struct lpc_timer_pwm_config* conf)
{
if (timer_num >= NUM_TIMERS)
- return;
- if (channel > 3)
- return;
-
- timer_devices[timer_num].regs->match_reg[channel] = val;
+ return -EINVAL;
+ if (timers[timer_num].cfg_ops && timers[timer_num].cfg_ops->pwm_config) {
+ return timers[timer_num].cfg_ops->pwm_config(timer_num, conf);
+ }
+ return -ENODEV;
}
-/***************************************************************************** */
-/* Timer Setup */
-/* Returns 0 on success
- * Takes a timer number and a timer config structure as arguments.
- * Refer to timer config structure for details.
+/* Timer Setup in timer or counter mode, with optionnal capture and match configuration
+ * Takes a timer number and a timer counter config structure as arguments.
+ * Returns 0 on success
*/
-int timer_setup(uint32_t timer_num, const struct timer_config* conf)
+int timer_counter_config(uint8_t timer_num, const struct lpc_tc_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);
+ return -EINVAL;
+ if (timers[timer_num].cfg_ops && timers[timer_num].cfg_ops->tc_config) {
+ return timers[timer_num].cfg_ops->tc_config(timer_num, conf);
}
-
- switch (conf->mode) {
- case LPC_TIMER_MODE_TIMER:
- timer->regs->capture_ctrl = 0; /* Timer mode ! */
- timer->regs->count_ctrl = LPC_COUNTER_IS_TIMER;
- break;
- 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)
- * Use the LPC_COUNTER_INC_INPUT(x) set by the user to do so automatically
- */
- 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:
- /* Make sure we have a PWM cycle length */
- if (conf->match[ conf->config[1] ] == 0) {
- return -EINVAL;
- }
- /* Activate selected PWM channels 0 to 3 */
- timer->regs->pwm_ctrl = (conf->config[0] & 0x0F);
- timer->regs->match_ctrl &= ~(LPC_TIMER_MATCH_ERASE(conf->config[1]));
- timer->regs->match_ctrl |= (LPC_TIMER_RESET_ON_MATCH << LPC_TIMER_MATCH_SHIFT(conf->config[1]));
- 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 */
+ return -ENODEV;
}
+/*******************************************************************************/
+/* Init operations */
/* 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.
+ * clkrate is the desired timer clock rate. It will be used to divide the main clock
+ * to get the timer prescaler value.
+ * Set clkrate to 0 to disable the prescaler.
+ * callback is the interrupt callback for this timer.
*/
-void timer_on(uint32_t timer_num, uint32_t clkrate, void (*callback)(uint32_t))
+int timer_on(uint8_t timer_num, uint32_t clkrate, void (*callback)(uint32_t))
{
- 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;
-
- /* Store the callback, OK even if none given */
- timer->callback = callback;
-
- /* Set the prescaler value */
- if (clkrate == 0) {
- prescale = 0;
- } else {
- prescale = (get_main_clock() / clkrate) - 1;
+ return -EINVAL;
+ if (timers[timer_num].init_ops && timers[timer_num].init_ops->timer_on) {
+ timers[timer_num].init_ops->timer_on(timer_num, clkrate, callback);
}
- timer->regs->prescale = prescale;
-
- NVIC_EnableIRQ(timer_devices[timer_num].irq);
+ return -ENODEV;
}
/* Removes the main clock from the selected timer block */
-void timer_off(uint32_t timer_num)
+int timer_off(uint8_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);
+ return -EINVAL;
+ if (timers[timer_num].init_ops && timers[timer_num].init_ops->timer_off) {
+ return timers[timer_num].init_ops->timer_off(timer_num);
+ }
+ return -ENODEV;
}
+
--- /dev/null
+/****************************************************************************
+ * drivers/countertimers.h
+ *
+ * Copyright 2012-2016 Nathael Pajani <nathael.pajani@ed3l.fr>
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ *
+ *************************************************************************** */
+
+#ifndef DRIVERS_COUNTER_TIMERS_H
+#define DRIVERS_COUNTER_TIMERS_H
+
+
+#include "lib/stdint.h"
+#include "core/lpc_regs.h"
+
+/***************************************************************************** */
+/* Counter Timer */
+/***************************************************************************** */
+/* Timers driver for the integrated timers of the LPC122x.
+ * The LPC122x Has two 16bits timers and two 32bits timers.
+ * Refer to LPC122x documentation (UM10441.pdf) for more information.
+ */
+#define NUM_COUNTERTIMERS 4
+#define NUM_COUNTERTIMERS_CHANS 4
+#define NUM_COUNTERTIMERS_16B_PWM_CHANS 2
+#define NUM_COUNTERTIMERS_32B_PWM_CHANS 4
+
+/* Timer (TMR) */
+struct lpc_timer
+{
+ volatile uint32_t int_reg; /* 0x000 : Interrupt Register (R/W) */
+ volatile uint32_t timer_ctrl; /* 0x004 : Timer Control Register (R/W) */
+ volatile uint32_t timer_counter; /* 0x008 : Timer Counter Register (R/W) */
+ volatile uint32_t prescale; /* 0x00C : Prescale Register (R/W) */
+ volatile uint32_t prescale_counter; /* 0x010 : Prescale Counter Register (R/W) */
+ volatile uint32_t match_ctrl; /* 0x014 : Match Control Register (R/W) */
+ volatile uint32_t match_reg[4]; /* 0x018 : Match Register 0 to 3 (R/W) */
+ volatile uint32_t capture_ctrl; /* 0x028 : Capture Control Register (R/W) */
+ volatile const uint32_t capture_reg[4]; /* 0x02C : Capture Register 0 to 3 (R/ ) */
+ volatile uint32_t external_match; /* 0x03C : External Match Register (R/W) */
+ uint32_t reserved_2[12];
+ volatile uint32_t count_ctrl; /* 0x070 : Count Control Register (R/W) */
+ volatile uint32_t pwm_ctrl; /* 0x074 : PWM Control Register (R/W) */
+};
+#define LPC_TMR16B0 ((struct lpc_timer *) LPC_TIMER0_BASE)
+#define LPC_TMR16B1 ((struct lpc_timer *) LPC_TIMER1_BASE)
+#define LPC_TMR32B0 ((struct lpc_timer *) LPC_TIMER2_BASE)
+#define LPC_TMR32B1 ((struct lpc_timer *) LPC_TIMER3_BASE)
+#define LPC_TIMER_REGS(x) ((struct lpc_timer *) (LPC_TIMER0_BASE + ((x) * 0x4000)))
+
+
+#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_INT_RESET_AND_STOP_ON_MATCH \
+ (LPC_TIMER_INTERRUPT_ON_MATCH | LPC_TIMER_RESET_ON_MATCH | LPC_TIMER_STOP_ON_MATCH)
+#define LPC_TIMER_MATCH_SHIFT(x) (((x) & 0x03) * 3)
+#define LPC_TIMER_MATCH_ERASE(x) (0x07 << LPC_TIMER_MATCH_SHIFT(x))
+
+/* 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_SHIFT(x) (((x) & 0x03) * 3)
+#define LPC_TIMER_CAPTURE_ERASE(x) (0x07 << LPC_TIMER_CAPTURE_SHIFT(x))
+
+/* 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_MATCH_SHIFT(x) (((x) * 2) + 4)
+#define LPC_TIMER_EXT_MATCH_ERASE(x) (0x03 << LPC_TIMER_EXT_MATCH_SHIFT(x))
+
+/* 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) & 0x03))
+
+
+#endif /* DRIVERS_COUNTER_TIMERS_H */
+
+
/****************************************************************************
* drivers/timers.h
*
- * Copyright 2012 Nathael Pajani <nathael.pajani@ed3l.fr>
+ * Copyright 2012-2016 Nathael Pajani <nathael.pajani@ed3l.fr>
*
* 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
/***************************************************************************** */
/* Timers */
/***************************************************************************** */
-/* Timers driver for the integrated timers of the LPC1224.
- * 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).
- * Refer to LPC1224 documentation (UM10441.pdf) for more information.
+
+/* Driver for the different kinds of timers available in the LPC122x
+ *
+ * This inludes :
+ * - 32 bits Timers driver
+ * The LPC122x has two 32 bits Timer.
+ * - 16 bits Timers driver
+ * The LPC122x has two 16 bits Timer.
+ *
+ * Refer to LPC122x documentation (UM10441.pdf) for more information.
+ *
+ * All common functions are available using a common interface. Only specific
+ * functions have a specific interface, described in separate headers files.
+ */
#include "lib/stdint.h"
+#include "drivers/countertimers.h"
+
+/***************************************************************************** */
+/* All timers have 4 channels. 32 bits timers have all 4 channels available
+ * on capture / match pins while 16 bits timers have only two (channels 0 and 1).
*/
#define NUM_TIMERS 4
-#define NUM_CHANS 4
+#define MAX_CHANNELS 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 */
-enum lpc_timer_mode {
- LPC_TIMER_MODE_TIMER = 0,
- LPC_TIMER_MODE_COUNTER,
- LPC_TIMER_MODE_CAPTURE,
- LPC_TIMER_MODE_MATCH,
- LPC_TIMER_MODE_PWM, /* Pulse Width Modulation */
- LPC_TIMER_MODE_PWD, /* Pulse Width Demodulation */
+enum lpc_timers {
+ LPC_TIMER_16B0 = 0, /* 16 bits timer 0 */
+ LPC_TIMER_16B1, /* 16 bits timer 1 */
+ LPC_TIMER_32B0, /* 32 bits timer 0 */
+ LPC_TIMER_32B1, /* 32 bits timer 1 */
};
-/* Structure used to pass parameters to configure a timer */
-/* Notes:
- * In counter or PWM mode, the config is done using config[0] for enabled channels and config[1] holds
- * the channel number used to control PWM cycle.
- * Note that the manual recommends Channel 3 be used for PWM cycle length.
- * 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
+/* Available timer modes *
+ * Some mode may be combined, and some are exlusive :
+ * Values here are important :
+ * PWM and PWD and any further mode handled by a specific configure function will have
+ * bit 2 set
*/
-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;
+#define LPC_TIMER_MODE_TIMER (0x00 << 0)
+#define LPC_TIMER_MODE_COUNTER (0x01 << 0)
+#define LPC_TIMER_MODE_SPECIFIC (0x02 << 0)
+#define LPC_TIMER_MODE_CAPTURE (0x01 << 6)
+#define LPC_TIMER_MODE_MATCH (0x01 << 7)
+/* Specific modes */
+#define LPC_TIMER_MODE_PWM (LPC_TIMER_MODE_SPECIFIC | (0x01 << 2)) /* Pulse Width Modulation */
+#define LPC_TIMER_MODE_PWD (LPC_TIMER_MODE_SPECIFIC | (0x02 << 2)) /* Pulse Width Demodulation */
+
+
+/*******************************************************************************/
+/* Configurations */
+
+/* PWM
+ * This structure is used for the timer_pwm_config() function.
+ * nb_channels is the number of PWM channels used
+ * period_chan is the channel number used to generate the period.
+ * period is the PWM cycle period, in timer clock cycles. It is the value to wich the timer
+ * will count.
+ * outputs[] is the list of outputs / channels corresponding to each 'match_values'
+ * match_values[] control the PWM channels duty-cycle. They are the timer values at wich
+ * the corresponding outputs will toggle. They must be lower than or equal to the period
+ * value.
+ * outputs_initial_state defines whether the outputs start low or high at the beginning of
+ * the PWM cycle. Support for this depends on the target.
+ * The LPC122x enforces a low state initial output.
+ */
+struct lpc_timer_pwm_config {
+ uint8_t nb_channels;
+ uint8_t period_chan;
+ uint32_t period;
+ uint8_t outputs[MAX_CHANNELS];
+ uint32_t match_values[MAX_CHANNELS];
+ uint32_t outputs_initial_state;
+};
+
+/* This structure is used for the timer_counter_config() function.
+ * mode is a mask of LPC_TIMER_MODE_* values. Note that some can be combined, and some are
+ * exclusive : only one of these : LPC_TIMER_MODE_TIMER or LPC_TIMER_MODE_COUNTER and
+ * possibly one or both of these : LPC_TIMER_MODE_CAPTURE and LPC_TIMER_MODE_MATCH
+ * prescale_val: if not nul, override the prescaler value computed during timer_on() with this
+ * value. It is not clear in the documentation whether the prescaler is used in counter mode
+ * or not.
+ * count_control selects the capture channel used to increment the counter when in counter mode.
+ * count_control is one of LPC_COUNTER_INC_ON_RISING or LPC_COUNTER_INC_ON_FALLING or
+ * LPC_COUNTER_INC_ON_BOTH.
+ * count_chann selects the channel used to increment the counter (value between 0 and MAX_CHANS).
+ * reset_on_cap is activated by adding LPC_COUNTER_CLEAR_ON_EVENT_EN to a capture reset event (one
+ * of LPC_COUNTER_CLEAR_ON_*** whith *** Going from CHAN0_RISE, CHAN0_FALL, ... to CHAN3_FALL)
+ * match_control[] is a combination of LPC_TIMER_INTERRUPT_ON_MATCH, LPC_TIMER_RESET_ON_MATCH and
+ * LPC_TIMER_STOP_ON_MATCH for each match channel.
+ * This controls the internal behaviour on each match event.
+ * match[] holds the match values.
+ * ext_match_config[] is one of LPC_TIMER_NOTHING_ON_MATCH, LPC_TIMER_CLEAR_ON_MATCH,
+ * LPC_TIMER_SET_ON_MATCH or LPC_TIMER_TOGGLE_ON_MATCH for each match channel.
+ * This controls the behavior of the output associated to the corresponding match channel. The
+ * corresponding pin must be configured as match output by the user.
+ * cap_control[] is a combination of LPC_TIMER_CAP_ON_RISING_EDGE, LPC_TIMER_CAP_ON_FALLING_EDGE
+ * and LPC_TIMER_INTERRUPT_ON_CAPTURE.
+ * This controls when the TC current value is loaded to the corresponding match value register,
+ * and whether this triggers an interrupt or not.
+ */
+struct lpc_tc_config {
+ uint16_t mode;
+ uint32_t prescale_val;
+ uint8_t count_control;
+ uint8_t count_chan;
+ uint8_t reset_on_cap;
+ uint8_t match_control[MAX_CHANNELS];
+ uint32_t match[MAX_CHANNELS];
+ uint8_t ext_match_config[MAX_CHANNELS];
+ uint8_t cap_control[MAX_CHANNELS];
};
+/*******************************************************************************/
+/* Operation structures, not for use by user programms */
+struct common_operations {
+ /* Running control */
+ void (*start)(uint8_t);
+ void (*stop)(uint8_t);
+ void (*pause)(uint8_t);
+ void (*cont)(uint8_t);
+ void (*restart)(uint8_t);
+ void (*halt)(uint8_t);
+ /* Counter */
+ int (*get_counter)(uint8_t, uint32_t*);
+ int (*get_capture)(uint8_t, uint8_t, uint32_t*);
+ int (*set_match)(uint8_t, uint8_t, uint32_t);
+};
+struct config_operations {
+ int (*pwm_config)(uint8_t, const struct lpc_timer_pwm_config*);
+ int (*tc_config)(uint8_t, const struct lpc_tc_config*);
+};
+struct init_operations {
+ int (*timer_on)(uint8_t, uint32_t, void (*)(uint32_t));
+ int (*timer_off)(uint8_t);
+};
+
+/*******************************************************************************/
+/* Common operations */
+
/* 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);
-void timer_continue(uint32_t timer_num);
+void timer_start(uint8_t timer_num);
+void timer_continue(uint8_t timer_num);
/* Pause the timer counter, does not reset */
-void timer_pause(uint32_t timer_num);
+void timer_pause(uint8_t timer_num);
/* Stop and reset the timer counter */
-void timer_stop(uint32_t timer_num);
+void timer_stop(uint8_t timer_num);
+void timer_halt(uint8_t timer_num);
/* Resets the timer and lets it count again imediately */
-void timer_restart(uint32_t timer_num);
+void timer_restart(uint8_t timer_num);
-uint32_t timer_get_capture_val(uint32_t timer_num, uint32_t channel);
-uint32_t timer_get_counter_val(uint32_t timer_num);
+/* Get the current counter value
+ * Return 0 if the value is valid.
+ */
+int timer_get_counter_val(uint8_t timer_num, uint32_t* val);
+
+
+/* Get the value of the timer when the capture event last triggered
+ * Return 0 if the value is valid.
+ */
+int timer_get_capture_val(uint8_t timer_num, uint8_t channel, uint32_t* val);
/* Change the match value of a single timer channel */
-void timer_set_match(uint32_t timer_num, uint32_t channel, uint32_t val);
+int timer_set_match(uint8_t timer_num, uint8_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.
+
+/*******************************************************************************/
+/* Configuration */
+
+/* PWM configuration in order to use the timer as a PWM controller.
+ * For the pwm_conf structure, refer to it's definition above..
+ * The Timer must be "on" (call timer_on() for this timer before the call to
+ * timer_pwm_config(), with the disired clock rate, which will define the
+ * length of a timer clock cycle, which is the base for the timer period
+ * definition).
+ * The timer will not be started. User code must call timer_start() in order
+ * to start the PWM.
+ */
+int timer_pwm_config(uint8_t timer_num, const struct lpc_timer_pwm_config* pwm_conf);
+
+
+/* Timer Setup in timer or counter mode, with optionnal capture and match configuration
+ * Takes a timer number and a timer counter config structure as arguments.
+ * Returns 0 on success
*/
-int timer_setup(uint32_t timer_num, const struct timer_config* conf);
+int timer_counter_config(uint8_t timer_num, const struct lpc_tc_config* conf);
+/*******************************************************************************/
+/* Init */
/* 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.
+ * clkrate is the desired timer clock rate. It will be used to divide the main clock
+ * to get the timer prescaler value.
+ * Set clkrate to 0 to disable the prescaler.
* callback is used for all the possible timer interrupts (activated using the
* config field in timer_config struct upon timer setup)
* The interrupt flags are passed to the interrupt routine as argument.
*/
-void timer_on(uint32_t timer_num, uint32_t clkrate, void (*callback)(uint32_t));
+int timer_on(uint8_t timer_num, uint32_t clkrate, void (*callback)(uint32_t));
/* Removes the main clock from the selected timer block */
-void timer_off(uint32_t timer_num);
+int timer_off(uint8_t timer_num);
+
#endif /* DRIVERS_TIMERS_H */