Add support for timers / PWM /Counter Most has not been tested yet.
authorNathael Pajani <nathael.pajani@ed3l.fr>
Thu, 7 Nov 2013 02:02:09 +0000 (03:02 +0100)
committerNathael Pajani <nathael.pajani@ed3l.fr>
Tue, 8 Nov 2022 16:03:04 +0000 (17:03 +0100)
drivers/timers.c [new file with mode: 0644]
include/core/lpc_regs_12xx.h
include/drivers/timers.h [new file with mode: 0644]

diff --git a/drivers/timers.c b/drivers/timers.c
new file mode 100644 (file)
index 0000000..1e03277
--- /dev/null
@@ -0,0 +1,234 @@
+/****************************************************************************
+ *  drivers/timers.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                                                       */
+/***************************************************************************** */
+
+#include <stdint.h>
+#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);
+}
index d9dce50..5ae6797 100644 (file)
@@ -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 (file)
index 0000000..48163a6
--- /dev/null
@@ -0,0 +1,162 @@
+/****************************************************************************
+ *  drivers/timers.h
+ *
+ * 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/>.
+ *
+ *************************************************************************** */
+
+
+#include <stdint.h>
+#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);